1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-12-23 09:52:26 +01:00

Add desktop support (#17683)

Use DesktopManager in background script to redirect internal and external connections to the desktop app.
Include DesktopController in the MetaMask controller.
Support desktop keyrings in MetaMask controller via the overrides object.
Create middleware handler to connect to the desktop app while UI code is pending.
Add build system support for desktop specific configuration variables.
This commit is contained in:
Matthew Walsh 2023-02-20 17:13:12 +00:00 committed by GitHub
parent 0af56b1b1e
commit cc99a25228
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 285 additions and 33 deletions

View File

@ -54,6 +54,16 @@ import setupEnsIpfsResolver from './lib/ens-ipfs/setup';
import { deferredPromise, getPlatform } from './lib/util'; import { deferredPromise, getPlatform } from './lib/util';
/* eslint-enable import/first */ /* eslint-enable import/first */
/* eslint-disable import/order */
///: BEGIN:ONLY_INCLUDE_IN(desktop)
import {
CONNECTION_TYPE_EXTERNAL,
CONNECTION_TYPE_INTERNAL,
} from '@metamask/desktop/dist/constants';
import DesktopManager from '@metamask/desktop/dist/desktop-manager';
///: END:ONLY_INCLUDE_IN
/* eslint-enable import/order */
const { sentry } = global; const { sentry } = global;
const firstTimeState = { ...rawFirstTimeState }; const firstTimeState = { ...rawFirstTimeState };
@ -97,6 +107,13 @@ const PHISHING_WARNING_PAGE_TIMEOUT = ONE_SECOND_IN_MILLISECONDS;
const ACK_KEEP_ALIVE_MESSAGE = 'ACK_KEEP_ALIVE_MESSAGE'; const ACK_KEEP_ALIVE_MESSAGE = 'ACK_KEEP_ALIVE_MESSAGE';
const WORKER_KEEP_ALIVE_MESSAGE = 'WORKER_KEEP_ALIVE_MESSAGE'; const WORKER_KEEP_ALIVE_MESSAGE = 'WORKER_KEEP_ALIVE_MESSAGE';
///: BEGIN:ONLY_INCLUDE_IN(desktop)
const OVERRIDE_ORIGIN = {
EXTENSION: 'EXTENSION',
DESKTOP: 'DESKTOP_APP',
};
///: END:ONLY_INCLUDE_IN
// Event emitter for state persistence // Event emitter for state persistence
export const statePersistenceEvents = new EventEmitter(); export const statePersistenceEvents = new EventEmitter();
@ -245,6 +262,11 @@ async function initialize() {
try { try {
const initState = await loadStateFromPersistence(); const initState = await loadStateFromPersistence();
const initLangCode = await getFirstPreferredLangCode(); const initLangCode = await getFirstPreferredLangCode();
///: BEGIN:ONLY_INCLUDE_IN(desktop)
await DesktopManager.init(platform.getVersion());
///: END:ONLY_INCLUDE_IN
setupController(initState, initLangCode); setupController(initState, initLangCode);
if (!isManifestV3) { if (!isManifestV3) {
await loadPhishingWarningPage(); await loadPhishingWarningPage();
@ -482,6 +504,26 @@ export function setupController(initState, initLangCode, overrides) {
* @param {Port} remotePort - The port provided by a new context. * @param {Port} remotePort - The port provided by a new context.
*/ */
connectRemote = async (remotePort) => { connectRemote = async (remotePort) => {
///: BEGIN:ONLY_INCLUDE_IN(desktop)
if (
DesktopManager.isDesktopEnabled() &&
OVERRIDE_ORIGIN.DESKTOP !== overrides?.getOrigin?.()
) {
DesktopManager.createStream(remotePort, CONNECTION_TYPE_INTERNAL).then(
() => {
// When in Desktop Mode the responsibility to send CONNECTION_READY is on the desktop app side
if (isManifestV3) {
// Message below if captured by UI code in app/scripts/ui.js which will trigger UI initialisation
// This ensures that UI is initialised only after background is ready
// It fixes the issue of blank screen coming when extension is loaded, the issue is very frequent in MV3
remotePort.postMessage({ name: 'CONNECTION_READY' });
}
},
);
return;
}
///: END:ONLY_INCLUDE_IN
const processName = remotePort.name; const processName = remotePort.name;
if (metamaskBlockedPorts.includes(remotePort.name)) { if (metamaskBlockedPorts.includes(remotePort.name)) {
@ -584,6 +626,16 @@ export function setupController(initState, initLangCode, overrides) {
// communication with page or other extension // communication with page or other extension
connectExternal = (remotePort) => { connectExternal = (remotePort) => {
///: BEGIN:ONLY_INCLUDE_IN(desktop)
if (
DesktopManager.isDesktopEnabled() &&
OVERRIDE_ORIGIN.DESKTOP !== overrides?.getOrigin?.()
) {
DesktopManager.createStream(remotePort, CONNECTION_TYPE_EXTERNAL);
return;
}
///: END:ONLY_INCLUDE_IN
const portStream = const portStream =
overrides?.getPortStream?.(remotePort) || new PortStream(remotePort); overrides?.getPortStream?.(remotePort) || new PortStream(remotePort);
controller.setupUntrustedCommunication({ controller.setupUntrustedCommunication({
@ -762,6 +814,14 @@ export function setupController(initState, initLangCode, overrides) {
updateBadge(); updateBadge();
} }
///: BEGIN:ONLY_INCLUDE_IN(desktop)
if (OVERRIDE_ORIGIN.DESKTOP !== overrides?.getOrigin?.()) {
controller.store.subscribe((state) => {
DesktopManager.setState(state);
});
}
///: END:ONLY_INCLUDE_IN
} }
// //

View File

@ -0,0 +1,43 @@
import { MESSAGE_TYPE } from '../../../../../../shared/constants/app';
/**
* A wrapper for `eth_accounts` that returns an empty array when permission is denied.
*/
const requestEthereumAccounts = {
methodNames: [MESSAGE_TYPE.ENABLE_DESKTOP],
implementation: enableDesktop,
hookNames: {
testDesktopConnection: true,
generateOtp: true,
},
};
export default requestEthereumAccounts;
/**
* @typedef {Record<string, Function>} EthAccountsOptions
* @property {Function} getAccounts - Gets the accounts for the requesting
* origin.
*/
/**
*
* @param {import('json-rpc-engine').JsonRpcRequest<unknown>} _req - The JSON-RPC request object.
* @param {import('json-rpc-engine').JsonRpcResponse<true>} res - The JSON-RPC response object.
* @param {Function} _next - The json-rpc-engine 'next' callback.
* @param {Function} end - The json-rpc-engine 'end' callback.
* @param {EthAccountsOptions} options - The RPC method hooks.
*/
async function enableDesktop(
_req,
res,
_next,
end,
{ testDesktopConnection, generateOtp },
) {
const testResult = await testDesktopConnection();
const otp = testResult.isConnected ? await generateOtp() : undefined;
res.result = { ...testResult, otp };
return end();
}

View File

@ -7,6 +7,10 @@ import sendMetadata from './send-metadata';
import switchEthereumChain from './switch-ethereum-chain'; import switchEthereumChain from './switch-ethereum-chain';
import watchAsset from './watch-asset'; import watchAsset from './watch-asset';
///: BEGIN:ONLY_INCLUDE_IN(desktop)
import enableDesktop from './desktop/enable-desktop';
///: END:ONLY_INCLUDE_IN
const handlers = [ const handlers = [
addEthereumChain, addEthereumChain,
ethAccounts, ethAccounts,
@ -16,5 +20,8 @@ const handlers = [
sendMetadata, sendMetadata,
switchEthereumChain, switchEthereumChain,
watchAsset, watchAsset,
///: BEGIN:ONLY_INCLUDE_IN(desktop)
enableDesktop,
///: END:ONLY_INCLUDE_IN
]; ];
export default handlers; export default handlers;

View File

@ -41,6 +41,7 @@ export const SENTRY_STATE = {
currentLocale: true, currentLocale: true,
customNonceValue: true, customNonceValue: true,
defaultHomeActiveTabName: true, defaultHomeActiveTabName: true,
desktopEnabled: true,
featureFlags: true, featureFlags: true,
firstTimeFlowType: true, firstTimeFlowType: true,
forgottenPassword: true, forgottenPassword: true,
@ -140,23 +141,7 @@ export default function setupSentry({ release, getState }) {
], ],
release, release,
beforeSend: (report) => rewriteReport(report, getState), beforeSend: (report) => rewriteReport(report, getState),
beforeBreadcrumb(breadcrumb) { beforeBreadcrumb: beforeBreadcrumb(getState),
if (getState) {
const appState = getState();
if (
Object.values(appState).length &&
(!appState?.store?.metamask?.participateInMetaMetrics ||
!appState?.store?.metamask?.completedOnboarding ||
breadcrumb?.category === 'ui.input')
) {
return null;
}
} else {
return null;
}
const newBreadcrumb = removeUrlsFromBreadCrumb(breadcrumb);
return newBreadcrumb;
},
}); });
return Sentry; return Sentry;
@ -178,6 +163,32 @@ function hideUrlIfNotInternal(url) {
return url; return url;
} }
/**
* Returns a method that handles the Sentry breadcrumb using a specific method to get the extension state
*
* @param {Function} getState - A method that returns the state of the extension
* @returns {(breadcrumb: object) => object} A method that modifies a Sentry breadcrumb object
*/
export function beforeBreadcrumb(getState) {
return (breadcrumb) => {
if (getState) {
const appState = getState();
if (
Object.values(appState).length &&
(!appState?.store?.metamask?.participateInMetaMetrics ||
!appState?.store?.metamask?.completedOnboarding ||
breadcrumb?.category === 'ui.input')
) {
return null;
}
} else {
return null;
}
const newBreadcrumb = removeUrlsFromBreadCrumb(breadcrumb);
return newBreadcrumb;
};
}
/** /**
* Receives a Sentry breadcrumb object and potentially removes urls * Receives a Sentry breadcrumb object and potentially removes urls
* from its `data` property, it particular those possibly found at * from its `data` property, it particular those possibly found at
@ -315,8 +326,11 @@ function rewriteErrorMessages(report, rewriteFn) {
} }
function rewriteReportUrls(report) { function rewriteReportUrls(report) {
// update request url if (report.request?.url) {
report.request.url = toMetamaskUrl(report.request.url); // update request url
report.request.url = toMetamaskUrl(report.request.url);
}
// update exception stack trace // update exception stack trace
if (report.exception && report.exception.values) { if (report.exception && report.exception.values) {
report.exception.values.forEach((item) => { report.exception.values.forEach((item) => {
@ -330,6 +344,10 @@ function rewriteReportUrls(report) {
} }
function toMetamaskUrl(origUrl) { function toMetamaskUrl(origUrl) {
if (!globalThis.location?.origin) {
return origUrl;
}
const filePath = origUrl?.split(globalThis.location.origin)[1]; const filePath = origUrl?.split(globalThis.location.origin)[1];
if (!filePath) { if (!filePath) {
return origUrl; return origUrl;

View File

@ -176,6 +176,16 @@ import {
import createRPCMethodTrackingMiddleware from './lib/createRPCMethodTrackingMiddleware'; import createRPCMethodTrackingMiddleware from './lib/createRPCMethodTrackingMiddleware';
import { securityProviderCheck } from './lib/security-provider-helpers'; import { securityProviderCheck } from './lib/security-provider-helpers';
/* eslint-disable import/first */
/* eslint-disable import/order */
///: BEGIN:ONLY_INCLUDE_IN(desktop)
import { DesktopController } from '@metamask/desktop/dist/controllers/desktop';
///: END:ONLY_INCLUDE_IN
/* eslint-enable import/first */
/* eslint-enable import/order */
export const METAMASK_CONTROLLER_EVENTS = { export const METAMASK_CONTROLLER_EVENTS = {
// Fired after state changes that impact the extension badge (unapproved msg count) // Fired after state changes that impact the extension badge (unapproved msg count)
// The process of updating the badge happens in app/scripts/background.js. // The process of updating the badge happens in app/scripts/background.js.
@ -650,10 +660,12 @@ export default class MetamaskController extends EventEmitter {
let additionalKeyrings = [keyringBuilderFactory(QRHardwareKeyring)]; let additionalKeyrings = [keyringBuilderFactory(QRHardwareKeyring)];
if (this.canUseHardwareWallets()) { if (this.canUseHardwareWallets()) {
const keyringOverrides = this.opts.overrides?.keyrings;
const additionalKeyringTypes = [ const additionalKeyringTypes = [
TrezorKeyring, keyringOverrides?.trezor || TrezorKeyring,
LedgerBridgeKeyring, keyringOverrides?.ledger || LedgerBridgeKeyring,
LatticeKeyring, keyringOverrides?.lattice || LatticeKeyring,
QRHardwareKeyring, QRHardwareKeyring,
]; ];
additionalKeyrings = additionalKeyringTypes.map((keyringType) => additionalKeyrings = additionalKeyringTypes.map((keyringType) =>
@ -743,7 +755,7 @@ export default class MetamaskController extends EventEmitter {
}); });
///: BEGIN:ONLY_INCLUDE_IN(flask) ///: BEGIN:ONLY_INCLUDE_IN(flask)
this.snapExecutionService = new IframeExecutionService({ const snapExecutionServiceArgs = {
iframeUrl: new URL( iframeUrl: new URL(
'https://metamask.github.io/iframe-execution-environment/0.12.0', 'https://metamask.github.io/iframe-execution-environment/0.12.0',
), ),
@ -751,7 +763,11 @@ export default class MetamaskController extends EventEmitter {
name: 'ExecutionService', name: 'ExecutionService',
}), }),
setupSnapProvider: this.setupSnapProvider.bind(this), setupSnapProvider: this.setupSnapProvider.bind(this),
}); };
this.snapExecutionService =
this.opts.overrides?.createSnapExecutionService?.(
snapExecutionServiceArgs,
) || new IframeExecutionService(snapExecutionServiceArgs);
const snapControllerMessenger = this.controllerMessenger.getRestricted({ const snapControllerMessenger = this.controllerMessenger.getRestricted({
name: 'SnapController', name: 'SnapController',
@ -852,6 +868,7 @@ export default class MetamaskController extends EventEmitter {
messenger: cronjobControllerMessenger, messenger: cronjobControllerMessenger,
}); });
///: END:ONLY_INCLUDE_IN ///: END:ONLY_INCLUDE_IN
this.detectTokensController = new DetectTokensController({ this.detectTokensController = new DetectTokensController({
preferences: this.preferencesController, preferences: this.preferencesController,
tokensController: this.tokensController, tokensController: this.tokensController,
@ -1112,6 +1129,12 @@ export default class MetamaskController extends EventEmitter {
initState.SmartTransactionsController, initState.SmartTransactionsController,
); );
///: BEGIN:ONLY_INCLUDE_IN(desktop)
this.desktopController = new DesktopController({
initState: initState.DesktopController,
});
///: END:ONLY_INCLUDE_IN
// ensure accountTracker updates balances after network change // ensure accountTracker updates balances after network change
this.networkController.on(NETWORK_EVENTS.NETWORK_DID_CHANGE, () => { this.networkController.on(NETWORK_EVENTS.NETWORK_DID_CHANGE, () => {
this.accountTracker._updateAccounts(); this.accountTracker._updateAccounts();
@ -1219,6 +1242,9 @@ export default class MetamaskController extends EventEmitter {
CronjobController: this.cronjobController, CronjobController: this.cronjobController,
NotificationController: this.notificationController, NotificationController: this.notificationController,
///: END:ONLY_INCLUDE_IN ///: END:ONLY_INCLUDE_IN
///: BEGIN:ONLY_INCLUDE_IN(desktop)
DesktopController: this.desktopController.store,
///: END:ONLY_INCLUDE_IN
...resetOnRestartStore, ...resetOnRestartStore,
}); });
@ -1251,6 +1277,9 @@ export default class MetamaskController extends EventEmitter {
CronjobController: this.cronjobController, CronjobController: this.cronjobController,
NotificationController: this.notificationController, NotificationController: this.notificationController,
///: END:ONLY_INCLUDE_IN ///: END:ONLY_INCLUDE_IN
///: BEGIN:ONLY_INCLUDE_IN(desktop)
DesktopController: this.desktopController.store,
///: END:ONLY_INCLUDE_IN
...resetOnRestartStore, ...resetOnRestartStore,
}, },
controllerMessenger: this.controllerMessenger, controllerMessenger: this.controllerMessenger,
@ -2154,6 +2183,24 @@ export default class MetamaskController extends EventEmitter {
assetsContractController.getBalancesInSingleCall.bind( assetsContractController.getBalancesInSingleCall.bind(
assetsContractController, assetsContractController,
), ),
///: BEGIN:ONLY_INCLUDE_IN(desktop)
getDesktopEnabled: this.desktopController.getDesktopEnabled.bind(
this.desktopController,
),
setDesktopEnabled: this.desktopController.setDesktopEnabled.bind(
this.desktopController,
),
generateOtp: this.desktopController.generateOtp.bind(
this.desktopController,
),
testDesktopConnection: this.desktopController.testDesktopConnection.bind(
this.desktopController,
),
disableDesktop: this.desktopController.disableDesktop.bind(
this.desktopController,
),
///: END:ONLY_INCLUDE_IN
}; };
} }
@ -2623,6 +2670,7 @@ export default class MetamaskController extends EventEmitter {
// //
async getKeyringForDevice(deviceName, hdPath = null) { async getKeyringForDevice(deviceName, hdPath = null) {
const keyringOverrides = this.opts.overrides?.keyrings;
let keyringName = null; let keyringName = null;
if ( if (
deviceName !== HardwareDeviceNames.QR && deviceName !== HardwareDeviceNames.QR &&
@ -2632,16 +2680,17 @@ export default class MetamaskController extends EventEmitter {
} }
switch (deviceName) { switch (deviceName) {
case HardwareDeviceNames.trezor: case HardwareDeviceNames.trezor:
keyringName = TrezorKeyring.type; keyringName = keyringOverrides?.trezor?.type || TrezorKeyring.type;
break; break;
case HardwareDeviceNames.ledger: case HardwareDeviceNames.ledger:
keyringName = LedgerBridgeKeyring.type; keyringName =
keyringOverrides?.ledger?.type || LedgerBridgeKeyring.type;
break; break;
case HardwareDeviceNames.qr: case HardwareDeviceNames.qr:
keyringName = QRHardwareKeyring.type; keyringName = QRHardwareKeyring.type;
break; break;
case HardwareDeviceNames.lattice: case HardwareDeviceNames.lattice:
keyringName = LatticeKeyring.type; keyringName = keyringOverrides?.lattice?.type || LatticeKeyring.type;
break; break;
default: default:
throw new Error( throw new Error(
@ -3999,6 +4048,11 @@ export default class MetamaskController extends EventEmitter {
this.alertController.setWeb3ShimUsageRecorded.bind( this.alertController.setWeb3ShimUsageRecorded.bind(
this.alertController, this.alertController,
), ),
///: BEGIN:ONLY_INCLUDE_IN(desktop)
testDesktopConnection: this.desktopController.testDesktopConnection,
generateOtp: this.desktopController.generateOtp,
///: END:ONLY_INCLUDE_IN
}), }),
); );

View File

@ -182,11 +182,12 @@ export default class ExtensionPlatform {
this._showNotification(title, message); this._showNotification(title, message);
} }
_showNotification(title, message, url) { async _showNotification(title, message, url) {
const iconUrl = await browser.runtime.getURL('../../images/icon-64.png');
browser.notifications.create(url, { browser.notifications.create(url, {
type: 'basic', type: 'basic',
title, title,
iconUrl: browser.runtime.getURL('../../images/icon-64.png'), iconUrl,
message, message,
}); });
} }

View File

@ -15,6 +15,11 @@ const configurationPropertyNames = [
'SEGMENT_WRITE_KEY', 'SEGMENT_WRITE_KEY',
'SENTRY_DSN_DEV', 'SENTRY_DSN_DEV',
'SWAPS_USE_DEV_APIS', 'SWAPS_USE_DEV_APIS',
// Desktop
'COMPATIBILITY_VERSION_EXTENSION',
'DISABLE_WEB_SOCKET_ENCRYPTION',
'METAMASK_DEBUG',
'SKIP_OTP_PAIRING_FLOW',
]; ];
const productionConfigurationPropertyNames = [ const productionConfigurationPropertyNames = [

View File

@ -1110,7 +1110,7 @@ async function getEnvironmentVariables({ buildTarget, buildType, version }) {
environment, environment,
testing, testing,
}), }),
METAMASK_DEBUG: devMode, METAMASK_DEBUG: devMode || config.METAMASK_DEBUG === '1',
METAMASK_ENVIRONMENT: environment, METAMASK_ENVIRONMENT: environment,
METAMASK_VERSION: version, METAMASK_VERSION: version,
METAMASK_BUILD_TYPE: buildType, METAMASK_BUILD_TYPE: buildType,
@ -1126,6 +1126,10 @@ async function getEnvironmentVariables({ buildTarget, buildType, version }) {
SWAPS_USE_DEV_APIS: config.SWAPS_USE_DEV_APIS === '1', SWAPS_USE_DEV_APIS: config.SWAPS_USE_DEV_APIS === '1',
TOKEN_ALLOWANCE_IMPROVEMENTS: config.TOKEN_ALLOWANCE_IMPROVEMENTS === '1', TOKEN_ALLOWANCE_IMPROVEMENTS: config.TOKEN_ALLOWANCE_IMPROVEMENTS === '1',
TRANSACTION_SECURITY_PROVIDER: config.TRANSACTION_SECURITY_PROVIDER === '1', TRANSACTION_SECURITY_PROVIDER: config.TRANSACTION_SECURITY_PROVIDER === '1',
// Desktop
COMPATIBILITY_VERSION_EXTENSION: config.COMPATIBILITY_VERSION_EXTENSION,
DISABLE_WEB_SOCKET_ENCRYPTION: config.DISABLE_WEB_SOCKET_ENCRYPTION === '1',
SKIP_OTP_PAIRING_FLOW: config.SKIP_OTP_PAIRING_FLOW === '1',
}; };
} }

View File

@ -231,6 +231,7 @@
"@metamask/contract-metadata": "^2.2.0", "@metamask/contract-metadata": "^2.2.0",
"@metamask/controller-utils": "^1.0.0", "@metamask/controller-utils": "^1.0.0",
"@metamask/design-tokens": "^1.9.0", "@metamask/design-tokens": "^1.9.0",
"@metamask/desktop": "^0.2.0",
"@metamask/eth-json-rpc-infura": "^7.0.0", "@metamask/eth-json-rpc-infura": "^7.0.0",
"@metamask/eth-keyring-controller": "^10.0.0", "@metamask/eth-keyring-controller": "^10.0.0",
"@metamask/eth-ledger-bridge-keyring": "^0.13.0", "@metamask/eth-ledger-bridge-keyring": "^0.13.0",

View File

@ -27,6 +27,7 @@ export const ENVIRONMENT_TYPE_BACKGROUND = 'background';
*/ */
export const BuildType = { export const BuildType = {
beta: 'beta', beta: 'beta',
desktop: 'desktop',
flask: 'flask', flask: 'flask',
main: 'main', main: 'main',
} as const; } as const;
@ -60,6 +61,9 @@ export const MESSAGE_TYPE = {
SNAP_DIALOG_CONFIRMATION: `${RestrictedMethods.snap_dialog}:confirmation`, SNAP_DIALOG_CONFIRMATION: `${RestrictedMethods.snap_dialog}:confirmation`,
SNAP_DIALOG_PROMPT: `${RestrictedMethods.snap_dialog}:prompt`, SNAP_DIALOG_PROMPT: `${RestrictedMethods.snap_dialog}:prompt`,
///: END:ONLY_INCLUDE_IN ///: END:ONLY_INCLUDE_IN
///: BEGIN:ONLY_INCLUDE_IN(desktop)
ENABLE_DESKTOP: `metamask_enableDesktop`,
///: END:ONLY_INCLUDE_IN
} as const; } as const;
///: BEGIN:ONLY_INCLUDE_IN(flask) ///: BEGIN:ONLY_INCLUDE_IN(flask)

View File

@ -92,6 +92,7 @@ async function main() {
} }
const configFile = path.join(__dirname, '.mocharc.js'); const configFile = path.join(__dirname, '.mocharc.js');
const extraArgs = process.env.E2E_ARGS?.split(' ') || [];
const dir = 'test/test-results/e2e'; const dir = 'test/test-results/e2e';
fs.mkdir(dir, { recursive: true }); fs.mkdir(dir, { recursive: true });
@ -104,6 +105,7 @@ async function main() {
`--config=${configFile}`, `--config=${configFile}`,
`--timeout=${testTimeoutInMilliseconds}`, `--timeout=${testTimeoutInMilliseconds}`,
'--reporter=xunit', '--reporter=xunit',
...extraArgs,
e2eTestPath, e2eTestPath,
exit, exit,
], ],

View File

@ -3666,6 +3666,24 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@metamask/desktop@npm:^0.2.0":
version: 0.2.0
resolution: "@metamask/desktop@npm:0.2.0"
dependencies:
"@metamask/obs-store": ^5.0.0
eciesjs: ^0.3.15
end-of-stream: ^1.4.4
extension-port-stream: ^2.0.0
loglevel: ^1.8.0
obj-multiplex: ^1.0.0
otpauth: ^8.0.3
uuid: ^8.3.2
webextension-polyfill: ^0.8.0
ws: ^7.4.6
checksum: 052d5dd58951c77733b538d9c392a5aa5b7d87bb600cf04a97e3213048f84936a4446adbf901258417c8eed01dbc68eb2c7117a53b6af7a416ecea975460ffb7
languageName: node
linkType: hard
"@metamask/eslint-config-jest@npm:^9.0.0": "@metamask/eslint-config-jest@npm:^9.0.0":
version: 9.0.0 version: 9.0.0
resolution: "@metamask/eslint-config-jest@npm:9.0.0" resolution: "@metamask/eslint-config-jest@npm:9.0.0"
@ -7537,7 +7555,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@types/secp256k1@npm:^4.0.1": "@types/secp256k1@npm:^4.0.1, @types/secp256k1@npm:^4.0.3":
version: 4.0.3 version: 4.0.3
resolution: "@types/secp256k1@npm:4.0.3" resolution: "@types/secp256k1@npm:4.0.3"
dependencies: dependencies:
@ -14292,6 +14310,17 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"eciesjs@npm:^0.3.15":
version: 0.3.16
resolution: "eciesjs@npm:0.3.16"
dependencies:
"@types/secp256k1": ^4.0.3
futoin-hkdf: ^1.5.1
secp256k1: ^4.0.3
checksum: e5e6b8d8d27d8ca4aed89f79c581f7b9bd329551a2332b0a70d61c9b4e378ad98058cd7a16c9cee10cce8bef26a203865dc514ef10d732af3137ba5c13e4254d
languageName: node
linkType: hard
"ee-first@npm:1.1.1": "ee-first@npm:1.1.1":
version: 1.1.1 version: 1.1.1
resolution: "ee-first@npm:1.1.1" resolution: "ee-first@npm:1.1.1"
@ -17475,6 +17504,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"futoin-hkdf@npm:^1.5.1":
version: 1.5.1
resolution: "futoin-hkdf@npm:1.5.1"
checksum: 1912bbf6013e56ff2866590242c9493ab1fe83dc132a175378890b75008ca844524a61dbc20b3fe2c7276ea214589f92f3f0cd48e26d5f5d00c404a6201c5e23
languageName: node
linkType: hard
"ganache@npm:^v7.0.4": "ganache@npm:^v7.0.4":
version: 7.0.4 version: 7.0.4
resolution: "ganache@npm:7.0.4" resolution: "ganache@npm:7.0.4"
@ -22386,6 +22422,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"jssha@npm:~3.2.0":
version: 3.2.0
resolution: "jssha@npm:3.2.0"
checksum: 2adb8a9a57a79360379e843c0548e240d072c2ef12aef39ef6a784315686bd6f65501e9353fdd2f3a604f64af07e7eab04a0ed92b221cdfea97d671d7b8e14f4
languageName: node
linkType: hard
"jsx-ast-utils@npm:^2.4.1 || ^3.0.0": "jsx-ast-utils@npm:^2.4.1 || ^3.0.0":
version: 3.3.2 version: 3.3.2
resolution: "jsx-ast-utils@npm:3.3.2" resolution: "jsx-ast-utils@npm:3.3.2"
@ -23270,7 +23313,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"loglevel@npm:^1.8.1": "loglevel@npm:^1.8.0, loglevel@npm:^1.8.1":
version: 1.8.1 version: 1.8.1
resolution: "loglevel@npm:1.8.1" resolution: "loglevel@npm:1.8.1"
checksum: a1a62db40291aaeaef2f612334c49e531bff71cc1d01a2acab689ab80d59e092f852ab164a5aedc1a752fdc46b7b162cb097d8a9eb2cf0b299511106c29af61d checksum: a1a62db40291aaeaef2f612334c49e531bff71cc1d01a2acab689ab80d59e092f852ab164a5aedc1a752fdc46b7b162cb097d8a9eb2cf0b299511106c29af61d
@ -24019,6 +24062,7 @@ __metadata:
"@metamask/contract-metadata": ^2.2.0 "@metamask/contract-metadata": ^2.2.0
"@metamask/controller-utils": ^1.0.0 "@metamask/controller-utils": ^1.0.0
"@metamask/design-tokens": ^1.9.0 "@metamask/design-tokens": ^1.9.0
"@metamask/desktop": ^0.2.0
"@metamask/eslint-config": ^9.0.0 "@metamask/eslint-config": ^9.0.0
"@metamask/eslint-config-jest": ^9.0.0 "@metamask/eslint-config-jest": ^9.0.0
"@metamask/eslint-config-mocha": ^9.0.0 "@metamask/eslint-config-mocha": ^9.0.0
@ -26179,6 +26223,15 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"otpauth@npm:^8.0.3":
version: 8.0.3
resolution: "otpauth@npm:8.0.3"
dependencies:
jssha: ~3.2.0
checksum: bc5f95194c7c942eb1d17fa0d515934803ef7db951a2e89bc31b75dfff03c47403346147b54664861720bdff82e0849ad48914e47fd84776b014d5f7ed73763c
languageName: node
linkType: hard
"outpipe@npm:^1.1.0": "outpipe@npm:^1.1.0":
version: 1.1.1 version: 1.1.1
resolution: "outpipe@npm:1.1.1" resolution: "outpipe@npm:1.1.1"
@ -30405,7 +30458,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"secp256k1@npm:^4.0.0, secp256k1@npm:^4.0.1": "secp256k1@npm:^4.0.0, secp256k1@npm:^4.0.1, secp256k1@npm:^4.0.3":
version: 4.0.3 version: 4.0.3
resolution: "secp256k1@npm:4.0.3" resolution: "secp256k1@npm:4.0.3"
dependencies: dependencies: