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