@metamask/snaps-controllers 3.6.0 → 4.1.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 (55) hide show
  1. package/CHANGELOG.md +19 -1
  2. package/dist/cjs/cronjob/CronjobController.js +2 -2
  3. package/dist/cjs/cronjob/CronjobController.js.map +1 -1
  4. package/dist/cjs/services/ProxyPostMessageStream.js +3 -10
  5. package/dist/cjs/services/ProxyPostMessageStream.js.map +1 -1
  6. package/dist/cjs/services/node/NodeProcessExecutionService.js +13 -1
  7. package/dist/cjs/services/node/NodeProcessExecutionService.js.map +1 -1
  8. package/dist/cjs/services/node/NodeThreadExecutionService.js +14 -1
  9. package/dist/cjs/services/node/NodeThreadExecutionService.js.map +1 -1
  10. package/dist/cjs/services/offscreen/OffscreenExecutionService.js +36 -99
  11. package/dist/cjs/services/offscreen/OffscreenExecutionService.js.map +1 -1
  12. package/dist/cjs/services/proxy/ProxyExecutionService.js +110 -0
  13. package/dist/cjs/services/proxy/ProxyExecutionService.js.map +1 -0
  14. package/dist/cjs/snaps/SnapController.js +206 -96
  15. package/dist/cjs/snaps/SnapController.js.map +1 -1
  16. package/dist/cjs/snaps/endowments/enum.js +1 -0
  17. package/dist/cjs/snaps/endowments/enum.js.map +1 -1
  18. package/dist/cjs/snaps/endowments/index.js +12 -4
  19. package/dist/cjs/snaps/endowments/index.js.map +1 -1
  20. package/dist/cjs/snaps/endowments/signature-insight.js +106 -0
  21. package/dist/cjs/snaps/endowments/signature-insight.js.map +1 -0
  22. package/dist/cjs/utils.js +32 -0
  23. package/dist/cjs/utils.js.map +1 -1
  24. package/dist/esm/cronjob/CronjobController.js +2 -2
  25. package/dist/esm/cronjob/CronjobController.js.map +1 -1
  26. package/dist/esm/services/ProxyPostMessageStream.js +3 -10
  27. package/dist/esm/services/ProxyPostMessageStream.js.map +1 -1
  28. package/dist/esm/services/node/NodeProcessExecutionService.js +13 -1
  29. package/dist/esm/services/node/NodeProcessExecutionService.js.map +1 -1
  30. package/dist/esm/services/node/NodeThreadExecutionService.js +14 -1
  31. package/dist/esm/services/node/NodeThreadExecutionService.js.map +1 -1
  32. package/dist/esm/services/offscreen/OffscreenExecutionService.js +36 -99
  33. package/dist/esm/services/offscreen/OffscreenExecutionService.js.map +1 -1
  34. package/dist/esm/services/proxy/ProxyExecutionService.js +100 -0
  35. package/dist/esm/services/proxy/ProxyExecutionService.js.map +1 -0
  36. package/dist/esm/snaps/SnapController.js +208 -98
  37. package/dist/esm/snaps/SnapController.js.map +1 -1
  38. package/dist/esm/snaps/endowments/enum.js +1 -0
  39. package/dist/esm/snaps/endowments/enum.js.map +1 -1
  40. package/dist/esm/snaps/endowments/index.js +10 -4
  41. package/dist/esm/snaps/endowments/index.js.map +1 -1
  42. package/dist/esm/snaps/endowments/signature-insight.js +99 -0
  43. package/dist/esm/snaps/endowments/signature-insight.js.map +1 -0
  44. package/dist/esm/utils.js +37 -0
  45. package/dist/esm/utils.js.map +1 -1
  46. package/dist/types/cronjob/CronjobController.d.ts +2 -2
  47. package/dist/types/services/ProxyPostMessageStream.d.ts +1 -2
  48. package/dist/types/services/offscreen/OffscreenExecutionService.d.ts +5 -22
  49. package/dist/types/services/proxy/ProxyExecutionService.d.ts +39 -0
  50. package/dist/types/snaps/SnapController.d.ts +33 -20
  51. package/dist/types/snaps/endowments/enum.d.ts +1 -0
  52. package/dist/types/snaps/endowments/index.d.ts +12 -0
  53. package/dist/types/snaps/endowments/signature-insight.d.ts +39 -0
  54. package/dist/types/utils.d.ts +99 -0
  55. package/package.json +10 -10
@@ -151,7 +151,7 @@ var _closeAllConnections = /*#__PURE__*/ new WeakMap(), _dynamicPermissions = /*
151
151
  _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
152
152
  * Constructor helper for registering the controller's messaging system
153
153
  * actions.
154
- */ _registerMessageHandlers = /*#__PURE__*/ new WeakSet(), _pollForLastRequestStatus = /*#__PURE__*/ new WeakSet(), _blockSnap = /*#__PURE__*/ new WeakSet(), /**
154
+ */ _registerMessageHandlers = /*#__PURE__*/ new WeakSet(), _handlePreinstalledSnaps = /*#__PURE__*/ new WeakSet(), _pollForLastRequestStatus = /*#__PURE__*/ new WeakSet(), _blockSnap = /*#__PURE__*/ new WeakSet(), /**
155
155
  * Unblocks a snap so that it can be enabled and started again. Emits
156
156
  * {@link SnapUnblocked}. Does nothing if the snap is not installed or already
157
157
  * unblocked.
@@ -168,7 +168,7 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
168
168
  *
169
169
  * @param snapId - The id of the snap to transition.
170
170
  * @param event - The event enum to use to transition.
171
- */ _transition = /*#__PURE__*/ new WeakSet(), _terminateSnap = /*#__PURE__*/ new WeakSet(), /**
171
+ */ _transition = /*#__PURE__*/ new WeakSet(), _terminateSnap = /*#__PURE__*/ new WeakSet(), _handleInitialConnections = /*#__PURE__*/ new WeakSet(), _addSnapToSubject = /*#__PURE__*/ new WeakSet(), /**
172
172
  * Removes a snap's permission (caveat) from all subjects.
173
173
  *
174
174
  * @param snapId - The id of the Snap.
@@ -189,7 +189,7 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
189
189
  *
190
190
  * @param args - The add snap args.
191
191
  * @returns The resulting snap object.
192
- */ _set = /*#__PURE__*/ new WeakSet(), _fetchSnap = /*#__PURE__*/ new WeakSet(), _validateSnapPermissions = /*#__PURE__*/ new WeakSet(), /**
192
+ */ _set = /*#__PURE__*/ new WeakSet(), _validateSnapPermissions = /*#__PURE__*/ new WeakSet(), /**
193
193
  * Gets the RPC message handler for the given snap.
194
194
  *
195
195
  * @param snapId - The id of the Snap whose message handler to get.
@@ -207,6 +207,16 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
207
207
  * @throws {@link Error}. If the snap exists before creation or if creation fails.
208
208
  * @returns A `RollbackSnapshot`.
209
209
  */ _createRollbackSnapshot = /*#__PURE__*/ new WeakSet(), _rollbackSnap = /*#__PURE__*/ new WeakSet(), _rollbackSnaps = /*#__PURE__*/ new WeakSet(), _getRuntime = /*#__PURE__*/ new WeakSet(), _getRuntimeExpect = /*#__PURE__*/ new WeakSet(), _setupRuntime = /*#__PURE__*/ new WeakSet(), _calculatePermissionsChange = /*#__PURE__*/ new WeakSet(), /**
210
+ * Updates the permissions for a snap following an install, update or rollback.
211
+ *
212
+ * Grants newly requested permissions and revokes unused/revoked permissions.
213
+ *
214
+ * @param args - An options bag.
215
+ * @param args.snapId - The snap ID.
216
+ * @param args.newPermissions - New permissions to be granted.
217
+ * @param args.unusedPermissions - Unused permissions to be revoked.
218
+ * @param args.requestData - Optional request data from an approval.
219
+ */ _updatePermissions = /*#__PURE__*/ new WeakSet(), /**
210
220
  * Checks if a snap will pass version validation checks
211
221
  * with the new version range that is requested. The first
212
222
  * check that is done is to check if the existing snap version
@@ -492,6 +502,10 @@ class SnapController extends _basecontroller.BaseController {
492
502
  if (!Array.isArray(snapIds)) {
493
503
  throw new Error('Expected array of snap ids.');
494
504
  }
505
+ snapIds.forEach((snapId)=>{
506
+ const snap = this.getExpect(snapId);
507
+ (0, _utils.assert)(snap.removable !== false, `${snapId} is not removable.`);
508
+ });
495
509
  await Promise.all(snapIds.map(async (snapId)=>{
496
510
  const snap = this.getExpect(snapId);
497
511
  const truncated = this.getTruncatedExpect(snapId);
@@ -506,7 +520,6 @@ class SnapController extends _basecontroller.BaseController {
506
520
  delete state.snaps[snapId];
507
521
  delete state.snapStates[snapId];
508
522
  });
509
- this.messagingSystem.publish(`SnapController:snapRemoved`, truncated);
510
523
  // If the snap has been fully installed before, also emit snapUninstalled.
511
524
  if (snap.status !== _snapsutils.SnapStatus.Installing) {
512
525
  this.messagingSystem.publish(`SnapController:snapUninstalled`, truncated);
@@ -686,6 +699,7 @@ class SnapController extends _basecontroller.BaseController {
686
699
  snapId,
687
700
  type: SNAP_APPROVAL_INSTALL
688
701
  });
702
+ this.messagingSystem.publish('SnapController:snapInstallStarted', snapId, origin, false);
689
703
  // Existing snaps must be stopped before overwriting
690
704
  if (existingSnap && this.isRunning(snapId)) {
691
705
  await this.stopSnap(snapId, _snapsutils.SnapStatusEvents.Stop);
@@ -719,11 +733,13 @@ class SnapController extends _basecontroller.BaseController {
719
733
  return truncated;
720
734
  } catch (error) {
721
735
  (0, _snapsutils.logError)(`Error when adding ${snapId}.`, error);
736
+ const errorString = error instanceof Error ? error.message : error.toString();
722
737
  _class_private_method_get(this, _updateApproval, updateApproval).call(this, pendingApproval.id, {
723
738
  loading: false,
724
739
  type: SNAP_APPROVAL_INSTALL,
725
- error: error instanceof Error ? error.message : error.toString()
740
+ error: errorString
726
741
  });
742
+ this.messagingSystem.publish('SnapController:snapInstallFailed', snapId, origin, false, errorString);
727
743
  throw error;
728
744
  }
729
745
  }
@@ -755,9 +771,11 @@ class SnapController extends _basecontroller.BaseController {
755
771
  type: SNAP_APPROVAL_UPDATE
756
772
  });
757
773
  try {
774
+ this.messagingSystem.publish('SnapController:snapInstallStarted', snapId, origin, true);
758
775
  const snap = this.getExpect(snapId);
759
- const newSnap = await _class_private_method_get(this, _fetchSnap, fetchSnap).call(this, snapId, location);
760
- const { sourceCode: sourceCodeFile, manifest: manifestFile } = newSnap.files;
776
+ const oldManifest = snap.manifest;
777
+ const newSnap = await (0, _utils1.fetchSnap)(snapId, location);
778
+ const { sourceCode: sourceCodeFile, manifest: manifestFile } = newSnap;
761
779
  const manifest = manifestFile.result;
762
780
  const newVersion = manifest.version;
763
781
  if (!(0, _utils.gtVersion)(newVersion, snap.version)) {
@@ -794,28 +812,22 @@ class SnapController extends _basecontroller.BaseController {
794
812
  _class_private_method_get(this, _set, set).call(this, {
795
813
  origin,
796
814
  id: snapId,
797
- files: newSnap.files,
815
+ files: newSnap,
798
816
  isUpdate: true
799
817
  });
800
- const unusedPermissionsKeys = Object.keys(unusedPermissions);
801
- if ((0, _utils.isNonEmptyArray)(unusedPermissionsKeys)) {
802
- this.messagingSystem.call('PermissionController:revokePermissions', {
803
- [snapId]: unusedPermissionsKeys
804
- });
805
- }
806
- if ((0, _utils.isNonEmptyArray)(Object.keys(approvedNewPermissions))) {
807
- this.messagingSystem.call('PermissionController:grantPermissions', {
808
- approvedPermissions: approvedNewPermissions,
809
- subject: {
810
- origin: snapId
811
- },
812
- requestData
813
- });
818
+ _class_private_method_get(this, _updatePermissions, updatePermissions).call(this, {
819
+ snapId,
820
+ unusedPermissions,
821
+ newPermissions: approvedNewPermissions,
822
+ requestData
823
+ });
824
+ if (manifest.initialConnections) {
825
+ _class_private_method_get(this, _handleInitialConnections, handleInitialConnections).call(this, snapId, oldManifest.initialConnections ?? null, manifest.initialConnections);
814
826
  }
815
827
  const rollbackSnapshot = _class_private_method_get(this, _getRollbackSnapshot, getRollbackSnapshot).call(this, snapId);
816
828
  if (rollbackSnapshot !== undefined) {
817
829
  rollbackSnapshot.permissions.revoked = unusedPermissions;
818
- rollbackSnapshot.permissions.granted = Object.keys(approvedNewPermissions);
830
+ rollbackSnapshot.permissions.granted = approvedNewPermissions;
819
831
  rollbackSnapshot.permissions.requestData = requestData;
820
832
  }
821
833
  const sourceCode = sourceCodeFile.toString();
@@ -839,11 +851,13 @@ class SnapController extends _basecontroller.BaseController {
839
851
  return truncatedSnap;
840
852
  } catch (error) {
841
853
  (0, _snapsutils.logError)(`Error when updating ${snapId},`, error);
854
+ const errorString = error instanceof Error ? error.message : error.toString();
842
855
  _class_private_method_get(this, _updateApproval, updateApproval).call(this, pendingApproval.id, {
843
856
  loading: false,
844
- error: error instanceof Error ? error.message : error.toString(),
857
+ error: errorString,
845
858
  type: SNAP_APPROVAL_UPDATE
846
859
  });
860
+ this.messagingSystem.publish('SnapController:snapInstallFailed', snapId, origin, true, errorString);
847
861
  throw error;
848
862
  }
849
863
  }
@@ -878,14 +892,13 @@ class SnapController extends _basecontroller.BaseController {
878
892
  permissions: processedPermissions
879
893
  });
880
894
  const { permissions: approvedPermissions, ...requestData } = await pendingApproval.promise;
881
- if ((0, _utils.isNonEmptyArray)(Object.keys(approvedPermissions))) {
882
- this.messagingSystem.call('PermissionController:grantPermissions', {
883
- approvedPermissions,
884
- subject: {
885
- origin: snapId
886
- },
887
- requestData
888
- });
895
+ _class_private_method_get(this, _updatePermissions, updatePermissions).call(this, {
896
+ snapId,
897
+ newPermissions: approvedPermissions,
898
+ requestData
899
+ });
900
+ if (snap.manifest.initialConnections) {
901
+ _class_private_method_get(this, _handleInitialConnections, handleInitialConnections).call(this, snapId, null, snap.manifest.initialConnections);
889
902
  }
890
903
  } finally{
891
904
  const runtime = _class_private_method_get(this, _getRuntimeExpect, getRuntimeExpect).call(this, snapId);
@@ -947,7 +960,7 @@ class SnapController extends _basecontroller.BaseController {
947
960
  }
948
961
  constructor({ closeAllConnections, messenger, state, dynamicPermissions = [
949
962
  'eth_accounts'
950
- ], environmentEndowmentPermissions = [], excludedPermissions = {}, idleTimeCheckInterval = (0, _utils.inMilliseconds)(5, _utils.Duration.Second), maxIdleTime = (0, _utils.inMilliseconds)(30, _utils.Duration.Second), maxRequestTime = (0, _utils.inMilliseconds)(60, _utils.Duration.Second), fetchFunction = globalThis.fetch.bind(globalThis), featureFlags = {}, detectSnapLocation: detectSnapLocationFunction = _location.detectSnapLocation }){
963
+ ], environmentEndowmentPermissions = [], excludedPermissions = {}, idleTimeCheckInterval = (0, _utils.inMilliseconds)(5, _utils.Duration.Second), maxIdleTime = (0, _utils.inMilliseconds)(30, _utils.Duration.Second), maxRequestTime = (0, _utils.inMilliseconds)(60, _utils.Duration.Second), fetchFunction = globalThis.fetch.bind(globalThis), featureFlags = {}, detectSnapLocation: detectSnapLocationFunction = _location.detectSnapLocation, preinstalledSnaps }){
951
964
  super({
952
965
  messenger,
953
966
  metadata: {
@@ -985,6 +998,7 @@ class SnapController extends _basecontroller.BaseController {
985
998
  });
986
999
  _class_private_method_init(this, _initializeStateMachine);
987
1000
  _class_private_method_init(this, _registerMessageHandlers);
1001
+ _class_private_method_init(this, _handlePreinstalledSnaps);
988
1002
  _class_private_method_init(this, _pollForLastRequestStatus);
989
1003
  /**
990
1004
  * Blocks an installed snap and prevents it from being started again. Emits
@@ -1002,6 +1016,8 @@ class SnapController extends _basecontroller.BaseController {
1002
1016
  *
1003
1017
  * @param snapId - The snap to terminate.
1004
1018
  */ _class_private_method_init(this, _terminateSnap);
1019
+ _class_private_method_init(this, _handleInitialConnections);
1020
+ _class_private_method_init(this, _addSnapToSubject);
1005
1021
  _class_private_method_init(this, _removeSnapFromSubjects);
1006
1022
  _class_private_method_init(this, _revokeAllSnapPermissions);
1007
1023
  _class_private_method_init(this, _createApproval);
@@ -1029,13 +1045,6 @@ class SnapController extends _basecontroller.BaseController {
1029
1045
  * @returns An array of the names of the endowments.
1030
1046
  */ _class_private_method_init(this, _getEndowments);
1031
1047
  _class_private_method_init(this, _set);
1032
- /**
1033
- * Fetches the manifest and source code of a snap.
1034
- *
1035
- * @param snapId - The id of the Snap.
1036
- * @param location - Source from which snap will be fetched.
1037
- * @returns A tuple of the Snap manifest object and the Snap source code.
1038
- */ _class_private_method_init(this, _fetchSnap);
1039
1048
  _class_private_method_init(this, _validateSnapPermissions);
1040
1049
  _class_private_method_init(this, _getRpcRequestHandler);
1041
1050
  _class_private_method_init(this, _triggerPhishingListUpdate);
@@ -1080,6 +1089,7 @@ class SnapController extends _basecontroller.BaseController {
1080
1089
  _class_private_method_init(this, _getRuntimeExpect);
1081
1090
  _class_private_method_init(this, _setupRuntime);
1082
1091
  _class_private_method_init(this, _calculatePermissionsChange);
1092
+ _class_private_method_init(this, _updatePermissions);
1083
1093
  _class_private_method_init(this, _isValidUpdate);
1084
1094
  /**
1085
1095
  * Call a lifecycle hook on a snap, if the snap has the
@@ -1176,7 +1186,10 @@ class SnapController extends _basecontroller.BaseController {
1176
1186
  });
1177
1187
  _class_private_method_get(this, _initializeStateMachine, initializeStateMachine).call(this);
1178
1188
  _class_private_method_get(this, _registerMessageHandlers, registerMessageHandlers).call(this);
1179
- Object.values(state?.snaps ?? {}).forEach((snap)=>_class_private_method_get(this, _setupRuntime, setupRuntime).call(this, snap.id));
1189
+ if (preinstalledSnaps) {
1190
+ _class_private_method_get(this, _handlePreinstalledSnaps, handlePreinstalledSnaps).call(this, preinstalledSnaps);
1191
+ }
1192
+ Object.values(this.state?.snaps ?? {}).forEach((snap)=>_class_private_method_get(this, _setupRuntime, setupRuntime).call(this, snap.id));
1180
1193
  }
1181
1194
  }
1182
1195
  function initializeStateMachine() {
@@ -1253,6 +1266,63 @@ function registerMessageHandlers() {
1253
1266
  this.messagingSystem.registerActionHandler(`${controllerName}:revokeDynamicPermissions`, (...args)=>this.revokeDynamicSnapPermissions(...args));
1254
1267
  this.messagingSystem.registerActionHandler(`${controllerName}:getFile`, async (...args)=>this.getSnapFile(...args));
1255
1268
  }
1269
+ function handlePreinstalledSnaps(preinstalledSnaps) {
1270
+ for (const { snapId, manifest, files, removable } of preinstalledSnaps){
1271
+ const existingSnap = this.get(snapId);
1272
+ const isAlreadyInstalled = existingSnap !== undefined;
1273
+ const isUpdate = isAlreadyInstalled && (0, _utils.gtVersion)(manifest.version, existingSnap.version);
1274
+ // Disallow downgrades and overwriting non preinstalled snaps
1275
+ if (isAlreadyInstalled && (!isUpdate || existingSnap.preinstalled !== true)) {
1276
+ continue;
1277
+ }
1278
+ const manifestFile = new _snapsutils.VirtualFile({
1279
+ path: _snapsutils.NpmSnapFileNames.Manifest,
1280
+ value: JSON.stringify(manifest),
1281
+ result: manifest
1282
+ });
1283
+ const virtualFiles = files.map(({ path, value })=>new _snapsutils.VirtualFile({
1284
+ value,
1285
+ path
1286
+ }));
1287
+ const { filePath, iconPath } = manifest.source.location.npm;
1288
+ const sourceCode = virtualFiles.find((file)=>file.path === filePath);
1289
+ const svgIcon = iconPath ? virtualFiles.find((file)=>file.path === iconPath) : undefined;
1290
+ (0, _utils.assert)(sourceCode, 'Source code not provided for preinstalled snap.');
1291
+ (0, _utils.assert)(!iconPath || iconPath && svgIcon, 'Icon not provided for preinstalled snap.');
1292
+ (0, _utils.assert)(manifest.source.files === undefined, 'Auxiliary files are not currently supported for preinstalled snaps.');
1293
+ const localizationFiles = manifest.source.locales?.map((path)=>virtualFiles.find((file)=>file.path === path)) ?? [];
1294
+ const validatedLocalizationFiles = (0, _snapsutils.getValidatedLocalizationFiles)(localizationFiles.filter(Boolean));
1295
+ (0, _utils.assert)(localizationFiles.length === validatedLocalizationFiles.length, 'Missing localization files for preinstalled snap.');
1296
+ const filesObject = {
1297
+ manifest: manifestFile,
1298
+ sourceCode,
1299
+ svgIcon,
1300
+ auxiliaryFiles: [],
1301
+ localizationFiles: validatedLocalizationFiles
1302
+ };
1303
+ // Add snap to the SnapController state
1304
+ _class_private_method_get(this, _set, set).call(this, {
1305
+ id: snapId,
1306
+ origin: 'metamask',
1307
+ files: filesObject,
1308
+ removable,
1309
+ preinstalled: true
1310
+ });
1311
+ // Setup permissions
1312
+ const processedPermissions = (0, _permissions.processSnapPermissions)(manifest.initialPermissions);
1313
+ _class_private_method_get(this, _validateSnapPermissions, validateSnapPermissions).call(this, processedPermissions);
1314
+ const { newPermissions, unusedPermissions } = _class_private_method_get(this, _calculatePermissionsChange, calculatePermissionsChange).call(this, snapId, processedPermissions);
1315
+ _class_private_method_get(this, _updatePermissions, updatePermissions).call(this, {
1316
+ snapId,
1317
+ newPermissions,
1318
+ unusedPermissions
1319
+ });
1320
+ // Set status
1321
+ this.update((state)=>{
1322
+ state.snaps[snapId].status = _snapsutils.SnapStatus.Stopped;
1323
+ });
1324
+ }
1325
+ }
1256
1326
  function pollForLastRequestStatus() {
1257
1327
  _class_private_field_set(this, _timeoutForLastRequestStatus, setTimeout(()=>{
1258
1328
  _class_private_method_get(this, _stopSnapsLastRequestPastMax, stopSnapsLastRequestPastMax).call(this).catch((error)=>{
@@ -1315,6 +1385,52 @@ async function terminateSnap(snapId) {
1315
1385
  await this.messagingSystem.call('ExecutionService:terminateSnap', snapId);
1316
1386
  this.messagingSystem.publish('SnapController:snapTerminated', this.getTruncatedExpect(snapId));
1317
1387
  }
1388
+ function handleInitialConnections(snapId, previousInitialConnections, initialConnections) {
1389
+ if (previousInitialConnections) {
1390
+ const revokedInitialConnections = (0, _utils1.setDiff)(previousInitialConnections, initialConnections);
1391
+ for (const origin of Object.keys(revokedInitialConnections)){
1392
+ this.removeSnapFromSubject(origin, snapId);
1393
+ }
1394
+ }
1395
+ for (const origin of Object.keys(initialConnections)){
1396
+ _class_private_method_get(this, _addSnapToSubject, addSnapToSubject).call(this, origin, snapId);
1397
+ }
1398
+ }
1399
+ function addSnapToSubject(origin, snapId) {
1400
+ const subjectPermissions = this.messagingSystem.call('PermissionController:getPermissions', origin);
1401
+ const existingCaveat = subjectPermissions?.[_snapsrpcmethods.WALLET_SNAP_PERMISSION_KEY]?.caveats?.find((caveat)=>caveat.type === _snapsutils.SnapCaveatType.SnapIds);
1402
+ const subjectHasSnap = Boolean((existingCaveat?.value)?.[snapId]);
1403
+ // If the subject is already connected to the snap, this is a no-op.
1404
+ if (subjectHasSnap) {
1405
+ return;
1406
+ }
1407
+ // If an existing caveat exists, we add the snap to that.
1408
+ if (existingCaveat) {
1409
+ this.messagingSystem.call('PermissionController:updateCaveat', origin, _snapsrpcmethods.WALLET_SNAP_PERMISSION_KEY, _snapsutils.SnapCaveatType.SnapIds, {
1410
+ ...existingCaveat,
1411
+ [snapId]: {}
1412
+ });
1413
+ return;
1414
+ }
1415
+ const approvedPermissions = {
1416
+ [_snapsrpcmethods.WALLET_SNAP_PERMISSION_KEY]: {
1417
+ caveats: [
1418
+ {
1419
+ type: _snapsutils.SnapCaveatType.SnapIds,
1420
+ value: {
1421
+ [snapId]: {}
1422
+ }
1423
+ }
1424
+ ]
1425
+ }
1426
+ };
1427
+ this.messagingSystem.call('PermissionController:grantPermissions', {
1428
+ approvedPermissions,
1429
+ subject: {
1430
+ origin
1431
+ }
1432
+ });
1433
+ }
1318
1434
  function removeSnapFromSubjects(snapId) {
1319
1435
  const subjects = this.messagingSystem.call('PermissionController:getSubjectNames');
1320
1436
  for (const subject of subjects){
@@ -1372,8 +1488,8 @@ async function add(args) {
1372
1488
  // If fetching and setting the snap succeeds, this property will be set
1373
1489
  // to null in the authorize() method.
1374
1490
  runtime.installPromise = (async ()=>{
1375
- const fetchedSnap = await _class_private_method_get(this, _fetchSnap, fetchSnap).call(this, snapId, location);
1376
- const manifest = fetchedSnap.files.manifest.result;
1491
+ const fetchedSnap = await (0, _utils1.fetchSnap)(snapId, location);
1492
+ const manifest = fetchedSnap.manifest.result;
1377
1493
  if (!(0, _utils.satisfiesVersionRange)(manifest.version, versionRange)) {
1378
1494
  throw new Error(`Version mismatch. Manifest for "${snapId}" specifies version "${manifest.version}" which doesn't satisfy requested version range "${versionRange}".`);
1379
1495
  }
@@ -1383,7 +1499,7 @@ async function add(args) {
1383
1499
  });
1384
1500
  return _class_private_method_get(this, _set, set).call(this, {
1385
1501
  ...args,
1386
- ...fetchedSnap,
1502
+ files: fetchedSnap,
1387
1503
  id: snapId
1388
1504
  });
1389
1505
  })();
@@ -1446,10 +1562,10 @@ async function getEndowments(snapId) {
1446
1562
  return dedupedEndowments;
1447
1563
  }
1448
1564
  function set(args) {
1449
- const { id: snapId, origin, files, isUpdate = false } = args;
1565
+ const { id: snapId, origin, files, isUpdate = false, removable, preinstalled } = args;
1450
1566
  const { manifest, sourceCode: sourceCodeFile, svgIcon, auxiliaryFiles: rawAuxiliaryFiles, localizationFiles } = files;
1451
1567
  (0, _snapsutils.assertIsSnapManifest)(manifest.result);
1452
- const { version } = manifest.result;
1568
+ const { version, proposedName } = manifest.result;
1453
1569
  const sourceCode = sourceCodeFile.toString();
1454
1570
  (0, _utils.assert)(typeof sourceCode === 'string' && sourceCode.length > 0, `Invalid source code for snap "${snapId}".`);
1455
1571
  const auxiliaryFiles = rawAuxiliaryFiles.map((file)=>{
@@ -1477,6 +1593,8 @@ function set(args) {
1477
1593
  // previous state.
1478
1594
  blocked: false,
1479
1595
  enabled: true,
1596
+ removable,
1597
+ preinstalled,
1480
1598
  id: snapId,
1481
1599
  initialPermissions: manifest.result.initialPermissions,
1482
1600
  manifest: manifest.result,
@@ -1501,42 +1619,18 @@ function set(args) {
1501
1619
  rollbackSnapshot.statePatches = inversePatches;
1502
1620
  }
1503
1621
  }
1504
- this.messagingSystem.publish(`SnapController:snapAdded`, snap, svgIcon?.toString());
1622
+ this.messagingSystem.call('SubjectMetadataController:addSubjectMetadata', {
1623
+ subjectType: _permissioncontroller.SubjectType.Snap,
1624
+ name: proposedName,
1625
+ origin: snap.id,
1626
+ version,
1627
+ svgIcon: svgIcon?.toString() ?? null
1628
+ });
1505
1629
  return {
1506
1630
  ...snap,
1507
1631
  sourceCode
1508
1632
  };
1509
1633
  }
1510
- async function fetchSnap(snapId, location) {
1511
- try {
1512
- const manifest = await location.manifest();
1513
- const sourceCode = await location.fetch(manifest.result.source.location.npm.filePath);
1514
- const { iconPath } = manifest.result.source.location.npm;
1515
- const svgIcon = iconPath ? await location.fetch(iconPath) : undefined;
1516
- const auxiliaryFiles = await (0, _utils1.getSnapFiles)(location, manifest.result.source.files);
1517
- await Promise.all(auxiliaryFiles.map(async (file)=>{
1518
- // This should still be safe
1519
- // eslint-disable-next-line require-atomic-updates
1520
- file.data.base64 = await (0, _snapsutils.encodeBase64)(file);
1521
- }));
1522
- const localizationFiles = await (0, _utils1.getSnapFiles)(location, manifest.result.source.locales);
1523
- const validatedLocalizationFiles = (0, _snapsutils.getValidatedLocalizationFiles)(localizationFiles);
1524
- const files = {
1525
- manifest,
1526
- sourceCode,
1527
- svgIcon,
1528
- auxiliaryFiles,
1529
- localizationFiles: validatedLocalizationFiles
1530
- };
1531
- await (0, _snapsutils.validateFetchedSnap)(files);
1532
- return {
1533
- files,
1534
- location
1535
- };
1536
- } catch (error) {
1537
- throw new Error(`Failed to fetch snap "${snapId}": ${(0, _snapssdk.getErrorMessage)(error)}.`);
1538
- }
1539
- }
1540
1634
  function validateSnapPermissions(processedPermissions) {
1541
1635
  const permissionKeys = Object.keys(processedPermissions);
1542
1636
  const handlerPermissions = Array.from(new Set(Object.values(_endowments.handlerEndowments)));
@@ -1629,6 +1723,17 @@ async function assertSnapRpcRequestResult(handlerType, result) {
1629
1723
  (0, _snapsutils.validateComponentLinks)(result.content, _class_private_method_get(this, _checkPhishingList, checkPhishingList).bind(this));
1630
1724
  break;
1631
1725
  }
1726
+ case _snapsutils.HandlerType.OnSignature:
1727
+ {
1728
+ (0, _utils.assertStruct)(result, _snapsutils.OnSignatureResponseStruct);
1729
+ // Null is an allowed return value here
1730
+ if (result === null) {
1731
+ return;
1732
+ }
1733
+ await _class_private_method_get(this, _triggerPhishingListUpdate, triggerPhishingListUpdate).call(this);
1734
+ (0, _snapsutils.validateComponentLinks)(result.content, _class_private_method_get(this, _checkPhishingList, checkPhishingList).bind(this));
1735
+ break;
1736
+ }
1632
1737
  case _snapsutils.HandlerType.OnHomePage:
1633
1738
  (0, _utils.assertStruct)(result, _snapsutils.OnHomePageResponseStruct);
1634
1739
  await _class_private_method_get(this, _triggerPhishingListUpdate, triggerPhishingListUpdate).call(this);
@@ -1667,11 +1772,7 @@ function createRollbackSnapshot(snapId) {
1667
1772
  (0, _utils.assert)(_class_private_field_get(this, _rollbackSnapshots).get(snapId) === undefined, new Error(`Snap "${snapId}" rollback snapshot already exists.`));
1668
1773
  _class_private_field_get(this, _rollbackSnapshots).set(snapId, {
1669
1774
  statePatches: [],
1670
- permissions: {
1671
- revoked: null,
1672
- granted: [],
1673
- requestData: null
1674
- },
1775
+ permissions: {},
1675
1776
  newVersion: ''
1676
1777
  });
1677
1778
  const newRollbackSnapshot = _class_private_field_get(this, _rollbackSnapshots).get(snapId);
@@ -1699,20 +1800,12 @@ async function rollbackSnap(snapId) {
1699
1800
  state.snaps[snapId].status = _snapsutils.SnapStatus.Stopped;
1700
1801
  });
1701
1802
  }
1702
- if (permissions.revoked && Object.keys(permissions.revoked).length) {
1703
- this.messagingSystem.call('PermissionController:grantPermissions', {
1704
- approvedPermissions: permissions.revoked,
1705
- subject: {
1706
- origin: snapId
1707
- },
1708
- requestData: permissions.requestData
1709
- });
1710
- }
1711
- if (permissions.granted?.length) {
1712
- this.messagingSystem.call('PermissionController:revokePermissions', {
1713
- [snapId]: permissions.granted
1714
- });
1715
- }
1803
+ _class_private_method_get(this, _updatePermissions, updatePermissions).call(this, {
1804
+ snapId,
1805
+ unusedPermissions: permissions.granted,
1806
+ newPermissions: permissions.revoked,
1807
+ requestData: permissions.requestData
1808
+ });
1716
1809
  const truncatedSnap = this.getTruncatedExpect(snapId);
1717
1810
  this.messagingSystem.publish('SnapController:snapRolledback', truncatedSnap, rollbackSnapshot.newVersion);
1718
1811
  _class_private_field_get(this, _rollbackSnapshots).delete(snapId);
@@ -1768,6 +1861,23 @@ function calculatePermissionsChange(snapId, desiredPermissionsSet) {
1768
1861
  approvedPermissions
1769
1862
  };
1770
1863
  }
1864
+ function updatePermissions({ snapId, unusedPermissions = {}, newPermissions = {}, requestData }) {
1865
+ const unusedPermissionsKeys = Object.keys(unusedPermissions);
1866
+ if ((0, _utils.isNonEmptyArray)(unusedPermissionsKeys)) {
1867
+ this.messagingSystem.call('PermissionController:revokePermissions', {
1868
+ [snapId]: unusedPermissionsKeys
1869
+ });
1870
+ }
1871
+ if ((0, _utils.isNonEmptyArray)(Object.keys(newPermissions))) {
1872
+ this.messagingSystem.call('PermissionController:grantPermissions', {
1873
+ approvedPermissions: newPermissions,
1874
+ subject: {
1875
+ origin: snapId
1876
+ },
1877
+ requestData
1878
+ });
1879
+ }
1880
+ }
1771
1881
  function isValidUpdate(snapId, newVersionRange) {
1772
1882
  const existingSnap = this.getExpect(snapId);
1773
1883
  if ((0, _utils.satisfiesVersionRange)(existingSnap.version, newVersionRange)) {