@metamask/snaps-controllers 4.0.0 → 5.0.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 (163) hide show
  1. package/CHANGELOG.md +33 -1
  2. package/dist/cjs/cronjob/CronjobController.js +3 -3
  3. package/dist/cjs/cronjob/CronjobController.js.map +1 -1
  4. package/dist/cjs/index.js +1 -0
  5. package/dist/cjs/index.js.map +1 -1
  6. package/dist/cjs/interface/SnapInterfaceController.js +166 -0
  7. package/dist/cjs/interface/SnapInterfaceController.js.map +1 -0
  8. package/dist/cjs/interface/index.js +20 -0
  9. package/dist/cjs/interface/index.js.map +1 -0
  10. package/dist/cjs/interface/utils.js +59 -0
  11. package/dist/cjs/interface/utils.js.map +1 -0
  12. package/dist/cjs/services/ProxyPostMessageStream.js +3 -10
  13. package/dist/cjs/services/ProxyPostMessageStream.js.map +1 -1
  14. package/dist/cjs/services/browser.js +1 -0
  15. package/dist/cjs/services/browser.js.map +1 -1
  16. package/dist/cjs/services/index.js +1 -0
  17. package/dist/cjs/services/index.js.map +1 -1
  18. package/dist/cjs/services/node/NodeProcessExecutionService.js +13 -1
  19. package/dist/cjs/services/node/NodeProcessExecutionService.js.map +1 -1
  20. package/dist/cjs/services/node/NodeThreadExecutionService.js +14 -1
  21. package/dist/cjs/services/node/NodeThreadExecutionService.js.map +1 -1
  22. package/dist/cjs/services/offscreen/OffscreenExecutionService.js +36 -99
  23. package/dist/cjs/services/offscreen/OffscreenExecutionService.js.map +1 -1
  24. package/dist/cjs/services/proxy/ProxyExecutionService.js +110 -0
  25. package/dist/cjs/services/proxy/ProxyExecutionService.js.map +1 -0
  26. package/dist/cjs/services/webview/WebViewExecutionService.js +99 -0
  27. package/dist/cjs/services/webview/WebViewExecutionService.js.map +1 -0
  28. package/dist/cjs/services/webview/WebViewMessageStream.js +127 -0
  29. package/dist/cjs/services/webview/WebViewMessageStream.js.map +1 -0
  30. package/dist/cjs/services/webview/index.js +20 -0
  31. package/dist/cjs/services/webview/index.js.map +1 -0
  32. package/dist/cjs/snaps/SnapController.js +303 -138
  33. package/dist/cjs/snaps/SnapController.js.map +1 -1
  34. package/dist/cjs/snaps/constants.js +25 -0
  35. package/dist/cjs/snaps/constants.js.map +1 -0
  36. package/dist/cjs/snaps/index.js +0 -2
  37. package/dist/cjs/snaps/index.js.map +1 -1
  38. package/dist/cjs/snaps/location/npm.js +13 -2
  39. package/dist/cjs/snaps/location/npm.js.map +1 -1
  40. package/dist/cjs/utils.js +32 -0
  41. package/dist/cjs/utils.js.map +1 -1
  42. package/dist/esm/cronjob/CronjobController.js +2 -2
  43. package/dist/esm/cronjob/CronjobController.js.map +1 -1
  44. package/dist/esm/index.js +1 -0
  45. package/dist/esm/index.js.map +1 -1
  46. package/dist/esm/interface/SnapInterfaceController.js +158 -0
  47. package/dist/esm/interface/SnapInterfaceController.js.map +1 -0
  48. package/dist/esm/interface/index.js +3 -0
  49. package/dist/esm/interface/index.js.map +1 -0
  50. package/dist/esm/interface/utils.js +62 -0
  51. package/dist/esm/interface/utils.js.map +1 -0
  52. package/dist/esm/services/ProxyPostMessageStream.js +3 -10
  53. package/dist/esm/services/ProxyPostMessageStream.js.map +1 -1
  54. package/dist/esm/services/browser.js +1 -0
  55. package/dist/esm/services/browser.js.map +1 -1
  56. package/dist/esm/services/index.js +1 -0
  57. package/dist/esm/services/index.js.map +1 -1
  58. package/dist/esm/services/node/NodeProcessExecutionService.js +13 -1
  59. package/dist/esm/services/node/NodeProcessExecutionService.js.map +1 -1
  60. package/dist/esm/services/node/NodeThreadExecutionService.js +14 -1
  61. package/dist/esm/services/node/NodeThreadExecutionService.js.map +1 -1
  62. package/dist/esm/services/offscreen/OffscreenExecutionService.js +36 -99
  63. package/dist/esm/services/offscreen/OffscreenExecutionService.js.map +1 -1
  64. package/dist/esm/services/proxy/ProxyExecutionService.js +100 -0
  65. package/dist/esm/services/proxy/ProxyExecutionService.js.map +1 -0
  66. package/dist/esm/services/webview/WebViewExecutionService.js +89 -0
  67. package/dist/esm/services/webview/WebViewExecutionService.js.map +1 -0
  68. package/dist/esm/services/webview/WebViewMessageStream.js +119 -0
  69. package/dist/esm/services/webview/WebViewMessageStream.js.map +1 -0
  70. package/dist/esm/services/webview/index.js +3 -0
  71. package/dist/esm/services/webview/index.js.map +1 -0
  72. package/dist/esm/snaps/SnapController.js +299 -134
  73. package/dist/esm/snaps/SnapController.js.map +1 -1
  74. package/dist/esm/snaps/constants.js +16 -0
  75. package/dist/esm/snaps/constants.js.map +1 -0
  76. package/dist/esm/snaps/index.js +0 -2
  77. package/dist/esm/snaps/index.js.map +1 -1
  78. package/dist/esm/snaps/location/npm.js +13 -2
  79. package/dist/esm/snaps/location/npm.js.map +1 -1
  80. package/dist/esm/utils.js +37 -0
  81. package/dist/esm/utils.js.map +1 -1
  82. package/dist/types/index.d.ts +1 -0
  83. package/dist/types/interface/SnapInterfaceController.d.ts +85 -0
  84. package/dist/types/interface/index.d.ts +1 -0
  85. package/dist/types/interface/utils.d.ts +36 -0
  86. package/dist/types/services/ProxyPostMessageStream.d.ts +1 -2
  87. package/dist/types/services/browser.d.ts +1 -0
  88. package/dist/types/services/index.d.ts +1 -0
  89. package/dist/types/services/offscreen/OffscreenExecutionService.d.ts +5 -22
  90. package/dist/types/services/proxy/ProxyExecutionService.d.ts +39 -0
  91. package/dist/types/services/webview/WebViewExecutionService.d.ts +20 -0
  92. package/dist/types/services/webview/WebViewMessageStream.d.ts +32 -0
  93. package/dist/types/services/webview/index.d.ts +1 -0
  94. package/dist/types/snaps/SnapController.d.ts +37 -6
  95. package/dist/types/snaps/constants.d.ts +1 -0
  96. package/dist/types/snaps/index.d.ts +0 -2
  97. package/dist/types/utils.d.ts +119 -0
  98. package/package.json +14 -14
  99. package/dist/cjs/snaps/endowments/cronjob.js +0 -100
  100. package/dist/cjs/snaps/endowments/cronjob.js.map +0 -1
  101. package/dist/cjs/snaps/endowments/enum.js +0 -25
  102. package/dist/cjs/snaps/endowments/enum.js.map +0 -1
  103. package/dist/cjs/snaps/endowments/ethereum-provider.js +0 -43
  104. package/dist/cjs/snaps/endowments/ethereum-provider.js.map +0 -1
  105. package/dist/cjs/snaps/endowments/home-page.js +0 -37
  106. package/dist/cjs/snaps/endowments/home-page.js.map +0 -1
  107. package/dist/cjs/snaps/endowments/index.js +0 -99
  108. package/dist/cjs/snaps/endowments/index.js.map +0 -1
  109. package/dist/cjs/snaps/endowments/keyring.js +0 -100
  110. package/dist/cjs/snaps/endowments/keyring.js.map +0 -1
  111. package/dist/cjs/snaps/endowments/lifecycle-hooks.js +0 -37
  112. package/dist/cjs/snaps/endowments/lifecycle-hooks.js.map +0 -1
  113. package/dist/cjs/snaps/endowments/name-lookup.js +0 -106
  114. package/dist/cjs/snaps/endowments/name-lookup.js.map +0 -1
  115. package/dist/cjs/snaps/endowments/network-access.js +0 -44
  116. package/dist/cjs/snaps/endowments/network-access.js.map +0 -1
  117. package/dist/cjs/snaps/endowments/rpc.js +0 -99
  118. package/dist/cjs/snaps/endowments/rpc.js.map +0 -1
  119. package/dist/cjs/snaps/endowments/transaction-insight.js +0 -106
  120. package/dist/cjs/snaps/endowments/transaction-insight.js.map +0 -1
  121. package/dist/cjs/snaps/endowments/web-assembly.js +0 -42
  122. package/dist/cjs/snaps/endowments/web-assembly.js.map +0 -1
  123. package/dist/cjs/snaps/permissions.js +0 -61
  124. package/dist/cjs/snaps/permissions.js.map +0 -1
  125. package/dist/esm/snaps/endowments/cronjob.js +0 -99
  126. package/dist/esm/snaps/endowments/cronjob.js.map +0 -1
  127. package/dist/esm/snaps/endowments/enum.js +0 -15
  128. package/dist/esm/snaps/endowments/enum.js.map +0 -1
  129. package/dist/esm/snaps/endowments/ethereum-provider.js +0 -33
  130. package/dist/esm/snaps/endowments/ethereum-provider.js.map +0 -1
  131. package/dist/esm/snaps/endowments/home-page.js +0 -27
  132. package/dist/esm/snaps/endowments/home-page.js.map +0 -1
  133. package/dist/esm/snaps/endowments/index.js +0 -54
  134. package/dist/esm/snaps/endowments/index.js.map +0 -1
  135. package/dist/esm/snaps/endowments/keyring.js +0 -91
  136. package/dist/esm/snaps/endowments/keyring.js.map +0 -1
  137. package/dist/esm/snaps/endowments/lifecycle-hooks.js +0 -27
  138. package/dist/esm/snaps/endowments/lifecycle-hooks.js.map +0 -1
  139. package/dist/esm/snaps/endowments/name-lookup.js +0 -98
  140. package/dist/esm/snaps/endowments/name-lookup.js.map +0 -1
  141. package/dist/esm/snaps/endowments/network-access.js +0 -34
  142. package/dist/esm/snaps/endowments/network-access.js.map +0 -1
  143. package/dist/esm/snaps/endowments/rpc.js +0 -88
  144. package/dist/esm/snaps/endowments/rpc.js.map +0 -1
  145. package/dist/esm/snaps/endowments/transaction-insight.js +0 -99
  146. package/dist/esm/snaps/endowments/transaction-insight.js.map +0 -1
  147. package/dist/esm/snaps/endowments/web-assembly.js +0 -32
  148. package/dist/esm/snaps/endowments/web-assembly.js.map +0 -1
  149. package/dist/esm/snaps/permissions.js +0 -50
  150. package/dist/esm/snaps/permissions.js.map +0 -1
  151. package/dist/types/snaps/endowments/cronjob.d.ts +0 -51
  152. package/dist/types/snaps/endowments/enum.d.ts +0 -12
  153. package/dist/types/snaps/endowments/ethereum-provider.d.ts +0 -14
  154. package/dist/types/snaps/endowments/home-page.d.ts +0 -15
  155. package/dist/types/snaps/endowments/index.d.ts +0 -115
  156. package/dist/types/snaps/endowments/keyring.d.ts +0 -39
  157. package/dist/types/snaps/endowments/lifecycle-hooks.d.ts +0 -15
  158. package/dist/types/snaps/endowments/name-lookup.d.ts +0 -38
  159. package/dist/types/snaps/endowments/network-access.d.ts +0 -14
  160. package/dist/types/snaps/endowments/rpc.d.ts +0 -38
  161. package/dist/types/snaps/endowments/transaction-insight.d.ts +0 -39
  162. package/dist/types/snaps/endowments/web-assembly.d.ts +0 -14
  163. package/dist/types/snaps/permissions.d.ts +0 -16
@@ -37,11 +37,8 @@ const _nanoid = require("nanoid");
37
37
  const _fsm1 = require("../fsm");
38
38
  const _logging = require("../logging");
39
39
  const _utils1 = require("../utils");
40
- const _endowments = require("./endowments");
41
- const _keyring = require("./endowments/keyring");
42
- const _rpc = require("./endowments/rpc");
40
+ const _constants = require("./constants");
43
41
  const _location = require("./location");
44
- const _permissions = require("./permissions");
45
42
  const _registry = require("./registry");
46
43
  const _RequestQueue = require("./RequestQueue");
47
44
  const _Timer = require("./Timer");
@@ -151,7 +148,7 @@ var _closeAllConnections = /*#__PURE__*/ new WeakMap(), _dynamicPermissions = /*
151
148
  _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
152
149
  * Constructor helper for registering the controller's messaging system
153
150
  * actions.
154
- */ _registerMessageHandlers = /*#__PURE__*/ new WeakSet(), _pollForLastRequestStatus = /*#__PURE__*/ new WeakSet(), _blockSnap = /*#__PURE__*/ new WeakSet(), /**
151
+ */ _registerMessageHandlers = /*#__PURE__*/ new WeakSet(), _handlePreinstalledSnaps = /*#__PURE__*/ new WeakSet(), _pollForLastRequestStatus = /*#__PURE__*/ new WeakSet(), _blockSnap = /*#__PURE__*/ new WeakSet(), /**
155
152
  * Unblocks a snap so that it can be enabled and started again. Emits
156
153
  * {@link SnapUnblocked}. Does nothing if the snap is not installed or already
157
154
  * unblocked.
@@ -168,7 +165,7 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
168
165
  *
169
166
  * @param snapId - The id of the snap to transition.
170
167
  * @param event - The event enum to use to transition.
171
- */ _transition = /*#__PURE__*/ new WeakSet(), _terminateSnap = /*#__PURE__*/ new WeakSet(), /**
168
+ */ _transition = /*#__PURE__*/ new WeakSet(), _terminateSnap = /*#__PURE__*/ new WeakSet(), _handleInitialConnections = /*#__PURE__*/ new WeakSet(), _addSnapToSubject = /*#__PURE__*/ new WeakSet(), /**
172
169
  * Removes a snap's permission (caveat) from all subjects.
173
170
  *
174
171
  * @param snapId - The id of the Snap.
@@ -189,12 +186,20 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
189
186
  *
190
187
  * @param args - The add snap args.
191
188
  * @returns The resulting snap object.
192
- */ _set = /*#__PURE__*/ new WeakSet(), _fetchSnap = /*#__PURE__*/ new WeakSet(), _validateSnapPermissions = /*#__PURE__*/ new WeakSet(), /**
189
+ */ _set = /*#__PURE__*/ new WeakSet(), _validateSnapPermissions = /*#__PURE__*/ new WeakSet(), /**
190
+ * Determine the execution timeout for a given handler permission.
191
+ *
192
+ * If no permission is specified or the permission itself has no execution timeout defined
193
+ * the constructor argument `maxRequestTime` will be used.
194
+ *
195
+ * @param permission - An optional permission constraint for the handler being called.
196
+ * @returns The execution timeout for the given handler.
197
+ */ _getExecutionTimeout = /*#__PURE__*/ new WeakSet(), /**
193
198
  * Gets the RPC message handler for the given snap.
194
199
  *
195
200
  * @param snapId - The id of the Snap whose message handler to get.
196
201
  * @returns The RPC handler for the given snap.
197
- */ _getRpcRequestHandler = /*#__PURE__*/ new WeakSet(), _triggerPhishingListUpdate = /*#__PURE__*/ new WeakSet(), _checkPhishingList = /*#__PURE__*/ new WeakSet(), _assertSnapRpcRequestResult = /*#__PURE__*/ new WeakSet(), _executeWithTimeout = /*#__PURE__*/ new WeakSet(), _recordSnapRpcRequestStart = /*#__PURE__*/ new WeakSet(), _recordSnapRpcRequestFinish = /*#__PURE__*/ new WeakSet(), /**
202
+ */ _getRpcRequestHandler = /*#__PURE__*/ new WeakSet(), _createInterface = /*#__PURE__*/ new WeakSet(), _assertInterfaceExists = /*#__PURE__*/ new WeakSet(), _transformSnapRpcRequestResult = /*#__PURE__*/ new WeakSet(), _assertSnapRpcRequestResult = /*#__PURE__*/ new WeakSet(), _executeWithTimeout = /*#__PURE__*/ new WeakSet(), _recordSnapRpcRequestStart = /*#__PURE__*/ new WeakSet(), _recordSnapRpcRequestFinish = /*#__PURE__*/ new WeakSet(), /**
198
203
  * Retrieves the rollback snapshot of a snap.
199
204
  *
200
205
  * @param snapId - The snap id.
@@ -207,6 +212,16 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
207
212
  * @throws {@link Error}. If the snap exists before creation or if creation fails.
208
213
  * @returns A `RollbackSnapshot`.
209
214
  */ _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(), /**
215
+ * Updates the permissions for a snap following an install, update or rollback.
216
+ *
217
+ * Grants newly requested permissions and revokes unused/revoked permissions.
218
+ *
219
+ * @param args - An options bag.
220
+ * @param args.snapId - The snap ID.
221
+ * @param args.newPermissions - New permissions to be granted.
222
+ * @param args.unusedPermissions - Unused permissions to be revoked.
223
+ * @param args.requestData - Optional request data from an approval.
224
+ */ _updatePermissions = /*#__PURE__*/ new WeakSet(), /**
210
225
  * Checks if a snap will pass version validation checks
211
226
  * with the new version range that is requested. The first
212
227
  * check that is done is to check if the existing snap version
@@ -492,6 +507,10 @@ class SnapController extends _basecontroller.BaseController {
492
507
  if (!Array.isArray(snapIds)) {
493
508
  throw new Error('Expected array of snap ids.');
494
509
  }
510
+ snapIds.forEach((snapId)=>{
511
+ const snap = this.getExpect(snapId);
512
+ (0, _utils.assert)(snap.removable !== false, `${snapId} is not removable.`);
513
+ });
495
514
  await Promise.all(snapIds.map(async (snapId)=>{
496
515
  const snap = this.getExpect(snapId);
497
516
  const truncated = this.getTruncatedExpect(snapId);
@@ -685,6 +704,7 @@ class SnapController extends _basecontroller.BaseController {
685
704
  snapId,
686
705
  type: SNAP_APPROVAL_INSTALL
687
706
  });
707
+ this.messagingSystem.publish('SnapController:snapInstallStarted', snapId, origin, false);
688
708
  // Existing snaps must be stopped before overwriting
689
709
  if (existingSnap && this.isRunning(snapId)) {
690
710
  await this.stopSnap(snapId, _snapsutils.SnapStatusEvents.Stop);
@@ -718,11 +738,13 @@ class SnapController extends _basecontroller.BaseController {
718
738
  return truncated;
719
739
  } catch (error) {
720
740
  (0, _snapsutils.logError)(`Error when adding ${snapId}.`, error);
741
+ const errorString = error instanceof Error ? error.message : error.toString();
721
742
  _class_private_method_get(this, _updateApproval, updateApproval).call(this, pendingApproval.id, {
722
743
  loading: false,
723
744
  type: SNAP_APPROVAL_INSTALL,
724
- error: error instanceof Error ? error.message : error.toString()
745
+ error: errorString
725
746
  });
747
+ this.messagingSystem.publish('SnapController:snapInstallFailed', snapId, origin, false, errorString);
726
748
  throw error;
727
749
  }
728
750
  }
@@ -754,9 +776,11 @@ class SnapController extends _basecontroller.BaseController {
754
776
  type: SNAP_APPROVAL_UPDATE
755
777
  });
756
778
  try {
779
+ this.messagingSystem.publish('SnapController:snapInstallStarted', snapId, origin, true);
757
780
  const snap = this.getExpect(snapId);
758
- const newSnap = await _class_private_method_get(this, _fetchSnap, fetchSnap).call(this, snapId, location);
759
- const { sourceCode: sourceCodeFile, manifest: manifestFile } = newSnap.files;
781
+ const oldManifest = snap.manifest;
782
+ const newSnap = await (0, _utils1.fetchSnap)(snapId, location);
783
+ const { sourceCode: sourceCodeFile, manifest: manifestFile } = newSnap;
760
784
  const manifest = manifestFile.result;
761
785
  const newVersion = manifest.version;
762
786
  if (!(0, _utils.gtVersion)(newVersion, snap.version)) {
@@ -767,9 +791,10 @@ class SnapController extends _basecontroller.BaseController {
767
791
  }
768
792
  await _class_private_method_get(this, _assertIsInstallAllowed, assertIsInstallAllowed).call(this, snapId, {
769
793
  version: newVersion,
770
- checksum: manifest.source.shasum
794
+ checksum: manifest.source.shasum,
795
+ permissions: manifest.initialPermissions
771
796
  });
772
- const processedPermissions = (0, _permissions.processSnapPermissions)(manifest.initialPermissions);
797
+ const processedPermissions = (0, _snapsrpcmethods.processSnapPermissions)(manifest.initialPermissions);
773
798
  _class_private_method_get(this, _validateSnapPermissions, validateSnapPermissions).call(this, processedPermissions);
774
799
  const { newPermissions, unusedPermissions, approvedPermissions } = _class_private_method_get(this, _calculatePermissionsChange, calculatePermissionsChange).call(this, snapId, processedPermissions);
775
800
  _class_private_method_get(this, _updateApproval, updateApproval).call(this, pendingApproval.id, {
@@ -793,28 +818,22 @@ class SnapController extends _basecontroller.BaseController {
793
818
  _class_private_method_get(this, _set, set).call(this, {
794
819
  origin,
795
820
  id: snapId,
796
- files: newSnap.files,
821
+ files: newSnap,
797
822
  isUpdate: true
798
823
  });
799
- const unusedPermissionsKeys = Object.keys(unusedPermissions);
800
- if ((0, _utils.isNonEmptyArray)(unusedPermissionsKeys)) {
801
- this.messagingSystem.call('PermissionController:revokePermissions', {
802
- [snapId]: unusedPermissionsKeys
803
- });
804
- }
805
- if ((0, _utils.isNonEmptyArray)(Object.keys(approvedNewPermissions))) {
806
- this.messagingSystem.call('PermissionController:grantPermissions', {
807
- approvedPermissions: approvedNewPermissions,
808
- subject: {
809
- origin: snapId
810
- },
811
- requestData
812
- });
824
+ _class_private_method_get(this, _updatePermissions, updatePermissions).call(this, {
825
+ snapId,
826
+ unusedPermissions,
827
+ newPermissions: approvedNewPermissions,
828
+ requestData
829
+ });
830
+ if (manifest.initialConnections) {
831
+ _class_private_method_get(this, _handleInitialConnections, handleInitialConnections).call(this, snapId, oldManifest.initialConnections ?? null, manifest.initialConnections);
813
832
  }
814
833
  const rollbackSnapshot = _class_private_method_get(this, _getRollbackSnapshot, getRollbackSnapshot).call(this, snapId);
815
834
  if (rollbackSnapshot !== undefined) {
816
835
  rollbackSnapshot.permissions.revoked = unusedPermissions;
817
- rollbackSnapshot.permissions.granted = Object.keys(approvedNewPermissions);
836
+ rollbackSnapshot.permissions.granted = approvedNewPermissions;
818
837
  rollbackSnapshot.permissions.requestData = requestData;
819
838
  }
820
839
  const sourceCode = sourceCodeFile.toString();
@@ -838,11 +857,13 @@ class SnapController extends _basecontroller.BaseController {
838
857
  return truncatedSnap;
839
858
  } catch (error) {
840
859
  (0, _snapsutils.logError)(`Error when updating ${snapId},`, error);
860
+ const errorString = error instanceof Error ? error.message : error.toString();
841
861
  _class_private_method_get(this, _updateApproval, updateApproval).call(this, pendingApproval.id, {
842
862
  loading: false,
843
- error: error instanceof Error ? error.message : error.toString(),
863
+ error: errorString,
844
864
  type: SNAP_APPROVAL_UPDATE
845
865
  });
866
+ this.messagingSystem.publish('SnapController:snapInstallFailed', snapId, origin, true, errorString);
846
867
  throw error;
847
868
  }
848
869
  }
@@ -870,21 +891,20 @@ class SnapController extends _basecontroller.BaseController {
870
891
  const snap = snapsState[snapId];
871
892
  const { initialPermissions } = snap;
872
893
  try {
873
- const processedPermissions = (0, _permissions.processSnapPermissions)(initialPermissions);
894
+ const processedPermissions = (0, _snapsrpcmethods.processSnapPermissions)(initialPermissions);
874
895
  _class_private_method_get(this, _validateSnapPermissions, validateSnapPermissions).call(this, processedPermissions);
875
896
  _class_private_method_get(this, _updateApproval, updateApproval).call(this, pendingApproval.id, {
876
897
  loading: false,
877
898
  permissions: processedPermissions
878
899
  });
879
900
  const { permissions: approvedPermissions, ...requestData } = await pendingApproval.promise;
880
- if ((0, _utils.isNonEmptyArray)(Object.keys(approvedPermissions))) {
881
- this.messagingSystem.call('PermissionController:grantPermissions', {
882
- approvedPermissions,
883
- subject: {
884
- origin: snapId
885
- },
886
- requestData
887
- });
901
+ _class_private_method_get(this, _updatePermissions, updatePermissions).call(this, {
902
+ snapId,
903
+ newPermissions: approvedPermissions,
904
+ requestData
905
+ });
906
+ if (snap.manifest.initialConnections) {
907
+ _class_private_method_get(this, _handleInitialConnections, handleInitialConnections).call(this, snapId, null, snap.manifest.initialConnections);
888
908
  }
889
909
  } finally{
890
910
  const runtime = _class_private_method_get(this, _getRuntimeExpect, getRuntimeExpect).call(this, snapId);
@@ -918,35 +938,38 @@ class SnapController extends _basecontroller.BaseController {
918
938
  ...rawRequest
919
939
  };
920
940
  (0, _utils.assertIsJsonRpcRequest)(request);
921
- const permissionName = _endowments.handlerEndowments[handlerType];
922
- const hasPermission = this.messagingSystem.call('PermissionController:hasPermission', snapId, permissionName);
923
- if (!hasPermission) {
941
+ const permissionName = _snapsrpcmethods.handlerEndowments[handlerType];
942
+ (0, _utils.assert)(typeof permissionName === 'string' || permissionName === null, "'permissionName' must be either a string or null.");
943
+ const permissions = this.messagingSystem.call('PermissionController:getPermissions', snapId);
944
+ // If permissionName is null, the handler does not require a permission.
945
+ if (permissionName !== null && (!permissions || !(0, _utils.hasProperty)(permissions, permissionName))) {
924
946
  throw new Error(`Snap "${snapId}" is not permitted to use "${permissionName}".`);
925
947
  }
926
- if (permissionName === _endowments.SnapEndowments.Rpc || permissionName === _endowments.SnapEndowments.Keyring) {
927
- const subject = this.messagingSystem.call('SubjectMetadataController:getSubjectMetadata', origin);
928
- const permissions = this.messagingSystem.call('PermissionController:getPermissions', snapId);
929
- const handlerPermissions = permissions?.[permissionName];
948
+ const handlerPermissions = permissionName ? permissions[permissionName] : undefined;
949
+ if (permissionName === _snapsrpcmethods.SnapEndowments.Rpc || permissionName === _snapsrpcmethods.SnapEndowments.Keyring) {
930
950
  (0, _utils.assert)(handlerPermissions);
931
- const origins = permissionName === _endowments.SnapEndowments.Rpc ? (0, _rpc.getRpcCaveatOrigins)(handlerPermissions) : (0, _keyring.getKeyringCaveatOrigins)(handlerPermissions);
951
+ const subject = this.messagingSystem.call('SubjectMetadataController:getSubjectMetadata', origin);
952
+ const origins = permissionName === _snapsrpcmethods.SnapEndowments.Rpc ? (0, _snapsrpcmethods.getRpcCaveatOrigins)(handlerPermissions) : (0, _snapsrpcmethods.getKeyringCaveatOrigins)(handlerPermissions);
932
953
  (0, _utils.assert)(origins);
933
954
  if (!(0, _snapsutils.isOriginAllowed)(origins, subject?.subjectType ?? _permissioncontroller.SubjectType.Website, origin)) {
934
955
  throw new Error(`Snap "${snapId}" is not permitted to handle requests from "${origin}".`);
935
956
  }
936
957
  }
937
- const handler = await _class_private_method_get(this, _getRpcRequestHandler, getRpcRequestHandler).call(this, snapId);
958
+ const handler = _class_private_method_get(this, _getRpcRequestHandler, getRpcRequestHandler).call(this, snapId);
938
959
  if (!handler) {
939
960
  throw new Error(`Snap RPC message handler not found for snap "${snapId}".`);
940
961
  }
962
+ const timeout = _class_private_method_get(this, _getExecutionTimeout, getExecutionTimeout).call(this, handlerPermissions);
941
963
  return handler({
942
964
  origin,
943
965
  handler: handlerType,
944
- request
966
+ request,
967
+ timeout
945
968
  });
946
969
  }
947
970
  constructor({ closeAllConnections, messenger, state, dynamicPermissions = [
948
971
  'eth_accounts'
949
- ], 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 }){
972
+ ], 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 }){
950
973
  super({
951
974
  messenger,
952
975
  metadata: {
@@ -984,6 +1007,7 @@ class SnapController extends _basecontroller.BaseController {
984
1007
  });
985
1008
  _class_private_method_init(this, _initializeStateMachine);
986
1009
  _class_private_method_init(this, _registerMessageHandlers);
1010
+ _class_private_method_init(this, _handlePreinstalledSnaps);
987
1011
  _class_private_method_init(this, _pollForLastRequestStatus);
988
1012
  /**
989
1013
  * Blocks an installed snap and prevents it from being started again. Emits
@@ -1001,6 +1025,8 @@ class SnapController extends _basecontroller.BaseController {
1001
1025
  *
1002
1026
  * @param snapId - The snap to terminate.
1003
1027
  */ _class_private_method_init(this, _terminateSnap);
1028
+ _class_private_method_init(this, _handleInitialConnections);
1029
+ _class_private_method_init(this, _addSnapToSubject);
1004
1030
  _class_private_method_init(this, _removeSnapFromSubjects);
1005
1031
  _class_private_method_init(this, _revokeAllSnapPermissions);
1006
1032
  _class_private_method_init(this, _createApproval);
@@ -1028,20 +1054,29 @@ class SnapController extends _basecontroller.BaseController {
1028
1054
  * @returns An array of the names of the endowments.
1029
1055
  */ _class_private_method_init(this, _getEndowments);
1030
1056
  _class_private_method_init(this, _set);
1031
- /**
1032
- * Fetches the manifest and source code of a snap.
1033
- *
1034
- * @param snapId - The id of the Snap.
1035
- * @param location - Source from which snap will be fetched.
1036
- * @returns A tuple of the Snap manifest object and the Snap source code.
1037
- */ _class_private_method_init(this, _fetchSnap);
1038
1057
  _class_private_method_init(this, _validateSnapPermissions);
1058
+ _class_private_method_init(this, _getExecutionTimeout);
1039
1059
  _class_private_method_init(this, _getRpcRequestHandler);
1040
- _class_private_method_init(this, _triggerPhishingListUpdate);
1041
- _class_private_method_init(this, _checkPhishingList);
1042
1060
  /**
1043
- * Asserts that the returned result of a Snap RPC call is the expected shape.
1061
+ * Create a dynamic interface in the SnapInterfaceController.
1062
+ *
1063
+ * @param snapId - The snap ID.
1064
+ * @param content - The initial interface content.
1065
+ * @returns An identifier that can be used to identify the interface.
1066
+ */ _class_private_method_init(this, _createInterface);
1067
+ _class_private_method_init(this, _assertInterfaceExists);
1068
+ /**
1069
+ * Transform a RPC request result if necessary.
1070
+ *
1071
+ * @param snapId - The snap ID of the snap that produced the result.
1072
+ * @param handlerType - The handler type that produced the result.
1073
+ * @param result - The result.
1074
+ * @returns The transformed result if applicable, otherwise the original result.
1075
+ */ _class_private_method_init(this, _transformSnapRpcRequestResult);
1076
+ /**
1077
+ * Assert that the returned result of a Snap RPC call is the expected shape.
1044
1078
  *
1079
+ * @param snapId - The snap ID.
1045
1080
  * @param handlerType - The handler type of the RPC Request.
1046
1081
  * @param result - The result of the RPC request.
1047
1082
  */ _class_private_method_init(this, _assertSnapRpcRequestResult);
@@ -1079,6 +1114,7 @@ class SnapController extends _basecontroller.BaseController {
1079
1114
  _class_private_method_init(this, _getRuntimeExpect);
1080
1115
  _class_private_method_init(this, _setupRuntime);
1081
1116
  _class_private_method_init(this, _calculatePermissionsChange);
1117
+ _class_private_method_init(this, _updatePermissions);
1082
1118
  _class_private_method_init(this, _isValidUpdate);
1083
1119
  /**
1084
1120
  * Call a lifecycle hook on a snap, if the snap has the
@@ -1175,7 +1211,10 @@ class SnapController extends _basecontroller.BaseController {
1175
1211
  });
1176
1212
  _class_private_method_get(this, _initializeStateMachine, initializeStateMachine).call(this);
1177
1213
  _class_private_method_get(this, _registerMessageHandlers, registerMessageHandlers).call(this);
1178
- Object.values(state?.snaps ?? {}).forEach((snap)=>_class_private_method_get(this, _setupRuntime, setupRuntime).call(this, snap.id));
1214
+ if (preinstalledSnaps) {
1215
+ _class_private_method_get(this, _handlePreinstalledSnaps, handlePreinstalledSnaps).call(this, preinstalledSnaps);
1216
+ }
1217
+ Object.values(this.state?.snaps ?? {}).forEach((snap)=>_class_private_method_get(this, _setupRuntime, setupRuntime).call(this, snap.id));
1179
1218
  }
1180
1219
  }
1181
1220
  function initializeStateMachine() {
@@ -1252,6 +1291,63 @@ function registerMessageHandlers() {
1252
1291
  this.messagingSystem.registerActionHandler(`${controllerName}:revokeDynamicPermissions`, (...args)=>this.revokeDynamicSnapPermissions(...args));
1253
1292
  this.messagingSystem.registerActionHandler(`${controllerName}:getFile`, async (...args)=>this.getSnapFile(...args));
1254
1293
  }
1294
+ function handlePreinstalledSnaps(preinstalledSnaps) {
1295
+ for (const { snapId, manifest, files, removable } of preinstalledSnaps){
1296
+ const existingSnap = this.get(snapId);
1297
+ const isAlreadyInstalled = existingSnap !== undefined;
1298
+ const isUpdate = isAlreadyInstalled && (0, _utils.gtVersion)(manifest.version, existingSnap.version);
1299
+ // Disallow downgrades and overwriting non preinstalled snaps
1300
+ if (isAlreadyInstalled && (!isUpdate || existingSnap.preinstalled !== true)) {
1301
+ continue;
1302
+ }
1303
+ const manifestFile = new _snapsutils.VirtualFile({
1304
+ path: _snapsutils.NpmSnapFileNames.Manifest,
1305
+ value: JSON.stringify(manifest),
1306
+ result: manifest
1307
+ });
1308
+ const virtualFiles = files.map(({ path, value })=>new _snapsutils.VirtualFile({
1309
+ value,
1310
+ path
1311
+ }));
1312
+ const { filePath, iconPath } = manifest.source.location.npm;
1313
+ const sourceCode = virtualFiles.find((file)=>file.path === filePath);
1314
+ const svgIcon = iconPath ? virtualFiles.find((file)=>file.path === iconPath) : undefined;
1315
+ (0, _utils.assert)(sourceCode, 'Source code not provided for preinstalled snap.');
1316
+ (0, _utils.assert)(!iconPath || iconPath && svgIcon, 'Icon not provided for preinstalled snap.');
1317
+ (0, _utils.assert)(manifest.source.files === undefined, 'Auxiliary files are not currently supported for preinstalled snaps.');
1318
+ const localizationFiles = manifest.source.locales?.map((path)=>virtualFiles.find((file)=>file.path === path)) ?? [];
1319
+ const validatedLocalizationFiles = (0, _snapsutils.getValidatedLocalizationFiles)(localizationFiles.filter(Boolean));
1320
+ (0, _utils.assert)(localizationFiles.length === validatedLocalizationFiles.length, 'Missing localization files for preinstalled snap.');
1321
+ const filesObject = {
1322
+ manifest: manifestFile,
1323
+ sourceCode,
1324
+ svgIcon,
1325
+ auxiliaryFiles: [],
1326
+ localizationFiles: validatedLocalizationFiles
1327
+ };
1328
+ // Add snap to the SnapController state
1329
+ _class_private_method_get(this, _set, set).call(this, {
1330
+ id: snapId,
1331
+ origin: 'metamask',
1332
+ files: filesObject,
1333
+ removable,
1334
+ preinstalled: true
1335
+ });
1336
+ // Setup permissions
1337
+ const processedPermissions = (0, _snapsrpcmethods.processSnapPermissions)(manifest.initialPermissions);
1338
+ _class_private_method_get(this, _validateSnapPermissions, validateSnapPermissions).call(this, processedPermissions);
1339
+ const { newPermissions, unusedPermissions } = _class_private_method_get(this, _calculatePermissionsChange, calculatePermissionsChange).call(this, snapId, processedPermissions);
1340
+ _class_private_method_get(this, _updatePermissions, updatePermissions).call(this, {
1341
+ snapId,
1342
+ newPermissions,
1343
+ unusedPermissions
1344
+ });
1345
+ // Set status
1346
+ this.update((state)=>{
1347
+ state.snaps[snapId].status = _snapsutils.SnapStatus.Stopped;
1348
+ });
1349
+ }
1350
+ }
1255
1351
  function pollForLastRequestStatus() {
1256
1352
  _class_private_field_set(this, _timeoutForLastRequestStatus, setTimeout(()=>{
1257
1353
  _class_private_method_get(this, _stopSnapsLastRequestPastMax, stopSnapsLastRequestPastMax).call(this).catch((error)=>{
@@ -1293,7 +1389,9 @@ async function assertIsInstallAllowed(snapId, snapInfo) {
1293
1389
  const result = results[snapId];
1294
1390
  if (result.status === _registry.SnapsRegistryStatus.Blocked) {
1295
1391
  throw new Error(`Cannot install version "${snapInfo.version}" of snap "${snapId}": The version is blocked. ${result.reason?.explanation ?? ''}`);
1296
- } else if (_class_private_field_get(this, _featureFlags).requireAllowlist && result.status !== _registry.SnapsRegistryStatus.Verified) {
1392
+ }
1393
+ const isAllowlistingRequired = Object.keys(snapInfo.permissions).some((permission)=>!_constants.ALLOWED_PERMISSIONS.includes(permission));
1394
+ if (_class_private_field_get(this, _featureFlags).requireAllowlist && isAllowlistingRequired && result.status !== _registry.SnapsRegistryStatus.Verified) {
1297
1395
  throw new Error(`Cannot install version "${snapInfo.version}" of snap "${snapId}": The snap is not on the allowlist.`);
1298
1396
  }
1299
1397
  }
@@ -1314,6 +1412,52 @@ async function terminateSnap(snapId) {
1314
1412
  await this.messagingSystem.call('ExecutionService:terminateSnap', snapId);
1315
1413
  this.messagingSystem.publish('SnapController:snapTerminated', this.getTruncatedExpect(snapId));
1316
1414
  }
1415
+ function handleInitialConnections(snapId, previousInitialConnections, initialConnections) {
1416
+ if (previousInitialConnections) {
1417
+ const revokedInitialConnections = (0, _utils1.setDiff)(previousInitialConnections, initialConnections);
1418
+ for (const origin of Object.keys(revokedInitialConnections)){
1419
+ this.removeSnapFromSubject(origin, snapId);
1420
+ }
1421
+ }
1422
+ for (const origin of Object.keys(initialConnections)){
1423
+ _class_private_method_get(this, _addSnapToSubject, addSnapToSubject).call(this, origin, snapId);
1424
+ }
1425
+ }
1426
+ function addSnapToSubject(origin, snapId) {
1427
+ const subjectPermissions = this.messagingSystem.call('PermissionController:getPermissions', origin);
1428
+ const existingCaveat = subjectPermissions?.[_snapsrpcmethods.WALLET_SNAP_PERMISSION_KEY]?.caveats?.find((caveat)=>caveat.type === _snapsutils.SnapCaveatType.SnapIds);
1429
+ const subjectHasSnap = Boolean((existingCaveat?.value)?.[snapId]);
1430
+ // If the subject is already connected to the snap, this is a no-op.
1431
+ if (subjectHasSnap) {
1432
+ return;
1433
+ }
1434
+ // If an existing caveat exists, we add the snap to that.
1435
+ if (existingCaveat) {
1436
+ this.messagingSystem.call('PermissionController:updateCaveat', origin, _snapsrpcmethods.WALLET_SNAP_PERMISSION_KEY, _snapsutils.SnapCaveatType.SnapIds, {
1437
+ ...existingCaveat,
1438
+ [snapId]: {}
1439
+ });
1440
+ return;
1441
+ }
1442
+ const approvedPermissions = {
1443
+ [_snapsrpcmethods.WALLET_SNAP_PERMISSION_KEY]: {
1444
+ caveats: [
1445
+ {
1446
+ type: _snapsutils.SnapCaveatType.SnapIds,
1447
+ value: {
1448
+ [snapId]: {}
1449
+ }
1450
+ }
1451
+ ]
1452
+ }
1453
+ };
1454
+ this.messagingSystem.call('PermissionController:grantPermissions', {
1455
+ approvedPermissions,
1456
+ subject: {
1457
+ origin
1458
+ }
1459
+ });
1460
+ }
1317
1461
  function removeSnapFromSubjects(snapId) {
1318
1462
  const subjects = this.messagingSystem.call('PermissionController:getSubjectNames');
1319
1463
  for (const subject of subjects){
@@ -1371,18 +1515,19 @@ async function add(args) {
1371
1515
  // If fetching and setting the snap succeeds, this property will be set
1372
1516
  // to null in the authorize() method.
1373
1517
  runtime.installPromise = (async ()=>{
1374
- const fetchedSnap = await _class_private_method_get(this, _fetchSnap, fetchSnap).call(this, snapId, location);
1375
- const manifest = fetchedSnap.files.manifest.result;
1518
+ const fetchedSnap = await (0, _utils1.fetchSnap)(snapId, location);
1519
+ const manifest = fetchedSnap.manifest.result;
1376
1520
  if (!(0, _utils.satisfiesVersionRange)(manifest.version, versionRange)) {
1377
1521
  throw new Error(`Version mismatch. Manifest for "${snapId}" specifies version "${manifest.version}" which doesn't satisfy requested version range "${versionRange}".`);
1378
1522
  }
1379
1523
  await _class_private_method_get(this, _assertIsInstallAllowed, assertIsInstallAllowed).call(this, snapId, {
1380
1524
  version: manifest.version,
1381
- checksum: manifest.source.shasum
1525
+ checksum: manifest.source.shasum,
1526
+ permissions: manifest.initialPermissions
1382
1527
  });
1383
1528
  return _class_private_method_get(this, _set, set).call(this, {
1384
1529
  ...args,
1385
- ...fetchedSnap,
1530
+ files: fetchedSnap,
1386
1531
  id: snapId
1387
1532
  });
1388
1533
  })();
@@ -1445,10 +1590,10 @@ async function getEndowments(snapId) {
1445
1590
  return dedupedEndowments;
1446
1591
  }
1447
1592
  function set(args) {
1448
- const { id: snapId, origin, files, isUpdate = false } = args;
1593
+ const { id: snapId, origin, files, isUpdate = false, removable, preinstalled } = args;
1449
1594
  const { manifest, sourceCode: sourceCodeFile, svgIcon, auxiliaryFiles: rawAuxiliaryFiles, localizationFiles } = files;
1450
1595
  (0, _snapsutils.assertIsSnapManifest)(manifest.result);
1451
- const { version, proposedName } = manifest.result;
1596
+ const { version } = manifest.result;
1452
1597
  const sourceCode = sourceCodeFile.toString();
1453
1598
  (0, _utils.assert)(typeof sourceCode === 'string' && sourceCode.length > 0, `Invalid source code for snap "${snapId}".`);
1454
1599
  const auxiliaryFiles = rawAuxiliaryFiles.map((file)=>{
@@ -1469,6 +1614,7 @@ function set(args) {
1469
1614
  origin
1470
1615
  }
1471
1616
  ];
1617
+ const localizedFiles = localizationFiles.map((file)=>file.result);
1472
1618
  const snap = {
1473
1619
  // Restore relevant snap state if it exists
1474
1620
  ...existingSnap,
@@ -1476,6 +1622,8 @@ function set(args) {
1476
1622
  // previous state.
1477
1623
  blocked: false,
1478
1624
  enabled: true,
1625
+ removable,
1626
+ preinstalled,
1479
1627
  id: snapId,
1480
1628
  initialPermissions: manifest.result.initialPermissions,
1481
1629
  manifest: manifest.result,
@@ -1484,7 +1632,7 @@ function set(args) {
1484
1632
  version,
1485
1633
  versionHistory,
1486
1634
  auxiliaryFiles,
1487
- localizationFiles: localizationFiles.map((file)=>file.result)
1635
+ localizationFiles: localizedFiles
1488
1636
  };
1489
1637
  // If the snap was blocked, it isn't any longer
1490
1638
  delete snap.blockInformation;
@@ -1500,6 +1648,9 @@ function set(args) {
1500
1648
  rollbackSnapshot.statePatches = inversePatches;
1501
1649
  }
1502
1650
  }
1651
+ // In case the Snap uses a localized manifest, we need to get the
1652
+ // proposed name from the localized manifest.
1653
+ const { proposedName } = (0, _snapsutils.getLocalizedSnapManifest)(manifest.result, 'en', localizedFiles);
1503
1654
  this.messagingSystem.call('SubjectMetadataController:addSubjectMetadata', {
1504
1655
  subjectType: _permissioncontroller.SubjectType.Snap,
1505
1656
  name: proposedName,
@@ -1512,40 +1663,10 @@ function set(args) {
1512
1663
  sourceCode
1513
1664
  };
1514
1665
  }
1515
- async function fetchSnap(snapId, location) {
1516
- try {
1517
- const manifest = await location.manifest();
1518
- const sourceCode = await location.fetch(manifest.result.source.location.npm.filePath);
1519
- const { iconPath } = manifest.result.source.location.npm;
1520
- const svgIcon = iconPath ? await location.fetch(iconPath) : undefined;
1521
- const auxiliaryFiles = await (0, _utils1.getSnapFiles)(location, manifest.result.source.files);
1522
- await Promise.all(auxiliaryFiles.map(async (file)=>{
1523
- // This should still be safe
1524
- // eslint-disable-next-line require-atomic-updates
1525
- file.data.base64 = await (0, _snapsutils.encodeBase64)(file);
1526
- }));
1527
- const localizationFiles = await (0, _utils1.getSnapFiles)(location, manifest.result.source.locales);
1528
- const validatedLocalizationFiles = (0, _snapsutils.getValidatedLocalizationFiles)(localizationFiles);
1529
- const files = {
1530
- manifest,
1531
- sourceCode,
1532
- svgIcon,
1533
- auxiliaryFiles,
1534
- localizationFiles: validatedLocalizationFiles
1535
- };
1536
- await (0, _snapsutils.validateFetchedSnap)(files);
1537
- return {
1538
- files,
1539
- location
1540
- };
1541
- } catch (error) {
1542
- throw new Error(`Failed to fetch snap "${snapId}": ${(0, _snapssdk.getErrorMessage)(error)}.`);
1543
- }
1544
- }
1545
1666
  function validateSnapPermissions(processedPermissions) {
1546
1667
  const permissionKeys = Object.keys(processedPermissions);
1547
- const handlerPermissions = Array.from(new Set(Object.values(_endowments.handlerEndowments)));
1548
- (0, _utils.assert)(permissionKeys.some((key)=>handlerPermissions.includes(key)), `A snap must request at least one of the following permissions: ${handlerPermissions.join(', ')}.`);
1668
+ const handlerPermissions = Array.from(new Set(Object.values(_snapsrpcmethods.handlerEndowments)));
1669
+ (0, _utils.assert)(permissionKeys.some((key)=>handlerPermissions.includes(key)), `A snap must request at least one of the following permissions: ${handlerPermissions.filter((handler)=>handler !== null).join(', ')}.`);
1549
1670
  const excludedPermissionErrors = permissionKeys.reduce((errors, permission)=>{
1550
1671
  if ((0, _utils.hasProperty)(_class_private_field_get(this, _excludedPermissions), permission)) {
1551
1672
  errors.push(_class_private_field_get(this, _excludedPermissions)[permission]);
@@ -1554,6 +1675,9 @@ function validateSnapPermissions(processedPermissions) {
1554
1675
  }, []);
1555
1676
  (0, _utils.assert)(excludedPermissionErrors.length === 0, `One or more permissions are not allowed:\n${excludedPermissionErrors.join('\n')}`);
1556
1677
  }
1678
+ function getExecutionTimeout(permission) {
1679
+ return (0, _snapsrpcmethods.getMaxRequestTimeCaveat)(permission) ?? this.maxRequestTime;
1680
+ }
1557
1681
  function getRpcRequestHandler(snapId) {
1558
1682
  const runtime = _class_private_method_get(this, _getRuntimeExpect, getRuntimeExpect).call(this, snapId);
1559
1683
  const existingHandler = runtime.rpcHandler;
@@ -1564,7 +1688,7 @@ function getRpcRequestHandler(snapId) {
1564
1688
  // We need to set up this promise map to map snapIds to their respective startPromises,
1565
1689
  // because otherwise we would lose context on the correct startPromise.
1566
1690
  const startPromises = new Map();
1567
- const rpcHandler = async ({ origin, handler: handlerType, request })=>{
1691
+ const rpcHandler = async ({ origin, handler: handlerType, request, timeout })=>{
1568
1692
  if (this.state.snaps[snapId].enabled === false) {
1569
1693
  throw new Error(`Snap "${snapId}" is disabled.`);
1570
1694
  }
@@ -1590,7 +1714,7 @@ function getRpcRequestHandler(snapId) {
1590
1714
  }
1591
1715
  }
1592
1716
  }
1593
- const timer = new _Timer.Timer(this.maxRequestTime);
1717
+ const timer = new _Timer.Timer(timeout);
1594
1718
  _class_private_method_get(this, _recordSnapRpcRequestStart, recordSnapRpcRequestStart).call(this, snapId, request.id, timer);
1595
1719
  const handleRpcRequestPromise = this.messagingSystem.call('ExecutionService:handleRpcRequest', snapId, {
1596
1720
  origin,
@@ -1600,8 +1724,8 @@ function getRpcRequestHandler(snapId) {
1600
1724
  // This will either get the result or reject due to the timeout.
1601
1725
  try {
1602
1726
  const result = await _class_private_method_get(this, _executeWithTimeout, executeWithTimeout).call(this, handleRpcRequestPromise, timer);
1603
- await _class_private_method_get(this, _assertSnapRpcRequestResult, assertSnapRpcRequestResult).call(this, handlerType, result);
1604
- return result;
1727
+ await _class_private_method_get(this, _assertSnapRpcRequestResult, assertSnapRpcRequestResult).call(this, snapId, handlerType, result);
1728
+ return _class_private_method_get(this, _transformSnapRpcRequestResult, transformSnapRpcRequestResult).call(this, snapId, handlerType, result);
1605
1729
  } catch (error) {
1606
1730
  const [jsonRpcError, handled] = (0, _snapsutils.unwrapError)(error);
1607
1731
  if (!handled) {
@@ -1615,29 +1739,64 @@ function getRpcRequestHandler(snapId) {
1615
1739
  runtime.rpcHandler = rpcHandler;
1616
1740
  return rpcHandler;
1617
1741
  }
1618
- async function triggerPhishingListUpdate() {
1619
- return this.messagingSystem.call('PhishingController:maybeUpdateState');
1742
+ async function createInterface(snapId, content) {
1743
+ return this.messagingSystem.call('SnapInterfaceController:createInterface', snapId, content);
1744
+ }
1745
+ function assertInterfaceExists(snapId, id) {
1746
+ // This will throw if the interface isn't accessible, but we assert nevertheless.
1747
+ (0, _utils.assert)(this.messagingSystem.call('SnapInterfaceController:getInterface', snapId, id));
1620
1748
  }
1621
- function checkPhishingList(origin) {
1622
- return this.messagingSystem.call('PhishingController:testOrigin', origin).result;
1749
+ async function transformSnapRpcRequestResult(snapId, handlerType, result) {
1750
+ switch(handlerType){
1751
+ case _snapsutils.HandlerType.OnTransaction:
1752
+ case _snapsutils.HandlerType.OnSignature:
1753
+ case _snapsutils.HandlerType.OnHomePage:
1754
+ {
1755
+ // Since this type has been asserted earlier we can cast
1756
+ const castResult = result;
1757
+ // If a handler returns static content, we turn it into a dynamic UI
1758
+ if (castResult && (0, _utils.hasProperty)(castResult, 'content')) {
1759
+ const { content, ...rest } = castResult;
1760
+ const id = await _class_private_method_get(this, _createInterface, createInterface).call(this, snapId, content);
1761
+ return {
1762
+ ...rest,
1763
+ id
1764
+ };
1765
+ }
1766
+ return result;
1767
+ }
1768
+ default:
1769
+ return result;
1770
+ }
1623
1771
  }
1624
- async function assertSnapRpcRequestResult(handlerType, result) {
1772
+ async function assertSnapRpcRequestResult(snapId, handlerType, result) {
1625
1773
  switch(handlerType){
1626
1774
  case _snapsutils.HandlerType.OnTransaction:
1627
1775
  {
1628
1776
  (0, _utils.assertStruct)(result, _snapsutils.OnTransactionResponseStruct);
1629
- // Null is an allowed return value here
1630
- if (result === null) {
1631
- return;
1777
+ if (result && (0, _utils.hasProperty)(result, 'id')) {
1778
+ _class_private_method_get(this, _assertInterfaceExists, assertInterfaceExists).call(this, snapId, result.id);
1779
+ }
1780
+ break;
1781
+ }
1782
+ case _snapsutils.HandlerType.OnSignature:
1783
+ {
1784
+ (0, _utils.assertStruct)(result, _snapsutils.OnSignatureResponseStruct);
1785
+ if (result && (0, _utils.hasProperty)(result, 'id')) {
1786
+ _class_private_method_get(this, _assertInterfaceExists, assertInterfaceExists).call(this, snapId, result.id);
1632
1787
  }
1633
- await _class_private_method_get(this, _triggerPhishingListUpdate, triggerPhishingListUpdate).call(this);
1634
- (0, _snapsutils.validateComponentLinks)(result.content, _class_private_method_get(this, _checkPhishingList, checkPhishingList).bind(this));
1635
1788
  break;
1636
1789
  }
1637
1790
  case _snapsutils.HandlerType.OnHomePage:
1638
- (0, _utils.assertStruct)(result, _snapsutils.OnHomePageResponseStruct);
1639
- await _class_private_method_get(this, _triggerPhishingListUpdate, triggerPhishingListUpdate).call(this);
1640
- (0, _snapsutils.validateComponentLinks)(result.content, _class_private_method_get(this, _checkPhishingList, checkPhishingList).bind(this));
1791
+ {
1792
+ (0, _utils.assertStruct)(result, _snapsutils.OnHomePageResponseStruct);
1793
+ if (result && (0, _utils.hasProperty)(result, 'id')) {
1794
+ _class_private_method_get(this, _assertInterfaceExists, assertInterfaceExists).call(this, snapId, result.id);
1795
+ }
1796
+ break;
1797
+ }
1798
+ case _snapsutils.HandlerType.OnNameLookup:
1799
+ (0, _utils.assertStruct)(result, _snapsutils.OnNameLookupResponseStruct);
1641
1800
  break;
1642
1801
  default:
1643
1802
  break;
@@ -1672,11 +1831,7 @@ function createRollbackSnapshot(snapId) {
1672
1831
  (0, _utils.assert)(_class_private_field_get(this, _rollbackSnapshots).get(snapId) === undefined, new Error(`Snap "${snapId}" rollback snapshot already exists.`));
1673
1832
  _class_private_field_get(this, _rollbackSnapshots).set(snapId, {
1674
1833
  statePatches: [],
1675
- permissions: {
1676
- revoked: null,
1677
- granted: [],
1678
- requestData: null
1679
- },
1834
+ permissions: {},
1680
1835
  newVersion: ''
1681
1836
  });
1682
1837
  const newRollbackSnapshot = _class_private_field_get(this, _rollbackSnapshots).get(snapId);
@@ -1704,20 +1859,12 @@ async function rollbackSnap(snapId) {
1704
1859
  state.snaps[snapId].status = _snapsutils.SnapStatus.Stopped;
1705
1860
  });
1706
1861
  }
1707
- if (permissions.revoked && Object.keys(permissions.revoked).length) {
1708
- this.messagingSystem.call('PermissionController:grantPermissions', {
1709
- approvedPermissions: permissions.revoked,
1710
- subject: {
1711
- origin: snapId
1712
- },
1713
- requestData: permissions.requestData
1714
- });
1715
- }
1716
- if (permissions.granted?.length) {
1717
- this.messagingSystem.call('PermissionController:revokePermissions', {
1718
- [snapId]: permissions.granted
1719
- });
1720
- }
1862
+ _class_private_method_get(this, _updatePermissions, updatePermissions).call(this, {
1863
+ snapId,
1864
+ unusedPermissions: permissions.granted,
1865
+ newPermissions: permissions.revoked,
1866
+ requestData: permissions.requestData
1867
+ });
1721
1868
  const truncatedSnap = this.getTruncatedExpect(snapId);
1722
1869
  this.messagingSystem.publish('SnapController:snapRolledback', truncatedSnap, rollbackSnapshot.newVersion);
1723
1870
  _class_private_field_get(this, _rollbackSnapshots).delete(snapId);
@@ -1773,6 +1920,23 @@ function calculatePermissionsChange(snapId, desiredPermissionsSet) {
1773
1920
  approvedPermissions
1774
1921
  };
1775
1922
  }
1923
+ function updatePermissions({ snapId, unusedPermissions = {}, newPermissions = {}, requestData }) {
1924
+ const unusedPermissionsKeys = Object.keys(unusedPermissions);
1925
+ if ((0, _utils.isNonEmptyArray)(unusedPermissionsKeys)) {
1926
+ this.messagingSystem.call('PermissionController:revokePermissions', {
1927
+ [snapId]: unusedPermissionsKeys
1928
+ });
1929
+ }
1930
+ if ((0, _utils.isNonEmptyArray)(Object.keys(newPermissions))) {
1931
+ this.messagingSystem.call('PermissionController:grantPermissions', {
1932
+ approvedPermissions: newPermissions,
1933
+ subject: {
1934
+ origin: snapId
1935
+ },
1936
+ requestData
1937
+ });
1938
+ }
1939
+ }
1776
1940
  function isValidUpdate(snapId, newVersionRange) {
1777
1941
  const existingSnap = this.getExpect(snapId);
1778
1942
  if ((0, _utils.satisfiesVersionRange)(existingSnap.version, newVersionRange)) {
@@ -1784,7 +1948,8 @@ function isValidUpdate(snapId, newVersionRange) {
1784
1948
  return true;
1785
1949
  }
1786
1950
  async function callLifecycleHook(snapId, handler) {
1787
- const permissionName = _endowments.handlerEndowments[handler];
1951
+ const permissionName = _snapsrpcmethods.handlerEndowments[handler];
1952
+ (0, _utils.assert)(permissionName, 'Lifecycle hook must have an endowment.');
1788
1953
  const hasPermission = this.messagingSystem.call('PermissionController:hasPermission', snapId, permissionName);
1789
1954
  if (!hasPermission) {
1790
1955
  return;