From 3cc578eb59b928663b78ea87859aeac92fcaaa15 Mon Sep 17 00:00:00 2001
From: Gustavo Antunes <17601467+gantunesr@users.noreply.github.com>
Date: Fri, 3 Feb 2023 12:50:21 -0300
Subject: [PATCH] Integrate PS
---
app/components/Nav/Main/RootRPCMethodsUI.js | 43 ++++
app/components/UI/DrawerView/index.js | 3 -
.../InstallSnapApproval.tsx | 65 ++++++
.../UI/InstallSnapApproval/index.ts | 6 +
.../UI/InstallSnapApproval/styles.ts | 52 +++++
.../UI/InstallSnapApproval/types.ts | 9 +
app/core/Engine.js | 179 +++++++++++------
app/core/Permissions/constants.js | 9 +
app/core/Permissions/specifications.js | 12 ++
app/core/RPCMethods/RPCMethodMiddleware.ts | 2 +
app/core/Snaps/index.ts | 10 +
app/core/Snaps/permissions.ts | 41 ++++
package.json | 3 +
.../@metamask+snap-controllers+0.23.0.patch | 188 ++++--------------
yarn.lock | 86 +++++++-
15 files changed, 497 insertions(+), 211 deletions(-)
create mode 100644 app/components/UI/InstallSnapApproval/InstallSnapApproval.tsx
create mode 100644 app/components/UI/InstallSnapApproval/index.ts
create mode 100644 app/components/UI/InstallSnapApproval/styles.ts
create mode 100644 app/components/UI/InstallSnapApproval/types.ts
create mode 100644 app/core/Snaps/permissions.ts
diff --git a/app/components/Nav/Main/RootRPCMethodsUI.js b/app/components/Nav/Main/RootRPCMethodsUI.js
index 96b0151a194..7f9cf4d0bd1 100644
--- a/app/components/Nav/Main/RootRPCMethodsUI.js
+++ b/app/components/Nav/Main/RootRPCMethodsUI.js
@@ -35,6 +35,7 @@ import Logger from '../../../util/Logger';
import MessageSign from '../../UI/MessageSign';
import Approve from '../../Views/ApproveView/Approve';
import WatchAssetRequest from '../../UI/WatchAssetRequest';
+import { InstallSnapApproval } from '../../UI/InstallSnapApproval';
import AccountApproval from '../../UI/AccountApproval';
import TransactionTypes from '../../../core/TransactionTypes';
import AddCustomNetwork from '../../UI/AddCustomNetwork';
@@ -684,6 +685,47 @@ const RootRPCMethodsUI = (props) => {
);
+ const onInstallSnapConfirm = () => {
+ acceptPendingApproval(hostToApprove.id, hostToApprove.requestData);
+ setShowPendingApproval(false);
+ };
+
+ const onInstallSnapReject = () => {
+ // eslint-disable-next-line no-console
+ console.log(
+ 'onInstallSnapReject',
+ hostToApprove.id,
+ hostToApprove.requestData,
+ );
+ rejectPendingApproval(hostToApprove.id, hostToApprove.requestData);
+ setShowPendingApproval(false);
+ };
+
+ /**
+ * Render the modal that asks the user to approve/reject connections to a dapp using the MetaMask SDK.
+ */
+ const renderInstallSnapApprovalModal = () => (
+
+
+
+ );
+
// unapprovedTransaction effect
useEffect(() => {
Engine.context.TransactionController.hub.on(
@@ -815,6 +857,7 @@ const RootRPCMethodsUI = (props) => {
{renderWatchAssetModal()}
{renderQRSigningModal()}
{renderAccountsApprovalModal()}
+ {renderInstallSnapApprovalModal()}
);
};
diff --git a/app/components/UI/DrawerView/index.js b/app/components/UI/DrawerView/index.js
index fd6f00fde69..57a619c1650 100644
--- a/app/components/UI/DrawerView/index.js
+++ b/app/components/UI/DrawerView/index.js
@@ -736,9 +736,6 @@ class DrawerView extends PureComponent {
const { navigation } = this.props;
navigation.navigate(Routes.SNAPS.HOME);
this.hideDrawer();
-
- // eslint-disable-next-line no-console
- // console.log('Navigate to snaps');
};
showSettings = async () => {
diff --git a/app/components/UI/InstallSnapApproval/InstallSnapApproval.tsx b/app/components/UI/InstallSnapApproval/InstallSnapApproval.tsx
new file mode 100644
index 00000000000..9dda44ae7ac
--- /dev/null
+++ b/app/components/UI/InstallSnapApproval/InstallSnapApproval.tsx
@@ -0,0 +1,65 @@
+import React from 'react';
+import { Text, View } from 'react-native';
+import { useSelector } from 'react-redux';
+import { InstallSnapApprovalArgs } from './types';
+import createStyles from './styles';
+import { useAppThemeFromContext, mockTheme } from '../../../util/theme';
+import StyledButton from '../StyledButton';
+import { strings } from '../../../../locales/i18n';
+import {
+ ACCOUNT_APROVAL_MODAL_CONTAINER_ID,
+ CANCEL_BUTTON_ID,
+} from '../../../constants/test-ids';
+
+const InstallSnapApproval = ({
+ requestData,
+ onConfirm,
+ onCancel,
+}: InstallSnapApprovalArgs) => {
+ const { colors } = useAppThemeFromContext() || mockTheme;
+ const styles = createStyles(colors);
+
+ const selectedAddress = useSelector(
+ (state: any) =>
+ state.engine.backgroundState.PreferencesController.selectedAddress,
+ );
+
+ const confirm = (): void => {
+ // eslint-disable-next-line no-console
+ console.log('confirm', onConfirm);
+ onConfirm();
+ // Add track event
+ };
+
+ const cancel = (): void => {
+ // Add track event
+ onCancel();
+ };
+
+ return (
+
+ {/* SNAP ID: {`${requestData.data.snapId}`} */}
+ {`${selectedAddress} ${JSON.stringify(requestData)}`}
+
+
+ {strings('accountApproval.cancel')}
+
+
+ Approve
+
+
+
+ );
+};
+
+export default InstallSnapApproval;
diff --git a/app/components/UI/InstallSnapApproval/index.ts b/app/components/UI/InstallSnapApproval/index.ts
new file mode 100644
index 00000000000..043b61328a1
--- /dev/null
+++ b/app/components/UI/InstallSnapApproval/index.ts
@@ -0,0 +1,6 @@
+import InstallSnapApproval from './InstallSnapApproval';
+import { InstallSnapApprovalArgs } from './types';
+
+export { InstallSnapApproval };
+
+export type { InstallSnapApprovalArgs };
diff --git a/app/components/UI/InstallSnapApproval/styles.ts b/app/components/UI/InstallSnapApproval/styles.ts
new file mode 100644
index 00000000000..85c12f6dedd
--- /dev/null
+++ b/app/components/UI/InstallSnapApproval/styles.ts
@@ -0,0 +1,52 @@
+import { StyleSheet } from 'react-native';
+import { fontStyles } from '../../../styles/common';
+import Device from '../../../util/device';
+
+const createStyles = (colors: any) =>
+ StyleSheet.create({
+ root: {
+ backgroundColor: colors.background.default,
+ paddingTop: 24,
+ borderTopLeftRadius: 20,
+ borderTopRightRadius: 20,
+ minHeight: 200,
+ paddingBottom: Device.isIphoneX() ? 20 : 0,
+ },
+ accountCardWrapper: {
+ paddingHorizontal: 24,
+ },
+ intro: {
+ ...fontStyles.bold,
+ textAlign: 'center',
+ color: colors.text.default,
+ fontSize: Device.isSmallDevice() ? 16 : 20,
+ marginBottom: 8,
+ marginTop: 16,
+ },
+ warning: {
+ ...fontStyles.thin,
+ color: colors.text.default,
+ paddingHorizontal: 24,
+ marginBottom: 16,
+ fontSize: 14,
+ width: '100%',
+ textAlign: 'center',
+ },
+ actionContainer: {
+ flex: 0,
+ flexDirection: 'row',
+ paddingVertical: 16,
+ paddingHorizontal: 24,
+ },
+ button: {
+ flex: 1,
+ },
+ cancel: {
+ marginRight: 8,
+ },
+ confirm: {
+ marginLeft: 8,
+ },
+ });
+
+export default createStyles;
diff --git a/app/components/UI/InstallSnapApproval/types.ts b/app/components/UI/InstallSnapApproval/types.ts
new file mode 100644
index 00000000000..5c5428ef789
--- /dev/null
+++ b/app/components/UI/InstallSnapApproval/types.ts
@@ -0,0 +1,9 @@
+interface InstallSnapApprovalArgs {
+ requestData: any;
+ onConfirm: () => void;
+ onCancel: () => void;
+ chainId?: string;
+}
+
+// eslint-disable-next-line import/prefer-default-export
+export type { InstallSnapApprovalArgs };
diff --git a/app/core/Engine.js b/app/core/Engine.js
index be42079e9af..cd820a4ddb8 100644
--- a/app/core/Engine.js
+++ b/app/core/Engine.js
@@ -54,7 +54,12 @@ import NotificationManager from './NotificationManager';
import Logger from '../util/Logger';
import { LAST_INCOMING_TX_BLOCK_INFO } from '../constants/storage';
import { EndowmentPermissions } from '../constants/permissions';
-import { SNAP_BLOCKLIST, checkSnapsBlockList } from '../util/snaps';
+import {
+ SNAP_BLOCKLIST,
+ checkSnapsBlockList,
+ buildSnapEndowmentSpecifications,
+ buildSnapRestrictedMethodSpecifications,
+} from '../util/snaps';
import { isZero } from '../util/lodash';
import { MetaMetricsEvents } from '../core/Analytics';
import AnalyticsV2 from '../util/analyticsV2';
@@ -252,6 +257,103 @@ class Engine {
'https://gas-api.metaswap.codefi.network/networks//suggestedGasFees',
});
+ const additionalKeyrings = [QRHardwareKeyring];
+
+ const getIdentities = () => {
+ const identities = preferencesController.state.identities;
+ const newIdentities = {};
+ Object.keys(identities).forEach((key) => {
+ newIdentities[key.toLowerCase()] = identities[key];
+ });
+ return newIdentities;
+ };
+
+ const keyringController = new KeyringController(
+ {
+ removeIdentity: preferencesController.removeIdentity.bind(
+ preferencesController,
+ ),
+ syncIdentities: preferencesController.syncIdentities.bind(
+ preferencesController,
+ ),
+ updateIdentities: preferencesController.updateIdentities.bind(
+ preferencesController,
+ ),
+ setSelectedAddress: preferencesController.setSelectedAddress.bind(
+ preferencesController,
+ ),
+ setAccountLabel: preferencesController.setAccountLabel.bind(
+ preferencesController,
+ ),
+ },
+ { encryptor, keyringTypes: additionalKeyrings },
+ initialState.KeyringController,
+ );
+
+ const approvalController = new ApprovalController({
+ messenger: this.controllerMessenger.getRestricted({
+ name: 'ApprovalController',
+ }),
+ showApprovalRequest: () => null,
+ });
+
+ const getSnapPermissionSpecifications = () => ({
+ ...buildSnapEndowmentSpecifications(),
+ ...buildSnapRestrictedMethodSpecifications({
+ clearSnapState: this.controllerMessenger.call.bind(
+ this.controllerMessenger,
+ 'SnapController:clearSnapState',
+ ),
+ // getMnemonic: this.getPrimaryKeyringMnemonic.bind(this),
+ // getUnlockPromise: this.appStateController.getUnlockPromise.bind(
+ // this.appStateController,
+ // ),
+ getSnap: this.controllerMessenger.call.bind(
+ this.controllerMessenger,
+ 'SnapController:get',
+ ),
+ handleSnapRpcRequest: this.controllerMessenger.call.bind(
+ this.controllerMessenger,
+ 'SnapController:handleRequest',
+ ),
+ getSnapState: this.controllerMessenger.call.bind(
+ this.controllerMessenger,
+ 'SnapController:getSnapState',
+ ),
+ // showConfirmation: (origin, confirmationData) =>
+ // this.approvalController.addAndShowApprovalRequest({
+ // origin,
+ // type: MESSAGE_TYPE.SNAP_CONFIRM,
+ // requestData: confirmationData,
+ // }),
+ updateSnapState: this.controllerMessenger.call.bind(
+ this.controllerMessenger,
+ 'SnapController:updateSnapState',
+ ),
+ }),
+ });
+
+ const permissionController = new PermissionController({
+ messenger: this.controllerMessenger.getRestricted({
+ name: 'PermissionController',
+ allowedActions: [
+ `${approvalController.name}:addRequest`,
+ `${approvalController.name}:hasRequest`,
+ `${approvalController.name}:acceptRequest`,
+ `${approvalController.name}:rejectRequest`,
+ ],
+ }),
+ state: initialState.PermissionController,
+ caveatSpecifications: getCaveatSpecifications({ getIdentities }),
+ permissionSpecifications: {
+ ...getPermissionSpecifications({
+ getAllAccounts: () => keyringController.getAccounts(),
+ }),
+ ...getSnapPermissionSpecifications(),
+ },
+ unrestrictedMethods,
+ });
+
this.setupSnapProvider = (snapId, connectionStream) => {
console.log(
'[ENGINE LOG] Engine+setupSnapProvider: Setup stream for Snap',
@@ -302,6 +404,16 @@ class Engine {
'ExecutionService:outboundResponse',
],
allowedActions: [
+ `${approvalController.name}:addRequest`,
+ `${permissionController.name}:getEndowments`,
+ `${permissionController.name}:getPermissions`,
+ `${permissionController.name}:hasPermission`,
+ `${permissionController.name}:hasPermissions`,
+ `${permissionController.name}:requestPermissions`,
+ `${permissionController.name}:revokeAllPermissions`,
+ `${permissionController.name}:revokePermissions`,
+ `${permissionController.name}:revokePermissionForAllSubjects`,
+ `${permissionController.name}:grantPermissions`,
'ExecutionService:executeSnap',
'ExecutionService:getRpcRequestHandler',
'ExecutionService:terminateSnap',
@@ -313,6 +425,7 @@ class Engine {
const snapController = new SnapController({
environmentEndowmentPermissions: Object.values(EndowmentPermissions),
featureFlags: { dappsCanUpdateSnaps: true },
+ // TO DO
getAppKey: async () =>
new Promise((resolve, reject) => {
resolve('mockAppKey');
@@ -324,55 +437,16 @@ class Engine {
fetchFunction: RNFetchBlob.config({ fileCache: true }).fetch.bind(
RNFetchBlob,
),
+ // TO DO
closeAllConnections: () =>
console.log(
'TO DO: Create method to close all connections (Closes all connections for the given origin, and removes the references)',
),
});
- const approvalController = new ApprovalController({
- messenger: this.controllerMessenger.getRestricted({
- name: 'ApprovalController',
- }),
- showApprovalRequest: () => null,
- });
-
const phishingController = new PhishingController();
phishingController.updatePhishingLists();
- const additionalKeyrings = [QRHardwareKeyring];
-
- const getIdentities = () => {
- const identities = preferencesController.state.identities;
- const newIdentities = {};
- Object.keys(identities).forEach((key) => {
- newIdentities[key.toLowerCase()] = identities[key];
- });
- return newIdentities;
- };
-
- const keyringController = new KeyringController(
- {
- removeIdentity: preferencesController.removeIdentity.bind(
- preferencesController,
- ),
- syncIdentities: preferencesController.syncIdentities.bind(
- preferencesController,
- ),
- updateIdentities: preferencesController.updateIdentities.bind(
- preferencesController,
- ),
- setSelectedAddress: preferencesController.setSelectedAddress.bind(
- preferencesController,
- ),
- setAccountLabel: preferencesController.setAccountLabel.bind(
- preferencesController,
- ),
- },
- { encryptor, keyringTypes: additionalKeyrings },
- initialState.KeyringController,
- );
-
const controllers = [
keyringController,
new AccountTrackerController({
@@ -503,28 +577,7 @@ class Engine {
),
gasFeeController,
approvalController,
- new PermissionController({
- messenger: this.controllerMessenger.getRestricted({
- name: 'PermissionController',
- allowedActions: [
- `${approvalController.name}:addRequest`,
- `${approvalController.name}:hasRequest`,
- `${approvalController.name}:acceptRequest`,
- `${approvalController.name}:rejectRequest`,
- ],
- }),
- state: initialState.PermissionController,
- caveatSpecifications: getCaveatSpecifications({ getIdentities }),
- permissionSpecifications: {
- ...getPermissionSpecifications({
- getAllAccounts: () => keyringController.getAccounts(),
- }),
- /*
- ...this.getSnapPermissionSpecifications(),
- */
- },
- unrestrictedMethods,
- }),
+ permissionController,
snapController,
];
diff --git a/app/core/Permissions/constants.js b/app/core/Permissions/constants.js
index 546945a4f6b..9f51c621608 100644
--- a/app/core/Permissions/constants.js
+++ b/app/core/Permissions/constants.js
@@ -4,4 +4,13 @@ export const CaveatTypes = Object.freeze({
export const RestrictedMethods = Object.freeze({
eth_accounts: 'eth_accounts',
+ // Snap Specific Restricted Methods
+ snap_confirm: 'snap_confirm',
+ snap_notify: 'snap_notify',
+ snap_manageState: 'snap_manageState',
+ snap_getBip32PublicKey: 'snap_getBip32PublicKey',
+ snap_getBip32Entropy: 'snap_getBip32Entropy',
+ snap_getBip44Entropy: 'snap_getBip44Entropy',
+ snap_getEntropy: 'snap_getEntropy',
+ 'wallet_snap_*': 'wallet_snap_*',
});
diff --git a/app/core/Permissions/specifications.js b/app/core/Permissions/specifications.js
index c0255d85943..c8c3aad5c8f 100644
--- a/app/core/Permissions/specifications.js
+++ b/app/core/Permissions/specifications.js
@@ -1,3 +1,5 @@
+import { endowmentCaveatSpecifications as snapsEndowmentCaveatSpecifications } from '@metamask/snap-controllers';
+import { caveatSpecifications as snapsCaveatsSpecifications } from '@metamask/rpc-methods';
import {
constructPermission,
PermissionType,
@@ -64,6 +66,8 @@ export const getCaveatSpecifications = ({ getIdentities }) => ({
validator: (caveat, _origin, _target) =>
validateCaveatAccounts(caveat.value, getIdentities),
},
+ ...snapsCaveatsSpecifications,
+ ...snapsEndowmentCaveatSpecifications,
});
/**
@@ -132,6 +136,14 @@ export const getPermissionSpecifications = ({ getAllAccounts }) => ({
}
},
},
+ [PermissionKeys.snap_confirm]: {
+ permissionType: PermissionType.RestrictedMethod,
+ targetKey: PermissionKeys.snap_confirm,
+ },
+ [PermissionKeys.snap_getBip44Entropy]: {
+ permissionType: PermissionType.RestrictedMethod,
+ targetKey: PermissionKeys.snap_getBip44Entropy,
+ },
});
/**
diff --git a/app/core/RPCMethods/RPCMethodMiddleware.ts b/app/core/RPCMethods/RPCMethodMiddleware.ts
index 4cae41ac601..61bf5d0f47d 100644
--- a/app/core/RPCMethods/RPCMethodMiddleware.ts
+++ b/app/core/RPCMethods/RPCMethodMiddleware.ts
@@ -29,6 +29,8 @@ export enum ApprovalTypes {
ADD_ETHEREUM_CHAIN = 'ADD_ETHEREUM_CHAIN',
SWITCH_ETHEREUM_CHAIN = 'SWITCH_ETHEREUM_CHAIN',
REQUEST_PERMISSIONS = 'wallet_requestPermissions',
+ INSTALL_SNAP = 'wallet_installSnap',
+ UPDATE_SNAP = 'wallet_updateSnap',
}
interface RPCMethodsMiddleParameters {
diff --git a/app/core/Snaps/index.ts b/app/core/Snaps/index.ts
index 88871986ebb..b8967a8def3 100644
--- a/app/core/Snaps/index.ts
+++ b/app/core/Snaps/index.ts
@@ -4,6 +4,12 @@ import WebviewExecutionService from './WebviewExecutionService';
import WebviewPostMessageStream from './WebviewPostMessageStream';
import SnapWebviewPostMessageStream from './SnapWebviewPostMessageStream';
import snapsState from './SnapsState';
+import {
+ buildSnapEndowmentSpecifications,
+ buildSnapRestrictedMethodSpecifications,
+ ExcludedSnapPermissions,
+ ExcludedSnapEndowments,
+} from './permissions';
export {
snapsState,
@@ -12,4 +18,8 @@ export {
WebviewExecutionService,
WebviewPostMessageStream,
SnapWebviewPostMessageStream,
+ buildSnapEndowmentSpecifications,
+ buildSnapRestrictedMethodSpecifications,
+ ExcludedSnapPermissions,
+ ExcludedSnapEndowments,
};
diff --git a/app/core/Snaps/permissions.ts b/app/core/Snaps/permissions.ts
new file mode 100644
index 00000000000..dce710ac7ce
--- /dev/null
+++ b/app/core/Snaps/permissions.ts
@@ -0,0 +1,41 @@
+import { endowmentPermissionBuilders } from '@metamask/snap-controllers';
+import {
+ restrictedMethodPermissionBuilders,
+ selectHooks,
+} from '@metamask/rpc-methods';
+
+export const ExcludedSnapPermissions = new Set(['snap_dialog']);
+export const ExcludedSnapEndowments = new Set(['endowment:keyring']);
+
+/**
+ * @returns {Record>} All endowment permission
+ * specifications.
+ */
+export const buildSnapEndowmentSpecifications = () =>
+ Object.values(endowmentPermissionBuilders).reduce(
+ (allSpecifications, { targetKey, specificationBuilder }) => {
+ if (!ExcludedSnapEndowments.has(targetKey)) {
+ allSpecifications[targetKey] = specificationBuilder();
+ }
+ return allSpecifications;
+ },
+ {},
+ );
+
+/**
+ * @param {Record} hooks - The hooks for the Snap
+ * restricted method implementations.
+ */
+export function buildSnapRestrictedMethodSpecifications(hooks: any) {
+ return Object.values(restrictedMethodPermissionBuilders).reduce(
+ (specifications, { targetKey, specificationBuilder, methodHooks }) => {
+ if (!ExcludedSnapPermissions.has(targetKey)) {
+ specifications[targetKey] = specificationBuilder({
+ methodHooks: selectHooks(hooks, methodHooks),
+ });
+ }
+ return specifications;
+ },
+ {},
+ );
+}
diff --git a/package.json b/package.json
index 0b2eb0ce488..46cfcf42695 100644
--- a/package.json
+++ b/package.json
@@ -153,8 +153,11 @@
"@metamask/permission-controller": "^1.0.2",
"@metamask/phishing-controller": "^1.1.0",
"@metamask/preferences-controller": "^1.0.1",
+ "@metamask/rpc-methods": "0.24.1",
+ "@metamask/post-message-stream": "6.0.0",
"@metamask/sdk-communication-layer": "^0.1.0",
"@metamask/snap-controllers": "0.23.0",
+ "@metamask/snaps-utils": "0.24.1",
"@metamask/swaps-controller": "^6.8.0",
"@metamask/transaction-controller": "^1.0.0",
"@ngraveio/bc-ur": "^1.1.6",
diff --git a/patches/@metamask+snap-controllers+0.23.0.patch b/patches/@metamask+snap-controllers+0.23.0.patch
index 20534694bba..7ed33b7d036 100644
--- a/patches/@metamask+snap-controllers+0.23.0.patch
+++ b/patches/@metamask+snap-controllers+0.23.0.patch
@@ -1,17 +1,16 @@
diff --git a/node_modules/@metamask/snap-controllers/dist/services/AbstractExecutionService.js b/node_modules/@metamask/snap-controllers/dist/services/AbstractExecutionService.js
-index 90e8295..613f999 100644
+index 90e8295..7ac6243 100644
--- a/node_modules/@metamask/snap-controllers/dist/services/AbstractExecutionService.js
+++ b/node_modules/@metamask/snap-controllers/dist/services/AbstractExecutionService.js
-@@ -12,6 +12,8 @@ const nanoid_1 = require("nanoid");
+@@ -12,6 +12,7 @@ const nanoid_1 = require("nanoid");
const pump_1 = __importDefault(require("pump"));
const json_rpc_middleware_stream_1 = require("json-rpc-middleware-stream");
const utils_2 = require("../utils");
+import { v4 as uuidv4 } from 'uuid';
-+
const controllerName = 'ExecutionService';
class AbstractExecutionService {
constructor({ setupSnapProvider, messenger, terminationTimeout = utils_1.Duration.Second, }) {
-@@ -43,6 +45,7 @@ class AbstractExecutionService {
+@@ -43,6 +44,7 @@ class AbstractExecutionService {
* @param jobId - The id of the job to be terminated.
*/
async terminate(jobId) {
@@ -19,19 +18,18 @@ index 90e8295..613f999 100644
const jobWrapper = this.jobs.get(jobId);
if (!jobWrapper) {
throw new Error(`Job with id "${jobId}" not found.`);
-@@ -52,8 +55,10 @@ class AbstractExecutionService {
+@@ -52,8 +54,9 @@ class AbstractExecutionService {
jsonrpc: '2.0',
method: 'terminate',
params: [],
- id: (0, nanoid_1.nanoid)(),
+ id: uuidv4(),
}), this._terminationTimeout);
-+
+ console.log('[EXEC SERVICE LOG] AbstractExecutionService+terminate: Snap result', result);
if (result === utils_2.hasTimedOut || result !== 'OK') {
// We tried to shutdown gracefully but failed. This probably means the Snap is in infite loop and
// hogging down the whole JS process.
-@@ -62,8 +67,10 @@ class AbstractExecutionService {
+@@ -62,8 +65,10 @@ class AbstractExecutionService {
// JS process.
console.error(`Job "${jobId}" failed to terminate gracefully.`, result);
}
@@ -42,16 +40,15 @@ index 90e8295..613f999 100644
!stream.destroyed && stream.destroy();
stream.removeAllListeners();
}
-@@ -74,7 +81,7 @@ class AbstractExecutionService {
- this._terminate(jobWrapper);
+@@ -75,6 +80,7 @@ class AbstractExecutionService {
this._removeSnapAndJobMapping(jobId);
this.jobs.delete(jobId);
-- console.log(`Job "${jobId}" terminated.`);
+ console.log(`Job "${jobId}" terminated.`);
+ console.log('[EXEC SERVICE LOG] AbstractExecutionService+executeSnap: Job', jobId, 'terminated and deleted');
}
/**
* Initiates a job for a snap.
-@@ -84,7 +91,7 @@ class AbstractExecutionService {
+@@ -84,7 +90,7 @@ class AbstractExecutionService {
* @returns Information regarding the created job.
*/
async _initJob() {
@@ -60,7 +57,7 @@ index 90e8295..613f999 100644
const { streams, worker } = await this._initStreams(jobId);
const rpcEngine = new json_rpc_engine_1.JsonRpcEngine();
const jsonRpcConnection = (0, json_rpc_middleware_stream_1.createStreamMiddleware)();
-@@ -97,6 +104,7 @@ class AbstractExecutionService {
+@@ -97,6 +103,7 @@ class AbstractExecutionService {
worker,
};
this.jobs.set(jobId, envMetadata);
@@ -68,7 +65,7 @@ index 90e8295..613f999 100644
return envMetadata;
}
/**
-@@ -108,6 +116,7 @@ class AbstractExecutionService {
+@@ -108,6 +115,7 @@ class AbstractExecutionService {
* @returns The streams to communicate with the worker and the worker itself.
*/
async _initStreams(jobId) {
@@ -76,15 +73,7 @@ index 90e8295..613f999 100644
const { worker, stream: envStream } = await this._initEnvStream(jobId);
// Typecast justification: stream type mismatch
const mux = setupMultiplex(envStream, `Job: "${jobId}"`);
-@@ -159,6 +168,7 @@ class AbstractExecutionService {
- * @param snapId - The ID of the snap to terminate.
- */
- async terminateSnap(snapId) {
-+
- const jobId = this.snapToJobMap.get(snapId);
- if (jobId) {
- await this.terminate(jobId);
-@@ -187,16 +197,18 @@ class AbstractExecutionService {
+@@ -187,6 +195,7 @@ class AbstractExecutionService {
* @throws If the execution service returns an error.
*/
async executeSnap(snapData) {
@@ -92,10 +81,7 @@ index 90e8295..613f999 100644
if (this.snapToJobMap.has(snapData.snapId)) {
throw new Error(`Snap "${snapData.snapId}" is already being executed.`);
}
- const job = await this._initJob();
- this._mapSnapAndJob(snapData.snapId, job.id);
- // Ping the worker to ensure that it started up
-+
+@@ -196,7 +205,7 @@ class AbstractExecutionService {
await this._command(job.id, {
jsonrpc: '2.0',
method: 'ping',
@@ -104,7 +90,7 @@ index 90e8295..613f999 100644
});
const rpcStream = job.streams.rpc;
this.setupSnapProvider(snapData.snapId, rpcStream);
-@@ -204,7 +216,7 @@ class AbstractExecutionService {
+@@ -204,7 +213,7 @@ class AbstractExecutionService {
jsonrpc: '2.0',
method: 'executeSnap',
params: snapData,
@@ -113,15 +99,15 @@ index 90e8295..613f999 100644
});
this._createSnapHooks(snapData.snapId, job.id);
return result;
-@@ -222,6 +234,7 @@ class AbstractExecutionService {
+@@ -222,6 +231,7 @@ class AbstractExecutionService {
if (response.error) {
throw new Error(response.error.message);
}
-+ // console.log({ response })
++ console.log('[EXEC SERVICE LOG] AbstractExecutionService+_command: response for jobId', jobId, '=>', response);
return response.result;
}
_removeSnapHooks(snapId) {
-@@ -230,7 +243,7 @@ class AbstractExecutionService {
+@@ -230,7 +240,7 @@ class AbstractExecutionService {
_createSnapHooks(snapId, workerId) {
const rpcHook = async ({ origin, handler, request }) => {
return await this._command(workerId, {
@@ -130,7 +116,7 @@ index 90e8295..613f999 100644
jsonrpc: '2.0',
method: 'snapRpc',
params: {
-@@ -241,6 +254,7 @@ class AbstractExecutionService {
+@@ -241,6 +251,7 @@ class AbstractExecutionService {
},
});
};
@@ -138,7 +124,7 @@ index 90e8295..613f999 100644
this._snapRpcHooks.set(snapId, rpcHook);
}
/**
-@@ -270,7 +284,9 @@ class AbstractExecutionService {
+@@ -270,7 +281,9 @@ class AbstractExecutionService {
if (!snapId) {
throw new Error(`job: "${jobId}" has no mapped snap.`);
}
@@ -149,10 +135,23 @@ index 90e8295..613f999 100644
this._removeSnapHooks(snapId);
}
diff --git a/node_modules/@metamask/snap-controllers/dist/snaps/SnapController.js b/node_modules/@metamask/snap-controllers/dist/snaps/SnapController.js
-index eb7e9bf..95b48aa 100644
+index eb7e9bf..38141a3 100644
--- a/node_modules/@metamask/snap-controllers/dist/snaps/SnapController.js
+++ b/node_modules/@metamask/snap-controllers/dist/snaps/SnapController.js
-@@ -378,6 +378,7 @@ class SnapController extends controllers_1.BaseControllerV2 {
+@@ -320,7 +320,12 @@ class SnapController extends controllers_1.BaseControllerV2 {
+ }
+ }
+ async _stopSnapsLastRequestPastMax() {
++
+ const entries = [...this._snapsRuntimeData.entries()];
++ entries.forEach((entry) => {
++ console.log('entry', { id: entry[0], activeReferences: entry[1].activeReferences, lastRequest: entry[1].lastRequest, pendingOutboundRequests: entry[1].pendingOutboundRequests, state: entry[1].state });
++ // console.log({ snapId: entry.snapId, references: entry.activeReferences, pendingInboundRequests: runtime.pendingInboundRequests.length, lastRequest: timeSince(runtime.lastRequest), maxIdleTime: this._maxIdleTime });
++ })
+ return Promise.all(entries
+ .filter(([_snapId, runtime]) => runtime.activeReferences === 0 &&
+ runtime.pendingInboundRequests.length === 0 &&
+@@ -378,6 +383,7 @@ class SnapController extends controllers_1.BaseControllerV2 {
* @param snapId - The id of the Snap to start.
*/
async startSnap(snapId) {
@@ -160,7 +159,7 @@ index eb7e9bf..95b48aa 100644
const runtime = this.getRuntimeExpect(snapId);
if (this.state.snaps[snapId].enabled === false) {
throw new Error(`Snap "${snapId}" is disabled.`);
-@@ -456,6 +457,7 @@ class SnapController extends controllers_1.BaseControllerV2 {
+@@ -456,6 +462,7 @@ class SnapController extends controllers_1.BaseControllerV2 {
* @param snapId - The snap to terminate.
*/
async terminateSnap(snapId) {
@@ -168,24 +167,7 @@ index eb7e9bf..95b48aa 100644
await this.messagingSystem.call('ExecutionService:terminateSnap', snapId);
this.messagingSystem.publish('SnapController:snapTerminated', this.getTruncatedExpect(snapId));
}
-@@ -728,6 +730,7 @@ class SnapController extends controllers_1.BaseControllerV2 {
- * snap couldn't be installed.
- */
- async installSnaps(origin, requestedSnaps) {
-+ console.log('[SNAP CONTROLLER LOG] SnapControllers+installSnaps: Install Snaps', requestedSnaps);
- const result = {};
- await Promise.all(Object.entries(requestedSnaps).map(async ([snapId, { version: rawVersion }]) => {
- const version = (0, snap_utils_1.resolveVersion)(rawVersion);
-@@ -738,7 +741,7 @@ class SnapController extends controllers_1.BaseControllerV2 {
- };
- return;
- }
-- if (await this.messagingSystem.call('PermissionController:hasPermission', origin, permissionName)) {
-+ if (true) {
- // Attempt to install and run the snap, storing any errors that
- // occur during the process.
- result[snapId] = Object.assign({}, (await this.processRequestedSnap(origin, snapId, version)));
-@@ -750,6 +753,7 @@ class SnapController extends controllers_1.BaseControllerV2 {
+@@ -750,6 +757,7 @@ class SnapController extends controllers_1.BaseControllerV2 {
};
}
}));
@@ -193,27 +175,22 @@ index eb7e9bf..95b48aa 100644
return result;
}
/**
-@@ -806,12 +810,18 @@ class SnapController extends controllers_1.BaseControllerV2 {
+@@ -806,11 +814,14 @@ class SnapController extends controllers_1.BaseControllerV2 {
id: snapId,
versionRange,
});
-- await this.authorize(origin, snapId);
-+
+ console.log('[SNAP CONTROLLER LOG] SnapControllers+processRequestedSnap: Snap', snapId, 'added');
-+
-+ // await this.authorize(origin, snapId);
-+
+ await this.authorize(origin, snapId);
++ console.log('[SNAP CONTROLLER LOG] SnapControllers+processRequestedSnap: Snap', snapId, 'authorized');
await this._startSnap({
snapId,
sourceCode,
});
-+
++ console.log('[SNAP CONTROLLER LOG] SnapControllers+processRequestedSnap: Snap', snapId, 'started');
const truncated = this.getTruncatedExpect(snapId);
-+
this.messagingSystem.publish(`SnapController:snapInstalled`, truncated);
return truncated;
- }
-@@ -846,6 +856,7 @@ class SnapController extends controllers_1.BaseControllerV2 {
+@@ -846,6 +857,7 @@ class SnapController extends controllers_1.BaseControllerV2 {
throw new Error(`Received invalid snap version range: "${newVersionRange}".`);
}
const newSnap = await this._fetchSnap(snapId, newVersionRange);
@@ -221,48 +198,15 @@ index eb7e9bf..95b48aa 100644
const newVersion = newSnap.manifest.version;
if (!(0, snap_utils_1.gtVersion)(newVersion, snap.version)) {
console.warn(`Tried updating snap "${snapId}" within "${newVersionRange}" version range, but newer version "${snap.version}" is already installed`);
-@@ -949,13 +960,19 @@ class SnapController extends controllers_1.BaseControllerV2 {
- throw error;
+@@ -950,6 +962,7 @@ class SnapController extends controllers_1.BaseControllerV2 {
}
}
-+
async _startSnap(snapData) {
+ console.log('[SNAP CONTROLLER LOG] SnapControllers+_startSnap: Start snap', snapData.snapId);
-+
const { snapId } = snapData;
if (this.isRunning(snapId)) {
throw new Error(`Snap "${snapId}" is already started.`);
- }
-+
- try {
-- const result = await this._executeWithTimeout(snapId, this.messagingSystem.call('ExecutionService:executeSnap', Object.assign(Object.assign({}, snapData), { endowments: await this._getEndowments(snapId) })));
-+ const endowments = await this._getEndowments(snapId)
-+ console.log('[SNAP CONTROLLER LOG] _startSnap - Current endowments =>', endowments);
-+ const result = await this._executeWithTimeout(snapId, this.messagingSystem.call('ExecutionService:executeSnap', Object.assign(Object.assign({}, snapData), { endowments })));
- this.transition(snapId, snap_utils_1.SnapStatusEvents.Start);
- return result;
- }
-@@ -978,8 +995,9 @@ class SnapController extends controllers_1.BaseControllerV2 {
- async _getEndowments(snapId) {
- let allEndowments = [];
- for (const permissionName of this._environmentEndowmentPermissions) {
-- if (await this.messagingSystem.call('PermissionController:hasPermission', snapId, permissionName)) {
-- const endowments = await this.messagingSystem.call('PermissionController:getEndowments', snapId, permissionName);
-+ if (true) {
-+ const dummyPromise = () => Promise.resolve([]);
-+ const endowments = await dummyPromise();
- if (endowments) {
- // We don't have any guarantees about the type of the endowments
- // value, so we have to guard at runtime.
-@@ -991,6 +1009,7 @@ class SnapController extends controllers_1.BaseControllerV2 {
- }
- }
- }
-+
- const dedupedEndowments = [
- ...new Set([...snap_utils_1.DEFAULT_ENDOWMENTS, ...allEndowments]),
- ];
-@@ -1103,21 +1122,23 @@ class SnapController extends controllers_1.BaseControllerV2 {
+@@ -1103,21 +1116,23 @@ class SnapController extends controllers_1.BaseControllerV2 {
// Local snaps are mostly used for development purposes. Fetches were cached in the browser and were not requested
// afterwards which lead to confusing development where old versions of snaps were installed.
// Thus we disable caching
@@ -273,7 +217,6 @@ index eb7e9bf..95b48aa 100644
throw new Error(`Invalid URL: Locally hosted Snaps must be hosted on localhost. Received URL: "${manifestUrl.toString()}"`);
}
- const manifest = await (await this._fetchFunction(manifestUrl.toString(), fetchOptions)).json();
-+
+ const manifest = await (await this._fetchFunction('GET', manifestUrl.toString(), fetchOptions)).json();
(0, snap_utils_1.assertIsSnapManifest)(manifest);
const { source: { location: { npm: { filePath, iconPath }, }, }, } = manifest;
@@ -286,11 +229,12 @@ index eb7e9bf..95b48aa 100644
: undefined,
]);
- (0, snap_utils_1.validateSnapShasum)(manifest, sourceCode);
++ // [TO DO] https://github.com/MetaMask/mobile-planning/issues/529
+ // (0, snap_utils_1.validateSnapShasum)(manifest, sourceCode);
return { manifest, sourceCode, svgIcon };
}
/**
-@@ -1228,6 +1249,7 @@ class SnapController extends controllers_1.BaseControllerV2 {
+@@ -1228,6 +1243,7 @@ class SnapController extends controllers_1.BaseControllerV2 {
// We need to set up this promise map to map snapIds to their respective startPromises,
// because otherwise we would lose context on the correct startPromise.
const startPromises = new Map();
@@ -298,47 +242,3 @@ index eb7e9bf..95b48aa 100644
const rpcHandler = async ({ origin, handler: handlerType, request, }) => {
if (this.state.snaps[snapId].enabled === false) {
throw new Error(`Snap "${snapId}" is disabled.`);
-@@ -1294,9 +1316,9 @@ class SnapController extends controllers_1.BaseControllerV2 {
- * @template PromiseValue - The value of the Promise.
- */
- async _executeWithTimeout(snapId, promise, timer) {
-- const isLongRunning = await this.messagingSystem.call('PermissionController:hasPermission', snapId, endowments_1.SnapEndowments.LongRunning);
-+ // const isLongRunning = await this.messagingSystem.call('PermissionController:hasPermission', snapId, endowments_1.SnapEndowments.LongRunning);
- // Long running snaps have timeouts disabled
-- if (isLongRunning) {
-+ if (true) {
- return promise;
- }
- const result = await (0, utils_2.withTimeout)(promise, timer !== null && timer !== void 0 ? timer : this._maxRequestTime);
-diff --git a/node_modules/@metamask/snap-controllers/dist/snaps/utils/npm.js b/node_modules/@metamask/snap-controllers/dist/snaps/utils/npm.js
-index 6a49c06..70f5c8a 100644
---- a/node_modules/@metamask/snap-controllers/dist/snaps/utils/npm.js
-+++ b/node_modules/@metamask/snap-controllers/dist/snaps/utils/npm.js
-@@ -53,7 +53,7 @@ exports.fetchNpmSnap = fetchNpmSnap;
- */
- async function fetchNpmTarball(packageName, versionRange, registryUrl = exports.DEFAULT_NPM_REGISTRY, fetchFunction = fetch) {
- var _a, _b, _c, _d;
-- const packageMetadata = await (await fetchFunction(new URL(packageName, registryUrl).toString())).json();
-+ const packageMetadata = await (await fetchFunction('GET', new URL(packageName, registryUrl).toString())).json();
- if (!(0, utils_1.isObject)(packageMetadata)) {
- throw new Error(`Failed to fetch package "${packageName}" metadata from npm.`);
- }
-@@ -71,11 +71,12 @@ async function fetchNpmTarball(packageName, versionRange, registryUrl = exports.
- newTarballUrl.hostname = newRegistryUrl.hostname;
- newTarballUrl.protocol = newRegistryUrl.protocol;
- // Perform a raw fetch because we want the Response object itself.
-- const tarballResponse = await fetchFunction(newTarballUrl.toString());
-- if (!tarballResponse.ok) {
-- throw new Error(`Failed to fetch tarball for package "${packageName}".`);
-- }
-- const stream = await tarballResponse.blob().then((blob) => blob.stream());
-+ const tarballResponse = await fetchFunction('GET', newTarballUrl.toString());
-+ // if (!tarballResponse.ok) {
-+ // throw new Error(`Failed to fetch tarball for package "${packageName}".`);
-+ // }
-+ // const stream = await tarballResponse.blob().then((blob) => blob.stream());
-+ console.log(await tarballResponse.blob());
- return [stream, targetVersion];
- }
- //# sourceMappingURL=npm.js.map
-\ No newline at end of file
diff --git a/yarn.lock b/yarn.lock
index 3f75af248ad..ca64316df71 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4399,6 +4399,14 @@
isomorphic-fetch "^3.0.0"
punycode "^2.1.1"
+"@metamask/post-message-stream@6.0.0":
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/@metamask/post-message-stream/-/post-message-stream-6.0.0.tgz#e8c26b1ef41452b2f1862004651ae3970b944868"
+ integrity sha512-z0l6UMW3z6W4qIhKSh48/6n2Q9AG1oOh9tus+Ncs9w6dAbkElKke+mXZ7tT6oAyE3T4JZLnDfGKbPWYoq+nrsA==
+ dependencies:
+ "@metamask/utils" "^2.0.0"
+ readable-stream "2.3.3"
+
"@metamask/post-message-stream@^6.0.0":
version "6.1.0"
resolved "https://registry.yarnpkg.com/@metamask/post-message-stream/-/post-message-stream-6.1.0.tgz#d36837ea02777e65c1151a8687d427f84bf7004f"
@@ -4415,6 +4423,24 @@
"@metamask/base-controller" "^1.1.1"
"@metamask/controller-utils" "^1.0.0"
+"@metamask/providers@^10.2.0":
+ version "10.2.1"
+ resolved "https://registry.yarnpkg.com/@metamask/providers/-/providers-10.2.1.tgz#61304940adeccc7421dcda30ffd1d834273cc77b"
+ integrity sha512-p2TXw2a1Nb8czntDGfeIYQnk4LLVbd5vlcb3GY//lylYlKdSqp+uUTegCvxiFblRDOT68jsY8Ib1VEEzVUOolA==
+ dependencies:
+ "@metamask/object-multiplex" "^1.1.0"
+ "@metamask/safe-event-emitter" "^2.0.0"
+ "@types/chrome" "^0.0.136"
+ detect-browser "^5.2.0"
+ eth-rpc-errors "^4.0.2"
+ extension-port-stream "^2.0.1"
+ fast-deep-equal "^2.0.1"
+ is-stream "^2.0.0"
+ json-rpc-engine "^6.1.0"
+ json-rpc-middleware-stream "^4.2.1"
+ pump "^3.0.0"
+ webextension-polyfill-ts "^0.25.0"
+
"@metamask/providers@^9.0.0":
version "9.1.0"
resolved "https://registry.yarnpkg.com/@metamask/providers/-/providers-9.1.0.tgz#ccbbfd698eeb777c5c45aee91c3ad97e20eab20b"
@@ -4433,6 +4459,21 @@
pump "^3.0.0"
webextension-polyfill-ts "^0.25.0"
+"@metamask/rpc-methods@0.24.1":
+ version "0.24.1"
+ resolved "https://registry.yarnpkg.com/@metamask/rpc-methods/-/rpc-methods-0.24.1.tgz#77bb9d3c0960a53b04aeec5e97967a7581a341f7"
+ integrity sha512-mUwN5Ya1F51p/yq81MAqUwR1D3R8CU1cLw3sKypfH0gVtwHNxXogp6Jeyv1VI/46Hrh4i9yXDztsRMOr+AjZEw==
+ dependencies:
+ "@metamask/controllers" "^32.0.2"
+ "@metamask/key-tree" "^6.0.0"
+ "@metamask/snaps-utils" "^0.24.1"
+ "@metamask/types" "^1.1.0"
+ "@metamask/utils" "^3.3.1"
+ "@noble/hashes" "^1.1.3"
+ eth-rpc-errors "^4.0.2"
+ nanoid "^3.1.31"
+ superstruct "^0.16.7"
+
"@metamask/rpc-methods@^0.23.0":
version "0.23.0"
resolved "https://registry.yarnpkg.com/@metamask/rpc-methods/-/rpc-methods-0.23.0.tgz#51e1ebb91891d7227d346b229a6f112b67984c8e"
@@ -4525,6 +4566,34 @@
ses "^0.17.0"
superstruct "^0.16.7"
+"@metamask/snaps-types@^0.24.1":
+ version "0.24.1"
+ resolved "https://registry.yarnpkg.com/@metamask/snaps-types/-/snaps-types-0.24.1.tgz#f315321f954611a7bdb514cf786a9c008897a3b6"
+ integrity sha512-Kt6pacC+nEISJm2BSa9Gu9HvYApIk03THCl6pmG8/HQr5g6r+VXRtQ56nQw7pHAiyV6eKlSITl/rFY+dYuJg6Q==
+ dependencies:
+ "@metamask/providers" "^10.2.0"
+ "@metamask/snaps-utils" "^0.24.1"
+ "@metamask/types" "^1.1.0"
+
+"@metamask/snaps-utils@0.24.1", "@metamask/snaps-utils@^0.24.1":
+ version "0.24.1"
+ resolved "https://registry.yarnpkg.com/@metamask/snaps-utils/-/snaps-utils-0.24.1.tgz#b95678828a739a5cf0c4e25c0843094931d3a0c7"
+ integrity sha512-W0wtDcvZd/y6MGyPWKnOBQ4ORpUTSCy8cwuxS0NsMuV1FhLgl9RlTmNWUlUS+eEGkUiQxdX8l+W34Cfe4ZZnuA==
+ dependencies:
+ "@babel/core" "^7.18.6"
+ "@babel/types" "^7.18.7"
+ "@metamask/snaps-types" "^0.24.1"
+ "@metamask/utils" "^3.3.1"
+ "@noble/hashes" "^1.1.3"
+ "@scure/base" "^1.1.1"
+ cron-parser "^4.5.0"
+ eth-rpc-errors "^4.0.3"
+ fast-deep-equal "^3.1.3"
+ rfdc "^1.3.0"
+ semver "^7.3.7"
+ ses "^0.17.0"
+ superstruct "^0.16.7"
+
"@metamask/swaps-controller@^6.8.0":
version "6.8.0"
resolved "https://registry.yarnpkg.com/@metamask/swaps-controller/-/swaps-controller-6.8.0.tgz#c2c43173dc1101fab9ec918e7f7853f14072740b"
@@ -4562,7 +4631,14 @@
resolved "https://registry.yarnpkg.com/@metamask/types/-/types-1.1.0.tgz#9bd14b33427932833c50c9187298804a18c2e025"
integrity sha512-EEV/GjlYkOSfSPnYXfOosxa3TqYtIW3fhg6jdw+cok/OhMgNn4wCfbENFqjytrHMU2f7ZKtBAvtiP5V8H44sSw==
-"@metamask/utils@^3.0.1":
+"@metamask/utils@^2.0.0":
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/@metamask/utils/-/utils-2.1.0.tgz#a65eaa0932b863383844ec323e05e293d8e718ab"
+ integrity sha512-4PHdo5B1ifpw6GbsdlDpp8oqA++rddSmt2pWBHtIGGL2tQMvmfHdaDDSns4JP9iC+AbMogVcUpv5Vt8ow1zsRA==
+ dependencies:
+ fast-deep-equal "^3.1.3"
+
+"@metamask/utils@^3.0.1", "@metamask/utils@^3.3.1":
version "3.4.1"
resolved "https://registry.yarnpkg.com/@metamask/utils/-/utils-3.4.1.tgz#7df750733960ee2bde27a00eb58fcfdf80570be2"
integrity sha512-FjhJrplzFiPNlNNuYXkY1ave55ULLZ3+kY/d3zaW5SjS5AjszlN7gsWz74VI32qME6VyD/gdoTOgqVbKlcEnag==
@@ -16469,6 +16545,14 @@ json-rpc-middleware-stream@3.0.0, json-rpc-middleware-stream@^3.0.0:
"@metamask/safe-event-emitter" "^2.0.0"
readable-stream "^2.3.3"
+json-rpc-middleware-stream@^4.2.1:
+ version "4.2.1"
+ resolved "https://registry.yarnpkg.com/json-rpc-middleware-stream/-/json-rpc-middleware-stream-4.2.1.tgz#e5cb8795ebfd7503c6ceaa43daaf065687cc2f22"
+ integrity sha512-6QKayke/8lg0nTjOpRCq4JCgRx7bVybldmloIfY21HSDV0GUevcV9i8DJNvuKTJx4KR9EDIf6HTStAnEovGUvA==
+ dependencies:
+ "@metamask/safe-event-emitter" "^2.0.0"
+ readable-stream "^2.3.3"
+
json-rpc-random-id@^1.0.0, json-rpc-random-id@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/json-rpc-random-id/-/json-rpc-random-id-1.0.1.tgz#ba49d96aded1444dbb8da3d203748acbbcdec8c8"