Merge pull request #14143 from MetaMask/Version-v10.11.4

Version v10.11.4 RC
feature/default_network_editable
Dan J Miller 3 years ago committed by GitHub
commit 48fa62f42a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 95
      .circleci/config.yml
  2. 36
      .circleci/scripts/release-create-gh-release.sh
  3. 27
      CHANGELOG.md
  4. 11
      app/_locales/en/messages.json
  5. 2
      app/scripts/controllers/permissions/caveat-mutators.js
  6. 2
      app/scripts/controllers/permissions/caveat-mutators.test.js
  7. 5
      app/scripts/controllers/permissions/flask/snap-permissions.js
  8. 5
      app/scripts/controllers/permissions/specifications.js
  9. 4
      app/scripts/lib/rpc-method-middleware/createMethodMiddleware.js
  10. 11
      app/scripts/metamask-controller.js
  11. 6
      development/build/README.md
  12. 12
      development/build/etc.js
  13. 24
      development/build/index.js
  14. 2
      development/build/manifest.js
  15. 15
      development/build/scripts.js
  16. 2
      development/build/static.js
  17. 2
      development/build/transforms/remove-fenced-code.js
  18. 2
      development/build/transforms/remove-fenced-code.test.js
  19. 17
      development/build/utils.js
  20. 12
      development/lib/build-type.js
  21. 21
      development/lib/get-version.js
  22. 2
      development/metamaskbot-build-announce.js
  23. 77
      development/sentry-publish.js
  24. 17
      development/sentry-upload-artifacts.sh
  25. 8
      lavamoat/browserify/beta/policy.json
  26. 11
      lavamoat/browserify/flask/policy.json
  27. 8
      lavamoat/browserify/main/policy.json
  28. 17
      package.json
  29. 3
      shared/constants/permissions.js
  30. 12
      shared/constants/permissions.test.js
  31. 20
      test/e2e/run-all.js
  32. 98
      test/e2e/snaps/test-snap-confirm.spec.js
  33. 90
      test/e2e/snaps/test-snap-error.spec.js
  34. 2
      test/e2e/webdriver/chrome.js
  35. 2
      test/e2e/webdriver/driver.js
  36. 17
      test/e2e/webdriver/firefox.js
  37. 4
      test/e2e/webdriver/index.js
  38. 1
      ui/components/app/app-components.scss
  39. 4
      ui/components/app/flask/snap-install-warning/index.scss
  40. 5
      ui/components/app/flask/snap-install-warning/snap-install-warning.js
  41. 1
      ui/components/app/flask/snap-remove-warning/index.js
  42. 12
      ui/components/app/flask/snap-remove-warning/index.scss
  43. 64
      ui/components/app/flask/snap-remove-warning/snap-remove-warning.js
  44. 2
      ui/components/app/flask/snap-settings-card/snap-settings-card.js
  45. 2
      ui/components/app/permission-page-container/index.scss
  46. 33
      ui/components/app/permissions-connect-header/permissions-connect-header.component.js
  47. 3
      ui/components/app/permissions-connect-permission-list/index.scss
  48. 16
      ui/components/ui/popover/README.mdx
  49. 14
      ui/components/ui/popover/index.scss
  50. 90
      ui/components/ui/popover/popover.component.js
  51. 21
      ui/components/ui/popover/popover.stories.js
  52. 4
      ui/pages/permissions-connect/flask/snap-install/index.scss
  53. 17
      ui/pages/permissions-connect/flask/snap-install/snap-install.js
  54. 2
      ui/pages/permissions-connect/permissions-connect.component.js
  55. 8
      ui/pages/settings/flask/view-snap/index.scss
  56. 25
      ui/pages/settings/flask/view-snap/view-snap.js
  57. 139
      yarn.lock

@ -53,6 +53,9 @@ workflows:
- prep-build-test:
requires:
- prep-deps
- prep-build-test-flask:
requires:
- prep-deps
- prep-build-test-metrics:
requires:
- prep-deps
@ -78,6 +81,12 @@ workflows:
- test-e2e-firefox:
requires:
- prep-build-test
- test-e2e-chrome-snaps:
requires:
- prep-build-test-flask
- test-e2e-firefox-snaps:
requires:
- prep-build-test-flask
- test-e2e-chrome-metrics:
requires:
- prep-build-test-metrics
@ -130,6 +139,8 @@ workflows:
- test-e2e-firefox
- test-e2e-chrome-metrics
- test-e2e-firefox-metrics
- test-e2e-chrome-snaps
- test-e2e-firefox-snaps
- benchmark:
requires:
- prep-build-test
@ -149,6 +160,7 @@ workflows:
requires:
- prep-deps
- prep-build
- prep-build-flask
- all-tests-pass
- job-publish-storybook:
filters:
@ -283,6 +295,28 @@ jobs:
- dist-flask
- builds-flask
prep-build-test-flask:
executor: node-browsers-medium-plus
steps:
- checkout
- attach_workspace:
at: .
- run:
name: Build extension for testing
command: yarn build:test:flask
- run:
name: Move test build to 'dist-test' to avoid conflict with production build
command: mv ./dist ./dist-test-flask
- run:
name: Move test zips to 'builds-test' to avoid conflict with production build
command: mv ./builds ./builds-test-flask
- persist_to_workspace:
root: .
paths:
- dist-test-flask
- builds-test-flask
prep-build-test:
executor: node-browsers-medium-plus
steps:
@ -465,6 +499,60 @@ jobs:
path: test-artifacts
destination: test-artifacts
test-e2e-firefox-snaps:
executor: node-browsers
steps:
- checkout
- run:
name: Install Firefox
command: ./.circleci/scripts/firefox-install.sh
- attach_workspace:
at: .
- run:
name: Move test build to dist
command: mv ./dist-test-flask ./dist
- run:
name: Move test zips to builds
command: mv ./builds-test-flask ./builds
- run:
name: test:e2e:firefox:snaps
command: |
if .circleci/scripts/test-run-e2e.sh
then
yarn test:e2e:firefox:snaps --retries 2
fi
no_output_timeout: 20m
- store_artifacts:
path: test-artifacts
destination: test-artifacts
test-e2e-chrome-snaps:
executor: node-browsers
steps:
- checkout
- run:
name: Re-Install Chrome
command: ./.circleci/scripts/chrome-install.sh
- attach_workspace:
at: .
- run:
name: Move test build to dist
command: mv ./dist-test-flask ./dist
- run:
name: Move test zips to builds
command: mv ./builds-test-flask ./builds
- run:
name: test:e2e:chrome:snaps
command: |
if .circleci/scripts/test-run-e2e.sh
then
yarn test:e2e:chrome:snaps --retries 2
fi
no_output_timeout: 20m
- store_artifacts:
path: test-artifacts
destination: test-artifacts
test-e2e-chrome-metrics:
executor: node-browsers
steps:
@ -631,8 +719,11 @@ jobs:
- attach_workspace:
at: .
- run:
name: sentry sourcemaps upload
command: SENTRY_ORG=metamask SENTRY_PROJECT=metamask yarn sentry:publish
name: Publish main release to Sentry
command: yarn sentry:publish
- run:
name: Publish Flask release to Sentry
command: yarn sentry:publish --build-type flask
- run:
name: Create GitHub release
command: |

@ -26,25 +26,53 @@ function install_github_cli ()
popd
}
function print_flask_version ()
{
local flask_filename
flask_filename="$(find ./builds-flask -type f -name 'metamask-flask-chrome-*.zip' -exec basename {} .zip \;)"
local flask_build_filename_prefix
flask_build_filename_prefix='metamask-flask-chrome-'
local flask_build_filename_prefix_size
flask_build_filename_prefix_size="${#flask_build_filename_prefix}"
# Use substring parameter expansion to remove the filename prefix, leaving just the version
echo "${flask_filename:$flask_build_filename_prefix_size}"
}
function publish_flask_tag ()
{
local flask_version="${1}"; shift
git config user.email "metamaskbot@users.noreply.github.com"
git config user.name "MetaMask Bot"
git tag -a "v${flask_version}" -m "Flask version ${flask_version}"
repo_slug="$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME"
git push "https://$GITHUB_TOKEN@github.com/$repo_slug" "v${flask_version}"
}
current_commit_msg=$(git show -s --format='%s' HEAD)
if [[ $current_commit_msg =~ Version[-[:space:]](v[[:digit:]]+.[[:digit:]]+.[[:digit:]]+) ]]
then
tag="${BASH_REMATCH[1]}"
flask_version="$(print_flask_version)"
install_github_cli
printf '%s\n' 'Creating GitHub Release'
release_body="$(awk -v version="${tag##v}" -f .circleci/scripts/show-changelog.awk CHANGELOG.md)"
pushd builds
hub release create \
--attach metamask-chrome-*.zip \
--attach metamask-firefox-*.zip \
--attach builds/metamask-chrome-*.zip \
--attach builds/metamask-firefox-*.zip \
--attach builds-flask/metamask-flask-chrome-*.zip \
--attach builds-flask/metamask-flask-firefox-*.zip \
--message "Version ${tag##v}" \
--message "$release_body" \
--commitish "$CIRCLE_SHA1" \
"$tag"
popd
publish_flask_tag "${flask_version}"
else
printf '%s\n' 'Version not found in commit message; skipping GitHub Release'
exit 0

@ -6,6 +6,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
## [10.11.4]
### Added
- **[FLASK]** Snap removal confirmation ([#13619](https://github.com/MetaMask/metamask-extension/pull/13619))
### Changed
- **[FLASK]** Update MetaMask Flask
- This is the first release of [MetaMask Flask](https://metamask.io/flask) since the initial release on January 18. This release includes a significant number of fixes and DevX improvements. Flask will henceforth be released at a more frequent cadence, usually in close proximity to releases of the regular MetaMask Extension.
- For reference, [#13462](https://github.com/MetaMask/metamask-extension/pull/13462) used the feature branch that produced the original Flask release after some additional changes were made.
- **[FLASK]** Update Snaps packages to version `^0.10.6` ([#13901](https://github.com/MetaMask/metamask-extension/pull/13901), [#14041](https://github.com/MetaMask/metamask-extension/pull/14041), [#14070](https://github.com/MetaMask/metamask-extension/pull/14070))
- Updates the following packages from `0.9.0` to `0.10.6`:
- `@metamask/iframe-execution-environment-service`
- `@metamask/rpc-methods`
- `@metamask/snap-controllers`
- Updates the targeted [`iframe-execution-environment`](https://github.com/MetaMask/iframe-execution-environment) version from `0.3.1` to `0.4.2`.
- These changes encompass a variety of fixes and devX improvements. See the [releases](https://github.com/MetaMask/snaps-skunkworks/releases) of the Snaps monorepo for details.
### Fixed
- **[FLASK]** Various UI issues ([#13462](https://github.com/MetaMask/metamask-extension/pull/13462))
- _Note:_ The original Flask release was cut from the feature branch of [#13462](https://github.com/MetaMask/metamask-extension/pull/13462) before it was merged.
- Fix Snaps permission request confirmation page title ([#13342](https://github.com/MetaMask/metamask-extension/pull/13342))
- Fix Snaps custom confirmation `textarea` height ([#13572](https://github.com/MetaMask/metamask-extension/pull/13572))
- Fix various styling issues ([#13577](https://github.com/MetaMask/metamask-extension/pull/13577))
- **[FLASK]** Fix Snap key management install warning appearance ([#13844](https://github.com/MetaMask/metamask-extension/pull/13844))
## [10.11.3]
### Changed
- Split secret recovery phrase input into one-field-per-word ([#14016](https://github.com/MetaMask/metamask-extension/pull/14016))
@ -2783,7 +2807,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Uncategorized
- Added the ability to restore accounts from seed words.
[Unreleased]: https://github.com/MetaMask/metamask-extension/compare/v10.11.3...HEAD
[Unreleased]: https://github.com/MetaMask/metamask-extension/compare/v10.11.4...HEAD
[10.11.4]: https://github.com/MetaMask/metamask-extension/compare/v10.11.3...v10.11.4
[10.11.3]: https://github.com/MetaMask/metamask-extension/compare/v10.11.2...v10.11.3
[10.11.2]: https://github.com/MetaMask/metamask-extension/compare/v10.11.1...v10.11.2
[10.11.1]: https://github.com/MetaMask/metamask-extension/compare/v10.11.0...v10.11.1

@ -2299,6 +2299,9 @@
"personalAddressDetected": {
"message": "Personal address detected. Input the token contract address."
},
"pleaseConfirm": {
"message": "Please confirm"
},
"plusXMore": {
"message": "+ $1 more",
"description": "$1 is a number of additional but unshown items in a list- this message will be shown in place of those items"
@ -2434,6 +2437,10 @@
"removeSnap": {
"message": "Remove Snap"
},
"removeSnapConfirmation": {
"message": "Are you sure you want to remove $1?",
"description": "$1 represents the name of the snap"
},
"removeSnapDescription": {
"message": "This action will delete the snap, its data and revoke your given permissions."
},
@ -2662,6 +2669,10 @@
"settings": {
"message": "Settings"
},
"shorthandVersion": {
"message": "v$1",
"description": "$1 is replaced by a version string (e.g. 1.2.3)"
},
"show": {
"message": "Show"
},

@ -1,4 +1,4 @@
import { CaveatMutatorOperation } from '@metamask/snap-controllers';
import { CaveatMutatorOperation } from '@metamask/controllers';
import { CaveatTypes } from '../../../../shared/constants/permissions';
/**

@ -1,4 +1,4 @@
import { CaveatMutatorOperation } from '@metamask/snap-controllers';
import { CaveatMutatorOperation } from '@metamask/controllers';
import { CaveatTypes } from '../../../../shared/constants/permissions';
import { CaveatMutatorFactories } from './caveat-mutators';

@ -1,8 +1,9 @@
import { endowmentPermissionBuilders } from '@metamask/controllers';
import {
restrictedMethodPermissionBuilders,
selectHooks,
} from '@metamask/rpc-methods';
import { endowmentPermissionBuilders } from '@metamask/snap-controllers';
import { ExcludedSnapPermissions } from '../../../../../shared/constants/permissions';
/**
* @returns {Record<string, Record<string, unknown>>} All endowment permission
@ -24,9 +25,11 @@ export const buildSnapEndowmentSpecifications = () =>
export function buildSnapRestrictedMethodSpecifications(hooks) {
return Object.values(restrictedMethodPermissionBuilders).reduce(
(specifications, { targetKey, specificationBuilder, methodHooks }) => {
if (!ExcludedSnapPermissions.has(targetKey)) {
specifications[targetKey] = specificationBuilder({
methodHooks: selectHooks(hooks, methodHooks),
});
}
return specifications;
},
{},

@ -1,7 +1,4 @@
import {
constructPermission,
PermissionType,
} from '@metamask/snap-controllers';
import { constructPermission, PermissionType } from '@metamask/controllers';
import {
CaveatTypes,
RestrictedMethods,

@ -1,10 +1,10 @@
///: BEGIN:ONLY_INCLUDE_IN(flask)
import { handlers as permittedSnapMethods } from '@metamask/rpc-methods/dist/permitted';
///: END:ONLY_INCLUDE_IN
import { flatten } from 'lodash';
import { permissionRpcMethods } from '@metamask/snap-controllers';
import { permissionRpcMethods } from '@metamask/controllers';
import { selectHooks } from '@metamask/rpc-methods';
import { ethErrors } from 'eth-rpc-errors';
import { flatten } from 'lodash';
import { UNSUPPORTED_RPC_METHODS } from '../../../../shared/constants/network';
import localHandlers from './handlers';

@ -70,6 +70,7 @@ import { MILLISECOND } from '../../shared/constants/time';
import {
///: BEGIN:ONLY_INCLUDE_IN(flask)
MESSAGE_TYPE,
PLATFORM_FIREFOX,
///: END:ONLY_INCLUDE_IN
POLLING_TOKEN_ENVIRONMENT_TYPES,
SUBJECT_TYPES,
@ -130,6 +131,10 @@ import {
///: END:ONLY_INCLUDE_IN
} from './controllers/permissions';
///: BEGIN:ONLY_INCLUDE_IN(flask)
import { getPlatform } from './lib/util';
///: END:ONLY_INCLUDE_IN
export const METAMASK_CONTROLLER_EVENTS = {
// Fired after state changes that impact the extension badge (unapproved msg count)
// The process of updating the badge happens in app/scripts/background.js.
@ -580,7 +585,7 @@ export default class MetamaskController extends EventEmitter {
this.workerController = new IframeExecutionService({
onError: this.onExecutionEnvironmentError.bind(this),
iframeUrl: new URL(
'https://metamask.github.io/iframe-execution-environment/0.3.1',
'https://metamask.github.io/iframe-execution-environment/0.4.2',
),
messenger: this.controllerMessenger.getRestricted({
name: 'ExecutionService',
@ -598,12 +603,16 @@ export default class MetamaskController extends EventEmitter {
`${this.permissionController.name}:getEndowments`,
`${this.permissionController.name}:getPermissions`,
`${this.permissionController.name}:hasPermission`,
`${this.permissionController.name}:hasPermissions`,
`${this.permissionController.name}:requestPermissions`,
`${this.permissionController.name}:revokeAllPermissions`,
],
});
const usingFirefox = getPlatform() === PLATFORM_FIREFOX;
this.snapController = new SnapController({
npmRegistryUrl: usingFirefox ? 'https://registry.npmjs.cf/' : undefined,
endowmentPermissionNames: Object.values(EndowmentPermissions),
terminateAllSnaps: this.workerController.terminateAllSnaps.bind(
this.workerController,

@ -1,6 +1,7 @@
# The MetaMask Build System
> _tl;dr_ `yarn dist` for prod, `yarn start` for local development
> _tl;dr_ `yarn dist` for prod, `yarn start` for local development.
> Add `--build-type flask` to build Flask, our canary distribution with more experimental features.
This directory contains the MetaMask build system, which is used to build the MetaMask Extension such that it can be used in a supported browser.
From the repository root, the build system entry file is located at [`./development/build/index.js`](https://github.com/MetaMask/metamask-extension/blob/develop/development/build/index.js).
@ -40,7 +41,8 @@ Commands:
e2e tests.
Options:
--build-type The "type" of build to create. One of: "beta", "main"
--build-type The "type" of build to create. One of: "beta", "flask",
"main"
[string] [default: "main"]
--lint-fence-files Whether files with code fences should be linted after
fences have been removed by the code fencing transform.

@ -4,13 +4,13 @@ const gulpZip = require('gulp-zip');
const del = require('del');
const pify = require('pify');
const pump = pify(require('pump'));
const { version } = require('../../package.json');
const { BuildType } = require('../lib/build-type');
const { createTask, composeParallel } = require('./task');
const { BuildType } = require('./utils');
module.exports = createEtcTasks;
function createEtcTasks({ browserPlatforms, buildType, livereload }) {
function createEtcTasks({ browserPlatforms, buildType, livereload, version }) {
const clean = createTask('clean', async function clean() {
await del(['./dist/*']);
await Promise.all(
@ -28,14 +28,16 @@ function createEtcTasks({ browserPlatforms, buildType, livereload }) {
const zip = createTask(
'zip',
composeParallel(
...browserPlatforms.map((platform) => createZipTask(platform, buildType)),
...browserPlatforms.map((platform) =>
createZipTask(platform, buildType, version),
),
),
);
return { clean, reload, zip };
}
function createZipTask(platform, buildType) {
function createZipTask(platform, buildType, version) {
return async () => {
const path =
buildType === BuildType.main

@ -7,6 +7,8 @@ const path = require('path');
const livereload = require('gulp-livereload');
const minimist = require('minimist');
const { sync: globby } = require('globby');
const { getVersion } = require('../lib/get-version');
const { BuildType } = require('../lib/build-type');
const {
createTask,
composeSeries,
@ -18,7 +20,7 @@ const createScriptTasks = require('./scripts');
const createStyleTasks = require('./styles');
const createStaticAssetTasks = require('./static');
const createEtcTasks = require('./etc');
const { BuildType, getBrowserVersionMap } = require('./utils');
const { getBrowserVersionMap } = require('./utils');
// Packages required dynamically via browserify configuration in dependencies
// Required for LavaMoat policy generation
@ -58,11 +60,12 @@ function defineAndRunBuildTasks() {
shouldIncludeLockdown,
shouldLintFenceFiles,
skipStats,
version,
} = parseArgv();
const browserPlatforms = ['firefox', 'chrome', 'brave', 'opera'];
const browserVersionMap = getBrowserVersionMap(browserPlatforms);
const browserVersionMap = getBrowserVersionMap(browserPlatforms, version);
const ignoredFiles = getIgnoredFiles(buildType);
@ -89,12 +92,14 @@ function defineAndRunBuildTasks() {
livereload,
policyOnly,
shouldLintFenceFiles,
version,
});
const { clean, reload, zip } = createEtcTasks({
livereload,
browserPlatforms,
buildType,
version,
});
// build for development (livereload)
@ -162,6 +167,7 @@ function defineAndRunBuildTasks() {
function parseArgv() {
const NamedArgs = {
BuildType: 'build-type',
BuildVersion: 'build-version',
LintFenceFiles: 'lint-fence-files',
Lockdown: 'lockdown',
PolicyOnly: 'policy-only',
@ -175,9 +181,10 @@ function parseArgv() {
NamedArgs.PolicyOnly,
NamedArgs.SkipStats,
],
string: [NamedArgs.BuildType],
string: [NamedArgs.BuildType, NamedArgs.BuildVersion],
default: {
[NamedArgs.BuildType]: BuildType.main,
[NamedArgs.BuildVersion]: '0',
[NamedArgs.LintFenceFiles]: true,
[NamedArgs.Lockdown]: true,
[NamedArgs.PolicyOnly]: false,
@ -201,6 +208,14 @@ function parseArgv() {
throw new Error(`MetaMask build: Invalid build type: "${buildType}"`);
}
const rawBuildVersion = argv[NamedArgs.BuildVersion];
const buildVersion = Number.parseInt(rawBuildVersion, 10);
if (rawBuildVersion.match(/^\d+$/u) === null || Number.isNaN(buildVersion)) {
throw new Error(
`MetaMask build: Invalid build version: "${rawBuildVersion}"`,
);
}
// Manually default this to `false` for dev builds only.
const shouldLintFenceFiles = process.argv.includes(
`--${NamedArgs.LintFenceFiles}`,
@ -210,6 +225,8 @@ function parseArgv() {
const policyOnly = argv[NamedArgs.PolicyOnly];
const version = getVersion(buildType, buildVersion);
return {
buildType,
entryTask,
@ -218,6 +235,7 @@ function parseArgv() {
shouldIncludeLockdown: argv[NamedArgs.Lockdown],
shouldLintFenceFiles,
skipStats: argv[NamedArgs.SkipStats],
version,
};
}

@ -3,9 +3,9 @@ const path = require('path');
const { mergeWith, cloneDeep } = require('lodash');
const baseManifest = require('../../app/manifest/_base.json');
const { BuildType } = require('../lib/build-type');
const { createTask, composeSeries } = require('./task');
const { BuildType } = require('./utils');
module.exports = createManifestTasks;

@ -45,7 +45,7 @@ const metamaskrc = require('rc')('metamask', {
});
const { streamFlatMap } = require('../stream-flat-map.js');
const { version } = require('../../package.json');
const { BuildType } = require('../lib/build-type');
const {
createTask,
@ -56,7 +56,6 @@ const {
const {
createRemoveFencedCodeTransform,
} = require('./transforms/remove-fenced-code');
const { BuildType } = require('./utils');
/**
* The build environment. This describes the environment this build was produced in.
@ -147,6 +146,7 @@ function createScriptTasks({
livereload,
shouldLintFenceFiles,
policyOnly,
version,
}) {
// internal tasks
const core = {
@ -192,6 +192,7 @@ function createScriptTasks({
policyOnly,
shouldLintFenceFiles,
testing,
version,
}),
);
@ -344,6 +345,7 @@ function createFactoredBuild({
policyOnly,
shouldLintFenceFiles,
testing,
version,
}) {
return async function () {
// create bundler setup and apply defaults
@ -355,7 +357,12 @@ function createFactoredBuild({
const reloadOnChange = Boolean(devMode);
const minify = Boolean(devMode) === false;
const envVars = getEnvironmentVariables({ buildType, devMode, testing });
const envVars = getEnvironmentVariables({
buildType,
devMode,
testing,
version,
});
setupBundlerDefaults(buildConfiguration, {
buildType,
devMode,
@ -780,7 +787,7 @@ async function bundleIt(buildConfiguration, { reloadOnChange }) {
}
}
function getEnvironmentVariables({ buildType, devMode, testing }) {
function getEnvironmentVariables({ buildType, devMode, testing, version }) {
const environment = getEnvironment({ devMode, testing });
if (environment === ENVIRONMENT.PRODUCTION && !process.env.SENTRY_DSN) {
throw new Error('Missing SENTRY_DSN environment variable');

@ -4,9 +4,9 @@ const watch = require('gulp-watch');
const glob = require('fast-glob');
const locales = require('../../app/_locales/index.json');
const { BuildType } = require('../lib/build-type');
const { createTask, composeSeries } = require('./task');
const { BuildType } = require('./utils');
const EMPTY_JS_FILE = './development/empty.js';

@ -1,6 +1,6 @@
const path = require('path');
const { PassThrough, Transform } = require('stream');
const { BuildType } = require('../utils');
const { BuildType } = require('../../lib/build-type');
const { lintTransformedFile } = require('./utils');
const hasKey = (obj, key) => Reflect.hasOwnProperty.call(obj, key);

@ -1,5 +1,5 @@
const deepFreeze = require('deep-freeze-strict');
const { BuildType } = require('../utils');
const { BuildType } = require('../../lib/build-type');
const {
createRemoveFencedCodeTransform,
removeFencedCode,

@ -1,16 +1,5 @@
const semver = require('semver');
const { version } = require('../../package.json');
/**
* The distribution this build is intended for.
*
* This should be kept in-sync with the `BuildType` map in `shared/constants/app.js`.
*/
const BuildType = {
beta: 'beta',
flask: 'flask',
main: 'main',
};
const { BuildType } = require('../lib/build-type');
/**
* Map the current version to a format that is compatible with each browser.
@ -20,11 +9,12 @@ const BuildType = {
* where the build version is a positive integer.
*
* @param {string[]} platforms - A list of browsers to generate versions for.
* @param {string} version - The current version.
* @returns {Object} An object with the browser as the key and the browser-specific version object
* as the value. For example, the version `9.6.0-beta.1` would return the object
* `{ firefox: { version: '9.6.0.beta1' }, chrome: { version: '9.6.0.1', version_name: '9.6.0-beta.1' } }`.
*/
function getBrowserVersionMap(platforms) {
function getBrowserVersionMap(platforms, version) {
const major = semver.major(version);
const minor = semver.minor(version);
const patch = semver.patch(version);
@ -62,6 +52,5 @@ function getBrowserVersionMap(platforms) {
}
module.exports = {
BuildType,
getBrowserVersionMap,
};

@ -0,0 +1,12 @@
/**
* The distribution this build is intended for.
*
* This should be kept in-sync with the `BuildType` map in `shared/constants/app.js`.
*/
const BuildType = {
beta: 'beta',
flask: 'flask',
main: 'main',
};
module.exports = { BuildType };

@ -0,0 +1,21 @@
const { version: manifestVersion } = require('../../package.json');
const { BuildType } = require('./build-type');
/**
* Get the current version of the MetaMask extension. The base manifest version
* is modified according to the build type and version.
*
* The build version is needed because certain build types (such as beta) may
* be released multiple times during the release process.
*
* @param {BuildType} buildType - The build type.
* @param {number} buildVersion - The build version.
* @returns {string} The MetaMask extension version.
*/
function getVersion(buildType, buildVersion) {
return buildType === BuildType.main
? manifestVersion
: `${manifestVersion}-${buildType}.${buildVersion}`;
}
module.exports = { getVersion };

@ -3,7 +3,7 @@ const { promises: fs } = require('fs');
const path = require('path');
const fetch = require('node-fetch');
const glob = require('fast-glob');
const VERSION = require('../dist/chrome/manifest.json').version; // eslint-disable-line import/no-unresolved
const VERSION = require('../package.json').version;
const { getHighlights } = require('./highlights');
start().catch(console.error);

@ -1,6 +1,11 @@
#!/usr/bin/env node
const VERSION = require('../dist/chrome/manifest.json').version; // eslint-disable-line import/no-unresolved
const yargs = require('yargs/yargs');
const { hideBin } = require('yargs/helpers');
const { runCommand, runInShell } = require('./lib/run-command');
const { getVersion } = require('./lib/get-version');
const { BuildType } = require('./lib/build-type');
start().catch((error) => {
console.error(error);
@ -8,34 +13,63 @@ start().catch((error) => {
});
async function start() {
if (!process.env.SENTRY_ORG) {
throw new Error('Missing required "SENTRY_ORG" environment variable');
} else if (!process.env.SENTRY_PROJECT) {
throw new Error('Missing required "SENTRY_PROJECT" environment variable');
}
const { argv } = yargs(hideBin(process.argv)).usage(
'$0 [options]',
'Publish a release to Sentry',
(_yargs) =>
_yargs
.option('org', {
default: 'metamask',
description: 'The Sentry organization',
type: 'string',
})
.option('project', {
default: 'metamask',
description: 'The Sentry project to publish',
type: 'string',
})
.option('build-type', {
default: BuildType.main,
description: 'The MetaMask extension build type',
choices: Object.values(BuildType),
})
.option('build-version', {
default: 0,
description: 'The MetaMask extension build version',
type: 'number',
}),
);
const { buildType, buildVersion, org, project } = argv;
process.env.SENTRY_ORG = org;
process.env.SENTRY_PROJECT = project;
const authWorked = await checkIfAuthWorks();
if (!authWorked) {
throw new Error(`Sentry auth failed`);
}
const version = getVersion(buildType, buildVersion);
// check if version exists or not
const versionAlreadyExists = await checkIfVersionExists();
const versionAlreadyExists = await checkIfVersionExists(version);
// abort if versions exists
if (versionAlreadyExists) {
console.log(
`Version "${VERSION}" already exists on Sentry, skipping version creation`,
`Version "${version}" already exists on Sentry, skipping version creation`,
);
} else {
// create sentry release
console.log(`creating Sentry release for "${VERSION}"...`);
await runCommand('sentry-cli', ['releases', 'new', VERSION]);
console.log(`creating Sentry release for "${version}"...`);
await runCommand('sentry-cli', ['releases', 'new', version]);
console.log(
`removing any existing files from Sentry release "${VERSION}"...`,
`removing any existing files from Sentry release "${version}"...`,
);
await runCommand('sentry-cli', [
'releases',
'files',
VERSION,
version,
'delete',
'--all',
]);
@ -43,18 +77,23 @@ async function start() {
// check if version has artifacts or not
const versionHasArtifacts =
versionAlreadyExists && (await checkIfVersionHasArtifacts());
versionAlreadyExists && (await checkIfVersionHasArtifacts(version));
if (versionHasArtifacts) {
console.log(
`Version "${VERSION}" already has artifacts on Sentry, skipping sourcemap upload`,
`Version "${version}" already has artifacts on Sentry, skipping sourcemap upload`,
);
return;
}
const additionalUploadArgs = [];
if (buildType !== BuildType.main) {
additionalUploadArgs.push('--dist-directory', `dist-${buildType}`);
}
// upload sentry source and sourcemaps
await runInShell('./development/sentry-upload-artifacts.sh', [
'--release',
VERSION,
version,
...additionalUploadArgs,
]);
}
@ -64,17 +103,17 @@ async function checkIfAuthWorks() {
);
}
async function checkIfVersionExists() {
async function checkIfVersionExists(version) {
return await doesNotFail(() =>
runCommand('sentry-cli', ['releases', 'info', VERSION]),
runCommand('sentry-cli', ['releases', 'info', version]),
);
}
async function checkIfVersionHasArtifacts() {
async function checkIfVersionHasArtifacts(version) {
const [artifact] = await runCommand('sentry-cli', [
'releases',
'files',
VERSION,
version,
'list',
]);
// When there's no artifacts, we get a response from the shell like this ['', '']

@ -23,17 +23,20 @@ Upload JavaScript bundles and sourcemaps to Sentry
Options:
-h, --help Show help text
-r, --release <release> Sentry release to upload files to (defaults to 'VERSION' environment variable)
--dist-directory <path> The 'dist' directory to use. Defaults to 'dist'.
EOF
}
function upload_sourcemaps {
local release="${1}"; shift
local dist_directory="${1}"; shift
sentry-cli releases files "${release}" upload-sourcemaps ./dist/chrome/*.js ./dist/sourcemaps/ --rewrite --url-prefix 'metamask'
sentry-cli releases files "${release}" upload-sourcemaps "${dist_directory}"/chrome/*.js "${dist_directory}"/sourcemaps/ --rewrite --url-prefix 'metamask'
}
function main {
local release=VERSION
local dist_directory='dist'
while :; do
case "${1-default}" in
@ -51,6 +54,16 @@ function main {
release="${2}"
shift
;;
--dist-directory)
if [[ -z $2 ]]
then
printf "'dist-directory' option requires an argument.\\n" >&2
printf '%s\n' "${__SEE_HELP_MESSAGE__}" >&2
exit 1
fi
dist_directory="${2}"
shift
;;
*)
break
esac
@ -70,7 +83,7 @@ function main {
fi
printf 'uploading source files and sourcemaps for Sentry release "%s"...\n' "${release}"
upload_sourcemaps "${release}"
upload_sourcemaps "${release}" "${dist_directory}"
printf 'all done!\n'
}

@ -695,6 +695,7 @@
},
"@metamask/rpc-methods": {
"packages": {
"@metamask/controllers": true,
"@metamask/key-tree": true,
"@metamask/snap-controllers": true,
"eth-rpc-errors": true
@ -734,25 +735,24 @@
"clearTimeout": true,
"console.error": true,
"console.log": true,
"fetch": true,
"console.warn": true,
"setTimeout": true
},
"packages": {
"@metamask/controllers": true,
"@metamask/execution-environments": true,
"@metamask/object-multiplex": true,
"@metamask/obs-store": true,
"@metamask/post-message-stream": true,
"@metamask/safe-event-emitter": true,
"@metamask/snap-workers": true,
"ajv": true,
"buffer": true,
"concat-stream": true,
"cross-fetch": true,
"crypto-browserify": true,
"deep-freeze-strict": true,
"eth-rpc-errors": true,
"fast-deep-equal": true,
"gunzip-maybe": true,
"immer": true,
"json-rpc-engine": true,
"json-rpc-middleware-stream": true,
"nanoid": true,

@ -630,16 +630,15 @@
"@metamask/iframe-execution-environment-service": {
"globals": {
"clearTimeout": true,
"console.log": true,
"document.body.appendChild": true,
"document.createElement": true,
"document.getElementById": true,
"setTimeout": true
},
"packages": {
"@metamask/execution-environments": true,
"@metamask/post-message-stream": true,
"@metamask/snap-controllers": true,
"@metamask/snap-workers": true,
"json-rpc-engine": true,
"json-rpc-middleware-stream": true,
"nanoid": true,
@ -714,6 +713,7 @@
},
"@metamask/rpc-methods": {
"packages": {
"@metamask/controllers": true,
"@metamask/key-tree": true,
"@metamask/snap-controllers": true,
"eth-rpc-errors": true
@ -753,25 +753,24 @@
"clearTimeout": true,
"console.error": true,
"console.log": true,
"fetch": true,
"console.warn": true,
"setTimeout": true
},
"packages": {
"@metamask/controllers": true,
"@metamask/execution-environments": true,
"@metamask/object-multiplex": true,
"@metamask/obs-store": true,
"@metamask/post-message-stream": true,
"@metamask/safe-event-emitter": true,
"@metamask/snap-workers": true,
"ajv": true,
"buffer": true,
"concat-stream": true,
"cross-fetch": true,
"crypto-browserify": true,
"deep-freeze-strict": true,
"eth-rpc-errors": true,
"fast-deep-equal": true,
"gunzip-maybe": true,
"immer": true,
"json-rpc-engine": true,
"json-rpc-middleware-stream": true,
"nanoid": true,

@ -695,6 +695,7 @@
},
"@metamask/rpc-methods": {
"packages": {
"@metamask/controllers": true,
"@metamask/key-tree": true,
"@metamask/snap-controllers": true,
"eth-rpc-errors": true
@ -734,25 +735,24 @@
"clearTimeout": true,
"console.error": true,
"console.log": true,
"fetch": true,
"console.warn": true,
"setTimeout": true
},
"packages": {
"@metamask/controllers": true,
"@metamask/execution-environments": true,
"@metamask/object-multiplex": true,
"@metamask/obs-store": true,
"@metamask/post-message-stream": true,
"@metamask/safe-event-emitter": true,
"@metamask/snap-workers": true,
"ajv": true,
"buffer": true,
"concat-stream": true,
"cross-fetch": true,
"crypto-browserify": true,
"deep-freeze-strict": true,
"eth-rpc-errors": true,
"fast-deep-equal": true,
"gunzip-maybe": true,
"immer": true,
"json-rpc-engine": true,
"json-rpc-middleware-stream": true,
"nanoid": true,

@ -1,6 +1,6 @@
{
"name": "metamask-crx",
"version": "10.11.3",
"version": "10.11.4",
"private": true,
"repository": {
"type": "git",
@ -18,6 +18,7 @@
"benchmark:chrome": "SELENIUM_BROWSER=chrome node test/e2e/benchmark.js",
"benchmark:firefox": "SELENIUM_BROWSER=firefox node test/e2e/benchmark.js",
"build:test": "yarn build test",
"build:test:flask": "yarn build test --build-type flask",
"build:test:metrics": "SEGMENT_HOST='http://localhost:9090' SEGMENT_WRITE_KEY='FAKE' yarn build test",
"test": "yarn lint && yarn test:unit && yarn test:unit:jest",
"dapp": "node development/static-server.js node_modules/@metamask/test-dapp/dist --port 8080",
@ -30,8 +31,10 @@
"test:unit:mocha": "mocha './app/**/*.test.js'",
"test:e2e:chrome": "SELENIUM_BROWSER=chrome node test/e2e/run-all.js",
"test:e2e:chrome:metrics": "SELENIUM_BROWSER=chrome node test/e2e/run-e2e-test.js test/e2e/metrics.spec.js",
"test:e2e:chrome:snaps": "SELENIUM_BROWSER=chrome node test/e2e/run-all.js --snaps",
"test:e2e:firefox": "SELENIUM_BROWSER=firefox node test/e2e/run-all.js",
"test:e2e:firefox:metrics": "SELENIUM_BROWSER=firefox node test/e2e/run-e2e-test.js test/e2e/metrics.spec.js",
"test:e2e:firefox:snaps": "SELENIUM_BROWSER=firefox node test/e2e/run-all.js --snaps",
"test:e2e:single": "node test/e2e/run-e2e-test.js",
"test:coverage:mocha": "nyc --reporter=text --reporter=html yarn test:unit:mocha",
"test:coverage:jest": "yarn test:unit:jest --coverage --maxWorkers=2",
@ -113,16 +116,16 @@
"@metamask/eth-ledger-bridge-keyring": "^0.10.0",
"@metamask/eth-token-tracker": "^4.0.0",
"@metamask/etherscan-link": "^2.1.0",
"@metamask/iframe-execution-environment-service": "^0.9.0",
"@metamask/iframe-execution-environment-service": "^0.10.6",
"@metamask/jazzicon": "^2.0.0",
"@metamask/logo": "^3.1.1",
"@metamask/obs-store": "^5.0.0",
"@metamask/post-message-stream": "^4.0.0",
"@metamask/providers": "^8.1.1",
"@metamask/rpc-methods": "^0.9.0",
"@metamask/rpc-methods": "^0.10.6",
"@metamask/slip44": "^2.0.0",
"@metamask/smart-transactions-controller": "^1.9.1",
"@metamask/snap-controllers": "^0.9.0",
"@metamask/snap-controllers": "^0.10.6",
"@ngraveio/bc-ur": "^1.1.6",
"@popperjs/core": "^2.4.0",
"@reduxjs/toolkit": "^1.6.2",
@ -417,7 +420,11 @@
"ganache>leveldown": false,
"geckodriver": true,
"react-devtools>electron": true,
"eth-trezor-keyring>trezor-connect>@trezor/transport>protobufjs": false
"eth-trezor-keyring>trezor-connect>@trezor/transport>protobufjs": false,
"@metamask/iframe-execution-environment-service>@metamask/execution-environments": false,
"@metamask/snap-controllers>@metamask/execution-environments": false,
"@metamask/iframe-execution-environment-service>@metamask/snap-controllers>@metamask/execution-environments": false,
"@metamask/rpc-methods>@metamask/snap-controllers>@metamask/execution-environments": false
}
}
}

@ -21,4 +21,7 @@ export const PermissionNamespaces = Object.freeze({
export const EndowmentPermissions = Object.freeze({
'endowment:network-access': 'endowment:network-access',
});
// Methods / permissions in external packages that we are temporarily excluding.
export const ExcludedSnapPermissions = new Set(['snap_notify']);
///: END:ONLY_INCLUDE_IN

@ -1,6 +1,10 @@
import { endowmentPermissionBuilders } from '@metamask/snap-controllers';
import { endowmentPermissionBuilders } from '@metamask/controllers';
import { restrictedMethodPermissionBuilders } from '@metamask/rpc-methods';
import { EndowmentPermissions, RestrictedMethods } from './permissions';
import {
EndowmentPermissions,
ExcludedSnapPermissions,
RestrictedMethods,
} from './permissions';
describe('EndowmentPermissions', () => {
it('has the expected permission keys', () => {
@ -15,7 +19,9 @@ describe('RestrictedMethods', () => {
expect(Object.keys(RestrictedMethods).sort()).toStrictEqual(
[
'eth_accounts',
...Object.keys(restrictedMethodPermissionBuilders),
...Object.keys(restrictedMethodPermissionBuilders).filter(
(targetKey) => !ExcludedSnapPermissions.has(targetKey),
),
].sort(),
);
});

@ -17,6 +17,10 @@ async function main() {
type: 'string',
choices: ['chrome', 'firefox'],
})
.option('snaps', {
description: `run snaps e2e tests`,
type: 'boolean',
})
.option('retries', {
description:
'Set how many times the test should be retried upon failure.',
@ -26,16 +30,22 @@ async function main() {
.strict()
.help('help');
const { browser, retries } = argv;
const { browser, retries, snaps } = argv;
let testDir = path.join(__dirname, 'tests');
const testDir = path.join(__dirname, 'tests');
const metamaskUiTest = path.join(__dirname, 'metamask-ui.spec.js');
if (snaps) {
testDir = path.join(__dirname, 'snaps');
}
const testFilenames = await fs.readdir(testDir);
const testPaths = testFilenames.map((filename) =>
path.join(testDir, filename),
);
const allE2eTestPaths = [...testPaths, metamaskUiTest];
if (!snaps) {
testPaths.push(path.join(__dirname, 'metamask-ui.spec.js'));
}
const runE2eTestPath = path.join(__dirname, 'run-e2e-test.js');
@ -47,7 +57,7 @@ async function main() {
args.push('--retries', retries);
}
for (const testPath of allE2eTestPaths) {
for (const testPath of testPaths) {
await runInShell('node', [...args, testPath]);
}
}

@ -0,0 +1,98 @@
const { strict: assert } = require('assert');
const { withFixtures } = require('../helpers');
describe('Test Snap Confirm', function () {
it('can pop up a snap confirm and get its result', async function () {
const ganacheOptions = {
accounts: [
{
secretKey:
'0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC',
balance: 25000000000000000000,
},
],
};
await withFixtures(
{
fixtures: 'imported-account',
ganacheOptions,
title: this.test.title,
driverOptions: {
type: 'flask',
},
},
async ({ driver }) => {
await driver.navigate();
// enter pw into extension
await driver.fill('#password', 'correct horse battery staple');
await driver.press('#password', driver.Key.ENTER);
// navigate to test snaps page and connect
await driver.driver.get('https://metamask.github.io/test-snaps/');
await driver.fill('.snapId', 'npm:@metamask/test-snap-confirm');
await driver.clickElement({
text: 'Connect To Confirm Snap',
tag: 'button',
});
// switch to metamask extension and click connect
await driver.waitUntilXWindowHandles(2, 5000, 10000);
let windowHandles = await driver.getAllWindowHandles();
await driver.switchToWindowWithTitle(
'MetaMask Notification',
windowHandles,
);
await driver.clickElement(
{
text: 'Connect',
tag: 'button',
},
10000,
);
await driver.delay(2000);
// approve install of snap
await driver.waitUntilXWindowHandles(2, 5000, 10000);
windowHandles = await driver.getAllWindowHandles();
await driver.switchToWindowWithTitle(
'MetaMask Notification',
windowHandles,
);
await driver.clickElement({
text: 'Approve & Install',
tag: 'button',
});
// click send inputs on test snap page
await driver.waitUntilXWindowHandles(1, 5000, 10000);
windowHandles = await driver.getAllWindowHandles();
await driver.switchToWindowWithTitle('Test Snaps', windowHandles);
await driver.clickElement({
text: 'Send Inputs to Hello Snap',
tag: 'button',
});
// hit 'approve' on the custom confirm
await driver.waitUntilXWindowHandles(2, 5000, 10000);
windowHandles = await driver.getAllWindowHandles();
await driver.switchToWindowWithTitle(
'MetaMask Notification',
windowHandles,
);
await driver.clickElement({
text: 'Approve',
tag: 'button',
});
// check the results of the custom confirm
await driver.waitUntilXWindowHandles(1, 5000, 10000);
windowHandles = await driver.getAllWindowHandles();
await driver.switchToWindowWithTitle('Test Snaps', windowHandles);
const confirmResult = await driver.findElement('.sendResults');
assert.equal(await confirmResult.getText(), 'true');
},
);
});
});

@ -0,0 +1,90 @@
const { strict: assert } = require('assert');
const { withFixtures } = require('../helpers');
const { PAGES } = require('../webdriver/driver');
describe('Test Snap Error', function () {
it('can pop up a snap error and see the error', async function () {
const ganacheOptions = {
accounts: [
{
secretKey:
'0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC',
balance: 25000000000000000000,
},
],
};
await withFixtures(
{
fixtures: 'imported-account',
ganacheOptions,
title: this.test.title,
driverOptions: { type: 'flask' },
},
async ({ driver }) => {
await driver.navigate();
// enter pw into extension
await driver.fill('#password', 'correct horse battery staple');
await driver.press('#password', driver.Key.ENTER);
// navigate to test snaps page and connect
await driver.driver.get('https://metamask.github.io/test-snaps/');
await driver.fill('.snapId2', 'npm:@metamask/test-snap-error');
await driver.clickElement({
text: 'Connect Error Snap',
tag: 'button',
});
// switch to metamask extension and click connect
await driver.waitUntilXWindowHandles(2, 5000, 10000);
let windowHandles = await driver.getAllWindowHandles();
await driver.switchToWindowWithTitle(
'MetaMask Notification',
windowHandles,
);
await driver.clickElement(
{
text: 'Connect',
tag: 'button',
},
10000,
);
await driver.delay(2000);
// approve install of snap
windowHandles = await driver.getAllWindowHandles();
await driver.switchToWindowWithTitle(
'MetaMask Notification',
windowHandles,
);
await driver.clickElement({
text: 'Approve & Install',
tag: 'button',
});
// click send inputs on test snap page
await driver.waitUntilXWindowHandles(1, 5000, 10000);
windowHandles = await driver.getAllWindowHandles();
await driver.switchToWindowWithTitle('Test Snaps', windowHandles);
await driver.clickElement({
text: 'Send Test to Error Snap',
tag: 'button',
});
await driver.navigate(PAGES.HOME);
const error = await driver.findElement(
'.home-notification__content-container',
);
const text = await error.getText();
assert.equal(
text.includes(
"Snap Error: 'random error inside'. Error Code: '-32603'",
),
true,
);
},
);
});
});

@ -69,7 +69,7 @@ class ChromeDriver {
for (let i = 0; i < extensions.length; i++) {
const extension = extensions[i].shadowRoot
const name = extension.querySelector('#name').textContent
if (name === "${extensionName}") {
if (name.startsWith("${extensionName}")) {
return extensions[i].getAttribute("id")
}
}

@ -382,7 +382,7 @@ class Driver {
const ignoredLogTypes = ['WARNING'];
const ignoredErrorMessages = [
// Third-party Favicon 404s show up as errors
'favicon.ico - Failed to load resource: the server responded with a status of 404 (Not Found)',
'favicon.ico - Failed to load resource: the server responded with a status of 404',
// Sentry rate limiting
'Failed to load resource: the server responded with a status of 429',
// 4Byte

@ -4,7 +4,8 @@ const path = require('path');
const { Builder, By, until } = require('selenium-webdriver');
const firefox = require('selenium-webdriver/firefox');
const proxy = require('selenium-webdriver/proxy');
const { version } = require('../../../package.json');
const { getVersion } = require('../../../development/lib/get-version');
const { BuildType } = require('../../../development/lib/build-type');
/**
* The prefix for temporary Firefox profiles. All Firefox profiles used for e2e tests
@ -31,9 +32,10 @@ class FirefoxDriver {
* @param {Object} options - the options for the build
* @param options.responsive
* @param options.port
* @param options.type
* @returns {Promise<{driver: !ThenableWebDriver, extensionUrl: string, extensionId: string}>}
*/
static async build({ responsive, port }) {
static async build({ responsive, port, type }) {
const templateProfile = fs.mkdtempSync(TEMP_PROFILE_PATH_PREFIX);
const options = new firefox.Options().setProfile(templateProfile);
options.setProxy(proxy.manual({ https: HTTPS_PROXY_HOST }));
@ -48,9 +50,14 @@ class FirefoxDriver {
const driver = builder.build();
const fxDriver = new FirefoxDriver(driver);
const extensionId = await fxDriver.installExtension(
`builds/metamask-firefox-${version}.zip`,
);
const version = getVersion(type || BuildType.main, 0);
let extensionString = `builds/metamask-firefox-${version}.zip`;
if (type) {
extensionString = `builds/metamask-${type}-firefox-${version}.zip`;
}
const extensionId = await fxDriver.installExtension(extensionString);
const internalExtensionId = await fxDriver.getInternalId();
if (responsive) {

@ -4,14 +4,14 @@ const Driver = require('./driver');
const ChromeDriver = require('./chrome');
const FirefoxDriver = require('./firefox');
async function buildWebDriver({ responsive, port } = {}) {
async function buildWebDriver({ responsive, port, type } = {}) {
const browser = process.env.SELENIUM_BROWSER;
const {
driver: seleniumDriver,
extensionId,
extensionUrl,
} = await buildBrowserWebDriver(browser, { responsive, port });
} = await buildBrowserWebDriver(browser, { responsive, port, type });
await setupFetchMocking(seleniumDriver);
const driver = new Driver(seleniumDriver, browser, extensionUrl);

@ -30,6 +30,7 @@
@import 'edit-gas-fee-popover/edit-gas-tooltip/index';
@import 'flask/experimental-area/index';
@import 'flask/snap-install-warning/index';
@import 'flask/snap-remove-warning/index';
@import 'flask/snap-settings-card/index';
@import 'flask/snaps-authorship-pill/index';
@import 'gas-customization/gas-modal-page-container/index';

@ -7,10 +7,6 @@
gap: 0 16px;
}
&__content {
padding: 0 16px 24px;
}
&__footer {
display: flex;
flex-flow: row;

@ -43,8 +43,10 @@ export default function SnapInstallWarning({ onCancel, onSubmit, snapName }) {
className="snap-install-warning"
title={t('areYouSure')}
footer={<SnapInstallWarningFooter />}
headerProps={{ padding: [6, 6, 0] }}
contentProps={{ padding: [0, 6, 4] }}
footerProps={{ padding: [4, 6] }}
>
<div className="snap-install-warning__content">
<Typography variant={TYPOGRAPHY.H6} boxProps={{ paddingBottom: 4 }}>
{t('snapInstallWarningCheck')}
</Typography>
@ -58,7 +60,6 @@ export default function SnapInstallWarning({ onCancel, onSubmit, snapName }) {
{t('snapInstallWarningKeyAccess', [snapName])}
</label>
</div>
</div>
</Popover>
);
}

@ -0,0 +1 @@
export { default } from './snap-remove-warning';

@ -0,0 +1,12 @@
.snap-remove-warning {
color: var(--black);
&__footer-button {
height: 40px;
margin-inline-end: 24px;
&:last-of-type {
margin-inline-end: 0;
}
}
}

@ -0,0 +1,64 @@
import React from 'react';
import PropTypes from 'prop-types';
import { useI18nContext } from '../../../../hooks/useI18nContext';
import Typography from '../../../ui/typography/typography';
import { TYPOGRAPHY } from '../../../../helpers/constants/design-system';
import Box from '../../../ui/box/box';
import Popover from '../../../ui/popover';
import Button from '../../../ui/button';
export default function SnapRemoveWarning({ onCancel, onSubmit, snapName }) {
const t = useI18nContext();
const SnapRemoveWarningFooter = () => {
return (
<>
<Button
className="snap-remove-warning__footer-button"
type="default"
onClick={onCancel}
>
{t('nevermind')}
</Button>
<Button
className="snap-remove-warning__footer-button"
type="danger-primary"
onClick={onSubmit}
>
{t('removeSnap')}
</Button>
</>
);
};
return (
<Popover
className="snap-remove-warning"
title={t('pleaseConfirm')}
footer={<SnapRemoveWarningFooter />}
onClose={onCancel}
headerProps={{ padding: [6, 4, 0, 6] }}
>
<Box paddingRight={4} paddingBottom={4} paddingLeft={6}>
<Typography variant={TYPOGRAPHY.H4}>
{t('removeSnapConfirmation', [snapName])}
</Typography>
</Box>
</Popover>
);
}
SnapRemoveWarning.propTypes = {
/**
* onCancel handler
*/
onCancel: PropTypes.func,
/**
* onSubmit handler
*/
onSubmit: PropTypes.func,
/**
* Name of snap
*/
snapName: PropTypes.string,
};

@ -180,7 +180,7 @@ const SnapSettingsCard = ({
tag="span"
className="snap-settings-card__version"
>
v {version}
{t('shorthandVersion', [version])}
</Typography>
</>
)}

@ -53,6 +53,7 @@
}
&__requested {
width: 100%;
text-align: left;
}
@ -91,6 +92,7 @@
}
&__permissions-container {
width: 100%;
display: flex;
flex-direction: column;
margin-top: 38px;

@ -5,12 +5,24 @@ import Box from '../../ui/box';
import {
FLEX_DIRECTION,
JUSTIFY_CONTENT,
///: BEGIN:ONLY_INCLUDE_IN(flask)
COLORS,
TYPOGRAPHY,
TEXT_ALIGN,
///: END:ONLY_INCLUDE_IN
} from '../../../helpers/constants/design-system';
///: BEGIN:ONLY_INCLUDE_IN(flask)
import SnapsAuthorshipPill from '../flask/snaps-authorship-pill';
import Typography from '../../ui/typography';
///: END:ONLY_INCLUDE_IN
export default class PermissionsConnectHeader extends Component {
///: BEGIN:ONLY_INCLUDE_IN(flask)
static contextTypes = {
t: PropTypes.func,
};
///: END:ONLY_INCLUDE_IN
static propTypes = {
iconUrl: PropTypes.string,
iconName: PropTypes.string.isRequired,
@ -20,6 +32,7 @@ export default class PermissionsConnectHeader extends Component {
headerText: PropTypes.string,
///: BEGIN:ONLY_INCLUDE_IN(flask)
npmPackageName: PropTypes.string,
snapVersion: PropTypes.string,
///: END:ONLY_INCLUDE_IN
};
@ -47,10 +60,12 @@ export default class PermissionsConnectHeader extends Component {
headerText,
///: BEGIN:ONLY_INCLUDE_IN(flask)
npmPackageName,
snapVersion,
///: END:ONLY_INCLUDE_IN
} = this.props;
///: BEGIN:ONLY_INCLUDE_IN(flask)
const npmPackageUrl = `https://www.npmjs.com/package/${npmPackageName}`;
const { t } = this.context;
///: END:ONLY_INCLUDE_IN
return (
<Box
@ -71,6 +86,24 @@ export default class PermissionsConnectHeader extends Component {
) : null
///: END:ONLY_INCLUDE_IN
}
{
///: BEGIN:ONLY_INCLUDE_IN(flask)
snapVersion && (
<Typography
boxProps={{
margin: [2, 0],
}}
color={COLORS.TEXT_MUTED}
variant={TYPOGRAPHY.H7}
align={TEXT_ALIGN.CENTER}
tag="span"
className="version"
>
{t('shorthandVersion', [snapVersion])}
</Typography>
)
///: END:ONLY_INCLUDE_IN
}
<div className="permissions-connect-header__subtitle">{headerText}</div>
</Box>
);

@ -1,7 +1,10 @@
.permissions-connect-permission-list {
width: 100%;
.permission {
@include H6;
width: 100%;
padding-bottom: 16px;
border-bottom: 1px solid var(--Grey-100);
display: flex;

@ -1,16 +0,0 @@
import { Story, Canvas, ArgsTable } from '@storybook/addon-docs';
import Popover from './popover.component';
# Popover
A modal component to show info
<Canvas>
<Story id="ui-components-ui-popover-popover-stories-js--default-story" />
</Canvas>
## Component API
<ArgsTable of={Popover} />

@ -21,12 +21,7 @@
}
&-header {
display: flex;
padding: 24px 16px 16px;
flex-direction: column;
background: white;
position: relative;
border-radius: 10px;
&__title {
display: flex;
@ -78,13 +73,8 @@
&-content {
overflow-y: auto;
position: relative;
display: flex;
flex: 1;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
align-content: stretch;
border-radius: 10px;
}
&-container {
@ -100,11 +90,7 @@
}
&-footer {
display: flex;
flex-direction: row;
justify-content: space-between;
border-top: 1px solid #d2d8dd;
padding: 16px 24px 24px;
> :only-child {
margin: 0 auto;

@ -3,6 +3,34 @@ import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import { useI18nContext } from '../../../hooks/useI18nContext';
import Box from '../box';
import {
ALIGN_ITEMS,
FLEX_DIRECTION,
JUSTIFY_CONTENT,
} from '../../../helpers/constants/design-system';
const defaultHeaderProps = {
padding: [6, 4, 4],
display: 'flex',
flexDirection: FLEX_DIRECTION.COLUMN,
backgroundColor: 'white',
borderRadius: 'xl',
};
const defaultContentProps = {
display: 'flex',
flexDirection: FLEX_DIRECTION.COLUMN,
justifyContent: JUSTIFY_CONTENT.FLEX_START,
alignItems: ALIGN_ITEMS.STRETCH,
borderRadius: 'xl',
};
const defaultFooterProps = {
display: 'flex',
justifyContent: JUSTIFY_CONTENT.SPACE_BETWEEN,
padding: [4, 6, 6],
};
const Popover = ({
title,
@ -18,23 +46,18 @@ const Popover = ({
CustomBackground,
popoverRef,
centerTitle,
headerProps = defaultHeaderProps,
contentProps = defaultContentProps,
footerProps = defaultFooterProps,
}) => {
const t = useI18nContext();
const showHeader = title || onBack || subtitle || onClose;
const Header = () => {
return (
<div className="popover-container">
{CustomBackground ? (
<CustomBackground onClose={onClose} />
) : (
<div className="popover-bg" onClick={onClose} />
)}
<section
className={classnames('popover-wrap', className)}
ref={popoverRef}
<Box
{...{ ...defaultHeaderProps, ...headerProps }}
className="popover-header"
>
{showArrow ? <div className="popover-arrow" /> : null}
{showHeader && (
<header className="popover-header">
<div
className={classnames(
'popover-header__title',
@ -63,17 +86,38 @@ const Popover = ({
{subtitle ? (
<p className="popover-header__subtitle">{subtitle}</p>
) : null}
</header>
</Box>
);
};
return (
<div className="popover-container">
{CustomBackground ? (
<CustomBackground onClose={onClose} />
) : (
<div className="popover-bg" onClick={onClose} />
)}
<section
className={classnames('popover-wrap', className)}
ref={popoverRef}
>
{showArrow ? <div className="popover-arrow" /> : null}
{showHeader && <Header />}
{children ? (
<div className={classnames('popover-content', contentClassName)}>
<Box
className={classnames('popover-content', contentClassName)}
{...{ ...defaultContentProps, ...contentProps }}
>
{children}
</div>
</Box>
) : null}
{footer ? (
<footer className={classnames('popover-footer', footerClassName)}>
<Box
className={classnames('popover-footer', footerClassName)}
{...{ ...defaultFooterProps, ...footerProps }}
>
{footer}
</footer>
</Box>
) : null}
</section>
</div>
@ -132,6 +176,18 @@ Popover.propTypes = {
* Check if use centered title
*/
centerTitle: PropTypes.bool,
/**
* Box props for the header
*/
headerProps: PropTypes.shape({ ...Box.propTypes }),
/**
* Box props for the content
*/
contentProps: PropTypes.shape({ ...Box.propTypes }),
/**
* Box props for the footer
*/
footerProps: PropTypes.shape({ ...Box.propTypes }),
};
export default class PopoverPortal extends PureComponent {

@ -1,18 +1,12 @@
import React, { useState } from 'react';
import Button from '../button';
import Box from '../box';
import README from './README.mdx';
import Popover from './popover.component';
export default {
title: 'Components/UI/Popover',
id: __filename,
component: Popover,
parameters: {
docs: {
page: README,
},
},
argTypes: {
title: { control: 'text' },
subtitle: { control: 'text' },
@ -26,6 +20,21 @@ export default {
showArrow: { control: 'boolean' },
popoverRef: { control: 'object' },
centerTitle: { control: 'boolean' },
headerProps: {
control: 'object',
description:
'Box component props used to add container CSS for the header',
},
contentProps: {
control: 'object',
description:
'Box component props used to add container CSS for the content',
},
footerProps: {
control: 'object',
description:
'Box component props used to add container CSS for the footer',
},
},
};

@ -22,6 +22,10 @@
}
}
.version {
font-family: monospace;
}
.page-container__footer {
width: 100%;
}

@ -75,16 +75,18 @@ export default function SnapInstall({
headerText={null} // TODO(ritave): Add header text when snaps support description
siteOrigin={targetSubjectMetadata.origin}
npmPackageName={npmId}
snapVersion={targetSubjectMetadata.version}
boxProps={{ alignItems: ALIGN_ITEMS.CENTER }}
/>
<Typography></Typography>
<Box
className="snap-requests-permission"
padding={4}
tag={TYPOGRAPHY.H7}
<Typography
boxProps={{
padding: [4, 4, 0, 4],
}}
variant={TYPOGRAPHY.H7}
tag="span"
>
<span>{t('snapRequestsPermission')}</span>
</Box>
{t('snapRequestsPermission')}
</Typography>
<PermissionsConnectPermissionList
permissions={request.permissions || {}}
/>
@ -149,5 +151,6 @@ SnapInstall.propTypes = {
name: PropTypes.string,
origin: PropTypes.string.isRequired,
sourceCode: PropTypes.string,
version: PropTypes.string,
}).isRequired,
};

@ -43,7 +43,7 @@ export default class PermissionConnect extends Component {
extensionId: PropTypes.string,
iconUrl: PropTypes.string,
name: PropTypes.string,
origin: PropTypes.string.isRequired,
origin: PropTypes.string,
subjectType: PropTypes.string,
}),
isRequestingAccounts: PropTypes.bool.isRequired,

@ -6,8 +6,6 @@
}
&__subheader {
@include H4;
padding: 16px 4px;
border-bottom: 1px solid var(--alto);
margin-right: 24px;
@ -51,12 +49,18 @@
}
&__toggle-container {
margin-left: auto;
@media screen and (max-width: $break-small) {
padding-left: 0;
display: inline-block;
}
}
&__toggle-button {
margin-right: -12px;
}
&__content-container {
@media screen and (max-width: $break-small) {
width: 100%;

@ -1,4 +1,4 @@
import React, { useEffect } from 'react';
import React, { useEffect, useState } from 'react';
import { useHistory, useLocation } from 'react-router-dom';
import { useDispatch, useSelector } from 'react-redux';
import Button from '../../../../components/ui/button';
@ -12,6 +12,7 @@ import {
} from '../../../../helpers/constants/design-system';
import SnapsAuthorshipPill from '../../../../components/app/flask/snaps-authorship-pill';
import Box from '../../../../components/ui/box';
import SnapRemoveWarning from '../../../../components/app/flask/snap-remove-warning';
import ToggleButton from '../../../../components/ui/toggle-button';
import PermissionsConnectPermissionList from '../../../../components/app/permissions-connect-permission-list/permissions-connect-permission-list';
import ConnectedSitesList from '../../../../components/app/connected-sites-list';
@ -39,6 +40,8 @@ function ViewSnap() {
return snapState.id === decoded;
});
const [isShowingRemoveWarning, setIsShowingRemoveWarning] = useState(false);
useEffect(() => {
if (!snap) {
history.push(SNAPS_LIST_ROUTE);
@ -86,15 +89,12 @@ function ViewSnap() {
url={authorshipPillUrl}
/>
</Box>
<Box
paddingLeft={4}
className="snap-settings-card__toggle-container view-snap__toggle-container"
>
<Box paddingLeft={4} className="view-snap__toggle-container">
<Tooltip interactive position="bottom" html={t('snapsToggle')}>
<ToggleButton
value={snap.enabled}
onToggle={onToggle}
className="snap-settings-card__toggle-container__toggle-button"
className="view-snap__toggle-button"
/>
</Tooltip>
</Box>
@ -155,12 +155,19 @@ function ViewSnap() {
css={{
maxWidth: '175px',
}}
onClick={async () => {
await dispatch(removeSnap(snap));
}}
onClick={() => setIsShowingRemoveWarning(true)}
>
{t('removeSnap')}
</Button>
{isShowingRemoveWarning && (
<SnapRemoveWarning
onCancel={() => setIsShowingRemoveWarning(false)}
onSubmit={async () => {
await dispatch(removeSnap(snap));
}}
snapName={snap.manifest.proposedName}
/>
)}
</div>
</Box>
</div>

@ -2707,6 +2707,46 @@
web3 "^0.20.7"
web3-provider-engine "^16.0.3"
"@metamask/controllers@^26.0.0":
version "26.0.0"
resolved "https://registry.yarnpkg.com/@metamask/controllers/-/controllers-26.0.0.tgz#3df4a3071ffb26d357ba99f288d52fb9d913c35a"
integrity sha512-iAWDoP/omxGzPfYyBFRNPJ32zcYvZHnUhIM2LyWoCwQj9ZYC1qh+dDX6I0O5jEeQcBrEb+Nl6AcnwHKVdEUz5Q==
dependencies:
"@ethereumjs/common" "^2.3.1"
"@ethereumjs/tx" "^3.2.1"
"@metamask/contract-metadata" "^1.31.0"
"@metamask/metamask-eth-abis" "3.0.0"
"@metamask/types" "^1.1.0"
"@types/uuid" "^8.3.0"
abort-controller "^3.0.0"
async-mutex "^0.2.6"
babel-runtime "^6.26.0"
deep-freeze-strict "^1.1.1"
eth-ens-namehash "^2.0.8"
eth-json-rpc-infura "^5.1.0"
eth-keyring-controller "^6.2.1"
eth-method-registry "1.1.0"
eth-phishing-detect "^1.1.14"
eth-query "^2.1.2"
eth-rpc-errors "^4.0.0"
eth-sig-util "^3.0.0"
ethereumjs-util "^7.0.10"
ethereumjs-wallet "^1.0.1"
ethers "^5.4.1"
ethjs-unit "^0.1.6"
fast-deep-equal "^3.1.3"
immer "^9.0.6"
isomorphic-fetch "^3.0.0"
json-rpc-engine "^6.1.0"
jsonschema "^1.2.4"
multiformats "^9.5.2"
nanoid "^3.1.31"
punycode "^2.1.1"
single-call-balance-checker-abi "^1.0.0"
uuid "^8.3.2"
web3 "^0.20.7"
web3-provider-engine "^16.0.3"
"@metamask/controllers@^27.0.0":
version "27.0.0"
resolved "https://registry.yarnpkg.com/@metamask/controllers/-/controllers-27.0.0.tgz#23fb24960880047635a7e0b226375b843f385ad1"
@ -2806,22 +2846,37 @@
resolved "https://registry.yarnpkg.com/@metamask/etherscan-link/-/etherscan-link-2.1.0.tgz#c0be8e68445b7b83cf85bcc03a56cdf8e256c973"
integrity sha512-ADuWlTUkFfN2vXlz81Bg/0BA+XRor+CdK1055p6k7H6BLIPoDKn9SBOFld9haQFuR9cKh/JYHcnlSIv5R4fUEw==
"@metamask/execution-environments@^0.10.6":
version "0.10.6"
resolved "https://registry.yarnpkg.com/@metamask/execution-environments/-/execution-environments-0.10.6.tgz#930ff056e63accf7ab7a59a28bd99064d217599c"
integrity sha512-6ID8vzmIiy418LqRiDKPuDl0RBhHmDMzhgyb65ia6zZNDXKhsBotF/lNMhtHlsAbD9+3XYhi/Nl6YprJxaRisA==
dependencies:
"@metamask/object-multiplex" "^1.2.0"
"@metamask/post-message-stream" "^4.0.0"
"@metamask/providers" "^8.1.1"
"@metamask/snap-types" "^0.10.6"
cross-fetch "^3.1.5"
eth-rpc-errors "^4.0.3"
pump "^3.0.0"
ses "^0.15.7"
stream-browserify "^3.0.0"
"@metamask/forwarder@^1.1.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@metamask/forwarder/-/forwarder-1.1.0.tgz#13829d8244bbf19ea658c0b20d21a77b67de0bdd"
integrity sha512-Hggj4y0QIjDzKGTXzarhEPIQyFSB2bi2y6YLJNwaT4JmP30UB5Cj6gqoY0M4pj3QT57fzp0BUuGp7F/AUe28tw==
"@metamask/iframe-execution-environment-service@^0.9.0":
version "0.9.0"
resolved "https://registry.yarnpkg.com/@metamask/iframe-execution-environment-service/-/iframe-execution-environment-service-0.9.0.tgz#721e15ee4651741a599940dbcfa524cc55eaaa47"
integrity sha512-a240sg83sX1dxfBDdRd0uoujaN4V9VtHKELMcTMgpYCI0uE83//Q01a7L8MiBtLhzr8o4D/xXRUIDR0Y9NKc3Q==
"@metamask/iframe-execution-environment-service@^0.10.6":
version "0.10.6"
resolved "https://registry.yarnpkg.com/@metamask/iframe-execution-environment-service/-/iframe-execution-environment-service-0.10.6.tgz#ba7e5499efba2018cef38fff916eaabbfbe1b27b"
integrity sha512-BZbE4+CffNmD1J7Mpvh/evvKcDuP82Jl9panIV0JKzddG8zzOi9xlGtImYf8QWjwN5zx2TyTHAb/WxA+R+nFzw==
dependencies:
"@metamask/controllers" "^25.1.0"
"@metamask/controllers" "^26.0.0"
"@metamask/execution-environments" "^0.10.6"
"@metamask/object-multiplex" "^1.2.0"
"@metamask/post-message-stream" "^4.0.0"
"@metamask/snap-controllers" "^0.9.0"
"@metamask/snap-types" "^0.9.0"
"@metamask/snap-workers" "^0.9.0"
"@metamask/snap-controllers" "^0.10.6"
"@metamask/snap-types" "^0.10.6"
eth-rpc-errors "^4.0.3"
json-rpc-engine "^6.1.0"
json-rpc-middleware-stream "^3.0.0"
@ -2915,13 +2970,15 @@
pump "^3.0.0"
webextension-polyfill-ts "^0.25.0"
"@metamask/rpc-methods@^0.9.0":
version "0.9.0"
resolved "https://registry.yarnpkg.com/@metamask/rpc-methods/-/rpc-methods-0.9.0.tgz#eb55cc39d2ea9a663211e8d805bdf566af70c764"
integrity sha512-wii0TMuRscet8+x3tqfAcEmY0TrMFzOnD3QFpFVUy3fznv4b/EzDD/XLQToafd2yUaDjUrrS9FHwU9omqzPxcg==
"@metamask/rpc-methods@^0.10.6":
version "0.10.6"
resolved "https://registry.yarnpkg.com/@metamask/rpc-methods/-/rpc-methods-0.10.6.tgz#6e91c2bf33c909e203db438effec6663da4e8692"
integrity sha512-cPqAQExwZGRLqtl22HUBVblr/tIAm/aI3ZMt6QKhwymwU7f/yM+4BOZB1M3+dVlhcIfX1SFFxDW3rFe+MsCksA==
dependencies:
"@metamask/controllers" "^26.0.0"
"@metamask/key-tree" "^3.0.1"
"@metamask/snap-controllers" "^0.9.0"
"@metamask/snap-controllers" "^0.10.6"
"@metamask/types" "^1.1.0"
eth-rpc-errors "^4.0.2"
"@metamask/safe-event-emitter@^2.0.0":
@ -2947,20 +3004,22 @@
isomorphic-fetch "^3.0.0"
lodash "^4.17.21"
"@metamask/snap-controllers@^0.9.0":
version "0.9.0"
resolved "https://registry.yarnpkg.com/@metamask/snap-controllers/-/snap-controllers-0.9.0.tgz#e0006fc9991e995dd86dff792106990aae2aeda0"
integrity sha512-os3fEai0w4ctpyy6ExlthY8tnww98Vm+RVwOZgrCKDY5dAXqlSXpyWc1uOfkQyiPhUEJtdznJTWzaWzNIO9MfQ==
"@metamask/snap-controllers@^0.10.6":
version "0.10.6"
resolved "https://registry.yarnpkg.com/@metamask/snap-controllers/-/snap-controllers-0.10.6.tgz#72fc52271264c4ae8c986ce4d1a64c34505c7a8b"
integrity sha512-Pa8g1dBVKtjkemRXFUPDQLnP8gcVmQpSgkTMK/m7tVqUinyZX7Gufq5wuApEh1pwRjMQ2R7he/zQn7oZ4qwTJw==
dependencies:
"@metamask/controllers" "^25.1.0"
"@metamask/controllers" "^26.0.0"
"@metamask/execution-environments" "^0.10.6"
"@metamask/object-multiplex" "^1.1.0"
"@metamask/obs-store" "^7.0.0"
"@metamask/post-message-stream" "4.0.0"
"@metamask/safe-event-emitter" "^2.0.0"
"@metamask/snap-workers" "^0.9.0"
"@types/deep-freeze-strict" "^1.1.0"
"@types/semver" "^7.3.9"
ajv "^8.8.2"
concat-stream "^2.0.0"
cross-fetch "^3.1.5"
deep-freeze-strict "^1.1.1"
eth-rpc-errors "^4.0.2"
fast-deep-equal "^3.1.3"
@ -2974,17 +3033,12 @@
semver "^7.3.5"
tar-stream "^2.2.0"
"@metamask/snap-types@^0.9.0":
version "0.9.0"
resolved "https://registry.yarnpkg.com/@metamask/snap-types/-/snap-types-0.9.0.tgz#aa164111be1b5c53fbaaf03c1bccbdbd0741daa4"
integrity sha512-pK4tvurUhcKMEkTD0XvQze5HCbtrgmpFWDztBekNIMJTXDrnYIEw4Dxn+LwCX7WJ0DN/03brQSEzmIrYbcBw7Q==
"@metamask/snap-types@^0.10.6":
version "0.10.6"
resolved "https://registry.yarnpkg.com/@metamask/snap-types/-/snap-types-0.10.6.tgz#83e3eab797829a9f31906a27fa967a8096e5b704"
integrity sha512-6eSc9hHhC+X133Nw6/WSUy0RWRipdOF3NBKgGFYJxCpvhkY4jGxQ1HAKDUZRzZ3eSewsmQ4hhhIQ+zczj0Subw==
dependencies:
"@metamask/controllers" "^25.1.0"
"@metamask/snap-workers@^0.9.0":
version "0.9.0"
resolved "https://registry.yarnpkg.com/@metamask/snap-workers/-/snap-workers-0.9.0.tgz#215407b632fef4723dd75af7accf1f02a6a46916"
integrity sha512-+4YY5CQ7OPFPWh4QF5e4COgc0aWL6Df7Oc8/y//Sabp1rmXWI429OzCOlBi+NGJfQ1K7ORBMlRtOwYB9ZmWyLA==
"@metamask/controllers" "^26.0.0"
"@metamask/test-dapp@^5.0.0":
version "5.0.0"
@ -4662,6 +4716,11 @@
dependencies:
"@types/node" "*"
"@types/semver@^7.3.9":
version "7.3.9"
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.9.tgz#152c6c20a7688c30b967ec1841d31ace569863fc"
integrity sha512-L/TMpyURfBkf+o/526Zb6kd/tchUP3iBDEPjqjb+U2MAJhVRxxrmr2fwpe08E7QsV7YLcpq0tUaQ9O9x97ZIxQ==
"@types/source-list-map@*":
version "0.1.2"
resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9"
@ -8842,12 +8901,12 @@ cross-fetch@^2.1.0:
node-fetch "2.1.2"
whatwg-fetch "2.0.4"
cross-fetch@^3.1.4:
version "3.1.4"
resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.4.tgz#9723f3a3a247bf8b89039f3a380a9244e8fa2f39"
integrity sha512-1eAtFWdIubi6T4XPy6ei9iUFoKpUkIF971QLN8lIvvvwueI65+Nw5haMNKUwfJxabqlIIDODJKGrQ66gxC0PbQ==
cross-fetch@^3.1.4, cross-fetch@^3.1.5:
version "3.1.5"
resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f"
integrity sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==
dependencies:
node-fetch "2.6.1"
node-fetch "2.6.7"
cross-spawn@7.0.3, cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3:
version "7.0.3"
@ -19714,12 +19773,7 @@ node-fetch@2.1.2:
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.1.2.tgz#ab884e8e7e57e38a944753cec706f788d1768bb5"
integrity sha1-q4hOjn5X44qUR1POxwb3iNF2i7U=
node-fetch@2.6.1:
version "2.6.1"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
node-fetch@^2.3.0, node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@~2.6.1:
node-fetch@2.6.7, node-fetch@^2.3.0, node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@~2.6.1:
version "2.6.7"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==
@ -24562,6 +24616,11 @@ ses@^0.12.4:
"@agoric/make-hardener" "^0.1.2"
"@agoric/transform-module" "^0.4.1"
ses@^0.15.7:
version "0.15.11"
resolved "https://registry.yarnpkg.com/ses/-/ses-0.15.11.tgz#851cb6a20d8967537075d25bb0185051c28c23db"
integrity sha512-lQg6q8/PVf+n18EjP+5Uv1tN9oVQ3br5QxJzPXoAVQleSYnlf20JY9coe7n1B9A6CtIKIHyr6m/TfskcRCufgA==
set-blocking@^2.0.0, set-blocking@~2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"

Loading…
Cancel
Save