@metamask/snaps-controllers 9.9.0 → 9.10.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 (82) hide show
  1. package/CHANGELOG.md +9 -1
  2. package/dist/cronjob/CronjobController.cjs +34 -21
  3. package/dist/cronjob/CronjobController.cjs.map +1 -1
  4. package/dist/cronjob/CronjobController.mjs +34 -21
  5. package/dist/cronjob/CronjobController.mjs.map +1 -1
  6. package/dist/insights/SnapInsightsController.cjs +144 -194
  7. package/dist/insights/SnapInsightsController.cjs.map +1 -1
  8. package/dist/insights/SnapInsightsController.mjs +143 -193
  9. package/dist/insights/SnapInsightsController.mjs.map +1 -1
  10. package/dist/interface/SnapInterfaceController.cjs +65 -90
  11. package/dist/interface/SnapInterfaceController.cjs.map +1 -1
  12. package/dist/interface/SnapInterfaceController.mjs +65 -90
  13. package/dist/interface/SnapInterfaceController.mjs.map +1 -1
  14. package/dist/interface/utils.cjs +6 -2
  15. package/dist/interface/utils.cjs.map +1 -1
  16. package/dist/interface/utils.d.cts.map +1 -1
  17. package/dist/interface/utils.d.mts.map +1 -1
  18. package/dist/interface/utils.mjs +6 -2
  19. package/dist/interface/utils.mjs.map +1 -1
  20. package/dist/services/AbstractExecutionService.cjs +77 -71
  21. package/dist/services/AbstractExecutionService.cjs.map +1 -1
  22. package/dist/services/AbstractExecutionService.mjs +77 -71
  23. package/dist/services/AbstractExecutionService.mjs.map +1 -1
  24. package/dist/services/ProxyPostMessageStream.cjs +26 -19
  25. package/dist/services/ProxyPostMessageStream.cjs.map +1 -1
  26. package/dist/services/ProxyPostMessageStream.mjs +26 -19
  27. package/dist/services/ProxyPostMessageStream.mjs.map +1 -1
  28. package/dist/services/iframe/IframeExecutionService.cjs +4 -2
  29. package/dist/services/iframe/IframeExecutionService.cjs.map +1 -1
  30. package/dist/services/iframe/IframeExecutionService.d.cts.map +1 -1
  31. package/dist/services/iframe/IframeExecutionService.d.mts.map +1 -1
  32. package/dist/services/iframe/IframeExecutionService.mjs +4 -2
  33. package/dist/services/iframe/IframeExecutionService.mjs.map +1 -1
  34. package/dist/services/offscreen/OffscreenExecutionService.cjs +16 -3
  35. package/dist/services/offscreen/OffscreenExecutionService.cjs.map +1 -1
  36. package/dist/services/offscreen/OffscreenExecutionService.mjs +16 -3
  37. package/dist/services/offscreen/OffscreenExecutionService.mjs.map +1 -1
  38. package/dist/services/proxy/ProxyExecutionService.cjs +17 -4
  39. package/dist/services/proxy/ProxyExecutionService.cjs.map +1 -1
  40. package/dist/services/proxy/ProxyExecutionService.mjs +17 -4
  41. package/dist/services/proxy/ProxyExecutionService.mjs.map +1 -1
  42. package/dist/services/webview/WebViewExecutionService.cjs +23 -9
  43. package/dist/services/webview/WebViewExecutionService.cjs.map +1 -1
  44. package/dist/services/webview/WebViewExecutionService.mjs +23 -9
  45. package/dist/services/webview/WebViewExecutionService.mjs.map +1 -1
  46. package/dist/services/webview/WebViewMessageStream.cjs +25 -12
  47. package/dist/services/webview/WebViewMessageStream.cjs.map +1 -1
  48. package/dist/services/webview/WebViewMessageStream.mjs +25 -12
  49. package/dist/services/webview/WebViewMessageStream.mjs.map +1 -1
  50. package/dist/services/webworker/WebWorkerExecutionService.cjs +27 -10
  51. package/dist/services/webworker/WebWorkerExecutionService.cjs.map +1 -1
  52. package/dist/services/webworker/WebWorkerExecutionService.mjs +27 -10
  53. package/dist/services/webworker/WebWorkerExecutionService.mjs.map +1 -1
  54. package/dist/snaps/RequestQueue.cjs +0 -2
  55. package/dist/snaps/RequestQueue.cjs.map +1 -1
  56. package/dist/snaps/RequestQueue.mjs +0 -2
  57. package/dist/snaps/RequestQueue.mjs.map +1 -1
  58. package/dist/snaps/SnapController.cjs +1001 -1141
  59. package/dist/snaps/SnapController.cjs.map +1 -1
  60. package/dist/snaps/SnapController.mjs +1000 -1140
  61. package/dist/snaps/SnapController.mjs.map +1 -1
  62. package/dist/snaps/Timer.cjs +0 -1
  63. package/dist/snaps/Timer.cjs.map +1 -1
  64. package/dist/snaps/Timer.mjs +0 -1
  65. package/dist/snaps/Timer.mjs.map +1 -1
  66. package/dist/snaps/location/http.cjs +7 -11
  67. package/dist/snaps/location/http.cjs.map +1 -1
  68. package/dist/snaps/location/http.mjs +7 -11
  69. package/dist/snaps/location/http.mjs.map +1 -1
  70. package/dist/snaps/location/local.cjs +17 -4
  71. package/dist/snaps/location/local.cjs.map +1 -1
  72. package/dist/snaps/location/local.mjs +17 -4
  73. package/dist/snaps/location/local.mjs.map +1 -1
  74. package/dist/snaps/location/npm.cjs +37 -25
  75. package/dist/snaps/location/npm.cjs.map +1 -1
  76. package/dist/snaps/location/npm.mjs +37 -25
  77. package/dist/snaps/location/npm.mjs.map +1 -1
  78. package/dist/snaps/registry/json.cjs +173 -172
  79. package/dist/snaps/registry/json.cjs.map +1 -1
  80. package/dist/snaps/registry/json.mjs +172 -171
  81. package/dist/snaps/registry/json.mjs.map +1 -1
  82. package/package.json +5 -5
@@ -1,4 +1,16 @@
1
1
  "use strict";
2
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
3
+ if (kind === "m") throw new TypeError("Private method is not writable");
4
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
5
+ 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");
6
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
7
+ };
8
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
9
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
10
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
11
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
12
+ };
13
+ var _SnapController_instances, _SnapController_closeAllConnections, _SnapController_dynamicPermissions, _SnapController_environmentEndowmentPermissions, _SnapController_excludedPermissions, _SnapController_featureFlags, _SnapController_fetchFunction, _SnapController_idleTimeCheckInterval, _SnapController_maxIdleTime, _SnapController_encryptor, _SnapController_getMnemonic, _SnapController_getFeatureFlags, _SnapController_detectSnapLocation, _SnapController_snapsRuntimeData, _SnapController_rollbackSnapshots, _SnapController_timeoutForLastRequestStatus, _SnapController_statusMachine, _SnapController_preinstalledSnaps, _SnapController_initializeStateMachine, _SnapController_registerMessageHandlers, _SnapController_handlePreinstalledSnaps, _SnapController_pollForLastRequestStatus, _SnapController_blockSnap, _SnapController_unblockSnap, _SnapController_assertIsInstallAllowed, _SnapController_assertCanInstallSnaps, _SnapController_assertCanUsePlatform, _SnapController_stopSnapsLastRequestPastMax, _SnapController_transition, _SnapController_terminateSnap, _SnapController_getSnapEncryptionKey, _SnapController_decryptSnapState, _SnapController_encryptSnapState, _SnapController_handleInitialConnections, _SnapController_addSnapToSubject, _SnapController_removeSnapFromSubjects, _SnapController_revokeAllSnapPermissions, _SnapController_createApproval, _SnapController_updateApproval, _SnapController_resolveAllowlistVersion, _SnapController_add, _SnapController_startSnap, _SnapController_getEndowments, _SnapController_set, _SnapController_validateSnapPermissions, _SnapController_getExecutionTimeout, _SnapController_getRpcRequestHandler, _SnapController_createInterface, _SnapController_assertInterfaceExists, _SnapController_transformSnapRpcRequestResult, _SnapController_assertSnapRpcRequestResult, _SnapController_recordSnapRpcRequestStart, _SnapController_recordSnapRpcRequestFinish, _SnapController_getRollbackSnapshot, _SnapController_createRollbackSnapshot, _SnapController_rollbackSnap, _SnapController_rollbackSnaps, _SnapController_getRuntime, _SnapController_getRuntimeExpect, _SnapController_setupRuntime, _SnapController_calculatePermissionsChange, _SnapController_isSubjectConnectedToSnap, _SnapController_calculateConnectionsChange, _SnapController_updatePermissions, _SnapController_isValidUpdate, _SnapController_callLifecycleHook;
2
14
  Object.defineProperty(exports, "__esModule", { value: true });
3
15
  exports.SnapController = exports.SNAP_APPROVAL_RESULT = exports.SNAP_APPROVAL_UPDATE = exports.SNAP_APPROVAL_INSTALL = exports.controllerName = void 0;
4
16
  const base_controller_1 = require("@metamask/base-controller");
@@ -59,25 +71,6 @@ const name = 'SnapController';
59
71
  * - Start: Initializes the snap in its SES realm with the authorized permissions.
60
72
  */
61
73
  class SnapController extends base_controller_1.BaseController {
62
- #closeAllConnections;
63
- #dynamicPermissions;
64
- #environmentEndowmentPermissions;
65
- #excludedPermissions;
66
- #featureFlags;
67
- #fetchFunction;
68
- #idleTimeCheckInterval;
69
- #maxIdleTime;
70
- // This property cannot be hash private yet because of tests.
71
- maxRequestTime;
72
- #encryptor;
73
- #getMnemonic;
74
- #getFeatureFlags;
75
- #detectSnapLocation;
76
- #snapsRuntimeData;
77
- #rollbackSnapshots;
78
- #timeoutForLastRequestStatus;
79
- #statusMachine;
80
- #preinstalledSnaps;
81
74
  constructor({ closeAllConnections, messenger, state, dynamicPermissions = ['eth_accounts'], 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(undefined), featureFlags = {}, detectSnapLocation: detectSnapLocationFunction = location_1.detectSnapLocation, preinstalledSnaps = null, encryptor, getMnemonic, getFeatureFlags = () => ({}), }) {
82
75
  super({
83
76
  messenger,
@@ -117,202 +110,65 @@ class SnapController extends base_controller_1.BaseController {
117
110
  ...state,
118
111
  },
119
112
  });
120
- this.#closeAllConnections = closeAllConnections;
121
- this.#dynamicPermissions = dynamicPermissions;
122
- this.#environmentEndowmentPermissions = environmentEndowmentPermissions;
123
- this.#excludedPermissions = excludedPermissions;
124
- this.#featureFlags = featureFlags;
125
- this.#fetchFunction = fetchFunction;
126
- this.#idleTimeCheckInterval = idleTimeCheckInterval;
127
- this.#maxIdleTime = maxIdleTime;
113
+ _SnapController_instances.add(this);
114
+ _SnapController_closeAllConnections.set(this, void 0);
115
+ _SnapController_dynamicPermissions.set(this, void 0);
116
+ _SnapController_environmentEndowmentPermissions.set(this, void 0);
117
+ _SnapController_excludedPermissions.set(this, void 0);
118
+ _SnapController_featureFlags.set(this, void 0);
119
+ _SnapController_fetchFunction.set(this, void 0);
120
+ _SnapController_idleTimeCheckInterval.set(this, void 0);
121
+ _SnapController_maxIdleTime.set(this, void 0);
122
+ _SnapController_encryptor.set(this, void 0);
123
+ _SnapController_getMnemonic.set(this, void 0);
124
+ _SnapController_getFeatureFlags.set(this, void 0);
125
+ _SnapController_detectSnapLocation.set(this, void 0);
126
+ _SnapController_snapsRuntimeData.set(this, void 0);
127
+ _SnapController_rollbackSnapshots.set(this, void 0);
128
+ _SnapController_timeoutForLastRequestStatus.set(this, void 0);
129
+ _SnapController_statusMachine.set(this, void 0);
130
+ _SnapController_preinstalledSnaps.set(this, void 0);
131
+ __classPrivateFieldSet(this, _SnapController_closeAllConnections, closeAllConnections, "f");
132
+ __classPrivateFieldSet(this, _SnapController_dynamicPermissions, dynamicPermissions, "f");
133
+ __classPrivateFieldSet(this, _SnapController_environmentEndowmentPermissions, environmentEndowmentPermissions, "f");
134
+ __classPrivateFieldSet(this, _SnapController_excludedPermissions, excludedPermissions, "f");
135
+ __classPrivateFieldSet(this, _SnapController_featureFlags, featureFlags, "f");
136
+ __classPrivateFieldSet(this, _SnapController_fetchFunction, fetchFunction, "f");
137
+ __classPrivateFieldSet(this, _SnapController_idleTimeCheckInterval, idleTimeCheckInterval, "f");
138
+ __classPrivateFieldSet(this, _SnapController_maxIdleTime, maxIdleTime, "f");
128
139
  this.maxRequestTime = maxRequestTime;
129
- this.#detectSnapLocation = detectSnapLocationFunction;
130
- this.#encryptor = encryptor;
131
- this.#getMnemonic = getMnemonic;
132
- this.#getFeatureFlags = getFeatureFlags;
133
- this.#preinstalledSnaps = preinstalledSnaps;
140
+ __classPrivateFieldSet(this, _SnapController_detectSnapLocation, detectSnapLocationFunction, "f");
141
+ __classPrivateFieldSet(this, _SnapController_encryptor, encryptor, "f");
142
+ __classPrivateFieldSet(this, _SnapController_getMnemonic, getMnemonic, "f");
143
+ __classPrivateFieldSet(this, _SnapController_getFeatureFlags, getFeatureFlags, "f");
144
+ __classPrivateFieldSet(this, _SnapController_preinstalledSnaps, preinstalledSnaps, "f");
134
145
  this._onUnhandledSnapError = this._onUnhandledSnapError.bind(this);
135
146
  this._onOutboundRequest = this._onOutboundRequest.bind(this);
136
147
  this._onOutboundResponse = this._onOutboundResponse.bind(this);
137
- this.#rollbackSnapshots = new Map();
138
- this.#snapsRuntimeData = new Map();
139
- this.#pollForLastRequestStatus();
148
+ __classPrivateFieldSet(this, _SnapController_rollbackSnapshots, new Map(), "f");
149
+ __classPrivateFieldSet(this, _SnapController_snapsRuntimeData, new Map(), "f");
150
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_pollForLastRequestStatus).call(this);
140
151
  /* eslint-disable @typescript-eslint/unbound-method */
141
152
  this.messagingSystem.subscribe('ExecutionService:unhandledError', this._onUnhandledSnapError);
142
153
  this.messagingSystem.subscribe('ExecutionService:outboundRequest', this._onOutboundRequest);
143
154
  this.messagingSystem.subscribe('ExecutionService:outboundResponse', this._onOutboundResponse);
144
155
  /* eslint-enable @typescript-eslint/unbound-method */
145
156
  this.messagingSystem.subscribe('SnapController:snapInstalled', ({ id }, origin) => {
146
- this.#callLifecycleHook(origin, id, snaps_utils_1.HandlerType.OnInstall).catch((error) => {
157
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_callLifecycleHook).call(this, origin, id, snaps_utils_1.HandlerType.OnInstall).catch((error) => {
147
158
  (0, snaps_utils_1.logError)(`Error when calling \`onInstall\` lifecycle hook for snap "${id}": ${(0, snaps_sdk_1.getErrorMessage)(error)}`);
148
159
  });
149
160
  });
150
161
  this.messagingSystem.subscribe('SnapController:snapUpdated', ({ id }, _oldVersion, origin) => {
151
- this.#callLifecycleHook(origin, id, snaps_utils_1.HandlerType.OnUpdate).catch((error) => {
162
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_callLifecycleHook).call(this, origin, id, snaps_utils_1.HandlerType.OnUpdate).catch((error) => {
152
163
  (0, snaps_utils_1.logError)(`Error when calling \`onUpdate\` lifecycle hook for snap "${id}": ${(0, snaps_sdk_1.getErrorMessage)(error)}`);
153
164
  });
154
165
  });
155
- this.#initializeStateMachine();
156
- this.#registerMessageHandlers();
157
- if (this.#preinstalledSnaps) {
158
- this.#handlePreinstalledSnaps(this.#preinstalledSnaps);
159
- }
160
- Object.values(this.state?.snaps ?? {}).forEach((snap) => this.#setupRuntime(snap.id));
161
- }
162
- /**
163
- * We track status of a Snap using a finite-state-machine.
164
- * It keeps track of whether the snap is started / stopped / etc.
165
- *
166
- * @see {@link SnapController.transition} for interacting with the machine.
167
- */
168
- // We initialize the machine in the instance because the status is currently tightly coupled
169
- // with the SnapController - the guard checks for enabled status inside the SnapController state.
170
- // In the future, side-effects could be added to the machine during transitions.
171
- #initializeStateMachine() {
172
- const disableGuard = ({ snapId }) => {
173
- return this.getExpect(snapId).enabled;
174
- };
175
- const statusConfig = {
176
- initial: snaps_utils_1.SnapStatus.Installing,
177
- states: {
178
- [snaps_utils_1.SnapStatus.Installing]: {
179
- on: {
180
- [snaps_utils_1.SnapStatusEvents.Start]: {
181
- target: snaps_utils_1.SnapStatus.Running,
182
- cond: disableGuard,
183
- },
184
- },
185
- },
186
- [snaps_utils_1.SnapStatus.Updating]: {
187
- on: {
188
- [snaps_utils_1.SnapStatusEvents.Start]: {
189
- target: snaps_utils_1.SnapStatus.Running,
190
- cond: disableGuard,
191
- },
192
- [snaps_utils_1.SnapStatusEvents.Stop]: snaps_utils_1.SnapStatus.Stopped,
193
- },
194
- },
195
- [snaps_utils_1.SnapStatus.Running]: {
196
- on: {
197
- [snaps_utils_1.SnapStatusEvents.Stop]: snaps_utils_1.SnapStatus.Stopped,
198
- [snaps_utils_1.SnapStatusEvents.Crash]: snaps_utils_1.SnapStatus.Crashed,
199
- },
200
- },
201
- [snaps_utils_1.SnapStatus.Stopped]: {
202
- on: {
203
- [snaps_utils_1.SnapStatusEvents.Start]: {
204
- target: snaps_utils_1.SnapStatus.Running,
205
- cond: disableGuard,
206
- },
207
- [snaps_utils_1.SnapStatusEvents.Update]: snaps_utils_1.SnapStatus.Updating,
208
- },
209
- },
210
- [snaps_utils_1.SnapStatus.Crashed]: {
211
- on: {
212
- [snaps_utils_1.SnapStatusEvents.Start]: {
213
- target: snaps_utils_1.SnapStatus.Running,
214
- cond: disableGuard,
215
- },
216
- [snaps_utils_1.SnapStatusEvents.Update]: snaps_utils_1.SnapStatus.Updating,
217
- },
218
- },
219
- },
220
- };
221
- this.#statusMachine = (0, fsm_1.createMachine)(statusConfig);
222
- (0, fsm_2.validateMachine)(this.#statusMachine);
223
- }
224
- /**
225
- * Constructor helper for registering the controller's messaging system
226
- * actions.
227
- */
228
- #registerMessageHandlers() {
229
- this.messagingSystem.registerActionHandler(`${exports.controllerName}:clearSnapState`, (...args) => this.clearSnapState(...args));
230
- this.messagingSystem.registerActionHandler(`${exports.controllerName}:get`, (...args) => this.get(...args));
231
- this.messagingSystem.registerActionHandler(`${exports.controllerName}:getSnapState`, async (...args) => this.getSnapState(...args));
232
- this.messagingSystem.registerActionHandler(`${exports.controllerName}:handleRequest`, async (...args) => this.handleRequest(...args));
233
- this.messagingSystem.registerActionHandler(`${exports.controllerName}:has`, (...args) => this.has(...args));
234
- this.messagingSystem.registerActionHandler(`${exports.controllerName}:updateBlockedSnaps`, async () => this.updateBlockedSnaps());
235
- this.messagingSystem.registerActionHandler(`${exports.controllerName}:updateSnapState`, async (...args) => this.updateSnapState(...args));
236
- this.messagingSystem.registerActionHandler(`${exports.controllerName}:enable`, (...args) => this.enableSnap(...args));
237
- this.messagingSystem.registerActionHandler(`${exports.controllerName}:disable`, async (...args) => this.disableSnap(...args));
238
- this.messagingSystem.registerActionHandler(`${exports.controllerName}:remove`, async (...args) => this.removeSnap(...args));
239
- this.messagingSystem.registerActionHandler(`${exports.controllerName}:getPermitted`, (...args) => this.getPermittedSnaps(...args));
240
- this.messagingSystem.registerActionHandler(`${exports.controllerName}:install`, async (...args) => this.installSnaps(...args));
241
- this.messagingSystem.registerActionHandler(`${exports.controllerName}:getAll`, (...args) => this.getAllSnaps(...args));
242
- this.messagingSystem.registerActionHandler(`${exports.controllerName}:incrementActiveReferences`, (...args) => this.incrementActiveReferences(...args));
243
- this.messagingSystem.registerActionHandler(`${exports.controllerName}:decrementActiveReferences`, (...args) => this.decrementActiveReferences(...args));
244
- this.messagingSystem.registerActionHandler(`${exports.controllerName}:getRegistryMetadata`, async (...args) => this.getRegistryMetadata(...args));
245
- this.messagingSystem.registerActionHandler(`${exports.controllerName}:disconnectOrigin`, (...args) => this.removeSnapFromSubject(...args));
246
- this.messagingSystem.registerActionHandler(`${exports.controllerName}:revokeDynamicPermissions`, (...args) => this.revokeDynamicSnapPermissions(...args));
247
- this.messagingSystem.registerActionHandler(`${exports.controllerName}:getFile`, async (...args) => this.getSnapFile(...args));
248
- this.messagingSystem.registerActionHandler(`${exports.controllerName}:stopAllSnaps`, async (...args) => this.stopAllSnaps(...args));
249
- }
250
- #handlePreinstalledSnaps(preinstalledSnaps) {
251
- for (const { snapId, manifest, files, removable, hidden, hideSnapBranding, } of preinstalledSnaps) {
252
- const existingSnap = this.get(snapId);
253
- const isAlreadyInstalled = existingSnap !== undefined;
254
- const isUpdate = isAlreadyInstalled && (0, utils_1.gtVersion)(manifest.version, existingSnap.version);
255
- // Disallow downgrades and overwriting non preinstalled snaps
256
- if (isAlreadyInstalled &&
257
- (!isUpdate || existingSnap.preinstalled !== true)) {
258
- continue;
259
- }
260
- const manifestFile = new snaps_utils_1.VirtualFile({
261
- path: snaps_utils_1.NpmSnapFileNames.Manifest,
262
- value: JSON.stringify(manifest),
263
- result: manifest,
264
- });
265
- const virtualFiles = files.map(({ path, value }) => new snaps_utils_1.VirtualFile({ value, path }));
266
- const { filePath, iconPath } = manifest.source.location.npm;
267
- const sourceCode = virtualFiles.find((file) => file.path === filePath);
268
- const svgIcon = iconPath
269
- ? virtualFiles.find((file) => file.path === iconPath)
270
- : undefined;
271
- (0, utils_1.assert)(sourceCode, 'Source code not provided for preinstalled snap.');
272
- (0, utils_1.assert)(!iconPath || (iconPath && svgIcon), 'Icon not provided for preinstalled snap.');
273
- (0, utils_1.assert)(manifest.source.files === undefined, 'Auxiliary files are not currently supported for preinstalled snaps.');
274
- const localizationFiles = manifest.source.locales?.map((path) => virtualFiles.find((file) => file.path === path)) ?? [];
275
- const validatedLocalizationFiles = (0, snaps_utils_1.getValidatedLocalizationFiles)(localizationFiles.filter(Boolean));
276
- (0, utils_1.assert)(localizationFiles.length === validatedLocalizationFiles.length, 'Missing localization files for preinstalled snap.');
277
- const filesObject = {
278
- manifest: manifestFile,
279
- sourceCode,
280
- svgIcon,
281
- auxiliaryFiles: [],
282
- localizationFiles: validatedLocalizationFiles,
283
- };
284
- // Add snap to the SnapController state
285
- this.#set({
286
- id: snapId,
287
- origin: 'metamask',
288
- files: filesObject,
289
- removable,
290
- hidden,
291
- hideSnapBranding,
292
- preinstalled: true,
293
- });
294
- // Setup permissions
295
- const processedPermissions = (0, snaps_rpc_methods_1.processSnapPermissions)(manifest.initialPermissions);
296
- this.#validateSnapPermissions(processedPermissions);
297
- const { newPermissions, unusedPermissions } = this.#calculatePermissionsChange(snapId, processedPermissions);
298
- this.#updatePermissions({ snapId, newPermissions, unusedPermissions });
299
- if (manifest.initialConnections) {
300
- this.#handleInitialConnections(snapId, existingSnap?.initialConnections ?? null, manifest.initialConnections);
301
- }
302
- // Set status
303
- this.update((state) => {
304
- state.snaps[snapId].status = snaps_utils_1.SnapStatus.Stopped;
305
- });
166
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_initializeStateMachine).call(this);
167
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_registerMessageHandlers).call(this);
168
+ if (__classPrivateFieldGet(this, _SnapController_preinstalledSnaps, "f")) {
169
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_handlePreinstalledSnaps).call(this, __classPrivateFieldGet(this, _SnapController_preinstalledSnaps, "f"));
306
170
  }
307
- }
308
- #pollForLastRequestStatus() {
309
- this.#timeoutForLastRequestStatus = setTimeout(() => {
310
- this.#stopSnapsLastRequestPastMax().catch((error) => {
311
- // TODO: Decide how to handle errors.
312
- (0, snaps_utils_1.logError)(error);
313
- });
314
- this.#pollForLastRequestStatus();
315
- }, this.#idleTimeCheckInterval);
171
+ Object.values(this.state?.snaps ?? {}).forEach((snap) => __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_setupRuntime).call(this, snap.id));
316
172
  }
317
173
  /**
318
174
  * Checks all installed snaps against the block list and
@@ -320,7 +176,7 @@ class SnapController extends base_controller_1.BaseController {
320
176
  * for more information.
321
177
  */
322
178
  async updateBlockedSnaps() {
323
- this.#assertCanUsePlatform();
179
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_assertCanUsePlatform).call(this);
324
180
  await this.messagingSystem.call('SnapsRegistry:update');
325
181
  const blockedSnaps = await this.messagingSystem.call('SnapsRegistry:get', Object.values(this.state.snaps).reduce((blockListArg, snap) => {
326
182
  blockListArg[snap.id] = {
@@ -331,91 +187,11 @@ class SnapController extends base_controller_1.BaseController {
331
187
  }, {}));
332
188
  await Promise.all(Object.entries(blockedSnaps).map(async ([snapId, { status, reason }]) => {
333
189
  if (status === registry_1.SnapsRegistryStatus.Blocked) {
334
- return this.#blockSnap(snapId, reason);
190
+ return __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_blockSnap).call(this, snapId, reason);
335
191
  }
336
- return this.#unblockSnap(snapId);
192
+ return __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_unblockSnap).call(this, snapId);
337
193
  }));
338
194
  }
339
- /**
340
- * Blocks an installed snap and prevents it from being started again. Emits
341
- * {@link SnapBlocked}. Does nothing if the snap is not installed.
342
- *
343
- * @param snapId - The snap to block.
344
- * @param blockedSnapInfo - Information detailing why the snap is blocked.
345
- */
346
- async #blockSnap(snapId, blockedSnapInfo) {
347
- if (!this.has(snapId)) {
348
- return;
349
- }
350
- try {
351
- this.update((state) => {
352
- state.snaps[snapId].blocked = true;
353
- state.snaps[snapId].blockInformation = blockedSnapInfo;
354
- });
355
- await this.disableSnap(snapId);
356
- }
357
- catch (error) {
358
- (0, snaps_utils_1.logError)(`Encountered error when stopping blocked snap "${snapId}".`, error);
359
- }
360
- this.messagingSystem.publish(`${exports.controllerName}:snapBlocked`, snapId, blockedSnapInfo);
361
- }
362
- /**
363
- * Unblocks a snap so that it can be enabled and started again. Emits
364
- * {@link SnapUnblocked}. Does nothing if the snap is not installed or already
365
- * unblocked.
366
- *
367
- * @param snapId - The id of the snap to unblock.
368
- */
369
- #unblockSnap(snapId) {
370
- if (!this.has(snapId) || !this.state.snaps[snapId].blocked) {
371
- return;
372
- }
373
- this.update((state) => {
374
- state.snaps[snapId].blocked = false;
375
- delete state.snaps[snapId].blockInformation;
376
- });
377
- this.messagingSystem.publish(`${exports.controllerName}:snapUnblocked`, snapId);
378
- }
379
- async #assertIsInstallAllowed(snapId, snapInfo) {
380
- const results = await this.messagingSystem.call('SnapsRegistry:get', {
381
- [snapId]: snapInfo,
382
- });
383
- const result = results[snapId];
384
- if (result.status === registry_1.SnapsRegistryStatus.Blocked) {
385
- throw new Error(`Cannot install version "${snapInfo.version}" of snap "${snapId}": The version is blocked. ${result.reason?.explanation ?? ''}`);
386
- }
387
- const isAllowlistingRequired = Object.keys(snapInfo.permissions).some((permission) => !constants_1.ALLOWED_PERMISSIONS.includes(permission));
388
- if (this.#featureFlags.requireAllowlist &&
389
- isAllowlistingRequired &&
390
- result.status !== registry_1.SnapsRegistryStatus.Verified) {
391
- throw new Error(`Cannot install version "${snapInfo.version}" of snap "${snapId}": ${result.status === registry_1.SnapsRegistryStatus.Unavailable
392
- ? 'The registry is temporarily unavailable.'
393
- : 'The snap is not on the allowlist.'}`);
394
- }
395
- }
396
- /**
397
- * Asserts whether new Snaps are allowed to be installed.
398
- */
399
- #assertCanInstallSnaps() {
400
- (0, utils_1.assert)(this.#featureFlags.disableSnapInstallation !== true, 'Installing Snaps is currently disabled in this version of MetaMask.');
401
- }
402
- /**
403
- * Asserts whether the Snaps platform is allowed to run.
404
- */
405
- #assertCanUsePlatform() {
406
- const flags = this.#getFeatureFlags();
407
- (0, utils_1.assert)(flags.disableSnaps !== true, 'The Snaps platform requires basic functionality to be used. Enable basic functionality in the settings to use the Snaps platform.');
408
- }
409
- async #stopSnapsLastRequestPastMax() {
410
- const entries = [...this.#snapsRuntimeData.entries()];
411
- return Promise.all(entries
412
- .filter(([_snapId, runtime]) => runtime.activeReferences === 0 &&
413
- runtime.pendingInboundRequests.length === 0 &&
414
- runtime.lastRequest &&
415
- this.#maxIdleTime &&
416
- (0, utils_1.timeSince)(runtime.lastRequest) > this.#maxIdleTime)
417
- .map(async ([snapId]) => this.stopSnap(snapId, snaps_utils_1.SnapStatusEvents.Stop)));
418
- }
419
195
  _onUnhandledSnapError(snapId, _error) {
420
196
  this.stopSnap(snapId, snaps_utils_1.SnapStatusEvents.Crash).catch((stopSnapError) => {
421
197
  // TODO: Decide how to handle errors.
@@ -423,7 +199,7 @@ class SnapController extends base_controller_1.BaseController {
423
199
  });
424
200
  }
425
201
  _onOutboundRequest(snapId) {
426
- const runtime = this.#getRuntimeExpect(snapId);
202
+ const runtime = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRuntimeExpect).call(this, snapId);
427
203
  // Ideally we would only pause the pending request that is making the outbound request
428
204
  // but right now we don't have a way to know which request initiated the outbound request
429
205
  runtime.pendingInboundRequests
@@ -432,7 +208,7 @@ class SnapController extends base_controller_1.BaseController {
432
208
  runtime.pendingOutboundRequests += 1;
433
209
  }
434
210
  _onOutboundResponse(snapId) {
435
- const runtime = this.#getRuntimeExpect(snapId);
211
+ const runtime = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRuntimeExpect).call(this, snapId);
436
212
  runtime.pendingOutboundRequests -= 1;
437
213
  if (runtime.pendingOutboundRequests === 0) {
438
214
  runtime.pendingInboundRequests
@@ -440,25 +216,6 @@ class SnapController extends base_controller_1.BaseController {
440
216
  .forEach((pendingRequest) => pendingRequest.timer.resume());
441
217
  }
442
218
  }
443
- /**
444
- * Transitions between states using `snapStatusStateMachineConfig` as the template to figure out
445
- * the next state. This transition function uses a very minimal subset of XState conventions:
446
- * - supports initial state
447
- * - .on supports raw event target string
448
- * - .on supports {target, cond} object
449
- * - the arguments for `cond` is the `SerializedSnap` instead of Xstate convention of `(event,
450
- * context) => boolean`
451
- *
452
- * @param snapId - The id of the snap to transition.
453
- * @param event - The event enum to use to transition.
454
- */
455
- #transition(snapId, event) {
456
- const { interpreter } = this.#getRuntimeExpect(snapId);
457
- interpreter.send(event);
458
- this.update((state) => {
459
- state.snaps[snapId].status = interpreter.state.value;
460
- });
461
- }
462
219
  /**
463
220
  * Starts the given snap. Throws an error if no such snap exists
464
221
  * or if it is already running.
@@ -466,12 +223,12 @@ class SnapController extends base_controller_1.BaseController {
466
223
  * @param snapId - The id of the Snap to start.
467
224
  */
468
225
  async startSnap(snapId) {
469
- this.#assertCanUsePlatform();
226
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_assertCanUsePlatform).call(this);
470
227
  const snap = this.state.snaps[snapId];
471
228
  if (snap.enabled === false) {
472
229
  throw new Error(`Snap "${snapId}" is disabled.`);
473
230
  }
474
- await this.#startSnap({
231
+ await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_startSnap).call(this, {
475
232
  snapId,
476
233
  sourceCode: snap.sourceCode,
477
234
  });
@@ -519,7 +276,7 @@ class SnapController extends base_controller_1.BaseController {
519
276
  * stopped.
520
277
  */
521
278
  async stopSnap(snapId, statusEvent = snaps_utils_1.SnapStatusEvents.Stop) {
522
- const runtime = this.#getRuntime(snapId);
279
+ const runtime = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRuntime).call(this, snapId);
523
280
  if (!runtime) {
524
281
  throw new Error(`The snap "${snapId}" is not running.`);
525
282
  }
@@ -532,8 +289,8 @@ class SnapController extends base_controller_1.BaseController {
532
289
  runtime.stopping = true;
533
290
  try {
534
291
  if (this.isRunning(snapId)) {
535
- this.#closeAllConnections?.(snapId);
536
- await this.#terminateSnap(snapId);
292
+ __classPrivateFieldGet(this, _SnapController_closeAllConnections, "f")?.call(this, snapId);
293
+ await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_terminateSnap).call(this, snapId);
537
294
  }
538
295
  }
539
296
  finally {
@@ -543,7 +300,7 @@ class SnapController extends base_controller_1.BaseController {
543
300
  runtime.pendingOutboundRequests = 0;
544
301
  runtime.stopping = false;
545
302
  if (this.isRunning(snapId)) {
546
- this.#transition(snapId, statusEvent);
303
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_transition).call(this, snapId, statusEvent);
547
304
  }
548
305
  }
549
306
  }
@@ -559,24 +316,6 @@ class SnapController extends base_controller_1.BaseController {
559
316
  const promises = snaps.map(async (snap) => this.stopSnap(snap.id, statusEvent));
560
317
  await Promise.allSettled(promises);
561
318
  }
562
- /**
563
- * Terminates the specified snap and emits the `snapTerminated` event.
564
- *
565
- * @param snapId - The snap to terminate.
566
- */
567
- async #terminateSnap(snapId) {
568
- await this.messagingSystem.call('ExecutionService:terminateSnap', snapId);
569
- // Hack to give up execution for a bit to let gracefully terminating Snaps return.
570
- await new Promise((resolve) => setTimeout(resolve, 1));
571
- const runtime = this.#getRuntimeExpect(snapId);
572
- // Unresponsive requests may still be timed, time them out.
573
- runtime.pendingInboundRequests
574
- .filter((pendingRequest) => pendingRequest.timer.status !== 'finished')
575
- .forEach((pendingRequest) => pendingRequest.timer.finish());
576
- // Hack to give up execution for a bit to let timed out requests return.
577
- await new Promise((resolve) => setTimeout(resolve, 1));
578
- this.messagingSystem.publish('SnapController:snapTerminated', this.getTruncatedExpect(snapId));
579
- }
580
319
  /**
581
320
  * Returns whether the given snap is running.
582
321
  * Throws an error if the snap doesn't exist.
@@ -644,86 +383,6 @@ class SnapController extends base_controller_1.BaseController {
644
383
  getTruncatedExpect(snapId) {
645
384
  return truncateSnap(this.getExpect(snapId));
646
385
  }
647
- /**
648
- * Generate an encryption key to be used for state encryption for a given Snap.
649
- *
650
- * @param options - An options bag.
651
- * @param options.snapId - The Snap ID.
652
- * @param options.salt - A salt to be used for the encryption key.
653
- * @param options.useCache - Whether to use caching or not.
654
- * @param options.keyMetadata - Optional metadata about how to derive the encryption key.
655
- * @returns An encryption key.
656
- */
657
- async #getSnapEncryptionKey({ snapId, salt: passedSalt, useCache, keyMetadata, }) {
658
- const runtime = this.#getRuntimeExpect(snapId);
659
- if (runtime.encryptionKey && runtime.encryptionSalt && useCache) {
660
- return {
661
- key: await this.#encryptor.importKey(runtime.encryptionKey),
662
- salt: runtime.encryptionSalt,
663
- };
664
- }
665
- const salt = passedSalt ?? this.#encryptor.generateSalt();
666
- const mnemonicPhrase = await this.#getMnemonic();
667
- const entropy = await (0, snaps_rpc_methods_1.getEncryptionEntropy)({ snapId, mnemonicPhrase });
668
- const encryptionKey = await this.#encryptor.keyFromPassword(entropy, salt, true, keyMetadata);
669
- const exportedKey = await this.#encryptor.exportKey(encryptionKey);
670
- // Cache exported encryption key in runtime
671
- if (useCache) {
672
- runtime.encryptionKey = exportedKey;
673
- runtime.encryptionSalt = salt;
674
- }
675
- return { key: encryptionKey, salt };
676
- }
677
- /**
678
- * Decrypt the encrypted state for a given Snap.
679
- *
680
- * @param snapId - The Snap ID.
681
- * @param state - The encrypted state as a string.
682
- * @returns A valid JSON object derived from the encrypted state.
683
- * @throws If the decryption fails or the decrypted state is not valid JSON.
684
- */
685
- async #decryptSnapState(snapId, state) {
686
- try {
687
- const parsed = (0, snaps_utils_1.parseJson)(state);
688
- const { salt, keyMetadata } = parsed;
689
- const useCache = this.#encryptor.isVaultUpdated(state);
690
- const { key } = await this.#getSnapEncryptionKey({
691
- snapId,
692
- salt,
693
- useCache,
694
- // When decrypting state we expect key metadata to be present.
695
- // If it isn't present, we assume that the Snap state we are decrypting is old enough to use the legacy encryption params.
696
- keyMetadata: keyMetadata ?? constants_1.LEGACY_ENCRYPTION_KEY_DERIVATION_OPTIONS,
697
- });
698
- const decryptedState = await this.#encryptor.decryptWithKey(key, parsed);
699
- (0, utils_1.assert)((0, utils_1.isValidJson)(decryptedState));
700
- return decryptedState;
701
- }
702
- catch {
703
- throw rpc_errors_1.rpcErrors.internal({
704
- message: 'Failed to decrypt snap state, the state must be corrupted.',
705
- });
706
- }
707
- }
708
- /**
709
- * Encrypt a JSON state object for a given Snap.
710
- *
711
- * Note: This function does not assert the validity of the object,
712
- * please ensure only valid JSON is passed to it.
713
- *
714
- * @param snapId - The Snap ID.
715
- * @param state - The state object.
716
- * @returns A string containing the encrypted JSON object.
717
- */
718
- async #encryptSnapState(snapId, state) {
719
- const { key, salt } = await this.#getSnapEncryptionKey({
720
- snapId,
721
- useCache: true,
722
- });
723
- const encryptedState = await this.#encryptor.encryptWithKey(key, state);
724
- encryptedState.salt = salt;
725
- return JSON.stringify(encryptedState);
726
- }
727
386
  /**
728
387
  * Updates the own state of the snap with the given id.
729
388
  * This is distinct from the state MetaMask uses to manage snaps.
@@ -734,7 +393,7 @@ class SnapController extends base_controller_1.BaseController {
734
393
  */
735
394
  async updateSnapState(snapId, newSnapState, encrypted) {
736
395
  if (encrypted) {
737
- const encryptedState = await this.#encryptSnapState(snapId, newSnapState);
396
+ const encryptedState = await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_encryptSnapState).call(this, snapId, newSnapState);
738
397
  this.update((state) => {
739
398
  state.snapStates[snapId] = encryptedState;
740
399
  });
@@ -780,7 +439,7 @@ class SnapController extends base_controller_1.BaseController {
780
439
  if (!encrypted) {
781
440
  return (0, snaps_utils_1.parseJson)(state);
782
441
  }
783
- const decrypted = await this.#decryptSnapState(snapId, state);
442
+ const decrypted = await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_decryptSnapState).call(this, snapId, state);
784
443
  return decrypted;
785
444
  }
786
445
  /**
@@ -808,22 +467,22 @@ class SnapController extends base_controller_1.BaseController {
808
467
  */
809
468
  async clearState() {
810
469
  const snapIds = Object.keys(this.state.snaps);
811
- if (this.#closeAllConnections) {
470
+ if (__classPrivateFieldGet(this, _SnapController_closeAllConnections, "f")) {
812
471
  snapIds.forEach((snapId) => {
813
- this.#closeAllConnections?.(snapId);
472
+ __classPrivateFieldGet(this, _SnapController_closeAllConnections, "f")?.call(this, snapId);
814
473
  });
815
474
  }
816
475
  await this.messagingSystem.call('ExecutionService:terminateAllSnaps');
817
- snapIds.forEach((snapId) => this.#revokeAllSnapPermissions(snapId));
476
+ snapIds.forEach((snapId) => __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_revokeAllSnapPermissions).call(this, snapId));
818
477
  this.update((state) => {
819
478
  state.snaps = {};
820
479
  state.snapStates = {};
821
480
  });
822
- this.#snapsRuntimeData.clear();
481
+ __classPrivateFieldGet(this, _SnapController_snapsRuntimeData, "f").clear();
823
482
  // We want to remove all snaps & permissions, except for preinstalled snaps
824
- if (this.#preinstalledSnaps) {
825
- this.#handlePreinstalledSnaps(this.#preinstalledSnaps);
826
- Object.values(this.state?.snaps).forEach((snap) => this.#setupRuntime(snap.id));
483
+ if (__classPrivateFieldGet(this, _SnapController_preinstalledSnaps, "f")) {
484
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_handlePreinstalledSnaps).call(this, __classPrivateFieldGet(this, _SnapController_preinstalledSnaps, "f"));
485
+ Object.values(this.state?.snaps).forEach((snap) => __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_setupRuntime).call(this, snap.id));
827
486
  }
828
487
  }
829
488
  /**
@@ -857,9 +516,9 @@ class SnapController extends base_controller_1.BaseController {
857
516
  // it. This ensures that the snap will not be restarted or otherwise
858
517
  // affect the host environment while we are deleting it.
859
518
  await this.disableSnap(snapId);
860
- this.#revokeAllSnapPermissions(snapId);
861
- this.#removeSnapFromSubjects(snapId);
862
- this.#snapsRuntimeData.delete(snapId);
519
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_revokeAllSnapPermissions).call(this, snapId);
520
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_removeSnapFromSubjects).call(this, snapId);
521
+ __classPrivateFieldGet(this, _SnapController_snapsRuntimeData, "f").delete(snapId);
863
522
  this.update((state) => {
864
523
  delete state.snaps[snapId];
865
524
  delete state.snapStates[snapId];
@@ -871,47 +530,6 @@ class SnapController extends base_controller_1.BaseController {
871
530
  }
872
531
  }));
873
532
  }
874
- #handleInitialConnections(snapId, previousInitialConnections, initialConnections) {
875
- if (previousInitialConnections) {
876
- const revokedInitialConnections = (0, utils_2.setDiff)(previousInitialConnections, initialConnections);
877
- for (const origin of Object.keys(revokedInitialConnections)) {
878
- this.removeSnapFromSubject(origin, snapId);
879
- }
880
- }
881
- for (const origin of Object.keys(initialConnections)) {
882
- this.#addSnapToSubject(origin, snapId);
883
- }
884
- }
885
- #addSnapToSubject(origin, snapId) {
886
- const subjectPermissions = this.messagingSystem.call('PermissionController:getPermissions', origin);
887
- const existingCaveat = subjectPermissions?.[snaps_rpc_methods_1.WALLET_SNAP_PERMISSION_KEY]?.caveats?.find((caveat) => caveat.type === snaps_utils_1.SnapCaveatType.SnapIds);
888
- const subjectHasSnap = Boolean(existingCaveat?.value?.[snapId]);
889
- // If the subject is already connected to the snap, this is a no-op.
890
- if (subjectHasSnap) {
891
- return;
892
- }
893
- // If an existing caveat exists, we add the snap to that.
894
- if (existingCaveat) {
895
- this.messagingSystem.call('PermissionController:updateCaveat', origin, snaps_rpc_methods_1.WALLET_SNAP_PERMISSION_KEY, snaps_utils_1.SnapCaveatType.SnapIds, { ...existingCaveat.value, [snapId]: {} });
896
- return;
897
- }
898
- const approvedPermissions = {
899
- [snaps_rpc_methods_1.WALLET_SNAP_PERMISSION_KEY]: {
900
- caveats: [
901
- {
902
- type: snaps_utils_1.SnapCaveatType.SnapIds,
903
- value: {
904
- [snapId]: {},
905
- },
906
- },
907
- ],
908
- },
909
- };
910
- this.messagingSystem.call('PermissionController:grantPermissions', {
911
- approvedPermissions,
912
- subject: { origin },
913
- });
914
- }
915
533
  /**
916
534
  * Removes a snap's permission (caveat) from the specified subject.
917
535
  *
@@ -948,39 +566,18 @@ class SnapController extends base_controller_1.BaseController {
948
566
  * @throws If non-dynamic permissions are passed.
949
567
  */
950
568
  revokeDynamicSnapPermissions(snapId, permissionNames) {
951
- (0, utils_1.assert)(permissionNames.every((permissionName) => this.#dynamicPermissions.includes(permissionName)), 'Non-dynamic permissions cannot be revoked');
569
+ (0, utils_1.assert)(permissionNames.every((permissionName) => __classPrivateFieldGet(this, _SnapController_dynamicPermissions, "f").includes(permissionName)), 'Non-dynamic permissions cannot be revoked');
952
570
  this.messagingSystem.call('PermissionController:revokePermissions', {
953
571
  [snapId]: permissionNames,
954
572
  });
955
573
  }
956
- /**
957
- * Removes a snap's permission (caveat) from all subjects.
958
- *
959
- * @param snapId - The id of the Snap.
960
- */
961
- #removeSnapFromSubjects(snapId) {
962
- const subjects = this.messagingSystem.call('PermissionController:getSubjectNames');
963
- for (const subject of subjects) {
964
- this.removeSnapFromSubject(subject, snapId);
965
- }
966
- }
967
- /**
968
- * Safely revokes all permissions granted to a Snap.
969
- *
970
- * @param snapId - The snap ID.
971
- */
972
- #revokeAllSnapPermissions(snapId) {
973
- if (this.messagingSystem.call('PermissionController:hasPermissions', snapId)) {
974
- this.messagingSystem.call('PermissionController:revokeAllPermissions', snapId);
975
- }
976
- }
977
574
  /**
978
575
  * Handles incrementing the activeReferences counter.
979
576
  *
980
577
  * @param snapId - The snap id of the snap that was referenced.
981
578
  */
982
579
  incrementActiveReferences(snapId) {
983
- const runtime = this.#getRuntimeExpect(snapId);
580
+ const runtime = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRuntimeExpect).call(this, snapId);
984
581
  runtime.activeReferences += 1;
985
582
  }
986
583
  /**
@@ -989,7 +586,7 @@ class SnapController extends base_controller_1.BaseController {
989
586
  * @param snapId - The snap id of the snap that was referenced..
990
587
  */
991
588
  decrementActiveReferences(snapId) {
992
- const runtime = this.#getRuntimeExpect(snapId);
589
+ const runtime = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRuntimeExpect).call(this, snapId);
993
590
  (0, utils_1.assert)(runtime.activeReferences > 0, 'SnapController reference management is in an invalid state.');
994
591
  runtime.activeReferences -= 1;
995
592
  }
@@ -1030,7 +627,7 @@ class SnapController extends base_controller_1.BaseController {
1030
627
  * snap couldn't be installed.
1031
628
  */
1032
629
  async installSnaps(origin, requestedSnaps) {
1033
- this.#assertCanUsePlatform();
630
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_assertCanUsePlatform).call(this);
1034
631
  const result = {};
1035
632
  const snapIds = Object.keys(requestedSnaps);
1036
633
  const pendingUpdates = [];
@@ -1042,23 +639,23 @@ class SnapController extends base_controller_1.BaseController {
1042
639
  if (error) {
1043
640
  throw rpc_errors_1.rpcErrors.invalidParams(`The "version" field must be a valid SemVer version range if specified. Received: "${rawVersion}".`);
1044
641
  }
1045
- const location = this.#detectSnapLocation(snapId, {
642
+ const location = __classPrivateFieldGet(this, _SnapController_detectSnapLocation, "f").call(this, snapId, {
1046
643
  versionRange: version,
1047
- fetch: this.#fetchFunction,
1048
- allowLocal: this.#featureFlags.allowLocalSnaps,
1049
- resolveVersion: async (range) => this.#featureFlags.requireAllowlist
1050
- ? await this.#resolveAllowlistVersion(snapId, range)
644
+ fetch: __classPrivateFieldGet(this, _SnapController_fetchFunction, "f"),
645
+ allowLocal: __classPrivateFieldGet(this, _SnapController_featureFlags, "f").allowLocalSnaps,
646
+ resolveVersion: async (range) => __classPrivateFieldGet(this, _SnapController_featureFlags, "f").requireAllowlist
647
+ ? await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_resolveAllowlistVersion).call(this, snapId, range)
1051
648
  : range,
1052
649
  });
1053
650
  // Existing snaps may need to be updated, unless they should be re-installed (e.g. local snaps)
1054
651
  // Everything else is treated as an install
1055
652
  const isUpdate = this.has(snapId) && !location.shouldAlwaysReload;
1056
- if (isUpdate && this.#isValidUpdate(snapId, version)) {
653
+ if (isUpdate && __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_isValidUpdate).call(this, snapId, version)) {
1057
654
  const existingSnap = this.getExpect(snapId);
1058
655
  pendingUpdates.push({ snapId, oldVersion: existingSnap.version });
1059
- let rollbackSnapshot = this.#getRollbackSnapshot(snapId);
656
+ let rollbackSnapshot = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRollbackSnapshot).call(this, snapId);
1060
657
  if (rollbackSnapshot === undefined) {
1061
- rollbackSnapshot = this.#createRollbackSnapshot(snapId);
658
+ rollbackSnapshot = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_createRollbackSnapshot).call(this, snapId);
1062
659
  rollbackSnapshot.newVersion = version;
1063
660
  }
1064
661
  else {
@@ -1073,16 +670,16 @@ class SnapController extends base_controller_1.BaseController {
1073
670
  // Once we finish all installs / updates, emit events.
1074
671
  pendingInstalls.forEach((snapId) => this.messagingSystem.publish(`SnapController:snapInstalled`, this.getTruncatedExpect(snapId), origin));
1075
672
  pendingUpdates.forEach(({ snapId, oldVersion }) => this.messagingSystem.publish(`SnapController:snapUpdated`, this.getTruncatedExpect(snapId), oldVersion, origin));
1076
- snapIds.forEach((snapId) => this.#rollbackSnapshots.delete(snapId));
673
+ snapIds.forEach((snapId) => __classPrivateFieldGet(this, _SnapController_rollbackSnapshots, "f").delete(snapId));
1077
674
  }
1078
675
  catch (error) {
1079
676
  const installed = pendingInstalls.filter((snapId) => this.has(snapId));
1080
677
  await this.removeSnaps(installed);
1081
- const snapshottedSnaps = [...this.#rollbackSnapshots.keys()];
678
+ const snapshottedSnaps = [...__classPrivateFieldGet(this, _SnapController_rollbackSnapshots, "f").keys()];
1082
679
  const snapsToRollback = pendingUpdates
1083
680
  .map(({ snapId }) => snapId)
1084
681
  .filter((snapId) => snapshottedSnaps.includes(snapId));
1085
- await this.#rollbackSnaps(snapsToRollback);
682
+ await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_rollbackSnaps).call(this, snapsToRollback);
1086
683
  throw error;
1087
684
  }
1088
685
  return result;
@@ -1112,8 +709,8 @@ class SnapController extends base_controller_1.BaseController {
1112
709
  // and we don't want to emit events prematurely.
1113
710
  false);
1114
711
  }
1115
- this.#assertCanInstallSnaps();
1116
- let pendingApproval = this.#createApproval({
712
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_assertCanInstallSnaps).call(this);
713
+ let pendingApproval = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_createApproval).call(this, {
1117
714
  origin,
1118
715
  snapId,
1119
716
  type: exports.SNAP_APPROVAL_INSTALL,
@@ -1125,27 +722,27 @@ class SnapController extends base_controller_1.BaseController {
1125
722
  }
1126
723
  // Existing snaps that should be re-installed should not maintain their existing permissions
1127
724
  if (existingSnap && location.shouldAlwaysReload) {
1128
- this.#revokeAllSnapPermissions(snapId);
725
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_revokeAllSnapPermissions).call(this, snapId);
1129
726
  }
1130
727
  try {
1131
- const { sourceCode } = await this.#add({
728
+ const { sourceCode } = await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_add).call(this, {
1132
729
  origin,
1133
730
  id: snapId,
1134
731
  location,
1135
732
  versionRange,
1136
733
  });
1137
734
  await this.authorize(snapId, pendingApproval);
1138
- pendingApproval = this.#createApproval({
735
+ pendingApproval = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_createApproval).call(this, {
1139
736
  origin,
1140
737
  snapId,
1141
738
  type: exports.SNAP_APPROVAL_RESULT,
1142
739
  });
1143
- await this.#startSnap({
740
+ await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_startSnap).call(this, {
1144
741
  snapId,
1145
742
  sourceCode,
1146
743
  });
1147
744
  const truncated = this.getTruncatedExpect(snapId);
1148
- this.#updateApproval(pendingApproval.id, {
745
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_updateApproval).call(this, pendingApproval.id, {
1149
746
  loading: false,
1150
747
  type: exports.SNAP_APPROVAL_INSTALL,
1151
748
  });
@@ -1154,7 +751,7 @@ class SnapController extends base_controller_1.BaseController {
1154
751
  catch (error) {
1155
752
  (0, snaps_utils_1.logError)(`Error when adding ${snapId}.`, error);
1156
753
  const errorString = error instanceof Error ? error.message : error.toString();
1157
- this.#updateApproval(pendingApproval.id, {
754
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_updateApproval).call(this, pendingApproval.id, {
1158
755
  loading: false,
1159
756
  type: exports.SNAP_APPROVAL_INSTALL,
1160
757
  error: errorString,
@@ -1163,34 +760,6 @@ class SnapController extends base_controller_1.BaseController {
1163
760
  throw error;
1164
761
  }
1165
762
  }
1166
- #createApproval({ origin, snapId, type, }) {
1167
- const id = (0, nanoid_1.nanoid)();
1168
- const promise = this.messagingSystem.call('ApprovalController:addRequest', {
1169
- origin,
1170
- id,
1171
- type,
1172
- requestData: {
1173
- // Mirror previous installation metadata
1174
- metadata: { id, origin: snapId, dappOrigin: origin },
1175
- snapId,
1176
- },
1177
- requestState: {
1178
- loading: true,
1179
- },
1180
- }, true);
1181
- return { id, promise };
1182
- }
1183
- #updateApproval(id, requestState) {
1184
- try {
1185
- this.messagingSystem.call('ApprovalController:updateRequestState', {
1186
- id,
1187
- requestState,
1188
- });
1189
- }
1190
- catch {
1191
- // Do nothing
1192
- }
1193
- }
1194
763
  /**
1195
764
  * Updates an installed snap. The flow is similar to
1196
765
  * {@link SnapController.installSnaps}. The user will be asked if they want
@@ -1211,12 +780,12 @@ class SnapController extends base_controller_1.BaseController {
1211
780
  * @returns The snap metadata if updated, `null` otherwise.
1212
781
  */
1213
782
  async updateSnap(origin, snapId, location, newVersionRange = snaps_utils_1.DEFAULT_REQUESTED_SNAP_VERSION, emitEvent = true) {
1214
- this.#assertCanInstallSnaps();
1215
- this.#assertCanUsePlatform();
783
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_assertCanInstallSnaps).call(this);
784
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_assertCanUsePlatform).call(this);
1216
785
  if (!(0, utils_1.isValidSemVerRange)(newVersionRange)) {
1217
786
  throw new Error(`Received invalid snap version range: "${newVersionRange}".`);
1218
787
  }
1219
- let pendingApproval = this.#createApproval({
788
+ let pendingApproval = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_createApproval).call(this, {
1220
789
  origin,
1221
790
  snapId,
1222
791
  type: exports.SNAP_APPROVAL_UPDATE,
@@ -1235,16 +804,16 @@ class SnapController extends base_controller_1.BaseController {
1235
804
  if (!(0, utils_1.satisfiesVersionRange)(newVersion, newVersionRange)) {
1236
805
  throw new Error(`Version mismatch. Manifest for "${snapId}" specifies version "${newVersion}" which doesn't satisfy requested version range "${newVersionRange}".`);
1237
806
  }
1238
- await this.#assertIsInstallAllowed(snapId, {
807
+ await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_assertIsInstallAllowed).call(this, snapId, {
1239
808
  version: newVersion,
1240
809
  checksum: manifest.source.shasum,
1241
810
  permissions: manifest.initialPermissions,
1242
811
  });
1243
812
  const processedPermissions = (0, snaps_rpc_methods_1.processSnapPermissions)(manifest.initialPermissions);
1244
- this.#validateSnapPermissions(processedPermissions);
1245
- const { newPermissions, unusedPermissions, approvedPermissions } = this.#calculatePermissionsChange(snapId, processedPermissions);
1246
- const { newConnections, unusedConnections, approvedConnections } = this.#calculateConnectionsChange(snapId, oldManifest.initialConnections ?? {}, manifest.initialConnections ?? {});
1247
- this.#updateApproval(pendingApproval.id, {
813
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_validateSnapPermissions).call(this, processedPermissions);
814
+ const { newPermissions, unusedPermissions, approvedPermissions } = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_calculatePermissionsChange).call(this, snapId, processedPermissions);
815
+ const { newConnections, unusedConnections, approvedConnections } = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_calculateConnectionsChange).call(this, snapId, oldManifest.initialConnections ?? {}, manifest.initialConnections ?? {});
816
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_updateApproval).call(this, pendingApproval.id, {
1248
817
  permissions: newPermissions,
1249
818
  newVersion: manifest.version,
1250
819
  newPermissions,
@@ -1256,7 +825,7 @@ class SnapController extends base_controller_1.BaseController {
1256
825
  loading: false,
1257
826
  });
1258
827
  const { permissions: approvedNewPermissions, ...requestData } = (await pendingApproval.promise);
1259
- pendingApproval = this.#createApproval({
828
+ pendingApproval = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_createApproval).call(this, {
1260
829
  origin,
1261
830
  snapId,
1262
831
  type: exports.SNAP_APPROVAL_RESULT,
@@ -1264,23 +833,23 @@ class SnapController extends base_controller_1.BaseController {
1264
833
  if (this.isRunning(snapId)) {
1265
834
  await this.stopSnap(snapId, snaps_utils_1.SnapStatusEvents.Stop);
1266
835
  }
1267
- this.#transition(snapId, snaps_utils_1.SnapStatusEvents.Update);
1268
- this.#set({
836
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_transition).call(this, snapId, snaps_utils_1.SnapStatusEvents.Update);
837
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_set).call(this, {
1269
838
  origin,
1270
839
  id: snapId,
1271
840
  files: newSnap,
1272
841
  isUpdate: true,
1273
842
  });
1274
- this.#updatePermissions({
843
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_updatePermissions).call(this, {
1275
844
  snapId,
1276
845
  unusedPermissions,
1277
846
  newPermissions: approvedNewPermissions,
1278
847
  requestData,
1279
848
  });
1280
849
  if (manifest.initialConnections) {
1281
- this.#handleInitialConnections(snapId, oldManifest.initialConnections ?? null, manifest.initialConnections);
850
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_handleInitialConnections).call(this, snapId, oldManifest.initialConnections ?? null, manifest.initialConnections);
1282
851
  }
1283
- const rollbackSnapshot = this.#getRollbackSnapshot(snapId);
852
+ const rollbackSnapshot = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRollbackSnapshot).call(this, snapId);
1284
853
  if (rollbackSnapshot !== undefined) {
1285
854
  rollbackSnapshot.permissions.revoked = unusedPermissions;
1286
855
  rollbackSnapshot.permissions.granted = approvedNewPermissions;
@@ -1289,7 +858,7 @@ class SnapController extends base_controller_1.BaseController {
1289
858
  const sourceCode = sourceCodeFile.toString();
1290
859
  (0, utils_1.assert)(typeof sourceCode === 'string' && sourceCode.length > 0, `Invalid source code for snap "${snapId}".`);
1291
860
  try {
1292
- await this.#startSnap({ snapId, sourceCode });
861
+ await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_startSnap).call(this, { snapId, sourceCode });
1293
862
  }
1294
863
  catch {
1295
864
  throw new Error(`Snap ${snapId} crashed with updated source code.`);
@@ -1298,7 +867,7 @@ class SnapController extends base_controller_1.BaseController {
1298
867
  if (emitEvent) {
1299
868
  this.messagingSystem.publish('SnapController:snapUpdated', truncatedSnap, snap.version, origin);
1300
869
  }
1301
- this.#updateApproval(pendingApproval.id, {
870
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_updateApproval).call(this, pendingApproval.id, {
1302
871
  loading: false,
1303
872
  type: exports.SNAP_APPROVAL_UPDATE,
1304
873
  });
@@ -1307,7 +876,7 @@ class SnapController extends base_controller_1.BaseController {
1307
876
  catch (error) {
1308
877
  (0, snaps_utils_1.logError)(`Error when updating ${snapId},`, error);
1309
878
  const errorString = error instanceof Error ? error.message : error.toString();
1310
- this.#updateApproval(pendingApproval.id, {
879
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_updateApproval).call(this, pendingApproval.id, {
1311
880
  loading: false,
1312
881
  error: errorString,
1313
882
  type: exports.SNAP_APPROVAL_UPDATE,
@@ -1316,9 +885,6 @@ class SnapController extends base_controller_1.BaseController {
1316
885
  throw error;
1317
886
  }
1318
887
  }
1319
- async #resolveAllowlistVersion(snapId, versionRange) {
1320
- return await this.messagingSystem.call('SnapsRegistry:resolveVersion', snapId, versionRange);
1321
- }
1322
888
  /**
1323
889
  * Get metadata for the given snap ID.
1324
890
  *
@@ -1327,215 +893,9 @@ class SnapController extends base_controller_1.BaseController {
1327
893
  * verified.
1328
894
  */
1329
895
  async getRegistryMetadata(snapId) {
1330
- this.#assertCanUsePlatform();
896
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_assertCanUsePlatform).call(this);
1331
897
  return await this.messagingSystem.call('SnapsRegistry:getMetadata', snapId);
1332
898
  }
1333
- /**
1334
- * Returns a promise representing the complete installation of the requested snap.
1335
- * If the snap is already being installed, the previously pending promise will be returned.
1336
- *
1337
- * @param args - Object containing the snap id and either the URL of the snap's manifest,
1338
- * or the snap's manifest and source code. The object may also optionally contain a target
1339
- * version.
1340
- * @returns The resulting snap object.
1341
- */
1342
- async #add(args) {
1343
- const { id: snapId, location, versionRange } = args;
1344
- this.#setupRuntime(snapId);
1345
- const runtime = this.#getRuntimeExpect(snapId);
1346
- if (!runtime.installPromise) {
1347
- (0, logging_1.log)(`Adding snap: ${snapId}`);
1348
- // If fetching and setting the snap succeeds, this property will be set
1349
- // to null in the authorize() method.
1350
- runtime.installPromise = (async () => {
1351
- const fetchedSnap = await (0, utils_2.fetchSnap)(snapId, location);
1352
- const manifest = fetchedSnap.manifest.result;
1353
- if (!(0, utils_1.satisfiesVersionRange)(manifest.version, versionRange)) {
1354
- throw new Error(`Version mismatch. Manifest for "${snapId}" specifies version "${manifest.version}" which doesn't satisfy requested version range "${versionRange}".`);
1355
- }
1356
- await this.#assertIsInstallAllowed(snapId, {
1357
- version: manifest.version,
1358
- checksum: manifest.source.shasum,
1359
- permissions: manifest.initialPermissions,
1360
- });
1361
- return this.#set({
1362
- ...args,
1363
- files: fetchedSnap,
1364
- id: snapId,
1365
- });
1366
- })();
1367
- }
1368
- try {
1369
- return await runtime.installPromise;
1370
- }
1371
- catch (error) {
1372
- // Reset promise so users can retry installation in case the problem is
1373
- // temporary.
1374
- runtime.installPromise = null;
1375
- throw error;
1376
- }
1377
- }
1378
- async #startSnap(snapData) {
1379
- const { snapId } = snapData;
1380
- if (this.isRunning(snapId)) {
1381
- throw new Error(`Snap "${snapId}" is already started.`);
1382
- }
1383
- try {
1384
- const runtime = this.#getRuntimeExpect(snapId);
1385
- const result = await this.messagingSystem.call('ExecutionService:executeSnap', {
1386
- ...snapData,
1387
- endowments: await this.#getEndowments(snapId),
1388
- });
1389
- this.#transition(snapId, snaps_utils_1.SnapStatusEvents.Start);
1390
- // We treat the initialization of the snap as the first request, for idle timing purposes.
1391
- runtime.lastRequest = Date.now();
1392
- return result;
1393
- }
1394
- catch (error) {
1395
- await this.#terminateSnap(snapId);
1396
- throw error;
1397
- }
1398
- }
1399
- /**
1400
- * Gets the names of all endowments that will be added to the Snap's
1401
- * Compartment when it executes. These should be the names of global
1402
- * JavaScript APIs accessible in the root realm of the execution environment.
1403
- *
1404
- * Throws an error if the endowment getter for a permission returns a truthy
1405
- * value that is not an array of strings.
1406
- *
1407
- * @param snapId - The id of the snap whose SES endowments to get.
1408
- * @returns An array of the names of the endowments.
1409
- */
1410
- async #getEndowments(snapId) {
1411
- let allEndowments = [];
1412
- for (const permissionName of this.#environmentEndowmentPermissions) {
1413
- if (this.messagingSystem.call('PermissionController:hasPermission', snapId, permissionName)) {
1414
- const endowments = await this.messagingSystem.call('PermissionController:getEndowments', snapId, permissionName);
1415
- if (endowments) {
1416
- // We don't have any guarantees about the type of the endowments
1417
- // value, so we have to guard at runtime.
1418
- if (!Array.isArray(endowments) ||
1419
- endowments.some((value) => typeof value !== 'string')) {
1420
- throw new Error('Expected an array of string endowment names.');
1421
- }
1422
- allEndowments = allEndowments.concat(endowments);
1423
- }
1424
- }
1425
- }
1426
- const dedupedEndowments = [
1427
- ...new Set([...snaps_utils_1.DEFAULT_ENDOWMENTS, ...allEndowments]),
1428
- ];
1429
- if (dedupedEndowments.length <
1430
- // This is a bug in TypeScript: https://github.com/microsoft/TypeScript/issues/48313
1431
- // eslint-disable-next-line @typescript-eslint/restrict-plus-operands
1432
- snaps_utils_1.DEFAULT_ENDOWMENTS.length + allEndowments.length) {
1433
- (0, snaps_utils_1.logError)(`Duplicate endowments found for ${snapId}. Default endowments should not be requested.`, allEndowments);
1434
- }
1435
- return dedupedEndowments;
1436
- }
1437
- /**
1438
- * Sets a snap in state. Called when a snap is installed or updated. Performs
1439
- * various validation checks on the received arguments, and will throw if
1440
- * validation fails.
1441
- *
1442
- * The snap will be enabled and unblocked by the time this method returns,
1443
- * regardless of its previous state.
1444
- *
1445
- * See {@link SnapController.add} and {@link SnapController.updateSnap} for
1446
- * usage.
1447
- *
1448
- * @param args - The add snap args.
1449
- * @returns The resulting snap object.
1450
- */
1451
- #set(args) {
1452
- const { id: snapId, origin, files, isUpdate = false, removable, preinstalled, hidden, hideSnapBranding, } = args;
1453
- const { manifest, sourceCode: sourceCodeFile, svgIcon, auxiliaryFiles: rawAuxiliaryFiles, localizationFiles, } = files;
1454
- (0, snaps_utils_1.assertIsSnapManifest)(manifest.result);
1455
- const { version } = manifest.result;
1456
- const sourceCode = sourceCodeFile.toString();
1457
- (0, utils_1.assert)(typeof sourceCode === 'string' && sourceCode.length > 0, `Invalid source code for snap "${snapId}".`);
1458
- const auxiliaryFiles = rawAuxiliaryFiles.map((file) => {
1459
- (0, utils_1.assert)(typeof file.data.base64 === 'string');
1460
- return {
1461
- path: file.path,
1462
- value: file.data.base64,
1463
- };
1464
- });
1465
- const snapsState = this.state.snaps;
1466
- const existingSnap = snapsState[snapId];
1467
- const previousVersionHistory = existingSnap?.versionHistory ?? [];
1468
- const versionHistory = [
1469
- ...previousVersionHistory,
1470
- {
1471
- version,
1472
- date: Date.now(),
1473
- origin,
1474
- },
1475
- ];
1476
- const localizedFiles = localizationFiles.map((file) => file.result);
1477
- const snap = {
1478
- // Restore relevant snap state if it exists
1479
- ...existingSnap,
1480
- // Note that the snap will be unblocked and enabled, regardless of its
1481
- // previous state.
1482
- blocked: false,
1483
- enabled: true,
1484
- removable,
1485
- preinstalled,
1486
- hidden,
1487
- hideSnapBranding,
1488
- id: snapId,
1489
- initialConnections: manifest.result.initialConnections,
1490
- initialPermissions: manifest.result.initialPermissions,
1491
- manifest: manifest.result,
1492
- status: this.#statusMachine.config.initial,
1493
- sourceCode,
1494
- version,
1495
- versionHistory,
1496
- auxiliaryFiles,
1497
- localizationFiles: localizedFiles,
1498
- };
1499
- // If the snap was blocked, it isn't any longer
1500
- delete snap.blockInformation;
1501
- // store the snap back in state
1502
- const { inversePatches } = this.update((state) => {
1503
- state.snaps[snapId] = snap;
1504
- });
1505
- // checking for isUpdate here as this function is also used in
1506
- // the install flow, we do not care to create snapshots for installs
1507
- if (isUpdate) {
1508
- const rollbackSnapshot = this.#getRollbackSnapshot(snapId);
1509
- if (rollbackSnapshot !== undefined) {
1510
- rollbackSnapshot.statePatches = inversePatches;
1511
- }
1512
- }
1513
- // In case the Snap uses a localized manifest, we need to get the
1514
- // proposed name from the localized manifest.
1515
- const { proposedName } = (0, snaps_utils_1.getLocalizedSnapManifest)(manifest.result, 'en', localizedFiles);
1516
- this.messagingSystem.call('SubjectMetadataController:addSubjectMetadata', {
1517
- subjectType: permission_controller_1.SubjectType.Snap,
1518
- name: proposedName,
1519
- origin: snap.id,
1520
- version,
1521
- svgIcon: svgIcon?.toString() ?? null,
1522
- });
1523
- return { ...snap, sourceCode };
1524
- }
1525
- #validateSnapPermissions(processedPermissions) {
1526
- const permissionKeys = Object.keys(processedPermissions);
1527
- const handlerPermissions = Array.from(new Set(Object.values(snaps_rpc_methods_1.handlerEndowments)));
1528
- (0, utils_1.assert)(permissionKeys.some((key) => handlerPermissions.includes(key)), `A snap must request at least one of the following permissions: ${handlerPermissions
1529
- .filter((handler) => handler !== null)
1530
- .join(', ')}.`);
1531
- const excludedPermissionErrors = permissionKeys.reduce((errors, permission) => {
1532
- if ((0, utils_1.hasProperty)(this.#excludedPermissions, permission)) {
1533
- errors.push(this.#excludedPermissions[permission]);
1534
- }
1535
- return errors;
1536
- }, []);
1537
- (0, utils_1.assert)(excludedPermissionErrors.length === 0, `One or more permissions are not allowed:\n${excludedPermissionErrors.join('\n')}`);
1538
- }
1539
899
  /**
1540
900
  * Initiates a request for the given snap's initial permissions.
1541
901
  * Must be called in order. See processRequestedSnap.
@@ -1553,31 +913,31 @@ class SnapController extends base_controller_1.BaseController {
1553
913
  const { initialPermissions, initialConnections } = snap;
1554
914
  try {
1555
915
  const processedPermissions = (0, snaps_rpc_methods_1.processSnapPermissions)(initialPermissions);
1556
- this.#validateSnapPermissions(processedPermissions);
1557
- this.#updateApproval(pendingApproval.id, {
916
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_validateSnapPermissions).call(this, processedPermissions);
917
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_updateApproval).call(this, pendingApproval.id, {
1558
918
  loading: false,
1559
919
  connections: initialConnections ?? {},
1560
920
  permissions: processedPermissions,
1561
921
  });
1562
922
  const { permissions: approvedPermissions, ...requestData } = (await pendingApproval.promise);
1563
- this.#updatePermissions({
923
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_updatePermissions).call(this, {
1564
924
  snapId,
1565
925
  newPermissions: approvedPermissions,
1566
926
  requestData,
1567
927
  });
1568
928
  if (snap.manifest.initialConnections) {
1569
- this.#handleInitialConnections(snapId, null, snap.manifest.initialConnections);
929
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_handleInitialConnections).call(this, snapId, null, snap.manifest.initialConnections);
1570
930
  }
1571
931
  }
1572
932
  finally {
1573
- const runtime = this.#getRuntimeExpect(snapId);
933
+ const runtime = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRuntimeExpect).call(this, snapId);
1574
934
  runtime.installPromise = null;
1575
935
  }
1576
936
  }
1577
937
  destroy() {
1578
938
  super.destroy();
1579
- if (this.#timeoutForLastRequestStatus) {
1580
- clearTimeout(this.#timeoutForLastRequestStatus);
939
+ if (__classPrivateFieldGet(this, _SnapController_timeoutForLastRequestStatus, "f")) {
940
+ clearTimeout(__classPrivateFieldGet(this, _SnapController_timeoutForLastRequestStatus, "f"));
1581
941
  }
1582
942
  /* eslint-disable @typescript-eslint/unbound-method */
1583
943
  this.messagingSystem.unsubscribe('ExecutionService:unhandledError', this._onUnhandledSnapError);
@@ -1598,7 +958,7 @@ class SnapController extends base_controller_1.BaseController {
1598
958
  * @returns The result of the JSON-RPC request.
1599
959
  */
1600
960
  async handleRequest({ snapId, origin, handler: handlerType, request: rawRequest, }) {
1601
- this.#assertCanUsePlatform();
961
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_assertCanUsePlatform).call(this);
1602
962
  const request = {
1603
963
  jsonrpc: '2.0',
1604
964
  id: (0, nanoid_1.nanoid)(),
@@ -1628,411 +988,911 @@ class SnapController extends base_controller_1.BaseController {
1628
988
  throw new Error(`Snap "${snapId}" is not permitted to handle requests from "${origin}".`);
1629
989
  }
1630
990
  }
1631
- const handler = this.#getRpcRequestHandler(snapId);
991
+ const handler = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRpcRequestHandler).call(this, snapId);
1632
992
  if (!handler) {
1633
993
  throw new Error(`Snap RPC message handler not found for snap "${snapId}".`);
1634
994
  }
1635
- const timeout = this.#getExecutionTimeout(handlerPermissions);
995
+ const timeout = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getExecutionTimeout).call(this, handlerPermissions);
1636
996
  return handler({ origin, handler: handlerType, request, timeout });
1637
997
  }
1638
- /**
1639
- * Determine the execution timeout for a given handler permission.
1640
- *
1641
- * If no permission is specified or the permission itself has no execution timeout defined
1642
- * the constructor argument `maxRequestTime` will be used.
1643
- *
1644
- * @param permission - An optional permission constraint for the handler being called.
1645
- * @returns The execution timeout for the given handler.
1646
- */
1647
- #getExecutionTimeout(permission) {
1648
- return (0, snaps_rpc_methods_1.getMaxRequestTimeCaveat)(permission) ?? this.maxRequestTime;
998
+ }
999
+ exports.SnapController = SnapController;
1000
+ _SnapController_closeAllConnections = new WeakMap(), _SnapController_dynamicPermissions = 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_encryptor = new WeakMap(), _SnapController_getMnemonic = new WeakMap(), _SnapController_getFeatureFlags = new WeakMap(), _SnapController_detectSnapLocation = new WeakMap(), _SnapController_snapsRuntimeData = new WeakMap(), _SnapController_rollbackSnapshots = new WeakMap(), _SnapController_timeoutForLastRequestStatus = new WeakMap(), _SnapController_statusMachine = new WeakMap(), _SnapController_preinstalledSnaps = new WeakMap(), _SnapController_instances = new WeakSet(), _SnapController_initializeStateMachine = function _SnapController_initializeStateMachine() {
1001
+ const disableGuard = ({ snapId }) => {
1002
+ return this.getExpect(snapId).enabled;
1003
+ };
1004
+ const statusConfig = {
1005
+ initial: snaps_utils_1.SnapStatus.Installing,
1006
+ states: {
1007
+ [snaps_utils_1.SnapStatus.Installing]: {
1008
+ on: {
1009
+ [snaps_utils_1.SnapStatusEvents.Start]: {
1010
+ target: snaps_utils_1.SnapStatus.Running,
1011
+ cond: disableGuard,
1012
+ },
1013
+ },
1014
+ },
1015
+ [snaps_utils_1.SnapStatus.Updating]: {
1016
+ on: {
1017
+ [snaps_utils_1.SnapStatusEvents.Start]: {
1018
+ target: snaps_utils_1.SnapStatus.Running,
1019
+ cond: disableGuard,
1020
+ },
1021
+ [snaps_utils_1.SnapStatusEvents.Stop]: snaps_utils_1.SnapStatus.Stopped,
1022
+ },
1023
+ },
1024
+ [snaps_utils_1.SnapStatus.Running]: {
1025
+ on: {
1026
+ [snaps_utils_1.SnapStatusEvents.Stop]: snaps_utils_1.SnapStatus.Stopped,
1027
+ [snaps_utils_1.SnapStatusEvents.Crash]: snaps_utils_1.SnapStatus.Crashed,
1028
+ },
1029
+ },
1030
+ [snaps_utils_1.SnapStatus.Stopped]: {
1031
+ on: {
1032
+ [snaps_utils_1.SnapStatusEvents.Start]: {
1033
+ target: snaps_utils_1.SnapStatus.Running,
1034
+ cond: disableGuard,
1035
+ },
1036
+ [snaps_utils_1.SnapStatusEvents.Update]: snaps_utils_1.SnapStatus.Updating,
1037
+ },
1038
+ },
1039
+ [snaps_utils_1.SnapStatus.Crashed]: {
1040
+ on: {
1041
+ [snaps_utils_1.SnapStatusEvents.Start]: {
1042
+ target: snaps_utils_1.SnapStatus.Running,
1043
+ cond: disableGuard,
1044
+ },
1045
+ [snaps_utils_1.SnapStatusEvents.Update]: snaps_utils_1.SnapStatus.Updating,
1046
+ },
1047
+ },
1048
+ },
1049
+ };
1050
+ __classPrivateFieldSet(this, _SnapController_statusMachine, (0, fsm_1.createMachine)(statusConfig), "f");
1051
+ (0, fsm_2.validateMachine)(__classPrivateFieldGet(this, _SnapController_statusMachine, "f"));
1052
+ }, _SnapController_registerMessageHandlers = function _SnapController_registerMessageHandlers() {
1053
+ this.messagingSystem.registerActionHandler(`${exports.controllerName}:clearSnapState`, (...args) => this.clearSnapState(...args));
1054
+ this.messagingSystem.registerActionHandler(`${exports.controllerName}:get`, (...args) => this.get(...args));
1055
+ this.messagingSystem.registerActionHandler(`${exports.controllerName}:getSnapState`, async (...args) => this.getSnapState(...args));
1056
+ this.messagingSystem.registerActionHandler(`${exports.controllerName}:handleRequest`, async (...args) => this.handleRequest(...args));
1057
+ this.messagingSystem.registerActionHandler(`${exports.controllerName}:has`, (...args) => this.has(...args));
1058
+ this.messagingSystem.registerActionHandler(`${exports.controllerName}:updateBlockedSnaps`, async () => this.updateBlockedSnaps());
1059
+ this.messagingSystem.registerActionHandler(`${exports.controllerName}:updateSnapState`, async (...args) => this.updateSnapState(...args));
1060
+ this.messagingSystem.registerActionHandler(`${exports.controllerName}:enable`, (...args) => this.enableSnap(...args));
1061
+ this.messagingSystem.registerActionHandler(`${exports.controllerName}:disable`, async (...args) => this.disableSnap(...args));
1062
+ this.messagingSystem.registerActionHandler(`${exports.controllerName}:remove`, async (...args) => this.removeSnap(...args));
1063
+ this.messagingSystem.registerActionHandler(`${exports.controllerName}:getPermitted`, (...args) => this.getPermittedSnaps(...args));
1064
+ this.messagingSystem.registerActionHandler(`${exports.controllerName}:install`, async (...args) => this.installSnaps(...args));
1065
+ this.messagingSystem.registerActionHandler(`${exports.controllerName}:getAll`, (...args) => this.getAllSnaps(...args));
1066
+ this.messagingSystem.registerActionHandler(`${exports.controllerName}:incrementActiveReferences`, (...args) => this.incrementActiveReferences(...args));
1067
+ this.messagingSystem.registerActionHandler(`${exports.controllerName}:decrementActiveReferences`, (...args) => this.decrementActiveReferences(...args));
1068
+ this.messagingSystem.registerActionHandler(`${exports.controllerName}:getRegistryMetadata`, async (...args) => this.getRegistryMetadata(...args));
1069
+ this.messagingSystem.registerActionHandler(`${exports.controllerName}:disconnectOrigin`, (...args) => this.removeSnapFromSubject(...args));
1070
+ this.messagingSystem.registerActionHandler(`${exports.controllerName}:revokeDynamicPermissions`, (...args) => this.revokeDynamicSnapPermissions(...args));
1071
+ this.messagingSystem.registerActionHandler(`${exports.controllerName}:getFile`, async (...args) => this.getSnapFile(...args));
1072
+ this.messagingSystem.registerActionHandler(`${exports.controllerName}:stopAllSnaps`, async (...args) => this.stopAllSnaps(...args));
1073
+ }, _SnapController_handlePreinstalledSnaps = function _SnapController_handlePreinstalledSnaps(preinstalledSnaps) {
1074
+ for (const { snapId, manifest, files, removable, hidden, hideSnapBranding, } of preinstalledSnaps) {
1075
+ const existingSnap = this.get(snapId);
1076
+ const isAlreadyInstalled = existingSnap !== undefined;
1077
+ const isUpdate = isAlreadyInstalled && (0, utils_1.gtVersion)(manifest.version, existingSnap.version);
1078
+ // Disallow downgrades and overwriting non preinstalled snaps
1079
+ if (isAlreadyInstalled &&
1080
+ (!isUpdate || existingSnap.preinstalled !== true)) {
1081
+ continue;
1082
+ }
1083
+ const manifestFile = new snaps_utils_1.VirtualFile({
1084
+ path: snaps_utils_1.NpmSnapFileNames.Manifest,
1085
+ value: JSON.stringify(manifest),
1086
+ result: manifest,
1087
+ });
1088
+ const virtualFiles = files.map(({ path, value }) => new snaps_utils_1.VirtualFile({ value, path }));
1089
+ const { filePath, iconPath } = manifest.source.location.npm;
1090
+ const sourceCode = virtualFiles.find((file) => file.path === filePath);
1091
+ const svgIcon = iconPath
1092
+ ? virtualFiles.find((file) => file.path === iconPath)
1093
+ : undefined;
1094
+ (0, utils_1.assert)(sourceCode, 'Source code not provided for preinstalled snap.');
1095
+ (0, utils_1.assert)(!iconPath || (iconPath && svgIcon), 'Icon not provided for preinstalled snap.');
1096
+ (0, utils_1.assert)(manifest.source.files === undefined, 'Auxiliary files are not currently supported for preinstalled snaps.');
1097
+ const localizationFiles = manifest.source.locales?.map((path) => virtualFiles.find((file) => file.path === path)) ?? [];
1098
+ const validatedLocalizationFiles = (0, snaps_utils_1.getValidatedLocalizationFiles)(localizationFiles.filter(Boolean));
1099
+ (0, utils_1.assert)(localizationFiles.length === validatedLocalizationFiles.length, 'Missing localization files for preinstalled snap.');
1100
+ const filesObject = {
1101
+ manifest: manifestFile,
1102
+ sourceCode,
1103
+ svgIcon,
1104
+ auxiliaryFiles: [],
1105
+ localizationFiles: validatedLocalizationFiles,
1106
+ };
1107
+ // Add snap to the SnapController state
1108
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_set).call(this, {
1109
+ id: snapId,
1110
+ origin: 'metamask',
1111
+ files: filesObject,
1112
+ removable,
1113
+ hidden,
1114
+ hideSnapBranding,
1115
+ preinstalled: true,
1116
+ });
1117
+ // Setup permissions
1118
+ const processedPermissions = (0, snaps_rpc_methods_1.processSnapPermissions)(manifest.initialPermissions);
1119
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_validateSnapPermissions).call(this, processedPermissions);
1120
+ const { newPermissions, unusedPermissions } = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_calculatePermissionsChange).call(this, snapId, processedPermissions);
1121
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_updatePermissions).call(this, { snapId, newPermissions, unusedPermissions });
1122
+ if (manifest.initialConnections) {
1123
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_handleInitialConnections).call(this, snapId, existingSnap?.initialConnections ?? null, manifest.initialConnections);
1124
+ }
1125
+ // Set status
1126
+ this.update((state) => {
1127
+ state.snaps[snapId].status = snaps_utils_1.SnapStatus.Stopped;
1128
+ });
1649
1129
  }
1650
- /**
1651
- * Gets the RPC message handler for the given snap.
1652
- *
1653
- * @param snapId - The id of the Snap whose message handler to get.
1654
- * @returns The RPC handler for the given snap.
1655
- */
1656
- #getRpcRequestHandler(snapId) {
1657
- const runtime = this.#getRuntimeExpect(snapId);
1658
- const existingHandler = runtime.rpcHandler;
1659
- if (existingHandler) {
1660
- return existingHandler;
1661
- }
1662
- const requestQueue = new RequestQueue_1.RequestQueue(5);
1663
- // We need to set up this promise map to map snapIds to their respective startPromises,
1664
- // because otherwise we would lose context on the correct startPromise.
1665
- const startPromises = new Map();
1666
- const rpcHandler = async ({ origin, handler: handlerType, request, timeout, }) => {
1667
- if (this.state.snaps[snapId].enabled === false) {
1668
- throw new Error(`Snap "${snapId}" is disabled.`);
1669
- }
1670
- if (this.state.snaps[snapId].status === snaps_utils_1.SnapStatus.Installing) {
1671
- throw new Error(`Snap "${snapId}" is currently being installed. Please try again later.`);
1672
- }
1673
- if (!this.isRunning(snapId)) {
1674
- let localStartPromise = startPromises.get(snapId);
1675
- if (!localStartPromise) {
1676
- localStartPromise = this.startSnap(snapId);
1677
- startPromises.set(snapId, localStartPromise);
1678
- }
1679
- else if (requestQueue.get(origin) >= requestQueue.maxQueueSize) {
1680
- throw new Error('Exceeds maximum number of requests waiting to be resolved, please try again.');
1681
- }
1682
- requestQueue.increment(origin);
1683
- try {
1684
- await localStartPromise;
1685
- }
1686
- finally {
1687
- requestQueue.decrement(origin);
1688
- // Only delete startPromise for a snap if its value hasn't changed
1689
- if (startPromises.get(snapId) === localStartPromise) {
1690
- startPromises.delete(snapId);
1691
- }
1692
- }
1693
- }
1694
- const timer = new Timer_1.Timer(timeout);
1695
- this.#recordSnapRpcRequestStart(snapId, request.id, timer);
1696
- const handleRpcRequestPromise = this.messagingSystem.call('ExecutionService:handleRpcRequest', snapId, { origin, handler: handlerType, request });
1697
- // This will either get the result or reject due to the timeout.
1698
- try {
1699
- const result = await (0, utils_2.withTimeout)(handleRpcRequestPromise, timer);
1700
- if (result === utils_2.hasTimedOut) {
1701
- throw new Error(`${snapId} failed to respond to the request in time.`);
1702
- }
1703
- await this.#assertSnapRpcRequestResult(snapId, handlerType, result);
1704
- const transformedResult = await this.#transformSnapRpcRequestResult(snapId, handlerType, result);
1705
- this.#recordSnapRpcRequestFinish(snapId, request.id);
1706
- return transformedResult;
1707
- }
1708
- catch (error) {
1709
- // We flag the RPC request as finished early since termination may affect pending requests
1710
- this.#recordSnapRpcRequestFinish(snapId, request.id);
1711
- const [jsonRpcError, handled] = (0, snaps_utils_1.unwrapError)(error);
1712
- if (!handled) {
1713
- await this.stopSnap(snapId, snaps_utils_1.SnapStatusEvents.Crash);
1714
- }
1715
- throw jsonRpcError;
1716
- }
1130
+ }, _SnapController_pollForLastRequestStatus = function _SnapController_pollForLastRequestStatus() {
1131
+ __classPrivateFieldSet(this, _SnapController_timeoutForLastRequestStatus, setTimeout(() => {
1132
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_stopSnapsLastRequestPastMax).call(this).catch((error) => {
1133
+ // TODO: Decide how to handle errors.
1134
+ (0, snaps_utils_1.logError)(error);
1135
+ });
1136
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_pollForLastRequestStatus).call(this);
1137
+ }, __classPrivateFieldGet(this, _SnapController_idleTimeCheckInterval, "f")), "f");
1138
+ }, _SnapController_blockSnap =
1139
+ /**
1140
+ * Blocks an installed snap and prevents it from being started again. Emits
1141
+ * {@link SnapBlocked}. Does nothing if the snap is not installed.
1142
+ *
1143
+ * @param snapId - The snap to block.
1144
+ * @param blockedSnapInfo - Information detailing why the snap is blocked.
1145
+ */
1146
+ async function _SnapController_blockSnap(snapId, blockedSnapInfo) {
1147
+ if (!this.has(snapId)) {
1148
+ return;
1149
+ }
1150
+ try {
1151
+ this.update((state) => {
1152
+ state.snaps[snapId].blocked = true;
1153
+ state.snaps[snapId].blockInformation = blockedSnapInfo;
1154
+ });
1155
+ await this.disableSnap(snapId);
1156
+ }
1157
+ catch (error) {
1158
+ (0, snaps_utils_1.logError)(`Encountered error when stopping blocked snap "${snapId}".`, error);
1159
+ }
1160
+ this.messagingSystem.publish(`${exports.controllerName}:snapBlocked`, snapId, blockedSnapInfo);
1161
+ }, _SnapController_unblockSnap = function _SnapController_unblockSnap(snapId) {
1162
+ if (!this.has(snapId) || !this.state.snaps[snapId].blocked) {
1163
+ return;
1164
+ }
1165
+ this.update((state) => {
1166
+ state.snaps[snapId].blocked = false;
1167
+ delete state.snaps[snapId].blockInformation;
1168
+ });
1169
+ this.messagingSystem.publish(`${exports.controllerName}:snapUnblocked`, snapId);
1170
+ }, _SnapController_assertIsInstallAllowed = async function _SnapController_assertIsInstallAllowed(snapId, snapInfo) {
1171
+ const results = await this.messagingSystem.call('SnapsRegistry:get', {
1172
+ [snapId]: snapInfo,
1173
+ });
1174
+ const result = results[snapId];
1175
+ if (result.status === registry_1.SnapsRegistryStatus.Blocked) {
1176
+ throw new Error(`Cannot install version "${snapInfo.version}" of snap "${snapId}": The version is blocked. ${result.reason?.explanation ?? ''}`);
1177
+ }
1178
+ const isAllowlistingRequired = Object.keys(snapInfo.permissions).some((permission) => !constants_1.ALLOWED_PERMISSIONS.includes(permission));
1179
+ if (__classPrivateFieldGet(this, _SnapController_featureFlags, "f").requireAllowlist &&
1180
+ isAllowlistingRequired &&
1181
+ result.status !== registry_1.SnapsRegistryStatus.Verified) {
1182
+ throw new Error(`Cannot install version "${snapInfo.version}" of snap "${snapId}": ${result.status === registry_1.SnapsRegistryStatus.Unavailable
1183
+ ? 'The registry is temporarily unavailable.'
1184
+ : 'The snap is not on the allowlist.'}`);
1185
+ }
1186
+ }, _SnapController_assertCanInstallSnaps = function _SnapController_assertCanInstallSnaps() {
1187
+ (0, utils_1.assert)(__classPrivateFieldGet(this, _SnapController_featureFlags, "f").disableSnapInstallation !== true, 'Installing Snaps is currently disabled in this version of MetaMask.');
1188
+ }, _SnapController_assertCanUsePlatform = function _SnapController_assertCanUsePlatform() {
1189
+ const flags = __classPrivateFieldGet(this, _SnapController_getFeatureFlags, "f").call(this);
1190
+ (0, utils_1.assert)(flags.disableSnaps !== true, 'The Snaps platform requires basic functionality to be used. Enable basic functionality in the settings to use the Snaps platform.');
1191
+ }, _SnapController_stopSnapsLastRequestPastMax = async function _SnapController_stopSnapsLastRequestPastMax() {
1192
+ const entries = [...__classPrivateFieldGet(this, _SnapController_snapsRuntimeData, "f").entries()];
1193
+ return Promise.all(entries
1194
+ .filter(([_snapId, runtime]) => runtime.activeReferences === 0 &&
1195
+ runtime.pendingInboundRequests.length === 0 &&
1196
+ runtime.lastRequest &&
1197
+ __classPrivateFieldGet(this, _SnapController_maxIdleTime, "f") &&
1198
+ (0, utils_1.timeSince)(runtime.lastRequest) > __classPrivateFieldGet(this, _SnapController_maxIdleTime, "f"))
1199
+ .map(async ([snapId]) => this.stopSnap(snapId, snaps_utils_1.SnapStatusEvents.Stop)));
1200
+ }, _SnapController_transition = function _SnapController_transition(snapId, event) {
1201
+ const { interpreter } = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRuntimeExpect).call(this, snapId);
1202
+ interpreter.send(event);
1203
+ this.update((state) => {
1204
+ state.snaps[snapId].status = interpreter.state.value;
1205
+ });
1206
+ }, _SnapController_terminateSnap =
1207
+ /**
1208
+ * Terminates the specified snap and emits the `snapTerminated` event.
1209
+ *
1210
+ * @param snapId - The snap to terminate.
1211
+ */
1212
+ async function _SnapController_terminateSnap(snapId) {
1213
+ await this.messagingSystem.call('ExecutionService:terminateSnap', snapId);
1214
+ // Hack to give up execution for a bit to let gracefully terminating Snaps return.
1215
+ await new Promise((resolve) => setTimeout(resolve, 1));
1216
+ const runtime = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRuntimeExpect).call(this, snapId);
1217
+ // Unresponsive requests may still be timed, time them out.
1218
+ runtime.pendingInboundRequests
1219
+ .filter((pendingRequest) => pendingRequest.timer.status !== 'finished')
1220
+ .forEach((pendingRequest) => pendingRequest.timer.finish());
1221
+ // Hack to give up execution for a bit to let timed out requests return.
1222
+ await new Promise((resolve) => setTimeout(resolve, 1));
1223
+ this.messagingSystem.publish('SnapController:snapTerminated', this.getTruncatedExpect(snapId));
1224
+ }, _SnapController_getSnapEncryptionKey =
1225
+ /**
1226
+ * Generate an encryption key to be used for state encryption for a given Snap.
1227
+ *
1228
+ * @param options - An options bag.
1229
+ * @param options.snapId - The Snap ID.
1230
+ * @param options.salt - A salt to be used for the encryption key.
1231
+ * @param options.useCache - Whether to use caching or not.
1232
+ * @param options.keyMetadata - Optional metadata about how to derive the encryption key.
1233
+ * @returns An encryption key.
1234
+ */
1235
+ async function _SnapController_getSnapEncryptionKey({ snapId, salt: passedSalt, useCache, keyMetadata, }) {
1236
+ const runtime = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRuntimeExpect).call(this, snapId);
1237
+ if (runtime.encryptionKey && runtime.encryptionSalt && useCache) {
1238
+ return {
1239
+ key: await __classPrivateFieldGet(this, _SnapController_encryptor, "f").importKey(runtime.encryptionKey),
1240
+ salt: runtime.encryptionSalt,
1717
1241
  };
1718
- runtime.rpcHandler = rpcHandler;
1719
- return rpcHandler;
1720
1242
  }
1721
- /**
1722
- * Create a dynamic interface in the SnapInterfaceController.
1723
- *
1724
- * @param snapId - The snap ID.
1725
- * @param content - The initial interface content.
1726
- * @returns An identifier that can be used to identify the interface.
1727
- */
1728
- async #createInterface(snapId, content) {
1729
- return this.messagingSystem.call('SnapInterfaceController:createInterface', snapId, content);
1243
+ const salt = passedSalt ?? __classPrivateFieldGet(this, _SnapController_encryptor, "f").generateSalt();
1244
+ const mnemonicPhrase = await __classPrivateFieldGet(this, _SnapController_getMnemonic, "f").call(this);
1245
+ const entropy = await (0, snaps_rpc_methods_1.getEncryptionEntropy)({ snapId, mnemonicPhrase });
1246
+ const encryptionKey = await __classPrivateFieldGet(this, _SnapController_encryptor, "f").keyFromPassword(entropy, salt, true, keyMetadata);
1247
+ const exportedKey = await __classPrivateFieldGet(this, _SnapController_encryptor, "f").exportKey(encryptionKey);
1248
+ // Cache exported encryption key in runtime
1249
+ if (useCache) {
1250
+ runtime.encryptionKey = exportedKey;
1251
+ runtime.encryptionSalt = salt;
1252
+ }
1253
+ return { key: encryptionKey, salt };
1254
+ }, _SnapController_decryptSnapState =
1255
+ /**
1256
+ * Decrypt the encrypted state for a given Snap.
1257
+ *
1258
+ * @param snapId - The Snap ID.
1259
+ * @param state - The encrypted state as a string.
1260
+ * @returns A valid JSON object derived from the encrypted state.
1261
+ * @throws If the decryption fails or the decrypted state is not valid JSON.
1262
+ */
1263
+ async function _SnapController_decryptSnapState(snapId, state) {
1264
+ try {
1265
+ const parsed = (0, snaps_utils_1.parseJson)(state);
1266
+ const { salt, keyMetadata } = parsed;
1267
+ const useCache = __classPrivateFieldGet(this, _SnapController_encryptor, "f").isVaultUpdated(state);
1268
+ const { key } = await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getSnapEncryptionKey).call(this, {
1269
+ snapId,
1270
+ salt,
1271
+ useCache,
1272
+ // When decrypting state we expect key metadata to be present.
1273
+ // If it isn't present, we assume that the Snap state we are decrypting is old enough to use the legacy encryption params.
1274
+ keyMetadata: keyMetadata ?? constants_1.LEGACY_ENCRYPTION_KEY_DERIVATION_OPTIONS,
1275
+ });
1276
+ const decryptedState = await __classPrivateFieldGet(this, _SnapController_encryptor, "f").decryptWithKey(key, parsed);
1277
+ (0, utils_1.assert)((0, utils_1.isValidJson)(decryptedState));
1278
+ return decryptedState;
1730
1279
  }
1731
- #assertInterfaceExists(snapId, id) {
1732
- // This will throw if the interface isn't accessible, but we assert nevertheless.
1733
- (0, utils_1.assert)(this.messagingSystem.call('SnapInterfaceController:getInterface', snapId, id));
1280
+ catch {
1281
+ throw rpc_errors_1.rpcErrors.internal({
1282
+ message: 'Failed to decrypt snap state, the state must be corrupted.',
1283
+ });
1734
1284
  }
1735
- /**
1736
- * Transform a RPC request result if necessary.
1737
- *
1738
- * @param snapId - The snap ID of the snap that produced the result.
1739
- * @param handlerType - The handler type that produced the result.
1740
- * @param result - The result.
1741
- * @returns The transformed result if applicable, otherwise the original result.
1742
- */
1743
- async #transformSnapRpcRequestResult(snapId, handlerType, result) {
1744
- switch (handlerType) {
1745
- case snaps_utils_1.HandlerType.OnTransaction:
1746
- case snaps_utils_1.HandlerType.OnSignature:
1747
- case snaps_utils_1.HandlerType.OnHomePage: {
1748
- // Since this type has been asserted earlier we can cast
1749
- const castResult = result;
1750
- // If a handler returns static content, we turn it into a dynamic UI
1751
- if (castResult && (0, utils_1.hasProperty)(castResult, 'content')) {
1752
- const { content, ...rest } = castResult;
1753
- const id = await this.#createInterface(snapId, content);
1754
- return { ...rest, id };
1285
+ }, _SnapController_encryptSnapState =
1286
+ /**
1287
+ * Encrypt a JSON state object for a given Snap.
1288
+ *
1289
+ * Note: This function does not assert the validity of the object,
1290
+ * please ensure only valid JSON is passed to it.
1291
+ *
1292
+ * @param snapId - The Snap ID.
1293
+ * @param state - The state object.
1294
+ * @returns A string containing the encrypted JSON object.
1295
+ */
1296
+ async function _SnapController_encryptSnapState(snapId, state) {
1297
+ const { key, salt } = await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getSnapEncryptionKey).call(this, {
1298
+ snapId,
1299
+ useCache: true,
1300
+ });
1301
+ const encryptedState = await __classPrivateFieldGet(this, _SnapController_encryptor, "f").encryptWithKey(key, state);
1302
+ encryptedState.salt = salt;
1303
+ return JSON.stringify(encryptedState);
1304
+ }, _SnapController_handleInitialConnections = function _SnapController_handleInitialConnections(snapId, previousInitialConnections, initialConnections) {
1305
+ if (previousInitialConnections) {
1306
+ const revokedInitialConnections = (0, utils_2.setDiff)(previousInitialConnections, initialConnections);
1307
+ for (const origin of Object.keys(revokedInitialConnections)) {
1308
+ this.removeSnapFromSubject(origin, snapId);
1309
+ }
1310
+ }
1311
+ for (const origin of Object.keys(initialConnections)) {
1312
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_addSnapToSubject).call(this, origin, snapId);
1313
+ }
1314
+ }, _SnapController_addSnapToSubject = function _SnapController_addSnapToSubject(origin, snapId) {
1315
+ const subjectPermissions = this.messagingSystem.call('PermissionController:getPermissions', origin);
1316
+ const existingCaveat = subjectPermissions?.[snaps_rpc_methods_1.WALLET_SNAP_PERMISSION_KEY]?.caveats?.find((caveat) => caveat.type === snaps_utils_1.SnapCaveatType.SnapIds);
1317
+ const subjectHasSnap = Boolean(existingCaveat?.value?.[snapId]);
1318
+ // If the subject is already connected to the snap, this is a no-op.
1319
+ if (subjectHasSnap) {
1320
+ return;
1321
+ }
1322
+ // If an existing caveat exists, we add the snap to that.
1323
+ if (existingCaveat) {
1324
+ this.messagingSystem.call('PermissionController:updateCaveat', origin, snaps_rpc_methods_1.WALLET_SNAP_PERMISSION_KEY, snaps_utils_1.SnapCaveatType.SnapIds, { ...existingCaveat.value, [snapId]: {} });
1325
+ return;
1326
+ }
1327
+ const approvedPermissions = {
1328
+ [snaps_rpc_methods_1.WALLET_SNAP_PERMISSION_KEY]: {
1329
+ caveats: [
1330
+ {
1331
+ type: snaps_utils_1.SnapCaveatType.SnapIds,
1332
+ value: {
1333
+ [snapId]: {},
1334
+ },
1335
+ },
1336
+ ],
1337
+ },
1338
+ };
1339
+ this.messagingSystem.call('PermissionController:grantPermissions', {
1340
+ approvedPermissions,
1341
+ subject: { origin },
1342
+ });
1343
+ }, _SnapController_removeSnapFromSubjects = function _SnapController_removeSnapFromSubjects(snapId) {
1344
+ const subjects = this.messagingSystem.call('PermissionController:getSubjectNames');
1345
+ for (const subject of subjects) {
1346
+ this.removeSnapFromSubject(subject, snapId);
1347
+ }
1348
+ }, _SnapController_revokeAllSnapPermissions = function _SnapController_revokeAllSnapPermissions(snapId) {
1349
+ if (this.messagingSystem.call('PermissionController:hasPermissions', snapId)) {
1350
+ this.messagingSystem.call('PermissionController:revokeAllPermissions', snapId);
1351
+ }
1352
+ }, _SnapController_createApproval = function _SnapController_createApproval({ origin, snapId, type, }) {
1353
+ const id = (0, nanoid_1.nanoid)();
1354
+ const promise = this.messagingSystem.call('ApprovalController:addRequest', {
1355
+ origin,
1356
+ id,
1357
+ type,
1358
+ requestData: {
1359
+ // Mirror previous installation metadata
1360
+ metadata: { id, origin: snapId, dappOrigin: origin },
1361
+ snapId,
1362
+ },
1363
+ requestState: {
1364
+ loading: true,
1365
+ },
1366
+ }, true);
1367
+ return { id, promise };
1368
+ }, _SnapController_updateApproval = function _SnapController_updateApproval(id, requestState) {
1369
+ try {
1370
+ this.messagingSystem.call('ApprovalController:updateRequestState', {
1371
+ id,
1372
+ requestState,
1373
+ });
1374
+ }
1375
+ catch {
1376
+ // Do nothing
1377
+ }
1378
+ }, _SnapController_resolveAllowlistVersion = async function _SnapController_resolveAllowlistVersion(snapId, versionRange) {
1379
+ return await this.messagingSystem.call('SnapsRegistry:resolveVersion', snapId, versionRange);
1380
+ }, _SnapController_add =
1381
+ /**
1382
+ * Returns a promise representing the complete installation of the requested snap.
1383
+ * If the snap is already being installed, the previously pending promise will be returned.
1384
+ *
1385
+ * @param args - Object containing the snap id and either the URL of the snap's manifest,
1386
+ * or the snap's manifest and source code. The object may also optionally contain a target
1387
+ * version.
1388
+ * @returns The resulting snap object.
1389
+ */
1390
+ async function _SnapController_add(args) {
1391
+ const { id: snapId, location, versionRange } = args;
1392
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_setupRuntime).call(this, snapId);
1393
+ const runtime = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRuntimeExpect).call(this, snapId);
1394
+ if (!runtime.installPromise) {
1395
+ (0, logging_1.log)(`Adding snap: ${snapId}`);
1396
+ // If fetching and setting the snap succeeds, this property will be set
1397
+ // to null in the authorize() method.
1398
+ runtime.installPromise = (async () => {
1399
+ const fetchedSnap = await (0, utils_2.fetchSnap)(snapId, location);
1400
+ const manifest = fetchedSnap.manifest.result;
1401
+ if (!(0, utils_1.satisfiesVersionRange)(manifest.version, versionRange)) {
1402
+ throw new Error(`Version mismatch. Manifest for "${snapId}" specifies version "${manifest.version}" which doesn't satisfy requested version range "${versionRange}".`);
1403
+ }
1404
+ await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_assertIsInstallAllowed).call(this, snapId, {
1405
+ version: manifest.version,
1406
+ checksum: manifest.source.shasum,
1407
+ permissions: manifest.initialPermissions,
1408
+ });
1409
+ return __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_set).call(this, {
1410
+ ...args,
1411
+ files: fetchedSnap,
1412
+ id: snapId,
1413
+ });
1414
+ })();
1415
+ }
1416
+ try {
1417
+ return await runtime.installPromise;
1418
+ }
1419
+ catch (error) {
1420
+ // Reset promise so users can retry installation in case the problem is
1421
+ // temporary.
1422
+ runtime.installPromise = null;
1423
+ throw error;
1424
+ }
1425
+ }, _SnapController_startSnap = async function _SnapController_startSnap(snapData) {
1426
+ const { snapId } = snapData;
1427
+ if (this.isRunning(snapId)) {
1428
+ throw new Error(`Snap "${snapId}" is already started.`);
1429
+ }
1430
+ try {
1431
+ const runtime = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRuntimeExpect).call(this, snapId);
1432
+ const result = await this.messagingSystem.call('ExecutionService:executeSnap', {
1433
+ ...snapData,
1434
+ endowments: await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getEndowments).call(this, snapId),
1435
+ });
1436
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_transition).call(this, snapId, snaps_utils_1.SnapStatusEvents.Start);
1437
+ // We treat the initialization of the snap as the first request, for idle timing purposes.
1438
+ runtime.lastRequest = Date.now();
1439
+ return result;
1440
+ }
1441
+ catch (error) {
1442
+ await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_terminateSnap).call(this, snapId);
1443
+ throw error;
1444
+ }
1445
+ }, _SnapController_getEndowments =
1446
+ /**
1447
+ * Gets the names of all endowments that will be added to the Snap's
1448
+ * Compartment when it executes. These should be the names of global
1449
+ * JavaScript APIs accessible in the root realm of the execution environment.
1450
+ *
1451
+ * Throws an error if the endowment getter for a permission returns a truthy
1452
+ * value that is not an array of strings.
1453
+ *
1454
+ * @param snapId - The id of the snap whose SES endowments to get.
1455
+ * @returns An array of the names of the endowments.
1456
+ */
1457
+ async function _SnapController_getEndowments(snapId) {
1458
+ let allEndowments = [];
1459
+ for (const permissionName of __classPrivateFieldGet(this, _SnapController_environmentEndowmentPermissions, "f")) {
1460
+ if (this.messagingSystem.call('PermissionController:hasPermission', snapId, permissionName)) {
1461
+ const endowments = await this.messagingSystem.call('PermissionController:getEndowments', snapId, permissionName);
1462
+ if (endowments) {
1463
+ // We don't have any guarantees about the type of the endowments
1464
+ // value, so we have to guard at runtime.
1465
+ if (!Array.isArray(endowments) ||
1466
+ endowments.some((value) => typeof value !== 'string')) {
1467
+ throw new Error('Expected an array of string endowment names.');
1755
1468
  }
1756
- return result;
1469
+ allEndowments = allEndowments.concat(endowments);
1757
1470
  }
1758
- default:
1759
- return result;
1760
1471
  }
1761
1472
  }
1762
- /**
1763
- * Assert that the returned result of a Snap RPC call is the expected shape.
1764
- *
1765
- * @param snapId - The snap ID.
1766
- * @param handlerType - The handler type of the RPC Request.
1767
- * @param result - The result of the RPC request.
1768
- */
1769
- async #assertSnapRpcRequestResult(snapId, handlerType, result) {
1770
- switch (handlerType) {
1771
- case snaps_utils_1.HandlerType.OnTransaction: {
1772
- (0, utils_1.assertStruct)(result, snaps_utils_1.OnTransactionResponseStruct);
1773
- if (result && (0, utils_1.hasProperty)(result, 'id')) {
1774
- this.#assertInterfaceExists(snapId, result.id);
1775
- }
1776
- break;
1473
+ const dedupedEndowments = [
1474
+ ...new Set([...snaps_utils_1.DEFAULT_ENDOWMENTS, ...allEndowments]),
1475
+ ];
1476
+ if (dedupedEndowments.length <
1477
+ // This is a bug in TypeScript: https://github.com/microsoft/TypeScript/issues/48313
1478
+ // eslint-disable-next-line @typescript-eslint/restrict-plus-operands
1479
+ snaps_utils_1.DEFAULT_ENDOWMENTS.length + allEndowments.length) {
1480
+ (0, snaps_utils_1.logError)(`Duplicate endowments found for ${snapId}. Default endowments should not be requested.`, allEndowments);
1481
+ }
1482
+ return dedupedEndowments;
1483
+ }, _SnapController_set = function _SnapController_set(args) {
1484
+ const { id: snapId, origin, files, isUpdate = false, removable, preinstalled, hidden, hideSnapBranding, } = args;
1485
+ const { manifest, sourceCode: sourceCodeFile, svgIcon, auxiliaryFiles: rawAuxiliaryFiles, localizationFiles, } = files;
1486
+ (0, snaps_utils_1.assertIsSnapManifest)(manifest.result);
1487
+ const { version } = manifest.result;
1488
+ const sourceCode = sourceCodeFile.toString();
1489
+ (0, utils_1.assert)(typeof sourceCode === 'string' && sourceCode.length > 0, `Invalid source code for snap "${snapId}".`);
1490
+ const auxiliaryFiles = rawAuxiliaryFiles.map((file) => {
1491
+ (0, utils_1.assert)(typeof file.data.base64 === 'string');
1492
+ return {
1493
+ path: file.path,
1494
+ value: file.data.base64,
1495
+ };
1496
+ });
1497
+ const snapsState = this.state.snaps;
1498
+ const existingSnap = snapsState[snapId];
1499
+ const previousVersionHistory = existingSnap?.versionHistory ?? [];
1500
+ const versionHistory = [
1501
+ ...previousVersionHistory,
1502
+ {
1503
+ version,
1504
+ date: Date.now(),
1505
+ origin,
1506
+ },
1507
+ ];
1508
+ const localizedFiles = localizationFiles.map((file) => file.result);
1509
+ const snap = {
1510
+ // Restore relevant snap state if it exists
1511
+ ...existingSnap,
1512
+ // Note that the snap will be unblocked and enabled, regardless of its
1513
+ // previous state.
1514
+ blocked: false,
1515
+ enabled: true,
1516
+ removable,
1517
+ preinstalled,
1518
+ hidden,
1519
+ hideSnapBranding,
1520
+ id: snapId,
1521
+ initialConnections: manifest.result.initialConnections,
1522
+ initialPermissions: manifest.result.initialPermissions,
1523
+ manifest: manifest.result,
1524
+ status: __classPrivateFieldGet(this, _SnapController_statusMachine, "f").config.initial,
1525
+ sourceCode,
1526
+ version,
1527
+ versionHistory,
1528
+ auxiliaryFiles,
1529
+ localizationFiles: localizedFiles,
1530
+ };
1531
+ // If the snap was blocked, it isn't any longer
1532
+ delete snap.blockInformation;
1533
+ // store the snap back in state
1534
+ const { inversePatches } = this.update((state) => {
1535
+ state.snaps[snapId] = snap;
1536
+ });
1537
+ // checking for isUpdate here as this function is also used in
1538
+ // the install flow, we do not care to create snapshots for installs
1539
+ if (isUpdate) {
1540
+ const rollbackSnapshot = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRollbackSnapshot).call(this, snapId);
1541
+ if (rollbackSnapshot !== undefined) {
1542
+ rollbackSnapshot.statePatches = inversePatches;
1543
+ }
1544
+ }
1545
+ // In case the Snap uses a localized manifest, we need to get the
1546
+ // proposed name from the localized manifest.
1547
+ const { proposedName } = (0, snaps_utils_1.getLocalizedSnapManifest)(manifest.result, 'en', localizedFiles);
1548
+ this.messagingSystem.call('SubjectMetadataController:addSubjectMetadata', {
1549
+ subjectType: permission_controller_1.SubjectType.Snap,
1550
+ name: proposedName,
1551
+ origin: snap.id,
1552
+ version,
1553
+ svgIcon: svgIcon?.toString() ?? null,
1554
+ });
1555
+ return { ...snap, sourceCode };
1556
+ }, _SnapController_validateSnapPermissions = function _SnapController_validateSnapPermissions(processedPermissions) {
1557
+ const permissionKeys = Object.keys(processedPermissions);
1558
+ const handlerPermissions = Array.from(new Set(Object.values(snaps_rpc_methods_1.handlerEndowments)));
1559
+ (0, utils_1.assert)(permissionKeys.some((key) => handlerPermissions.includes(key)), `A snap must request at least one of the following permissions: ${handlerPermissions
1560
+ .filter((handler) => handler !== null)
1561
+ .join(', ')}.`);
1562
+ const excludedPermissionErrors = permissionKeys.reduce((errors, permission) => {
1563
+ if ((0, utils_1.hasProperty)(__classPrivateFieldGet(this, _SnapController_excludedPermissions, "f"), permission)) {
1564
+ errors.push(__classPrivateFieldGet(this, _SnapController_excludedPermissions, "f")[permission]);
1565
+ }
1566
+ return errors;
1567
+ }, []);
1568
+ (0, utils_1.assert)(excludedPermissionErrors.length === 0, `One or more permissions are not allowed:\n${excludedPermissionErrors.join('\n')}`);
1569
+ }, _SnapController_getExecutionTimeout = function _SnapController_getExecutionTimeout(permission) {
1570
+ return (0, snaps_rpc_methods_1.getMaxRequestTimeCaveat)(permission) ?? this.maxRequestTime;
1571
+ }, _SnapController_getRpcRequestHandler = function _SnapController_getRpcRequestHandler(snapId) {
1572
+ const runtime = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRuntimeExpect).call(this, snapId);
1573
+ const existingHandler = runtime.rpcHandler;
1574
+ if (existingHandler) {
1575
+ return existingHandler;
1576
+ }
1577
+ const requestQueue = new RequestQueue_1.RequestQueue(5);
1578
+ // We need to set up this promise map to map snapIds to their respective startPromises,
1579
+ // because otherwise we would lose context on the correct startPromise.
1580
+ const startPromises = new Map();
1581
+ const rpcHandler = async ({ origin, handler: handlerType, request, timeout, }) => {
1582
+ if (this.state.snaps[snapId].enabled === false) {
1583
+ throw new Error(`Snap "${snapId}" is disabled.`);
1584
+ }
1585
+ if (this.state.snaps[snapId].status === snaps_utils_1.SnapStatus.Installing) {
1586
+ throw new Error(`Snap "${snapId}" is currently being installed. Please try again later.`);
1587
+ }
1588
+ if (!this.isRunning(snapId)) {
1589
+ let localStartPromise = startPromises.get(snapId);
1590
+ if (!localStartPromise) {
1591
+ localStartPromise = this.startSnap(snapId);
1592
+ startPromises.set(snapId, localStartPromise);
1777
1593
  }
1778
- case snaps_utils_1.HandlerType.OnSignature: {
1779
- (0, utils_1.assertStruct)(result, snaps_utils_1.OnSignatureResponseStruct);
1780
- if (result && (0, utils_1.hasProperty)(result, 'id')) {
1781
- this.#assertInterfaceExists(snapId, result.id);
1782
- }
1783
- break;
1594
+ else if (requestQueue.get(origin) >= requestQueue.maxQueueSize) {
1595
+ throw new Error('Exceeds maximum number of requests waiting to be resolved, please try again.');
1784
1596
  }
1785
- case snaps_utils_1.HandlerType.OnHomePage: {
1786
- (0, utils_1.assertStruct)(result, snaps_utils_1.OnHomePageResponseStruct);
1787
- if (result && (0, utils_1.hasProperty)(result, 'id')) {
1788
- this.#assertInterfaceExists(snapId, result.id);
1597
+ requestQueue.increment(origin);
1598
+ try {
1599
+ await localStartPromise;
1600
+ }
1601
+ finally {
1602
+ requestQueue.decrement(origin);
1603
+ // Only delete startPromise for a snap if its value hasn't changed
1604
+ if (startPromises.get(snapId) === localStartPromise) {
1605
+ startPromises.delete(snapId);
1789
1606
  }
1790
- break;
1791
1607
  }
1792
- case snaps_utils_1.HandlerType.OnNameLookup:
1793
- (0, utils_1.assertStruct)(result, snaps_utils_1.OnNameLookupResponseStruct);
1794
- break;
1795
- default:
1796
- break;
1797
- }
1798
- }
1799
- #recordSnapRpcRequestStart(snapId, requestId, timer) {
1800
- const runtime = this.#getRuntimeExpect(snapId);
1801
- runtime.pendingInboundRequests.push({ requestId, timer });
1802
- runtime.lastRequest = null;
1803
- }
1804
- #recordSnapRpcRequestFinish(snapId, requestId) {
1805
- const runtime = this.#getRuntimeExpect(snapId);
1806
- runtime.pendingInboundRequests = runtime.pendingInboundRequests.filter((request) => request.requestId !== requestId);
1807
- if (runtime.pendingInboundRequests.length === 0) {
1808
- runtime.lastRequest = Date.now();
1809
- }
1810
- }
1811
- /**
1812
- * Retrieves the rollback snapshot of a snap.
1813
- *
1814
- * @param snapId - The snap id.
1815
- * @returns A `RollbackSnapshot` or `undefined` if one doesn't exist.
1816
- */
1817
- #getRollbackSnapshot(snapId) {
1818
- return this.#rollbackSnapshots.get(snapId);
1819
- }
1820
- /**
1821
- * Creates a `RollbackSnapshot` that is used to help ensure
1822
- * atomicity in multiple snap updates.
1823
- *
1824
- * @param snapId - The snap id.
1825
- * @throws {@link Error}. If the snap exists before creation or if creation fails.
1826
- * @returns A `RollbackSnapshot`.
1827
- */
1828
- #createRollbackSnapshot(snapId) {
1829
- (0, utils_1.assert)(this.#rollbackSnapshots.get(snapId) === undefined, new Error(`Snap "${snapId}" rollback snapshot already exists.`));
1830
- this.#rollbackSnapshots.set(snapId, {
1831
- statePatches: [],
1832
- permissions: {},
1833
- newVersion: '',
1834
- });
1835
- const newRollbackSnapshot = this.#rollbackSnapshots.get(snapId);
1836
- (0, utils_1.assert)(newRollbackSnapshot !== undefined, new Error(`Snapshot creation failed for ${snapId}.`));
1837
- return newRollbackSnapshot;
1838
- }
1839
- /**
1840
- * Rolls back a snap to its previous state, permissions
1841
- * and source code based on the `RollbackSnapshot` that
1842
- * is captured during the update process. After rolling back,
1843
- * the function also emits an event indicating that the
1844
- * snap has been rolled back and it clears the snapshot
1845
- * for that snap.
1846
- *
1847
- * @param snapId - The snap id.
1848
- * @throws {@link Error}. If a snapshot does not exist.
1849
- */
1850
- async #rollbackSnap(snapId) {
1851
- const rollbackSnapshot = this.#getRollbackSnapshot(snapId);
1852
- if (!rollbackSnapshot) {
1853
- throw new Error('A snapshot does not exist for this snap.');
1854
1608
  }
1855
- await this.stopSnap(snapId, snaps_utils_1.SnapStatusEvents.Stop);
1856
- // Always set to stopped even if it wasn't running initially
1857
- if (this.get(snapId)?.status !== snaps_utils_1.SnapStatus.Stopped) {
1858
- this.#transition(snapId, snaps_utils_1.SnapStatusEvents.Stop);
1609
+ const timer = new Timer_1.Timer(timeout);
1610
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_recordSnapRpcRequestStart).call(this, snapId, request.id, timer);
1611
+ const handleRpcRequestPromise = this.messagingSystem.call('ExecutionService:handleRpcRequest', snapId, { origin, handler: handlerType, request });
1612
+ // This will either get the result or reject due to the timeout.
1613
+ try {
1614
+ const result = await (0, utils_2.withTimeout)(handleRpcRequestPromise, timer);
1615
+ if (result === utils_2.hasTimedOut) {
1616
+ throw new Error(`${snapId} failed to respond to the request in time.`);
1617
+ }
1618
+ await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_assertSnapRpcRequestResult).call(this, snapId, handlerType, result);
1619
+ const transformedResult = await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_transformSnapRpcRequestResult).call(this, snapId, handlerType, result);
1620
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_recordSnapRpcRequestFinish).call(this, snapId, request.id);
1621
+ return transformedResult;
1859
1622
  }
1860
- const { statePatches, permissions } = rollbackSnapshot;
1861
- if (statePatches?.length) {
1862
- this.applyPatches(statePatches);
1623
+ catch (error) {
1624
+ // We flag the RPC request as finished early since termination may affect pending requests
1625
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_recordSnapRpcRequestFinish).call(this, snapId, request.id);
1626
+ const [jsonRpcError, handled] = (0, snaps_utils_1.unwrapError)(error);
1627
+ if (!handled) {
1628
+ await this.stopSnap(snapId, snaps_utils_1.SnapStatusEvents.Crash);
1629
+ }
1630
+ throw jsonRpcError;
1863
1631
  }
1864
- // Reset snap status, as we may have been in another state when we stored state patches
1865
- // But now we are 100% in a stopped state
1866
- if (this.get(snapId)?.status !== snaps_utils_1.SnapStatus.Stopped) {
1867
- this.update((state) => {
1868
- state.snaps[snapId].status = snaps_utils_1.SnapStatus.Stopped;
1869
- });
1632
+ };
1633
+ runtime.rpcHandler = rpcHandler;
1634
+ return rpcHandler;
1635
+ }, _SnapController_createInterface =
1636
+ /**
1637
+ * Create a dynamic interface in the SnapInterfaceController.
1638
+ *
1639
+ * @param snapId - The snap ID.
1640
+ * @param content - The initial interface content.
1641
+ * @returns An identifier that can be used to identify the interface.
1642
+ */
1643
+ async function _SnapController_createInterface(snapId, content) {
1644
+ return this.messagingSystem.call('SnapInterfaceController:createInterface', snapId, content);
1645
+ }, _SnapController_assertInterfaceExists = function _SnapController_assertInterfaceExists(snapId, id) {
1646
+ // This will throw if the interface isn't accessible, but we assert nevertheless.
1647
+ (0, utils_1.assert)(this.messagingSystem.call('SnapInterfaceController:getInterface', snapId, id));
1648
+ }, _SnapController_transformSnapRpcRequestResult =
1649
+ /**
1650
+ * Transform a RPC request result if necessary.
1651
+ *
1652
+ * @param snapId - The snap ID of the snap that produced the result.
1653
+ * @param handlerType - The handler type that produced the result.
1654
+ * @param result - The result.
1655
+ * @returns The transformed result if applicable, otherwise the original result.
1656
+ */
1657
+ async function _SnapController_transformSnapRpcRequestResult(snapId, handlerType, result) {
1658
+ switch (handlerType) {
1659
+ case snaps_utils_1.HandlerType.OnTransaction:
1660
+ case snaps_utils_1.HandlerType.OnSignature:
1661
+ case snaps_utils_1.HandlerType.OnHomePage: {
1662
+ // Since this type has been asserted earlier we can cast
1663
+ const castResult = result;
1664
+ // If a handler returns static content, we turn it into a dynamic UI
1665
+ if (castResult && (0, utils_1.hasProperty)(castResult, 'content')) {
1666
+ const { content, ...rest } = castResult;
1667
+ const id = await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_createInterface).call(this, snapId, content);
1668
+ return { ...rest, id };
1669
+ }
1670
+ return result;
1870
1671
  }
1871
- this.#updatePermissions({
1872
- snapId,
1873
- unusedPermissions: permissions.granted,
1874
- newPermissions: permissions.revoked,
1875
- requestData: permissions.requestData,
1876
- });
1877
- const truncatedSnap = this.getTruncatedExpect(snapId);
1878
- this.messagingSystem.publish('SnapController:snapRolledback', truncatedSnap, rollbackSnapshot.newVersion);
1879
- this.#rollbackSnapshots.delete(snapId);
1672
+ default:
1673
+ return result;
1880
1674
  }
1881
- /**
1882
- * Iterates through an array of snap ids
1883
- * and calls `rollbackSnap` on them.
1884
- *
1885
- * @param snapIds - An array of snap ids.
1886
- */
1887
- async #rollbackSnaps(snapIds) {
1888
- for (const snapId of snapIds) {
1889
- await this.#rollbackSnap(snapId);
1675
+ }, _SnapController_assertSnapRpcRequestResult =
1676
+ /**
1677
+ * Assert that the returned result of a Snap RPC call is the expected shape.
1678
+ *
1679
+ * @param snapId - The snap ID.
1680
+ * @param handlerType - The handler type of the RPC Request.
1681
+ * @param result - The result of the RPC request.
1682
+ */
1683
+ async function _SnapController_assertSnapRpcRequestResult(snapId, handlerType, result) {
1684
+ switch (handlerType) {
1685
+ case snaps_utils_1.HandlerType.OnTransaction: {
1686
+ (0, utils_1.assertStruct)(result, snaps_utils_1.OnTransactionResponseStruct);
1687
+ if (result && (0, utils_1.hasProperty)(result, 'id')) {
1688
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_assertInterfaceExists).call(this, snapId, result.id);
1689
+ }
1690
+ break;
1890
1691
  }
1891
- }
1892
- #getRuntime(snapId) {
1893
- return this.#snapsRuntimeData.get(snapId);
1894
- }
1895
- #getRuntimeExpect(snapId) {
1896
- const runtime = this.#getRuntime(snapId);
1897
- (0, utils_1.assert)(runtime !== undefined, new Error(`Snap "${snapId}" runtime data not found`));
1898
- return runtime;
1899
- }
1900
- #setupRuntime(snapId) {
1901
- if (this.#snapsRuntimeData.has(snapId)) {
1902
- return;
1692
+ case snaps_utils_1.HandlerType.OnSignature: {
1693
+ (0, utils_1.assertStruct)(result, snaps_utils_1.OnSignatureResponseStruct);
1694
+ if (result && (0, utils_1.hasProperty)(result, 'id')) {
1695
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_assertInterfaceExists).call(this, snapId, result.id);
1696
+ }
1697
+ break;
1903
1698
  }
1904
- const snap = this.get(snapId);
1905
- const interpreter = (0, fsm_1.interpret)(this.#statusMachine);
1906
- interpreter.start({
1907
- context: { snapId },
1908
- value: snap?.status ??
1909
- this.#statusMachine.config.initial,
1910
- });
1911
- (0, fsm_2.forceStrict)(interpreter);
1912
- this.#snapsRuntimeData.set(snapId, {
1913
- lastRequest: null,
1914
- rpcHandler: null,
1915
- installPromise: null,
1916
- encryptionKey: null,
1917
- encryptionSalt: null,
1918
- activeReferences: 0,
1919
- pendingInboundRequests: [],
1920
- pendingOutboundRequests: 0,
1921
- interpreter,
1922
- stopping: false,
1699
+ case snaps_utils_1.HandlerType.OnHomePage: {
1700
+ (0, utils_1.assertStruct)(result, snaps_utils_1.OnHomePageResponseStruct);
1701
+ if (result && (0, utils_1.hasProperty)(result, 'id')) {
1702
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_assertInterfaceExists).call(this, snapId, result.id);
1703
+ }
1704
+ break;
1705
+ }
1706
+ case snaps_utils_1.HandlerType.OnNameLookup:
1707
+ (0, utils_1.assertStruct)(result, snaps_utils_1.OnNameLookupResponseStruct);
1708
+ break;
1709
+ default:
1710
+ break;
1711
+ }
1712
+ }, _SnapController_recordSnapRpcRequestStart = function _SnapController_recordSnapRpcRequestStart(snapId, requestId, timer) {
1713
+ const runtime = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRuntimeExpect).call(this, snapId);
1714
+ runtime.pendingInboundRequests.push({ requestId, timer });
1715
+ runtime.lastRequest = null;
1716
+ }, _SnapController_recordSnapRpcRequestFinish = function _SnapController_recordSnapRpcRequestFinish(snapId, requestId) {
1717
+ const runtime = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRuntimeExpect).call(this, snapId);
1718
+ runtime.pendingInboundRequests = runtime.pendingInboundRequests.filter((request) => request.requestId !== requestId);
1719
+ if (runtime.pendingInboundRequests.length === 0) {
1720
+ runtime.lastRequest = Date.now();
1721
+ }
1722
+ }, _SnapController_getRollbackSnapshot = function _SnapController_getRollbackSnapshot(snapId) {
1723
+ return __classPrivateFieldGet(this, _SnapController_rollbackSnapshots, "f").get(snapId);
1724
+ }, _SnapController_createRollbackSnapshot = function _SnapController_createRollbackSnapshot(snapId) {
1725
+ (0, utils_1.assert)(__classPrivateFieldGet(this, _SnapController_rollbackSnapshots, "f").get(snapId) === undefined, new Error(`Snap "${snapId}" rollback snapshot already exists.`));
1726
+ __classPrivateFieldGet(this, _SnapController_rollbackSnapshots, "f").set(snapId, {
1727
+ statePatches: [],
1728
+ permissions: {},
1729
+ newVersion: '',
1730
+ });
1731
+ const newRollbackSnapshot = __classPrivateFieldGet(this, _SnapController_rollbackSnapshots, "f").get(snapId);
1732
+ (0, utils_1.assert)(newRollbackSnapshot !== undefined, new Error(`Snapshot creation failed for ${snapId}.`));
1733
+ return newRollbackSnapshot;
1734
+ }, _SnapController_rollbackSnap =
1735
+ /**
1736
+ * Rolls back a snap to its previous state, permissions
1737
+ * and source code based on the `RollbackSnapshot` that
1738
+ * is captured during the update process. After rolling back,
1739
+ * the function also emits an event indicating that the
1740
+ * snap has been rolled back and it clears the snapshot
1741
+ * for that snap.
1742
+ *
1743
+ * @param snapId - The snap id.
1744
+ * @throws {@link Error}. If a snapshot does not exist.
1745
+ */
1746
+ async function _SnapController_rollbackSnap(snapId) {
1747
+ const rollbackSnapshot = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRollbackSnapshot).call(this, snapId);
1748
+ if (!rollbackSnapshot) {
1749
+ throw new Error('A snapshot does not exist for this snap.');
1750
+ }
1751
+ await this.stopSnap(snapId, snaps_utils_1.SnapStatusEvents.Stop);
1752
+ // Always set to stopped even if it wasn't running initially
1753
+ if (this.get(snapId)?.status !== snaps_utils_1.SnapStatus.Stopped) {
1754
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_transition).call(this, snapId, snaps_utils_1.SnapStatusEvents.Stop);
1755
+ }
1756
+ const { statePatches, permissions } = rollbackSnapshot;
1757
+ if (statePatches?.length) {
1758
+ this.applyPatches(statePatches);
1759
+ }
1760
+ // Reset snap status, as we may have been in another state when we stored state patches
1761
+ // But now we are 100% in a stopped state
1762
+ if (this.get(snapId)?.status !== snaps_utils_1.SnapStatus.Stopped) {
1763
+ this.update((state) => {
1764
+ state.snaps[snapId].status = snaps_utils_1.SnapStatus.Stopped;
1923
1765
  });
1924
1766
  }
1925
- #calculatePermissionsChange(snapId, desiredPermissionsSet) {
1926
- const oldPermissions = this.messagingSystem.call('PermissionController:getPermissions', snapId) ?? {};
1927
- const newPermissions = (0, utils_2.permissionsDiff)(desiredPermissionsSet, oldPermissions);
1928
- // TODO(ritave): The assumption that these are unused only holds so long as we do not
1929
- // permit dynamic permission requests.
1930
- const unusedPermissions = (0, utils_2.permissionsDiff)(oldPermissions, desiredPermissionsSet);
1931
- // It's a Set Intersection of oldPermissions and desiredPermissionsSet
1932
- // oldPermissions ∖ (oldPermissions desiredPermissionsSet) ⟺ oldPermissions ∩ desiredPermissionsSet
1933
- const approvedPermissions = (0, utils_2.permissionsDiff)(oldPermissions, unusedPermissions);
1934
- return { newPermissions, unusedPermissions, approvedPermissions };
1935
- }
1936
- #isSubjectConnectedToSnap(snapId, origin) {
1937
- const subjectPermissions = this.messagingSystem.call('PermissionController:getPermissions', origin);
1938
- const existingCaveat = subjectPermissions?.[snaps_rpc_methods_1.WALLET_SNAP_PERMISSION_KEY]?.caveats?.find((caveat) => caveat.type === snaps_utils_1.SnapCaveatType.SnapIds);
1939
- return Boolean(existingCaveat?.value?.[snapId]);
1940
- }
1941
- #calculateConnectionsChange(snapId, oldConnectionsSet, desiredConnectionsSet) {
1942
- // Filter out any origins that have been revoked since last install/update.
1943
- // That way they will be represented as new.
1944
- const filteredOldConnections = Object.keys(oldConnectionsSet)
1945
- .filter((origin) => this.#isSubjectConnectedToSnap(snapId, origin))
1946
- .reduce((accumulator, origin) => {
1947
- accumulator[origin] = oldConnectionsSet[origin];
1948
- return accumulator;
1949
- }, {});
1950
- const newConnections = (0, utils_2.setDiff)(desiredConnectionsSet, filteredOldConnections);
1951
- const unusedConnections = (0, utils_2.setDiff)(filteredOldConnections, desiredConnectionsSet);
1952
- // It's a Set Intersection of oldConnections and desiredConnectionsSet
1953
- // oldConnections ∖ (oldConnections ∖ desiredConnectionsSet) ⟺ oldConnections ∩ desiredConnectionsSet
1954
- const approvedConnections = (0, utils_2.setDiff)(filteredOldConnections, unusedConnections);
1955
- return { newConnections, unusedConnections, approvedConnections };
1767
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_updatePermissions).call(this, {
1768
+ snapId,
1769
+ unusedPermissions: permissions.granted,
1770
+ newPermissions: permissions.revoked,
1771
+ requestData: permissions.requestData,
1772
+ });
1773
+ const truncatedSnap = this.getTruncatedExpect(snapId);
1774
+ this.messagingSystem.publish('SnapController:snapRolledback', truncatedSnap, rollbackSnapshot.newVersion);
1775
+ __classPrivateFieldGet(this, _SnapController_rollbackSnapshots, "f").delete(snapId);
1776
+ }, _SnapController_rollbackSnaps =
1777
+ /**
1778
+ * Iterates through an array of snap ids
1779
+ * and calls `rollbackSnap` on them.
1780
+ *
1781
+ * @param snapIds - An array of snap ids.
1782
+ */
1783
+ async function _SnapController_rollbackSnaps(snapIds) {
1784
+ for (const snapId of snapIds) {
1785
+ await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_rollbackSnap).call(this, snapId);
1786
+ }
1787
+ }, _SnapController_getRuntime = function _SnapController_getRuntime(snapId) {
1788
+ return __classPrivateFieldGet(this, _SnapController_snapsRuntimeData, "f").get(snapId);
1789
+ }, _SnapController_getRuntimeExpect = function _SnapController_getRuntimeExpect(snapId) {
1790
+ const runtime = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRuntime).call(this, snapId);
1791
+ (0, utils_1.assert)(runtime !== undefined, new Error(`Snap "${snapId}" runtime data not found`));
1792
+ return runtime;
1793
+ }, _SnapController_setupRuntime = function _SnapController_setupRuntime(snapId) {
1794
+ if (__classPrivateFieldGet(this, _SnapController_snapsRuntimeData, "f").has(snapId)) {
1795
+ return;
1796
+ }
1797
+ const snap = this.get(snapId);
1798
+ const interpreter = (0, fsm_1.interpret)(__classPrivateFieldGet(this, _SnapController_statusMachine, "f"));
1799
+ interpreter.start({
1800
+ context: { snapId },
1801
+ value: snap?.status ??
1802
+ __classPrivateFieldGet(this, _SnapController_statusMachine, "f").config.initial,
1803
+ });
1804
+ (0, fsm_2.forceStrict)(interpreter);
1805
+ __classPrivateFieldGet(this, _SnapController_snapsRuntimeData, "f").set(snapId, {
1806
+ lastRequest: null,
1807
+ rpcHandler: null,
1808
+ installPromise: null,
1809
+ encryptionKey: null,
1810
+ encryptionSalt: null,
1811
+ activeReferences: 0,
1812
+ pendingInboundRequests: [],
1813
+ pendingOutboundRequests: 0,
1814
+ interpreter,
1815
+ stopping: false,
1816
+ });
1817
+ }, _SnapController_calculatePermissionsChange = function _SnapController_calculatePermissionsChange(snapId, desiredPermissionsSet) {
1818
+ const oldPermissions = this.messagingSystem.call('PermissionController:getPermissions', snapId) ?? {};
1819
+ const newPermissions = (0, utils_2.permissionsDiff)(desiredPermissionsSet, oldPermissions);
1820
+ // TODO(ritave): The assumption that these are unused only holds so long as we do not
1821
+ // permit dynamic permission requests.
1822
+ const unusedPermissions = (0, utils_2.permissionsDiff)(oldPermissions, desiredPermissionsSet);
1823
+ // It's a Set Intersection of oldPermissions and desiredPermissionsSet
1824
+ // oldPermissions ∖ (oldPermissions ∖ desiredPermissionsSet) ⟺ oldPermissions ∩ desiredPermissionsSet
1825
+ const approvedPermissions = (0, utils_2.permissionsDiff)(oldPermissions, unusedPermissions);
1826
+ return { newPermissions, unusedPermissions, approvedPermissions };
1827
+ }, _SnapController_isSubjectConnectedToSnap = function _SnapController_isSubjectConnectedToSnap(snapId, origin) {
1828
+ const subjectPermissions = this.messagingSystem.call('PermissionController:getPermissions', origin);
1829
+ const existingCaveat = subjectPermissions?.[snaps_rpc_methods_1.WALLET_SNAP_PERMISSION_KEY]?.caveats?.find((caveat) => caveat.type === snaps_utils_1.SnapCaveatType.SnapIds);
1830
+ return Boolean(existingCaveat?.value?.[snapId]);
1831
+ }, _SnapController_calculateConnectionsChange = function _SnapController_calculateConnectionsChange(snapId, oldConnectionsSet, desiredConnectionsSet) {
1832
+ // Filter out any origins that have been revoked since last install/update.
1833
+ // That way they will be represented as new.
1834
+ const filteredOldConnections = Object.keys(oldConnectionsSet)
1835
+ .filter((origin) => __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_isSubjectConnectedToSnap).call(this, snapId, origin))
1836
+ .reduce((accumulator, origin) => {
1837
+ accumulator[origin] = oldConnectionsSet[origin];
1838
+ return accumulator;
1839
+ }, {});
1840
+ const newConnections = (0, utils_2.setDiff)(desiredConnectionsSet, filteredOldConnections);
1841
+ const unusedConnections = (0, utils_2.setDiff)(filteredOldConnections, desiredConnectionsSet);
1842
+ // It's a Set Intersection of oldConnections and desiredConnectionsSet
1843
+ // oldConnections ∖ (oldConnections ∖ desiredConnectionsSet) ⟺ oldConnections ∩ desiredConnectionsSet
1844
+ const approvedConnections = (0, utils_2.setDiff)(filteredOldConnections, unusedConnections);
1845
+ return { newConnections, unusedConnections, approvedConnections };
1846
+ }, _SnapController_updatePermissions = function _SnapController_updatePermissions({ snapId, unusedPermissions = {}, newPermissions = {}, requestData, }) {
1847
+ const unusedPermissionsKeys = Object.keys(unusedPermissions);
1848
+ if ((0, utils_1.isNonEmptyArray)(unusedPermissionsKeys)) {
1849
+ this.messagingSystem.call('PermissionController:revokePermissions', {
1850
+ [snapId]: unusedPermissionsKeys,
1851
+ });
1956
1852
  }
1957
- /**
1958
- * Updates the permissions for a snap following an install, update or rollback.
1959
- *
1960
- * Grants newly requested permissions and revokes unused/revoked permissions.
1961
- *
1962
- * @param args - An options bag.
1963
- * @param args.snapId - The snap ID.
1964
- * @param args.newPermissions - New permissions to be granted.
1965
- * @param args.unusedPermissions - Unused permissions to be revoked.
1966
- * @param args.requestData - Optional request data from an approval.
1967
- */
1968
- #updatePermissions({ snapId, unusedPermissions = {}, newPermissions = {}, requestData, }) {
1969
- const unusedPermissionsKeys = Object.keys(unusedPermissions);
1970
- if ((0, utils_1.isNonEmptyArray)(unusedPermissionsKeys)) {
1971
- this.messagingSystem.call('PermissionController:revokePermissions', {
1972
- [snapId]: unusedPermissionsKeys,
1973
- });
1974
- }
1975
- if ((0, utils_1.isNonEmptyArray)(Object.keys(newPermissions))) {
1976
- this.messagingSystem.call('PermissionController:grantPermissions', {
1977
- approvedPermissions: newPermissions,
1978
- subject: { origin: snapId },
1979
- requestData,
1980
- });
1981
- }
1853
+ if ((0, utils_1.isNonEmptyArray)(Object.keys(newPermissions))) {
1854
+ this.messagingSystem.call('PermissionController:grantPermissions', {
1855
+ approvedPermissions: newPermissions,
1856
+ subject: { origin: snapId },
1857
+ requestData,
1858
+ });
1982
1859
  }
1983
- /**
1984
- * Checks if a snap will pass version validation checks
1985
- * with the new version range that is requested. The first
1986
- * check that is done is to check if the existing snap version
1987
- * falls inside the requested range. If it does, we want to return
1988
- * false because we do not care to create a rollback snapshot in
1989
- * that scenario. The second check is to ensure that the current
1990
- * snap version is not greater than all possible versions in
1991
- * the requested version range. If it is, then we also want
1992
- * to return false in that scenario.
1993
- *
1994
- * @param snapId - The snap id.
1995
- * @param newVersionRange - The new version range being requested.
1996
- * @returns `true` if validation checks pass and `false` if they do not.
1997
- */
1998
- #isValidUpdate(snapId, newVersionRange) {
1999
- const existingSnap = this.getExpect(snapId);
2000
- if ((0, utils_1.satisfiesVersionRange)(existingSnap.version, newVersionRange)) {
2001
- return false;
2002
- }
2003
- if ((0, utils_1.gtRange)(existingSnap.version, newVersionRange)) {
2004
- return false;
2005
- }
2006
- return true;
1860
+ }, _SnapController_isValidUpdate = function _SnapController_isValidUpdate(snapId, newVersionRange) {
1861
+ const existingSnap = this.getExpect(snapId);
1862
+ if ((0, utils_1.satisfiesVersionRange)(existingSnap.version, newVersionRange)) {
1863
+ return false;
2007
1864
  }
2008
- /**
2009
- * Call a lifecycle hook on a snap, if the snap has the
2010
- * `endowment:lifecycle-hooks` permission. If the snap does not have the
2011
- * permission, nothing happens.
2012
- *
2013
- * @param origin - The origin.
2014
- * @param snapId - The snap ID.
2015
- * @param handler - The lifecycle hook to call. This should be one of the
2016
- * supported lifecycle hooks.
2017
- * @private
2018
- */
2019
- async #callLifecycleHook(origin, snapId, handler) {
2020
- const permissionName = snaps_rpc_methods_1.handlerEndowments[handler];
2021
- (0, utils_1.assert)(permissionName, 'Lifecycle hook must have an endowment.');
2022
- const hasPermission = this.messagingSystem.call('PermissionController:hasPermission', snapId, permissionName);
2023
- if (!hasPermission) {
2024
- return;
2025
- }
2026
- await this.handleRequest({
2027
- snapId,
2028
- handler,
2029
- origin,
2030
- request: {
2031
- jsonrpc: '2.0',
2032
- method: handler,
2033
- },
2034
- });
1865
+ if ((0, utils_1.gtRange)(existingSnap.version, newVersionRange)) {
1866
+ return false;
2035
1867
  }
2036
- }
2037
- exports.SnapController = SnapController;
1868
+ return true;
1869
+ }, _SnapController_callLifecycleHook =
1870
+ /**
1871
+ * Call a lifecycle hook on a snap, if the snap has the
1872
+ * `endowment:lifecycle-hooks` permission. If the snap does not have the
1873
+ * permission, nothing happens.
1874
+ *
1875
+ * @param origin - The origin.
1876
+ * @param snapId - The snap ID.
1877
+ * @param handler - The lifecycle hook to call. This should be one of the
1878
+ * supported lifecycle hooks.
1879
+ * @private
1880
+ */
1881
+ async function _SnapController_callLifecycleHook(origin, snapId, handler) {
1882
+ const permissionName = snaps_rpc_methods_1.handlerEndowments[handler];
1883
+ (0, utils_1.assert)(permissionName, 'Lifecycle hook must have an endowment.');
1884
+ const hasPermission = this.messagingSystem.call('PermissionController:hasPermission', snapId, permissionName);
1885
+ if (!hasPermission) {
1886
+ return;
1887
+ }
1888
+ await this.handleRequest({
1889
+ snapId,
1890
+ handler,
1891
+ origin,
1892
+ request: {
1893
+ jsonrpc: '2.0',
1894
+ method: handler,
1895
+ },
1896
+ });
1897
+ };
2038
1898
  //# sourceMappingURL=SnapController.cjs.map