diff --git a/.circleci/config.yml b/.circleci/config.yml index 3836d5e4048e..74815818582f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -87,15 +87,15 @@ aliases: cat ${HOME}/project/.circleci/scripts/enable-vnc.sh >> ~/.bashrc fi - # Check if MMI tests should run - - &check-mmi-trigger - name: Check if MMI tests should run + # Check if MMI Optional tests should run + - &check-mmi-optional + name: Check if MMI Optional tests should run command: | - source mmi_trigger.env - if [ "${run_mmi_tests}" == "true" ]; then - echo "Running MMI tests" + RUN_MMI_OPTIONAL=$(cat ./RUN_MMI_OPTIONAL) + if [[ "${RUN_MMI_OPTIONAL}" == "true" ]]; then + echo "Running MMI Optional tests" else - echo "Skipping MMI tests" + echo "Skipping MMI Optional tests" circleci step halt fi @@ -114,7 +114,7 @@ workflows: - trigger-beta-build: requires: - prep-deps - - check-mmi-trigger + - check-pr-tag - prep-deps - get-changed-files-with-git-diff: filters: @@ -179,7 +179,7 @@ workflows: - prep-build-test-mmi-playwright: requires: - prep-deps - - check-mmi-trigger + - check-pr-tag - prep-build-storybook: requires: - prep-deps @@ -231,7 +231,7 @@ workflows: requires: - prep-build-test-mmi - get-changed-files-with-git-diff - - test-e2e-mmi-playwright: + - test-e2e-mmi-playwright - OPTIONAL: requires: - prep-build-test-mmi-playwright - test-e2e-chrome-rpc-mmi: @@ -421,6 +421,39 @@ jobs: name: Create GitHub Pull Request for version command: .circleci/scripts/release-create-release-pr.sh + check-pr-tag: + docker: + - image: cimg/base:stable + steps: + - run: + name: Check for MMI Team Tag + command: | + #!/bin/bash + + GH_LABEL=team-mmi + if [ -z "$CIRCLE_PULL_REQUESTS" ]; then + echo "Skipping tag check; this is not a PR." + echo "false" > ./RUN_MMI_OPTIONAL + exit 0 + fi + + echo $CIRCLE_PULL_REQUESTS | sed 's/,/\n/g' + + # See if any associated PRs have matching label + HAS_MATCHING_PR=$(echo $CIRCLE_PULL_REQUESTS \ + | sed -e 's#,#\n#g' -e 's#/github.com/#/api.github.com/repos/#g' -e 's#/pull/#/pulls/#g' \ + | xargs -n1 curl -s \ + | jq -s "map((.labels|map(select(.name==\"${GH_LABEL}\"))))|flatten|length > 0") + + echo "${GH_LABEL} tag presence: ${HAS_MATCHING_PR}" + + # assign the RUN_MMI_OPTIONAL variable + echo "${HAS_MATCHING_PR}" > ./RUN_MMI_OPTIONAL + - persist_to_workspace: + root: . + paths: + - RUN_MMI_OPTIONAL + prep-deps: executor: node-browsers-medium steps: @@ -806,7 +839,7 @@ jobs: - run: corepack enable - attach_workspace: at: . - - run: *check-mmi-trigger + - run: *check-mmi-optional - run: name: Build MMI extension for Playwright e2e command: | @@ -821,6 +854,7 @@ jobs: - persist_to_workspace: root: . paths: + - RUN_MMI_OPTIONAL - dist-test-mmi-playwright - builds-test-mmi-playwright - store_artifacts: @@ -1272,7 +1306,7 @@ jobs: - store_test_results: path: test/test-results/e2e - test-e2e-mmi-playwright: + test-e2e-mmi-playwright - OPTIONAL: executor: playwright parallelism: 2 steps: @@ -1280,7 +1314,7 @@ jobs: - run: corepack enable - attach_workspace: at: . - - run: *check-mmi-trigger + - run: *check-mmi-optional - run: name: Move test build to dist command: mv ./dist-test-mmi-playwright ./dist @@ -1709,18 +1743,3 @@ jobs: - run: name: All Tests Passed command: echo 'whew - everything passed!' - - check-mmi-trigger: - executor: node-browsers-medium - steps: - - checkout - - run: - name: Check for MMI Team Label or Reviewer - command: ./.circleci/scripts/check_mmi_trigger.sh - - store_artifacts: - path: mmi_trigger.env - destination: mmi_trigger.env - - persist_to_workspace: - root: . - paths: - - mmi_trigger.env diff --git a/.circleci/scripts/check_mmi_trigger.sh b/.circleci/scripts/check_mmi_trigger.sh deleted file mode 100755 index 2de2f69044d4..000000000000 --- a/.circleci/scripts/check_mmi_trigger.sh +++ /dev/null @@ -1,60 +0,0 @@ -#!/bin/bash -set -eo pipefail - -# Ensure required environment variables are set -if [ -z "$CIRCLE_PULL_REQUEST" ] || [ -z "$GITHUB_TOKEN" ]; then - echo "This appears to be a fork or required environment variables are not set." - echo "Skipping MMI tests." - echo "run_mmi_tests=false" > mmi_trigger.env - exit 0 -fi - -# Extract PR number from the pull request URL -PR_NUMBER=$(echo "$CIRCLE_PULL_REQUEST" | awk -F'/' '{print $NF}') - -# Define repository details -REPO_OWNER="$CIRCLE_PROJECT_USERNAME" -REPO_NAME=$(basename "$CIRCLE_REPOSITORY_URL" .git) - -# Fetch PR details using GitHub API -PR_DETAILS=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \ - "https://api.github.com/repos/$REPO_OWNER/$REPO_NAME/pulls/$PR_NUMBER") - -# Fetch submitted reviews -SUBMITTED_REVIEWS=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \ - "https://api.github.com/repos/$REPO_OWNER/$REPO_NAME/pulls/$PR_NUMBER/reviews") - -# Check for label 'team-mmi' -LABEL_EXISTS=$(jq -r '.labels[]? | select(.name == "team-mmi") | length > 0' <<< "$PR_DETAILS") - -# Check for individual reviewer 'mmi' -REVIEWER_REQUESTED=$(jq -r '.requested_reviewers[]? | select(.login == "mmi") | length > 0' <<< "$PR_DETAILS") - -# Check for team reviewer 'mmi' -TEAM_REQUESTED=$(jq -r '.requested_teams[]? | select(.slug == "mmi") | length > 0' <<< "$PR_DETAILS") - -# Check if 'mmi' submitted a review -REVIEWER_SUBMITTED=$(jq -r '.[]? | select(.user.login == "mmi") | length > 0' <<< "$SUBMITTED_REVIEWS") - -# Determine which condition was met and trigger tests if needed -if [[ "$LABEL_EXISTS" == "true" || "$REVIEWER_REQUESTED" == "true" || "$TEAM_REQUESTED" == "true" || "$REVIEWER_SUBMITTED" == "true" ]]; then - echo "run_mmi_tests=true" > mmi_trigger.env - - # Log exactly which condition was met - echo "Conditions met:" - if [[ "$LABEL_EXISTS" == "true" ]]; then - echo "- Label 'team-mmi' found." - fi - if [[ "$REVIEWER_REQUESTED" == "true" ]]; then - echo "- Reviewer 'mmi' requested." - fi - if [[ "$TEAM_REQUESTED" == "true" ]]; then - echo "- Team 'mmi' requested." - fi - if [[ "$REVIEWER_SUBMITTED" == "true" ]]; then - echo "- Reviewer 'mmi' submitted a review." - fi -else - echo "run_mmi_tests=false" > mmi_trigger.env - echo "Skipping MMI tests: Neither the 'team-mmi' label was found nor a reviewer from the 'MetaMask/mmi' team was assigned." -fi diff --git a/.depcheckrc.yml b/.depcheckrc.yml index 50b79a78ec30..d0d6eac5b5bc 100644 --- a/.depcheckrc.yml +++ b/.depcheckrc.yml @@ -81,8 +81,6 @@ ignores: # trezor - 'ts-mixer' - '@testing-library/dom' - - 'mini-css-extract-plugin' - - 'webpack-cli' # files depcheck should not parse ignorePatterns: diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index f37a101e6cb2..e14d27619a07 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -52,12 +52,7 @@ privacy-snapshot.json @MetaMask/extension-privacy-reviewers .devcontainer/ @MetaMask/library-admins @HowardBraham @plasmacorral # Confirmations team to own code for confirmations on UI. -app/scripts/lib/ppom @MetaMask/confirmations -app/scripts/lib/signature @MetaMask/confirmations -app/scripts/lib/transaction/decode @MetaMask/confirmations -app/scripts/lib/transaction/metrics.* @MetaMask/confirmations -app/scripts/lib/transaction/util.* @MetaMask/confirmations -ui/pages/confirmations @MetaMask/confirmations +ui/pages/confirmations @MetaMask/confirmations # MMI team is responsible for code related with Institutioanl version of MetaMask ui/pages/institutional @MetaMask/mmi diff --git a/.gitignore b/.gitignore index 074f4076a7cc..1671e69527e0 100644 --- a/.gitignore +++ b/.gitignore @@ -80,6 +80,3 @@ html-report/ /app/images/branding /changed-files - -# UI Integration tests -test/integration/config/assets diff --git a/.metamaskrc.dist b/.metamaskrc.dist index cbb55baedc7b..fc2a5a831a4b 100644 --- a/.metamaskrc.dist +++ b/.metamaskrc.dist @@ -4,10 +4,6 @@ ; This variable is required INFURA_PROJECT_ID=00000000000 -; This variable is not required but it's necessary for the storybook -; to render stories that use onchain data. -INFURA_STORYBOOK_PROJECT_ID= - ;PASSWORD=METAMASK PASSWORD ;SEGMENT_WRITE_KEY= ;BRIDGE_USE_DEV_APIS= diff --git a/.storybook/main.js b/.storybook/main.js index 2b4384250c9c..d63d924aa2e2 100644 --- a/.storybook/main.js +++ b/.storybook/main.js @@ -1,9 +1,6 @@ const path = require('path'); const { ProvidePlugin } = require('webpack'); const CopyWebpackPlugin = require('copy-webpack-plugin'); -const dotenv = require('dotenv'); -dotenv.config({ path: path.resolve(__dirname, '../.metamaskrc') }); - module.exports = { core: { disableTelemetry: true, @@ -32,7 +29,6 @@ module.exports = { env: (config) => ({ ...config, ENABLE_CONFIRMATION_REDESIGN: true, - INFURA_PROJECT_ID: process.env.INFURA_STORYBOOK_PROJECT_ID || '', }), // Uses babel.config.js settings and prevents "Missing class properties transform" error babel: async (options) => ({ @@ -52,16 +48,10 @@ module.exports = { config.resolve.alias['../../../../../../store/actions'] = require.resolve( '../ui/__mocks__/actions.js', ); - config.resolve.alias['../../../store/actions'] = require.resolve( - '../ui/__mocks__/actions.js', - ); // Import within controller-utils crashes storybook. config.resolve.alias['@ethereumjs/util'] = require.resolve( '../ui/__mocks__/ethereumjs-util.js', ); - config.resolve.alias['./useNftCollectionsMetadata'] = require.resolve( - '../ui/__mocks__/useNftCollectionsMetadata.js', - ); config.resolve.fallback = { child_process: false, constants: false, @@ -96,7 +86,7 @@ module.exports = { sourceMap: true, implementation: require('sass-embedded'), sassOptions: { - includePaths: ['ui/css/', 'node_modules/'], + includePaths: ['ui/css/', 'node_modules/',], }, }, }, @@ -106,7 +96,12 @@ module.exports = { new CopyWebpackPlugin({ patterns: [ { - from: path.join('ui', 'css', 'utilities', 'fonts/'), + from: path.join( + 'ui', + 'css', + 'utilities', + 'fonts/', + ), to: 'fonts', }, { diff --git a/.storybook/test-data.js b/.storybook/test-data.js index 13006e5d1ff7..cbcebb6347ed 100644 --- a/.storybook/test-data.js +++ b/.storybook/test-data.js @@ -1596,7 +1596,6 @@ const state = { }, }, }, - openSeaEnabled: true, }, appState: { shouldClose: false, diff --git a/.yarn/patches/@babel-core-npm-7.23.2-b93f586907.patch b/.yarn/patches/@babel-core-npm-7.23.2-b93f586907.patch new file mode 100644 index 000000000000..fdae8d6b2b4e --- /dev/null +++ b/.yarn/patches/@babel-core-npm-7.23.2-b93f586907.patch @@ -0,0 +1,12 @@ +diff --git a/lib/index.js b/lib/index.js +index 64ff8344f6280d20988f8c3c81e1f248a1869e53..6739e7bd2271be6b861479ec384bbd007bdb5df8 100644 +--- a/lib/index.js ++++ b/lib/index.js +@@ -222,7 +222,6 @@ var _transform = require("./transform.js"); + var _transformFile = require("./transform-file.js"); + var _transformAst = require("./transform-ast.js"); + var _parse = require("./parse.js"); +-var thisFile = require("./index.js"); + ; + const version = "7.23.2"; + exports.version = version; diff --git a/.yarn/patches/@babel-core-npm-7.25.9-4ae3bff7f3.patch b/.yarn/patches/@babel-core-npm-7.25.9-4ae3bff7f3.patch deleted file mode 100644 index 5010df3a0e88..000000000000 --- a/.yarn/patches/@babel-core-npm-7.25.9-4ae3bff7f3.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff --git a/lib/index.js b/lib/index.js -index 55b58e10eef589ff80ae33ebd1f1efe488b01153..e919c190d33ab9563f1364667fb4f5894bb6435d 100644 ---- a/lib/index.js -+++ b/lib/index.js -@@ -211,7 +211,6 @@ var _transform = require("./transform.js"); - var _transformFile = require("./transform-file.js"); - var _transformAst = require("./transform-ast.js"); - var _parse = require("./parse.js"); --var thisFile = require("./index.js"); - ; - const version = exports.version = "7.25.9"; - const resolvePlugin = (name, dirname) => resolvers.resolvePlugin(name, dirname, false).filepath; diff --git a/.yarn/patches/@babel-runtime-npm-7.25.9-fe8c62510a.patch b/.yarn/patches/@babel-runtime-npm-7.24.0-7eb1dd11a2.patch similarity index 87% rename from .yarn/patches/@babel-runtime-npm-7.25.9-fe8c62510a.patch rename to .yarn/patches/@babel-runtime-npm-7.24.0-7eb1dd11a2.patch index 4fec43fdb0c3..0fb2e4f26622 100644 --- a/.yarn/patches/@babel-runtime-npm-7.25.9-fe8c62510a.patch +++ b/.yarn/patches/@babel-runtime-npm-7.24.0-7eb1dd11a2.patch @@ -1,10 +1,10 @@ diff --git a/helpers/construct.js b/helpers/construct.js -index aee8e70448824f509d6605e2dfa4455167442f21..00a69eba8d4c15a1f9aa318a50abb96c2ec447d9 100644 +index 771e1d7952e80f11619424fbabb3744b959ffa49..5fe152bc1129bd8c8b7bb217ca1972ac4e089051 100644 --- a/helpers/construct.js +++ b/helpers/construct.js -@@ -1,10 +1,22 @@ - var isNativeReflectConstruct = require("./isNativeReflectConstruct.js"); +@@ -1,10 +1,21 @@ -var setPrototypeOf = require("./setPrototypeOf.js"); +-var isNativeReflectConstruct = require("./isNativeReflectConstruct.js"); -function _construct(t, e, r) { - if (isNativeReflectConstruct()) return Reflect.construct.apply(null, arguments); - var o = [null]; diff --git a/.yarn/patches/@metamask-assets-controllers-npm-41.0.0-57b3d695bb.patch b/.yarn/patches/@metamask-assets-controllers-npm-38.3.0-57b3d695bb.patch similarity index 100% rename from .yarn/patches/@metamask-assets-controllers-npm-41.0.0-57b3d695bb.patch rename to .yarn/patches/@metamask-assets-controllers-npm-38.3.0-57b3d695bb.patch diff --git a/.yarn/patches/@metamask-snaps-utils-npm-8.4.1-90481bac4b.patch b/.yarn/patches/@metamask-snaps-utils-npm-8.4.1-90481bac4b.patch deleted file mode 100644 index 1c0aa8a99b3a..000000000000 --- a/.yarn/patches/@metamask-snaps-utils-npm-8.4.1-90481bac4b.patch +++ /dev/null @@ -1,26 +0,0 @@ -diff --git a/dist/json-rpc.cjs b/dist/json-rpc.cjs -index 6061f7b8b42f0521b0718d616e5a12a1a7520068..11d0233a7bd4b610a99da6a3d105840e88e108e6 100644 ---- a/dist/json-rpc.cjs -+++ b/dist/json-rpc.cjs -@@ -68,7 +68,7 @@ function createOriginRegExp(matcher) { - const escaped = matcher.replace(/[.*+?^${}()|[\]\\]/gu, '\\$&'); - // Support wildcards - const regex = escaped.replace(/\\\*/gu, '.*'); -- return RegExp(`${regex}$`, 'u'); -+ return RegExp(`^${regex}$`, 'u'); - } - /** - * Check whether an origin is allowed or not using a matcher string. -diff --git a/dist/json-rpc.mjs b/dist/json-rpc.mjs -index bfa1c2dbbed46a2221ef708afdb97b15db84bc1b..81bc2150cf5d6a9bdabe8d43b04352b299bc1c4d 100644 ---- a/dist/json-rpc.mjs -+++ b/dist/json-rpc.mjs -@@ -63,7 +63,7 @@ function createOriginRegExp(matcher) { - const escaped = matcher.replace(/[.*+?^${}()|[\]\\]/gu, '\\$&'); - // Support wildcards - const regex = escaped.replace(/\\\*/gu, '.*'); -- return RegExp(`${regex}$`, 'u'); -+ return RegExp(`^${regex}$`, 'u'); - } - /** - * Check whether an origin is allowed or not using a matcher string. diff --git a/CHANGELOG.md b/CHANGELOG.md index e849728f7781..fa15862a7fd1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,41 +6,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -## [12.7.0] -### Added -- Added Token Network Filter UI, allowing users to filter tokens by network (behind a feature flag) ([#27884](https://github.com/MetaMask/metamask-extension/pull/27884)) -- Added Ape token icon for mainnet ([#27974](https://github.com/MetaMask/metamask-extension/pull/27974)) -- Implemented redesigned native asset transfer for both wallet-initiated and dApp-initiated confirmations ([#27979](https://github.com/MetaMask/metamask-extension/pull/27979)) -- Enabled the Security Alerts API with a fallback mechanism to ensure user experience is not disrupted ([#28040](https://github.com/MetaMask/metamask-extension/pull/28040)) -- Added re-simulation logic to the transaction controller ([#28104](https://github.com/MetaMask/metamask-extension/pull/28104)) -- Made the message section in the signature page collapsible and added a copy option ([#28038](https://github.com/MetaMask/metamask-extension/pull/28038)) -- Added token transfer confirmation for ERC721 and ERC1155 tokens ([#27955](https://github.com/MetaMask/metamask-extension/pull/27955)) -- Added support for external links in feature announcements ([#26491](https://github.com/MetaMask/metamask-extension/pull/26491)) -- Added a Notifications option to the settings page ([#26843](https://github.com/MetaMask/metamask-extension/pull/26843)) -- Enabled the use of a preview token to view unpublished content from Contentful ([#27809](https://github.com/MetaMask/metamask-extension/pull/27809)) -- Added account syncing to MetaMask, allowing users to synchronize accounts and account names across devices ([#28120](https://github.com/MetaMask/metamask-extension/pull/28120)) -- Introduced a new phishing warning UI with improved design ([#27942](https://github.com/MetaMask/metamask-extension/pull/27942)) -- Added a privacy mode toggle to hide and show sensitive information and token balances ([#28021](https://github.com/MetaMask/metamask-extension/pull/28021)) -- Added test network to the default selected networks list if it is the globally selected network during a connection request ([#27980](https://github.com/MetaMask/metamask-extension/pull/27980)) - -### Changed -- Allowed users to remove Linea from the networks list and added it to the Popular Networks section ([#27512](https://github.com/MetaMask/metamask-extension/pull/27512)) -- Updated transaction controller to reduce gas limit fallback and remove global network usage from transaction simulation ([#27954](https://github.com/MetaMask/metamask-extension/pull/27954)) -- Reduced usage of scientific notation by implementing a decimals rounding strategy and added tooltips for full values ([#27992](https://github.com/MetaMask/metamask-extension/pull/27992)) -- Improved visibility of decrypted messages and added a "scroll to bottom" button ([#27622](https://github.com/MetaMask/metamask-extension/pull/27622)) -- Updated network message to show the full network name on the Review Permission and Connections pages ([#28126](https://github.com/MetaMask/metamask-extension/pull/28126)) -- Removed the feature flag for the confirmations screen ([#27877](https://github.com/MetaMask/metamask-extension/pull/27877)) - -### Fixed -- Fixed issue where token balance showed as 0 during send flow when navigating from the token details page ([#28136](https://github.com/MetaMask/metamask-extension/pull/28136)) -- Fixed issue where small spending caps were coerced to zero on the approve screen ([#28179](https://github.com/MetaMask/metamask-extension/pull/28179)) -- Fixed gas calculations for low Max base fee and Priority fee ([#28037](https://github.com/MetaMask/metamask-extension/pull/28037)) -- Disabled notifications when Basic functionality is turned off ([#28045]) -- Fixed alignment issues of custom UI links in Snaps ([#27957](https://github.com/MetaMask/metamask-extension/pull/27957)) -- Fixed misalignment of the quote rate in swaps ([#28016](https://github.com/MetaMask/metamask-extension/pull/28016)) -- Prevented scrolling to the account list item on the send page to keep the relevant UI in view ([#27934](https://github.com/MetaMask/metamask-extension/pull/27934)) -- Improved handling of network switching and adding networks to prevent issues with queued transactions ([#28090](https://github.com/MetaMask/metamask-extension/pull/28090)) -- Prevented redirect after adding a network in Onboarding Settings ([#28165](https://github.com/MetaMask/metamask-extension/pull/28165)) +## [12.6.2] +### Fixed +- Prevent QR code scanning from setting incorrect recipient addresses during the send flow by restricting the QR scanner feature to only handle simple sends, and fail on QR codes that encode more complex transaction types ([#28521](https://github.com/MetaMask/metamask-extension/pull/28521)) ## [12.6.1] ### Fixed @@ -5343,8 +5311,8 @@ Update styles and spacing on the critical error page ([#20350](https://github.c - Added the ability to restore accounts from seed words. -[Unreleased]: https://github.com/MetaMask/metamask-extension/compare/v12.7.0...HEAD -[12.7.0]: https://github.com/MetaMask/metamask-extension/compare/v12.6.1...v12.7.0 +[Unreleased]: https://github.com/MetaMask/metamask-extension/compare/v12.6.2...HEAD +[12.6.2]: https://github.com/MetaMask/metamask-extension/compare/v12.6.1...v12.6.2 [12.6.1]: https://github.com/MetaMask/metamask-extension/compare/v12.6.0...v12.6.1 [12.6.0]: https://github.com/MetaMask/metamask-extension/compare/v12.5.1...v12.6.0 [12.5.1]: https://github.com/MetaMask/metamask-extension/compare/v12.5.0...v12.5.1 diff --git a/app/_locales/de/messages.json b/app/_locales/de/messages.json index 9af24022bcb3..fe0c84afcfac 100644 --- a/app/_locales/de/messages.json +++ b/app/_locales/de/messages.json @@ -207,6 +207,23 @@ "addCustomNetwork": { "message": "Benutzerdefiniertes Netzwerk hinzufügen" }, + "addEthereumChainConfirmationDescription": { + "message": "Dadurch kann dieses Netzwerk innerhalb MetaMask verwendet werden." + }, + "addEthereumChainConfirmationRisks": { + "message": "MetaMask überprüft keine benutzerdefinierten Netzwerke." + }, + "addEthereumChainConfirmationRisksLearnMore": { + "message": "Erfahren Sie mehr über $1.", + "description": "$1 is a link with text that is provided by the 'addEthereumChainConfirmationRisksLearnMoreLink' key" + }, + "addEthereumChainConfirmationRisksLearnMoreLink": { + "message": "Betrug und Sicherheitsrisiken im Netzwerk", + "description": "Link text for the 'addEthereumChainConfirmationRisksLearnMore' translation key" + }, + "addEthereumChainConfirmationTitle": { + "message": "Dieser Seite das Hinzufügen eines Netzwerks erlauben?" + }, "addEthereumChainWarningModalHeader": { "message": "Fügen Sie diesen RPC-Anbieter nur hinzu, wenn Sie sich sicher sind, dass Sie ihm vertrauen können. $1", "description": "$1 is addEthereumChainWarningModalHeaderPartTwo passed separately so that it can be bolded" @@ -805,6 +822,10 @@ "buyNow": { "message": "Jetzt kaufen" }, + "buyToken": { + "message": "$1 kaufen", + "description": "$1 is the token symbol" + }, "bytes": { "message": "Bytes" }, @@ -1879,6 +1900,13 @@ "functionType": { "message": "Funktionstyp" }, + "fundYourWallet": { + "message": "Versehen Sie Ihre Wallet mit Geldern" + }, + "fundYourWalletDescription": { + "message": "Legen Sie los, indem Sie Ihrer Wallet $1 hinzufügen.", + "description": "$1 is the token symbol" + }, "gas": { "message": "Gas" }, @@ -1962,6 +1990,14 @@ "genericExplorerView": { "message": "Konto auf $1 ansehen" }, + "getStartedWithNFTs": { + "message": "Erhalten Sie $1 für den Kauf von NFTs", + "description": "$1 is the token symbol" + }, + "getStartedWithNFTsDescription": { + "message": "Legen Sie mit NFTs los, indem Sie Ihrer Wallet $1 hinzufügen.", + "description": "$1 is the token symbol" + }, "goBack": { "message": "Zurück" }, @@ -4822,6 +4858,9 @@ "message": "Kontaktieren Sie die Ersteller von $1 für weitere Unterstützung.", "description": "This is shown when the insight snap throws an error. $1 is the snap name" }, + "someNetworksMayPoseSecurity": { + "message": "Einige Netzwerke können Sicherheits- und/oder Datenschutzrisiken bergen. Informieren Sie sich über die Risiken, bevor Sie ein Netzwerk hinzufügen und nutzen." + }, "somethingDoesntLookRight": { "message": "Scheint irgendetwas nicht in Ordnung zu sein? $1", "description": "A false positive message for users to contact support. $1 is a link to the support page." @@ -4975,6 +5014,14 @@ "stake": { "message": "Anteil" }, + "startYourJourney": { + "message": "Beginnen Sie Ihre Reise mit $1", + "description": "$1 is the token symbol" + }, + "startYourJourneyDescription": { + "message": "Legen Sie mit web3 los, indem Sie Ihrer Wallet $1 hinzufügen.", + "description": "$1 is the token symbol" + }, "stateLogError": { "message": "Fehler beim Abfragen der Statusprotokolle." }, @@ -5960,6 +6007,9 @@ "viewActivity": { "message": "Aktivität anzeigen" }, + "viewAllDetails": { + "message": "Alle Details anzeigen" + }, "viewAllQuotes": { "message": "alle Angebote anzeigen" }, diff --git a/app/_locales/el/messages.json b/app/_locales/el/messages.json index 308099b1c2b1..5c42a8d829b4 100644 --- a/app/_locales/el/messages.json +++ b/app/_locales/el/messages.json @@ -207,6 +207,23 @@ "addCustomNetwork": { "message": "Προσθήκη προσαρμοσμένου δικτύου" }, + "addEthereumChainConfirmationDescription": { + "message": "Αυτό θα επιτρέψει σε αυτό το δίκτυο να χρησιμοποιηθεί στο MetaMask." + }, + "addEthereumChainConfirmationRisks": { + "message": "Το MetaMask δεν επαληθεύει τα προσαρμοσμένα δίκτυα." + }, + "addEthereumChainConfirmationRisksLearnMore": { + "message": "Μάθετε για το $1.", + "description": "$1 is a link with text that is provided by the 'addEthereumChainConfirmationRisksLearnMoreLink' key" + }, + "addEthereumChainConfirmationRisksLearnMoreLink": { + "message": "απάτες και κίνδυνοι ασφάλειας δικτύου", + "description": "Link text for the 'addEthereumChainConfirmationRisksLearnMore' translation key" + }, + "addEthereumChainConfirmationTitle": { + "message": "Επιτρέπετε σε αυτήν την ιστοσελίδα να προσθέσει ένα δίκτυο;" + }, "addEthereumChainWarningModalHeader": { "message": "Προσθέστε αυτόν τον πάροχο RPC μόνο αν είστε σίγουροι ότι μπορείτε να τον εμπιστευτείτε. $1", "description": "$1 is addEthereumChainWarningModalHeaderPartTwo passed separately so that it can be bolded" @@ -805,6 +822,10 @@ "buyNow": { "message": "Αγοράστε Τώρα" }, + "buyToken": { + "message": "Αγορά $1", + "description": "$1 is the token symbol" + }, "bytes": { "message": "Bytes" }, @@ -1879,6 +1900,13 @@ "functionType": { "message": "Τύπος λειτουργίας" }, + "fundYourWallet": { + "message": "Χρηματοδοτήστε το πορτοφόλι σας" + }, + "fundYourWalletDescription": { + "message": "Ξεκινήστε προσθέτοντας περίπου $1 στο πορτοφόλι σας.", + "description": "$1 is the token symbol" + }, "gas": { "message": "Τέλος συναλλαγής" }, @@ -1962,6 +1990,14 @@ "genericExplorerView": { "message": "Προβολή λογαριασμού σε $1" }, + "getStartedWithNFTs": { + "message": "Λάβετε $1 για να αγοράσετε NFT", + "description": "$1 is the token symbol" + }, + "getStartedWithNFTsDescription": { + "message": "Ξεκινήστε με NFT προσθέτοντας περίπου $1 στο πορτοφόλι σας.", + "description": "$1 is the token symbol" + }, "goBack": { "message": "Πηγαίνετε πίσω" }, @@ -4822,6 +4858,9 @@ "message": "Επικοινωνήστε με τους διαχειριστές του $1 για περαιτέρω υποστήριξη.", "description": "This is shown when the insight snap throws an error. $1 is the snap name" }, + "someNetworksMayPoseSecurity": { + "message": "Ορισμένα δίκτυα ενδέχεται να ενέχουν κινδύνους για την ασφάλεια ή/και το απόρρητο. Ενημερωθείτε για τους κινδύνους πριν προσθέσετε και χρησιμοποιήσετε ένα δίκτυο." + }, "somethingDoesntLookRight": { "message": "Κάτι δεν φαίνεται σωστό; $1", "description": "A false positive message for users to contact support. $1 is a link to the support page." @@ -4975,6 +5014,14 @@ "stake": { "message": "Stake" }, + "startYourJourney": { + "message": "Ξεκινήστε το ταξίδι σας με $1", + "description": "$1 is the token symbol" + }, + "startYourJourneyDescription": { + "message": "Ξεκινήστε με Web3 προσθέτοντας περίπου $1 στο πορτοφόλι σας.", + "description": "$1 is the token symbol" + }, "stateLogError": { "message": "Σφάλμα κατά την ανάκτηση αρχείων καταγραφής κατάστασης." }, @@ -5960,6 +6007,9 @@ "viewActivity": { "message": "Προβολή δραστηριότητας" }, + "viewAllDetails": { + "message": "Προβολή όλων των λεπτομερειών" + }, "viewAllQuotes": { "message": "προβολή όλων των προσφορών" }, diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 512eb3b0ee36..1c45a0cd6a6e 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -231,6 +231,23 @@ "addCustomNetwork": { "message": "Add custom network" }, + "addEthereumChainConfirmationDescription": { + "message": "This will allow this network to be used within MetaMask." + }, + "addEthereumChainConfirmationRisks": { + "message": "MetaMask does not verify custom networks." + }, + "addEthereumChainConfirmationRisksLearnMore": { + "message": "Learn about $1.", + "description": "$1 is a link with text that is provided by the 'addEthereumChainConfirmationRisksLearnMoreLink' key" + }, + "addEthereumChainConfirmationRisksLearnMoreLink": { + "message": "scams and network security risks", + "description": "Link text for the 'addEthereumChainConfirmationRisksLearnMore' translation key" + }, + "addEthereumChainConfirmationTitle": { + "message": "Allow this site to add a network?" + }, "addEthereumChainWarningModalHeader": { "message": "Only add this RPC provider if you’re sure you can trust it. $1", "description": "$1 is addEthereumChainWarningModalHeaderPartTwo passed separately so that it can be bolded" @@ -497,10 +514,6 @@ "allCustodianAccountsConnectedTitle": { "message": "No accounts available to connect" }, - "allNetworks": { - "message": "All Networks", - "description": "Speicifies to token network filter to filter by all Networks" - }, "allOfYour": { "message": "All of your $1", "description": "$1 is the symbol or name of the token that the user is approving spending" @@ -875,6 +888,12 @@ "message": "Buy $1", "description": "$1 is the ticker symbol of a an asset the user is being prompted to purchase" }, + "buyCrypto": { + "message": "Buy crypto" + }, + "buyFirstCrypto": { + "message": "Buy your first crypto with a debit or credit card." + }, "buyMoreAsset": { "message": "Buy more $1", "description": "$1 is the ticker symbol of a an asset the user is being prompted to purchase" @@ -882,6 +901,10 @@ "buyNow": { "message": "Buy Now" }, + "buyToken": { + "message": "Buy $1", + "description": "$1 is the token symbol" + }, "bytes": { "message": "Bytes" }, @@ -1167,14 +1190,10 @@ "message": "Connected with $1", "description": "$1 represents account name" }, - "connectedWithNetwork": { + "connectedWithNetworks": { "message": "$1 networks connected", "description": "$1 represents network length" }, - "connectedWithNetworkName": { - "message": "Connected with $1", - "description": "$1 represents network name" - }, "connecting": { "message": "Connecting" }, @@ -1349,10 +1368,6 @@ "currentLanguage": { "message": "Current language" }, - "currentNetwork": { - "message": "Current Network", - "description": "Speicifies to token network filter to filter by current Network. Will render when network nickname is not available" - }, "currentRpcUrlDeprecated": { "message": "The current rpc url for this network has been deprecated." }, @@ -1512,9 +1527,6 @@ "dcent": { "message": "D'Cent" }, - "debitCreditPurchaseOptions": { - "message": "Debit or credit card purchase options" - }, "decimal": { "message": "Token decimal" }, @@ -2107,8 +2119,12 @@ "functionType": { "message": "Function type" }, - "fundingMethod": { - "message": "Funding method" + "fundYourWallet": { + "message": "Fund your wallet" + }, + "fundYourWalletDescription": { + "message": "Get started by adding some $1 to your wallet.", + "description": "$1 is the token symbol" }, "gas": { "message": "Gas" @@ -2199,6 +2215,20 @@ "genericExplorerView": { "message": "View account on $1" }, + "getStarted": { + "message": "Get Started" + }, + "getStartedByFundingWallet": { + "message": "Get started by adding some crypto to your wallet." + }, + "getStartedWithNFTs": { + "message": "Get $1 to buy NFTs", + "description": "$1 is the token symbol" + }, + "getStartedWithNFTsDescription": { + "message": "Get started with NFTs by adding some $1 to your wallet.", + "description": "$1 is the token symbol" + }, "goBack": { "message": "Go back" }, @@ -4457,10 +4487,6 @@ "message": "Requesting for $1", "description": "Name of Account" }, - "requestingForNetwork": { - "message": "Requesting for $1", - "description": "Name of Network" - }, "requestsAwaitingAcknowledgement": { "message": "requests waiting to be acknowledged" }, @@ -4648,9 +4674,6 @@ "securityDescription": { "message": "Reduce your chances of joining unsafe networks and protect your accounts" }, - "securityMessageLinkForNetworks": { - "message": "network scams and security risks" - }, "securityPrivacyPath": { "message": "Settings > Security & Privacy." }, @@ -4743,6 +4766,9 @@ "selectEnableDisplayMediaPrivacyPreference": { "message": "Turn on Display NFT Media" }, + "selectFundingMethod": { + "message": "Select a funding method" + }, "selectHdPath": { "message": "Select HD path" }, @@ -5218,6 +5244,9 @@ "message": "Contact the creators of $1 for further support.", "description": "This is shown when the insight snap throws an error. $1 is the snap name" }, + "someNetworksMayPoseSecurity": { + "message": "Some networks may pose security and/or privacy risks. Understand the risks before adding & using a network." + }, "somethingDoesntLookRight": { "message": "Something doesn't look right? $1", "description": "A false positive message for users to contact support. $1 is a link to the support page." @@ -5390,6 +5419,14 @@ "stake": { "message": "Stake" }, + "startYourJourney": { + "message": "Start your journey with $1", + "description": "$1 is the token symbol" + }, + "startYourJourneyDescription": { + "message": "Get started with web3 by adding some $1 to your wallet.", + "description": "$1 is the token symbol" + }, "stateLogError": { "message": "Error in retrieving state logs." }, @@ -5993,12 +6030,6 @@ "tips": { "message": "Tips" }, - "tipsForUsingAWallet": { - "message": "Tips for using a wallet" - }, - "tipsForUsingAWalletDescription": { - "message": "Adding tokens unlocks more ways to use web3." - }, "to": { "message": "To" }, @@ -6051,9 +6082,6 @@ "tokenList": { "message": "Token lists" }, - "tokenMarketplace": { - "message": "Token marketplace" - }, "tokenScamSecurityRisk": { "message": "token scams and security risks" }, @@ -6302,6 +6330,9 @@ "unknown": { "message": "Unknown" }, + "unknownChainWarning": { + "message": "We can’t verify custom networks. To avoid malicious providers from recording your network activity, only add networks you trust." + }, "unknownCollection": { "message": "Unnamed collection" }, @@ -6430,6 +6461,9 @@ "viewActivity": { "message": "View activity" }, + "viewAllDetails": { + "message": "View all details" + }, "viewAllQuotes": { "message": "view all quotes" }, @@ -6512,10 +6546,6 @@ "watchEthereumAccountsToggle": { "message": "Watch Ethereum Accounts (Beta)" }, - "watchOutMessage": { - "message": "Beware of $1.", - "description": "$1 is a link with text that is provided by the 'securityMessageLinkForNetworks' key" - }, "weak": { "message": "Weak" }, diff --git a/app/_locales/es/messages.json b/app/_locales/es/messages.json index ada162b9a12b..97d6f4be9854 100644 --- a/app/_locales/es/messages.json +++ b/app/_locales/es/messages.json @@ -207,6 +207,23 @@ "addCustomNetwork": { "message": "Agregar red personalizada" }, + "addEthereumChainConfirmationDescription": { + "message": "Esto permitirá que la red se utilice en MetaMask." + }, + "addEthereumChainConfirmationRisks": { + "message": "MetaMask no verifica redes personalizadas." + }, + "addEthereumChainConfirmationRisksLearnMore": { + "message": "Obtenga más información sobre $1.", + "description": "$1 is a link with text that is provided by the 'addEthereumChainConfirmationRisksLearnMoreLink' key" + }, + "addEthereumChainConfirmationRisksLearnMoreLink": { + "message": "estafas y riesgos de seguridad de la red", + "description": "Link text for the 'addEthereumChainConfirmationRisksLearnMore' translation key" + }, + "addEthereumChainConfirmationTitle": { + "message": "¿Permitir que este sitio agregue una red?" + }, "addEthereumChainWarningModalHeader": { "message": "Agregue este proveedor de RPC solo si está seguro de que puede confiar en él. $1", "description": "$1 is addEthereumChainWarningModalHeaderPartTwo passed separately so that it can be bolded" @@ -802,6 +819,10 @@ "buyNow": { "message": "Comprar ahora" }, + "buyToken": { + "message": "Comprar $1", + "description": "$1 is the token symbol" + }, "bytes": { "message": "Bytes" }, @@ -1876,6 +1897,13 @@ "functionType": { "message": "Tipo de función" }, + "fundYourWallet": { + "message": "Agregar fondos a su monedero" + }, + "fundYourWalletDescription": { + "message": "Comience agregando $1 a su monedero.", + "description": "$1 is the token symbol" + }, "gas": { "message": "Gas" }, @@ -1959,6 +1987,14 @@ "genericExplorerView": { "message": "Ver cuenta en $1" }, + "getStartedWithNFTs": { + "message": "Obtenga $1 para comprar NFT", + "description": "$1 is the token symbol" + }, + "getStartedWithNFTsDescription": { + "message": "Comience con los NFT agregando $1 a su monedero.", + "description": "$1 is the token symbol" + }, "goBack": { "message": "Volver" }, @@ -4819,6 +4855,9 @@ "message": "Póngase en contacto con los creadores de $1 para obtener más ayuda.", "description": "This is shown when the insight snap throws an error. $1 is the snap name" }, + "someNetworksMayPoseSecurity": { + "message": "Algunas redes pueden presentar riesgos de seguridad y/o privacidad. Comprenda los riesgos antes de agregar y utilizar una red." + }, "somethingDoesntLookRight": { "message": "Algo no se ve bien, ¿cierto? $1", "description": "A false positive message for users to contact support. $1 is a link to the support page." @@ -4972,6 +5011,14 @@ "stake": { "message": "Staking" }, + "startYourJourney": { + "message": "Comience su recorrido con $1", + "description": "$1 is the token symbol" + }, + "startYourJourneyDescription": { + "message": "Comience con la web3 agregando $1 a su monedero.", + "description": "$1 is the token symbol" + }, "stateLogError": { "message": "Error al recuperar los registros de estado." }, @@ -5957,6 +6004,9 @@ "viewActivity": { "message": "Ver actividad" }, + "viewAllDetails": { + "message": "Ver todos los detalles" + }, "viewAllQuotes": { "message": "ver todas las cotizaciones" }, diff --git a/app/_locales/es_419/messages.json b/app/_locales/es_419/messages.json index cebfc3cef106..672823c370ba 100644 --- a/app/_locales/es_419/messages.json +++ b/app/_locales/es_419/messages.json @@ -100,6 +100,23 @@ "addContact": { "message": "Agregar contacto" }, + "addEthereumChainConfirmationDescription": { + "message": "Esto permitirá que la red se utilice en MetaMask." + }, + "addEthereumChainConfirmationRisks": { + "message": "MetaMask no verifica redes personalizadas." + }, + "addEthereumChainConfirmationRisksLearnMore": { + "message": "Obtenga más información sobre $1.", + "description": "$1 is a link with text that is provided by the 'addEthereumChainConfirmationRisksLearnMoreLink' key" + }, + "addEthereumChainConfirmationRisksLearnMoreLink": { + "message": "estafas y riesgos de seguridad de la red", + "description": "Link text for the 'addEthereumChainConfirmationRisksLearnMore' translation key" + }, + "addEthereumChainConfirmationTitle": { + "message": "¿Permitir que este sitio agregue una red?" + }, "addFriendsAndAddresses": { "message": "Agregue amigos y direcciones de confianza" }, @@ -2318,6 +2335,9 @@ "userName": { "message": "Nombre de usuario" }, + "viewAllDetails": { + "message": "Ver todos los detalles" + }, "viewContact": { "message": "Ver contacto" }, diff --git a/app/_locales/fr/messages.json b/app/_locales/fr/messages.json index 856638ba2b8a..dbaffd44cf38 100644 --- a/app/_locales/fr/messages.json +++ b/app/_locales/fr/messages.json @@ -207,6 +207,23 @@ "addCustomNetwork": { "message": "Ajouter un réseau personnalisé" }, + "addEthereumChainConfirmationDescription": { + "message": "Cela permettra d’utiliser ce réseau dans MetaMask." + }, + "addEthereumChainConfirmationRisks": { + "message": "MetaMask ne vérifie pas les réseaux personnalisés." + }, + "addEthereumChainConfirmationRisksLearnMore": { + "message": "En savoir plus sur $1.", + "description": "$1 is a link with text that is provided by the 'addEthereumChainConfirmationRisksLearnMoreLink' key" + }, + "addEthereumChainConfirmationRisksLearnMoreLink": { + "message": "les risques de fraude et de sécurité des réseaux", + "description": "Link text for the 'addEthereumChainConfirmationRisksLearnMore' translation key" + }, + "addEthereumChainConfirmationTitle": { + "message": "Autoriser ce site à ajouter un réseau ?" + }, "addEthereumChainWarningModalHeader": { "message": "N’ajoutez ce fournisseur de RPC que si vous lui faites confiance. $1", "description": "$1 is addEthereumChainWarningModalHeaderPartTwo passed separately so that it can be bolded" @@ -805,6 +822,10 @@ "buyNow": { "message": "Achetez maintenant" }, + "buyToken": { + "message": "Acheter des $1", + "description": "$1 is the token symbol" + }, "bytes": { "message": "Octets" }, @@ -1879,6 +1900,13 @@ "functionType": { "message": "Type de fonction" }, + "fundYourWallet": { + "message": "Approvisionnez votre portefeuille" + }, + "fundYourWalletDescription": { + "message": "Commencez par ajouter quelques $1 à votre portefeuille.", + "description": "$1 is the token symbol" + }, "gas": { "message": "Carburant" }, @@ -1962,6 +1990,14 @@ "genericExplorerView": { "message": "Voir le compte sur $1" }, + "getStartedWithNFTs": { + "message": "Obtenez des $1 pour acheter des NFT", + "description": "$1 is the token symbol" + }, + "getStartedWithNFTsDescription": { + "message": "Débutez avec les NFT en ajoutant quelques $1 à votre portefeuille.", + "description": "$1 is the token symbol" + }, "goBack": { "message": "Retour" }, @@ -4822,6 +4858,9 @@ "message": "L’interface utilisateur (IU) spécifiée par le snap n’est pas valide.", "description": "This is shown when the insight snap throws an error. $1 is the snap name" }, + "someNetworksMayPoseSecurity": { + "message": "Certains réseaux peuvent présenter des risques pour la sécurité et/ou la vie privée. Informez-vous sur les risques avant d’ajouter et d’utiliser un réseau." + }, "somethingDoesntLookRight": { "message": "On dirait que quelque chose ne va pas ? $1", "description": "A false positive message for users to contact support. $1 is a link to the support page." @@ -4975,6 +5014,14 @@ "stake": { "message": "Staker" }, + "startYourJourney": { + "message": "Lancez-vous dans les $1", + "description": "$1 is the token symbol" + }, + "startYourJourneyDescription": { + "message": "Lancez-vous dans le Web3 en ajoutant quelques $1 à votre portefeuille.", + "description": "$1 is the token symbol" + }, "stateLogError": { "message": "Erreur lors du chargement des journaux d’état." }, @@ -5960,6 +6007,9 @@ "viewActivity": { "message": "Voir l’activité" }, + "viewAllDetails": { + "message": "Afficher tous les détails" + }, "viewAllQuotes": { "message": "afficher toutes les cotations" }, diff --git a/app/_locales/hi/messages.json b/app/_locales/hi/messages.json index 45e64a972e17..0e624b4ba807 100644 --- a/app/_locales/hi/messages.json +++ b/app/_locales/hi/messages.json @@ -207,6 +207,23 @@ "addCustomNetwork": { "message": "कस्टम नेटवर्क जोड़ें" }, + "addEthereumChainConfirmationDescription": { + "message": "इससे इस नेटवर्क को MetaMask के अंदर इस्तेमाल करने की अनुमति मिलेगी।" + }, + "addEthereumChainConfirmationRisks": { + "message": "MetaMask कस्टम नेटवर्क को वेरीफ़ाई नहीं करता है।" + }, + "addEthereumChainConfirmationRisksLearnMore": { + "message": "$1 के बारे में जानें।", + "description": "$1 is a link with text that is provided by the 'addEthereumChainConfirmationRisksLearnMoreLink' key" + }, + "addEthereumChainConfirmationRisksLearnMoreLink": { + "message": "स्कैम और नेटवर्क से जुड़े सुरक्षा जोखिम", + "description": "Link text for the 'addEthereumChainConfirmationRisksLearnMore' translation key" + }, + "addEthereumChainConfirmationTitle": { + "message": "इस साइट को नेटवर्क जोड़ने की अनुमति दें?" + }, "addEthereumChainWarningModalHeader": { "message": "इस RPC प्रोवाइडर को केवल तभी जोड़ें जब आप निश्चित हैं कि आप इस पर विश्वास कर सकते हैं। $1", "description": "$1 is addEthereumChainWarningModalHeaderPartTwo passed separately so that it can be bolded" @@ -805,6 +822,10 @@ "buyNow": { "message": "अभी खरीदें" }, + "buyToken": { + "message": "$1 खरीदें", + "description": "$1 is the token symbol" + }, "bytes": { "message": "बाइट" }, @@ -1879,6 +1900,13 @@ "functionType": { "message": "फ़ंक्शन का प्रकार" }, + "fundYourWallet": { + "message": "अपने वॉलेट को फंड करें" + }, + "fundYourWalletDescription": { + "message": "अपने वॉलेट में कुछ $1 जोड़कर शुरुआत करें।", + "description": "$1 is the token symbol" + }, "gas": { "message": "गैस" }, @@ -1962,6 +1990,14 @@ "genericExplorerView": { "message": "$1 पर अकाउंट देखें" }, + "getStartedWithNFTs": { + "message": "NFTs खरीदने के लिए $1 प्राप्त करें", + "description": "$1 is the token symbol" + }, + "getStartedWithNFTsDescription": { + "message": "अपने वॉलेट में कुछ $1 जोड़कर NFTs से शुरुआत करें।", + "description": "$1 is the token symbol" + }, "goBack": { "message": "वापस जाएं" }, @@ -4822,6 +4858,9 @@ "message": "अधिक सहायता के लिए $1 के निर्माताओं से कॉन्टेक्ट करें।", "description": "This is shown when the insight snap throws an error. $1 is the snap name" }, + "someNetworksMayPoseSecurity": { + "message": "कुछ नेटवर्क सुरक्षा और/या गोपनीयता संबंधी जोखिम पैदा कर सकते हैं। नेटवर्क जोड़ने और इस्तेमाल करने से पहले जोखिमों को समझें।" + }, "somethingDoesntLookRight": { "message": "कुछ तो गड़बड़ है? $1", "description": "A false positive message for users to contact support. $1 is a link to the support page." @@ -4975,6 +5014,14 @@ "stake": { "message": "हिस्सेदारी" }, + "startYourJourney": { + "message": "$1 से अपनी यात्रा शुरू करें", + "description": "$1 is the token symbol" + }, + "startYourJourneyDescription": { + "message": "अपने वॉलेट में कुछ $1 जोड़कर Web3 से शुरुआत करें।", + "description": "$1 is the token symbol" + }, "stateLogError": { "message": "स्टेट लॉग को पुनर्प्राप्त करने में गड़बड़ी।" }, @@ -5960,6 +6007,9 @@ "viewActivity": { "message": "एक्टिविटी देखें" }, + "viewAllDetails": { + "message": "सभी विवरण देखें" + }, "viewAllQuotes": { "message": "सभी उद्धरण को देखें" }, diff --git a/app/_locales/id/messages.json b/app/_locales/id/messages.json index 6314d9ed3468..68b52556c3f6 100644 --- a/app/_locales/id/messages.json +++ b/app/_locales/id/messages.json @@ -207,6 +207,23 @@ "addCustomNetwork": { "message": "Tambahkan jaringan khusus" }, + "addEthereumChainConfirmationDescription": { + "message": "Tindakan ini akan membantu jaringan ini agar dapat digunakan di MetaMask." + }, + "addEthereumChainConfirmationRisks": { + "message": "MetaMask tidak memverifikasi jaringan kustom." + }, + "addEthereumChainConfirmationRisksLearnMore": { + "message": "Pelajari tentang $1.", + "description": "$1 is a link with text that is provided by the 'addEthereumChainConfirmationRisksLearnMoreLink' key" + }, + "addEthereumChainConfirmationRisksLearnMoreLink": { + "message": "penipuan dan risiko keamanan jaringan", + "description": "Link text for the 'addEthereumChainConfirmationRisksLearnMore' translation key" + }, + "addEthereumChainConfirmationTitle": { + "message": "Izinkan situs ini untuk menambahkan jaringan?" + }, "addEthereumChainWarningModalHeader": { "message": "Cukup tambahkan penyedia RPC ini jika Anda yakin dapat memercayainya. $1", "description": "$1 is addEthereumChainWarningModalHeaderPartTwo passed separately so that it can be bolded" @@ -805,6 +822,10 @@ "buyNow": { "message": "Beli Sekarang" }, + "buyToken": { + "message": "Beli $1", + "description": "$1 is the token symbol" + }, "bytes": { "message": "Byte" }, @@ -1879,6 +1900,13 @@ "functionType": { "message": "Jenis fungsi" }, + "fundYourWallet": { + "message": "Danai dompet Anda" + }, + "fundYourWalletDescription": { + "message": "Mulailah dengan menambahkan sejumlah $1 ke dompet Anda.", + "description": "$1 is the token symbol" + }, "gas": { "message": "Gas" }, @@ -1962,6 +1990,14 @@ "genericExplorerView": { "message": "Lihat akun di $1" }, + "getStartedWithNFTs": { + "message": "Dapatkan $1 untuk membeli NFT", + "description": "$1 is the token symbol" + }, + "getStartedWithNFTsDescription": { + "message": "Mulailah menggunakan NFT dengan menambahkan sejumlah $1 ke dompet Anda.", + "description": "$1 is the token symbol" + }, "goBack": { "message": "Kembali" }, @@ -4822,6 +4858,9 @@ "message": "Hubungi pembuat $1 untuk dukungan lebih lanjut.", "description": "This is shown when the insight snap throws an error. $1 is the snap name" }, + "someNetworksMayPoseSecurity": { + "message": "Beberapa jaringan dapat menimbulkan risiko keamanan dan/atau privasi. Pahami risikonya sebelum menambahkan dan menggunakan jaringan." + }, "somethingDoesntLookRight": { "message": "Ada yang tidak beres? $1", "description": "A false positive message for users to contact support. $1 is a link to the support page." @@ -4975,6 +5014,14 @@ "stake": { "message": "Stake" }, + "startYourJourney": { + "message": "Mulailah perjalanan Anda dengan $1", + "description": "$1 is the token symbol" + }, + "startYourJourneyDescription": { + "message": "Mulailah dengan web3 dengan menambahkan sejumlah $1 ke dompet Anda.", + "description": "$1 is the token symbol" + }, "stateLogError": { "message": "Terjadi kesalahan pada log status pengambilan." }, @@ -5960,6 +6007,9 @@ "viewActivity": { "message": "Lihat aktivitas" }, + "viewAllDetails": { + "message": "Lihat semua detail" + }, "viewAllQuotes": { "message": "lihat semua kuotasi" }, diff --git a/app/_locales/it/messages.json b/app/_locales/it/messages.json index 0c79dd8fc6fc..4dac80c253b3 100644 --- a/app/_locales/it/messages.json +++ b/app/_locales/it/messages.json @@ -152,6 +152,23 @@ "addContact": { "message": "Aggiungi contatto" }, + "addEthereumChainConfirmationDescription": { + "message": "Ciò consentirà a questa rete di essere utilizzata all'interno di MetaMask." + }, + "addEthereumChainConfirmationRisks": { + "message": "MetaMask non verifica le reti personalizzate." + }, + "addEthereumChainConfirmationRisksLearnMore": { + "message": "Maggiori informazioni su $1.", + "description": "$1 is a link with text that is provided by the 'addEthereumChainConfirmationRisksLearnMoreLink' key" + }, + "addEthereumChainConfirmationRisksLearnMoreLink": { + "message": "truffe e rischi per la sicurezza della rete", + "description": "Link text for the 'addEthereumChainConfirmationRisksLearnMore' translation key" + }, + "addEthereumChainConfirmationTitle": { + "message": "Consenti a questo sito di aggiungere una rete?" + }, "addFriendsAndAddresses": { "message": "Aggiungi amici e indirizzi di cui ti fidi" }, @@ -1632,6 +1649,9 @@ "userName": { "message": "Nome utente" }, + "viewAllDetails": { + "message": "Vedi tutti i dettagli" + }, "viewContact": { "message": "Visualizza contatto" }, diff --git a/app/_locales/ja/messages.json b/app/_locales/ja/messages.json index 61730b2bc325..73f3f8300646 100644 --- a/app/_locales/ja/messages.json +++ b/app/_locales/ja/messages.json @@ -207,6 +207,23 @@ "addCustomNetwork": { "message": "カスタムネットワークを追加" }, + "addEthereumChainConfirmationDescription": { + "message": "これにより、このネットワークはMetaMask内で使用できるようになります。" + }, + "addEthereumChainConfirmationRisks": { + "message": "MetaMaskはカスタムネットワークを検証しません。" + }, + "addEthereumChainConfirmationRisksLearnMore": { + "message": "$1の詳細。", + "description": "$1 is a link with text that is provided by the 'addEthereumChainConfirmationRisksLearnMoreLink' key" + }, + "addEthereumChainConfirmationRisksLearnMoreLink": { + "message": "詐欺やネットワークセキュリティのリスク", + "description": "Link text for the 'addEthereumChainConfirmationRisksLearnMore' translation key" + }, + "addEthereumChainConfirmationTitle": { + "message": "このサイトにネットワークの追加を許可しますか?" + }, "addEthereumChainWarningModalHeader": { "message": "このRPCプロバイダーは、確実に信頼できる場合のみ追加してください。$1", "description": "$1 is addEthereumChainWarningModalHeaderPartTwo passed separately so that it can be bolded" @@ -805,6 +822,10 @@ "buyNow": { "message": "今すぐ購入" }, + "buyToken": { + "message": "$1を購入", + "description": "$1 is the token symbol" + }, "bytes": { "message": "バイト" }, @@ -1879,6 +1900,13 @@ "functionType": { "message": "機能の種類" }, + "fundYourWallet": { + "message": "ウォレットへの入金" + }, + "fundYourWalletDescription": { + "message": "ウォレットに$1を追加して開始します。", + "description": "$1 is the token symbol" + }, "gas": { "message": "ガス" }, @@ -1962,6 +1990,14 @@ "genericExplorerView": { "message": "$1でアカウントを表示" }, + "getStartedWithNFTs": { + "message": "$1を入手してNFTを購入", + "description": "$1 is the token symbol" + }, + "getStartedWithNFTsDescription": { + "message": "ウォレットに$1を追加してNFTの利用を開始します。", + "description": "$1 is the token symbol" + }, "goBack": { "message": "戻る" }, @@ -4822,6 +4858,9 @@ "message": "今後のサポートは、$1の作成者にお問い合わせください。", "description": "This is shown when the insight snap throws an error. $1 is the snap name" }, + "someNetworksMayPoseSecurity": { + "message": "ネットワークによっては、セキュリティやプライバシーの面でリスクが伴う可能性があります。ネットワークを追加・使用する前にリスクを理解するようにしてください。" + }, "somethingDoesntLookRight": { "message": "何か不審な点があれば、$1", "description": "A false positive message for users to contact support. $1 is a link to the support page." @@ -4975,6 +5014,14 @@ "stake": { "message": "ステーク" }, + "startYourJourney": { + "message": "$1で利用開始", + "description": "$1 is the token symbol" + }, + "startYourJourneyDescription": { + "message": "ウォレットに$1を追加してWeb3の利用を開始します。", + "description": "$1 is the token symbol" + }, "stateLogError": { "message": "ステートログの取得中にエラーが発生しました。" }, @@ -5960,6 +6007,9 @@ "viewActivity": { "message": "アクティビティを表示" }, + "viewAllDetails": { + "message": "すべての詳細の表示" + }, "viewAllQuotes": { "message": "すべてのクォートを表示" }, diff --git a/app/_locales/ko/messages.json b/app/_locales/ko/messages.json index 05c04fbd17a9..be1de55c51c7 100644 --- a/app/_locales/ko/messages.json +++ b/app/_locales/ko/messages.json @@ -207,6 +207,23 @@ "addCustomNetwork": { "message": "맞춤 네트워크 추가" }, + "addEthereumChainConfirmationDescription": { + "message": "이렇게 하면 MetaMask 내에서 이 네트워크를 사용할 수 있습니다." + }, + "addEthereumChainConfirmationRisks": { + "message": "MetaMask는 맞춤 네트워크를 검증하지 않습니다." + }, + "addEthereumChainConfirmationRisksLearnMore": { + "message": "$1에 대해 알아보기", + "description": "$1 is a link with text that is provided by the 'addEthereumChainConfirmationRisksLearnMoreLink' key" + }, + "addEthereumChainConfirmationRisksLearnMoreLink": { + "message": "사기 및 네트워크 보안 위험", + "description": "Link text for the 'addEthereumChainConfirmationRisksLearnMore' translation key" + }, + "addEthereumChainConfirmationTitle": { + "message": "이 사이트에서 네트워크를 추가하도록 허용하시겠습니까?" + }, "addEthereumChainWarningModalHeader": { "message": "신뢰할 수 있는 경우에만 이 RPC 공급입체를 추가하세요. $1", "description": "$1 is addEthereumChainWarningModalHeaderPartTwo passed separately so that it can be bolded" @@ -805,6 +822,10 @@ "buyNow": { "message": "지금 구매" }, + "buyToken": { + "message": "$1 구매", + "description": "$1 is the token symbol" + }, "bytes": { "message": "바이트" }, @@ -1879,6 +1900,13 @@ "functionType": { "message": "기능 유형" }, + "fundYourWallet": { + "message": "지갑에 자금 추가" + }, + "fundYourWalletDescription": { + "message": "지갑에 $1의 자금을 추가하여 시작하세요.", + "description": "$1 is the token symbol" + }, "gas": { "message": "가스" }, @@ -1962,6 +1990,14 @@ "genericExplorerView": { "message": "$1에서 계정 보기" }, + "getStartedWithNFTs": { + "message": "$1 받고 NFT 구매하기", + "description": "$1 is the token symbol" + }, + "getStartedWithNFTsDescription": { + "message": "지갑에 $1의 자금을 추가하여 시작하세요.", + "description": "$1 is the token symbol" + }, "goBack": { "message": "뒤로 가기" }, @@ -4822,6 +4858,9 @@ "message": "$1 작성자에게 연락하여 향후 지원을 요청하세요.", "description": "This is shown when the insight snap throws an error. $1 is the snap name" }, + "someNetworksMayPoseSecurity": { + "message": "네트워크에 따라 보안이나 개인 정보 유출의 위험이 있을 수 있습니다. 네트워크 추가 및 사용 이전에 위험 요소를 파악하세요." + }, "somethingDoesntLookRight": { "message": "무언가 잘못되었나요? $1", "description": "A false positive message for users to contact support. $1 is a link to the support page." @@ -4975,6 +5014,14 @@ "stake": { "message": "스테이크" }, + "startYourJourney": { + "message": "$1 토큰으로 여정을 시작하세요", + "description": "$1 is the token symbol" + }, + "startYourJourneyDescription": { + "message": "지갑에 $1 토큰을 추가하여 웹3를 시작하세요.", + "description": "$1 is the token symbol" + }, "stateLogError": { "message": "상태 로그를 가져오는 도중 오류가 발생했습니다." }, @@ -5960,6 +6007,9 @@ "viewActivity": { "message": "활동 보기" }, + "viewAllDetails": { + "message": "모든 세부 정보 보기" + }, "viewAllQuotes": { "message": "모든 견적 보기" }, diff --git a/app/_locales/ph/messages.json b/app/_locales/ph/messages.json index e6b4bc3e7811..1687cb7818f0 100644 --- a/app/_locales/ph/messages.json +++ b/app/_locales/ph/messages.json @@ -42,6 +42,23 @@ "addContact": { "message": "Magdagdag ng contact" }, + "addEthereumChainConfirmationDescription": { + "message": "Bibigyang-daan nito na magamit ang network na ito sa MetaMask." + }, + "addEthereumChainConfirmationRisks": { + "message": "Hindi vine-verify ng MetaMask ang mga custom na network." + }, + "addEthereumChainConfirmationRisksLearnMore": { + "message": "Matuto tungkol sa $1.", + "description": "$1 is a link with text that is provided by the 'addEthereumChainConfirmationRisksLearnMoreLink' key" + }, + "addEthereumChainConfirmationRisksLearnMoreLink": { + "message": "mga scam at panganib sa seguridad ng network", + "description": "Link text for the 'addEthereumChainConfirmationRisksLearnMore' translation key" + }, + "addEthereumChainConfirmationTitle": { + "message": "Payagan ang site na ito na magdagdag ng network?" + }, "addFriendsAndAddresses": { "message": "Magdagdag ng mga kaibigan at address na pinagkakatiwalaan mo" }, @@ -1577,6 +1594,9 @@ "userName": { "message": "Username" }, + "viewAllDetails": { + "message": "Tingnan ang lahat ng detalye" + }, "viewContact": { "message": "Tingnan ang Contact" }, diff --git a/app/_locales/pt/messages.json b/app/_locales/pt/messages.json index 4c02a9dc223e..656733cecf65 100644 --- a/app/_locales/pt/messages.json +++ b/app/_locales/pt/messages.json @@ -207,6 +207,23 @@ "addCustomNetwork": { "message": "Adicionar rede personalizada" }, + "addEthereumChainConfirmationDescription": { + "message": "Isso permitirá que essa rede seja usada dentro da MetaMask." + }, + "addEthereumChainConfirmationRisks": { + "message": "A MetaMask não verifica redes personalizadas." + }, + "addEthereumChainConfirmationRisksLearnMore": { + "message": "Saiba mais sobre $1.", + "description": "$1 is a link with text that is provided by the 'addEthereumChainConfirmationRisksLearnMoreLink' key" + }, + "addEthereumChainConfirmationRisksLearnMoreLink": { + "message": "golpes e riscos de segurança nas redes", + "description": "Link text for the 'addEthereumChainConfirmationRisksLearnMore' translation key" + }, + "addEthereumChainConfirmationTitle": { + "message": "Permitir que este site adicione uma rede?" + }, "addEthereumChainWarningModalHeader": { "message": "Adicione esse provedor de RPC apenas se tiver certeza de que é confiável. $1", "description": "$1 is addEthereumChainWarningModalHeaderPartTwo passed separately so that it can be bolded" @@ -805,6 +822,10 @@ "buyNow": { "message": "Comprar agora" }, + "buyToken": { + "message": "Comprar $1", + "description": "$1 is the token symbol" + }, "bytes": { "message": "Bytes" }, @@ -1879,6 +1900,13 @@ "functionType": { "message": "Tipo de função" }, + "fundYourWallet": { + "message": "Adicione valores à sua carteira" + }, + "fundYourWalletDescription": { + "message": "Comece adicionando $1 à sua carteira.", + "description": "$1 is the token symbol" + }, "gas": { "message": "Gás" }, @@ -1962,6 +1990,14 @@ "genericExplorerView": { "message": "Ver conta na $1" }, + "getStartedWithNFTs": { + "message": "Adquira $1 para comprar NFTs", + "description": "$1 is the token symbol" + }, + "getStartedWithNFTsDescription": { + "message": "Comece sua jornada com NFTs adicionando $1 à sua carteira.", + "description": "$1 is the token symbol" + }, "goBack": { "message": "Voltar" }, @@ -4822,6 +4858,9 @@ "message": "Contate os criadores de $1 para receber mais suporte.", "description": "This is shown when the insight snap throws an error. $1 is the snap name" }, + "someNetworksMayPoseSecurity": { + "message": "Algumas redes podem representar riscos de segurança e/ou privacidade. Tenha os riscos em mente antes de adicionar e usar uma rede." + }, "somethingDoesntLookRight": { "message": "Alguma coisa não parece certa? $1", "description": "A false positive message for users to contact support. $1 is a link to the support page." @@ -4975,6 +5014,14 @@ "stake": { "message": "Stake" }, + "startYourJourney": { + "message": "Comece sua jornada com $1", + "description": "$1 is the token symbol" + }, + "startYourJourneyDescription": { + "message": "Comece sua jornada na web3 adicionando $1 à sua conta.", + "description": "$1 is the token symbol" + }, "stateLogError": { "message": "Erro ao recuperar os logs de estado." }, @@ -5960,6 +6007,9 @@ "viewActivity": { "message": "Ver atividade" }, + "viewAllDetails": { + "message": "Ver todos os detalhes" + }, "viewAllQuotes": { "message": "ver todas as cotações" }, diff --git a/app/_locales/pt_BR/messages.json b/app/_locales/pt_BR/messages.json index 58d7f6ea8718..6062013d6d6f 100644 --- a/app/_locales/pt_BR/messages.json +++ b/app/_locales/pt_BR/messages.json @@ -100,6 +100,23 @@ "addContact": { "message": "Adicionar contato" }, + "addEthereumChainConfirmationDescription": { + "message": "Isso permitirá que essa rede seja usada dentro da MetaMask." + }, + "addEthereumChainConfirmationRisks": { + "message": "A MetaMask não verifica redes personalizadas." + }, + "addEthereumChainConfirmationRisksLearnMore": { + "message": "Saiba mais sobre $1.", + "description": "$1 is a link with text that is provided by the 'addEthereumChainConfirmationRisksLearnMoreLink' key" + }, + "addEthereumChainConfirmationRisksLearnMoreLink": { + "message": "fraudes e riscos de segurança da rede", + "description": "Link text for the 'addEthereumChainConfirmationRisksLearnMore' translation key" + }, + "addEthereumChainConfirmationTitle": { + "message": "Permitir que esse site adicione uma rede?" + }, "addFriendsAndAddresses": { "message": "Adicionar amigos e endereços confiáveis" }, @@ -2318,6 +2335,9 @@ "userName": { "message": "Nome de usuário" }, + "viewAllDetails": { + "message": "Ver todos os detalhes" + }, "viewContact": { "message": "Ver contato" }, diff --git a/app/_locales/ru/messages.json b/app/_locales/ru/messages.json index f1e5d27589c5..0c2f92821ed2 100644 --- a/app/_locales/ru/messages.json +++ b/app/_locales/ru/messages.json @@ -207,6 +207,23 @@ "addCustomNetwork": { "message": "Добавить пользовательскую сеть" }, + "addEthereumChainConfirmationDescription": { + "message": "Это позволит использовать эту сеть в MetaMask." + }, + "addEthereumChainConfirmationRisks": { + "message": "MetaMask не проверяет пользовательские сети." + }, + "addEthereumChainConfirmationRisksLearnMore": { + "message": "Подробнее о $1.", + "description": "$1 is a link with text that is provided by the 'addEthereumChainConfirmationRisksLearnMoreLink' key" + }, + "addEthereumChainConfirmationRisksLearnMoreLink": { + "message": "мошенничестве и угрозах безопасности в сети", + "description": "Link text for the 'addEthereumChainConfirmationRisksLearnMore' translation key" + }, + "addEthereumChainConfirmationTitle": { + "message": "Разрешить этому сайту добавить сеть?" + }, "addEthereumChainWarningModalHeader": { "message": "Добавляйте этого поставщика RPC только в том случае, если уверены, что ему можно доверять. $1", "description": "$1 is addEthereumChainWarningModalHeaderPartTwo passed separately so that it can be bolded" @@ -805,6 +822,10 @@ "buyNow": { "message": "Купить сейчас" }, + "buyToken": { + "message": "Купить $1", + "description": "$1 is the token symbol" + }, "bytes": { "message": "Байты" }, @@ -1879,6 +1900,13 @@ "functionType": { "message": "Тип функции" }, + "fundYourWallet": { + "message": "Пополните свой кошелек" + }, + "fundYourWalletDescription": { + "message": "Начните с добавления $1 в свой кошелек.", + "description": "$1 is the token symbol" + }, "gas": { "message": "Газ" }, @@ -1962,6 +1990,14 @@ "genericExplorerView": { "message": "Посмотреть счет на $1" }, + "getStartedWithNFTs": { + "message": "Получите $1 для покупки NFT", + "description": "$1 is the token symbol" + }, + "getStartedWithNFTsDescription": { + "message": "Начните использовать NFT, добавив $1 в свой кошелек.", + "description": "$1 is the token symbol" + }, "goBack": { "message": "Назад" }, @@ -4822,6 +4858,9 @@ "message": "Свяжитесь с авторами $1 для получения дополнительной поддержки.", "description": "This is shown when the insight snap throws an error. $1 is the snap name" }, + "someNetworksMayPoseSecurity": { + "message": "Некоторые сети могут представлять угрозу безопасности и/или конфиденциальности. Прежде чем добавлять и использовать сеть, ознакомьтесь с рисками." + }, "somethingDoesntLookRight": { "message": "Что-то не так? $1", "description": "A false positive message for users to contact support. $1 is a link to the support page." @@ -4975,6 +5014,14 @@ "stake": { "message": "Выполнить стейкинг" }, + "startYourJourney": { + "message": "Начните свое путешествие с $1", + "description": "$1 is the token symbol" + }, + "startYourJourneyDescription": { + "message": "Начните использовать Web3, добавив $1 в свой кошелек.", + "description": "$1 is the token symbol" + }, "stateLogError": { "message": "Ошибка при получении журналов состояния." }, @@ -5960,6 +6007,9 @@ "viewActivity": { "message": "Смотреть активность" }, + "viewAllDetails": { + "message": "Смотреть все сведения" + }, "viewAllQuotes": { "message": "смотреть все котировки" }, diff --git a/app/_locales/tl/messages.json b/app/_locales/tl/messages.json index 76e91829fc2c..61d8ff6e5d8c 100644 --- a/app/_locales/tl/messages.json +++ b/app/_locales/tl/messages.json @@ -207,6 +207,23 @@ "addCustomNetwork": { "message": "Magdagdag ng custom na network" }, + "addEthereumChainConfirmationDescription": { + "message": "Magpapahintulot ito sa network na ito na gamitin sa loob ng MetaMask." + }, + "addEthereumChainConfirmationRisks": { + "message": "Ang MetaMask ay hindi nagve-verify ng mga custom na network." + }, + "addEthereumChainConfirmationRisksLearnMore": { + "message": "Alamin ang tungkol sa $1.", + "description": "$1 is a link with text that is provided by the 'addEthereumChainConfirmationRisksLearnMoreLink' key" + }, + "addEthereumChainConfirmationRisksLearnMoreLink": { + "message": "mga panloloko at panganib ng seguridad ng network", + "description": "Link text for the 'addEthereumChainConfirmationRisksLearnMore' translation key" + }, + "addEthereumChainConfirmationTitle": { + "message": "Payagan ang site na ito na magdagdag ng network?" + }, "addEthereumChainWarningModalHeader": { "message": "Idagdag lamang ang RPC provider na ito kung sigurado kang mapagkakatiwalaan ito. $1", "description": "$1 is addEthereumChainWarningModalHeaderPartTwo passed separately so that it can be bolded" @@ -805,6 +822,10 @@ "buyNow": { "message": "Bilhin Ngayon" }, + "buyToken": { + "message": "Bumili ng $1", + "description": "$1 is the token symbol" + }, "bytes": { "message": "Bytes" }, @@ -1879,6 +1900,13 @@ "functionType": { "message": "Uri ng Function" }, + "fundYourWallet": { + "message": "Pondohan ang iyong wallet" + }, + "fundYourWalletDescription": { + "message": "Magsimula sa pamamagitan ng pagdagdag ng $1 sa iyong wallet.", + "description": "$1 is the token symbol" + }, "gas": { "message": "Gas" }, @@ -1962,6 +1990,14 @@ "genericExplorerView": { "message": "Tingnan ang account sa $1" }, + "getStartedWithNFTs": { + "message": "Kumuha ng $1 para bumili ng mga NFT", + "description": "$1 is the token symbol" + }, + "getStartedWithNFTsDescription": { + "message": "Magsimula sa mga NFT sa pamamagitan ng pagdagdag ng $1 sa iyong wallet.", + "description": "$1 is the token symbol" + }, "goBack": { "message": "Bumalik" }, @@ -4822,6 +4858,9 @@ "message": "Makipag-ugnayan sa mga tagalikha ng $1 para sa karagdagang suporta.", "description": "This is shown when the insight snap throws an error. $1 is the snap name" }, + "someNetworksMayPoseSecurity": { + "message": "Maaaring magdulot ang ilang network ng mga panganib sa seguridad at/o pagkapribado. Unawain ang mga panganib bago idagdag o gamitin ang isang network." + }, "somethingDoesntLookRight": { "message": "Mayroon bang hindi tama? $1", "description": "A false positive message for users to contact support. $1 is a link to the support page." @@ -4975,6 +5014,14 @@ "stake": { "message": "Mag-stake" }, + "startYourJourney": { + "message": "Simulan ang iyong paglalakbay sa $1", + "description": "$1 is the token symbol" + }, + "startYourJourneyDescription": { + "message": "Magsimula sa web3 sa pamamagitan ng pagdagdag ng $1 sa iyong wallet.", + "description": "$1 is the token symbol" + }, "stateLogError": { "message": "Error sa pagkuha ng mga log ng estado." }, @@ -5960,6 +6007,9 @@ "viewActivity": { "message": "Tingnan ang aktibidad" }, + "viewAllDetails": { + "message": "Tingnan ang lahat ng detalye" + }, "viewAllQuotes": { "message": "tingnan ang lahat ng quote" }, diff --git a/app/_locales/tr/messages.json b/app/_locales/tr/messages.json index 3b1899614d70..d80d6564b880 100644 --- a/app/_locales/tr/messages.json +++ b/app/_locales/tr/messages.json @@ -207,6 +207,23 @@ "addCustomNetwork": { "message": "Özel ağ ekle" }, + "addEthereumChainConfirmationDescription": { + "message": "Bu, bu ağın MetaMas dahilinde kullanılmasına olanak tanıyacaktır." + }, + "addEthereumChainConfirmationRisks": { + "message": "MetaMask özel ağları doğrulamaz." + }, + "addEthereumChainConfirmationRisksLearnMore": { + "message": "$1 hakkında bilgi edinin.", + "description": "$1 is a link with text that is provided by the 'addEthereumChainConfirmationRisksLearnMoreLink' key" + }, + "addEthereumChainConfirmationRisksLearnMoreLink": { + "message": "dolandırıcılık ve ağ güvenliği riskleri", + "description": "Link text for the 'addEthereumChainConfirmationRisksLearnMore' translation key" + }, + "addEthereumChainConfirmationTitle": { + "message": "Bu sitenin ağ eklemesine izin ver?" + }, "addEthereumChainWarningModalHeader": { "message": "Bu RPC sağlayıcısını sadece ona güvenebileceğinizden eminseniz ekleyin. $1", "description": "$1 is addEthereumChainWarningModalHeaderPartTwo passed separately so that it can be bolded" @@ -805,6 +822,10 @@ "buyNow": { "message": "Şimdi Satın Al" }, + "buyToken": { + "message": "$1 Al", + "description": "$1 is the token symbol" + }, "bytes": { "message": "Bayt" }, @@ -1879,6 +1900,13 @@ "functionType": { "message": "İşlev türü" }, + "fundYourWallet": { + "message": "Cüzdanınıza para ekleyin" + }, + "fundYourWalletDescription": { + "message": "Cüzdanınıza biraz $1 ekleyerek başlayın.", + "description": "$1 is the token symbol" + }, "gas": { "message": "Gaz" }, @@ -1962,6 +1990,14 @@ "genericExplorerView": { "message": "Hesabı $1 üzerinde görüntüleyin" }, + "getStartedWithNFTs": { + "message": "NFT satın almak için $1 edinin", + "description": "$1 is the token symbol" + }, + "getStartedWithNFTsDescription": { + "message": "Cüzdanınıza biraz $1 ekleyerek NFT'lere başlayın.", + "description": "$1 is the token symbol" + }, "goBack": { "message": "Geri git" }, @@ -4822,6 +4858,9 @@ "message": "Daha fazla destek için $1 oluşturucuları ile iletişime geçin.", "description": "This is shown when the insight snap throws an error. $1 is the snap name" }, + "someNetworksMayPoseSecurity": { + "message": "Bazı ağlar güvenlik ve/veya gizlilik riskleri teşkil edebilir. Bir ağ eklemeden ve kullanmadan önce riskleri anlayın." + }, "somethingDoesntLookRight": { "message": "Doğru görünmeyen bir şeyler mi var? $1", "description": "A false positive message for users to contact support. $1 is a link to the support page." @@ -4975,6 +5014,14 @@ "stake": { "message": "Pay" }, + "startYourJourney": { + "message": "$1 ile yolculuğunuza başlayın", + "description": "$1 is the token symbol" + }, + "startYourJourneyDescription": { + "message": "Cüzdanınıza biraz $1 ekleyerek web3'e başlayın.", + "description": "$1 is the token symbol" + }, "stateLogError": { "message": "Durum günlükleri alınırken hata." }, @@ -5960,6 +6007,9 @@ "viewActivity": { "message": "Aktiviteyi görüntüle" }, + "viewAllDetails": { + "message": "Tüm bilgileri görüntüle" + }, "viewAllQuotes": { "message": "tüm teklifleri görüntüle" }, diff --git a/app/_locales/vi/messages.json b/app/_locales/vi/messages.json index 4bfcba6dac1f..0bac5423d1ee 100644 --- a/app/_locales/vi/messages.json +++ b/app/_locales/vi/messages.json @@ -207,6 +207,23 @@ "addCustomNetwork": { "message": "Thêm mạng tùy chỉnh" }, + "addEthereumChainConfirmationDescription": { + "message": "Thao tác này sẽ cho phép sử dụng mạng này trong MetaMask." + }, + "addEthereumChainConfirmationRisks": { + "message": "MetaMask không xác minh mạng tùy chỉnh." + }, + "addEthereumChainConfirmationRisksLearnMore": { + "message": "Tìm hiểu về $1.", + "description": "$1 is a link with text that is provided by the 'addEthereumChainConfirmationRisksLearnMoreLink' key" + }, + "addEthereumChainConfirmationRisksLearnMoreLink": { + "message": "lừa đảo và các nguy cơ về an ninh mạng", + "description": "Link text for the 'addEthereumChainConfirmationRisksLearnMore' translation key" + }, + "addEthereumChainConfirmationTitle": { + "message": "Cho phép trang này thêm một mạng?" + }, "addEthereumChainWarningModalHeader": { "message": "Chỉ thêm nhà cung cấp RPC này nếu bạn chắc chắn bạn có thể tin tưởng. $1", "description": "$1 is addEthereumChainWarningModalHeaderPartTwo passed separately so that it can be bolded" @@ -805,6 +822,10 @@ "buyNow": { "message": "Mua ngay" }, + "buyToken": { + "message": "Mua $1", + "description": "$1 is the token symbol" + }, "bytes": { "message": "Byte" }, @@ -1879,6 +1900,13 @@ "functionType": { "message": "Loại chức năng" }, + "fundYourWallet": { + "message": "Nạp tiền vào ví của bạn" + }, + "fundYourWalletDescription": { + "message": "Hãy bắt đầu bằng cách nạp một ít $1 vào ví của bạn.", + "description": "$1 is the token symbol" + }, "gas": { "message": "Gas" }, @@ -1962,6 +1990,14 @@ "genericExplorerView": { "message": "Xem tài khoản trên $1" }, + "getStartedWithNFTs": { + "message": "Nhận $1 để mua NFT", + "description": "$1 is the token symbol" + }, + "getStartedWithNFTsDescription": { + "message": "Hãy bắt đầu với NFT bằng cách nạp một ít $1 vào ví của bạn.", + "description": "$1 is the token symbol" + }, "goBack": { "message": "Quay Lại" }, @@ -4822,6 +4858,9 @@ "message": "Liên hệ với những người tạo ra $1 để được hỗ trợ thêm.", "description": "This is shown when the insight snap throws an error. $1 is the snap name" }, + "someNetworksMayPoseSecurity": { + "message": "Một số mạng có thể gây ra rủi ro về bảo mật và/hoặc quyền riêng tư. Bạn cần hiểu rõ các rủi ro này trước khi thêm và sử dụng mạng." + }, "somethingDoesntLookRight": { "message": "Có gì đó không ổn? $1", "description": "A false positive message for users to contact support. $1 is a link to the support page." @@ -4975,6 +5014,14 @@ "stake": { "message": "Stake" }, + "startYourJourney": { + "message": "Bắt đầu hành trình của bạn với $1", + "description": "$1 is the token symbol" + }, + "startYourJourneyDescription": { + "message": "Hãy bắt đầu với Web3 bằng cách nạp một ít $1 vào ví của bạn.", + "description": "$1 is the token symbol" + }, "stateLogError": { "message": "Lỗi khi truy xuất nhật ký trạng thái." }, @@ -5960,6 +6007,9 @@ "viewActivity": { "message": "Xem hoạt động" }, + "viewAllDetails": { + "message": "Xem toàn bộ chi tiết" + }, "viewAllQuotes": { "message": "xem tất cả báo giá" }, diff --git a/app/_locales/zh_CN/messages.json b/app/_locales/zh_CN/messages.json index 80a31d532482..7209fb1c5b44 100644 --- a/app/_locales/zh_CN/messages.json +++ b/app/_locales/zh_CN/messages.json @@ -207,6 +207,23 @@ "addCustomNetwork": { "message": "添加自定义网络" }, + "addEthereumChainConfirmationDescription": { + "message": "这将允许在 MetaMask 中使用此网络。" + }, + "addEthereumChainConfirmationRisks": { + "message": "MetaMask 不验证自定义网络。" + }, + "addEthereumChainConfirmationRisksLearnMore": { + "message": "了解 $1。", + "description": "$1 is a link with text that is provided by the 'addEthereumChainConfirmationRisksLearnMoreLink' key" + }, + "addEthereumChainConfirmationRisksLearnMoreLink": { + "message": "欺诈和网络安全风险", + "description": "Link text for the 'addEthereumChainConfirmationRisksLearnMore' translation key" + }, + "addEthereumChainConfirmationTitle": { + "message": "允许此网站添加一个网络到MetaMask上?" + }, "addEthereumChainWarningModalHeader": { "message": "仅当您确定可以信任此 RPC 提供商时才能添加它。$1", "description": "$1 is addEthereumChainWarningModalHeaderPartTwo passed separately so that it can be bolded" @@ -805,6 +822,10 @@ "buyNow": { "message": "立即购买" }, + "buyToken": { + "message": "购买 $1", + "description": "$1 is the token symbol" + }, "bytes": { "message": "字节" }, @@ -1879,6 +1900,13 @@ "functionType": { "message": "功能类型" }, + "fundYourWallet": { + "message": "向您的钱包存入资金" + }, + "fundYourWalletDescription": { + "message": "将一些 $1 添加到您的钱包并开始使用", + "description": "$1 is the token symbol" + }, "gas": { "message": "燃料" }, @@ -1962,6 +1990,14 @@ "genericExplorerView": { "message": "在$1查看账户" }, + "getStartedWithNFTs": { + "message": "获取 $1 以购买 NFT", + "description": "$1 is the token symbol" + }, + "getStartedWithNFTsDescription": { + "message": "将一些 $1 添加到您的钱包并开始使用 NFT", + "description": "$1 is the token symbol" + }, "goBack": { "message": "返回" }, @@ -4822,6 +4858,9 @@ "message": "联系 $1 的创建者以获得进一步支持。", "description": "This is shown when the insight snap throws an error. $1 is the snap name" }, + "someNetworksMayPoseSecurity": { + "message": "某些网络可能会带来安全和/或隐私风险。在添加和使用网络之前,请先了解风险。" + }, "somethingDoesntLookRight": { "message": "有什么不对劲吗?$1", "description": "A false positive message for users to contact support. $1 is a link to the support page." @@ -4975,6 +5014,14 @@ "stake": { "message": "质押" }, + "startYourJourney": { + "message": "从 $1 开始您的旅程", + "description": "$1 is the token symbol" + }, + "startYourJourneyDescription": { + "message": "将一些 $1 添加到您的钱包并开始使用 Web3", + "description": "$1 is the token symbol" + }, "stateLogError": { "message": "检索状态日志时出错。" }, @@ -5960,6 +6007,9 @@ "viewActivity": { "message": "查看活动" }, + "viewAllDetails": { + "message": "查看所有详情" + }, "viewAllQuotes": { "message": "查看所有报价" }, diff --git a/app/_locales/zh_TW/messages.json b/app/_locales/zh_TW/messages.json index 7a7fdb68cf1f..7cdfa8e28add 100644 --- a/app/_locales/zh_TW/messages.json +++ b/app/_locales/zh_TW/messages.json @@ -42,6 +42,23 @@ "addContact": { "message": "新增合約" }, + "addEthereumChainConfirmationDescription": { + "message": "這會允許在 MetaMask 內使用這個網路。" + }, + "addEthereumChainConfirmationRisks": { + "message": "MetaMask 不會對自訂的網路做驗證。" + }, + "addEthereumChainConfirmationRisksLearnMore": { + "message": "了解更多關於$1的事。", + "description": "$1 is a link with text that is provided by the 'addEthereumChainConfirmationRisksLearnMoreLink' key" + }, + "addEthereumChainConfirmationRisksLearnMoreLink": { + "message": "詐騙與網路安全風險", + "description": "Link text for the 'addEthereumChainConfirmationRisksLearnMore' translation key" + }, + "addEthereumChainConfirmationTitle": { + "message": "允許這個網站新增一個網路?" + }, "addFriendsAndAddresses": { "message": "新增朋友和您信任的位址" }, @@ -1353,6 +1370,9 @@ "userName": { "message": "使用者名稱" }, + "viewAllDetails": { + "message": "查看所有詳情" + }, "viewContact": { "message": "觀看聯絡資訊" }, diff --git a/app/images/ape-token.svg b/app/images/ape-token.svg deleted file mode 100644 index 8c1777f1a497..000000000000 --- a/app/images/ape-token.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/images/icons/collapse.svg b/app/images/icons/collapse.svg deleted file mode 100644 index 5ec98b457f78..000000000000 --- a/app/images/icons/collapse.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/app/images/ramps-card-nft-illustration.png b/app/images/ramps-card-nft-illustration.png new file mode 100644 index 000000000000..1cbc824592f8 Binary files /dev/null and b/app/images/ramps-card-nft-illustration.png differ diff --git a/app/scripts/background.js b/app/scripts/background.js index bacb6adddf9f..ad6e3b6f22c2 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -298,9 +298,6 @@ function maybeDetectPhishing(theController) { category: MetaMetricsEventCategory.Phishing, properties: { url: hostname, - referrer: { - url: hostname, - }, reason: blockReason, }, }); diff --git a/app/scripts/constants/sentry-state.ts b/app/scripts/constants/sentry-state.ts index 3125016ea0b5..76fb2386f1f6 100644 --- a/app/scripts/constants/sentry-state.ts +++ b/app/scripts/constants/sentry-state.ts @@ -98,7 +98,6 @@ export const SENTRY_BACKGROUND_STATE = { BridgeController: { bridgeState: { bridgeFeatureFlags: { - extensionConfig: false, extensionSupport: false, destNetworkAllowlist: [], srcNetworkAllowlist: [], @@ -107,18 +106,6 @@ export const SENTRY_BACKGROUND_STATE = { destTopAssets: [], srcTokens: {}, srcTopAssets: [], - quoteRequest: { - walletAddress: false, - srcTokenAddress: true, - slippage: true, - srcChainId: true, - destChainId: true, - destTokenAddress: true, - srcTokenAmount: true, - }, - quotes: [], - quotesLastFetched: true, - quotesLoadingStatus: true, }, }, CronjobController: { @@ -246,7 +233,6 @@ export const SENTRY_BACKGROUND_STATE = { showNativeTokenAsMainBalance: true, petnamesEnabled: true, showConfirmationAdvancedDetails: true, - privacyMode: false, }, useExternalServices: false, selectedAddress: false, diff --git a/app/scripts/controllers/bridge/bridge-controller.test.ts b/app/scripts/controllers/bridge/bridge-controller.test.ts index 35449cb40764..25b6eae98c33 100644 --- a/app/scripts/controllers/bridge/bridge-controller.test.ts +++ b/app/scripts/controllers/bridge/bridge-controller.test.ts @@ -2,10 +2,6 @@ import nock from 'nock'; import { BRIDGE_API_BASE_URL } from '../../../../shared/constants/bridge'; import { CHAIN_IDS } from '../../../../shared/constants/network'; import { SWAPS_API_V2_BASE_URL } from '../../../../shared/constants/swaps'; -import { flushPromises } from '../../../../test/lib/timer-helpers'; -// TODO: Remove restricted import -// eslint-disable-next-line import/no-restricted-paths -import * as bridgeUtil from '../../../../ui/pages/bridge/bridge.util'; import BridgeController from './bridge-controller'; import { BridgeControllerMessenger } from './types'; import { DEFAULT_BRIDGE_CONTROLLER_STATE } from './constants'; @@ -30,15 +26,9 @@ describe('BridgeController', function () { beforeEach(() => { jest.clearAllMocks(); - jest.clearAllTimers(); - nock(BRIDGE_API_BASE_URL) .get('/getAllFeatureFlags') .reply(200, { - 'extension-config': { - refreshRate: 3, - maxRefreshCount: 1, - }, 'extension-support': true, 'src-network-allowlist': [10, 534352], 'dest-network-allowlist': [137, 42161], @@ -65,7 +55,6 @@ describe('BridgeController', function () { symbol: 'ABC', }, ]); - bridgeController.resetState(); }); it('constructor should setup correctly', function () { @@ -77,35 +66,13 @@ describe('BridgeController', function () { extensionSupport: true, destNetworkAllowlist: [CHAIN_IDS.POLYGON, CHAIN_IDS.ARBITRUM], srcNetworkAllowlist: [CHAIN_IDS.OPTIMISM, CHAIN_IDS.SCROLL], - extensionConfig: { - maxRefreshCount: 1, - refreshRate: 3, - }, }; expect(bridgeController.state).toStrictEqual(EMPTY_INIT_STATE); - const setIntervalLengthSpy = jest.spyOn( - bridgeController, - 'setIntervalLength', - ); - await bridgeController.setBridgeFeatureFlags(); expect(bridgeController.state.bridgeState.bridgeFeatureFlags).toStrictEqual( expectedFeatureFlagsResponse, ); - expect(setIntervalLengthSpy).toHaveBeenCalledTimes(1); - expect(setIntervalLengthSpy).toHaveBeenCalledWith(3); - - bridgeController.resetState(); - expect(bridgeController.state.bridgeState).toStrictEqual( - expect.objectContaining({ - bridgeFeatureFlags: expectedFeatureFlagsResponse, - quotes: DEFAULT_BRIDGE_CONTROLLER_STATE.quotes, - quotesLastFetched: DEFAULT_BRIDGE_CONTROLLER_STATE.quotesLastFetched, - quotesLoadingStatus: - DEFAULT_BRIDGE_CONTROLLER_STATE.quotesLoadingStatus, - }), - ); }); it('selectDestNetwork should set the bridge dest tokens and top assets', async function () { @@ -127,11 +94,6 @@ describe('BridgeController', function () { expect(bridgeController.state.bridgeState.destTopAssets).toStrictEqual([ { address: '0x1f9840a85d5af5bf1d1762f925bdaddc4201f984', symbol: 'ABC' }, ]); - expect(bridgeController.state.bridgeState.quoteRequest).toStrictEqual({ - slippage: 0.5, - srcTokenAddress: '0x0000000000000000000000000000000000000000', - walletAddress: undefined, - }); }); it('selectSrcNetwork should set the bridge src tokens and top assets', async function () { @@ -156,240 +118,5 @@ describe('BridgeController', function () { symbol: 'ABC', }, ]); - expect(bridgeController.state.bridgeState.quoteRequest).toStrictEqual({ - slippage: 0.5, - srcTokenAddress: '0x0000000000000000000000000000000000000000', - walletAddress: undefined, - }); - }); - - it('updateBridgeQuoteRequestParams should update the quoteRequest state', function () { - bridgeController.updateBridgeQuoteRequestParams({ srcChainId: 1 }); - expect(bridgeController.state.bridgeState.quoteRequest).toStrictEqual({ - srcChainId: 1, - slippage: 0.5, - srcTokenAddress: '0x0000000000000000000000000000000000000000', - walletAddress: undefined, - }); - - bridgeController.updateBridgeQuoteRequestParams({ destChainId: 10 }); - expect(bridgeController.state.bridgeState.quoteRequest).toStrictEqual({ - destChainId: 10, - slippage: 0.5, - srcTokenAddress: '0x0000000000000000000000000000000000000000', - walletAddress: undefined, - }); - - bridgeController.updateBridgeQuoteRequestParams({ destChainId: undefined }); - expect(bridgeController.state.bridgeState.quoteRequest).toStrictEqual({ - destChainId: undefined, - slippage: 0.5, - srcTokenAddress: '0x0000000000000000000000000000000000000000', - walletAddress: undefined, - }); - - bridgeController.updateBridgeQuoteRequestParams({ - srcTokenAddress: undefined, - }); - expect(bridgeController.state.bridgeState.quoteRequest).toStrictEqual({ - slippage: 0.5, - srcTokenAddress: undefined, - walletAddress: undefined, - }); - - bridgeController.updateBridgeQuoteRequestParams({ - srcTokenAmount: '100000', - destTokenAddress: '0x123', - slippage: 0.5, - srcTokenAddress: '0x0000000000000000000000000000000000000000', - walletAddress: undefined, - }); - expect(bridgeController.state.bridgeState.quoteRequest).toStrictEqual({ - srcTokenAmount: '100000', - destTokenAddress: '0x123', - slippage: 0.5, - srcTokenAddress: '0x0000000000000000000000000000000000000000', - walletAddress: undefined, - }); - - bridgeController.updateBridgeQuoteRequestParams({ - srcTokenAddress: '0x2ABC', - }); - expect(bridgeController.state.bridgeState.quoteRequest).toStrictEqual({ - slippage: 0.5, - srcTokenAddress: '0x2ABC', - walletAddress: undefined, - }); - - bridgeController.resetState(); - expect(bridgeController.state.bridgeState.quoteRequest).toStrictEqual({ - slippage: 0.5, - srcTokenAddress: '0x0000000000000000000000000000000000000000', - walletAddress: undefined, - }); - }); - - it('updateBridgeQuoteRequestParams should trigger quote polling if request is valid', async function () { - jest.useFakeTimers(); - const stopAllPollingSpy = jest.spyOn(bridgeController, 'stopAllPolling'); - const startPollingByNetworkClientIdSpy = jest.spyOn( - bridgeController, - 'startPollingByNetworkClientId', - ); - messengerMock.call.mockReturnValue({ address: '0x123' } as never); - - const fetchBridgeQuotesSpy = jest - .spyOn(bridgeUtil, 'fetchBridgeQuotes') - .mockImplementationOnce(async () => { - return await new Promise((resolve) => { - return setTimeout(() => { - resolve([1, 2, 3] as never); - }, 5000); - }); - }); - - fetchBridgeQuotesSpy.mockImplementationOnce(async () => { - return await new Promise((resolve) => { - return setTimeout(() => { - resolve([5, 6, 7] as never); - }, 10000); - }); - }); - - fetchBridgeQuotesSpy.mockImplementationOnce(async () => { - return await new Promise((_, reject) => { - return setTimeout(() => { - reject(new Error('Network error')); - }, 10000); - }); - }); - - const quoteParams = { - srcChainId: 1, - destChainId: 10, - srcTokenAddress: '0x0000000000000000000000000000000000000000', - destTokenAddress: '0x123', - srcTokenAmount: '1000000000000000000', - }; - const quoteRequest = { - ...quoteParams, - slippage: 0.5, - walletAddress: '0x123', - }; - bridgeController.updateBridgeQuoteRequestParams(quoteParams); - - expect(stopAllPollingSpy).toHaveBeenCalledTimes(1); - expect(startPollingByNetworkClientIdSpy).toHaveBeenCalledTimes(1); - expect(startPollingByNetworkClientIdSpy).toHaveBeenCalledWith( - '1', - quoteRequest, - ); - - expect(bridgeController.state.bridgeState).toStrictEqual( - expect.objectContaining({ - quoteRequest: { ...quoteRequest, walletAddress: undefined }, - quotes: DEFAULT_BRIDGE_CONTROLLER_STATE.quotes, - quotesLastFetched: DEFAULT_BRIDGE_CONTROLLER_STATE.quotesLastFetched, - quotesLoadingStatus: - DEFAULT_BRIDGE_CONTROLLER_STATE.quotesLoadingStatus, - }), - ); - - // Loading state - jest.advanceTimersByTime(1000); - await flushPromises(); - expect(fetchBridgeQuotesSpy).toHaveBeenCalledTimes(1); - expect(fetchBridgeQuotesSpy).toHaveBeenCalledWith(quoteRequest); - - const firstFetchTime = - bridgeController.state.bridgeState.quotesLastFetched ?? 0; - expect(firstFetchTime).toBeGreaterThan(0); - expect(bridgeController.state.bridgeState).toEqual( - expect.objectContaining({ - quoteRequest: { ...quoteRequest, walletAddress: undefined }, - quotes: [], - quotesLoadingStatus: 0, - }), - ); - - // After first fetch - jest.advanceTimersByTime(10000); - await flushPromises(); - expect(bridgeController.state.bridgeState).toEqual( - expect.objectContaining({ - quoteRequest: { ...quoteRequest, walletAddress: undefined }, - quotes: [1, 2, 3], - quotesLoadingStatus: 1, - }), - ); - expect(bridgeController.state.bridgeState.quotesLastFetched).toStrictEqual( - firstFetchTime, - ); - - // After 2nd fetch - jest.advanceTimersByTime(50000); - await flushPromises(); - expect(fetchBridgeQuotesSpy).toHaveBeenCalledTimes(2); - expect(bridgeController.state.bridgeState).toEqual( - expect.objectContaining({ - quoteRequest: { ...quoteRequest, walletAddress: undefined }, - quotes: [5, 6, 7], - quotesLoadingStatus: 1, - }), - ); - const secondFetchTime = - bridgeController.state.bridgeState.quotesLastFetched; - expect(secondFetchTime).toBeGreaterThan(firstFetchTime); - - // After 3nd fetch throws an error - jest.advanceTimersByTime(50000); - await flushPromises(); - expect(fetchBridgeQuotesSpy).toHaveBeenCalledTimes(3); - expect(bridgeController.state.bridgeState).toEqual( - expect.objectContaining({ - quoteRequest: { ...quoteRequest, walletAddress: undefined }, - quotes: [5, 6, 7], - quotesLoadingStatus: 2, - }), - ); - expect(bridgeController.state.bridgeState.quotesLastFetched).toStrictEqual( - secondFetchTime, - ); - }); - - it('updateBridgeQuoteRequestParams should not trigger quote polling if request is invalid', function () { - const stopAllPollingSpy = jest.spyOn(bridgeController, 'stopAllPolling'); - const startPollingByNetworkClientIdSpy = jest.spyOn( - bridgeController, - 'startPollingByNetworkClientId', - ); - messengerMock.call.mockReturnValueOnce({ address: '0x123' } as never); - - bridgeController.updateBridgeQuoteRequestParams({ - srcChainId: 1, - destChainId: 10, - srcTokenAddress: '0x0000000000000000000000000000000000000000', - destTokenAddress: '0x123', - }); - - expect(stopAllPollingSpy).toHaveBeenCalledTimes(1); - expect(startPollingByNetworkClientIdSpy).not.toHaveBeenCalled(); - - expect(bridgeController.state.bridgeState).toStrictEqual( - expect.objectContaining({ - quoteRequest: { - srcChainId: 1, - slippage: 0.5, - srcTokenAddress: '0x0000000000000000000000000000000000000000', - walletAddress: undefined, - destChainId: 10, - destTokenAddress: '0x123', - }, - quotes: DEFAULT_BRIDGE_CONTROLLER_STATE.quotes, - quotesLastFetched: DEFAULT_BRIDGE_CONTROLLER_STATE.quotesLastFetched, - quotesLoadingStatus: - DEFAULT_BRIDGE_CONTROLLER_STATE.quotesLoadingStatus, - }), - ); }); }); diff --git a/app/scripts/controllers/bridge/bridge-controller.ts b/app/scripts/controllers/bridge/bridge-controller.ts index 1d20e6f404e4..841d735ac52c 100644 --- a/app/scripts/controllers/bridge/bridge-controller.ts +++ b/app/scripts/controllers/bridge/bridge-controller.ts @@ -1,10 +1,7 @@ -import { StateMetadata } from '@metamask/base-controller'; +import { BaseController, StateMetadata } from '@metamask/base-controller'; import { Hex } from '@metamask/utils'; -import { StaticIntervalPollingController } from '@metamask/polling-controller'; -import { NetworkClientId } from '@metamask/network-controller'; import { fetchBridgeFeatureFlags, - fetchBridgeQuotes, fetchBridgeTokens, // TODO: Remove restricted import // eslint-disable-next-line import/no-restricted-paths @@ -12,24 +9,11 @@ import { // TODO: Remove restricted import // eslint-disable-next-line import/no-restricted-paths import { fetchTopAssetsList } from '../../../../ui/pages/swaps/swaps.util'; -import { decimalToHex } from '../../../../shared/modules/conversion.utils'; -// TODO: Remove restricted import -// eslint-disable-next-line import/no-restricted-paths -import { QuoteRequest } from '../../../../ui/pages/bridge/types'; -// TODO: Remove restricted import -// eslint-disable-next-line import/no-restricted-paths -import { isValidQuoteRequest } from '../../../../ui/pages/bridge/utils/quote'; import { BRIDGE_CONTROLLER_NAME, DEFAULT_BRIDGE_CONTROLLER_STATE, - REFRESH_INTERVAL_MS, - RequestStatus, } from './constants'; -import { - BridgeControllerState, - BridgeControllerMessenger, - BridgeFeatureFlagsKey, -} from './types'; +import { BridgeControllerState, BridgeControllerMessenger } from './types'; const metadata: StateMetadata<{ bridgeState: BridgeControllerState }> = { bridgeState: { @@ -38,7 +22,7 @@ const metadata: StateMetadata<{ bridgeState: BridgeControllerState }> = { }, }; -export default class BridgeController extends StaticIntervalPollingController< +export default class BridgeController extends BaseController< typeof BRIDGE_CONTROLLER_NAME, { bridgeState: BridgeControllerState }, BridgeControllerMessenger @@ -48,13 +32,9 @@ export default class BridgeController extends StaticIntervalPollingController< name: BRIDGE_CONTROLLER_NAME, metadata, messenger, - state: { - bridgeState: DEFAULT_BRIDGE_CONTROLLER_STATE, - }, + state: { bridgeState: DEFAULT_BRIDGE_CONTROLLER_STATE }, }); - this.setIntervalLength(REFRESH_INTERVAL_MS); - this.messagingSystem.registerActionHandler( `${BRIDGE_CONTROLLER_NAME}:setBridgeFeatureFlags`, this.setBridgeFeatureFlags.bind(this), @@ -67,55 +47,12 @@ export default class BridgeController extends StaticIntervalPollingController< `${BRIDGE_CONTROLLER_NAME}:selectDestNetwork`, this.selectDestNetwork.bind(this), ); - this.messagingSystem.registerActionHandler( - `${BRIDGE_CONTROLLER_NAME}:updateBridgeQuoteRequestParams`, - this.updateBridgeQuoteRequestParams.bind(this), - ); } - _executePoll = async ( - _: NetworkClientId, - updatedQuoteRequest: QuoteRequest, - ) => { - await this.#fetchBridgeQuotes(updatedQuoteRequest); - }; - - updateBridgeQuoteRequestParams = (paramsToUpdate: Partial) => { - this.stopAllPolling(); - const { bridgeState } = this.state; - const updatedQuoteRequest = { - ...DEFAULT_BRIDGE_CONTROLLER_STATE.quoteRequest, - ...paramsToUpdate, - }; - - this.update((_state) => { - _state.bridgeState = { - ...bridgeState, - quoteRequest: updatedQuoteRequest, - quotes: DEFAULT_BRIDGE_CONTROLLER_STATE.quotes, - quotesLastFetched: DEFAULT_BRIDGE_CONTROLLER_STATE.quotesLastFetched, - quotesLoadingStatus: - DEFAULT_BRIDGE_CONTROLLER_STATE.quotesLoadingStatus, - }; - }); - - if (isValidQuoteRequest(updatedQuoteRequest)) { - const walletAddress = this.#getSelectedAccount().address; - this.startPollingByNetworkClientId( - decimalToHex(updatedQuoteRequest.srcChainId), - { ...updatedQuoteRequest, walletAddress }, - ); - } - }; - resetState = () => { - this.stopAllPolling(); this.update((_state) => { _state.bridgeState = { - ..._state.bridgeState, ...DEFAULT_BRIDGE_CONTROLLER_STATE, - quotes: [], - bridgeFeatureFlags: _state.bridgeState.bridgeFeatureFlags, }; }); }; @@ -126,9 +63,6 @@ export default class BridgeController extends StaticIntervalPollingController< this.update((_state) => { _state.bridgeState = { ...bridgeState, bridgeFeatureFlags }; }); - this.setIntervalLength( - bridgeFeatureFlags[BridgeFeatureFlagsKey.EXTENSION_CONFIG].refreshRate, - ); }; selectSrcNetwork = async (chainId: Hex) => { @@ -141,36 +75,6 @@ export default class BridgeController extends StaticIntervalPollingController< await this.#setTokens(chainId, 'destTokens'); }; - #fetchBridgeQuotes = async (request: QuoteRequest) => { - const { bridgeState } = this.state; - this.update((_state) => { - _state.bridgeState = { - ...bridgeState, - quotesLastFetched: Date.now(), - quotesLoadingStatus: RequestStatus.LOADING, - }; - }); - - try { - const quotes = await fetchBridgeQuotes(request); - this.update((_state) => { - _state.bridgeState = { - ..._state.bridgeState, - quotes, - quotesLoadingStatus: RequestStatus.FETCHED, - }; - }); - } catch (error) { - console.log('Failed to fetch bridge quotes', error); - this.update((_state) => { - _state.bridgeState = { - ...bridgeState, - quotesLoadingStatus: RequestStatus.ERROR, - }; - }); - } - }; - #setTopAssets = async ( chainId: Hex, stateKey: 'srcTopAssets' | 'destTopAssets', @@ -189,8 +93,4 @@ export default class BridgeController extends StaticIntervalPollingController< _state.bridgeState = { ...bridgeState, [stateKey]: tokens }; }); }; - - #getSelectedAccount() { - return this.messagingSystem.call('AccountsController:getSelectedAccount'); - } } diff --git a/app/scripts/controllers/bridge/constants.ts b/app/scripts/controllers/bridge/constants.ts index a4aa3264fdc8..58c7d015b7bb 100644 --- a/app/scripts/controllers/bridge/constants.ts +++ b/app/scripts/controllers/bridge/constants.ts @@ -1,23 +1,9 @@ -import { zeroAddress } from 'ethereumjs-util'; import { BridgeControllerState, BridgeFeatureFlagsKey } from './types'; export const BRIDGE_CONTROLLER_NAME = 'BridgeController'; -export const REFRESH_INTERVAL_MS = 30 * 1000; -const DEFAULT_MAX_REFRESH_COUNT = 5; -const DEFAULT_SLIPPAGE = 0.5; - -export enum RequestStatus { - LOADING, - FETCHED, - ERROR, -} export const DEFAULT_BRIDGE_CONTROLLER_STATE: BridgeControllerState = { bridgeFeatureFlags: { - [BridgeFeatureFlagsKey.EXTENSION_CONFIG]: { - refreshRate: REFRESH_INTERVAL_MS, - maxRefreshCount: DEFAULT_MAX_REFRESH_COUNT, - }, [BridgeFeatureFlagsKey.EXTENSION_SUPPORT]: false, [BridgeFeatureFlagsKey.NETWORK_SRC_ALLOWLIST]: [], [BridgeFeatureFlagsKey.NETWORK_DEST_ALLOWLIST]: [], @@ -26,12 +12,4 @@ export const DEFAULT_BRIDGE_CONTROLLER_STATE: BridgeControllerState = { srcTopAssets: [], destTokens: {}, destTopAssets: [], - quoteRequest: { - walletAddress: undefined, - srcTokenAddress: zeroAddress(), - slippage: DEFAULT_SLIPPAGE, - }, - quotes: [], - quotesLastFetched: undefined, - quotesLoadingStatus: undefined, }; diff --git a/app/scripts/controllers/bridge/types.ts b/app/scripts/controllers/bridge/types.ts index 10c2d8646545..2fb36e1e983e 100644 --- a/app/scripts/controllers/bridge/types.ts +++ b/app/scripts/controllers/bridge/types.ts @@ -3,26 +3,17 @@ import { RestrictedControllerMessenger, } from '@metamask/base-controller'; import { Hex } from '@metamask/utils'; -import { AccountsControllerGetSelectedAccountAction } from '@metamask/accounts-controller'; import { SwapsTokenObject } from '../../../../shared/constants/swaps'; -// TODO: Remove restricted import -// eslint-disable-next-line import/no-restricted-paths -import { QuoteRequest, QuoteResponse } from '../../../../ui/pages/bridge/types'; import BridgeController from './bridge-controller'; -import { BRIDGE_CONTROLLER_NAME, RequestStatus } from './constants'; +import { BRIDGE_CONTROLLER_NAME } from './constants'; export enum BridgeFeatureFlagsKey { - EXTENSION_CONFIG = 'extensionConfig', EXTENSION_SUPPORT = 'extensionSupport', NETWORK_SRC_ALLOWLIST = 'srcNetworkAllowlist', NETWORK_DEST_ALLOWLIST = 'destNetworkAllowlist', } export type BridgeFeatureFlags = { - [BridgeFeatureFlagsKey.EXTENSION_CONFIG]: { - refreshRate: number; - maxRefreshCount: number; - }; [BridgeFeatureFlagsKey.EXTENSION_SUPPORT]: boolean; [BridgeFeatureFlagsKey.NETWORK_SRC_ALLOWLIST]: Hex[]; [BridgeFeatureFlagsKey.NETWORK_DEST_ALLOWLIST]: Hex[]; @@ -34,16 +25,11 @@ export type BridgeControllerState = { srcTopAssets: { address: string }[]; destTokens: Record; destTopAssets: { address: string }[]; - quoteRequest: Partial; - quotes: QuoteResponse[]; - quotesLastFetched?: number; - quotesLoadingStatus?: RequestStatus; }; export enum BridgeUserAction { SELECT_SRC_NETWORK = 'selectSrcNetwork', SELECT_DEST_NETWORK = 'selectDestNetwork', - UPDATE_QUOTE_PARAMS = 'updateBridgeQuoteRequestParams', } export enum BridgeBackgroundAction { SET_FEATURE_FLAGS = 'setBridgeFeatureFlags', @@ -58,24 +44,20 @@ type BridgeControllerAction = { type BridgeControllerActions = | BridgeControllerAction | BridgeControllerAction - | BridgeControllerAction - | BridgeControllerAction; + | BridgeControllerAction; type BridgeControllerEvents = ControllerStateChangeEvent< typeof BRIDGE_CONTROLLER_NAME, BridgeControllerState >; -type AllowedActions = AccountsControllerGetSelectedAccountAction['type']; -type AllowedEvents = never; - /** * The messenger for the BridgeController. */ export type BridgeControllerMessenger = RestrictedControllerMessenger< typeof BRIDGE_CONTROLLER_NAME, - BridgeControllerActions | AccountsControllerGetSelectedAccountAction, + BridgeControllerActions, BridgeControllerEvents, - AllowedActions, - AllowedEvents + never, + never >; diff --git a/app/scripts/controllers/metametrics.ts b/app/scripts/controllers/metametrics.js similarity index 60% rename from app/scripts/controllers/metametrics.ts rename to app/scripts/controllers/metametrics.js index ded99dd917f4..aa5546ef7899 100644 --- a/app/scripts/controllers/metametrics.ts +++ b/app/scripts/controllers/metametrics.js @@ -11,40 +11,13 @@ import { import { ObservableStore } from '@metamask/obs-store'; import { bufferToHex, keccak } from 'ethereumjs-util'; import { v4 as uuidv4 } from 'uuid'; -import { NameControllerState, NameType } from '@metamask/name-controller'; -import { AccountsControllerState } from '@metamask/accounts-controller'; -import { - getErrorMessage, - Hex, - isErrorWithMessage, - isErrorWithStack, -} from '@metamask/utils'; -import { NetworkState } from '@metamask/network-controller'; -import { Browser } from 'webextension-polyfill'; -import { - Nft, - NftControllerState, - TokensControllerState, -} from '@metamask/assets-controllers'; -import { captureException as sentryCaptureException } from '@sentry/browser'; -import { AddressBookControllerState } from '@metamask/address-book-controller'; +import { NameType } from '@metamask/name-controller'; import { ENVIRONMENT_TYPE_BACKGROUND } from '../../../shared/constants/app'; import { METAMETRICS_ANONYMOUS_ID, METAMETRICS_BACKGROUND_PAGE_OBJECT, - MetaMetricsEventCategory, MetaMetricsEventName, - MetaMetricsEventFragment, MetaMetricsUserTrait, - MetaMetricsUserTraits, - SegmentEventPayload, - MetaMetricsContext, - MetaMetricsEventPayload, - MetaMetricsEventOptions, - MetaMetricsPagePayload, - MetaMetricsPageOptions, - MetaMetricsPageObject, - MetaMetricsReferrerObject, } from '../../../shared/constants/metametrics'; import { SECOND } from '../../../shared/constants/time'; import { isManifestV3 } from '../../../shared/modules/mv3.utils'; @@ -54,15 +27,11 @@ import { AnonymousTransactionMetaMetricsEvent, TransactionMetaMetricsEvent, } from '../../../shared/constants/transaction'; -import { LedgerTransportTypes } from '../../../shared/constants/hardware-wallets'; -import Analytics from '../lib/segment/analytics'; ///: BEGIN:ONLY_INCLUDE_IF(build-main) import { ENVIRONMENT } from '../../../development/build/constants'; ///: END:ONLY_INCLUDE_IF -import type { PreferencesControllerState } from './preferences-controller'; - const EXTENSION_UNINSTALL_URL = 'https://metamask.io/uninstalled'; export const overrideAnonymousEventNames = { @@ -82,9 +51,9 @@ export const overrideAnonymousEventNames = { MetaMetricsEventName.SignatureApprovedAnon, [MetaMetricsEventName.SignatureRejected]: MetaMetricsEventName.SignatureRejectedAnon, -} as const; +}; -const defaultCaptureException = (err: unknown) => { +const defaultCaptureException = (err) => { // throw error on clean stack so its captured by platform integrations (eg sentry) // but does not interrupt the call stack setTimeout(() => { @@ -94,11 +63,7 @@ const defaultCaptureException = (err: unknown) => { // The function is used to build a unique messageId for segment messages // It uses actionId and uniqueIdentifier from event if present -const buildUniqueMessageId = (args: { - uniqueIdentifier?: string; - actionId?: string; - isDuplicateAnonymizedEvent?: boolean; -}): string => { +const buildUniqueMessageId = (args) => { const messageIdParts = []; if (args.uniqueIdentifier) { messageIdParts.push(args.uniqueIdentifier); @@ -115,134 +80,55 @@ const buildUniqueMessageId = (args: { return generateRandomId(); }; -const exceptionsToFilter: Record = { +const exceptionsToFilter = { [`You must pass either an "anonymousId" or a "userId".`]: true, }; /** - * The type of a Segment event to create. - * - * Must correspond to the name of a method in {@link Analytics}. + * @typedef {import('../../../shared/constants/metametrics').MetaMetricsContext} MetaMetricsContext + * @typedef {import('../../../shared/constants/metametrics').MetaMetricsEventPayload} MetaMetricsEventPayload + * @typedef {import('../../../shared/constants/metametrics').MetaMetricsEventOptions} MetaMetricsEventOptions + * @typedef {import('../../../shared/constants/metametrics').SegmentEventPayload} SegmentEventPayload + * @typedef {import('../../../shared/constants/metametrics').SegmentInterface} SegmentInterface + * @typedef {import('../../../shared/constants/metametrics').MetaMetricsPagePayload} MetaMetricsPagePayload + * @typedef {import('../../../shared/constants/metametrics').MetaMetricsPageOptions} MetaMetricsPageOptions + * @typedef {import('../../../shared/constants/metametrics').MetaMetricsEventFragment} MetaMetricsEventFragment + * @typedef {import('../../../shared/constants/metametrics').MetaMetricsTraits} MetaMetricsTraits */ -type SegmentEventType = 'identify' | 'track' | 'page'; - -// TODO: Complete MetaMaskState by adding the full state definition and relocate it after the background is converted to TypeScript. -export type MetaMaskState = { - ledgerTransportType: LedgerTransportTypes; - networkConfigurationsByChainId: NetworkState['networkConfigurationsByChainId']; - internalAccounts: AccountsControllerState['internalAccounts']; - allNfts: NftControllerState['allNfts']; - allTokens: TokensControllerState['allTokens']; - theme: string; - participateInMetaMetrics: boolean; - dataCollectionForMarketing: boolean; - ShowNativeTokenAsMainBalance: boolean; - useNftDetection: PreferencesControllerState['useNftDetection']; - openSeaEnabled: PreferencesControllerState['openSeaEnabled']; - securityAlertsEnabled: PreferencesControllerState['securityAlertsEnabled']; - useTokenDetection: PreferencesControllerState['useTokenDetection']; - tokenSortConfig: PreferencesControllerState['preferences']['tokenSortConfig']; - names: NameControllerState['names']; - security_providers: string[]; - addressBook: AddressBookControllerState['addressBook']; - currentCurrency: string; - ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) - custodyAccountDetails: { - [address: string]: { - custodianName: string; - }; - }; - ///: END:ONLY_INCLUDE_IF -}; /** - * The state that MetaMetricsController stores. - * - * @property metaMetricsId - The user's metaMetricsId that will be attached to all non-anonymized event payloads - * @property participateInMetaMetrics - The user's preference for participating in the MetaMetrics analytics program. - * This setting controls whether or not events are tracked - * @property latestNonAnonymousEventTimestamp - The timestamp at which last non anonymous event is tracked. - * @property fragments - Object keyed by UUID with stored fragments as values. - * @property eventsBeforeMetricsOptIn - Array of queued events added before a user opts into metrics. - * @property traits - Traits that are not derived from other state keys. - * @property previousUserTraits - The user traits the last time they were computed. - * @property dataCollectionForMarketing - Flag to determine if data collection for marketing is enabled. - * @property marketingCampaignCookieId - The marketing campaign cookie id. - * @property segmentApiCalls - Object keyed by messageId with segment event type and payload as values. + * @typedef {object} MetaMetricsControllerState + * @property {string} [metaMetricsId] - The user's metaMetricsId that will be + * attached to all non-anonymized event payloads + * @property {boolean} [participateInMetaMetrics] - The user's preference for + * participating in the MetaMetrics analytics program. This setting controls + * whether or not events are tracked + * @property {boolean} [latestNonAnonymousEventTimestamp] - The timestamp at which last non anonymous event is tracked. + * @property {{[string]: MetaMetricsEventFragment}} [fragments] - Object keyed + * by UUID with stored fragments as values. + * @property {Array} [eventsBeforeMetricsOptIn] - Array of queued events added before + * a user opts into metrics. + * @property {object} [traits] - Traits that are not derived from other state keys. + * @property {Record} [previousUserTraits] - The user traits the last + * time they were computed. */ -export type MetaMetricsControllerState = { - metaMetricsId: string | null; - participateInMetaMetrics: boolean | null; - latestNonAnonymousEventTimestamp: number; - fragments: Record; - eventsBeforeMetricsOptIn: MetaMetricsEventPayload[]; - traits: MetaMetricsUserTraits; - previousUserTraits?: MetaMetricsUserTraits; - dataCollectionForMarketing: boolean | null; - marketingCampaignCookieId: string | null; - segmentApiCalls: Record< - string, - { - eventType: SegmentEventType; - payload: SegmentEventPayload; - } - >; -}; - -type CaptureException = - | typeof sentryCaptureException - | ((err: unknown) => void); - -export type MetaMetricsControllerOptions = { - initState: Partial; - segment: Analytics; - preferencesControllerState: PreferencesControllerState; - onPreferencesStateChange: ( - listener: (state: PreferencesControllerState) => void, - ) => void; - onNetworkDidChange: (listener: (networkState: NetworkState) => void) => void; - getCurrentChainId: () => Hex; - version: string; - environment: string; - extension: Browser; - captureException?: CaptureException; -}; export default class MetaMetricsController { - store: ObservableStore; - - #captureException: CaptureException; - - chainId: Hex; - - locale: string; - - version: MetaMetricsControllerOptions['version']; - - #extension: MetaMetricsControllerOptions['extension']; - - #environment: MetaMetricsControllerOptions['environment']; - - ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) - #selectedAddress: PreferencesControllerState['selectedAddress']; - ///: END:ONLY_INCLUDE_IF - - #segment: MetaMetricsControllerOptions['segment']; - /** - * @param options - * @param options.segment - an instance of analytics for tracking - * events that conform to the new MetaMetrics tracking plan. - * @param options.preferencesControllerState - The state of preferences controller - * @param options.onPreferencesStateChange - Used to attach a listener to the - * stateChange event emitted by the PreferencesController - * @param options.onNetworkDidChange - Used to attach a listener to the - * networkDidChange event emitted by the networkController - * @param options.getCurrentChainId - Gets the current chain id from the network controller. - * @param options.version - The version of the extension - * @param options.environment - The environment the extension is running in - * @param options.extension - webextension-polyfill - * @param options.initState - State to initialized with + * @param {object} options + * @param {object} options.segment - an instance of analytics for tracking + * events that conform to the new MetaMetrics tracking plan. + * @param {object} options.preferencesControllerState - The state of preferences controller + * @param {Function} options.onPreferencesStateChange - Used to attach a listener to the + * stateChange event emitted by the PreferencesController + * @param {Function} options.onNetworkDidChange - Used to attach a listener to the + * networkDidChange event emitted by the networkController + * @param {Function} options.getCurrentChainId - Gets the current chain id from the + * network controller + * @param {string} options.version - The version of the extension + * @param {string} options.environment - The environment the extension is running in + * @param {string} options.extension - webextension-polyfill + * @param {MetaMetricsControllerState} options.initState - State to initialized with * @param options.captureException */ constructor({ @@ -256,12 +142,11 @@ export default class MetaMetricsController { initState, extension, captureException = defaultCaptureException, - }: MetaMetricsControllerOptions) { - this.#captureException = (err: unknown) => { - const message = getErrorMessage(err); + }) { + this._captureException = (err) => { // This is a temporary measure. Currently there are errors flooding sentry due to a problem in how we are tracking anonymousId // We intend on removing this as soon as we understand how to correctly solve that problem. - if (!exceptionsToFilter[message]) { + if (!exceptionsToFilter[err.message]) { captureException(err); } }; @@ -269,11 +154,11 @@ export default class MetaMetricsController { this.locale = preferencesControllerState.currentLocale.replace('_', '-'); this.version = environment === 'production' ? version : `${version}-${environment}`; - this.#extension = extension; - this.#environment = environment; + this.extension = extension; + this.environment = environment; ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) - this.#selectedAddress = preferencesControllerState.selectedAddress; + this.selectedAddress = preferencesControllerState.selectedAddress; ///: END:ONLY_INCLUDE_IF const abandonedFragments = omitBy(initState?.fragments, 'persist'); @@ -304,7 +189,7 @@ export default class MetaMetricsController { onNetworkDidChange(() => { this.chainId = getCurrentChainId(); }); - this.#segment = segment; + this.segment = segment; // Track abandoned fragments that weren't properly cleaned up. // Abandoned fragments are those that were stored in persistent memory @@ -313,16 +198,16 @@ export default class MetaMetricsController { // fragments that are not marked as persistent will be purged and the // failure event will be emitted. Object.values(abandonedFragments).forEach((fragment) => { - this.processAbandonedFragment(fragment); + this.finalizeEventFragment(fragment.id, { abandoned: true }); }); // Code below submits any pending segmentApiCalls to Segment if/when the controller is re-instantiated if (isManifestV3) { Object.values(segmentApiCalls).forEach(({ eventType, payload }) => { try { - this.#submitSegmentAPICall(eventType, payload); + this._submitSegmentAPICall(eventType, payload); } catch (error) { - this.#captureException(error); + this._captureException(error); } }); } @@ -334,14 +219,14 @@ export default class MetaMetricsController { // tracked if the event isn't progressed within that amount of time. if (isManifestV3) { /* eslint-disable no-undef */ - this.#extension.alarms.getAll().then((alarms) => { + this.extension.alarms.getAll().then((alarms) => { const hasAlarm = checkAlarmExists( alarms, METAMETRICS_FINALIZE_EVENT_FRAGMENT_ALARM, ); if (!hasAlarm) { - this.#extension.alarms.create( + this.extension.alarms.create( METAMETRICS_FINALIZE_EVENT_FRAGMENT_ALARM, { delayInMinutes: 1, @@ -350,7 +235,7 @@ export default class MetaMetricsController { ); } }); - this.#extension.alarms.onAlarm.addListener((alarmInfo) => { + this.extension.alarms.onAlarm.addListener((alarmInfo) => { if (alarmInfo.name === METAMETRICS_FINALIZE_EVENT_FRAGMENT_ALARM) { this.finalizeAbandonedFragments(); } @@ -362,19 +247,18 @@ export default class MetaMetricsController { } } - finalizeAbandonedFragments(): void { + finalizeAbandonedFragments() { Object.values(this.store.getState().fragments).forEach((fragment) => { if ( fragment.timeout && - fragment.lastUpdated && Date.now() - fragment.lastUpdated / 1000 > fragment.timeout ) { - this.processAbandonedFragment(fragment); + this.finalizeEventFragment(fragment.id, { abandoned: true }); } }); } - generateMetaMetricsId(): string { + generateMetaMetricsId() { return bufferToHex( keccak( Buffer.from( @@ -388,11 +272,11 @@ export default class MetaMetricsController { /** * Create an event fragment in state and returns the event fragment object. * - * @param options - Fragment settings and properties to initiate the fragment with. + * @param {MetaMetricsEventFragment} options - Fragment settings and properties + * to initiate the fragment with. + * @returns {MetaMetricsEventFragment} */ - createEventFragment( - options: Omit, - ): MetaMetricsEventFragment { + createEventFragment(options) { if (!options.successEvent || !options.category) { throw new Error( `Must specify success event and category. Success event was: ${ @@ -415,34 +299,14 @@ export default class MetaMetricsController { ...options, lastUpdated: Date.now(), }; - - /** - * HACK: "transaction-submitted-" fragment hack - * A "transaction-submitted-" fragment may exist following the "Transaction Added" - * event to persist accumulated event fragment props to the "Transaction Submitted" event - * which fires after a user confirms a transaction. Rejecting a confirmation does not fire the - * "Transaction Submitted" event. In this case, these abandoned fragments will be deleted - * instead of finalized with canDeleteIfAbandoned set to true. - */ - const hasExistingSubmittedFragment = - options.initialEvent === TransactionMetaMetricsEvent.submitted && - fragments[id]; - - const additionalFragmentProps = hasExistingSubmittedFragment - ? { - ...fragments[id], - canDeleteIfAbandoned: false, - } - : {}; - this.store.updateState({ fragments: { ...fragments, - [id]: merge(additionalFragmentProps, fragment), + [id]: fragment, }, }); - if (fragment.initialEvent) { + if (options.initialEvent) { this.trackEvent({ event: fragment.initialEvent, category: fragment.category, @@ -466,9 +330,10 @@ export default class MetaMetricsController { * Returns the fragment stored in memory with provided id or undefined if it * does not exist. * - * @param id - id of fragment to retrieve + * @param {string} id - id of fragment to retrieve + * @returns {[MetaMetricsEventFragment]} */ - getEventFragmentById(id: string): MetaMetricsEventFragment { + getEventFragmentById(id) { const { fragments } = this.store.getState(); const fragment = fragments[id]; @@ -476,49 +341,19 @@ export default class MetaMetricsController { return fragment; } - /** - * Deletes to finalizes event fragment based on the canDeleteIfAbandoned property. - * - * @param fragment - */ - processAbandonedFragment(fragment: MetaMetricsEventFragment): void { - if (fragment.canDeleteIfAbandoned) { - this.deleteEventFragment(fragment.id); - } else { - this.finalizeEventFragment(fragment.id, { abandoned: true }); - } - } - /** * Updates an event fragment in state * - * @param id - The fragment id to update - * @param payload - Fragment settings and properties to initiate the fragment with. + * @param {string} id - The fragment id to update + * @param {Partial} payload - Fragment settings and + * properties to initiate the fragment with. */ - updateEventFragment( - id: string, - payload: Partial, - ): void { + updateEventFragment(id, payload) { const { fragments } = this.store.getState(); const fragment = fragments[id]; - /** - * HACK: "transaction-submitted-" fragment hack - * Creates a "transaction-submitted-" fragment if it does not exist to persist - * accumulated event metrics. In the case it is unused, the abandoned fragment will - * eventually be deleted with canDeleteIfAbandoned set to true. - */ - const createIfNotFound = !fragment && id.includes('transaction-submitted-'); - - if (createIfNotFound) { - fragments[id] = { - canDeleteIfAbandoned: true, - category: MetaMetricsEventCategory.Transactions, - successEvent: TransactionMetaMetricsEvent.finalized, - id, - }; - } else if (!fragment) { + if (!fragment) { throw new Error(`Event fragment with id ${id} does not exist.`); } @@ -534,41 +369,24 @@ export default class MetaMetricsController { } /** - * Deletes an event fragment from state - * - * @param id - The fragment id to delete + * @typedef {object} MetaMetricsFinalizeEventFragmentOptions + * @property {boolean} [abandoned = false] - if true track the failure + * event instead of the success event + * @property {MetaMetricsContext.page} [page] - page the final event + * occurred on. This will override whatever is set on the fragment + * @property {MetaMetricsContext.referrer} [referrer] - Dapp that + * originated the fragment. This is for fallback only, the fragment referrer + * property will take precedence. */ - deleteEventFragment(id: string): void { - const { fragments } = this.store.getState(); - - if (fragments[id]) { - delete fragments[id]; - } - } /** * Finalizes a fragment, tracking either a success event or failure Event * and then removes the fragment from state. * - * @param id - UUID of the event fragment to be closed - * @param options - * @param options.abandoned - if true track the failure event instead of the success event - * @param options.page - page the final event occurred on. This will override whatever is set on the fragment - * @param options.referrer - Dapp that originated the fragment. This is for fallback only, the fragment referrer - * property will take precedence. + * @param {string} id - UUID of the event fragment to be closed + * @param {MetaMetricsFinalizeEventFragmentOptions} options */ - finalizeEventFragment( - id: string, - { - abandoned = false, - page, - referrer, - }: { - abandoned?: boolean; - page?: MetaMetricsPageObject; - referrer?: MetaMetricsReferrerObject; - } = {}, - ): void { + finalizeEventFragment(id, { abandoned = false, page, referrer } = {}) { const fragment = this.store.getState().fragments[id]; if (!fragment) { throw new Error(`Funnel with id ${id} does not exist.`); @@ -577,7 +395,7 @@ export default class MetaMetricsController { const eventName = abandoned ? fragment.failureEvent : fragment.successEvent; this.trackEvent({ - event: eventName ?? '', + event: eventName, category: fragment.category, properties: fragment.properties, sensitiveProperties: fragment.sensitiveProperties, @@ -606,9 +424,9 @@ export default class MetaMetricsController { * Calls this._identify with validated metaMetricsId and user traits if user is participating * in the MetaMetrics analytics program * - * @param userTraits + * @param {object} userTraits */ - identify(userTraits: Partial): void { + identify(userTraits) { const { metaMetricsId, participateInMetaMetrics } = this.state; if (!participateInMetaMetrics || !metaMetricsId || !userTraits) { @@ -621,33 +439,26 @@ export default class MetaMetricsController { return; } - const allValidTraits = this.#buildValidTraits(userTraits); + const allValidTraits = this._buildValidTraits(userTraits); - this.#identify(allValidTraits); + this._identify(allValidTraits); } // It sets an uninstall URL ("Sorry to see you go!" page), // which is opened if a user uninstalls the extension. - updateExtensionUninstallUrl( - participateInMetaMetrics: boolean, - metaMetricsId: string, - ): void { - const query: { - mmi?: string; - env?: string; - av?: string; - } = {}; + updateExtensionUninstallUrl(participateInMetaMetrics, metaMetricsId) { + const query = {}; if (participateInMetaMetrics) { // We only want to track these things if a user opted into metrics. query.mmi = Buffer.from(metaMetricsId).toString('base64'); - query.env = this.#environment; + query.env = this.environment; query.av = this.version; } const queryString = new URLSearchParams(query); // this.extension not currently defined in tests - if (this.#extension && this.#extension.runtime) { - this.#extension.runtime.setUninstallURL( + if (this.extension && this.extension.runtime) { + this.extension.runtime.setUninstallURL( `${EXTENSION_UNINSTALL_URL}?${queryString}`, ); } @@ -656,12 +467,12 @@ export default class MetaMetricsController { /** * Setter for the `participateInMetaMetrics` property * - * @param participateInMetaMetrics - Whether or not the user wants to participate in MetaMetrics if not set - * @returns The string of the new metametrics id, or null + * @param {boolean} participateInMetaMetrics - Whether or not the user wants + * to participate in MetaMetrics + * @returns {Promise} the string of the new metametrics id, or null + * if not set */ - async setParticipateInMetaMetrics( - participateInMetaMetrics: boolean, - ): Promise { + async setParticipateInMetaMetrics(participateInMetaMetrics) { const { metaMetricsId: existingMetaMetricsId } = this.state; const metaMetricsId = @@ -679,10 +490,7 @@ export default class MetaMetricsController { } ///: BEGIN:ONLY_INCLUDE_IF(build-main) - if ( - this.#environment !== ENVIRONMENT.DEVELOPMENT && - metaMetricsId !== null - ) { + if (this.environment !== ENVIRONMENT.DEVELOPMENT) { this.updateExtensionUninstallUrl(participateInMetaMetrics, metaMetricsId); } ///: END:ONLY_INCLUDE_IF @@ -690,9 +498,7 @@ export default class MetaMetricsController { return metaMetricsId; } - setDataCollectionForMarketing( - dataCollectionForMarketing: boolean, - ): MetaMetricsControllerState['metaMetricsId'] { + setDataCollectionForMarketing(dataCollectionForMarketing) { const { metaMetricsId } = this.state; this.store.updateState({ dataCollectionForMarketing }); @@ -704,24 +510,25 @@ export default class MetaMetricsController { return metaMetricsId; } - setMarketingCampaignCookieId(marketingCampaignCookieId: string | null): void { + setMarketingCampaignCookieId(marketingCampaignCookieId) { this.store.updateState({ marketingCampaignCookieId }); } - get state(): MetaMetricsControllerState { + get state() { return this.store.getState(); } /** * track a page view with Segment * - * @param payload - details of the page viewed. - * @param options - options for handling the page view. + * @param {MetaMetricsPagePayload} payload - details of the page viewed + * @param {MetaMetricsPageOptions} [options] - options for handling the page + * view */ trackPage( - payload: MetaMetricsPagePayload, - options?: MetaMetricsPageOptions, - ): void { + { name, params, environmentType, page, referrer, actionId }, + options, + ) { try { if (this.state.participateInMetaMetrics === false) { return; @@ -733,13 +540,10 @@ export default class MetaMetricsController { ) { return; } - - const { name, params, environmentType, page, referrer, actionId } = - payload; const { metaMetricsId } = this.state; const idTrait = metaMetricsId ? 'userId' : 'anonymousId'; const idValue = metaMetricsId ?? METAMETRICS_ANONYMOUS_ID; - this.#submitSegmentAPICall('page', { + this._submitSegmentAPICall('page', { messageId: buildUniqueMessageId({ actionId }), [idTrait]: idValue, name, @@ -749,27 +553,24 @@ export default class MetaMetricsController { chain_id: this.chainId, environment_type: environmentType, }, - context: this.#buildContext(referrer, page), + context: this._buildContext(referrer, page), }); } catch (err) { - this.#captureException(err); + this._captureException(err); } } /** * submits a metametrics event, not waiting for it to complete or allowing its error to bubble up * - * @param payload - details of the event - * @param options - options for handling/routing the event + * @param {MetaMetricsEventPayload} payload - details of the event + * @param {MetaMetricsEventOptions} [options] - options for handling/routing the event */ - trackEvent( - payload: MetaMetricsEventPayload, - options?: MetaMetricsEventOptions, - ): void { + trackEvent(payload, options) { // validation is not caught and handled this.validatePayload(payload); this.submitEvent(payload, options).catch((err) => - this.#captureException(err), + this._captureException(err), ); } @@ -779,13 +580,11 @@ export default class MetaMetricsController { * with sensitiveProperties into two events, tracking the sensitiveProperties * with the anonymousId only. * - * @param payload - details of the event - * @param options - options for handling/routing the event + * @param {MetaMetricsEventPayload} payload - details of the event + * @param {MetaMetricsEventOptions} [options] - options for handling/routing the event + * @returns {Promise} */ - async submitEvent( - payload: MetaMetricsEventPayload, - options?: MetaMetricsEventOptions, - ): Promise { + async submitEvent(payload, options) { this.validatePayload(payload); if (!this.state.participateInMetaMetrics && !options?.isOptIn) { @@ -808,7 +607,6 @@ export default class MetaMetricsController { // change anonymous event names const anonymousEventName = - // @ts-expect-error This property may not exist. We check for it below. overrideAnonymousEventNames[`${payload.event}`]; const anonymousPayload = { ...payload, @@ -821,8 +619,8 @@ export default class MetaMetricsController { ); events.push( - this.#track( - this.#buildEventPayload({ + this._track( + this._buildEventPayload({ ...anonymousPayload, properties: combinedProperties, isDuplicateAnonymizedEvent: true, @@ -832,7 +630,7 @@ export default class MetaMetricsController { ); } - events.push(this.#track(this.#buildEventPayload(payload), options)); + events.push(this._track(this._buildEventPayload(payload), options)); await Promise.all(events); } @@ -840,9 +638,9 @@ export default class MetaMetricsController { /** * validates a metametrics event * - * @param payload - details of the event + * @param {MetaMetricsEventPayload} payload - details of the event */ - validatePayload(payload: MetaMetricsEventPayload): void { + validatePayload(payload) { // event and category are required fields for all payloads if (!payload.event || !payload.category) { throw new Error( @@ -859,7 +657,7 @@ export default class MetaMetricsController { } } - handleMetaMaskStateUpdate(newState: MetaMaskState): void { + handleMetaMaskStateUpdate(newState) { const userTraits = this._buildUserTraitsObject(newState); if (userTraits) { this.identify(userTraits); @@ -867,7 +665,7 @@ export default class MetaMetricsController { } // Track all queued events after a user opted into metrics. - trackEventsAfterMetricsOptIn(): void { + trackEventsAfterMetricsOptIn() { const { eventsBeforeMetricsOptIn } = this.store.getState(); eventsBeforeMetricsOptIn.forEach((eventBeforeMetricsOptIn) => { this.trackEvent(eventBeforeMetricsOptIn); @@ -875,14 +673,14 @@ export default class MetaMetricsController { } // Once we track queued events after a user opts into metrics, we want to clear the event queue. - clearEventsAfterMetricsOptIn(): void { + clearEventsAfterMetricsOptIn() { this.store.updateState({ eventsBeforeMetricsOptIn: [], }); } // It adds an event into a queue, which is only tracked if a user opts into metrics. - addEventBeforeMetricsOptIn(event: MetaMetricsEventPayload): void { + addEventBeforeMetricsOptIn(event) { const prevState = this.store.getState().eventsBeforeMetricsOptIn; this.store.updateState({ eventsBeforeMetricsOptIn: [...prevState, event], @@ -890,7 +688,7 @@ export default class MetaMetricsController { } // Add or update traits for tracking. - updateTraits(newTraits: MetaMetricsUserTraits): void { + updateTraits(newTraits) { const { traits } = this.store.getState(); this.store.updateState({ traits: { ...traits, ...newTraits }, @@ -898,7 +696,7 @@ export default class MetaMetricsController { } // Retrieve (or generate if doesn't exist) the client metametrics id - getMetaMetricsId(): string { + getMetaMetricsId() { let { metaMetricsId } = this.state; if (!metaMetricsId) { metaMetricsId = this.generateMetaMetricsId(); @@ -913,22 +711,18 @@ export default class MetaMetricsController { * Build the context object to attach to page and track events. * * @private - * @param referrer - dapp origin that initialized - * the notification window. - * @param page - page object describing the current - * view of the extension. Defaults to the background-process object. + * @param {Pick} [referrer] - dapp origin that initialized + * the notification window. + * @param {Pick} [page] - page object describing the current + * view of the extension. Defaults to the background-process object. + * @returns {MetaMetricsContext} */ - #buildContext( - referrer: MetaMetricsContext['referrer'], - page: MetaMetricsContext['page'] = METAMETRICS_BACKGROUND_PAGE_OBJECT, - ): MetaMetricsContext { + _buildContext(referrer, page = METAMETRICS_BACKGROUND_PAGE_OBJECT) { ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) - const mmiProps: { - extensionId?: string; - } = {}; + const mmiProps = {}; - if (this.#extension?.runtime?.id) { - mmiProps.extensionId = this.#extension.runtime.id; + if (this.extension?.runtime?.id) { + mmiProps.extensionId = this.extension.runtime.id; } ///: END:ONLY_INCLUDE_IF @@ -952,12 +746,12 @@ export default class MetaMetricsController { * fed to Segment's track method * * @private - * @param rawPayload - raw payload provided to trackEvent - * @returns formatted event payload for segment + * @param { + * Omit + * } rawPayload - raw payload provided to trackEvent + * @returns {SegmentEventPayload} formatted event payload for segment */ - #buildEventPayload( - rawPayload: Omit, - ): SegmentEventPayload { + _buildEventPayload(rawPayload) { const { event, properties, @@ -971,17 +765,14 @@ export default class MetaMetricsController { } = rawPayload; ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) - const mmiProps: { - extensionId?: string; - accountAddress?: string; - } = {}; + const mmiProps = {}; - if (this.#extension?.runtime?.id) { - mmiProps.extensionId = this.#extension.runtime.id; + if (this.extension?.runtime?.id) { + mmiProps.extensionId = this.extension.runtime.id; } - if (this.#selectedAddress) { - mmiProps.accountAddress = this.#selectedAddress; + if (this.selectedAddress) { + mmiProps.accountAddress = this.selectedAddress; } ///: END:ONLY_INCLUDE_IF @@ -1001,18 +792,13 @@ export default class MetaMetricsController { currency, category, locale: this.locale, - chain_id: - properties && - 'chain_id' in properties && - typeof properties.chain_id === 'string' - ? properties.chain_id - : this.chainId, + chain_id: properties?.chain_id ?? this.chainId, environment_type: environmentType, ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) ...mmiProps, ///: END:ONLY_INCLUDE_IF }, - context: this.#buildContext(referrer, page), + context: this._buildContext(referrer, page), }; } @@ -1020,12 +806,10 @@ export default class MetaMetricsController { * This method generates the MetaMetrics user traits object, omitting any * traits that have not changed since the last invocation of this method. * - * @param metamaskState - Full metamask state object. - * @returns traits that have changed since last update + * @param {object} metamaskState - Full metamask state object. + * @returns {MetaMetricsTraits | null} traits that have changed since last update */ - _buildUserTraitsObject( - metamaskState: MetaMaskState, - ): Partial | null { + _buildUserTraitsObject(metamaskState) { ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) const mmiAccountAddress = metamaskState.custodyAccountDetails && @@ -1035,11 +819,10 @@ export default class MetaMetricsController { ///: END:ONLY_INCLUDE_IF const { traits, previousUserTraits } = this.store.getState(); + /** @type {MetaMetricsTraits} */ const currentTraits = { [MetaMetricsUserTrait.AddressBookEntries]: sum( - Object.values(metamaskState.addressBook).map((v) => - size(v as object | string | null | undefined), - ), + Object.values(metamaskState.addressBook).map(size), ), [MetaMetricsUserTrait.InstallDateExt]: traits[MetaMetricsUserTrait.InstallDateExt] || '', @@ -1059,30 +842,29 @@ export default class MetaMetricsController { metamaskState.internalAccounts.accounts, ).length, [MetaMetricsUserTrait.NumberOfNftCollections]: - this.#getAllUniqueNFTAddressesLength(metamaskState.allNfts), - [MetaMetricsUserTrait.NumberOfNfts]: this.#getAllNFTsFlattened( + this._getAllUniqueNFTAddressesLength(metamaskState.allNfts), + [MetaMetricsUserTrait.NumberOfNfts]: this._getAllNFTsFlattened( metamaskState.allNfts, ).length, - [MetaMetricsUserTrait.NumberOfTokens]: this.#getNumberOfTokens( - metamaskState.allTokens, - ), - [MetaMetricsUserTrait.OpenSeaApiEnabled]: metamaskState.openSeaEnabled, + [MetaMetricsUserTrait.NumberOfTokens]: + this._getNumberOfTokens(metamaskState), + [MetaMetricsUserTrait.OpenseaApiEnabled]: metamaskState.openSeaEnabled, [MetaMetricsUserTrait.ThreeBoxEnabled]: false, // deprecated, hard-coded as false [MetaMetricsUserTrait.Theme]: metamaskState.theme || 'default', [MetaMetricsUserTrait.TokenDetectionEnabled]: metamaskState.useTokenDetection, [MetaMetricsUserTrait.ShowNativeTokenAsMainBalance]: - metamaskState.ShowNativeTokenAsMainBalance, + metamaskState.showNativeTokenAsMainBalance, [MetaMetricsUserTrait.CurrentCurrency]: metamaskState.currentCurrency, ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) - [MetaMetricsUserTrait.MmiExtensionId]: this.#extension?.runtime?.id, - [MetaMetricsUserTrait.MmiAccountAddress]: mmiAccountAddress ?? null, + [MetaMetricsUserTrait.MmiExtensionId]: this.extension?.runtime?.id, + [MetaMetricsUserTrait.MmiAccountAddress]: mmiAccountAddress, [MetaMetricsUserTrait.MmiIsCustodian]: Boolean(mmiAccountAddress), ///: END:ONLY_INCLUDE_IF [MetaMetricsUserTrait.SecurityProviders]: metamaskState.securityAlertsEnabled ? ['blockaid'] : [], [MetaMetricsUserTrait.PetnameAddressCount]: - this.#getPetnameAddressCount(metamaskState), + this._getPetnameAddressCount(metamaskState), [MetaMetricsUserTrait.IsMetricsOptedIn]: metamaskState.participateInMetaMetrics, [MetaMetricsUserTrait.HasMarketingConsent]: @@ -1092,18 +874,15 @@ export default class MetaMetricsController { }; if (!previousUserTraits) { - this.store.updateState({ - previousUserTraits: currentTraits, - }); + this.store.updateState({ previousUserTraits: currentTraits }); return currentTraits; } if (previousUserTraits && !isEqual(previousUserTraits, currentTraits)) { - const updates = pickBy(currentTraits, (v, k) => { - // @ts-expect-error It's okay that `k` may not be a key of `previousUserTraits`, because we assume `isEqual` can handle it - const previous = previousUserTraits[k]; - return !isEqual(previous, v); - }); + const updates = pickBy( + currentTraits, + (v, k) => !isEqual(previousUserTraits[k], v), + ); this.store.updateState({ previousUserTraits: currentTraits }); return updates; } @@ -1115,42 +894,33 @@ export default class MetaMetricsController { * Returns a new object of all valid user traits. For dates, we transform them into ISO-8601 timestamp strings. * * @see {@link https://segment.com/docs/connections/spec/common/#timestamps} - * @param userTraits + * @param {object} userTraits + * @returns {object} */ - #buildValidTraits( - userTraits: Partial, - ): MetaMetricsUserTraits { - return Object.entries(userTraits).reduce( - (validTraits: MetaMetricsUserTraits, [key, value]) => { - if (this.#isValidTraitDate(value)) { - return { - ...validTraits, - [key]: value.toISOString(), - }; - } else if (this.#isValidTrait(value)) { - return { - ...validTraits, - [key]: value, - }; - } - + _buildValidTraits(userTraits) { + return Object.entries(userTraits).reduce((validTraits, [key, value]) => { + if (this._isValidTraitDate(value)) { + validTraits[key] = value.toISOString(); + } else if (this._isValidTrait(value)) { + validTraits[key] = value; + } else { console.warn( `MetaMetricsController: "${key}" value is not a valid trait type`, ); - return validTraits; - }, - {}, - ); + } + return validTraits; + }, {}); } /** * Returns an array of all of the NFTs the user * possesses across all networks and accounts. * - * @param allNfts + * @param {object} allNfts + * @returns {[]} */ - #getAllNFTsFlattened = memoize((allNfts: MetaMaskState['allNfts'] = {}) => { - return Object.values(allNfts).reduce((result: Nft[], chainNFTs) => { + _getAllNFTsFlattened = memoize((allNfts = {}) => { + return Object.values(allNfts).reduce((result, chainNFTs) => { return result.concat(...Object.values(chainNFTs)); }, []); }); @@ -1159,12 +929,11 @@ export default class MetaMetricsController { * Returns the number of unique NFT addresses the user * possesses across all networks and accounts. * - * @param allNfts + * @param {object} allNfts + * @returns {number} */ - #getAllUniqueNFTAddressesLength( - allNfts: MetaMaskState['allNfts'] = {}, - ): number { - const allNFTAddresses = this.#getAllNFTsFlattened(allNfts).map( + _getAllUniqueNFTAddressesLength(allNfts = {}) { + const allNFTAddresses = this._getAllNFTsFlattened(allNfts).map( (nft) => nft.address, ); const uniqueAddresses = new Set(allNFTAddresses); @@ -1172,22 +941,26 @@ export default class MetaMetricsController { } /** - * @param allTokens + * @param {object} metamaskState * @returns number of unique token addresses */ - #getNumberOfTokens(allTokens: MetaMaskState['allTokens']): number { - return Object.values(allTokens).reduce((result, accountsByChain) => { - return result + sum(Object.values(accountsByChain).map(size)); - }, 0); + _getNumberOfTokens(metamaskState) { + return Object.values(metamaskState.allTokens).reduce( + (result, accountsByChain) => { + return result + sum(Object.values(accountsByChain).map(size)); + }, + 0, + ); } /** * Calls segment.identify with given user traits * * @see {@link https://segment.com/docs/connections/sources/catalog/libraries/server/node/#identify} - * @param userTraits + * @private + * @param {object} userTraits */ - #identify(userTraits: MetaMetricsUserTraits): void { + _identify(userTraits) { const { metaMetricsId } = this.state; if (!userTraits || Object.keys(userTraits).length === 0) { @@ -1196,12 +969,12 @@ export default class MetaMetricsController { } try { - this.#submitSegmentAPICall('identify', { - userId: metaMetricsId ?? undefined, + this._submitSegmentAPICall('identify', { + userId: metaMetricsId, traits: userTraits, }); } catch (err) { - this.#captureException(err); + this._captureException(err); } } @@ -1209,26 +982,28 @@ export default class MetaMetricsController { * Validates the trait value. Segment accepts any data type. We are adding validation here to * support data types for our Segment destination(s) e.g. MixPanel * - * @param value + * @param {*} value + * @returns {boolean} */ - #isValidTrait(value: unknown): boolean { + _isValidTrait(value) { const type = typeof value; return ( type === 'string' || type === 'boolean' || type === 'number' || - this.#isValidTraitArray(value) || - this.#isValidTraitDate(value) + this._isValidTraitArray(value) || + this._isValidTraitDate(value) ); } /** * Segment accepts any data type value. We have special logic to validate arrays. * - * @param value + * @param {*} value + * @returns {boolean} */ - #isValidTraitArray(value: unknown): boolean { + _isValidTraitArray = (value) => { return ( Array.isArray(value) && (value.every((element) => { @@ -1241,16 +1016,17 @@ export default class MetaMetricsController { return typeof element === 'number'; })) ); - } + }; /** * Returns true if the value is an accepted date type * - * @param value + * @param {*} value + * @returns {boolean} */ - #isValidTraitDate(value: unknown): value is Date { + _isValidTraitDate = (value) => { return Object.prototype.toString.call(value) === '[object Date]'; - } + }; /** * Perform validation on the payload and update the id type to use before @@ -1258,20 +1034,19 @@ export default class MetaMetricsController { * event appropriately. * * @private - * @param payload - properties to attach to event - * @param options - options for routing and handling the event + * @param {SegmentEventPayload} payload - properties to attach to event + * @param {MetaMetricsEventOptions} [options] - options for routing and + * handling the event + * @returns {Promise} */ - #track( - payload: SegmentEventPayload, - options?: MetaMetricsEventOptions, - ): Promise { + _track(payload, options) { const { isOptIn, metaMetricsId: metaMetricsIdOverride, matomoEvent, flushImmediately, } = options || {}; - let idType: 'userId' | 'anonymousId' = 'userId'; + let idType = 'userId'; let idValue = this.state.metaMetricsId; let excludeMetaMetricsId = options?.excludeMetaMetricsId ?? false; // This is carried over from the old implementation, and will likely need @@ -1298,7 +1073,7 @@ export default class MetaMetricsController { } else if (isOptIn && metaMetricsIdOverride) { idValue = metaMetricsIdOverride; } - payload[idType] = idValue ?? undefined; + payload[idType] = idValue; // If this is an event on the old matomo schema, add a key to the payload // to designate it as such @@ -1310,43 +1085,33 @@ export default class MetaMetricsController { // event that relies on this promise being fulfilled before performing UI // updates, or otherwise delaying user interaction, supply the // 'flushImmediately' flag to the trackEvent method. - return new Promise((resolve, reject) => { - const callback = (err: unknown) => { + return new Promise((resolve, reject) => { + const callback = (err) => { if (err) { - const message = isErrorWithMessage(err) ? err.message : ''; - const stack = isErrorWithStack(err) ? err.stack : undefined; // The error that segment gives us has some manipulation done to it // that seemingly breaks with lockdown enabled. Creating a new error // here prevents the system from freezing when the network request to // segment fails for any reason. - const safeError = new Error(message); - if (stack) { - safeError.stack = stack; - } + const safeError = new Error(err.message); + safeError.stack = err.stack; return reject(safeError); } return resolve(); }; - this.#submitSegmentAPICall('track', payload, callback); + this._submitSegmentAPICall('track', payload, callback); if (flushImmediately) { - this.#segment.flush(); + this.segment.flush(); } }); } - /* - * Method below submits the request to analytics SDK. - * It will also add event to controller store - * and pass a callback to remove it from store once request is submitted to segment - * Saving segmentApiCalls in controller store in MV3 ensures that events are tracked - * even if service worker terminates before events are submitted to segment. - */ - #submitSegmentAPICall( - eventType: SegmentEventType, - payload: Partial, - callback?: (result: unknown) => unknown, - ): void { + // Method below submits the request to analytics SDK. + // It will also add event to controller store + // and pass a callback to remove it from store once request is submitted to segment + // Saving segmentApiCalls in controller store in MV3 ensures that events are tracked + // even if service worker terminates before events are submiteed to segment. + _submitSegmentAPICall(eventType, payload, callback) { const { metaMetricsId, participateInMetaMetrics, @@ -1364,19 +1129,13 @@ export default class MetaMetricsController { timestamp = payloadDate; } } - const modifiedPayload = { - ...payload, - messageId, - timestamp, - }; + const modifiedPayload = { ...payload, messageId, timestamp }; this.store.updateState({ ...this.store.getState(), latestNonAnonymousEventTimestamp: modifiedPayload.anonymousId === METAMETRICS_ANONYMOUS_ID ? latestNonAnonymousEventTimestamp : timestamp.valueOf(), - // @ts-expect-error The reason this is needed is that the event property in the payload can be missing, - // whereas the state expects it to be present. It's unclear how best to handle this discrepancy. segmentApiCalls: { ...this.store.getState().segmentApiCalls, [messageId]: { @@ -1388,7 +1147,7 @@ export default class MetaMetricsController { }, }, }); - const modifiedCallback = (result: unknown) => { + const modifiedCallback = (result) => { const { segmentApiCalls } = this.store.getState(); delete segmentApiCalls[messageId]; this.store.updateState({ @@ -1396,16 +1155,17 @@ export default class MetaMetricsController { }); return callback?.(result); }; - this.#segment[eventType](modifiedPayload, modifiedCallback); + this.segment[eventType](modifiedPayload, modifiedCallback); } /** * Returns the total number of Ethereum addresses with saved petnames, * including all chain ID variations. * - * @param metamaskState + * @param {object} metamaskState + * @returns {number} */ - #getPetnameAddressCount(metamaskState: MetaMaskState): number { + _getPetnameAddressCount(metamaskState) { const addressNames = metamaskState.names?.[NameType.ETHEREUM_ADDRESS] ?? {}; return Object.keys(addressNames).reduce((totalCount, address) => { diff --git a/app/scripts/controllers/metametrics.test.ts b/app/scripts/controllers/metametrics.test.js similarity index 69% rename from app/scripts/controllers/metametrics.test.ts rename to app/scripts/controllers/metametrics.test.js index 4b2a1f09a562..ca5602de33c8 100644 --- a/app/scripts/controllers/metametrics.test.ts +++ b/app/scripts/controllers/metametrics.test.js @@ -1,38 +1,18 @@ import { toHex } from '@metamask/controller-utils'; -import { NetworkState } from '@metamask/network-controller'; -import { NameEntry, NameType } from '@metamask/name-controller'; -import { AddressBookEntry } from '@metamask/address-book-controller'; -import { - Nft, - Token, - TokensControllerState, -} from '@metamask/assets-controllers'; -import { InternalAccount } from '@metamask/keyring-api'; -import { Browser } from 'webextension-polyfill'; -import { Hex } from '@metamask/utils'; -import { merge } from 'lodash'; +import { NameType } from '@metamask/name-controller'; import { ENVIRONMENT_TYPE_BACKGROUND } from '../../../shared/constants/app'; import { createSegmentMock } from '../lib/segment'; import { METAMETRICS_ANONYMOUS_ID, METAMETRICS_BACKGROUND_PAGE_OBJECT, MetaMetricsUserTrait, - MetaMetricsUserTraits, } from '../../../shared/constants/metametrics'; import { CHAIN_IDS } from '../../../shared/constants/network'; -import { LedgerTransportTypes } from '../../../shared/constants/hardware-wallets'; import * as Utils from '../lib/util'; import { mockNetworkState } from '../../../test/stub/networks'; -import MetaMetricsController, { - MetaMetricsControllerOptions, - MetaMetricsControllerState, -} from './metametrics'; -import { - getDefaultPreferencesControllerState, - PreferencesControllerState, -} from './preferences-controller'; +import MetaMetricsController from './metametrics'; -const segmentMock = createSegmentMock(2); +const segment = createSegmentMock(2, 10000); const VERSION = '0.0.1-test'; const FAKE_CHAIN_ID = '0x1338'; @@ -47,7 +27,7 @@ const MOCK_EXTENSION = { id: MOCK_EXTENSION_ID, setUninstallURL: () => undefined, }, -} as unknown as Browser; +}; const MOCK_TRAITS = { test_boolean: true, @@ -56,12 +36,12 @@ const MOCK_TRAITS = { test_bool_array: [true, true, false], test_string_array: ['test', 'test', 'test'], test_boolean_array: [1, 2, 3], -} as MetaMetricsUserTraits; +}; const MOCK_INVALID_TRAITS = { test_null: null, test_array_multi_types: [true, 'a', 1], -} as MetaMetricsUserTraits; +}; const DEFAULT_TEST_CONTEXT = { app: { @@ -94,19 +74,8 @@ const DEFAULT_PAGE_PROPERTIES = { ...DEFAULT_SHARED_PROPERTIES, }; -const SAMPLE_TX_SUBMITTED_PARTIAL_FRAGMENT = { - id: 'transaction-submitted-0000', - canDeleteIfAbandoned: true, - category: 'Unit Test', - successEvent: 'Transaction Finalized', - persist: true, - properties: { - simulation_response: 'no_balance_change', - test_stored_prop: 1, - }, -}; - -const SAMPLE_PERSISTED_EVENT_NO_ID = { +const SAMPLE_PERSISTED_EVENT = { + id: 'testid', persist: true, category: 'Unit Test', successEvent: 'sample persisted event success', @@ -116,11 +85,6 @@ const SAMPLE_PERSISTED_EVENT_NO_ID = { }, }; -const SAMPLE_PERSISTED_EVENT = { - id: 'testid', - ...SAMPLE_PERSISTED_EVENT_NO_ID, -}; - const SAMPLE_NON_PERSISTED_EVENT = { id: 'testid2', persist: false, @@ -137,7 +101,7 @@ function getMetaMetricsController({ participateInMetaMetrics = true, metaMetricsId = TEST_META_METRICS_ID, marketingCampaignCookieId = null, - currentLocale = LOCALE, + preferencesControllerState = { currentLocale: LOCALE }, onPreferencesStateChange = () => { // do nothing }, @@ -145,26 +109,13 @@ function getMetaMetricsController({ onNetworkDidChange = () => { // do nothing }, - segment = segmentMock, -}: { - currentLocale?: string; - participateInMetaMetrics?: MetaMetricsControllerState['participateInMetaMetrics']; - metaMetricsId?: MetaMetricsControllerState['metaMetricsId']; - dataCollectionForMarketing?: MetaMetricsControllerState['dataCollectionForMarketing']; - marketingCampaignCookieId?: MetaMetricsControllerState['marketingCampaignCookieId']; - onPreferencesStateChange?: MetaMetricsControllerOptions['onPreferencesStateChange']; - getCurrentChainId?: MetaMetricsControllerOptions['getCurrentChainId']; - onNetworkDidChange?: MetaMetricsControllerOptions['onNetworkDidChange']; - segment?: MetaMetricsControllerOptions['segment']; + segmentInstance, } = {}) { return new MetaMetricsController({ - segment, + segment: segmentInstance || segment, getCurrentChainId, onNetworkDidChange, - preferencesControllerState: { - ...getDefaultPreferencesControllerState(), - currentLocale, - }, + preferencesControllerState, onPreferencesStateChange, version: '0.0.1', environment: 'test', @@ -176,6 +127,7 @@ function getMetaMetricsController({ testid: SAMPLE_PERSISTED_EVENT, testid2: SAMPLE_NON_PERSISTED_EVENT, }, + events: {}, }, extension: MOCK_EXTENSION, }); @@ -191,7 +143,7 @@ describe('MetaMetricsController', function () { describe('constructor', function () { it('should properly initialize', function () { - const spy = jest.spyOn(segmentMock, 'track'); + const spy = jest.spyOn(segment, 'track'); const metaMetricsController = getMetaMetricsController(); expect(metaMetricsController.version).toStrictEqual(VERSION); expect(metaMetricsController.chainId).toStrictEqual(FAKE_CHAIN_ID); @@ -228,13 +180,9 @@ describe('MetaMetricsController', function () { }); it('should update when network changes', function () { - let chainId: Hex = '0x111'; - let networkDidChangeListener: (state: NetworkState) => void = () => { - // do nothing - }; - const onNetworkDidChange: ( - listener: (state: NetworkState) => void, - ) => void = (listener) => { + let chainId = '0x111'; + let networkDidChangeListener; + const onNetworkDidChange = (listener) => { networkDidChangeListener = listener; }; const metaMetricsController = getMetaMetricsController({ @@ -243,214 +191,26 @@ describe('MetaMetricsController', function () { }); chainId = '0x222'; - - networkDidChangeListener({} as NetworkState); + networkDidChangeListener(); expect(metaMetricsController.chainId).toStrictEqual('0x222'); }); it('should update when preferences changes', function () { - let subscribeListener: ( - state: PreferencesControllerState, - ) => void = () => { - // do nothing + let subscribeListener; + const onPreferencesStateChange = (listener) => { + subscribeListener = listener; }; - const onPreferencesStateChange: MetaMetricsControllerOptions['onPreferencesStateChange'] = - (listener) => { - subscribeListener = listener; - }; const metaMetricsController = getMetaMetricsController({ - currentLocale: LOCALE, + preferencesControllerState: { currentLocale: LOCALE }, onPreferencesStateChange, }); - subscribeListener({ - ...getDefaultPreferencesControllerState(), - currentLocale: 'en_UK', - }); + subscribeListener({ currentLocale: 'en_UK' }); expect(metaMetricsController.locale).toStrictEqual('en-UK'); }); }); - describe('createEventFragment', function () { - it('should throw an error if the param is missing successEvent or category', async function () { - const metaMetricsController = getMetaMetricsController(); - - await expect(() => { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-expect-error because we are testing the error case - metaMetricsController.createEventFragment({ event: 'test' }); - }).toThrow(/Must specify success event and category\./u); - - await expect(() => { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-expect-error because we are testing the error case - metaMetricsController.createEventFragment({ category: 'test' }); - }).toThrow(/Must specify success event and category\./u); - }); - - it('should update fragments state with new fragment', function () { - jest.useFakeTimers().setSystemTime(1730798301422); - - const metaMetricsController = getMetaMetricsController(); - const mockNewId = 'testid3'; - - metaMetricsController.createEventFragment({ - ...SAMPLE_PERSISTED_EVENT_NO_ID, - uniqueIdentifier: mockNewId, - }); - - const resultFragment = metaMetricsController.state.fragments[mockNewId]; - - expect(resultFragment).toStrictEqual({ - ...SAMPLE_PERSISTED_EVENT_NO_ID, - id: mockNewId, - uniqueIdentifier: mockNewId, - lastUpdated: 1730798301422, - }); - - jest.useRealTimers(); - }); - - it('should track the initial event if provided', function () { - const metaMetricsController = getMetaMetricsController({ - participateInMetaMetrics: true, - }); - const spy = jest.spyOn(segmentMock, 'track'); - const mockInitialEventName = 'Test Initial Event'; - - metaMetricsController.createEventFragment({ - ...SAMPLE_PERSISTED_EVENT_NO_ID, - initialEvent: mockInitialEventName, - }); - - expect(spy).toHaveBeenCalledTimes(1); - }); - - it('should not call track if no initialEvent was provided', function () { - const metaMetricsController = getMetaMetricsController({ - participateInMetaMetrics: true, - }); - const spy = jest.spyOn(segmentMock, 'track'); - - metaMetricsController.createEventFragment({ - ...SAMPLE_PERSISTED_EVENT_NO_ID, - }); - - expect(spy).toHaveBeenCalledTimes(0); - }); - - describe('when intialEvent is "Transaction Submitted" and a fragment exists before createEventFragment is called', function () { - it('should update existing fragment state with new fragment props', function () { - jest.useFakeTimers().setSystemTime(1730798302222); - - const metaMetricsController = getMetaMetricsController(); - const { id } = SAMPLE_TX_SUBMITTED_PARTIAL_FRAGMENT; - - metaMetricsController.updateEventFragment( - SAMPLE_TX_SUBMITTED_PARTIAL_FRAGMENT.id, - { - ...SAMPLE_TX_SUBMITTED_PARTIAL_FRAGMENT, - }, - ); - metaMetricsController.createEventFragment({ - ...SAMPLE_PERSISTED_EVENT_NO_ID, - initialEvent: 'Transaction Submitted', - uniqueIdentifier: id, - }); - - const resultFragment = metaMetricsController.state.fragments[id]; - const expectedFragment = merge( - SAMPLE_TX_SUBMITTED_PARTIAL_FRAGMENT, - SAMPLE_PERSISTED_EVENT_NO_ID, - { - canDeleteIfAbandoned: false, - id, - initialEvent: 'Transaction Submitted', - uniqueIdentifier: id, - lastUpdated: 1730798302222, - }, - ); - - expect(resultFragment).toStrictEqual(expectedFragment); - - jest.useRealTimers(); - }); - }); - }); - - describe('updateEventFragment', function () { - beforeEach(function () { - jest.useFakeTimers().setSystemTime(1730798303333); - }); - afterEach(function () { - jest.useRealTimers(); - }); - - it('updates fragment with additional provided props', async function () { - const metaMetricsController = getMetaMetricsController(); - const MOCK_PROPS_TO_UPDATE = { - properties: { - test: 1, - }, - }; - - metaMetricsController.updateEventFragment( - SAMPLE_PERSISTED_EVENT.id, - MOCK_PROPS_TO_UPDATE, - ); - - const resultFragment = - metaMetricsController.state.fragments[SAMPLE_PERSISTED_EVENT.id]; - const expectedPartialFragment = { - ...SAMPLE_PERSISTED_EVENT, - ...MOCK_PROPS_TO_UPDATE, - lastUpdated: 1730798303333, - }; - expect(resultFragment).toStrictEqual(expectedPartialFragment); - }); - - it('throws error when no existing fragment exists', async function () { - const metaMetricsController = getMetaMetricsController(); - - const MOCK_NONEXISTING_ID = 'test-nonexistingid'; - - await expect(() => { - metaMetricsController.updateEventFragment(MOCK_NONEXISTING_ID, { - properties: { test: 1 }, - }); - }).toThrow(/Event fragment with id test-nonexistingid does not exist\./u); - }); - - describe('when id includes "transaction-submitted"', function () { - it('creates and stores new fragment props with canDeleteIfAbandoned set to true', function () { - const metaMetricsController = getMetaMetricsController(); - const MOCK_ID = 'transaction-submitted-1111'; - const MOCK_PROPS_TO_UPDATE = { - properties: { - test: 1, - }, - }; - - metaMetricsController.updateEventFragment( - MOCK_ID, - MOCK_PROPS_TO_UPDATE, - ); - - const resultFragment = metaMetricsController.state.fragments[MOCK_ID]; - const expectedPartialFragment = { - ...MOCK_PROPS_TO_UPDATE, - category: 'Transactions', - canDeleteIfAbandoned: true, - id: MOCK_ID, - lastUpdated: 1730798303333, - successEvent: 'Transaction Finalized', - }; - expect(resultFragment).toStrictEqual(expectedPartialFragment); - }); - }); - }); - describe('generateMetaMetricsId', function () { it('should generate an 0x prefixed hex string', function () { const metaMetricsController = getMetaMetricsController(); @@ -482,7 +242,7 @@ describe('MetaMetricsController', function () { describe('identify', function () { it('should call segment.identify for valid traits if user is participating in metametrics', function () { - const spy = jest.spyOn(segmentMock, 'identify'); + const spy = jest.spyOn(segment, 'identify'); const metaMetricsController = getMetaMetricsController({ participateInMetaMetrics: true, metaMetricsId: TEST_META_METRICS_ID, @@ -504,14 +264,12 @@ describe('MetaMetricsController', function () { }); it('should transform date type traits into ISO-8601 timestamp strings', function () { - const spy = jest.spyOn(segmentMock, 'identify'); + const spy = jest.spyOn(segment, 'identify'); const metaMetricsController = getMetaMetricsController({ participateInMetaMetrics: true, metaMetricsId: TEST_META_METRICS_ID, }); - metaMetricsController.identify({ - test_date: new Date().toISOString(), - } as MetaMetricsUserTraits); + metaMetricsController.identify({ test_date: new Date().toISOString() }); expect(spy).toHaveBeenCalledTimes(1); expect(spy).toHaveBeenCalledWith( { @@ -527,7 +285,7 @@ describe('MetaMetricsController', function () { }); it('should not call segment.identify if user is not participating in metametrics', function () { - const spy = jest.spyOn(segmentMock, 'identify'); + const spy = jest.spyOn(segment, 'identify'); const metaMetricsController = getMetaMetricsController({ participateInMetaMetrics: false, }); @@ -536,7 +294,7 @@ describe('MetaMetricsController', function () { }); it('should not call segment.identify if there are no valid traits to identify', function () { - const spy = jest.spyOn(segmentMock, 'identify'); + const spy = jest.spyOn(segment, 'identify'); const metaMetricsController = getMetaMetricsController({ participateInMetaMetrics: true, metaMetricsId: TEST_META_METRICS_ID, @@ -601,7 +359,7 @@ describe('MetaMetricsController', function () { describe('submitEvent', function () { it('should not track an event if user is not participating in metametrics', function () { - const spy = jest.spyOn(segmentMock, 'track'); + const spy = jest.spyOn(segment, 'track'); const metaMetricsController = getMetaMetricsController({ participateInMetaMetrics: false, }); @@ -609,7 +367,7 @@ describe('MetaMetricsController', function () { event: 'Fake Event', category: 'Unit Test', properties: { - chain_id: '1', + test: 1, }, }); expect(spy).toHaveBeenCalledTimes(0); @@ -619,13 +377,13 @@ describe('MetaMetricsController', function () { const metaMetricsController = getMetaMetricsController({ participateInMetaMetrics: true, }); - const spy = jest.spyOn(segmentMock, 'track'); + const spy = jest.spyOn(segment, 'track'); metaMetricsController.submitEvent( { event: 'Fake Event', category: 'Unit Test', properties: { - chain_id: '1', + test: 1, }, }, { isOptIn: true }, @@ -637,8 +395,8 @@ describe('MetaMetricsController', function () { anonymousId: METAMETRICS_ANONYMOUS_ID, context: DEFAULT_TEST_CONTEXT, properties: { + test: 1, ...DEFAULT_EVENT_PROPERTIES, - chain_id: '1', }, messageId: Utils.generateRandomId(), timestamp: new Date(), @@ -651,13 +409,13 @@ describe('MetaMetricsController', function () { const metaMetricsController = getMetaMetricsController({ participateInMetaMetrics: true, }); - const spy = jest.spyOn(segmentMock, 'track'); + const spy = jest.spyOn(segment, 'track'); metaMetricsController.submitEvent( { event: 'Fake Event', category: 'Unit Test', properties: { - chain_id: '1', + test: 1, }, }, { isOptIn: true, metaMetricsId: 'TESTID' }, @@ -669,8 +427,8 @@ describe('MetaMetricsController', function () { userId: 'TESTID', context: DEFAULT_TEST_CONTEXT, properties: { + test: 1, ...DEFAULT_EVENT_PROPERTIES, - chain_id: '1', }, messageId: Utils.generateRandomId(), timestamp: new Date(), @@ -681,13 +439,13 @@ describe('MetaMetricsController', function () { it('should track a legacy event', function () { const metaMetricsController = getMetaMetricsController(); - const spy = jest.spyOn(segmentMock, 'track'); + const spy = jest.spyOn(segment, 'track'); metaMetricsController.submitEvent( { event: 'Fake Event', category: 'Unit Test', properties: { - chain_id: '1', + test: 1, }, }, { matomoEvent: true }, @@ -699,9 +457,9 @@ describe('MetaMetricsController', function () { userId: TEST_META_METRICS_ID, context: DEFAULT_TEST_CONTEXT, properties: { - ...DEFAULT_EVENT_PROPERTIES, + test: 1, legacy_event: true, - chain_id: '1', + ...DEFAULT_EVENT_PROPERTIES, }, messageId: Utils.generateRandomId(), timestamp: new Date(), @@ -712,12 +470,12 @@ describe('MetaMetricsController', function () { it('should track a non legacy event', function () { const metaMetricsController = getMetaMetricsController(); - const spy = jest.spyOn(segmentMock, 'track'); + const spy = jest.spyOn(segment, 'track'); metaMetricsController.submitEvent({ event: 'Fake Event', category: 'Unit Test', properties: { - chain_id: '1', + test: 1, }, }); expect(spy).toHaveBeenCalledTimes(1); @@ -725,8 +483,8 @@ describe('MetaMetricsController', function () { { event: 'Fake Event', properties: { + test: 1, ...DEFAULT_EVENT_PROPERTIES, - chain_id: '1', }, context: DEFAULT_TEST_CONTEXT, userId: TEST_META_METRICS_ID, @@ -739,7 +497,7 @@ describe('MetaMetricsController', function () { it('should immediately flush queue if flushImmediately set to true', function () { const metaMetricsController = getMetaMetricsController(); - const spy = jest.spyOn(segmentMock, 'flush'); + const spy = jest.spyOn(segment, 'flush'); metaMetricsController.submitEvent( { event: 'Fake Event', @@ -754,14 +512,10 @@ describe('MetaMetricsController', function () { const metaMetricsController = getMetaMetricsController(); await expect( - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-expect-error because we are testing the error case metaMetricsController.submitEvent({ event: 'test' }), ).rejects.toThrow(/Must specify event and category\./u); await expect( - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-expect-error because we are testing the error case metaMetricsController.submitEvent({ category: 'test' }), ).rejects.toThrow(/Must specify event and category\./u); }); @@ -784,7 +538,7 @@ describe('MetaMetricsController', function () { it('should track sensitiveProperties in a separate, anonymous event', function () { const metaMetricsController = getMetaMetricsController(); - const spy = jest.spyOn(segmentMock, 'track'); + const spy = jest.spyOn(segment, 'track'); metaMetricsController.submitEvent({ event: 'Fake Event', category: 'Unit Test', @@ -820,16 +574,15 @@ describe('MetaMetricsController', function () { }); describe('Change Signature XXX anonymous event names', function () { - // @ts-expect-error This function is missing from the Mocha type definitions it.each([ ['Signature Requested', 'Signature Requested Anon'], ['Signature Rejected', 'Signature Rejected Anon'], ['Signature Approved', 'Signature Approved Anon'], ])( 'should change "%s" anonymous event names to "%s"', - (eventType: string, anonEventType: string) => { + (eventType, anonEventType) => { const metaMetricsController = getMetaMetricsController(); - const spy = jest.spyOn(segmentMock, 'track'); + const spy = jest.spyOn(segment, 'track'); metaMetricsController.submitEvent({ event: eventType, category: 'Unit Test', @@ -855,7 +608,7 @@ describe('MetaMetricsController', function () { describe('Change Transaction XXX anonymous event namnes', function () { it('should change "Transaction Added" anonymous event names to "Transaction Added Anon"', function () { const metaMetricsController = getMetaMetricsController(); - const spy = jest.spyOn(segmentMock, 'track'); + const spy = jest.spyOn(segment, 'track'); metaMetricsController.submitEvent({ event: 'Transaction Added', category: 'Unit Test', @@ -880,7 +633,7 @@ describe('MetaMetricsController', function () { it('should change "Transaction Submitted" anonymous event names to "Transaction Added Anon"', function () { const metaMetricsController = getMetaMetricsController(); - const spy = jest.spyOn(segmentMock, 'track'); + const spy = jest.spyOn(segment, 'track'); metaMetricsController.submitEvent({ event: 'Transaction Submitted', category: 'Unit Test', @@ -905,7 +658,7 @@ describe('MetaMetricsController', function () { it('should change "Transaction Finalized" anonymous event names to "Transaction Added Anon"', function () { const metaMetricsController = getMetaMetricsController(); - const spy = jest.spyOn(segmentMock, 'track'); + const spy = jest.spyOn(segment, 'track'); metaMetricsController.submitEvent({ event: 'Transaction Finalized', category: 'Unit Test', @@ -932,9 +685,10 @@ describe('MetaMetricsController', function () { describe('trackPage', function () { it('should track a page view', function () { const metaMetricsController = getMetaMetricsController(); - const spy = jest.spyOn(segmentMock, 'page'); + const spy = jest.spyOn(segment, 'page'); metaMetricsController.trackPage({ name: 'home', + params: null, environmentType: ENVIRONMENT_TYPE_BACKGROUND, page: METAMETRICS_BACKGROUND_PAGE_OBJECT, }); @@ -945,7 +699,7 @@ describe('MetaMetricsController', function () { userId: TEST_META_METRICS_ID, context: DEFAULT_TEST_CONTEXT, properties: { - params: undefined, + params: null, ...DEFAULT_PAGE_PROPERTIES, }, messageId: Utils.generateRandomId(), @@ -959,9 +713,10 @@ describe('MetaMetricsController', function () { const metaMetricsController = getMetaMetricsController({ participateInMetaMetrics: false, }); - const spy = jest.spyOn(segmentMock, 'page'); + const spy = jest.spyOn(segment, 'page'); metaMetricsController.trackPage({ name: 'home', + params: null, environmentType: ENVIRONMENT_TYPE_BACKGROUND, page: METAMETRICS_BACKGROUND_PAGE_OBJECT, }); @@ -970,14 +725,17 @@ describe('MetaMetricsController', function () { it('should track a page view if isOptInPath is true and user not yet opted in', function () { const metaMetricsController = getMetaMetricsController({ - currentLocale: LOCALE, - participateInMetaMetrics: true, + preferencesControllerState: { + currentLocale: LOCALE, + participateInMetaMetrics: null, + }, onPreferencesStateChange: jest.fn(), }); - const spy = jest.spyOn(segmentMock, 'page'); + const spy = jest.spyOn(segment, 'page'); metaMetricsController.trackPage( { name: 'home', + params: null, environmentType: ENVIRONMENT_TYPE_BACKGROUND, page: METAMETRICS_BACKGROUND_PAGE_OBJECT, }, @@ -991,6 +749,7 @@ describe('MetaMetricsController', function () { userId: TEST_META_METRICS_ID, context: DEFAULT_TEST_CONTEXT, properties: { + params: null, ...DEFAULT_PAGE_PROPERTIES, }, messageId: Utils.generateRandomId(), @@ -1002,14 +761,17 @@ describe('MetaMetricsController', function () { it('multiple trackPage call with same actionId should result in same messageId being sent to segment', function () { const metaMetricsController = getMetaMetricsController({ - currentLocale: LOCALE, - participateInMetaMetrics: true, + preferencesControllerState: { + currentLocale: LOCALE, + participateInMetaMetrics: null, + }, onPreferencesStateChange: jest.fn(), }); - const spy = jest.spyOn(segmentMock, 'page'); + const spy = jest.spyOn(segment, 'page'); metaMetricsController.trackPage( { name: 'home', + params: null, actionId: DUMMY_ACTION_ID, environmentType: ENVIRONMENT_TYPE_BACKGROUND, page: METAMETRICS_BACKGROUND_PAGE_OBJECT, @@ -1019,6 +781,7 @@ describe('MetaMetricsController', function () { metaMetricsController.trackPage( { name: 'home', + params: null, actionId: DUMMY_ACTION_ID, environmentType: ENVIRONMENT_TYPE_BACKGROUND, page: METAMETRICS_BACKGROUND_PAGE_OBJECT, @@ -1032,7 +795,10 @@ describe('MetaMetricsController', function () { name: 'home', userId: TEST_META_METRICS_ID, context: DEFAULT_TEST_CONTEXT, - properties: DEFAULT_PAGE_PROPERTIES, + properties: { + params: null, + ...DEFAULT_PAGE_PROPERTIES, + }, messageId: DUMMY_ACTION_ID, timestamp: new Date(), }, @@ -1044,13 +810,11 @@ describe('MetaMetricsController', function () { describe('deterministic messageId', function () { it('should use the actionId as messageId when provided', function () { const metaMetricsController = getMetaMetricsController(); - const spy = jest.spyOn(segmentMock, 'track'); + const spy = jest.spyOn(segment, 'track'); metaMetricsController.submitEvent({ event: 'Fake Event', category: 'Unit Test', - properties: { - chain_id: 'bar', - }, + properties: { foo: 'bar' }, actionId: '0x001', }); expect(spy).toHaveBeenCalledTimes(1); @@ -1060,8 +824,8 @@ describe('MetaMetricsController', function () { userId: TEST_META_METRICS_ID, context: DEFAULT_TEST_CONTEXT, properties: { + foo: 'bar', ...DEFAULT_EVENT_PROPERTIES, - chain_id: 'bar', }, messageId: '0x001', timestamp: new Date(), @@ -1072,7 +836,7 @@ describe('MetaMetricsController', function () { it('should append 0x000 to the actionId of anonymized event when tracking sensitiveProperties', function () { const metaMetricsController = getMetaMetricsController(); - const spy = jest.spyOn(segmentMock, 'track'); + const spy = jest.spyOn(segment, 'track'); metaMetricsController.submitEvent({ event: 'Fake Event', category: 'Unit Test', @@ -1099,7 +863,9 @@ describe('MetaMetricsController', function () { event: 'Fake Event', userId: TEST_META_METRICS_ID, context: DEFAULT_TEST_CONTEXT, - properties: DEFAULT_EVENT_PROPERTIES, + properties: { + ...DEFAULT_EVENT_PROPERTIES, + }, messageId: '0x001', timestamp: new Date(), }, @@ -1109,13 +875,11 @@ describe('MetaMetricsController', function () { it('should use the uniqueIdentifier as messageId when provided', function () { const metaMetricsController = getMetaMetricsController(); - const spy = jest.spyOn(segmentMock, 'track'); + const spy = jest.spyOn(segment, 'track'); metaMetricsController.submitEvent({ event: 'Fake Event', category: 'Unit Test', - properties: { - chain_id: 'bar', - }, + properties: { foo: 'bar' }, uniqueIdentifier: 'transaction-submitted-0000', }); expect(spy).toHaveBeenCalledTimes(1); @@ -1125,8 +889,8 @@ describe('MetaMetricsController', function () { userId: TEST_META_METRICS_ID, context: DEFAULT_TEST_CONTEXT, properties: { + foo: 'bar', ...DEFAULT_EVENT_PROPERTIES, - chain_id: 'bar', }, messageId: 'transaction-submitted-0000', timestamp: new Date(), @@ -1137,7 +901,7 @@ describe('MetaMetricsController', function () { it('should append 0x000 to the uniqueIdentifier of anonymized event when tracking sensitiveProperties', function () { const metaMetricsController = getMetaMetricsController(); - const spy = jest.spyOn(segmentMock, 'track'); + const spy = jest.spyOn(segment, 'track'); metaMetricsController.submitEvent({ event: 'Fake Event', category: 'Unit Test', @@ -1176,11 +940,11 @@ describe('MetaMetricsController', function () { it('should combine the uniqueIdentifier and actionId as messageId when both provided', function () { const metaMetricsController = getMetaMetricsController(); - const spy = jest.spyOn(segmentMock, 'track'); + const spy = jest.spyOn(segment, 'track'); metaMetricsController.submitEvent({ event: 'Fake Event', category: 'Unit Test', - properties: { chain_id: 'bar' }, + properties: { foo: 'bar' }, actionId: '0x001', uniqueIdentifier: 'transaction-submitted-0000', }); @@ -1191,8 +955,8 @@ describe('MetaMetricsController', function () { userId: TEST_META_METRICS_ID, context: DEFAULT_TEST_CONTEXT, properties: { + foo: 'bar', ...DEFAULT_EVENT_PROPERTIES, - chain_id: 'bar', }, messageId: 'transaction-submitted-0000-0x001', timestamp: new Date(), @@ -1203,7 +967,7 @@ describe('MetaMetricsController', function () { it('should append 0x000 to the combined uniqueIdentifier and actionId of anonymized event when tracking sensitiveProperties', function () { const metaMetricsController = getMetaMetricsController(); - const spy = jest.spyOn(segmentMock, 'track'); + const spy = jest.spyOn(segment, 'track'); metaMetricsController.submitEvent({ event: 'Fake Event', category: 'Unit Test', @@ -1244,7 +1008,7 @@ describe('MetaMetricsController', function () { describe('_buildUserTraitsObject', function () { it('should return full user traits object on first call', function () { - const MOCK_ALL_TOKENS: TokensControllerState['allTokens'] = { + const MOCK_ALL_TOKENS = { [toHex(1)]: { '0x1235ce91d74254f29d4609f25932fe6d97bf4842': [ { @@ -1253,12 +1017,12 @@ describe('MetaMetricsController', function () { { address: '0xabc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9', }, - ] as Token[], + ], '0xe364b0f9d1879e53e8183055c9d7dd2b7375d86b': [ { address: '0xd2cea331e5f5d8ee9fb1055c297795937645de91', }, - ] as Token[], + ], }, [toHex(4)]: { '0x1235ce91d74254f29d4609f25932fe6d97bf4842': [ @@ -1268,26 +1032,15 @@ describe('MetaMetricsController', function () { { address: '0x12317F958D2ee523a2206206994597C13D831ec7', }, - ] as Token[], + ], }, }; const metaMetricsController = getMetaMetricsController(); const traits = metaMetricsController._buildUserTraitsObject({ addressBook: { - [CHAIN_IDS.MAINNET]: { - '0x': { - address: '0x', - } as AddressBookEntry, - }, - [CHAIN_IDS.GOERLI]: { - '0x': { - address: '0x', - } as AddressBookEntry, - '0x0': { - address: '0x0', - } as AddressBookEntry, - }, + [CHAIN_IDS.MAINNET]: [{ address: '0x' }], + [CHAIN_IDS.GOERLI]: [{ address: '0x' }, { address: '0x0' }], }, allNfts: { '0xac706cE8A9BF27Afecf080fB298d0ee13cfb978A': { @@ -1304,7 +1057,7 @@ describe('MetaMetricsController', function () { address: '0x7488d2ce5deb26db021285b50b661d655eb3d3d9', tokenId: '99', }, - ] as Nft[], + ], }, '0xe04AB39684A24D8D4124b114F3bd6FBEB779cacA': { [toHex(59)]: [ @@ -1312,7 +1065,7 @@ describe('MetaMetricsController', function () { address: '0x63d646bc7380562376d5de205123a57b1718184d', tokenId: '14', }, - ] as Nft[], + ], }, }, allTokens: MOCK_ALL_TOKENS, @@ -1323,41 +1076,48 @@ describe('MetaMetricsController', function () { ), internalAccounts: { accounts: { - mock1: {} as InternalAccount, - mock2: {} as InternalAccount, + mock1: {}, + mock2: {}, }, - selectedAccount: 'mock1', }, - ledgerTransportType: LedgerTransportTypes.webhid, + identities: [{}, {}], + ledgerTransportType: 'web-hid', openSeaEnabled: true, useNftDetection: false, securityAlertsEnabled: true, theme: 'default', useTokenDetection: true, - ShowNativeTokenAsMainBalance: true, + showNativeTokenAsMainBalance: true, security_providers: [], names: { [NameType.ETHEREUM_ADDRESS]: { '0x123': { '0x1': { name: 'Test 1', - } as NameEntry, + }, '0x2': { name: 'Test 2', - } as NameEntry, + }, '0x3': { name: null, - } as NameEntry, + }, }, '0x456': { '0x1': { name: 'Test 3', - } as NameEntry, + }, }, '0x789': { '0x1': { name: null, - } as NameEntry, + }, + }, + }, + otherType: { + otherValue: { + otherVariation: { + name: 'Test 4', + }, }, }, }, @@ -1366,19 +1126,12 @@ describe('MetaMetricsController', function () { order: 'dsc', sortCallback: 'stringNumeric', }, - participateInMetaMetrics: true, - currentCurrency: 'usd', - dataCollectionForMarketing: false, - ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) - custodyAccountDetails: {}, - ///: END:ONLY_INCLUDE_IF }); expect(traits).toStrictEqual({ [MetaMetricsUserTrait.AddressBookEntries]: 3, [MetaMetricsUserTrait.InstallDateExt]: '', - [MetaMetricsUserTrait.LedgerConnectionType]: - LedgerTransportTypes.webhid, + [MetaMetricsUserTrait.LedgerConnectionType]: 'web-hid', [MetaMetricsUserTrait.NetworksAdded]: [ CHAIN_IDS.MAINNET, CHAIN_IDS.GOERLI, @@ -1390,15 +1143,12 @@ describe('MetaMetricsController', function () { [MetaMetricsUserTrait.NumberOfNftCollections]: 3, [MetaMetricsUserTrait.NumberOfNfts]: 4, [MetaMetricsUserTrait.NumberOfTokens]: 5, - [MetaMetricsUserTrait.OpenSeaApiEnabled]: true, + [MetaMetricsUserTrait.OpenseaApiEnabled]: true, [MetaMetricsUserTrait.ThreeBoxEnabled]: false, [MetaMetricsUserTrait.Theme]: 'default', [MetaMetricsUserTrait.TokenDetectionEnabled]: true, [MetaMetricsUserTrait.ShowNativeTokenAsMainBalance]: true, - [MetaMetricsUserTrait.CurrentCurrency]: 'usd', - [MetaMetricsUserTrait.HasMarketingConsent]: false, [MetaMetricsUserTrait.SecurityProviders]: ['blockaid'], - [MetaMetricsUserTrait.IsMetricsOptedIn]: true, ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) [MetaMetricsUserTrait.MmiExtensionId]: 'testid', [MetaMetricsUserTrait.MmiAccountAddress]: null, @@ -1419,31 +1169,20 @@ describe('MetaMetricsController', function () { ); metaMetricsController._buildUserTraitsObject({ addressBook: { - [CHAIN_IDS.MAINNET]: { - '0x': { - address: '0x', - } as AddressBookEntry, - }, - [CHAIN_IDS.GOERLI]: { - '0x': { - address: '0x', - } as AddressBookEntry, - '0x0': { - address: '0x0', - } as AddressBookEntry, - }, + [CHAIN_IDS.MAINNET]: [{ address: '0x' }], + [CHAIN_IDS.GOERLI]: [{ address: '0x' }, { address: '0x0' }], }, allTokens: {}, ...networkState, - ledgerTransportType: LedgerTransportTypes.webhid, + ledgerTransportType: 'web-hid', openSeaEnabled: true, internalAccounts: { accounts: { - mock1: {} as InternalAccount, - mock2: {} as InternalAccount, + mock1: {}, + mock2: {}, }, - selectedAccount: 'mock1', }, + identities: [{}, {}], useNftDetection: false, theme: 'default', useTokenDetection: true, @@ -1452,56 +1191,30 @@ describe('MetaMetricsController', function () { order: 'dsc', sortCallback: 'stringNumeric', }, - ShowNativeTokenAsMainBalance: true, - allNfts: {}, - participateInMetaMetrics: true, - dataCollectionForMarketing: false, - securityAlertsEnabled: true, - names: { - ethereumAddress: {}, - }, - security_providers: ['blockaid'], - currentCurrency: 'usd', - ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) - custodyAccountDetails: {}, - ///: END:ONLY_INCLUDE_IF + showNativeTokenAsMainBalance: true, }); const updatedTraits = metaMetricsController._buildUserTraitsObject({ addressBook: { - [CHAIN_IDS.MAINNET]: { - '0x': { - address: '0x', - } as AddressBookEntry, - '0x1': { - address: '0x1', - } as AddressBookEntry, - }, - [CHAIN_IDS.GOERLI]: { - '0x': { - address: '0x', - } as AddressBookEntry, - '0x0': { - address: '0x0', - } as AddressBookEntry, - }, + [CHAIN_IDS.MAINNET]: [{ address: '0x' }, { address: '0x1' }], + [CHAIN_IDS.GOERLI]: [{ address: '0x' }, { address: '0x0' }], }, allTokens: { [toHex(1)]: { - '0xabcde': [{ address: '0xtestAddress' } as Token], + '0xabcde': [{ '0x12345': { address: '0xtestAddress' } }], }, }, ...networkState, - ledgerTransportType: LedgerTransportTypes.webhid, + ledgerTransportType: 'web-hid', openSeaEnabled: false, internalAccounts: { accounts: { - mock1: {} as InternalAccount, - mock2: {} as InternalAccount, - mock3: {} as InternalAccount, + mock1: {}, + mock2: {}, + mock3: {}, }, - selectedAccount: 'mock1', }, + identities: [{}, {}, {}], useNftDetection: false, theme: 'default', useTokenDetection: true, @@ -1510,26 +1223,14 @@ describe('MetaMetricsController', function () { order: 'dsc', sortCallback: 'stringNumeric', }, - ShowNativeTokenAsMainBalance: false, - names: { - ethereumAddress: {}, - }, - security_providers: ['blockaid'], - currentCurrency: 'usd', - allNfts: {}, - participateInMetaMetrics: true, - dataCollectionForMarketing: false, - securityAlertsEnabled: true, - ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) - custodyAccountDetails: {}, - ///: END:ONLY_INCLUDE_IF + showNativeTokenAsMainBalance: false, }); expect(updatedTraits).toStrictEqual({ [MetaMetricsUserTrait.AddressBookEntries]: 4, [MetaMetricsUserTrait.NumberOfAccounts]: 3, [MetaMetricsUserTrait.NumberOfTokens]: 1, - [MetaMetricsUserTrait.OpenSeaApiEnabled]: false, + [MetaMetricsUserTrait.OpenseaApiEnabled]: false, [MetaMetricsUserTrait.ShowNativeTokenAsMainBalance]: false, }); }); @@ -1542,31 +1243,20 @@ describe('MetaMetricsController', function () { ); metaMetricsController._buildUserTraitsObject({ addressBook: { - [CHAIN_IDS.MAINNET]: { - '0x': { - address: '0x', - } as AddressBookEntry, - }, - [CHAIN_IDS.GOERLI]: { - '0x': { - address: '0x', - } as AddressBookEntry, - '0x0': { - address: '0x0', - } as AddressBookEntry, - }, + [CHAIN_IDS.MAINNET]: [{ address: '0x' }], + [CHAIN_IDS.GOERLI]: [{ address: '0x' }, { address: '0x0' }], }, allTokens: {}, ...networkState, - ledgerTransportType: LedgerTransportTypes.webhid, + ledgerTransportType: 'web-hid', openSeaEnabled: true, internalAccounts: { accounts: { - mock1: {} as InternalAccount, - mock2: {} as InternalAccount, + mock1: {}, + mock2: {}, }, - selectedAccount: 'mock1', }, + identities: [{}, {}], useNftDetection: true, theme: 'default', useTokenDetection: true, @@ -1575,46 +1265,25 @@ describe('MetaMetricsController', function () { order: 'dsc', sortCallback: 'stringNumeric', }, - ShowNativeTokenAsMainBalance: true, - allNfts: {}, - names: { - ethereumAddress: {}, - }, - participateInMetaMetrics: true, - dataCollectionForMarketing: false, - securityAlertsEnabled: true, - security_providers: ['blockaid'], - currentCurrency: 'usd', - ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) - custodyAccountDetails: {}, - ///: END:ONLY_INCLUDE_IF + showNativeTokenAsMainBalance: true, }); const updatedTraits = metaMetricsController._buildUserTraitsObject({ addressBook: { - [CHAIN_IDS.MAINNET]: { - '0x': { - address: '0x', - } as AddressBookEntry, - }, - [CHAIN_IDS.GOERLI]: { - '0x': { - address: '0x', - } as AddressBookEntry, - '0x0': { address: '0x0' } as AddressBookEntry, - }, + [CHAIN_IDS.MAINNET]: [{ address: '0x' }], + [CHAIN_IDS.GOERLI]: [{ address: '0x' }, { address: '0x0' }], }, allTokens: {}, ...networkState, - ledgerTransportType: LedgerTransportTypes.webhid, + ledgerTransportType: 'web-hid', openSeaEnabled: true, internalAccounts: { accounts: { - mock1: {} as InternalAccount, - mock2: {} as InternalAccount, + mock1: {}, + mock2: {}, }, - selectedAccount: 'mock1', }, + identities: [{}, {}], useNftDetection: true, theme: 'default', useTokenDetection: true, @@ -1623,19 +1292,7 @@ describe('MetaMetricsController', function () { order: 'dsc', sortCallback: 'stringNumeric', }, - ShowNativeTokenAsMainBalance: true, - allNfts: {}, - participateInMetaMetrics: true, - dataCollectionForMarketing: false, - names: { - ethereumAddress: {}, - }, - securityAlertsEnabled: true, - security_providers: ['blockaid'], - currentCurrency: 'usd', - ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) - custodyAccountDetails: {}, - ///: END:ONLY_INCLUDE_IF + showNativeTokenAsMainBalance: true, }); expect(updatedTraits).toStrictEqual(null); }); @@ -1644,24 +1301,23 @@ describe('MetaMetricsController', function () { describe('submitting segmentApiCalls to segment SDK', function () { it('should add event to store when submitting to SDK', function () { const metaMetricsController = getMetaMetricsController({}); - metaMetricsController.trackPage({}, { isOptInPath: true }); + metaMetricsController.trackPage({}, { isOptIn: true }); const { segmentApiCalls } = metaMetricsController.store.getState(); expect(Object.keys(segmentApiCalls).length > 0).toStrictEqual(true); }); it('should remove event from store when callback is invoked', function () { - const segmentInstance = createSegmentMock(2); - const stubFn = (...args: unknown[]) => { - const cb = args[1] as () => void; + const segmentInstance = createSegmentMock(2, 10000); + const stubFn = (_, cb) => { cb(); }; jest.spyOn(segmentInstance, 'track').mockImplementation(stubFn); jest.spyOn(segmentInstance, 'page').mockImplementation(stubFn); const metaMetricsController = getMetaMetricsController({ - segment: segmentInstance, + segmentInstance, }); - metaMetricsController.trackPage({}, { isOptInPath: true }); + metaMetricsController.trackPage({}, { isOptIn: true }); const { segmentApiCalls } = metaMetricsController.store.getState(); expect(Object.keys(segmentApiCalls).length === 0).toStrictEqual(true); }); @@ -1677,13 +1333,13 @@ describe('MetaMetricsController', function () { expect( metaMetricsController.state.marketingCampaignCookieId, ).toStrictEqual(TEST_GA_COOKIE_ID); - const spy = jest.spyOn(segmentMock, 'track'); + const spy = jest.spyOn(segment, 'track'); metaMetricsController.submitEvent( { event: 'Fake Event', category: 'Unit Test', properties: { - chain_id: '1', + test: 1, }, }, { isOptIn: true }, @@ -1698,8 +1354,8 @@ describe('MetaMetricsController', function () { marketingCampaignCookieId: TEST_GA_COOKIE_ID, }, properties: { + test: 1, ...DEFAULT_EVENT_PROPERTIES, - chain_id: '1', }, messageId: Utils.generateRandomId(), timestamp: new Date(), @@ -1727,7 +1383,7 @@ describe('MetaMetricsController', function () { }); afterEach(function () { // flush the queues manually after each test - segmentMock.flush(); + segment.flush(); jest.useRealTimers(); jest.restoreAllMocks(); }); diff --git a/app/scripts/controllers/mmi-controller.test.ts b/app/scripts/controllers/mmi-controller.test.ts index 64bc46132724..7fb87c6d143b 100644 --- a/app/scripts/controllers/mmi-controller.test.ts +++ b/app/scripts/controllers/mmi-controller.test.ts @@ -237,6 +237,8 @@ describe('MMIController', function () { messenger: mockMessenger, }), isEthSignEnabled: jest.fn(), + getAllState: jest.fn(), + getCurrentChainId: jest.fn(), }), appStateController: new AppStateController({ addUnlockListener: jest.fn(), diff --git a/app/scripts/controllers/preferences-controller.test.ts b/app/scripts/controllers/preferences-controller.test.ts index 74daf39e17ad..9c28ed7c43a0 100644 --- a/app/scripts/controllers/preferences-controller.test.ts +++ b/app/scripts/controllers/preferences-controller.test.ts @@ -730,7 +730,6 @@ describe('preferences controller', () => { expect(controller.state.preferences).toStrictEqual({ autoLockTimeLimit: undefined, showExtensionInFullSizeView: false, - privacyMode: false, showFiatInTestnets: false, showTestNetworks: false, smartTransactionsOptInStatus: null, @@ -750,7 +749,6 @@ describe('preferences controller', () => { order: 'dsc', sortCallback: 'stringNumeric', }, - tokenNetworkFilter: {}, }); }); @@ -766,7 +764,6 @@ describe('preferences controller', () => { useNativeCurrencyAsPrimaryCurrency: true, hideZeroBalanceTokens: false, petnamesEnabled: true, - privacyMode: false, redesignedConfirmationsEnabled: true, redesignedTransactionsEnabled: true, shouldShowAggregatedBalancePopover: true, @@ -780,7 +777,6 @@ describe('preferences controller', () => { order: 'dsc', sortCallback: 'stringNumeric', }, - tokenNetworkFilter: {}, }); }); }); diff --git a/app/scripts/controllers/preferences-controller.ts b/app/scripts/controllers/preferences-controller.ts index f6537952d651..536ec33b34eb 100644 --- a/app/scripts/controllers/preferences-controller.ts +++ b/app/scripts/controllers/preferences-controller.ts @@ -112,7 +112,6 @@ export type Preferences = { redesignedTransactionsEnabled: boolean; featureNotificationsEnabled: boolean; showMultiRpcModal: boolean; - privacyMode: boolean; isRedesignedConfirmationsDeveloperEnabled: boolean; showConfirmationAdvancedDetails: boolean; tokenSortConfig: { @@ -120,7 +119,6 @@ export type Preferences = { order: string; sortCallback: string; }; - tokenNetworkFilter: Record; shouldShowAggregatedBalancePopover: boolean; }; @@ -216,14 +214,12 @@ export const getDefaultPreferencesControllerState = isRedesignedConfirmationsDeveloperEnabled: false, showConfirmationAdvancedDetails: false, showMultiRpcModal: false, - privacyMode: false, shouldShowAggregatedBalancePopover: true, // by default user should see popover; tokenSortConfig: { key: 'tokenFiatAmount', order: 'dsc', sortCallback: 'stringNumeric', }, - tokenNetworkFilter: {}, }, // ENS decentralized website resolution ipfsGateway: IPFS_DEFAULT_GATEWAY_URL, diff --git a/app/scripts/fixtures/with-preferences.js b/app/scripts/fixtures/with-preferences.js index c3a482ef8f94..8d1e4293e8a4 100644 --- a/app/scripts/fixtures/with-preferences.js +++ b/app/scripts/fixtures/with-preferences.js @@ -13,7 +13,6 @@ export const FIXTURES_PREFERENCES = { showNftAutodetectModal: false, isRedesignedConfirmationsDeveloperEnabled: false, showConfirmationAdvancedDetails: false, - privacyMode: false, }, featureFlags: { sendHexData: true, diff --git a/app/scripts/lib/createRPCMethodTrackingMiddleware.js b/app/scripts/lib/createRPCMethodTrackingMiddleware.js index cb57c681649f..a1c5a036f13f 100644 --- a/app/scripts/lib/createRPCMethodTrackingMiddleware.js +++ b/app/scripts/lib/createRPCMethodTrackingMiddleware.js @@ -53,8 +53,6 @@ const RATE_LIMIT_MAP = { [MESSAGE_TYPE.ETH_DECRYPT]: RATE_LIMIT_TYPES.NON_RATE_LIMITED, [MESSAGE_TYPE.ETH_GET_ENCRYPTION_PUBLIC_KEY]: RATE_LIMIT_TYPES.NON_RATE_LIMITED, - [MESSAGE_TYPE.ADD_ETHEREUM_CHAIN]: RATE_LIMIT_TYPES.NON_RATE_LIMITED, - [MESSAGE_TYPE.SWITCH_ETHEREUM_CHAIN]: RATE_LIMIT_TYPES.NON_RATE_LIMITED, [MESSAGE_TYPE.ETH_REQUEST_ACCOUNTS]: RATE_LIMIT_TYPES.TIMEOUT, [MESSAGE_TYPE.WALLET_REQUEST_PERMISSIONS]: RATE_LIMIT_TYPES.TIMEOUT, [MESSAGE_TYPE.SEND_METADATA]: RATE_LIMIT_TYPES.BLOCKED, @@ -128,8 +126,6 @@ const EVENT_NAME_MAP = { */ const TRANSFORM_PARAMS_MAP = { [MESSAGE_TYPE.WATCH_ASSET]: ({ type }) => ({ type }), - [MESSAGE_TYPE.ADD_ETHEREUM_CHAIN]: ([{ chainId }]) => ({ chainId }), - [MESSAGE_TYPE.SWITCH_ETHEREUM_CHAIN]: ([{ chainId }]) => ({ chainId }), }; const rateLimitTimeoutsByMethod = {}; diff --git a/app/scripts/lib/createRPCMethodTrackingMiddleware.test.js b/app/scripts/lib/createRPCMethodTrackingMiddleware.test.js index 244a995bf5f7..01daaf2974a4 100644 --- a/app/scripts/lib/createRPCMethodTrackingMiddleware.test.js +++ b/app/scripts/lib/createRPCMethodTrackingMiddleware.test.js @@ -883,34 +883,6 @@ describe('createRPCMethodTrackingMiddleware', () => { }, { type: 'ERC20' }, ], - [ - 'only the chain ID', - 'wallet_addEthereumChain', - [ - { - chainId: '0x64', - chainName: 'Gnosis', - rpcUrls: ['https://rpc.gnosischain.com'], - iconUrls: [ - 'https://xdaichain.com/fake/example/url/xdai.svg', - 'https://xdaichain.com/fake/example/url/xdai.png', - ], - nativeCurrency: { - name: 'XDAI', - symbol: 'XDAI', - decimals: 18, - }, - blockExplorerUrls: ['https://blockscout.com/poa/xdai/'], - }, - ], - { chainId: '0x64' }, - ], - [ - 'only the chain ID', - 'wallet_switchEthereumChain', - [{ chainId: '0x123' }], - { chainId: '0x123' }, - ], ])( `should include %s in the '%s' tracked events params property`, async (_, method, params, expected) => { diff --git a/app/scripts/lib/ppom/ppom-middleware.test.ts b/app/scripts/lib/ppom/ppom-middleware.test.ts index 16317fef0380..8977c00aa3d7 100644 --- a/app/scripts/lib/ppom/ppom-middleware.test.ts +++ b/app/scripts/lib/ppom/ppom-middleware.test.ts @@ -7,6 +7,7 @@ import { BlockaidReason, BlockaidResultType, } from '../../../../shared/constants/security-provider'; +import { flushPromises } from '../../../../test/lib/timer-helpers'; import { mockNetworkState } from '../../../../test/stub/networks'; import { createPPOMMiddleware, PPOMMiddlewareRequest } from './ppom-middleware'; import { @@ -104,6 +105,7 @@ const createMiddleware = ( }; describe('PPOMMiddleware', () => { + const validateRequestWithPPOMMock = jest.mocked(validateRequestWithPPOM); const generateSecurityAlertIdMock = jest.mocked(generateSecurityAlertId); const handlePPOMErrorMock = jest.mocked(handlePPOMError); const isChainSupportedMock = jest.mocked(isChainSupported); @@ -112,6 +114,7 @@ describe('PPOMMiddleware', () => { beforeEach(() => { jest.resetAllMocks(); + validateRequestWithPPOMMock.mockResolvedValue(SECURITY_ALERT_RESPONSE_MOCK); generateSecurityAlertIdMock.mockReturnValue(SECURITY_ALERT_ID_MOCK); handlePPOMErrorMock.mockReturnValue(SECURITY_ALERT_RESPONSE_MOCK); isChainSupportedMock.mockResolvedValue(true); @@ -126,14 +129,14 @@ describe('PPOMMiddleware', () => { }; }); - it('adds checking chain response to confirmation requests while validation is in progress', async () => { + it('updates alert response after validating request', async () => { const updateSecurityAlertResponse = jest.fn(); const middlewareFunction = createMiddleware({ updateSecurityAlertResponse, }); - const req: PPOMMiddlewareRequest<(string | { to: string })[]> = { + const req = { ...REQUEST_MOCK, method: 'eth_sendTransaction', securityAlertResponse: undefined, @@ -145,9 +148,32 @@ describe('PPOMMiddleware', () => { () => undefined, ); - expect(req.securityAlertResponse?.reason).toBe( - BlockaidReason.checkingChain, + await flushPromises(); + + expect(updateSecurityAlertResponse).toHaveBeenCalledTimes(1); + expect(updateSecurityAlertResponse).toHaveBeenCalledWith( + req.method, + SECURITY_ALERT_ID_MOCK, + SECURITY_ALERT_RESPONSE_MOCK, ); + }); + + it('adds loading response to confirmation requests while validation is in progress', async () => { + const middlewareFunction = createMiddleware(); + + const req: PPOMMiddlewareRequest<(string | { to: string })[]> = { + ...REQUEST_MOCK, + method: 'eth_sendTransaction', + securityAlertResponse: undefined, + }; + + await middlewareFunction( + req, + { ...JsonRpcResponseStruct.TYPE }, + () => undefined, + ); + + expect(req.securityAlertResponse?.reason).toBe(BlockaidReason.inProgress); expect(req.securityAlertResponse?.result_type).toBe( BlockaidResultType.Loading, ); @@ -171,6 +197,50 @@ describe('PPOMMiddleware', () => { expect(validateRequestWithPPOM).not.toHaveBeenCalled(); }); + it('does not do validation if unable to get the chainId from the network provider config', async () => { + isChainSupportedMock.mockResolvedValue(false); + const middlewareFunction = createMiddleware({ + chainId: null, + }); + + const req = { + ...REQUEST_MOCK, + method: 'eth_sendTransaction', + securityAlertResponse: undefined, + }; + + await middlewareFunction( + req, + { ...JsonRpcResponseStruct.TYPE }, + () => undefined, + ); + + expect(req.securityAlertResponse).toBeUndefined(); + expect(validateRequestWithPPOM).not.toHaveBeenCalled(); + }); + + it('does not do validation if user is not on a supported network', async () => { + isChainSupportedMock.mockResolvedValue(false); + const middlewareFunction = createMiddleware({ + chainId: '0x2', + }); + + const req = { + ...REQUEST_MOCK, + method: 'eth_sendTransaction', + securityAlertResponse: undefined, + }; + + await middlewareFunction( + req, + { ...JsonRpcResponseStruct.TYPE }, + () => undefined, + ); + + expect(req.securityAlertResponse).toBeUndefined(); + expect(validateRequestWithPPOM).not.toHaveBeenCalled(); + }); + it('does not do validation when request is not for confirmation method', async () => { const middlewareFunction = createMiddleware(); diff --git a/app/scripts/lib/ppom/ppom-middleware.ts b/app/scripts/lib/ppom/ppom-middleware.ts index 44c7a8a965c4..7eb8dc0cc5a2 100644 --- a/app/scripts/lib/ppom/ppom-middleware.ts +++ b/app/scripts/lib/ppom/ppom-middleware.ts @@ -13,16 +13,17 @@ import { MESSAGE_TYPE } from '../../../../shared/constants/app'; import { SIGNING_METHODS } from '../../../../shared/constants/transaction'; import { PreferencesController } from '../../controllers/preferences-controller'; import { AppStateController } from '../../controllers/app-state-controller'; -import { SECURITY_ALERT_RESPONSE_CHECKING_CHAIN } from '../../../../shared/constants/security-provider'; +import { LOADING_SECURITY_ALERT_RESPONSE } from '../../../../shared/constants/security-provider'; // eslint-disable-next-line import/no-restricted-paths import { getProviderConfig } from '../../../../ui/ducks/metamask/metamask'; import { trace, TraceContext, TraceName } from '../../../../shared/lib/trace'; import { generateSecurityAlertId, handlePPOMError, + isChainSupported, validateRequestWithPPOM, } from './ppom-util'; -import { SecurityAlertResponse, UpdateSecurityAlertResponse } from './types'; +import { SecurityAlertResponse } from './types'; const CONFIRMATION_METHODS = Object.freeze([ 'eth_sendRawTransaction', @@ -63,7 +64,11 @@ export function createPPOMMiddleware< networkController: NetworkController, appStateController: AppStateController, accountsController: AccountsController, - updateSecurityAlertResponse: UpdateSecurityAlertResponse, + updateSecurityAlertResponse: ( + method: string, + signatureAlertId: string, + securityAlertResponse: SecurityAlertResponse, + ) => void, ) { return async ( req: PPOMMiddlewareRequest, @@ -81,9 +86,12 @@ export function createPPOMMiddleware< return; } + const isSupportedChain = await isChainSupported(chainId); + if ( !securityAlertsEnabled || - !CONFIRMATION_METHODS.includes(req.method) + !CONFIRMATION_METHODS.includes(req.method) || + !isSupportedChain ) { return; } @@ -115,22 +123,27 @@ export function createPPOMMiddleware< request: req, securityAlertId, chainId, - updateSecurityAlertResponse, + }).then((securityAlertResponse) => { + updateSecurityAlertResponse( + req.method, + securityAlertId, + securityAlertResponse, + ); }), ); - const securityAlertResponseCheckingChain: SecurityAlertResponse = { - ...SECURITY_ALERT_RESPONSE_CHECKING_CHAIN, + const loadingSecurityAlertResponse: SecurityAlertResponse = { + ...LOADING_SECURITY_ALERT_RESPONSE, securityAlertId, }; if (SIGNING_METHODS.includes(req.method)) { appStateController.addSignatureSecurityAlertResponse( - securityAlertResponseCheckingChain, + loadingSecurityAlertResponse, ); } - req.securityAlertResponse = securityAlertResponseCheckingChain; + req.securityAlertResponse = loadingSecurityAlertResponse; } catch (error) { req.securityAlertResponse = handlePPOMError( error, diff --git a/app/scripts/lib/ppom/ppom-util.test.ts b/app/scripts/lib/ppom/ppom-util.test.ts index 8acb6dd9788c..ea62c3b88533 100644 --- a/app/scripts/lib/ppom/ppom-util.test.ts +++ b/app/scripts/lib/ppom/ppom-util.test.ts @@ -10,12 +10,9 @@ import { SignatureController, SignatureRequest, } from '@metamask/signature-controller'; -import { Hex } from '@metamask/utils'; import { BlockaidReason, BlockaidResultType, - LOADING_SECURITY_ALERT_RESPONSE, - SECURITY_ALERT_RESPONSE_CHAIN_NOT_SUPPORTED, SecurityAlertSource, } from '../../../../shared/constants/security-provider'; import { AppStateController } from '../../controllers/app-state-controller'; @@ -35,7 +32,7 @@ jest.mock('@metamask/transaction-controller', () => ({ const SECURITY_ALERT_ID_MOCK = '1234-5678'; const TRANSACTION_ID_MOCK = '123'; -const CHAIN_ID_MOCK = '0x1' as Hex; +const CHAIN_ID_MOCK = '0x1'; const REQUEST_MOCK = { method: 'eth_signTypedData_v4', @@ -48,7 +45,6 @@ const SECURITY_ALERT_RESPONSE_MOCK: SecurityAlertResponse = { result_type: 'success', reason: 'success', source: SecurityAlertSource.Local, - securityAlertId: SECURITY_ALERT_ID_MOCK, }; const TRANSACTION_PARAMS_MOCK_1: TransactionParams = { @@ -114,15 +110,6 @@ describe('PPOM Utils', () => { ); let isSecurityAlertsEnabledMock: jest.SpyInstance; - const updateSecurityAlertResponseMock = jest.fn(); - - const validateRequestWithPPOMOptionsBase = { - request: REQUEST_MOCK, - securityAlertId: SECURITY_ALERT_ID_MOCK, - chainId: CHAIN_ID_MOCK, - updateSecurityAlertResponse: updateSecurityAlertResponseMock, - }; - beforeEach(() => { jest.resetAllMocks(); jest.spyOn(console, 'error').mockImplementation(() => undefined); @@ -132,7 +119,7 @@ describe('PPOM Utils', () => { }); describe('validateRequestWithPPOM', () => { - it('updates response from validation with PPOM instance via controller', async () => { + it('returns response from validation with PPOM instance via controller', async () => { const ppom = createPPOMMock(); const ppomController = createPPOMControllerMock(); @@ -142,39 +129,23 @@ describe('PPOM Utils', () => { (callback) => callback(ppom as any) as any, ); - await validateRequestWithPPOM({ - ...validateRequestWithPPOMOptionsBase, + const response = await validateRequestWithPPOM({ ppomController, + request: REQUEST_MOCK, + securityAlertId: SECURITY_ALERT_ID_MOCK, + chainId: CHAIN_ID_MOCK, }); - expect(updateSecurityAlertResponseMock).toHaveBeenCalledWith( - REQUEST_MOCK.method, - SECURITY_ALERT_ID_MOCK, - { - ...SECURITY_ALERT_RESPONSE_MOCK, - securityAlertId: SECURITY_ALERT_ID_MOCK, - }, - ); - - expect(ppom.validateJsonRpc).toHaveBeenCalledTimes(1); - expect(ppom.validateJsonRpc).toHaveBeenCalledWith(REQUEST_MOCK); - }); - it('updates securityAlertResponse with loading state', async () => { - const ppomController = createPPOMControllerMock(); - - await validateRequestWithPPOM({ - ...validateRequestWithPPOMOptionsBase, - ppomController, + expect(response).toStrictEqual({ + ...SECURITY_ALERT_RESPONSE_MOCK, + securityAlertId: SECURITY_ALERT_ID_MOCK, }); - expect(updateSecurityAlertResponseMock).toHaveBeenCalledWith( - REQUEST_MOCK.method, - SECURITY_ALERT_ID_MOCK, - LOADING_SECURITY_ALERT_RESPONSE, - ); + expect(ppom.validateJsonRpc).toHaveBeenCalledTimes(1); + expect(ppom.validateJsonRpc).toHaveBeenCalledWith(REQUEST_MOCK); }); - it('updates error response if validation with PPOM instance throws', async () => { + it('returns error response if validation with PPOM instance throws', async () => { const ppom = createPPOMMock(); const ppomController = createPPOMControllerMock(); @@ -186,41 +157,37 @@ describe('PPOM Utils', () => { callback(ppom as any) as any, ); - await validateRequestWithPPOM({ - ...validateRequestWithPPOMOptionsBase, + const response = await validateRequestWithPPOM({ ppomController, + request: REQUEST_MOCK, + securityAlertId: SECURITY_ALERT_ID_MOCK, + chainId: CHAIN_ID_MOCK, }); - expect(updateSecurityAlertResponseMock).toHaveBeenCalledWith( - validateRequestWithPPOMOptionsBase.request.method, - SECURITY_ALERT_ID_MOCK, - { - result_type: BlockaidResultType.Errored, - reason: BlockaidReason.errored, - description: 'Test Error: Test error message', - }, - ); + expect(response).toStrictEqual({ + result_type: BlockaidResultType.Errored, + reason: BlockaidReason.errored, + description: 'Test Error: Test error message', + }); }); - it('updates error response if controller throws', async () => { + it('returns error response if controller throws', async () => { const ppomController = createPPOMControllerMock(); ppomController.usePPOM.mockRejectedValue(createErrorMock()); - await validateRequestWithPPOM({ - ...validateRequestWithPPOMOptionsBase, + const response = await validateRequestWithPPOM({ ppomController, + request: REQUEST_MOCK, + securityAlertId: SECURITY_ALERT_ID_MOCK, + chainId: CHAIN_ID_MOCK, }); - expect(updateSecurityAlertResponseMock).toHaveBeenCalledWith( - validateRequestWithPPOMOptionsBase.request.method, - SECURITY_ALERT_ID_MOCK, - { - result_type: BlockaidResultType.Errored, - reason: BlockaidReason.errored, - description: 'Test Error: Test error message', - }, - ); + expect(response).toStrictEqual({ + result_type: BlockaidResultType.Errored, + reason: BlockaidReason.errored, + description: 'Test Error: Test error message', + }); }); it('normalizes request if method is eth_sendTransaction', async () => { @@ -242,9 +209,10 @@ describe('PPOM Utils', () => { }; await validateRequestWithPPOM({ - ...validateRequestWithPPOMOptionsBase, ppomController, request, + securityAlertId: SECURITY_ALERT_ID_MOCK, + chainId: CHAIN_ID_MOCK, }); expect(ppom.validateJsonRpc).toHaveBeenCalledTimes(1); @@ -258,23 +226,6 @@ describe('PPOM Utils', () => { TRANSACTION_PARAMS_MOCK_1, ); }); - - it('updates response indicating chain is not supported', async () => { - const ppomController = {} as PPOMController; - const CHAIN_ID_UNSUPPORTED_MOCK = '0x2'; - - await validateRequestWithPPOM({ - ...validateRequestWithPPOMOptionsBase, - ppomController, - chainId: CHAIN_ID_UNSUPPORTED_MOCK, - }); - - expect(updateSecurityAlertResponseMock).toHaveBeenCalledWith( - validateRequestWithPPOMOptionsBase.request.method, - SECURITY_ALERT_ID_MOCK, - SECURITY_ALERT_RESPONSE_CHAIN_NOT_SUPPORTED, - ); - }); }); describe('generateSecurityAlertId', () => { @@ -367,9 +318,10 @@ describe('PPOM Utils', () => { const ppomController = createPPOMControllerMock(); await validateRequestWithPPOM({ - ...validateRequestWithPPOMOptionsBase, ppomController, request, + securityAlertId: SECURITY_ALERT_ID_MOCK, + chainId: CHAIN_ID_MOCK, }); expect(ppomController.usePPOM).not.toHaveBeenCalled(); @@ -393,9 +345,10 @@ describe('PPOM Utils', () => { .mockRejectedValue(new Error('Test Error')); await validateRequestWithPPOM({ - ...validateRequestWithPPOMOptionsBase, ppomController, request, + securityAlertId: SECURITY_ALERT_ID_MOCK, + chainId: CHAIN_ID_MOCK, }); expect(ppomController.usePPOM).toHaveBeenCalledTimes(1); diff --git a/app/scripts/lib/ppom/ppom-util.ts b/app/scripts/lib/ppom/ppom-util.ts index fa1172ba01eb..7662c364b651 100644 --- a/app/scripts/lib/ppom/ppom-util.ts +++ b/app/scripts/lib/ppom/ppom-util.ts @@ -11,14 +11,12 @@ import { SignatureController } from '@metamask/signature-controller'; import { BlockaidReason, BlockaidResultType, - LOADING_SECURITY_ALERT_RESPONSE, - SECURITY_ALERT_RESPONSE_CHAIN_NOT_SUPPORTED, SECURITY_PROVIDER_SUPPORTED_CHAIN_IDS, SecurityAlertSource, } from '../../../../shared/constants/security-provider'; import { SIGNING_METHODS } from '../../../../shared/constants/transaction'; import { AppStateController } from '../../controllers/app-state-controller'; -import { SecurityAlertResponse, UpdateSecurityAlertResponse } from './types'; +import { SecurityAlertResponse } from './types'; import { getSecurityAlertsAPISupportedChainIds, isSecurityAlertsAPIEnabled, @@ -39,36 +37,18 @@ type PPOMRequest = Omit & { method: typeof METHOD_SEND_TRANSACTION; params: [TransactionParams]; }; - export async function validateRequestWithPPOM({ ppomController, request, securityAlertId, chainId, - updateSecurityAlertResponse: updateSecurityResponse, }: { ppomController: PPOMController; request: JsonRpcRequest; securityAlertId: string; chainId: Hex; - updateSecurityAlertResponse: UpdateSecurityAlertResponse; -}) { +}): Promise { try { - if (!(await isChainSupported(chainId))) { - await updateSecurityResponse( - request.method, - securityAlertId, - SECURITY_ALERT_RESPONSE_CHAIN_NOT_SUPPORTED, - ); - return; - } - - await updateSecurityResponse( - request.method, - securityAlertId, - LOADING_SECURITY_ALERT_RESPONSE, - ); - const normalizedRequest = normalizePPOMRequest(request); const ppomResponse = isSecurityAlertsAPIEnabled() @@ -78,13 +58,13 @@ export async function validateRequestWithPPOM({ normalizedRequest, chainId, ); - await updateSecurityResponse(request.method, securityAlertId, ppomResponse); - } catch (error: unknown) { - await updateSecurityResponse( - request.method, + + return { + ...ppomResponse, securityAlertId, - handlePPOMError(error, 'Error validating JSON RPC using PPOM: '), - ); + }; + } catch (error: unknown) { + return handlePPOMError(error, 'Error validating JSON RPC using PPOM: '); } } @@ -117,15 +97,12 @@ export async function updateSecurityAlertResponse({ ); if (isSignatureRequest) { - appStateController.addSignatureSecurityAlertResponse({ - ...securityAlertResponse, - securityAlertId, - }); + appStateController.addSignatureSecurityAlertResponse(securityAlertResponse); } else { - transactionController.updateSecurityAlertResponse(confirmation.id, { - ...securityAlertResponse, - securityAlertId, - } as SecurityAlertResponse); + transactionController.updateSecurityAlertResponse( + confirmation.id, + securityAlertResponse, + ); } } diff --git a/app/scripts/lib/ppom/security-alerts-api.test.ts b/app/scripts/lib/ppom/security-alerts-api.test.ts index 460139c1d359..9d2d97652d4f 100644 --- a/app/scripts/lib/ppom/security-alerts-api.test.ts +++ b/app/scripts/lib/ppom/security-alerts-api.test.ts @@ -27,8 +27,6 @@ const RESPONSE_MOCK = { description: 'Test Description', }; -const BASE_URL = 'https://example.com'; - describe('Security Alerts API', () => { const fetchMock = jest.fn(); @@ -42,7 +40,7 @@ describe('Security Alerts API', () => { json: async () => RESPONSE_MOCK, }); - process.env.SECURITY_ALERTS_API_URL = BASE_URL; + process.env.SECURITY_ALERTS_API_URL = 'https://example.com'; }); describe('validateWithSecurityAlertsAPI', () => { @@ -56,14 +54,8 @@ describe('Security Alerts API', () => { expect(fetchMock).toHaveBeenCalledTimes(1); expect(fetchMock).toHaveBeenCalledWith( - `${BASE_URL}/validate/${CHAIN_ID_MOCK}`, - expect.objectContaining({ - method: 'POST', - body: JSON.stringify(REQUEST_MOCK), - headers: { - 'Content-Type': 'application/json', - }, - }), + `https://example.com/validate/${CHAIN_ID_MOCK}`, + expect.any(Object), ); }); @@ -109,7 +101,7 @@ describe('Security Alerts API', () => { expect(fetchMock).toHaveBeenCalledTimes(1); expect(fetchMock).toHaveBeenCalledWith( - `${BASE_URL}/supportedChains`, + `https://example.com/supportedChains`, undefined, ); }); diff --git a/app/scripts/lib/ppom/types.ts b/app/scripts/lib/ppom/types.ts index 57dd4a9e533d..6188d644aa12 100644 --- a/app/scripts/lib/ppom/types.ts +++ b/app/scripts/lib/ppom/types.ts @@ -10,9 +10,3 @@ export type SecurityAlertResponse = { securityAlertId?: string; source?: SecurityAlertSource; }; - -export type UpdateSecurityAlertResponse = ( - method: string, - securityAlertId: string, - securityAlertResponse: SecurityAlertResponse, -) => Promise; diff --git a/app/scripts/lib/transaction/metrics.test.ts b/app/scripts/lib/transaction/metrics.test.ts index 7dcedd4e467e..75ea5c4b84c0 100644 --- a/app/scripts/lib/transaction/metrics.test.ts +++ b/app/scripts/lib/transaction/metrics.test.ts @@ -17,7 +17,6 @@ import { import { MetaMetricsTransactionEventSource, MetaMetricsEventCategory, - MetaMetricsEventUiCustomization, } from '../../../../shared/constants/metametrics'; import { TRANSACTION_ENVELOPE_TYPE_NAMES } from '../../../../shared/lib/transactions-controller-utils'; import { @@ -148,7 +147,6 @@ describe('Transaction metrics', () => { eip_1559_version: '0', gas_edit_attempted: 'none', gas_estimation_failed: false, - is_smart_transaction: undefined, gas_edit_type: 'none', network: mockNetworkId, referrer: ORIGIN_METAMASK, @@ -157,9 +155,8 @@ describe('Transaction metrics', () => { token_standard: TokenStandard.none, transaction_speed_up: false, transaction_type: TransactionType.simpleSend, - ui_customizations: ['redesigned_confirmation'], - transaction_advanced_view: undefined, - transaction_contract_method: undefined, + ui_customizations: null, + transaction_advanced_view: null, }; expectedSensitiveProperties = { @@ -168,7 +165,7 @@ describe('Transaction metrics', () => { first_seen: 1624408066355, gas_limit: '0x7b0d', gas_price: '2', - transaction_contract_address: undefined, + transaction_contract_method: undefined, transaction_envelope_type: TRANSACTION_ENVELOPE_TYPE_NAMES.LEGACY, transaction_replaced: undefined, }; @@ -236,10 +233,7 @@ describe('Transaction metrics', () => { persist: true, properties: { ...expectedProperties, - ui_customizations: [ - 'gas_estimation_failed', - 'redesigned_confirmation', - ], + ui_customizations: ['gas_estimation_failed'], gas_estimation_failed: true, }, sensitiveProperties: expectedSensitiveProperties, @@ -269,10 +263,7 @@ describe('Transaction metrics', () => { ...expectedProperties, security_alert_reason: BlockaidReason.maliciousDomain, security_alert_response: 'Malicious', - ui_customizations: [ - 'flagged_as_malicious', - 'redesigned_confirmation', - ], + ui_customizations: ['flagged_as_malicious'], ppom_eth_call_count: 5, ppom_eth_getCode_count: 3, }, @@ -362,10 +353,7 @@ describe('Transaction metrics', () => { persist: true, properties: { ...expectedProperties, - ui_customizations: [ - 'flagged_as_malicious', - 'redesigned_confirmation', - ], + ui_customizations: ['flagged_as_malicious'], security_alert_reason: BlockaidReason.maliciousDomain, security_alert_response: 'Malicious', ppom_eth_call_count: 5, @@ -382,10 +370,7 @@ describe('Transaction metrics', () => { { properties: { ...expectedProperties, - ui_customizations: [ - 'flagged_as_malicious', - 'redesigned_confirmation', - ], + ui_customizations: ['flagged_as_malicious'], security_alert_reason: BlockaidReason.maliciousDomain, security_alert_response: 'Malicious', ppom_eth_call_count: 5, @@ -505,10 +490,7 @@ describe('Transaction metrics', () => { persist: true, properties: { ...expectedProperties, - ui_customizations: [ - 'flagged_as_malicious', - 'redesigned_confirmation', - ], + ui_customizations: ['flagged_as_malicious'], security_alert_reason: BlockaidReason.maliciousDomain, security_alert_response: 'Malicious', ppom_eth_call_count: 5, @@ -528,10 +510,7 @@ describe('Transaction metrics', () => { { properties: { ...expectedProperties, - ui_customizations: [ - 'flagged_as_malicious', - 'redesigned_confirmation', - ], + ui_customizations: ['flagged_as_malicious'], security_alert_reason: BlockaidReason.maliciousDomain, security_alert_response: 'Malicious', ppom_eth_call_count: 5, @@ -708,10 +687,7 @@ describe('Transaction metrics', () => { persist: true, properties: { ...expectedProperties, - ui_customizations: [ - 'flagged_as_malicious', - 'redesigned_confirmation', - ], + ui_customizations: ['flagged_as_malicious'], security_alert_reason: BlockaidReason.maliciousDomain, security_alert_response: 'Malicious', ppom_eth_call_count: 5, @@ -733,10 +709,7 @@ describe('Transaction metrics', () => { { properties: { ...expectedProperties, - ui_customizations: [ - 'flagged_as_malicious', - 'redesigned_confirmation', - ], + ui_customizations: ['flagged_as_malicious'], security_alert_reason: BlockaidReason.maliciousDomain, security_alert_response: 'Malicious', ppom_eth_call_count: 5, @@ -758,72 +731,6 @@ describe('Transaction metrics', () => { mockTransactionMetricsRequest.finalizeEventFragment, ).toBeCalledWith(expectedUniqueId); }); - - it('should create, update, finalize event fragment with transaction_contract_address', async () => { - mockTransactionMeta.txReceipt = { - gasUsed: '0x123', - status: '0x0', - }; - mockTransactionMeta.submittedTime = 123; - mockTransactionMeta.status = TransactionStatus.confirmed; - mockTransactionMeta.type = TransactionType.contractInteraction; - const expectedUniqueId = 'transaction-submitted-1'; - const properties = { - ...expectedProperties, - status: TransactionStatus.confirmed, - transaction_type: TransactionType.contractInteraction, - asset_type: AssetType.unknown, - ui_customizations: [ - MetaMetricsEventUiCustomization.RedesignedConfirmation, - ], - is_smart_transaction: undefined, - transaction_advanced_view: undefined, - }; - const sensitiveProperties = { - ...expectedSensitiveProperties, - transaction_contract_address: - '0x1678a085c290ebd122dc42cba69373b5953b831d', - completion_time: expect.any(String), - gas_used: '0.000000291', - status: METRICS_STATUS_FAILED, - }; - - await handleTransactionConfirmed(mockTransactionMetricsRequest, { - ...mockTransactionMeta, - actionId: mockActionId, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any - } as any); - - expect(mockTransactionMetricsRequest.createEventFragment).toBeCalledTimes( - 1, - ); - expect(mockTransactionMetricsRequest.createEventFragment).toBeCalledWith({ - actionId: mockActionId, - category: MetaMetricsEventCategory.Transactions, - successEvent: TransactionMetaMetricsEvent.finalized, - uniqueIdentifier: expectedUniqueId, - persist: true, - properties, - sensitiveProperties, - }); - expect(mockTransactionMetricsRequest.updateEventFragment).toBeCalledTimes( - 1, - ); - expect(mockTransactionMetricsRequest.updateEventFragment).toBeCalledWith( - expectedUniqueId, - { - properties, - sensitiveProperties, - }, - ); - expect( - mockTransactionMetricsRequest.finalizeEventFragment, - ).toHaveBeenCalledTimes(1); - expect( - mockTransactionMetricsRequest.finalizeEventFragment, - ).toHaveBeenCalledWith(expectedUniqueId); - }); }); describe('handleTransactionDropped', () => { @@ -913,10 +820,7 @@ describe('Transaction metrics', () => { persist: true, properties: { ...expectedProperties, - ui_customizations: [ - 'flagged_as_malicious', - 'redesigned_confirmation', - ], + ui_customizations: ['flagged_as_malicious'], security_alert_reason: BlockaidReason.maliciousDomain, security_alert_response: 'Malicious', ppom_eth_call_count: 5, @@ -937,10 +841,7 @@ describe('Transaction metrics', () => { { properties: { ...expectedProperties, - ui_customizations: [ - 'flagged_as_malicious', - 'redesigned_confirmation', - ], + ui_customizations: ['flagged_as_malicious'], security_alert_reason: BlockaidReason.maliciousDomain, security_alert_response: 'Malicious', ppom_eth_call_count: 5, @@ -1046,10 +947,7 @@ describe('Transaction metrics', () => { persist: true, properties: { ...expectedProperties, - ui_customizations: [ - 'flagged_as_malicious', - 'redesigned_confirmation', - ], + ui_customizations: ['flagged_as_malicious'], security_alert_reason: BlockaidReason.maliciousDomain, security_alert_response: 'Malicious', ppom_eth_call_count: 5, @@ -1066,10 +964,7 @@ describe('Transaction metrics', () => { { properties: { ...expectedProperties, - ui_customizations: [ - 'flagged_as_malicious', - 'redesigned_confirmation', - ], + ui_customizations: ['flagged_as_malicious'], security_alert_reason: BlockaidReason.maliciousDomain, security_alert_response: 'Malicious', ppom_eth_call_count: 5, diff --git a/app/scripts/lib/transaction/metrics.ts b/app/scripts/lib/transaction/metrics.ts index 3cdc14619b0d..e0be105b1f10 100644 --- a/app/scripts/lib/transaction/metrics.ts +++ b/app/scripts/lib/transaction/metrics.ts @@ -56,7 +56,7 @@ import { export type TransactionMetricsRequest = { createEventFragment: ( - options: Omit, + options: MetaMetricsEventFragment, ) => MetaMetricsEventFragment; finalizeEventFragment: ( fragmentId: string, @@ -528,12 +528,7 @@ function createTransactionEventFragment({ transactionMetricsRequest.getEventFragmentById, eventName, transactionMeta, - ) && - /** - * HACK: "transaction-submitted-" fragment hack - * can continue to createEventFragment if "transaction-submitted-" submitted fragment exists - */ - eventName !== TransactionMetaMetricsEvent.submitted + ) ) { return; } @@ -647,14 +642,25 @@ function updateTransactionEventFragment({ switch (eventName) { case TransactionMetaMetricsEvent.approved: + transactionMetricsRequest.updateEventFragment(uniqueId, { + properties: payload.properties, + sensitiveProperties: payload.sensitiveProperties, + }); + break; + case TransactionMetaMetricsEvent.rejected: - case TransactionMetaMetricsEvent.finalized: transactionMetricsRequest.updateEventFragment(uniqueId, { properties: payload.properties, sensitiveProperties: payload.sensitiveProperties, }); break; + case TransactionMetaMetricsEvent.finalized: + transactionMetricsRequest.updateEventFragment(uniqueId, { + properties: payload.properties, + sensitiveProperties: payload.sensitiveProperties, + }); + break; default: break; } @@ -673,7 +679,6 @@ function finalizeTransactionEventFragment({ switch (eventName) { case TransactionMetaMetricsEvent.approved: - case TransactionMetaMetricsEvent.finalized: transactionMetricsRequest.finalizeEventFragment(uniqueId); break; @@ -683,6 +688,9 @@ function finalizeTransactionEventFragment({ }); break; + case TransactionMetaMetricsEvent.finalized: + transactionMetricsRequest.finalizeEventFragment(uniqueId); + break; default: break; } @@ -908,7 +916,6 @@ async function buildEventFragmentProperties({ let transactionContractMethod; let transactionApprovalAmountVsProposedRatio; let transactionApprovalAmountVsBalanceRatio; - let transactionContractAddress; let transactionType = TransactionType.simpleSend; if (type === TransactionType.swapAndSend) { transactionType = TransactionType.swapAndSend; @@ -921,7 +928,6 @@ async function buildEventFragmentProperties({ } else if (contractInteractionTypes) { transactionType = TransactionType.contractInteraction; transactionContractMethod = contractMethodName; - transactionContractAddress = transactionMeta.txParams?.to; if ( transactionContractMethod === contractMethodNames.APPROVE && tokenStandard === TokenStandard.ERC20 @@ -998,7 +1004,7 @@ async function buildEventFragmentProperties({ } const isRedesignedConfirmationsDeveloperSettingEnabled = transactionMetricsRequest.getIsRedesignedConfirmationsDeveloperEnabled() || - process.env.ENABLE_CONFIRMATION_REDESIGN === 'true'; + Boolean(process.env.ENABLE_CONFIRMATION_REDESIGN); const isRedesignedTransactionsUserSettingEnabled = transactionMetricsRequest.getRedesignedTransactionsEnabled(); @@ -1054,7 +1060,6 @@ async function buildEventFragmentProperties({ // ui_customizations must come after ...blockaidProperties ui_customizations: uiCustomizations.length > 0 ? uiCustomizations : null, transaction_advanced_view: isAdvancedDetailsOpen, - transaction_contract_method: transactionContractMethod, ...smartTransactionMetricsProperties, ...swapAndSendMetricsProperties, // TODO: Replace `any` with type @@ -1081,8 +1086,8 @@ async function buildEventFragmentProperties({ : TRANSACTION_ENVELOPE_TYPE_NAMES.LEGACY, first_seen: time, gas_limit: gasLimit, + transaction_contract_method: transactionContractMethod, transaction_replaced: transactionReplaced, - transaction_contract_address: transactionContractAddress, ...extraParams, ...gasParamsInGwei, // TODO: Replace `any` with type diff --git a/app/scripts/lib/transaction/util.test.ts b/app/scripts/lib/transaction/util.test.ts index fbbee025381b..16077e0f08ed 100644 --- a/app/scripts/lib/transaction/util.test.ts +++ b/app/scripts/lib/transaction/util.test.ts @@ -16,6 +16,7 @@ import { BlockaidReason, BlockaidResultType, } from '../../../../shared/constants/security-provider'; +import { SecurityAlertResponse } from '../ppom/types'; import { flushPromises } from '../../../../test/lib/timer-helpers'; import { createMockInternalAccount } from '../../../../test/jest/mocks'; import { @@ -78,6 +79,11 @@ const TRANSACTION_REQUEST_MOCK: AddTransactionRequest = { internalAccounts: [], } as unknown as AddTransactionRequest; +const SECURITY_ALERT_RESPONSE_MOCK: SecurityAlertResponse = { + result_type: BlockaidResultType.Malicious, + reason: BlockaidReason.maliciousDomain, +}; + function createTransactionControllerMock() { return { addTransaction: jest.fn(), @@ -400,6 +406,10 @@ describe('Transaction Utils', () => { describe('validates using security provider', () => { it('adds loading response to request options', async () => { + validateRequestWithPPOMMock.mockResolvedValue( + SECURITY_ALERT_RESPONSE_MOCK, + ); + await addTransaction({ ...request, securityAlertsEnabled: true, @@ -415,13 +425,36 @@ describe('Transaction Utils', () => { ).toHaveBeenCalledWith(TRANSACTION_PARAMS_MOCK, { ...TRANSACTION_OPTIONS_MOCK, securityAlertResponse: { - reason: BlockaidReason.checkingChain, + reason: BlockaidReason.inProgress, result_type: BlockaidResultType.Loading, securityAlertId: SECURITY_ALERT_ID_MOCK, }, }); }); + it('updates response after validation', async () => { + validateRequestWithPPOMMock.mockResolvedValue( + SECURITY_ALERT_RESPONSE_MOCK, + ); + + await addTransaction({ + ...request, + securityAlertsEnabled: true, + chainId: '0x1', + }); + + await flushPromises(); + + expect(request.updateSecurityAlertResponse).toHaveBeenCalledTimes(1); + expect(request.updateSecurityAlertResponse).toHaveBeenCalledWith( + 'eth_sendTransaction', + SECURITY_ALERT_ID_MOCK, + SECURITY_ALERT_RESPONSE_MOCK, + ); + + expect(validateRequestWithPPOMMock).toHaveBeenCalledTimes(1); + }); + it('unless blockaid is disabled', async () => { await addTransaction({ ...request, @@ -472,6 +505,29 @@ describe('Transaction Utils', () => { expect(validateRequestWithPPOMMock).toHaveBeenCalledTimes(0); }); + it('unless chain is not supported', async () => { + isChainSupportedMock.mockResolvedValue(false); + + await addTransaction({ + ...request, + securityAlertsEnabled: true, + chainId: '0xF', + }); + + expect( + request.transactionController.addTransaction, + ).toHaveBeenCalledTimes(1); + + expect( + request.transactionController.addTransaction, + ).toHaveBeenCalledWith( + TRANSACTION_PARAMS_MOCK, + TRANSACTION_OPTIONS_MOCK, + ); + + expect(validateRequestWithPPOMMock).toHaveBeenCalledTimes(0); + }); + it('unless transaction type is swap', async () => { const swapRequest = { ...request }; swapRequest.transactionOptions.type = TransactionType.swap; diff --git a/app/scripts/lib/transaction/util.ts b/app/scripts/lib/transaction/util.ts index 0bbf93afd8a8..8b71e33119f8 100644 --- a/app/scripts/lib/transaction/util.ts +++ b/app/scripts/lib/transaction/util.ts @@ -16,14 +16,12 @@ import { PPOMController } from '@metamask/ppom-validator'; import { generateSecurityAlertId, handlePPOMError, + isChainSupported, validateRequestWithPPOM, } from '../ppom/ppom-util'; +import { SecurityAlertResponse } from '../ppom/types'; import { - SecurityAlertResponse, - UpdateSecurityAlertResponse, -} from '../ppom/types'; -import { - SECURITY_ALERT_RESPONSE_CHECKING_CHAIN, + LOADING_SECURITY_ALERT_RESPONSE, SECURITY_PROVIDER_EXCLUDED_TRANSACTION_TYPES, } from '../../../../shared/constants/security-provider'; import { endTrace, TraceName } from '../../../../shared/lib/trace'; @@ -40,7 +38,11 @@ type BaseAddTransactionRequest = { selectedAccount: InternalAccount; transactionParams: TransactionParams; transactionController: TransactionController; - updateSecurityAlertResponse: UpdateSecurityAlertResponse; + updateSecurityAlertResponse: ( + method: string, + securityAlertId: string, + securityAlertResponse: SecurityAlertResponse, + ) => void; userOperationController: UserOperationController; internalAccounts: InternalAccount[]; }; @@ -237,12 +239,18 @@ async function validateSecurity(request: AddTransactionRequest) { const { type } = transactionOptions; + const isCurrentChainSupported = await isChainSupported(chainId); + const typeIsExcludedFromPPOM = SECURITY_PROVIDER_EXCLUDED_TRANSACTION_TYPES.includes( type as TransactionType, ); - if (!securityAlertsEnabled || typeIsExcludedFromPPOM) { + if ( + !securityAlertsEnabled || + !isCurrentChainSupported || + typeIsExcludedFromPPOM + ) { return; } @@ -282,16 +290,21 @@ async function validateSecurity(request: AddTransactionRequest) { request: ppomRequest, securityAlertId, chainId, - updateSecurityAlertResponse, + }).then((securityAlertResponse) => { + updateSecurityAlertResponse( + ppomRequest.method, + securityAlertId, + securityAlertResponse, + ); }); - const securityAlertResponseCheckingChain: SecurityAlertResponse = { - ...SECURITY_ALERT_RESPONSE_CHECKING_CHAIN, + const loadingSecurityAlertResponse: SecurityAlertResponse = { + ...LOADING_SECURITY_ALERT_RESPONSE, securityAlertId, }; request.transactionOptions.securityAlertResponse = - securityAlertResponseCheckingChain; + loadingSecurityAlertResponse; } catch (error) { handlePPOMError(error, 'Error validating JSON RPC using PPOM: '); } diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index a275c4d17e5a..4284a2614a9d 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -14,9 +14,9 @@ import { fetchMultiExchangeRate, } from '@metamask/assets-controllers'; import { JsonRpcEngine } from '@metamask/json-rpc-engine'; -import { createEngineStream } from '@metamask/json-rpc-middleware-stream'; import { ObservableStore } from '@metamask/obs-store'; import { storeAsStream } from '@metamask/obs-store/dist/asStream'; +import { createEngineStream } from 'json-rpc-middleware-stream'; import { providerAsMiddleware } from '@metamask/eth-json-rpc-middleware'; import { debounce, throttle, memoize, wrap } from 'lodash'; import { @@ -765,25 +765,31 @@ export default class MetamaskController extends EventEmitter { }); this.metaMetricsController = new MetaMetricsController({ - initState: initState.MetaMetricsController, segment, - preferencesControllerState: { - currentLocale: this.preferencesController.state.currentLocale, - selectedAddress: this.preferencesController.state.selectedAddress, - }, onPreferencesStateChange: preferencesMessenger.subscribe.bind( preferencesMessenger, 'PreferencesController:stateChange', ), + preferencesControllerState: { + currentLocale: this.preferencesController.state.currentLocale, + selectedAddress: this.preferencesController.state.selectedAddress, + }, onNetworkDidChange: networkControllerMessenger.subscribe.bind( networkControllerMessenger, 'NetworkController:networkDidChange', ), + getNetworkIdentifier: () => { + const { type, rpcUrl } = getProviderConfig({ + metamask: this.networkController.state, + }); + return type === NETWORK_TYPES.RPC ? rpcUrl : type; + }, getCurrentChainId: () => getCurrentChainId({ metamask: this.networkController.state }), version: process.env.METAMASK_VERSION, environment: process.env.METAMASK_ENVIRONMENT, extension: this.extension, + initState: initState.MetaMetricsController, captureException, }); @@ -878,13 +884,13 @@ export default class MetamaskController extends EventEmitter { messenger: currencyRateMessenger, state: initState.CurrencyController, }); - const initialFetchMultiExchangeRate = - this.currencyRateController.fetchMultiExchangeRate.bind( + const initialFetchExchangeRate = + this.currencyRateController.fetchExchangeRate.bind( this.currencyRateController, ); - this.currencyRateController.fetchMultiExchangeRate = (...args) => { + this.currencyRateController.fetchExchangeRate = (...args) => { if (this.preferencesController.state.useCurrencyRateCheck) { - return initialFetchMultiExchangeRate(...args); + return initialFetchExchangeRate(...args); } return { conversionRate: null, @@ -1990,9 +1996,11 @@ export default class MetamaskController extends EventEmitter { `${this.keyringController.name}:signPersonalMessage`, `${this.keyringController.name}:signTypedMessage`, `${this.loggingController.name}:add`, - `${this.networkController.name}:getNetworkClientById`, ], }), + getAllState: this.getState.bind(this), + getCurrentChainId: () => + getCurrentChainId({ metamask: this.networkController.state }), trace, }); @@ -2120,7 +2128,7 @@ export default class MetamaskController extends EventEmitter { const bridgeControllerMessenger = this.controllerMessenger.getRestricted({ name: BRIDGE_CONTROLLER_NAME, - allowedActions: ['AccountsController:getSelectedAccount'], + allowedActions: [], allowedEvents: [], }); this.bridgeController = new BridgeController({ @@ -3944,11 +3952,6 @@ export default class MetamaskController extends EventEmitter { this.controllerMessenger, `${BRIDGE_CONTROLLER_NAME}:${BridgeUserAction.SELECT_DEST_NETWORK}`, ), - [BridgeUserAction.UPDATE_QUOTE_PARAMS]: - this.controllerMessenger.call.bind( - this.controllerMessenger, - `${BRIDGE_CONTROLLER_NAME}:${BridgeUserAction.UPDATE_QUOTE_PARAMS}`, - ), // Smart Transactions fetchSmartTransactionFees: smartTransactionsController.getFees.bind( @@ -4009,9 +4012,10 @@ export default class MetamaskController extends EventEmitter { ), // CurrencyRateController - currencyRateStartPolling: currencyRateController.startPolling.bind( - currencyRateController, - ), + currencyRateStartPollingByNetworkClientId: + currencyRateController.startPollingByNetworkClientId.bind( + currencyRateController, + ), currencyRateStopPollingByPollingToken: currencyRateController.stopPollingByPollingToken.bind( currencyRateController, @@ -6529,12 +6533,10 @@ export default class MetamaskController extends EventEmitter { ); }, getRedesignedConfirmationsEnabled: () => { - return this.preferencesController.state.preferences - .redesignedConfirmationsEnabled; + return this.preferencesController.getRedesignedConfirmationsEnabled; }, getRedesignedTransactionsEnabled: () => { - return this.preferencesController.state.preferences - .redesignedTransactionsEnabled; + return this.preferencesController.getRedesignedTransactionsEnabled; }, getMethodData: (data) => { if (!data) { @@ -6683,34 +6685,12 @@ export default class MetamaskController extends EventEmitter { * @param {string} origin - the domain to safelist */ safelistPhishingDomain(origin) { - this.metaMetricsController.trackEvent({ - category: MetaMetricsEventCategory.Phishing, - event: MetaMetricsEventName.ProceedAnywayClicked, - properties: { - url: origin, - referrer: { - url: origin, - }, - }, - }); - return this.phishingController.bypass(origin); } async backToSafetyPhishingWarning() { - const portfolioBaseURL = process.env.PORTFOLIO_URL; - const portfolioURL = `${portfolioBaseURL}/?metamaskEntry=phishing_page_portfolio_button`; - - this.metaMetricsController.trackEvent({ - category: MetaMetricsEventCategory.Navigation, - event: MetaMetricsEventName.PortfolioLinkClicked, - properties: { - location: 'phishing_page', - text: 'Back to safety', - }, - }); - - await this.platform.switchToAnotherURL(undefined, portfolioURL); + const extensionURL = this.platform.getExtensionURL(); + await this.platform.switchToAnotherURL(undefined, extensionURL); } /** diff --git a/app/scripts/ui.js b/app/scripts/ui.js index 794037e12761..4f06fb8998c4 100644 --- a/app/scripts/ui.js +++ b/app/scripts/ui.js @@ -104,7 +104,7 @@ async function start() { if (isManifestV3 && isUIInitialised) { // Currently when service worker is revived we create new streams // in later version we might try to improve it by reviving same streams. - updateUiStreams(connectionStream); + updateUiStreams(); } else { await initializeUiWithTab( activeTab, diff --git a/builds.yml b/builds.yml index 824e08e32167..bcd035b56bc1 100644 --- a/builds.yml +++ b/builds.yml @@ -173,12 +173,6 @@ env: - SENTRY_MMI_DSN: '' - ### - # Storybook - ### - - STORYBOOK: false - - INFURA_STORYBOOK_PROJECT_ID - ### # Notifications Feature ### @@ -273,14 +267,12 @@ env: - BARAD_DUR: '' # Determines if feature flagged Chain permissions - CHAIN_PERMISSIONS: '' - # Determines if feature flagged Filter toggle - - FILTER_TOKENS_TOGGLE: '' # Enables use of test gas fee flow to debug gas fee estimation - TEST_GAS_FEE_FLOWS: false # Temporary mechanism to enable security alerts API prior to release - - SECURITY_ALERTS_API_ENABLED: 'true' + - SECURITY_ALERTS_API_ENABLED: '' # URL of security alerts API used to validate dApp requests - - SECURITY_ALERTS_API_URL: 'https://security-alerts.api.cx.metamask.io' + - SECURITY_ALERTS_API_URL: 'http://localhost:3000' # API key to authenticate Etherscan requests to avoid rate limiting - ETHERSCAN_API_KEY: '' diff --git a/development/README.md b/development/README.md index 86733ef172e6..33ab036975e4 100644 --- a/development/README.md +++ b/development/README.md @@ -65,7 +65,7 @@ or `https://api.segment.io/v1/batch` respectively. 2. To display Sentry logs, include `DEBUG=metamask:sentry:*` in `.metamaskrc`. -3. To display more verbose logs if not in a developer build, include `METAMASK_DEBUG=1` in `.metamaskrc`. +3. To display more verbose logs if not in a developer build, include `METAMASK_DEBUG=true` in `.metamaskrc`. 4. Ensure metrics are enabled during onboarding or via `Settings > Security & privacy > Participate in MetaMetrics`. diff --git a/development/webpack/webpack.integration.tests.config.ts b/development/webpack/webpack.integration.tests.config.ts deleted file mode 100644 index 77e032581180..000000000000 --- a/development/webpack/webpack.integration.tests.config.ts +++ /dev/null @@ -1,116 +0,0 @@ -/** - * @file The webpack configuration file to enable debug previewing for UI integration tests. - */ - -import { readFileSync } from 'node:fs'; -import { join } from 'node:path'; -import { - type Configuration, - type WebpackPluginInstance, - ProgressPlugin, -} from 'webpack'; -import MiniCssExtractPlugin from 'mini-css-extract-plugin'; -import CopyPlugin from 'copy-webpack-plugin'; -import rtlCss from 'postcss-rtlcss'; -import autoprefixer from 'autoprefixer'; - -const context = join(__dirname, '../../app'); -const browsersListPath = join(context, '../.browserslistrc'); -const browsersListQuery = readFileSync(browsersListPath, 'utf8'); - -const plugins: WebpackPluginInstance[] = [ - new CopyPlugin({ - patterns: [ - { from: join(context, '_locales'), to: '_locales' }, // translations - // misc images - // TODO: fix overlap between this folder and automatically bundled assets - { from: join(context, 'images'), to: 'images' }, - ], - }), - new ProgressPlugin(), - new MiniCssExtractPlugin({ filename: '[name].css' }), -]; - -const config = { - entry: { - index: join(context, '../ui/css/index.scss'), - }, - plugins, - mode: 'development', - context, - stats: 'normal', - name: `MetaMask UI integration test`, - output: { - path: join(context, '..', 'test/integration/config/assets'), - clean: true, - }, - // note: loaders in a `use` array are applied in *reverse* order, i.e., bottom - // to top, (or right to left depending on the current formatting of the file) - module: { - rules: [ - // css, sass/scss - { - test: /\.(css|sass|scss)$/u, - use: [ - MiniCssExtractPlugin.loader, - // Resolves CSS `@import` and `url()` paths and loads the files. - { - loader: 'css-loader', - options: { - url: true, - }, - }, - { - loader: 'postcss-loader', - options: { - postcssOptions: { - plugins: [ - autoprefixer({ overrideBrowserslist: browsersListQuery }), - rtlCss({ processEnv: false }), - ], - }, - }, - }, - { - loader: 'resolve-url-loader', - }, - // Compiles Sass to CSS - { - loader: 'sass-loader', - options: { - // Use 'sass-embedded', as it is usually faster than 'sass' - implementation: 'sass-embedded', - sassOptions: { - api: 'modern', - // We don't need to specify the charset because the HTML - // already does and browsers use the HTML's charset for CSS. - // Additionally, webpack + sass can cause problems with the - // charset placement, as described here: - // https://github.com/webpack-contrib/css-loader/issues/1212 - charset: false, - // The order of includePaths is important; prefer our own - // folders over `node_modules` - includePaths: [ - // enables aliases to `@use design - system`, - // `@use utilities`, etc. - join(context, '../ui/css'), - join(context, '../node_modules'), - ], - // Disable the webpackImporter, as we: - // a) don't want to rely on it in case we want to switch away - // from webpack in the future - // b) the sass importer is faster - // c) the "modern" sass api doesn't work with the - // webpackImporter yet. - webpackImporter: false, - }, - sourceMap: true, - }, - }, - ], - }, - ], - }, -} as const satisfies Configuration; - -export default config; diff --git a/jest.config.js b/jest.config.js index 56a75bfc68ed..f1d38ab4aea3 100644 --- a/jest.config.js +++ b/jest.config.js @@ -35,7 +35,6 @@ module.exports = { '/development/**/*.test.(js|ts|tsx)', '/test/unit-global/**/*.test.(js|ts|tsx)', '/test/e2e/helpers.test.js', - '/test/e2e/helpers/**/*.test.(js|ts|tsx)', ], testPathIgnorePatterns: ['/development/webpack/'], testTimeout: 5500, diff --git a/jest.integration.config.js b/jest.integration.config.js index d7236b832aed..6f5d79484386 100644 --- a/jest.integration.config.js +++ b/jest.integration.config.js @@ -35,13 +35,4 @@ module.exports = { customExportConditions: ['node', 'node-addons'], }, workerIdleMemoryLimit: '500MB', - transform: { - // Use babel-jest to transpile tests with the next/babel preset - // https://jestjs.io/docs/configuration#transform-objectstring-pathtotransformer--pathtotransformer-object - '^.+\\.(js|jsx|ts|tsx)$': 'babel-jest', - '^.+\\.(css|scss|sass|less)$': 'jest-preview/transforms/css', - '^(?!.*\\.(js|jsx|mjs|cjs|ts|tsx|css|json)$)': - 'jest-preview/transforms/file', - }, - transformIgnorePatterns: ['/node_modules/'], }; diff --git a/lavamoat/browserify/beta/policy.json b/lavamoat/browserify/beta/policy.json index ac719964d896..ef4c915328c2 100644 --- a/lavamoat/browserify/beta/policy.json +++ b/lavamoat/browserify/beta/policy.json @@ -150,7 +150,7 @@ "console.warn": true }, "packages": { - "@ethereumjs/tx>@ethereumjs/util>@ethereumjs/rlp": true, + "@ethereumjs/tx>@ethereumjs/rlp": true, "@ethereumjs/tx>@ethereumjs/util>micro-ftch": true, "@ethereumjs/tx>ethereum-cryptography": true, "browserify>buffer": true, @@ -158,11 +158,6 @@ "webpack>events": true } }, - "@ethereumjs/tx>@ethereumjs/util>@ethereumjs/rlp": { - "globals": { - "TextEncoder": true - } - }, "@ethereumjs/tx>@ethereumjs/util>micro-ftch": { "globals": { "Headers": true, @@ -187,28 +182,16 @@ "crypto": true }, "packages": { - "@ethereumjs/tx>ethereum-cryptography>@noble/hashes": true, "@ethereumjs/tx>ethereum-cryptography>@scure/bip32": true, - "@metamask/message-signing-snap>@noble/curves": true - } - }, - "@ethereumjs/tx>ethereum-cryptography>@noble/hashes": { - "globals": { - "TextEncoder": true, - "crypto": true + "@metamask/message-signing-snap>@noble/curves": true, + "@noble/hashes": true } }, "@ethereumjs/tx>ethereum-cryptography>@scure/bip32": { "packages": { - "@ethereumjs/tx>ethereum-cryptography>@scure/bip32>@noble/hashes": true, "@metamask/message-signing-snap>@noble/curves": true, - "@metamask/utils>@scure/base": true - } - }, - "@ethereumjs/tx>ethereum-cryptography>@scure/bip32>@noble/hashes": { - "globals": { - "TextEncoder": true, - "crypto": true + "@metamask/utils>@scure/base": true, + "@noble/hashes": true } }, "@ethersproject/abi": { @@ -375,7 +358,7 @@ "@ethereumjs/tx": true, "@keystonehq/bc-ur-registry-eth": true, "@keystonehq/metamask-airgapped-keyring>@keystonehq/base-eth-keyring": true, - "@metamask/obs-store": true, + "@keystonehq/metamask-airgapped-keyring>@metamask/obs-store": true, "browserify>buffer": true, "ethereumjs-util>rlp": true, "uuid": true, @@ -398,6 +381,54 @@ "TextEncoder": true } }, + "@keystonehq/metamask-airgapped-keyring>@metamask/obs-store": { + "packages": { + "@keystonehq/metamask-airgapped-keyring>@metamask/obs-store>@metamask/safe-event-emitter": true, + "@keystonehq/metamask-airgapped-keyring>@metamask/obs-store>through2": true, + "stream-browserify": true + } + }, + "@keystonehq/metamask-airgapped-keyring>@metamask/obs-store>@metamask/safe-event-emitter": { + "globals": { + "setTimeout": true + }, + "packages": { + "webpack>events": true + } + }, + "@keystonehq/metamask-airgapped-keyring>@metamask/obs-store>through2": { + "packages": { + "@keystonehq/metamask-airgapped-keyring>@metamask/obs-store>through2>readable-stream": true, + "browserify>util": true, + "process": true, + "watchify>xtend": true + } + }, + "@keystonehq/metamask-airgapped-keyring>@metamask/obs-store>through2>readable-stream": { + "packages": { + "@keystonehq/metamask-airgapped-keyring>@metamask/obs-store>through2>readable-stream>isarray": true, + "@keystonehq/metamask-airgapped-keyring>@metamask/obs-store>through2>readable-stream>safe-buffer": true, + "@keystonehq/metamask-airgapped-keyring>@metamask/obs-store>through2>readable-stream>string_decoder": true, + "browserify>browser-resolve": true, + "browserify>timers-browserify": true, + "process": true, + "pumpify>inherits": true, + "readable-stream-2>core-util-is": true, + "readable-stream-2>process-nextick-args": true, + "readable-stream>util-deprecate": true, + "webpack>events": true + } + }, + "@keystonehq/metamask-airgapped-keyring>@metamask/obs-store>through2>readable-stream>safe-buffer": { + "packages": { + "browserify>buffer": true + } + }, + "@keystonehq/metamask-airgapped-keyring>@metamask/obs-store>through2>readable-stream>string_decoder": { + "packages": { + "@keystonehq/metamask-airgapped-keyring>@metamask/obs-store>through2>readable-stream>safe-buffer": true + } + }, "@lavamoat/lavadome-react": { "globals": { "Document.prototype": true, @@ -674,14 +705,14 @@ "@ethersproject/providers": true, "@metamask/abi-utils": true, "@metamask/assets-controllers>@metamask/polling-controller": true, - "@metamask/assets-controllers>@metamask/utils": true, + "@metamask/assets-controllers>@metamask/rpc-errors": true, "@metamask/base-controller": true, "@metamask/contract-metadata": true, "@metamask/controller-utils": true, "@metamask/eth-query": true, "@metamask/metamask-eth-abis": true, "@metamask/name-controller>async-mutex": true, - "@metamask/rpc-errors": true, + "@metamask/utils": true, "bn.js": true, "cockatiel": true, "ethers>@ethersproject/address": true, @@ -702,19 +733,10 @@ "uuid": true } }, - "@metamask/assets-controllers>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, + "@metamask/assets-controllers>@metamask/rpc-errors": { "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true + "@metamask/rpc-errors>fast-safe-stringify": true, + "@metamask/utils": true } }, "@metamask/base-controller": { @@ -841,13 +863,70 @@ "console.error": true }, "packages": { - "@metamask/eth-query": true, - "@metamask/json-rpc-engine": true, - "@metamask/name-controller>async-mutex": true, + "@metamask/eth-json-rpc-filters>@metamask/eth-query": true, + "@metamask/eth-json-rpc-filters>@metamask/json-rpc-engine": true, + "@metamask/eth-json-rpc-filters>async-mutex": true, "@metamask/safe-event-emitter": true, "pify": true } }, + "@metamask/eth-json-rpc-filters>@metamask/eth-query": { + "packages": { + "@metamask/eth-query>json-rpc-random-id": true, + "watchify>xtend": true + } + }, + "@metamask/eth-json-rpc-filters>@metamask/json-rpc-engine": { + "packages": { + "@metamask/eth-json-rpc-filters>@metamask/json-rpc-engine>@metamask/rpc-errors": true, + "@metamask/eth-json-rpc-filters>@metamask/json-rpc-engine>@metamask/utils": true, + "@metamask/safe-event-emitter": true + } + }, + "@metamask/eth-json-rpc-filters>@metamask/json-rpc-engine>@metamask/rpc-errors": { + "packages": { + "@metamask/eth-json-rpc-filters>@metamask/json-rpc-engine>@metamask/rpc-errors>@metamask/utils": true, + "@metamask/rpc-errors>fast-safe-stringify": true + } + }, + "@metamask/eth-json-rpc-filters>@metamask/json-rpc-engine>@metamask/rpc-errors>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/utils>@metamask/superstruct": true, + "@metamask/utils>@scure/base": true, + "@metamask/utils>pony-cause": true, + "@noble/hashes": true, + "browserify>buffer": true, + "nock>debug": true, + "semver": true + } + }, + "@metamask/eth-json-rpc-filters>@metamask/json-rpc-engine>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/utils>@metamask/superstruct": true, + "@metamask/utils>@scure/base": true, + "@metamask/utils>pony-cause": true, + "@noble/hashes": true, + "browserify>buffer": true, + "nock>debug": true, + "semver": true + } + }, + "@metamask/eth-json-rpc-filters>async-mutex": { + "globals": { + "setTimeout": true + }, + "packages": { + "@swc/helpers>tslib": true + } + }, "@metamask/eth-json-rpc-middleware": { "globals": { "URL": true, @@ -887,19 +966,14 @@ }, "packages": { "@ethereumjs/tx": true, + "@ethereumjs/tx>@ethereumjs/rlp": true, "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/eth-ledger-bridge-keyring>@ethereumjs/rlp": true, "@metamask/eth-sig-util": true, "@metamask/eth-trezor-keyring>hdkey": true, "browserify>buffer": true, "webpack>events": true } }, - "@metamask/eth-ledger-bridge-keyring>@ethereumjs/rlp": { - "globals": { - "TextEncoder": true - } - }, "@metamask/eth-query": { "packages": { "@metamask/eth-query>json-rpc-random-id": true, @@ -1241,24 +1315,24 @@ "uuid": true } }, - "@metamask/gas-fee-controller>@metamask/base-controller": { + "@metamask/gas-fee-controller>@metamask/polling-controller": { "globals": { + "clearTimeout": true, + "console.error": true, "setTimeout": true }, "packages": { - "immer": true + "@metamask/gas-fee-controller>@metamask/polling-controller>@metamask/base-controller": true, + "@metamask/snaps-utils>fast-json-stable-stringify": true, + "uuid": true } }, - "@metamask/gas-fee-controller>@metamask/polling-controller": { + "@metamask/gas-fee-controller>@metamask/polling-controller>@metamask/base-controller": { "globals": { - "clearTimeout": true, - "console.error": true, "setTimeout": true }, "packages": { - "@metamask/gas-fee-controller>@metamask/base-controller": true, - "@metamask/snaps-utils>fast-json-stable-stringify": true, - "uuid": true + "immer": true } }, "@metamask/jazzicon": { @@ -1315,17 +1389,6 @@ "semver": true } }, - "@metamask/json-rpc-middleware-stream": { - "globals": { - "console.warn": true, - "setTimeout": true - }, - "packages": { - "@metamask/safe-event-emitter": true, - "@metamask/utils": true, - "readable-stream": true - } - }, "@metamask/keyring-api": { "globals": { "URL": true @@ -1465,13 +1528,7 @@ "TextEncoder": true }, "packages": { - "@metamask/message-signing-snap>@noble/curves>@noble/hashes": true - } - }, - "@metamask/message-signing-snap>@noble/curves>@noble/hashes": { - "globals": { - "TextEncoder": true, - "crypto": true + "@noble/hashes": true } }, "@metamask/name-controller": { @@ -1753,7 +1810,6 @@ "@metamask/notification-services-controller>@contentful/rich-text-html-renderer": true, "@metamask/notification-services-controller>firebase": true, "@metamask/profile-sync-controller": true, - "@metamask/utils": true, "bignumber.js": true, "loglevel": true, "uuid": true @@ -2025,21 +2081,9 @@ "@ethereumjs/tx>ethereum-cryptography": true, "@metamask/base-controller": true, "@metamask/controller-utils": true, + "@metamask/phishing-controller>fastest-levenshtein": true, "@noble/hashes": true, - "punycode": true, - "webpack-cli>fastest-levenshtein": true - } - }, - "@metamask/polling-controller": { - "globals": { - "clearTimeout": true, - "console.error": true, - "setTimeout": true - }, - "packages": { - "@metamask/base-controller": true, - "@metamask/snaps-utils>fast-json-stable-stringify": true, - "uuid": true + "punycode": true } }, "@metamask/post-message-stream": { @@ -2323,57 +2367,16 @@ "packages": { "@metamask/base-controller": true, "@metamask/controller-utils": true, + "@metamask/eth-sig-util": true, "@metamask/keyring-controller": true, "@metamask/logging-controller": true, "@metamask/message-manager>jsonschema": true, - "@metamask/signature-controller>@metamask/eth-sig-util": true, - "@metamask/signature-controller>@metamask/utils": true, + "@metamask/utils": true, "browserify>buffer": true, "uuid": true, "webpack>events": true } }, - "@metamask/signature-controller>@metamask/eth-sig-util": { - "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@ethereumjs/tx>ethereum-cryptography": true, - "@metamask/abi-utils": true, - "@metamask/eth-sig-util>tweetnacl": true, - "@metamask/signature-controller>@metamask/eth-sig-util>@metamask/utils": true, - "@metamask/utils>@scure/base": true, - "browserify>buffer": true - } - }, - "@metamask/signature-controller>@metamask/eth-sig-util>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, - "@metamask/signature-controller>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/smart-transactions-controller": { "globals": { "URLSearchParams": true, @@ -2387,9 +2390,9 @@ "@ethersproject/bytes": true, "@metamask/controller-utils": true, "@metamask/eth-query": true, + "@metamask/gas-fee-controller>@metamask/polling-controller": true, "@metamask/smart-transactions-controller>@ethereumjs/tx": true, "@metamask/smart-transactions-controller>@ethereumjs/util": true, - "@metamask/smart-transactions-controller>@metamask/polling-controller": true, "@metamask/smart-transactions-controller>@metamask/transaction-controller": true, "@metamask/smart-transactions-controller>bignumber.js": true, "browserify>buffer": true, @@ -2397,12 +2400,17 @@ "lodash": true } }, + "@metamask/smart-transactions-controller>@babel/runtime": { + "globals": { + "regeneratorRuntime": "write" + } + }, "@metamask/smart-transactions-controller>@ethereumjs/tx": { "packages": { "@ethereumjs/tx>ethereum-cryptography": true, "@metamask/smart-transactions-controller>@ethereumjs/tx>@ethereumjs/common": true, - "@metamask/smart-transactions-controller>@ethereumjs/util": true, - "eth-lattice-keyring>gridplus-sdk>@ethereumjs/rlp": true + "@metamask/smart-transactions-controller>@ethereumjs/tx>@ethereumjs/rlp": true, + "@metamask/smart-transactions-controller>@ethereumjs/util": true } }, "@metamask/smart-transactions-controller>@ethereumjs/tx>@ethereumjs/common": { @@ -2411,6 +2419,11 @@ "webpack>events": true } }, + "@metamask/smart-transactions-controller>@ethereumjs/tx>@ethereumjs/rlp": { + "globals": { + "TextEncoder": true + } + }, "@metamask/smart-transactions-controller>@ethereumjs/util": { "globals": { "console.warn": true, @@ -2418,10 +2431,15 @@ }, "packages": { "@ethereumjs/tx>ethereum-cryptography": true, - "eth-lattice-keyring>gridplus-sdk>@ethereumjs/rlp": true, + "@metamask/smart-transactions-controller>@ethereumjs/util>@ethereumjs/rlp": true, "webpack>events": true } }, + "@metamask/smart-transactions-controller>@ethereumjs/util>@ethereumjs/rlp": { + "globals": { + "TextEncoder": true + } + }, "@metamask/smart-transactions-controller>@metamask/base-controller": { "globals": { "setTimeout": true @@ -2435,18 +2453,6 @@ "crypto.getRandomValues": true } }, - "@metamask/smart-transactions-controller>@metamask/polling-controller": { - "globals": { - "clearTimeout": true, - "console.error": true, - "setTimeout": true - }, - "packages": { - "@metamask/smart-transactions-controller>@metamask/base-controller": true, - "@metamask/snaps-utils>fast-json-stable-stringify": true, - "uuid": true - } - }, "@metamask/smart-transactions-controller>@metamask/transaction-controller": { "globals": { "clearTimeout": true, @@ -2471,9 +2477,9 @@ "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/nonce-tracker": true, "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/rpc-errors": true, "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/utils": true, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry": true, "bn.js": true, "browserify>buffer": true, - "eth-method-registry": true, "fast-json-patch": true, "lodash": true, "uuid": true, @@ -2483,26 +2489,21 @@ "@metamask/smart-transactions-controller>@metamask/transaction-controller>@ethereumjs/tx": { "packages": { "@ethereumjs/tx>@ethereumjs/common": true, + "@ethereumjs/tx>@ethereumjs/rlp": true, "@ethereumjs/tx>ethereum-cryptography": true, - "@metamask/smart-transactions-controller>@metamask/transaction-controller>@ethereumjs/tx>@ethereumjs/rlp": true, "@metamask/smart-transactions-controller>@metamask/transaction-controller>@ethereumjs/util": true, "browserify>buffer": true, "browserify>insert-module-globals>is-buffer": true } }, - "@metamask/smart-transactions-controller>@metamask/transaction-controller>@ethereumjs/tx>@ethereumjs/rlp": { - "globals": { - "TextEncoder": true - } - }, "@metamask/smart-transactions-controller>@metamask/transaction-controller>@ethereumjs/util": { "globals": { "console.warn": true }, "packages": { + "@ethereumjs/tx>@ethereumjs/rlp": true, "@ethereumjs/tx>@ethereumjs/util>micro-ftch": true, "@ethereumjs/tx>ethereum-cryptography": true, - "@metamask/smart-transactions-controller>@metamask/transaction-controller>@ethereumjs/tx>@ethereumjs/rlp": true, "browserify>buffer": true, "browserify>insert-module-globals>is-buffer": true, "webpack>events": true @@ -2545,6 +2546,58 @@ "semver": true } }, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry": { + "packages": { + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry>@metamask/ethjs-contract": true, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry>@metamask/ethjs-query": true + } + }, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry>@metamask/ethjs-contract": { + "packages": { + "@metamask/ethjs>ethjs-abi": true, + "@metamask/ethjs>js-sha3": true, + "@metamask/smart-transactions-controller>@babel/runtime": true, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry>@metamask/ethjs-contract>@metamask/ethjs-filter": true, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry>@metamask/ethjs-contract>@metamask/ethjs-util": true, + "promise-to-callback": true + } + }, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry>@metamask/ethjs-contract>@metamask/ethjs-filter": { + "globals": { + "clearInterval": true, + "setInterval": true + } + }, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry>@metamask/ethjs-contract>@metamask/ethjs-util": { + "packages": { + "@metamask/ethjs>@metamask/ethjs-util>is-hex-prefixed": true, + "@metamask/ethjs>@metamask/ethjs-util>strip-hex-prefix": true, + "browserify>buffer": true + } + }, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry>@metamask/ethjs-query": { + "globals": { + "console": true + }, + "packages": { + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry>@metamask/ethjs-query>@metamask/ethjs-format": true, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry>@metamask/ethjs-query>@metamask/ethjs-rpc": true, + "promise-to-callback": true + } + }, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry>@metamask/ethjs-query>@metamask/ethjs-format": { + "packages": { + "@metamask/ethjs-query>@metamask/ethjs-format>ethjs-schema": true, + "@metamask/ethjs>@metamask/ethjs-util>strip-hex-prefix": true, + "@metamask/ethjs>@metamask/number-to-bn": true, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry>@metamask/ethjs-contract>@metamask/ethjs-util": true + } + }, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry>@metamask/ethjs-query>@metamask/ethjs-rpc": { + "packages": { + "promise-to-callback": true + } + }, "@metamask/smart-transactions-controller>bignumber.js": { "globals": { "crypto": true, @@ -2561,11 +2614,11 @@ "setTimeout": true }, "packages": { - "@metamask/json-rpc-middleware-stream": true, "@metamask/object-multiplex": true, "@metamask/post-message-stream": true, "@metamask/snaps-controllers>@metamask/base-controller": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, + "@metamask/snaps-controllers>@metamask/json-rpc-middleware-stream": true, "@metamask/snaps-controllers>@metamask/permission-controller": true, "@metamask/snaps-controllers>@metamask/rpc-errors": true, "@metamask/snaps-controllers>@xstate/fsm": true, @@ -2605,6 +2658,17 @@ "@metamask/utils": true } }, + "@metamask/snaps-controllers>@metamask/json-rpc-middleware-stream": { + "globals": { + "console.warn": true, + "setTimeout": true + }, + "packages": { + "@metamask/safe-event-emitter": true, + "@metamask/utils": true, + "readable-stream": true + } + }, "@metamask/snaps-controllers>@metamask/permission-controller": { "globals": { "console.error": true @@ -2958,9 +3022,9 @@ "@metamask/controller-utils": true, "@metamask/eth-query": true, "@metamask/gas-fee-controller": true, + "@metamask/gas-fee-controller>@metamask/polling-controller": true, "@metamask/transaction-controller": true, "@metamask/user-operation-controller>@metamask/base-controller": true, - "@metamask/user-operation-controller>@metamask/polling-controller": true, "@metamask/user-operation-controller>@metamask/rpc-errors": true, "@metamask/user-operation-controller>@metamask/utils": true, "bn.js": true, @@ -2978,18 +3042,6 @@ "immer": true } }, - "@metamask/user-operation-controller>@metamask/polling-controller": { - "globals": { - "clearTimeout": true, - "console.error": true, - "setTimeout": true - }, - "packages": { - "@metamask/snaps-utils>fast-json-stable-stringify": true, - "@metamask/user-operation-controller>@metamask/base-controller": true, - "uuid": true - } - }, "@metamask/user-operation-controller>@metamask/rpc-errors": { "packages": { "@metamask/rpc-errors>fast-safe-stringify": true, @@ -4025,16 +4077,59 @@ "setInterval": true }, "packages": { - "@ethereumjs/tx": true, "@ethereumjs/tx>@ethereumjs/util": true, "bn.js": true, "browserify>buffer": true, "crypto-browserify": true, + "eth-lattice-keyring>@ethereumjs/tx": true, "eth-lattice-keyring>gridplus-sdk": true, "eth-lattice-keyring>rlp": true, "webpack>events": true } }, + "eth-lattice-keyring>@ethereumjs/tx": { + "packages": { + "@ethereumjs/tx>@ethereumjs/common": true, + "@ethereumjs/tx>@ethereumjs/rlp": true, + "@ethereumjs/tx>@ethereumjs/util": true, + "@ethersproject/providers": true, + "browserify>buffer": true, + "browserify>insert-module-globals>is-buffer": true, + "eth-lattice-keyring>@ethereumjs/tx>@chainsafe/ssz": true, + "eth-lattice-keyring>@ethereumjs/tx>ethereum-cryptography": true + } + }, + "eth-lattice-keyring>@ethereumjs/tx>@chainsafe/ssz": { + "packages": { + "browserify": true, + "browserify>buffer": true, + "eth-lattice-keyring>@ethereumjs/tx>@chainsafe/ssz>@chainsafe/persistent-merkle-tree": true, + "eth-lattice-keyring>@ethereumjs/tx>@chainsafe/ssz>case": true + } + }, + "eth-lattice-keyring>@ethereumjs/tx>@chainsafe/ssz>@chainsafe/persistent-merkle-tree": { + "globals": { + "WeakRef": true + }, + "packages": { + "browserify": true + } + }, + "eth-lattice-keyring>@ethereumjs/tx>ethereum-cryptography": { + "globals": { + "TextDecoder": true, + "crypto": true + }, + "packages": { + "eth-lattice-keyring>@ethereumjs/tx>ethereum-cryptography>@noble/hashes": true + } + }, + "eth-lattice-keyring>@ethereumjs/tx>ethereum-cryptography>@noble/hashes": { + "globals": { + "TextEncoder": true, + "crypto": true + } + }, "eth-lattice-keyring>gridplus-sdk": { "globals": { "AbortController": true, @@ -4052,59 +4147,67 @@ "packages": { "@ethereumjs/tx>@ethereumjs/common>crc-32": true, "@ethersproject/abi": true, - "@metamask/eth-sig-util": true, "@metamask/ethjs>js-sha3": true, "@metamask/keyring-api>bech32": true, - "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>buffer": true, "eth-lattice-keyring>gridplus-sdk>@ethereumjs/common": true, - "eth-lattice-keyring>gridplus-sdk>@ethereumjs/rlp": true, "eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx": true, "eth-lattice-keyring>gridplus-sdk>aes-js": true, "eth-lattice-keyring>gridplus-sdk>bignumber.js": true, + "eth-lattice-keyring>gridplus-sdk>bitwise": true, "eth-lattice-keyring>gridplus-sdk>borc": true, - "eth-lattice-keyring>gridplus-sdk>bs58check": true, - "eth-lattice-keyring>gridplus-sdk>secp256k1": true, + "eth-lattice-keyring>gridplus-sdk>elliptic": true, + "eth-lattice-keyring>gridplus-sdk>eth-eip712-util-browser": true, + "eth-lattice-keyring>gridplus-sdk>rlp": true, "eth-lattice-keyring>gridplus-sdk>uuid": true, + "ethereumjs-util>ethereum-cryptography>bs58check": true, "ethers>@ethersproject/sha2>hash.js": true, + "ganache>secp256k1": true, "lodash": true } }, "eth-lattice-keyring>gridplus-sdk>@ethereumjs/common": { "packages": { - "eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>@ethereumjs/util": true, + "@ethereumjs/tx>@ethereumjs/common>crc-32": true, + "@ethereumjs/tx>@ethereumjs/util": true, + "browserify>buffer": true, "webpack>events": true } }, - "eth-lattice-keyring>gridplus-sdk>@ethereumjs/rlp": { - "globals": { - "TextEncoder": true - } - }, "eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx": { "packages": { - "@ethereumjs/tx>ethereum-cryptography": true, - "eth-lattice-keyring>gridplus-sdk>@ethereumjs/rlp": true, + "@ethereumjs/tx>@ethereumjs/rlp": true, + "@ethereumjs/tx>@ethereumjs/util": true, + "@ethersproject/providers": true, + "browserify>buffer": true, + "browserify>insert-module-globals>is-buffer": true, + "eth-lattice-keyring>@ethereumjs/tx>@chainsafe/ssz": true, "eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>@ethereumjs/common": true, - "eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>@ethereumjs/util": true + "eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>ethereum-cryptography": true } }, "eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>@ethereumjs/common": { "packages": { - "eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>@ethereumjs/util": true, + "@ethereumjs/tx>@ethereumjs/common>crc-32": true, + "@ethereumjs/tx>@ethereumjs/util": true, + "browserify>buffer": true, "webpack>events": true } }, - "eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>@ethereumjs/util": { + "eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>ethereum-cryptography": { "globals": { - "console.warn": true, - "fetch": true + "TextDecoder": true, + "crypto": true }, "packages": { - "@ethereumjs/tx>ethereum-cryptography": true, - "eth-lattice-keyring>gridplus-sdk>@ethereumjs/rlp": true, - "webpack>events": true + "eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>ethereum-cryptography>@noble/hashes": true + } + }, + "eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>ethereum-cryptography>@noble/hashes": { + "globals": { + "TextEncoder": true, + "crypto": true } }, "eth-lattice-keyring>gridplus-sdk>aes-js": { @@ -4118,6 +4221,11 @@ "define": true } }, + "eth-lattice-keyring>gridplus-sdk>bitwise": { + "packages": { + "browserify>buffer": true + } + }, "eth-lattice-keyring>gridplus-sdk>borc": { "globals": { "console": true @@ -4139,24 +4247,33 @@ "globals": { "URL": true, "URLSearchParams": true, - "location": true, - "navigator": true + "location": true } }, - "eth-lattice-keyring>gridplus-sdk>bs58check": { + "eth-lattice-keyring>gridplus-sdk>elliptic": { "packages": { - "@noble/hashes": true, - "eth-lattice-keyring>gridplus-sdk>bs58check>bs58": true + "@metamask/ppom-validator>elliptic>brorand": true, + "@metamask/ppom-validator>elliptic>hmac-drbg": true, + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, + "@metamask/ppom-validator>elliptic>minimalistic-crypto-utils": true, + "bn.js": true, + "ethers>@ethersproject/sha2>hash.js": true, + "pumpify>inherits": true } }, - "eth-lattice-keyring>gridplus-sdk>bs58check>bs58": { + "eth-lattice-keyring>gridplus-sdk>eth-eip712-util-browser": { + "globals": { + "intToBuffer": true + }, "packages": { - "eth-lattice-keyring>gridplus-sdk>bs58check>bs58>base-x": true + "@metamask/ethjs>js-sha3": true, + "bn.js": true, + "buffer": true } }, - "eth-lattice-keyring>gridplus-sdk>secp256k1": { - "packages": { - "@metamask/ppom-validator>elliptic": true + "eth-lattice-keyring>gridplus-sdk>rlp": { + "globals": { + "TextEncoder": true } }, "eth-lattice-keyring>gridplus-sdk>uuid": { @@ -4480,9 +4597,20 @@ "ethers>@ethersproject/signing-key": { "packages": { "@ethersproject/bytes": true, - "@metamask/ppom-validator>elliptic": true, "ethers>@ethersproject/logger": true, - "ethers>@ethersproject/properties": true + "ethers>@ethersproject/properties": true, + "ethers>@ethersproject/signing-key>elliptic": true + } + }, + "ethers>@ethersproject/signing-key>elliptic": { + "packages": { + "@metamask/ppom-validator>elliptic>brorand": true, + "@metamask/ppom-validator>elliptic>hmac-drbg": true, + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, + "@metamask/ppom-validator>elliptic>minimalistic-crypto-utils": true, + "bn.js": true, + "ethers>@ethersproject/sha2>hash.js": true, + "pumpify>inherits": true } }, "ethers>@ethersproject/solidity": { @@ -4615,6 +4743,16 @@ "stream-http": true } }, + "json-rpc-middleware-stream": { + "globals": { + "console.warn": true, + "setTimeout": true + }, + "packages": { + "@metamask/safe-event-emitter": true, + "readable-stream": true + } + }, "koa>content-disposition>safe-buffer": { "packages": { "browserify>buffer": true @@ -5131,10 +5269,10 @@ "document": true }, "packages": { - "@babel/runtime": true, "prop-types": true, "react": true, "react-dom": true, + "react-redux>@babel/runtime": true, "react-redux>hoist-non-react-statics": true, "react-redux>react-is": true } @@ -5358,6 +5496,16 @@ "webpack>events": true } }, + "readable-stream-2>core-util-is": { + "packages": { + "browserify>insert-module-globals>is-buffer": true + } + }, + "readable-stream-2>process-nextick-args": { + "packages": { + "process": true + } + }, "readable-stream>util-deprecate": { "globals": { "console.trace": true, diff --git a/lavamoat/browserify/flask/policy.json b/lavamoat/browserify/flask/policy.json index ac719964d896..ef4c915328c2 100644 --- a/lavamoat/browserify/flask/policy.json +++ b/lavamoat/browserify/flask/policy.json @@ -150,7 +150,7 @@ "console.warn": true }, "packages": { - "@ethereumjs/tx>@ethereumjs/util>@ethereumjs/rlp": true, + "@ethereumjs/tx>@ethereumjs/rlp": true, "@ethereumjs/tx>@ethereumjs/util>micro-ftch": true, "@ethereumjs/tx>ethereum-cryptography": true, "browserify>buffer": true, @@ -158,11 +158,6 @@ "webpack>events": true } }, - "@ethereumjs/tx>@ethereumjs/util>@ethereumjs/rlp": { - "globals": { - "TextEncoder": true - } - }, "@ethereumjs/tx>@ethereumjs/util>micro-ftch": { "globals": { "Headers": true, @@ -187,28 +182,16 @@ "crypto": true }, "packages": { - "@ethereumjs/tx>ethereum-cryptography>@noble/hashes": true, "@ethereumjs/tx>ethereum-cryptography>@scure/bip32": true, - "@metamask/message-signing-snap>@noble/curves": true - } - }, - "@ethereumjs/tx>ethereum-cryptography>@noble/hashes": { - "globals": { - "TextEncoder": true, - "crypto": true + "@metamask/message-signing-snap>@noble/curves": true, + "@noble/hashes": true } }, "@ethereumjs/tx>ethereum-cryptography>@scure/bip32": { "packages": { - "@ethereumjs/tx>ethereum-cryptography>@scure/bip32>@noble/hashes": true, "@metamask/message-signing-snap>@noble/curves": true, - "@metamask/utils>@scure/base": true - } - }, - "@ethereumjs/tx>ethereum-cryptography>@scure/bip32>@noble/hashes": { - "globals": { - "TextEncoder": true, - "crypto": true + "@metamask/utils>@scure/base": true, + "@noble/hashes": true } }, "@ethersproject/abi": { @@ -375,7 +358,7 @@ "@ethereumjs/tx": true, "@keystonehq/bc-ur-registry-eth": true, "@keystonehq/metamask-airgapped-keyring>@keystonehq/base-eth-keyring": true, - "@metamask/obs-store": true, + "@keystonehq/metamask-airgapped-keyring>@metamask/obs-store": true, "browserify>buffer": true, "ethereumjs-util>rlp": true, "uuid": true, @@ -398,6 +381,54 @@ "TextEncoder": true } }, + "@keystonehq/metamask-airgapped-keyring>@metamask/obs-store": { + "packages": { + "@keystonehq/metamask-airgapped-keyring>@metamask/obs-store>@metamask/safe-event-emitter": true, + "@keystonehq/metamask-airgapped-keyring>@metamask/obs-store>through2": true, + "stream-browserify": true + } + }, + "@keystonehq/metamask-airgapped-keyring>@metamask/obs-store>@metamask/safe-event-emitter": { + "globals": { + "setTimeout": true + }, + "packages": { + "webpack>events": true + } + }, + "@keystonehq/metamask-airgapped-keyring>@metamask/obs-store>through2": { + "packages": { + "@keystonehq/metamask-airgapped-keyring>@metamask/obs-store>through2>readable-stream": true, + "browserify>util": true, + "process": true, + "watchify>xtend": true + } + }, + "@keystonehq/metamask-airgapped-keyring>@metamask/obs-store>through2>readable-stream": { + "packages": { + "@keystonehq/metamask-airgapped-keyring>@metamask/obs-store>through2>readable-stream>isarray": true, + "@keystonehq/metamask-airgapped-keyring>@metamask/obs-store>through2>readable-stream>safe-buffer": true, + "@keystonehq/metamask-airgapped-keyring>@metamask/obs-store>through2>readable-stream>string_decoder": true, + "browserify>browser-resolve": true, + "browserify>timers-browserify": true, + "process": true, + "pumpify>inherits": true, + "readable-stream-2>core-util-is": true, + "readable-stream-2>process-nextick-args": true, + "readable-stream>util-deprecate": true, + "webpack>events": true + } + }, + "@keystonehq/metamask-airgapped-keyring>@metamask/obs-store>through2>readable-stream>safe-buffer": { + "packages": { + "browserify>buffer": true + } + }, + "@keystonehq/metamask-airgapped-keyring>@metamask/obs-store>through2>readable-stream>string_decoder": { + "packages": { + "@keystonehq/metamask-airgapped-keyring>@metamask/obs-store>through2>readable-stream>safe-buffer": true + } + }, "@lavamoat/lavadome-react": { "globals": { "Document.prototype": true, @@ -674,14 +705,14 @@ "@ethersproject/providers": true, "@metamask/abi-utils": true, "@metamask/assets-controllers>@metamask/polling-controller": true, - "@metamask/assets-controllers>@metamask/utils": true, + "@metamask/assets-controllers>@metamask/rpc-errors": true, "@metamask/base-controller": true, "@metamask/contract-metadata": true, "@metamask/controller-utils": true, "@metamask/eth-query": true, "@metamask/metamask-eth-abis": true, "@metamask/name-controller>async-mutex": true, - "@metamask/rpc-errors": true, + "@metamask/utils": true, "bn.js": true, "cockatiel": true, "ethers>@ethersproject/address": true, @@ -702,19 +733,10 @@ "uuid": true } }, - "@metamask/assets-controllers>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, + "@metamask/assets-controllers>@metamask/rpc-errors": { "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true + "@metamask/rpc-errors>fast-safe-stringify": true, + "@metamask/utils": true } }, "@metamask/base-controller": { @@ -841,13 +863,70 @@ "console.error": true }, "packages": { - "@metamask/eth-query": true, - "@metamask/json-rpc-engine": true, - "@metamask/name-controller>async-mutex": true, + "@metamask/eth-json-rpc-filters>@metamask/eth-query": true, + "@metamask/eth-json-rpc-filters>@metamask/json-rpc-engine": true, + "@metamask/eth-json-rpc-filters>async-mutex": true, "@metamask/safe-event-emitter": true, "pify": true } }, + "@metamask/eth-json-rpc-filters>@metamask/eth-query": { + "packages": { + "@metamask/eth-query>json-rpc-random-id": true, + "watchify>xtend": true + } + }, + "@metamask/eth-json-rpc-filters>@metamask/json-rpc-engine": { + "packages": { + "@metamask/eth-json-rpc-filters>@metamask/json-rpc-engine>@metamask/rpc-errors": true, + "@metamask/eth-json-rpc-filters>@metamask/json-rpc-engine>@metamask/utils": true, + "@metamask/safe-event-emitter": true + } + }, + "@metamask/eth-json-rpc-filters>@metamask/json-rpc-engine>@metamask/rpc-errors": { + "packages": { + "@metamask/eth-json-rpc-filters>@metamask/json-rpc-engine>@metamask/rpc-errors>@metamask/utils": true, + "@metamask/rpc-errors>fast-safe-stringify": true + } + }, + "@metamask/eth-json-rpc-filters>@metamask/json-rpc-engine>@metamask/rpc-errors>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/utils>@metamask/superstruct": true, + "@metamask/utils>@scure/base": true, + "@metamask/utils>pony-cause": true, + "@noble/hashes": true, + "browserify>buffer": true, + "nock>debug": true, + "semver": true + } + }, + "@metamask/eth-json-rpc-filters>@metamask/json-rpc-engine>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/utils>@metamask/superstruct": true, + "@metamask/utils>@scure/base": true, + "@metamask/utils>pony-cause": true, + "@noble/hashes": true, + "browserify>buffer": true, + "nock>debug": true, + "semver": true + } + }, + "@metamask/eth-json-rpc-filters>async-mutex": { + "globals": { + "setTimeout": true + }, + "packages": { + "@swc/helpers>tslib": true + } + }, "@metamask/eth-json-rpc-middleware": { "globals": { "URL": true, @@ -887,19 +966,14 @@ }, "packages": { "@ethereumjs/tx": true, + "@ethereumjs/tx>@ethereumjs/rlp": true, "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/eth-ledger-bridge-keyring>@ethereumjs/rlp": true, "@metamask/eth-sig-util": true, "@metamask/eth-trezor-keyring>hdkey": true, "browserify>buffer": true, "webpack>events": true } }, - "@metamask/eth-ledger-bridge-keyring>@ethereumjs/rlp": { - "globals": { - "TextEncoder": true - } - }, "@metamask/eth-query": { "packages": { "@metamask/eth-query>json-rpc-random-id": true, @@ -1241,24 +1315,24 @@ "uuid": true } }, - "@metamask/gas-fee-controller>@metamask/base-controller": { + "@metamask/gas-fee-controller>@metamask/polling-controller": { "globals": { + "clearTimeout": true, + "console.error": true, "setTimeout": true }, "packages": { - "immer": true + "@metamask/gas-fee-controller>@metamask/polling-controller>@metamask/base-controller": true, + "@metamask/snaps-utils>fast-json-stable-stringify": true, + "uuid": true } }, - "@metamask/gas-fee-controller>@metamask/polling-controller": { + "@metamask/gas-fee-controller>@metamask/polling-controller>@metamask/base-controller": { "globals": { - "clearTimeout": true, - "console.error": true, "setTimeout": true }, "packages": { - "@metamask/gas-fee-controller>@metamask/base-controller": true, - "@metamask/snaps-utils>fast-json-stable-stringify": true, - "uuid": true + "immer": true } }, "@metamask/jazzicon": { @@ -1315,17 +1389,6 @@ "semver": true } }, - "@metamask/json-rpc-middleware-stream": { - "globals": { - "console.warn": true, - "setTimeout": true - }, - "packages": { - "@metamask/safe-event-emitter": true, - "@metamask/utils": true, - "readable-stream": true - } - }, "@metamask/keyring-api": { "globals": { "URL": true @@ -1465,13 +1528,7 @@ "TextEncoder": true }, "packages": { - "@metamask/message-signing-snap>@noble/curves>@noble/hashes": true - } - }, - "@metamask/message-signing-snap>@noble/curves>@noble/hashes": { - "globals": { - "TextEncoder": true, - "crypto": true + "@noble/hashes": true } }, "@metamask/name-controller": { @@ -1753,7 +1810,6 @@ "@metamask/notification-services-controller>@contentful/rich-text-html-renderer": true, "@metamask/notification-services-controller>firebase": true, "@metamask/profile-sync-controller": true, - "@metamask/utils": true, "bignumber.js": true, "loglevel": true, "uuid": true @@ -2025,21 +2081,9 @@ "@ethereumjs/tx>ethereum-cryptography": true, "@metamask/base-controller": true, "@metamask/controller-utils": true, + "@metamask/phishing-controller>fastest-levenshtein": true, "@noble/hashes": true, - "punycode": true, - "webpack-cli>fastest-levenshtein": true - } - }, - "@metamask/polling-controller": { - "globals": { - "clearTimeout": true, - "console.error": true, - "setTimeout": true - }, - "packages": { - "@metamask/base-controller": true, - "@metamask/snaps-utils>fast-json-stable-stringify": true, - "uuid": true + "punycode": true } }, "@metamask/post-message-stream": { @@ -2323,57 +2367,16 @@ "packages": { "@metamask/base-controller": true, "@metamask/controller-utils": true, + "@metamask/eth-sig-util": true, "@metamask/keyring-controller": true, "@metamask/logging-controller": true, "@metamask/message-manager>jsonschema": true, - "@metamask/signature-controller>@metamask/eth-sig-util": true, - "@metamask/signature-controller>@metamask/utils": true, + "@metamask/utils": true, "browserify>buffer": true, "uuid": true, "webpack>events": true } }, - "@metamask/signature-controller>@metamask/eth-sig-util": { - "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@ethereumjs/tx>ethereum-cryptography": true, - "@metamask/abi-utils": true, - "@metamask/eth-sig-util>tweetnacl": true, - "@metamask/signature-controller>@metamask/eth-sig-util>@metamask/utils": true, - "@metamask/utils>@scure/base": true, - "browserify>buffer": true - } - }, - "@metamask/signature-controller>@metamask/eth-sig-util>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, - "@metamask/signature-controller>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/smart-transactions-controller": { "globals": { "URLSearchParams": true, @@ -2387,9 +2390,9 @@ "@ethersproject/bytes": true, "@metamask/controller-utils": true, "@metamask/eth-query": true, + "@metamask/gas-fee-controller>@metamask/polling-controller": true, "@metamask/smart-transactions-controller>@ethereumjs/tx": true, "@metamask/smart-transactions-controller>@ethereumjs/util": true, - "@metamask/smart-transactions-controller>@metamask/polling-controller": true, "@metamask/smart-transactions-controller>@metamask/transaction-controller": true, "@metamask/smart-transactions-controller>bignumber.js": true, "browserify>buffer": true, @@ -2397,12 +2400,17 @@ "lodash": true } }, + "@metamask/smart-transactions-controller>@babel/runtime": { + "globals": { + "regeneratorRuntime": "write" + } + }, "@metamask/smart-transactions-controller>@ethereumjs/tx": { "packages": { "@ethereumjs/tx>ethereum-cryptography": true, "@metamask/smart-transactions-controller>@ethereumjs/tx>@ethereumjs/common": true, - "@metamask/smart-transactions-controller>@ethereumjs/util": true, - "eth-lattice-keyring>gridplus-sdk>@ethereumjs/rlp": true + "@metamask/smart-transactions-controller>@ethereumjs/tx>@ethereumjs/rlp": true, + "@metamask/smart-transactions-controller>@ethereumjs/util": true } }, "@metamask/smart-transactions-controller>@ethereumjs/tx>@ethereumjs/common": { @@ -2411,6 +2419,11 @@ "webpack>events": true } }, + "@metamask/smart-transactions-controller>@ethereumjs/tx>@ethereumjs/rlp": { + "globals": { + "TextEncoder": true + } + }, "@metamask/smart-transactions-controller>@ethereumjs/util": { "globals": { "console.warn": true, @@ -2418,10 +2431,15 @@ }, "packages": { "@ethereumjs/tx>ethereum-cryptography": true, - "eth-lattice-keyring>gridplus-sdk>@ethereumjs/rlp": true, + "@metamask/smart-transactions-controller>@ethereumjs/util>@ethereumjs/rlp": true, "webpack>events": true } }, + "@metamask/smart-transactions-controller>@ethereumjs/util>@ethereumjs/rlp": { + "globals": { + "TextEncoder": true + } + }, "@metamask/smart-transactions-controller>@metamask/base-controller": { "globals": { "setTimeout": true @@ -2435,18 +2453,6 @@ "crypto.getRandomValues": true } }, - "@metamask/smart-transactions-controller>@metamask/polling-controller": { - "globals": { - "clearTimeout": true, - "console.error": true, - "setTimeout": true - }, - "packages": { - "@metamask/smart-transactions-controller>@metamask/base-controller": true, - "@metamask/snaps-utils>fast-json-stable-stringify": true, - "uuid": true - } - }, "@metamask/smart-transactions-controller>@metamask/transaction-controller": { "globals": { "clearTimeout": true, @@ -2471,9 +2477,9 @@ "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/nonce-tracker": true, "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/rpc-errors": true, "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/utils": true, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry": true, "bn.js": true, "browserify>buffer": true, - "eth-method-registry": true, "fast-json-patch": true, "lodash": true, "uuid": true, @@ -2483,26 +2489,21 @@ "@metamask/smart-transactions-controller>@metamask/transaction-controller>@ethereumjs/tx": { "packages": { "@ethereumjs/tx>@ethereumjs/common": true, + "@ethereumjs/tx>@ethereumjs/rlp": true, "@ethereumjs/tx>ethereum-cryptography": true, - "@metamask/smart-transactions-controller>@metamask/transaction-controller>@ethereumjs/tx>@ethereumjs/rlp": true, "@metamask/smart-transactions-controller>@metamask/transaction-controller>@ethereumjs/util": true, "browserify>buffer": true, "browserify>insert-module-globals>is-buffer": true } }, - "@metamask/smart-transactions-controller>@metamask/transaction-controller>@ethereumjs/tx>@ethereumjs/rlp": { - "globals": { - "TextEncoder": true - } - }, "@metamask/smart-transactions-controller>@metamask/transaction-controller>@ethereumjs/util": { "globals": { "console.warn": true }, "packages": { + "@ethereumjs/tx>@ethereumjs/rlp": true, "@ethereumjs/tx>@ethereumjs/util>micro-ftch": true, "@ethereumjs/tx>ethereum-cryptography": true, - "@metamask/smart-transactions-controller>@metamask/transaction-controller>@ethereumjs/tx>@ethereumjs/rlp": true, "browserify>buffer": true, "browserify>insert-module-globals>is-buffer": true, "webpack>events": true @@ -2545,6 +2546,58 @@ "semver": true } }, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry": { + "packages": { + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry>@metamask/ethjs-contract": true, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry>@metamask/ethjs-query": true + } + }, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry>@metamask/ethjs-contract": { + "packages": { + "@metamask/ethjs>ethjs-abi": true, + "@metamask/ethjs>js-sha3": true, + "@metamask/smart-transactions-controller>@babel/runtime": true, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry>@metamask/ethjs-contract>@metamask/ethjs-filter": true, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry>@metamask/ethjs-contract>@metamask/ethjs-util": true, + "promise-to-callback": true + } + }, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry>@metamask/ethjs-contract>@metamask/ethjs-filter": { + "globals": { + "clearInterval": true, + "setInterval": true + } + }, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry>@metamask/ethjs-contract>@metamask/ethjs-util": { + "packages": { + "@metamask/ethjs>@metamask/ethjs-util>is-hex-prefixed": true, + "@metamask/ethjs>@metamask/ethjs-util>strip-hex-prefix": true, + "browserify>buffer": true + } + }, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry>@metamask/ethjs-query": { + "globals": { + "console": true + }, + "packages": { + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry>@metamask/ethjs-query>@metamask/ethjs-format": true, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry>@metamask/ethjs-query>@metamask/ethjs-rpc": true, + "promise-to-callback": true + } + }, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry>@metamask/ethjs-query>@metamask/ethjs-format": { + "packages": { + "@metamask/ethjs-query>@metamask/ethjs-format>ethjs-schema": true, + "@metamask/ethjs>@metamask/ethjs-util>strip-hex-prefix": true, + "@metamask/ethjs>@metamask/number-to-bn": true, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry>@metamask/ethjs-contract>@metamask/ethjs-util": true + } + }, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry>@metamask/ethjs-query>@metamask/ethjs-rpc": { + "packages": { + "promise-to-callback": true + } + }, "@metamask/smart-transactions-controller>bignumber.js": { "globals": { "crypto": true, @@ -2561,11 +2614,11 @@ "setTimeout": true }, "packages": { - "@metamask/json-rpc-middleware-stream": true, "@metamask/object-multiplex": true, "@metamask/post-message-stream": true, "@metamask/snaps-controllers>@metamask/base-controller": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, + "@metamask/snaps-controllers>@metamask/json-rpc-middleware-stream": true, "@metamask/snaps-controllers>@metamask/permission-controller": true, "@metamask/snaps-controllers>@metamask/rpc-errors": true, "@metamask/snaps-controllers>@xstate/fsm": true, @@ -2605,6 +2658,17 @@ "@metamask/utils": true } }, + "@metamask/snaps-controllers>@metamask/json-rpc-middleware-stream": { + "globals": { + "console.warn": true, + "setTimeout": true + }, + "packages": { + "@metamask/safe-event-emitter": true, + "@metamask/utils": true, + "readable-stream": true + } + }, "@metamask/snaps-controllers>@metamask/permission-controller": { "globals": { "console.error": true @@ -2958,9 +3022,9 @@ "@metamask/controller-utils": true, "@metamask/eth-query": true, "@metamask/gas-fee-controller": true, + "@metamask/gas-fee-controller>@metamask/polling-controller": true, "@metamask/transaction-controller": true, "@metamask/user-operation-controller>@metamask/base-controller": true, - "@metamask/user-operation-controller>@metamask/polling-controller": true, "@metamask/user-operation-controller>@metamask/rpc-errors": true, "@metamask/user-operation-controller>@metamask/utils": true, "bn.js": true, @@ -2978,18 +3042,6 @@ "immer": true } }, - "@metamask/user-operation-controller>@metamask/polling-controller": { - "globals": { - "clearTimeout": true, - "console.error": true, - "setTimeout": true - }, - "packages": { - "@metamask/snaps-utils>fast-json-stable-stringify": true, - "@metamask/user-operation-controller>@metamask/base-controller": true, - "uuid": true - } - }, "@metamask/user-operation-controller>@metamask/rpc-errors": { "packages": { "@metamask/rpc-errors>fast-safe-stringify": true, @@ -4025,16 +4077,59 @@ "setInterval": true }, "packages": { - "@ethereumjs/tx": true, "@ethereumjs/tx>@ethereumjs/util": true, "bn.js": true, "browserify>buffer": true, "crypto-browserify": true, + "eth-lattice-keyring>@ethereumjs/tx": true, "eth-lattice-keyring>gridplus-sdk": true, "eth-lattice-keyring>rlp": true, "webpack>events": true } }, + "eth-lattice-keyring>@ethereumjs/tx": { + "packages": { + "@ethereumjs/tx>@ethereumjs/common": true, + "@ethereumjs/tx>@ethereumjs/rlp": true, + "@ethereumjs/tx>@ethereumjs/util": true, + "@ethersproject/providers": true, + "browserify>buffer": true, + "browserify>insert-module-globals>is-buffer": true, + "eth-lattice-keyring>@ethereumjs/tx>@chainsafe/ssz": true, + "eth-lattice-keyring>@ethereumjs/tx>ethereum-cryptography": true + } + }, + "eth-lattice-keyring>@ethereumjs/tx>@chainsafe/ssz": { + "packages": { + "browserify": true, + "browserify>buffer": true, + "eth-lattice-keyring>@ethereumjs/tx>@chainsafe/ssz>@chainsafe/persistent-merkle-tree": true, + "eth-lattice-keyring>@ethereumjs/tx>@chainsafe/ssz>case": true + } + }, + "eth-lattice-keyring>@ethereumjs/tx>@chainsafe/ssz>@chainsafe/persistent-merkle-tree": { + "globals": { + "WeakRef": true + }, + "packages": { + "browserify": true + } + }, + "eth-lattice-keyring>@ethereumjs/tx>ethereum-cryptography": { + "globals": { + "TextDecoder": true, + "crypto": true + }, + "packages": { + "eth-lattice-keyring>@ethereumjs/tx>ethereum-cryptography>@noble/hashes": true + } + }, + "eth-lattice-keyring>@ethereumjs/tx>ethereum-cryptography>@noble/hashes": { + "globals": { + "TextEncoder": true, + "crypto": true + } + }, "eth-lattice-keyring>gridplus-sdk": { "globals": { "AbortController": true, @@ -4052,59 +4147,67 @@ "packages": { "@ethereumjs/tx>@ethereumjs/common>crc-32": true, "@ethersproject/abi": true, - "@metamask/eth-sig-util": true, "@metamask/ethjs>js-sha3": true, "@metamask/keyring-api>bech32": true, - "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>buffer": true, "eth-lattice-keyring>gridplus-sdk>@ethereumjs/common": true, - "eth-lattice-keyring>gridplus-sdk>@ethereumjs/rlp": true, "eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx": true, "eth-lattice-keyring>gridplus-sdk>aes-js": true, "eth-lattice-keyring>gridplus-sdk>bignumber.js": true, + "eth-lattice-keyring>gridplus-sdk>bitwise": true, "eth-lattice-keyring>gridplus-sdk>borc": true, - "eth-lattice-keyring>gridplus-sdk>bs58check": true, - "eth-lattice-keyring>gridplus-sdk>secp256k1": true, + "eth-lattice-keyring>gridplus-sdk>elliptic": true, + "eth-lattice-keyring>gridplus-sdk>eth-eip712-util-browser": true, + "eth-lattice-keyring>gridplus-sdk>rlp": true, "eth-lattice-keyring>gridplus-sdk>uuid": true, + "ethereumjs-util>ethereum-cryptography>bs58check": true, "ethers>@ethersproject/sha2>hash.js": true, + "ganache>secp256k1": true, "lodash": true } }, "eth-lattice-keyring>gridplus-sdk>@ethereumjs/common": { "packages": { - "eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>@ethereumjs/util": true, + "@ethereumjs/tx>@ethereumjs/common>crc-32": true, + "@ethereumjs/tx>@ethereumjs/util": true, + "browserify>buffer": true, "webpack>events": true } }, - "eth-lattice-keyring>gridplus-sdk>@ethereumjs/rlp": { - "globals": { - "TextEncoder": true - } - }, "eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx": { "packages": { - "@ethereumjs/tx>ethereum-cryptography": true, - "eth-lattice-keyring>gridplus-sdk>@ethereumjs/rlp": true, + "@ethereumjs/tx>@ethereumjs/rlp": true, + "@ethereumjs/tx>@ethereumjs/util": true, + "@ethersproject/providers": true, + "browserify>buffer": true, + "browserify>insert-module-globals>is-buffer": true, + "eth-lattice-keyring>@ethereumjs/tx>@chainsafe/ssz": true, "eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>@ethereumjs/common": true, - "eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>@ethereumjs/util": true + "eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>ethereum-cryptography": true } }, "eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>@ethereumjs/common": { "packages": { - "eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>@ethereumjs/util": true, + "@ethereumjs/tx>@ethereumjs/common>crc-32": true, + "@ethereumjs/tx>@ethereumjs/util": true, + "browserify>buffer": true, "webpack>events": true } }, - "eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>@ethereumjs/util": { + "eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>ethereum-cryptography": { "globals": { - "console.warn": true, - "fetch": true + "TextDecoder": true, + "crypto": true }, "packages": { - "@ethereumjs/tx>ethereum-cryptography": true, - "eth-lattice-keyring>gridplus-sdk>@ethereumjs/rlp": true, - "webpack>events": true + "eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>ethereum-cryptography>@noble/hashes": true + } + }, + "eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>ethereum-cryptography>@noble/hashes": { + "globals": { + "TextEncoder": true, + "crypto": true } }, "eth-lattice-keyring>gridplus-sdk>aes-js": { @@ -4118,6 +4221,11 @@ "define": true } }, + "eth-lattice-keyring>gridplus-sdk>bitwise": { + "packages": { + "browserify>buffer": true + } + }, "eth-lattice-keyring>gridplus-sdk>borc": { "globals": { "console": true @@ -4139,24 +4247,33 @@ "globals": { "URL": true, "URLSearchParams": true, - "location": true, - "navigator": true + "location": true } }, - "eth-lattice-keyring>gridplus-sdk>bs58check": { + "eth-lattice-keyring>gridplus-sdk>elliptic": { "packages": { - "@noble/hashes": true, - "eth-lattice-keyring>gridplus-sdk>bs58check>bs58": true + "@metamask/ppom-validator>elliptic>brorand": true, + "@metamask/ppom-validator>elliptic>hmac-drbg": true, + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, + "@metamask/ppom-validator>elliptic>minimalistic-crypto-utils": true, + "bn.js": true, + "ethers>@ethersproject/sha2>hash.js": true, + "pumpify>inherits": true } }, - "eth-lattice-keyring>gridplus-sdk>bs58check>bs58": { + "eth-lattice-keyring>gridplus-sdk>eth-eip712-util-browser": { + "globals": { + "intToBuffer": true + }, "packages": { - "eth-lattice-keyring>gridplus-sdk>bs58check>bs58>base-x": true + "@metamask/ethjs>js-sha3": true, + "bn.js": true, + "buffer": true } }, - "eth-lattice-keyring>gridplus-sdk>secp256k1": { - "packages": { - "@metamask/ppom-validator>elliptic": true + "eth-lattice-keyring>gridplus-sdk>rlp": { + "globals": { + "TextEncoder": true } }, "eth-lattice-keyring>gridplus-sdk>uuid": { @@ -4480,9 +4597,20 @@ "ethers>@ethersproject/signing-key": { "packages": { "@ethersproject/bytes": true, - "@metamask/ppom-validator>elliptic": true, "ethers>@ethersproject/logger": true, - "ethers>@ethersproject/properties": true + "ethers>@ethersproject/properties": true, + "ethers>@ethersproject/signing-key>elliptic": true + } + }, + "ethers>@ethersproject/signing-key>elliptic": { + "packages": { + "@metamask/ppom-validator>elliptic>brorand": true, + "@metamask/ppom-validator>elliptic>hmac-drbg": true, + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, + "@metamask/ppom-validator>elliptic>minimalistic-crypto-utils": true, + "bn.js": true, + "ethers>@ethersproject/sha2>hash.js": true, + "pumpify>inherits": true } }, "ethers>@ethersproject/solidity": { @@ -4615,6 +4743,16 @@ "stream-http": true } }, + "json-rpc-middleware-stream": { + "globals": { + "console.warn": true, + "setTimeout": true + }, + "packages": { + "@metamask/safe-event-emitter": true, + "readable-stream": true + } + }, "koa>content-disposition>safe-buffer": { "packages": { "browserify>buffer": true @@ -5131,10 +5269,10 @@ "document": true }, "packages": { - "@babel/runtime": true, "prop-types": true, "react": true, "react-dom": true, + "react-redux>@babel/runtime": true, "react-redux>hoist-non-react-statics": true, "react-redux>react-is": true } @@ -5358,6 +5496,16 @@ "webpack>events": true } }, + "readable-stream-2>core-util-is": { + "packages": { + "browserify>insert-module-globals>is-buffer": true + } + }, + "readable-stream-2>process-nextick-args": { + "packages": { + "process": true + } + }, "readable-stream>util-deprecate": { "globals": { "console.trace": true, diff --git a/lavamoat/browserify/main/policy.json b/lavamoat/browserify/main/policy.json index ac719964d896..ef4c915328c2 100644 --- a/lavamoat/browserify/main/policy.json +++ b/lavamoat/browserify/main/policy.json @@ -150,7 +150,7 @@ "console.warn": true }, "packages": { - "@ethereumjs/tx>@ethereumjs/util>@ethereumjs/rlp": true, + "@ethereumjs/tx>@ethereumjs/rlp": true, "@ethereumjs/tx>@ethereumjs/util>micro-ftch": true, "@ethereumjs/tx>ethereum-cryptography": true, "browserify>buffer": true, @@ -158,11 +158,6 @@ "webpack>events": true } }, - "@ethereumjs/tx>@ethereumjs/util>@ethereumjs/rlp": { - "globals": { - "TextEncoder": true - } - }, "@ethereumjs/tx>@ethereumjs/util>micro-ftch": { "globals": { "Headers": true, @@ -187,28 +182,16 @@ "crypto": true }, "packages": { - "@ethereumjs/tx>ethereum-cryptography>@noble/hashes": true, "@ethereumjs/tx>ethereum-cryptography>@scure/bip32": true, - "@metamask/message-signing-snap>@noble/curves": true - } - }, - "@ethereumjs/tx>ethereum-cryptography>@noble/hashes": { - "globals": { - "TextEncoder": true, - "crypto": true + "@metamask/message-signing-snap>@noble/curves": true, + "@noble/hashes": true } }, "@ethereumjs/tx>ethereum-cryptography>@scure/bip32": { "packages": { - "@ethereumjs/tx>ethereum-cryptography>@scure/bip32>@noble/hashes": true, "@metamask/message-signing-snap>@noble/curves": true, - "@metamask/utils>@scure/base": true - } - }, - "@ethereumjs/tx>ethereum-cryptography>@scure/bip32>@noble/hashes": { - "globals": { - "TextEncoder": true, - "crypto": true + "@metamask/utils>@scure/base": true, + "@noble/hashes": true } }, "@ethersproject/abi": { @@ -375,7 +358,7 @@ "@ethereumjs/tx": true, "@keystonehq/bc-ur-registry-eth": true, "@keystonehq/metamask-airgapped-keyring>@keystonehq/base-eth-keyring": true, - "@metamask/obs-store": true, + "@keystonehq/metamask-airgapped-keyring>@metamask/obs-store": true, "browserify>buffer": true, "ethereumjs-util>rlp": true, "uuid": true, @@ -398,6 +381,54 @@ "TextEncoder": true } }, + "@keystonehq/metamask-airgapped-keyring>@metamask/obs-store": { + "packages": { + "@keystonehq/metamask-airgapped-keyring>@metamask/obs-store>@metamask/safe-event-emitter": true, + "@keystonehq/metamask-airgapped-keyring>@metamask/obs-store>through2": true, + "stream-browserify": true + } + }, + "@keystonehq/metamask-airgapped-keyring>@metamask/obs-store>@metamask/safe-event-emitter": { + "globals": { + "setTimeout": true + }, + "packages": { + "webpack>events": true + } + }, + "@keystonehq/metamask-airgapped-keyring>@metamask/obs-store>through2": { + "packages": { + "@keystonehq/metamask-airgapped-keyring>@metamask/obs-store>through2>readable-stream": true, + "browserify>util": true, + "process": true, + "watchify>xtend": true + } + }, + "@keystonehq/metamask-airgapped-keyring>@metamask/obs-store>through2>readable-stream": { + "packages": { + "@keystonehq/metamask-airgapped-keyring>@metamask/obs-store>through2>readable-stream>isarray": true, + "@keystonehq/metamask-airgapped-keyring>@metamask/obs-store>through2>readable-stream>safe-buffer": true, + "@keystonehq/metamask-airgapped-keyring>@metamask/obs-store>through2>readable-stream>string_decoder": true, + "browserify>browser-resolve": true, + "browserify>timers-browserify": true, + "process": true, + "pumpify>inherits": true, + "readable-stream-2>core-util-is": true, + "readable-stream-2>process-nextick-args": true, + "readable-stream>util-deprecate": true, + "webpack>events": true + } + }, + "@keystonehq/metamask-airgapped-keyring>@metamask/obs-store>through2>readable-stream>safe-buffer": { + "packages": { + "browserify>buffer": true + } + }, + "@keystonehq/metamask-airgapped-keyring>@metamask/obs-store>through2>readable-stream>string_decoder": { + "packages": { + "@keystonehq/metamask-airgapped-keyring>@metamask/obs-store>through2>readable-stream>safe-buffer": true + } + }, "@lavamoat/lavadome-react": { "globals": { "Document.prototype": true, @@ -674,14 +705,14 @@ "@ethersproject/providers": true, "@metamask/abi-utils": true, "@metamask/assets-controllers>@metamask/polling-controller": true, - "@metamask/assets-controllers>@metamask/utils": true, + "@metamask/assets-controllers>@metamask/rpc-errors": true, "@metamask/base-controller": true, "@metamask/contract-metadata": true, "@metamask/controller-utils": true, "@metamask/eth-query": true, "@metamask/metamask-eth-abis": true, "@metamask/name-controller>async-mutex": true, - "@metamask/rpc-errors": true, + "@metamask/utils": true, "bn.js": true, "cockatiel": true, "ethers>@ethersproject/address": true, @@ -702,19 +733,10 @@ "uuid": true } }, - "@metamask/assets-controllers>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, + "@metamask/assets-controllers>@metamask/rpc-errors": { "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true + "@metamask/rpc-errors>fast-safe-stringify": true, + "@metamask/utils": true } }, "@metamask/base-controller": { @@ -841,13 +863,70 @@ "console.error": true }, "packages": { - "@metamask/eth-query": true, - "@metamask/json-rpc-engine": true, - "@metamask/name-controller>async-mutex": true, + "@metamask/eth-json-rpc-filters>@metamask/eth-query": true, + "@metamask/eth-json-rpc-filters>@metamask/json-rpc-engine": true, + "@metamask/eth-json-rpc-filters>async-mutex": true, "@metamask/safe-event-emitter": true, "pify": true } }, + "@metamask/eth-json-rpc-filters>@metamask/eth-query": { + "packages": { + "@metamask/eth-query>json-rpc-random-id": true, + "watchify>xtend": true + } + }, + "@metamask/eth-json-rpc-filters>@metamask/json-rpc-engine": { + "packages": { + "@metamask/eth-json-rpc-filters>@metamask/json-rpc-engine>@metamask/rpc-errors": true, + "@metamask/eth-json-rpc-filters>@metamask/json-rpc-engine>@metamask/utils": true, + "@metamask/safe-event-emitter": true + } + }, + "@metamask/eth-json-rpc-filters>@metamask/json-rpc-engine>@metamask/rpc-errors": { + "packages": { + "@metamask/eth-json-rpc-filters>@metamask/json-rpc-engine>@metamask/rpc-errors>@metamask/utils": true, + "@metamask/rpc-errors>fast-safe-stringify": true + } + }, + "@metamask/eth-json-rpc-filters>@metamask/json-rpc-engine>@metamask/rpc-errors>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/utils>@metamask/superstruct": true, + "@metamask/utils>@scure/base": true, + "@metamask/utils>pony-cause": true, + "@noble/hashes": true, + "browserify>buffer": true, + "nock>debug": true, + "semver": true + } + }, + "@metamask/eth-json-rpc-filters>@metamask/json-rpc-engine>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/utils>@metamask/superstruct": true, + "@metamask/utils>@scure/base": true, + "@metamask/utils>pony-cause": true, + "@noble/hashes": true, + "browserify>buffer": true, + "nock>debug": true, + "semver": true + } + }, + "@metamask/eth-json-rpc-filters>async-mutex": { + "globals": { + "setTimeout": true + }, + "packages": { + "@swc/helpers>tslib": true + } + }, "@metamask/eth-json-rpc-middleware": { "globals": { "URL": true, @@ -887,19 +966,14 @@ }, "packages": { "@ethereumjs/tx": true, + "@ethereumjs/tx>@ethereumjs/rlp": true, "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/eth-ledger-bridge-keyring>@ethereumjs/rlp": true, "@metamask/eth-sig-util": true, "@metamask/eth-trezor-keyring>hdkey": true, "browserify>buffer": true, "webpack>events": true } }, - "@metamask/eth-ledger-bridge-keyring>@ethereumjs/rlp": { - "globals": { - "TextEncoder": true - } - }, "@metamask/eth-query": { "packages": { "@metamask/eth-query>json-rpc-random-id": true, @@ -1241,24 +1315,24 @@ "uuid": true } }, - "@metamask/gas-fee-controller>@metamask/base-controller": { + "@metamask/gas-fee-controller>@metamask/polling-controller": { "globals": { + "clearTimeout": true, + "console.error": true, "setTimeout": true }, "packages": { - "immer": true + "@metamask/gas-fee-controller>@metamask/polling-controller>@metamask/base-controller": true, + "@metamask/snaps-utils>fast-json-stable-stringify": true, + "uuid": true } }, - "@metamask/gas-fee-controller>@metamask/polling-controller": { + "@metamask/gas-fee-controller>@metamask/polling-controller>@metamask/base-controller": { "globals": { - "clearTimeout": true, - "console.error": true, "setTimeout": true }, "packages": { - "@metamask/gas-fee-controller>@metamask/base-controller": true, - "@metamask/snaps-utils>fast-json-stable-stringify": true, - "uuid": true + "immer": true } }, "@metamask/jazzicon": { @@ -1315,17 +1389,6 @@ "semver": true } }, - "@metamask/json-rpc-middleware-stream": { - "globals": { - "console.warn": true, - "setTimeout": true - }, - "packages": { - "@metamask/safe-event-emitter": true, - "@metamask/utils": true, - "readable-stream": true - } - }, "@metamask/keyring-api": { "globals": { "URL": true @@ -1465,13 +1528,7 @@ "TextEncoder": true }, "packages": { - "@metamask/message-signing-snap>@noble/curves>@noble/hashes": true - } - }, - "@metamask/message-signing-snap>@noble/curves>@noble/hashes": { - "globals": { - "TextEncoder": true, - "crypto": true + "@noble/hashes": true } }, "@metamask/name-controller": { @@ -1753,7 +1810,6 @@ "@metamask/notification-services-controller>@contentful/rich-text-html-renderer": true, "@metamask/notification-services-controller>firebase": true, "@metamask/profile-sync-controller": true, - "@metamask/utils": true, "bignumber.js": true, "loglevel": true, "uuid": true @@ -2025,21 +2081,9 @@ "@ethereumjs/tx>ethereum-cryptography": true, "@metamask/base-controller": true, "@metamask/controller-utils": true, + "@metamask/phishing-controller>fastest-levenshtein": true, "@noble/hashes": true, - "punycode": true, - "webpack-cli>fastest-levenshtein": true - } - }, - "@metamask/polling-controller": { - "globals": { - "clearTimeout": true, - "console.error": true, - "setTimeout": true - }, - "packages": { - "@metamask/base-controller": true, - "@metamask/snaps-utils>fast-json-stable-stringify": true, - "uuid": true + "punycode": true } }, "@metamask/post-message-stream": { @@ -2323,57 +2367,16 @@ "packages": { "@metamask/base-controller": true, "@metamask/controller-utils": true, + "@metamask/eth-sig-util": true, "@metamask/keyring-controller": true, "@metamask/logging-controller": true, "@metamask/message-manager>jsonschema": true, - "@metamask/signature-controller>@metamask/eth-sig-util": true, - "@metamask/signature-controller>@metamask/utils": true, + "@metamask/utils": true, "browserify>buffer": true, "uuid": true, "webpack>events": true } }, - "@metamask/signature-controller>@metamask/eth-sig-util": { - "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@ethereumjs/tx>ethereum-cryptography": true, - "@metamask/abi-utils": true, - "@metamask/eth-sig-util>tweetnacl": true, - "@metamask/signature-controller>@metamask/eth-sig-util>@metamask/utils": true, - "@metamask/utils>@scure/base": true, - "browserify>buffer": true - } - }, - "@metamask/signature-controller>@metamask/eth-sig-util>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, - "@metamask/signature-controller>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/smart-transactions-controller": { "globals": { "URLSearchParams": true, @@ -2387,9 +2390,9 @@ "@ethersproject/bytes": true, "@metamask/controller-utils": true, "@metamask/eth-query": true, + "@metamask/gas-fee-controller>@metamask/polling-controller": true, "@metamask/smart-transactions-controller>@ethereumjs/tx": true, "@metamask/smart-transactions-controller>@ethereumjs/util": true, - "@metamask/smart-transactions-controller>@metamask/polling-controller": true, "@metamask/smart-transactions-controller>@metamask/transaction-controller": true, "@metamask/smart-transactions-controller>bignumber.js": true, "browserify>buffer": true, @@ -2397,12 +2400,17 @@ "lodash": true } }, + "@metamask/smart-transactions-controller>@babel/runtime": { + "globals": { + "regeneratorRuntime": "write" + } + }, "@metamask/smart-transactions-controller>@ethereumjs/tx": { "packages": { "@ethereumjs/tx>ethereum-cryptography": true, "@metamask/smart-transactions-controller>@ethereumjs/tx>@ethereumjs/common": true, - "@metamask/smart-transactions-controller>@ethereumjs/util": true, - "eth-lattice-keyring>gridplus-sdk>@ethereumjs/rlp": true + "@metamask/smart-transactions-controller>@ethereumjs/tx>@ethereumjs/rlp": true, + "@metamask/smart-transactions-controller>@ethereumjs/util": true } }, "@metamask/smart-transactions-controller>@ethereumjs/tx>@ethereumjs/common": { @@ -2411,6 +2419,11 @@ "webpack>events": true } }, + "@metamask/smart-transactions-controller>@ethereumjs/tx>@ethereumjs/rlp": { + "globals": { + "TextEncoder": true + } + }, "@metamask/smart-transactions-controller>@ethereumjs/util": { "globals": { "console.warn": true, @@ -2418,10 +2431,15 @@ }, "packages": { "@ethereumjs/tx>ethereum-cryptography": true, - "eth-lattice-keyring>gridplus-sdk>@ethereumjs/rlp": true, + "@metamask/smart-transactions-controller>@ethereumjs/util>@ethereumjs/rlp": true, "webpack>events": true } }, + "@metamask/smart-transactions-controller>@ethereumjs/util>@ethereumjs/rlp": { + "globals": { + "TextEncoder": true + } + }, "@metamask/smart-transactions-controller>@metamask/base-controller": { "globals": { "setTimeout": true @@ -2435,18 +2453,6 @@ "crypto.getRandomValues": true } }, - "@metamask/smart-transactions-controller>@metamask/polling-controller": { - "globals": { - "clearTimeout": true, - "console.error": true, - "setTimeout": true - }, - "packages": { - "@metamask/smart-transactions-controller>@metamask/base-controller": true, - "@metamask/snaps-utils>fast-json-stable-stringify": true, - "uuid": true - } - }, "@metamask/smart-transactions-controller>@metamask/transaction-controller": { "globals": { "clearTimeout": true, @@ -2471,9 +2477,9 @@ "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/nonce-tracker": true, "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/rpc-errors": true, "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/utils": true, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry": true, "bn.js": true, "browserify>buffer": true, - "eth-method-registry": true, "fast-json-patch": true, "lodash": true, "uuid": true, @@ -2483,26 +2489,21 @@ "@metamask/smart-transactions-controller>@metamask/transaction-controller>@ethereumjs/tx": { "packages": { "@ethereumjs/tx>@ethereumjs/common": true, + "@ethereumjs/tx>@ethereumjs/rlp": true, "@ethereumjs/tx>ethereum-cryptography": true, - "@metamask/smart-transactions-controller>@metamask/transaction-controller>@ethereumjs/tx>@ethereumjs/rlp": true, "@metamask/smart-transactions-controller>@metamask/transaction-controller>@ethereumjs/util": true, "browserify>buffer": true, "browserify>insert-module-globals>is-buffer": true } }, - "@metamask/smart-transactions-controller>@metamask/transaction-controller>@ethereumjs/tx>@ethereumjs/rlp": { - "globals": { - "TextEncoder": true - } - }, "@metamask/smart-transactions-controller>@metamask/transaction-controller>@ethereumjs/util": { "globals": { "console.warn": true }, "packages": { + "@ethereumjs/tx>@ethereumjs/rlp": true, "@ethereumjs/tx>@ethereumjs/util>micro-ftch": true, "@ethereumjs/tx>ethereum-cryptography": true, - "@metamask/smart-transactions-controller>@metamask/transaction-controller>@ethereumjs/tx>@ethereumjs/rlp": true, "browserify>buffer": true, "browserify>insert-module-globals>is-buffer": true, "webpack>events": true @@ -2545,6 +2546,58 @@ "semver": true } }, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry": { + "packages": { + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry>@metamask/ethjs-contract": true, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry>@metamask/ethjs-query": true + } + }, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry>@metamask/ethjs-contract": { + "packages": { + "@metamask/ethjs>ethjs-abi": true, + "@metamask/ethjs>js-sha3": true, + "@metamask/smart-transactions-controller>@babel/runtime": true, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry>@metamask/ethjs-contract>@metamask/ethjs-filter": true, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry>@metamask/ethjs-contract>@metamask/ethjs-util": true, + "promise-to-callback": true + } + }, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry>@metamask/ethjs-contract>@metamask/ethjs-filter": { + "globals": { + "clearInterval": true, + "setInterval": true + } + }, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry>@metamask/ethjs-contract>@metamask/ethjs-util": { + "packages": { + "@metamask/ethjs>@metamask/ethjs-util>is-hex-prefixed": true, + "@metamask/ethjs>@metamask/ethjs-util>strip-hex-prefix": true, + "browserify>buffer": true + } + }, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry>@metamask/ethjs-query": { + "globals": { + "console": true + }, + "packages": { + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry>@metamask/ethjs-query>@metamask/ethjs-format": true, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry>@metamask/ethjs-query>@metamask/ethjs-rpc": true, + "promise-to-callback": true + } + }, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry>@metamask/ethjs-query>@metamask/ethjs-format": { + "packages": { + "@metamask/ethjs-query>@metamask/ethjs-format>ethjs-schema": true, + "@metamask/ethjs>@metamask/ethjs-util>strip-hex-prefix": true, + "@metamask/ethjs>@metamask/number-to-bn": true, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry>@metamask/ethjs-contract>@metamask/ethjs-util": true + } + }, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry>@metamask/ethjs-query>@metamask/ethjs-rpc": { + "packages": { + "promise-to-callback": true + } + }, "@metamask/smart-transactions-controller>bignumber.js": { "globals": { "crypto": true, @@ -2561,11 +2614,11 @@ "setTimeout": true }, "packages": { - "@metamask/json-rpc-middleware-stream": true, "@metamask/object-multiplex": true, "@metamask/post-message-stream": true, "@metamask/snaps-controllers>@metamask/base-controller": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, + "@metamask/snaps-controllers>@metamask/json-rpc-middleware-stream": true, "@metamask/snaps-controllers>@metamask/permission-controller": true, "@metamask/snaps-controllers>@metamask/rpc-errors": true, "@metamask/snaps-controllers>@xstate/fsm": true, @@ -2605,6 +2658,17 @@ "@metamask/utils": true } }, + "@metamask/snaps-controllers>@metamask/json-rpc-middleware-stream": { + "globals": { + "console.warn": true, + "setTimeout": true + }, + "packages": { + "@metamask/safe-event-emitter": true, + "@metamask/utils": true, + "readable-stream": true + } + }, "@metamask/snaps-controllers>@metamask/permission-controller": { "globals": { "console.error": true @@ -2958,9 +3022,9 @@ "@metamask/controller-utils": true, "@metamask/eth-query": true, "@metamask/gas-fee-controller": true, + "@metamask/gas-fee-controller>@metamask/polling-controller": true, "@metamask/transaction-controller": true, "@metamask/user-operation-controller>@metamask/base-controller": true, - "@metamask/user-operation-controller>@metamask/polling-controller": true, "@metamask/user-operation-controller>@metamask/rpc-errors": true, "@metamask/user-operation-controller>@metamask/utils": true, "bn.js": true, @@ -2978,18 +3042,6 @@ "immer": true } }, - "@metamask/user-operation-controller>@metamask/polling-controller": { - "globals": { - "clearTimeout": true, - "console.error": true, - "setTimeout": true - }, - "packages": { - "@metamask/snaps-utils>fast-json-stable-stringify": true, - "@metamask/user-operation-controller>@metamask/base-controller": true, - "uuid": true - } - }, "@metamask/user-operation-controller>@metamask/rpc-errors": { "packages": { "@metamask/rpc-errors>fast-safe-stringify": true, @@ -4025,16 +4077,59 @@ "setInterval": true }, "packages": { - "@ethereumjs/tx": true, "@ethereumjs/tx>@ethereumjs/util": true, "bn.js": true, "browserify>buffer": true, "crypto-browserify": true, + "eth-lattice-keyring>@ethereumjs/tx": true, "eth-lattice-keyring>gridplus-sdk": true, "eth-lattice-keyring>rlp": true, "webpack>events": true } }, + "eth-lattice-keyring>@ethereumjs/tx": { + "packages": { + "@ethereumjs/tx>@ethereumjs/common": true, + "@ethereumjs/tx>@ethereumjs/rlp": true, + "@ethereumjs/tx>@ethereumjs/util": true, + "@ethersproject/providers": true, + "browserify>buffer": true, + "browserify>insert-module-globals>is-buffer": true, + "eth-lattice-keyring>@ethereumjs/tx>@chainsafe/ssz": true, + "eth-lattice-keyring>@ethereumjs/tx>ethereum-cryptography": true + } + }, + "eth-lattice-keyring>@ethereumjs/tx>@chainsafe/ssz": { + "packages": { + "browserify": true, + "browserify>buffer": true, + "eth-lattice-keyring>@ethereumjs/tx>@chainsafe/ssz>@chainsafe/persistent-merkle-tree": true, + "eth-lattice-keyring>@ethereumjs/tx>@chainsafe/ssz>case": true + } + }, + "eth-lattice-keyring>@ethereumjs/tx>@chainsafe/ssz>@chainsafe/persistent-merkle-tree": { + "globals": { + "WeakRef": true + }, + "packages": { + "browserify": true + } + }, + "eth-lattice-keyring>@ethereumjs/tx>ethereum-cryptography": { + "globals": { + "TextDecoder": true, + "crypto": true + }, + "packages": { + "eth-lattice-keyring>@ethereumjs/tx>ethereum-cryptography>@noble/hashes": true + } + }, + "eth-lattice-keyring>@ethereumjs/tx>ethereum-cryptography>@noble/hashes": { + "globals": { + "TextEncoder": true, + "crypto": true + } + }, "eth-lattice-keyring>gridplus-sdk": { "globals": { "AbortController": true, @@ -4052,59 +4147,67 @@ "packages": { "@ethereumjs/tx>@ethereumjs/common>crc-32": true, "@ethersproject/abi": true, - "@metamask/eth-sig-util": true, "@metamask/ethjs>js-sha3": true, "@metamask/keyring-api>bech32": true, - "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>buffer": true, "eth-lattice-keyring>gridplus-sdk>@ethereumjs/common": true, - "eth-lattice-keyring>gridplus-sdk>@ethereumjs/rlp": true, "eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx": true, "eth-lattice-keyring>gridplus-sdk>aes-js": true, "eth-lattice-keyring>gridplus-sdk>bignumber.js": true, + "eth-lattice-keyring>gridplus-sdk>bitwise": true, "eth-lattice-keyring>gridplus-sdk>borc": true, - "eth-lattice-keyring>gridplus-sdk>bs58check": true, - "eth-lattice-keyring>gridplus-sdk>secp256k1": true, + "eth-lattice-keyring>gridplus-sdk>elliptic": true, + "eth-lattice-keyring>gridplus-sdk>eth-eip712-util-browser": true, + "eth-lattice-keyring>gridplus-sdk>rlp": true, "eth-lattice-keyring>gridplus-sdk>uuid": true, + "ethereumjs-util>ethereum-cryptography>bs58check": true, "ethers>@ethersproject/sha2>hash.js": true, + "ganache>secp256k1": true, "lodash": true } }, "eth-lattice-keyring>gridplus-sdk>@ethereumjs/common": { "packages": { - "eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>@ethereumjs/util": true, + "@ethereumjs/tx>@ethereumjs/common>crc-32": true, + "@ethereumjs/tx>@ethereumjs/util": true, + "browserify>buffer": true, "webpack>events": true } }, - "eth-lattice-keyring>gridplus-sdk>@ethereumjs/rlp": { - "globals": { - "TextEncoder": true - } - }, "eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx": { "packages": { - "@ethereumjs/tx>ethereum-cryptography": true, - "eth-lattice-keyring>gridplus-sdk>@ethereumjs/rlp": true, + "@ethereumjs/tx>@ethereumjs/rlp": true, + "@ethereumjs/tx>@ethereumjs/util": true, + "@ethersproject/providers": true, + "browserify>buffer": true, + "browserify>insert-module-globals>is-buffer": true, + "eth-lattice-keyring>@ethereumjs/tx>@chainsafe/ssz": true, "eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>@ethereumjs/common": true, - "eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>@ethereumjs/util": true + "eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>ethereum-cryptography": true } }, "eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>@ethereumjs/common": { "packages": { - "eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>@ethereumjs/util": true, + "@ethereumjs/tx>@ethereumjs/common>crc-32": true, + "@ethereumjs/tx>@ethereumjs/util": true, + "browserify>buffer": true, "webpack>events": true } }, - "eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>@ethereumjs/util": { + "eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>ethereum-cryptography": { "globals": { - "console.warn": true, - "fetch": true + "TextDecoder": true, + "crypto": true }, "packages": { - "@ethereumjs/tx>ethereum-cryptography": true, - "eth-lattice-keyring>gridplus-sdk>@ethereumjs/rlp": true, - "webpack>events": true + "eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>ethereum-cryptography>@noble/hashes": true + } + }, + "eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>ethereum-cryptography>@noble/hashes": { + "globals": { + "TextEncoder": true, + "crypto": true } }, "eth-lattice-keyring>gridplus-sdk>aes-js": { @@ -4118,6 +4221,11 @@ "define": true } }, + "eth-lattice-keyring>gridplus-sdk>bitwise": { + "packages": { + "browserify>buffer": true + } + }, "eth-lattice-keyring>gridplus-sdk>borc": { "globals": { "console": true @@ -4139,24 +4247,33 @@ "globals": { "URL": true, "URLSearchParams": true, - "location": true, - "navigator": true + "location": true } }, - "eth-lattice-keyring>gridplus-sdk>bs58check": { + "eth-lattice-keyring>gridplus-sdk>elliptic": { "packages": { - "@noble/hashes": true, - "eth-lattice-keyring>gridplus-sdk>bs58check>bs58": true + "@metamask/ppom-validator>elliptic>brorand": true, + "@metamask/ppom-validator>elliptic>hmac-drbg": true, + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, + "@metamask/ppom-validator>elliptic>minimalistic-crypto-utils": true, + "bn.js": true, + "ethers>@ethersproject/sha2>hash.js": true, + "pumpify>inherits": true } }, - "eth-lattice-keyring>gridplus-sdk>bs58check>bs58": { + "eth-lattice-keyring>gridplus-sdk>eth-eip712-util-browser": { + "globals": { + "intToBuffer": true + }, "packages": { - "eth-lattice-keyring>gridplus-sdk>bs58check>bs58>base-x": true + "@metamask/ethjs>js-sha3": true, + "bn.js": true, + "buffer": true } }, - "eth-lattice-keyring>gridplus-sdk>secp256k1": { - "packages": { - "@metamask/ppom-validator>elliptic": true + "eth-lattice-keyring>gridplus-sdk>rlp": { + "globals": { + "TextEncoder": true } }, "eth-lattice-keyring>gridplus-sdk>uuid": { @@ -4480,9 +4597,20 @@ "ethers>@ethersproject/signing-key": { "packages": { "@ethersproject/bytes": true, - "@metamask/ppom-validator>elliptic": true, "ethers>@ethersproject/logger": true, - "ethers>@ethersproject/properties": true + "ethers>@ethersproject/properties": true, + "ethers>@ethersproject/signing-key>elliptic": true + } + }, + "ethers>@ethersproject/signing-key>elliptic": { + "packages": { + "@metamask/ppom-validator>elliptic>brorand": true, + "@metamask/ppom-validator>elliptic>hmac-drbg": true, + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, + "@metamask/ppom-validator>elliptic>minimalistic-crypto-utils": true, + "bn.js": true, + "ethers>@ethersproject/sha2>hash.js": true, + "pumpify>inherits": true } }, "ethers>@ethersproject/solidity": { @@ -4615,6 +4743,16 @@ "stream-http": true } }, + "json-rpc-middleware-stream": { + "globals": { + "console.warn": true, + "setTimeout": true + }, + "packages": { + "@metamask/safe-event-emitter": true, + "readable-stream": true + } + }, "koa>content-disposition>safe-buffer": { "packages": { "browserify>buffer": true @@ -5131,10 +5269,10 @@ "document": true }, "packages": { - "@babel/runtime": true, "prop-types": true, "react": true, "react-dom": true, + "react-redux>@babel/runtime": true, "react-redux>hoist-non-react-statics": true, "react-redux>react-is": true } @@ -5358,6 +5496,16 @@ "webpack>events": true } }, + "readable-stream-2>core-util-is": { + "packages": { + "browserify>insert-module-globals>is-buffer": true + } + }, + "readable-stream-2>process-nextick-args": { + "packages": { + "process": true + } + }, "readable-stream>util-deprecate": { "globals": { "console.trace": true, diff --git a/lavamoat/browserify/mmi/policy.json b/lavamoat/browserify/mmi/policy.json index 1b4e98aae177..94c331a71ee5 100644 --- a/lavamoat/browserify/mmi/policy.json +++ b/lavamoat/browserify/mmi/policy.json @@ -150,7 +150,7 @@ "console.warn": true }, "packages": { - "@ethereumjs/tx>@ethereumjs/util>@ethereumjs/rlp": true, + "@ethereumjs/tx>@ethereumjs/rlp": true, "@ethereumjs/tx>@ethereumjs/util>micro-ftch": true, "@ethereumjs/tx>ethereum-cryptography": true, "browserify>buffer": true, @@ -158,11 +158,6 @@ "webpack>events": true } }, - "@ethereumjs/tx>@ethereumjs/util>@ethereumjs/rlp": { - "globals": { - "TextEncoder": true - } - }, "@ethereumjs/tx>@ethereumjs/util>micro-ftch": { "globals": { "Headers": true, @@ -187,28 +182,16 @@ "crypto": true }, "packages": { - "@ethereumjs/tx>ethereum-cryptography>@noble/hashes": true, "@ethereumjs/tx>ethereum-cryptography>@scure/bip32": true, - "@metamask/message-signing-snap>@noble/curves": true - } - }, - "@ethereumjs/tx>ethereum-cryptography>@noble/hashes": { - "globals": { - "TextEncoder": true, - "crypto": true + "@metamask/message-signing-snap>@noble/curves": true, + "@noble/hashes": true } }, "@ethereumjs/tx>ethereum-cryptography>@scure/bip32": { "packages": { - "@ethereumjs/tx>ethereum-cryptography>@scure/bip32>@noble/hashes": true, "@metamask/message-signing-snap>@noble/curves": true, - "@metamask/utils>@scure/base": true - } - }, - "@ethereumjs/tx>ethereum-cryptography>@scure/bip32>@noble/hashes": { - "globals": { - "TextEncoder": true, - "crypto": true + "@metamask/utils>@scure/base": true, + "@noble/hashes": true } }, "@ethersproject/abi": { @@ -375,7 +358,7 @@ "@ethereumjs/tx": true, "@keystonehq/bc-ur-registry-eth": true, "@keystonehq/metamask-airgapped-keyring>@keystonehq/base-eth-keyring": true, - "@metamask/obs-store": true, + "@keystonehq/metamask-airgapped-keyring>@metamask/obs-store": true, "browserify>buffer": true, "ethereumjs-util>rlp": true, "uuid": true, @@ -398,6 +381,54 @@ "TextEncoder": true } }, + "@keystonehq/metamask-airgapped-keyring>@metamask/obs-store": { + "packages": { + "@keystonehq/metamask-airgapped-keyring>@metamask/obs-store>@metamask/safe-event-emitter": true, + "@keystonehq/metamask-airgapped-keyring>@metamask/obs-store>through2": true, + "stream-browserify": true + } + }, + "@keystonehq/metamask-airgapped-keyring>@metamask/obs-store>@metamask/safe-event-emitter": { + "globals": { + "setTimeout": true + }, + "packages": { + "webpack>events": true + } + }, + "@keystonehq/metamask-airgapped-keyring>@metamask/obs-store>through2": { + "packages": { + "@keystonehq/metamask-airgapped-keyring>@metamask/obs-store>through2>readable-stream": true, + "browserify>util": true, + "process": true, + "watchify>xtend": true + } + }, + "@keystonehq/metamask-airgapped-keyring>@metamask/obs-store>through2>readable-stream": { + "packages": { + "@keystonehq/metamask-airgapped-keyring>@metamask/obs-store>through2>readable-stream>isarray": true, + "@keystonehq/metamask-airgapped-keyring>@metamask/obs-store>through2>readable-stream>safe-buffer": true, + "@keystonehq/metamask-airgapped-keyring>@metamask/obs-store>through2>readable-stream>string_decoder": true, + "browserify>browser-resolve": true, + "browserify>timers-browserify": true, + "process": true, + "pumpify>inherits": true, + "readable-stream-2>core-util-is": true, + "readable-stream-2>process-nextick-args": true, + "readable-stream>util-deprecate": true, + "webpack>events": true + } + }, + "@keystonehq/metamask-airgapped-keyring>@metamask/obs-store>through2>readable-stream>safe-buffer": { + "packages": { + "browserify>buffer": true + } + }, + "@keystonehq/metamask-airgapped-keyring>@metamask/obs-store>through2>readable-stream>string_decoder": { + "packages": { + "@keystonehq/metamask-airgapped-keyring>@metamask/obs-store>through2>readable-stream>safe-buffer": true + } + }, "@lavamoat/lavadome-react": { "globals": { "Document.prototype": true, @@ -766,14 +797,14 @@ "@ethersproject/providers": true, "@metamask/abi-utils": true, "@metamask/assets-controllers>@metamask/polling-controller": true, - "@metamask/assets-controllers>@metamask/utils": true, + "@metamask/assets-controllers>@metamask/rpc-errors": true, "@metamask/base-controller": true, "@metamask/contract-metadata": true, "@metamask/controller-utils": true, "@metamask/eth-query": true, "@metamask/metamask-eth-abis": true, "@metamask/name-controller>async-mutex": true, - "@metamask/rpc-errors": true, + "@metamask/utils": true, "bn.js": true, "cockatiel": true, "ethers>@ethersproject/address": true, @@ -794,19 +825,10 @@ "uuid": true } }, - "@metamask/assets-controllers>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, + "@metamask/assets-controllers>@metamask/rpc-errors": { "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true + "@metamask/rpc-errors>fast-safe-stringify": true, + "@metamask/utils": true } }, "@metamask/base-controller": { @@ -933,13 +955,70 @@ "console.error": true }, "packages": { - "@metamask/eth-query": true, - "@metamask/json-rpc-engine": true, - "@metamask/name-controller>async-mutex": true, + "@metamask/eth-json-rpc-filters>@metamask/eth-query": true, + "@metamask/eth-json-rpc-filters>@metamask/json-rpc-engine": true, + "@metamask/eth-json-rpc-filters>async-mutex": true, "@metamask/safe-event-emitter": true, "pify": true } }, + "@metamask/eth-json-rpc-filters>@metamask/eth-query": { + "packages": { + "@metamask/eth-query>json-rpc-random-id": true, + "watchify>xtend": true + } + }, + "@metamask/eth-json-rpc-filters>@metamask/json-rpc-engine": { + "packages": { + "@metamask/eth-json-rpc-filters>@metamask/json-rpc-engine>@metamask/rpc-errors": true, + "@metamask/eth-json-rpc-filters>@metamask/json-rpc-engine>@metamask/utils": true, + "@metamask/safe-event-emitter": true + } + }, + "@metamask/eth-json-rpc-filters>@metamask/json-rpc-engine>@metamask/rpc-errors": { + "packages": { + "@metamask/eth-json-rpc-filters>@metamask/json-rpc-engine>@metamask/rpc-errors>@metamask/utils": true, + "@metamask/rpc-errors>fast-safe-stringify": true + } + }, + "@metamask/eth-json-rpc-filters>@metamask/json-rpc-engine>@metamask/rpc-errors>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/utils>@metamask/superstruct": true, + "@metamask/utils>@scure/base": true, + "@metamask/utils>pony-cause": true, + "@noble/hashes": true, + "browserify>buffer": true, + "nock>debug": true, + "semver": true + } + }, + "@metamask/eth-json-rpc-filters>@metamask/json-rpc-engine>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/utils>@metamask/superstruct": true, + "@metamask/utils>@scure/base": true, + "@metamask/utils>pony-cause": true, + "@noble/hashes": true, + "browserify>buffer": true, + "nock>debug": true, + "semver": true + } + }, + "@metamask/eth-json-rpc-filters>async-mutex": { + "globals": { + "setTimeout": true + }, + "packages": { + "@swc/helpers>tslib": true + } + }, "@metamask/eth-json-rpc-middleware": { "globals": { "URL": true, @@ -979,19 +1058,14 @@ }, "packages": { "@ethereumjs/tx": true, + "@ethereumjs/tx>@ethereumjs/rlp": true, "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/eth-ledger-bridge-keyring>@ethereumjs/rlp": true, "@metamask/eth-sig-util": true, "@metamask/eth-trezor-keyring>hdkey": true, "browserify>buffer": true, "webpack>events": true } }, - "@metamask/eth-ledger-bridge-keyring>@ethereumjs/rlp": { - "globals": { - "TextEncoder": true - } - }, "@metamask/eth-query": { "packages": { "@metamask/eth-query>json-rpc-random-id": true, @@ -1333,24 +1407,24 @@ "uuid": true } }, - "@metamask/gas-fee-controller>@metamask/base-controller": { + "@metamask/gas-fee-controller>@metamask/polling-controller": { "globals": { + "clearTimeout": true, + "console.error": true, "setTimeout": true }, "packages": { - "immer": true + "@metamask/gas-fee-controller>@metamask/polling-controller>@metamask/base-controller": true, + "@metamask/snaps-utils>fast-json-stable-stringify": true, + "uuid": true } }, - "@metamask/gas-fee-controller>@metamask/polling-controller": { + "@metamask/gas-fee-controller>@metamask/polling-controller>@metamask/base-controller": { "globals": { - "clearTimeout": true, - "console.error": true, "setTimeout": true }, "packages": { - "@metamask/gas-fee-controller>@metamask/base-controller": true, - "@metamask/snaps-utils>fast-json-stable-stringify": true, - "uuid": true + "immer": true } }, "@metamask/jazzicon": { @@ -1407,17 +1481,6 @@ "semver": true } }, - "@metamask/json-rpc-middleware-stream": { - "globals": { - "console.warn": true, - "setTimeout": true - }, - "packages": { - "@metamask/safe-event-emitter": true, - "@metamask/utils": true, - "readable-stream": true - } - }, "@metamask/keyring-api": { "globals": { "URL": true @@ -1557,13 +1620,7 @@ "TextEncoder": true }, "packages": { - "@metamask/message-signing-snap>@noble/curves>@noble/hashes": true - } - }, - "@metamask/message-signing-snap>@noble/curves>@noble/hashes": { - "globals": { - "TextEncoder": true, - "crypto": true + "@noble/hashes": true } }, "@metamask/name-controller": { @@ -1845,7 +1902,6 @@ "@metamask/notification-services-controller>@contentful/rich-text-html-renderer": true, "@metamask/notification-services-controller>firebase": true, "@metamask/profile-sync-controller": true, - "@metamask/utils": true, "bignumber.js": true, "loglevel": true, "uuid": true @@ -2117,21 +2173,9 @@ "@ethereumjs/tx>ethereum-cryptography": true, "@metamask/base-controller": true, "@metamask/controller-utils": true, + "@metamask/phishing-controller>fastest-levenshtein": true, "@noble/hashes": true, - "punycode": true, - "webpack-cli>fastest-levenshtein": true - } - }, - "@metamask/polling-controller": { - "globals": { - "clearTimeout": true, - "console.error": true, - "setTimeout": true - }, - "packages": { - "@metamask/base-controller": true, - "@metamask/snaps-utils>fast-json-stable-stringify": true, - "uuid": true + "punycode": true } }, "@metamask/post-message-stream": { @@ -2415,57 +2459,16 @@ "packages": { "@metamask/base-controller": true, "@metamask/controller-utils": true, + "@metamask/eth-sig-util": true, "@metamask/keyring-controller": true, "@metamask/logging-controller": true, "@metamask/message-manager>jsonschema": true, - "@metamask/signature-controller>@metamask/eth-sig-util": true, - "@metamask/signature-controller>@metamask/utils": true, + "@metamask/utils": true, "browserify>buffer": true, "uuid": true, "webpack>events": true } }, - "@metamask/signature-controller>@metamask/eth-sig-util": { - "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@ethereumjs/tx>ethereum-cryptography": true, - "@metamask/abi-utils": true, - "@metamask/eth-sig-util>tweetnacl": true, - "@metamask/signature-controller>@metamask/eth-sig-util>@metamask/utils": true, - "@metamask/utils>@scure/base": true, - "browserify>buffer": true - } - }, - "@metamask/signature-controller>@metamask/eth-sig-util>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, - "@metamask/signature-controller>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/smart-transactions-controller": { "globals": { "URLSearchParams": true, @@ -2479,9 +2482,9 @@ "@ethersproject/bytes": true, "@metamask/controller-utils": true, "@metamask/eth-query": true, + "@metamask/gas-fee-controller>@metamask/polling-controller": true, "@metamask/smart-transactions-controller>@ethereumjs/tx": true, "@metamask/smart-transactions-controller>@ethereumjs/util": true, - "@metamask/smart-transactions-controller>@metamask/polling-controller": true, "@metamask/smart-transactions-controller>@metamask/transaction-controller": true, "@metamask/smart-transactions-controller>bignumber.js": true, "browserify>buffer": true, @@ -2489,12 +2492,17 @@ "lodash": true } }, + "@metamask/smart-transactions-controller>@babel/runtime": { + "globals": { + "regeneratorRuntime": "write" + } + }, "@metamask/smart-transactions-controller>@ethereumjs/tx": { "packages": { "@ethereumjs/tx>ethereum-cryptography": true, "@metamask/smart-transactions-controller>@ethereumjs/tx>@ethereumjs/common": true, - "@metamask/smart-transactions-controller>@ethereumjs/util": true, - "eth-lattice-keyring>gridplus-sdk>@ethereumjs/rlp": true + "@metamask/smart-transactions-controller>@ethereumjs/tx>@ethereumjs/rlp": true, + "@metamask/smart-transactions-controller>@ethereumjs/util": true } }, "@metamask/smart-transactions-controller>@ethereumjs/tx>@ethereumjs/common": { @@ -2503,6 +2511,11 @@ "webpack>events": true } }, + "@metamask/smart-transactions-controller>@ethereumjs/tx>@ethereumjs/rlp": { + "globals": { + "TextEncoder": true + } + }, "@metamask/smart-transactions-controller>@ethereumjs/util": { "globals": { "console.warn": true, @@ -2510,10 +2523,15 @@ }, "packages": { "@ethereumjs/tx>ethereum-cryptography": true, - "eth-lattice-keyring>gridplus-sdk>@ethereumjs/rlp": true, + "@metamask/smart-transactions-controller>@ethereumjs/util>@ethereumjs/rlp": true, "webpack>events": true } }, + "@metamask/smart-transactions-controller>@ethereumjs/util>@ethereumjs/rlp": { + "globals": { + "TextEncoder": true + } + }, "@metamask/smart-transactions-controller>@metamask/base-controller": { "globals": { "setTimeout": true @@ -2527,18 +2545,6 @@ "crypto.getRandomValues": true } }, - "@metamask/smart-transactions-controller>@metamask/polling-controller": { - "globals": { - "clearTimeout": true, - "console.error": true, - "setTimeout": true - }, - "packages": { - "@metamask/smart-transactions-controller>@metamask/base-controller": true, - "@metamask/snaps-utils>fast-json-stable-stringify": true, - "uuid": true - } - }, "@metamask/smart-transactions-controller>@metamask/transaction-controller": { "globals": { "clearTimeout": true, @@ -2563,9 +2569,9 @@ "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/nonce-tracker": true, "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/rpc-errors": true, "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/utils": true, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry": true, "bn.js": true, "browserify>buffer": true, - "eth-method-registry": true, "fast-json-patch": true, "lodash": true, "uuid": true, @@ -2575,26 +2581,21 @@ "@metamask/smart-transactions-controller>@metamask/transaction-controller>@ethereumjs/tx": { "packages": { "@ethereumjs/tx>@ethereumjs/common": true, + "@ethereumjs/tx>@ethereumjs/rlp": true, "@ethereumjs/tx>ethereum-cryptography": true, - "@metamask/smart-transactions-controller>@metamask/transaction-controller>@ethereumjs/tx>@ethereumjs/rlp": true, "@metamask/smart-transactions-controller>@metamask/transaction-controller>@ethereumjs/util": true, "browserify>buffer": true, "browserify>insert-module-globals>is-buffer": true } }, - "@metamask/smart-transactions-controller>@metamask/transaction-controller>@ethereumjs/tx>@ethereumjs/rlp": { - "globals": { - "TextEncoder": true - } - }, "@metamask/smart-transactions-controller>@metamask/transaction-controller>@ethereumjs/util": { "globals": { "console.warn": true }, "packages": { + "@ethereumjs/tx>@ethereumjs/rlp": true, "@ethereumjs/tx>@ethereumjs/util>micro-ftch": true, "@ethereumjs/tx>ethereum-cryptography": true, - "@metamask/smart-transactions-controller>@metamask/transaction-controller>@ethereumjs/tx>@ethereumjs/rlp": true, "browserify>buffer": true, "browserify>insert-module-globals>is-buffer": true, "webpack>events": true @@ -2637,6 +2638,58 @@ "semver": true } }, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry": { + "packages": { + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry>@metamask/ethjs-contract": true, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry>@metamask/ethjs-query": true + } + }, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry>@metamask/ethjs-contract": { + "packages": { + "@metamask/ethjs>ethjs-abi": true, + "@metamask/ethjs>js-sha3": true, + "@metamask/smart-transactions-controller>@babel/runtime": true, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry>@metamask/ethjs-contract>@metamask/ethjs-filter": true, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry>@metamask/ethjs-contract>@metamask/ethjs-util": true, + "promise-to-callback": true + } + }, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry>@metamask/ethjs-contract>@metamask/ethjs-filter": { + "globals": { + "clearInterval": true, + "setInterval": true + } + }, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry>@metamask/ethjs-contract>@metamask/ethjs-util": { + "packages": { + "@metamask/ethjs>@metamask/ethjs-util>is-hex-prefixed": true, + "@metamask/ethjs>@metamask/ethjs-util>strip-hex-prefix": true, + "browserify>buffer": true + } + }, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry>@metamask/ethjs-query": { + "globals": { + "console": true + }, + "packages": { + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry>@metamask/ethjs-query>@metamask/ethjs-format": true, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry>@metamask/ethjs-query>@metamask/ethjs-rpc": true, + "promise-to-callback": true + } + }, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry>@metamask/ethjs-query>@metamask/ethjs-format": { + "packages": { + "@metamask/ethjs-query>@metamask/ethjs-format>ethjs-schema": true, + "@metamask/ethjs>@metamask/ethjs-util>strip-hex-prefix": true, + "@metamask/ethjs>@metamask/number-to-bn": true, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry>@metamask/ethjs-contract>@metamask/ethjs-util": true + } + }, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>eth-method-registry>@metamask/ethjs-query>@metamask/ethjs-rpc": { + "packages": { + "promise-to-callback": true + } + }, "@metamask/smart-transactions-controller>bignumber.js": { "globals": { "crypto": true, @@ -2653,11 +2706,11 @@ "setTimeout": true }, "packages": { - "@metamask/json-rpc-middleware-stream": true, "@metamask/object-multiplex": true, "@metamask/post-message-stream": true, "@metamask/snaps-controllers>@metamask/base-controller": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, + "@metamask/snaps-controllers>@metamask/json-rpc-middleware-stream": true, "@metamask/snaps-controllers>@metamask/permission-controller": true, "@metamask/snaps-controllers>@metamask/rpc-errors": true, "@metamask/snaps-controllers>@xstate/fsm": true, @@ -2697,6 +2750,17 @@ "@metamask/utils": true } }, + "@metamask/snaps-controllers>@metamask/json-rpc-middleware-stream": { + "globals": { + "console.warn": true, + "setTimeout": true + }, + "packages": { + "@metamask/safe-event-emitter": true, + "@metamask/utils": true, + "readable-stream": true + } + }, "@metamask/snaps-controllers>@metamask/permission-controller": { "globals": { "console.error": true @@ -3050,9 +3114,9 @@ "@metamask/controller-utils": true, "@metamask/eth-query": true, "@metamask/gas-fee-controller": true, + "@metamask/gas-fee-controller>@metamask/polling-controller": true, "@metamask/transaction-controller": true, "@metamask/user-operation-controller>@metamask/base-controller": true, - "@metamask/user-operation-controller>@metamask/polling-controller": true, "@metamask/user-operation-controller>@metamask/rpc-errors": true, "@metamask/user-operation-controller>@metamask/utils": true, "bn.js": true, @@ -3070,18 +3134,6 @@ "immer": true } }, - "@metamask/user-operation-controller>@metamask/polling-controller": { - "globals": { - "clearTimeout": true, - "console.error": true, - "setTimeout": true - }, - "packages": { - "@metamask/snaps-utils>fast-json-stable-stringify": true, - "@metamask/user-operation-controller>@metamask/base-controller": true, - "uuid": true - } - }, "@metamask/user-operation-controller>@metamask/rpc-errors": { "packages": { "@metamask/rpc-errors>fast-safe-stringify": true, @@ -4117,16 +4169,59 @@ "setInterval": true }, "packages": { - "@ethereumjs/tx": true, "@ethereumjs/tx>@ethereumjs/util": true, "bn.js": true, "browserify>buffer": true, "crypto-browserify": true, + "eth-lattice-keyring>@ethereumjs/tx": true, "eth-lattice-keyring>gridplus-sdk": true, "eth-lattice-keyring>rlp": true, "webpack>events": true } }, + "eth-lattice-keyring>@ethereumjs/tx": { + "packages": { + "@ethereumjs/tx>@ethereumjs/common": true, + "@ethereumjs/tx>@ethereumjs/rlp": true, + "@ethereumjs/tx>@ethereumjs/util": true, + "@ethersproject/providers": true, + "browserify>buffer": true, + "browserify>insert-module-globals>is-buffer": true, + "eth-lattice-keyring>@ethereumjs/tx>@chainsafe/ssz": true, + "eth-lattice-keyring>@ethereumjs/tx>ethereum-cryptography": true + } + }, + "eth-lattice-keyring>@ethereumjs/tx>@chainsafe/ssz": { + "packages": { + "browserify": true, + "browserify>buffer": true, + "eth-lattice-keyring>@ethereumjs/tx>@chainsafe/ssz>@chainsafe/persistent-merkle-tree": true, + "eth-lattice-keyring>@ethereumjs/tx>@chainsafe/ssz>case": true + } + }, + "eth-lattice-keyring>@ethereumjs/tx>@chainsafe/ssz>@chainsafe/persistent-merkle-tree": { + "globals": { + "WeakRef": true + }, + "packages": { + "browserify": true + } + }, + "eth-lattice-keyring>@ethereumjs/tx>ethereum-cryptography": { + "globals": { + "TextDecoder": true, + "crypto": true + }, + "packages": { + "eth-lattice-keyring>@ethereumjs/tx>ethereum-cryptography>@noble/hashes": true + } + }, + "eth-lattice-keyring>@ethereumjs/tx>ethereum-cryptography>@noble/hashes": { + "globals": { + "TextEncoder": true, + "crypto": true + } + }, "eth-lattice-keyring>gridplus-sdk": { "globals": { "AbortController": true, @@ -4144,59 +4239,67 @@ "packages": { "@ethereumjs/tx>@ethereumjs/common>crc-32": true, "@ethersproject/abi": true, - "@metamask/eth-sig-util": true, "@metamask/ethjs>js-sha3": true, "@metamask/keyring-api>bech32": true, - "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>buffer": true, "eth-lattice-keyring>gridplus-sdk>@ethereumjs/common": true, - "eth-lattice-keyring>gridplus-sdk>@ethereumjs/rlp": true, "eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx": true, "eth-lattice-keyring>gridplus-sdk>aes-js": true, "eth-lattice-keyring>gridplus-sdk>bignumber.js": true, + "eth-lattice-keyring>gridplus-sdk>bitwise": true, "eth-lattice-keyring>gridplus-sdk>borc": true, - "eth-lattice-keyring>gridplus-sdk>bs58check": true, - "eth-lattice-keyring>gridplus-sdk>secp256k1": true, + "eth-lattice-keyring>gridplus-sdk>elliptic": true, + "eth-lattice-keyring>gridplus-sdk>eth-eip712-util-browser": true, + "eth-lattice-keyring>gridplus-sdk>rlp": true, "eth-lattice-keyring>gridplus-sdk>uuid": true, + "ethereumjs-util>ethereum-cryptography>bs58check": true, "ethers>@ethersproject/sha2>hash.js": true, + "ganache>secp256k1": true, "lodash": true } }, "eth-lattice-keyring>gridplus-sdk>@ethereumjs/common": { "packages": { - "eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>@ethereumjs/util": true, + "@ethereumjs/tx>@ethereumjs/common>crc-32": true, + "@ethereumjs/tx>@ethereumjs/util": true, + "browserify>buffer": true, "webpack>events": true } }, - "eth-lattice-keyring>gridplus-sdk>@ethereumjs/rlp": { - "globals": { - "TextEncoder": true - } - }, "eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx": { "packages": { - "@ethereumjs/tx>ethereum-cryptography": true, - "eth-lattice-keyring>gridplus-sdk>@ethereumjs/rlp": true, + "@ethereumjs/tx>@ethereumjs/rlp": true, + "@ethereumjs/tx>@ethereumjs/util": true, + "@ethersproject/providers": true, + "browserify>buffer": true, + "browserify>insert-module-globals>is-buffer": true, + "eth-lattice-keyring>@ethereumjs/tx>@chainsafe/ssz": true, "eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>@ethereumjs/common": true, - "eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>@ethereumjs/util": true + "eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>ethereum-cryptography": true } }, "eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>@ethereumjs/common": { "packages": { - "eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>@ethereumjs/util": true, + "@ethereumjs/tx>@ethereumjs/common>crc-32": true, + "@ethereumjs/tx>@ethereumjs/util": true, + "browserify>buffer": true, "webpack>events": true } }, - "eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>@ethereumjs/util": { + "eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>ethereum-cryptography": { "globals": { - "console.warn": true, - "fetch": true + "TextDecoder": true, + "crypto": true }, "packages": { - "@ethereumjs/tx>ethereum-cryptography": true, - "eth-lattice-keyring>gridplus-sdk>@ethereumjs/rlp": true, - "webpack>events": true + "eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>ethereum-cryptography>@noble/hashes": true + } + }, + "eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>ethereum-cryptography>@noble/hashes": { + "globals": { + "TextEncoder": true, + "crypto": true } }, "eth-lattice-keyring>gridplus-sdk>aes-js": { @@ -4210,6 +4313,11 @@ "define": true } }, + "eth-lattice-keyring>gridplus-sdk>bitwise": { + "packages": { + "browserify>buffer": true + } + }, "eth-lattice-keyring>gridplus-sdk>borc": { "globals": { "console": true @@ -4231,24 +4339,33 @@ "globals": { "URL": true, "URLSearchParams": true, - "location": true, - "navigator": true + "location": true } }, - "eth-lattice-keyring>gridplus-sdk>bs58check": { + "eth-lattice-keyring>gridplus-sdk>elliptic": { "packages": { - "@noble/hashes": true, - "eth-lattice-keyring>gridplus-sdk>bs58check>bs58": true + "@metamask/ppom-validator>elliptic>brorand": true, + "@metamask/ppom-validator>elliptic>hmac-drbg": true, + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, + "@metamask/ppom-validator>elliptic>minimalistic-crypto-utils": true, + "bn.js": true, + "ethers>@ethersproject/sha2>hash.js": true, + "pumpify>inherits": true } }, - "eth-lattice-keyring>gridplus-sdk>bs58check>bs58": { + "eth-lattice-keyring>gridplus-sdk>eth-eip712-util-browser": { + "globals": { + "intToBuffer": true + }, "packages": { - "eth-lattice-keyring>gridplus-sdk>bs58check>bs58>base-x": true + "@metamask/ethjs>js-sha3": true, + "bn.js": true, + "buffer": true } }, - "eth-lattice-keyring>gridplus-sdk>secp256k1": { - "packages": { - "@metamask/ppom-validator>elliptic": true + "eth-lattice-keyring>gridplus-sdk>rlp": { + "globals": { + "TextEncoder": true } }, "eth-lattice-keyring>gridplus-sdk>uuid": { @@ -4572,9 +4689,20 @@ "ethers>@ethersproject/signing-key": { "packages": { "@ethersproject/bytes": true, - "@metamask/ppom-validator>elliptic": true, "ethers>@ethersproject/logger": true, - "ethers>@ethersproject/properties": true + "ethers>@ethersproject/properties": true, + "ethers>@ethersproject/signing-key>elliptic": true + } + }, + "ethers>@ethersproject/signing-key>elliptic": { + "packages": { + "@metamask/ppom-validator>elliptic>brorand": true, + "@metamask/ppom-validator>elliptic>hmac-drbg": true, + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, + "@metamask/ppom-validator>elliptic>minimalistic-crypto-utils": true, + "bn.js": true, + "ethers>@ethersproject/sha2>hash.js": true, + "pumpify>inherits": true } }, "ethers>@ethersproject/solidity": { @@ -4707,6 +4835,16 @@ "stream-http": true } }, + "json-rpc-middleware-stream": { + "globals": { + "console.warn": true, + "setTimeout": true + }, + "packages": { + "@metamask/safe-event-emitter": true, + "readable-stream": true + } + }, "koa>content-disposition>safe-buffer": { "packages": { "browserify>buffer": true @@ -5223,10 +5361,10 @@ "document": true }, "packages": { - "@babel/runtime": true, "prop-types": true, "react": true, "react-dom": true, + "react-redux>@babel/runtime": true, "react-redux>hoist-non-react-statics": true, "react-redux>react-is": true } @@ -5426,6 +5564,16 @@ "webpack>events": true } }, + "readable-stream-2>core-util-is": { + "packages": { + "browserify>insert-module-globals>is-buffer": true + } + }, + "readable-stream-2>process-nextick-args": { + "packages": { + "process": true + } + }, "readable-stream>util-deprecate": { "globals": { "console.trace": true, diff --git a/lavamoat/build-system/policy.json b/lavamoat/build-system/policy.json index 5a607452526c..e7ce64ceec23 100644 --- a/lavamoat/build-system/policy.json +++ b/lavamoat/build-system/policy.json @@ -64,7 +64,6 @@ "v8": true }, "globals": { - "URL": true, "console.error": true, "console.log": true, "process.env.BABEL_ENV": true, @@ -124,14 +123,12 @@ }, "@babel/core>@babel/generator>jsesc": { "globals": { - "Buffer": true + "Buffer.isBuffer": true } }, "@babel/core>@babel/helper-compilation-targets": { "globals": { "console.warn": true, - "process.env.BROWSERSLIST": true, - "process.env.BROWSERSLIST_CONFIG": true, "process.versions.node": true }, "packages": { @@ -183,7 +180,8 @@ "@babel/core>@babel/helpers": { "packages": { "@babel/core>@babel/template": true, - "@babel/core>@babel/types": true + "@babel/core>@babel/types": true, + "depcheck>@babel/traverse": true } }, "@babel/core>@babel/template": { @@ -196,10 +194,11 @@ "@babel/core>@babel/types": { "globals": { "console.warn": true, - "process.env": true + "process.env.BABEL_TYPES_8_BREAKING": true }, "packages": { "@babel/core>@babel/types>@babel/helper-string-parser": true, + "@babel/core>@babel/types>to-fast-properties": true, "lavamoat>@babel/highlight>@babel/helper-validator-identifier": true } }, @@ -283,17 +282,30 @@ }, "packages": { "@babel/core>@babel/helper-compilation-targets": true, + "@babel/core>@babel/types": true, "@babel/plugin-transform-logical-assignment-operators": true, "@babel/preset-env>@babel/compat-data": true, "@babel/preset-env>@babel/helper-plugin-utils": true, "@babel/preset-env>@babel/helper-validator-option": true, - "@babel/preset-env>@babel/plugin-bugfix-firefox-class-in-computed-class-key": true, - "@babel/preset-env>@babel/plugin-bugfix-safari-class-field-initializer-scope": true, "@babel/preset-env>@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": true, "@babel/preset-env>@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": true, - "@babel/preset-env>@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": true, + "@babel/preset-env>@babel/plugin-syntax-async-generators": true, + "@babel/preset-env>@babel/plugin-syntax-class-properties": true, + "@babel/preset-env>@babel/plugin-syntax-class-static-block": true, + "@babel/preset-env>@babel/plugin-syntax-dynamic-import": true, + "@babel/preset-env>@babel/plugin-syntax-export-namespace-from": true, "@babel/preset-env>@babel/plugin-syntax-import-assertions": true, "@babel/preset-env>@babel/plugin-syntax-import-attributes": true, + "@babel/preset-env>@babel/plugin-syntax-import-meta": true, + "@babel/preset-env>@babel/plugin-syntax-json-strings": true, + "@babel/preset-env>@babel/plugin-syntax-logical-assignment-operators": true, + "@babel/preset-env>@babel/plugin-syntax-nullish-coalescing-operator": true, + "@babel/preset-env>@babel/plugin-syntax-numeric-separator": true, + "@babel/preset-env>@babel/plugin-syntax-object-rest-spread": true, + "@babel/preset-env>@babel/plugin-syntax-optional-catch-binding": true, + "@babel/preset-env>@babel/plugin-syntax-optional-chaining": true, + "@babel/preset-env>@babel/plugin-syntax-private-property-in-object": true, + "@babel/preset-env>@babel/plugin-syntax-top-level-await": true, "@babel/preset-env>@babel/plugin-syntax-unicode-sets-regex": true, "@babel/preset-env>@babel/plugin-transform-arrow-functions": true, "@babel/preset-env>@babel/plugin-transform-async-generator-functions": true, @@ -307,7 +319,6 @@ "@babel/preset-env>@babel/plugin-transform-destructuring": true, "@babel/preset-env>@babel/plugin-transform-dotall-regex": true, "@babel/preset-env>@babel/plugin-transform-duplicate-keys": true, - "@babel/preset-env>@babel/plugin-transform-duplicate-named-capturing-groups-regex": true, "@babel/preset-env>@babel/plugin-transform-dynamic-import": true, "@babel/preset-env>@babel/plugin-transform-exponentiation-operator": true, "@babel/preset-env>@babel/plugin-transform-export-namespace-from": true, @@ -351,36 +362,42 @@ "@babel/preset-env>semver": true } }, - "@babel/preset-env>@babel/plugin-bugfix-firefox-class-in-computed-class-key": { + "@babel/preset-env>@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { "packages": { - "@babel/preset-env>@babel/helper-plugin-utils": true, - "depcheck>@babel/traverse": true + "@babel/preset-env>@babel/helper-plugin-utils": true } }, - "@babel/preset-env>@babel/plugin-bugfix-safari-class-field-initializer-scope": { + "@babel/preset-env>@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { "packages": { "@babel/core": true, + "@babel/preset-env>@babel/helper-plugin-utils": true, + "@babel/preset-env>@babel/plugin-transform-optional-chaining": true, + "@babel/preset-env>@babel/plugin-transform-spread>@babel/helper-skip-transparent-expression-wrappers": true + } + }, + "@babel/preset-env>@babel/plugin-syntax-async-generators": { + "packages": { "@babel/preset-env>@babel/helper-plugin-utils": true } }, - "@babel/preset-env>@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "@babel/preset-env>@babel/plugin-syntax-class-properties": { "packages": { "@babel/preset-env>@babel/helper-plugin-utils": true } }, - "@babel/preset-env>@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "@babel/preset-env>@babel/plugin-syntax-class-static-block": { "packages": { - "@babel/core": true, - "@babel/preset-env>@babel/helper-plugin-utils": true, - "@babel/preset-env>@babel/plugin-transform-for-of>@babel/helper-skip-transparent-expression-wrappers": true, - "@babel/preset-env>@babel/plugin-transform-optional-chaining": true + "@babel/preset-env>@babel/helper-plugin-utils": true } }, - "@babel/preset-env>@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { + "@babel/preset-env>@babel/plugin-syntax-dynamic-import": { "packages": { - "@babel/core": true, - "@babel/preset-env>@babel/helper-plugin-utils": true, - "depcheck>@babel/traverse": true + "@babel/preset-env>@babel/helper-plugin-utils": true + } + }, + "@babel/preset-env>@babel/plugin-syntax-export-namespace-from": { + "packages": { + "@babel/preset-env>@babel/helper-plugin-utils": true } }, "@babel/preset-env>@babel/plugin-syntax-import-assertions": { @@ -393,6 +410,56 @@ "@babel/preset-env>@babel/helper-plugin-utils": true } }, + "@babel/preset-env>@babel/plugin-syntax-import-meta": { + "packages": { + "@babel/preset-env>@babel/helper-plugin-utils": true + } + }, + "@babel/preset-env>@babel/plugin-syntax-json-strings": { + "packages": { + "@babel/preset-env>@babel/helper-plugin-utils": true + } + }, + "@babel/preset-env>@babel/plugin-syntax-logical-assignment-operators": { + "packages": { + "@babel/preset-env>@babel/helper-plugin-utils": true + } + }, + "@babel/preset-env>@babel/plugin-syntax-nullish-coalescing-operator": { + "packages": { + "@babel/preset-env>@babel/helper-plugin-utils": true + } + }, + "@babel/preset-env>@babel/plugin-syntax-numeric-separator": { + "packages": { + "@babel/preset-env>@babel/helper-plugin-utils": true + } + }, + "@babel/preset-env>@babel/plugin-syntax-object-rest-spread": { + "packages": { + "@babel/preset-env>@babel/helper-plugin-utils": true + } + }, + "@babel/preset-env>@babel/plugin-syntax-optional-catch-binding": { + "packages": { + "@babel/preset-env>@babel/helper-plugin-utils": true + } + }, + "@babel/preset-env>@babel/plugin-syntax-optional-chaining": { + "packages": { + "@babel/preset-env>@babel/helper-plugin-utils": true + } + }, + "@babel/preset-env>@babel/plugin-syntax-private-property-in-object": { + "packages": { + "@babel/preset-env>@babel/helper-plugin-utils": true + } + }, + "@babel/preset-env>@babel/plugin-syntax-top-level-await": { + "packages": { + "@babel/preset-env>@babel/helper-plugin-utils": true + } + }, "@babel/preset-env>@babel/plugin-syntax-unicode-sets-regex": { "packages": { "@babel/preset-env>@babel/helper-plugin-utils": true, @@ -408,8 +475,9 @@ "packages": { "@babel/core": true, "@babel/preset-env>@babel/helper-plugin-utils": true, + "@babel/preset-env>@babel/plugin-syntax-async-generators": true, "@babel/preset-env>@babel/plugin-transform-async-to-generator>@babel/helper-remap-async-to-generator": true, - "depcheck>@babel/traverse": true + "@babel/preset-env>@babel/plugin-transform-classes>@babel/helper-environment-visitor": true } }, "@babel/preset-env>@babel/plugin-transform-async-to-generator": { @@ -425,14 +493,14 @@ "@babel/core": true, "@babel/preset-env>@babel/plugin-transform-async-to-generator>@babel/helper-remap-async-to-generator>@babel/helper-wrap-function": true, "@babel/preset-env>@babel/plugin-transform-classes>@babel/helper-annotate-as-pure": true, - "depcheck>@babel/traverse": true + "@babel/preset-env>@babel/plugin-transform-classes>@babel/helper-environment-visitor": true } }, "@babel/preset-env>@babel/plugin-transform-async-to-generator>@babel/helper-remap-async-to-generator>@babel/helper-wrap-function": { "packages": { "@babel/core>@babel/template": true, "@babel/core>@babel/types": true, - "depcheck>@babel/traverse": true + "@babel/preset-env>@babel/plugin-transform-classes>@babel/helper-function-name": true } }, "@babel/preset-env>@babel/plugin-transform-block-scoped-functions": { @@ -456,6 +524,7 @@ "@babel/preset-env>@babel/plugin-transform-class-static-block": { "packages": { "@babel/preset-env>@babel/helper-plugin-utils": true, + "@babel/preset-env>@babel/plugin-syntax-class-static-block": true, "@babel/preset-env>@babel/plugin-transform-private-methods>@babel/helper-create-class-features-plugin": true } }, @@ -465,9 +534,12 @@ "@babel/core>@babel/helper-compilation-targets": true, "@babel/preset-env>@babel/helper-plugin-utils": true, "@babel/preset-env>@babel/plugin-transform-classes>@babel/helper-annotate-as-pure": true, + "@babel/preset-env>@babel/plugin-transform-classes>@babel/helper-environment-visitor": true, + "@babel/preset-env>@babel/plugin-transform-classes>@babel/helper-function-name": true, + "@babel/preset-env>@babel/plugin-transform-classes>@babel/helper-optimise-call-expression": true, "@babel/preset-env>@babel/plugin-transform-classes>@babel/helper-replace-supers": true, - "@babel/preset-env>@babel/plugin-transform-classes>globals": true, - "depcheck>@babel/traverse": true + "@babel/preset-env>@babel/plugin-transform-classes>@babel/helper-split-export-declaration": true, + "@babel/preset-env>@babel/plugin-transform-classes>globals": true } }, "@babel/preset-env>@babel/plugin-transform-classes>@babel/helper-annotate-as-pure": { @@ -475,11 +547,22 @@ "@babel/core>@babel/types": true } }, + "@babel/preset-env>@babel/plugin-transform-classes>@babel/helper-function-name": { + "packages": { + "@babel/core>@babel/template": true, + "@babel/core>@babel/types": true + } + }, + "@babel/preset-env>@babel/plugin-transform-classes>@babel/helper-optimise-call-expression": { + "packages": { + "@babel/core>@babel/types": true + } + }, "@babel/preset-env>@babel/plugin-transform-classes>@babel/helper-replace-supers": { "packages": { "@babel/core": true, + "@babel/preset-env>@babel/plugin-transform-classes>@babel/helper-optimise-call-expression": true, "@babel/preset-env>@babel/plugin-transform-classes>@babel/helper-replace-supers>@babel/helper-member-expression-to-functions": true, - "@babel/preset-env>@babel/plugin-transform-classes>@babel/helper-replace-supers>@babel/helper-optimise-call-expression": true, "depcheck>@babel/traverse": true } }, @@ -488,7 +571,7 @@ "@babel/core>@babel/types": true } }, - "@babel/preset-env>@babel/plugin-transform-classes>@babel/helper-replace-supers>@babel/helper-optimise-call-expression": { + "@babel/preset-env>@babel/plugin-transform-classes>@babel/helper-split-export-declaration": { "packages": { "@babel/core>@babel/types": true } @@ -525,19 +608,19 @@ "characterClassItem.kind": true }, "packages": { + "@babel/preset-env>@babel/plugin-transform-dotall-regex>@babel/helper-create-regexp-features-plugin>regexpu-core>@babel/regjsgen": true, "@babel/preset-env>@babel/plugin-transform-dotall-regex>@babel/helper-create-regexp-features-plugin>regexpu-core>regenerate": true, - "@babel/preset-env>@babel/plugin-transform-dotall-regex>@babel/helper-create-regexp-features-plugin>regexpu-core>regjsgen": true, "@babel/preset-env>@babel/plugin-transform-dotall-regex>@babel/helper-create-regexp-features-plugin>regexpu-core>regjsparser": true, "@babel/preset-env>@babel/plugin-transform-dotall-regex>@babel/helper-create-regexp-features-plugin>regexpu-core>unicode-match-property-ecmascript": true, "@babel/preset-env>@babel/plugin-transform-dotall-regex>@babel/helper-create-regexp-features-plugin>regexpu-core>unicode-match-property-value-ecmascript": true } }, - "@babel/preset-env>@babel/plugin-transform-dotall-regex>@babel/helper-create-regexp-features-plugin>regexpu-core>regenerate": { + "@babel/preset-env>@babel/plugin-transform-dotall-regex>@babel/helper-create-regexp-features-plugin>regexpu-core>@babel/regjsgen": { "globals": { "define": true } }, - "@babel/preset-env>@babel/plugin-transform-dotall-regex>@babel/helper-create-regexp-features-plugin>regexpu-core>regjsgen": { + "@babel/preset-env>@babel/plugin-transform-dotall-regex>@babel/helper-create-regexp-features-plugin>regexpu-core>regenerate": { "globals": { "define": true } @@ -565,15 +648,10 @@ "@babel/preset-env>@babel/helper-plugin-utils": true } }, - "@babel/preset-env>@babel/plugin-transform-duplicate-named-capturing-groups-regex": { - "packages": { - "@babel/preset-env>@babel/helper-plugin-utils": true, - "@babel/preset-env>@babel/plugin-transform-dotall-regex>@babel/helper-create-regexp-features-plugin": true - } - }, "@babel/preset-env>@babel/plugin-transform-dynamic-import": { "packages": { - "@babel/preset-env>@babel/helper-plugin-utils": true + "@babel/preset-env>@babel/helper-plugin-utils": true, + "@babel/preset-env>@babel/plugin-syntax-dynamic-import": true } }, "@babel/preset-env>@babel/plugin-transform-exponentiation-operator": { @@ -591,31 +669,27 @@ "@babel/preset-env>@babel/plugin-transform-export-namespace-from": { "packages": { "@babel/core": true, - "@babel/preset-env>@babel/helper-plugin-utils": true + "@babel/preset-env>@babel/helper-plugin-utils": true, + "@babel/preset-env>@babel/plugin-syntax-export-namespace-from": true } }, "@babel/preset-env>@babel/plugin-transform-for-of": { "packages": { "@babel/core": true, - "@babel/preset-env>@babel/helper-plugin-utils": true, - "@babel/preset-env>@babel/plugin-transform-for-of>@babel/helper-skip-transparent-expression-wrappers": true - } - }, - "@babel/preset-env>@babel/plugin-transform-for-of>@babel/helper-skip-transparent-expression-wrappers": { - "packages": { - "@babel/core>@babel/types": true + "@babel/preset-env>@babel/helper-plugin-utils": true } }, "@babel/preset-env>@babel/plugin-transform-function-name": { "packages": { "@babel/core>@babel/helper-compilation-targets": true, "@babel/preset-env>@babel/helper-plugin-utils": true, - "depcheck>@babel/traverse": true + "@babel/preset-env>@babel/plugin-transform-classes>@babel/helper-function-name": true } }, "@babel/preset-env>@babel/plugin-transform-json-strings": { "packages": { - "@babel/preset-env>@babel/helper-plugin-utils": true + "@babel/preset-env>@babel/helper-plugin-utils": true, + "@babel/preset-env>@babel/plugin-syntax-json-strings": true } }, "@babel/preset-env>@babel/plugin-transform-literals": { @@ -652,10 +726,15 @@ "@babel/core": true, "@babel/core>@babel/helper-module-transforms": true, "@babel/preset-env>@babel/helper-plugin-utils": true, - "depcheck>@babel/traverse": true, + "@babel/preset-env>@babel/plugin-transform-modules-systemjs>@babel/helper-hoist-variables": true, "lavamoat>@babel/highlight>@babel/helper-validator-identifier": true } }, + "@babel/preset-env>@babel/plugin-transform-modules-systemjs>@babel/helper-hoist-variables": { + "packages": { + "@babel/core>@babel/types": true + } + }, "@babel/preset-env>@babel/plugin-transform-modules-umd": { "builtin": { "path.basename": true, @@ -682,19 +761,23 @@ "@babel/preset-env>@babel/plugin-transform-nullish-coalescing-operator": { "packages": { "@babel/core": true, - "@babel/preset-env>@babel/helper-plugin-utils": true + "@babel/preset-env>@babel/helper-plugin-utils": true, + "@babel/preset-env>@babel/plugin-syntax-nullish-coalescing-operator": true } }, "@babel/preset-env>@babel/plugin-transform-numeric-separator": { "packages": { - "@babel/preset-env>@babel/helper-plugin-utils": true + "@babel/preset-env>@babel/helper-plugin-utils": true, + "@babel/preset-env>@babel/plugin-syntax-numeric-separator": true } }, "@babel/preset-env>@babel/plugin-transform-object-rest-spread": { "packages": { "@babel/core": true, "@babel/core>@babel/helper-compilation-targets": true, + "@babel/preset-env>@babel/compat-data": true, "@babel/preset-env>@babel/helper-plugin-utils": true, + "@babel/preset-env>@babel/plugin-syntax-object-rest-spread": true, "@babel/preset-env>@babel/plugin-transform-parameters": true } }, @@ -707,14 +790,16 @@ }, "@babel/preset-env>@babel/plugin-transform-optional-catch-binding": { "packages": { - "@babel/preset-env>@babel/helper-plugin-utils": true + "@babel/preset-env>@babel/helper-plugin-utils": true, + "@babel/preset-env>@babel/plugin-syntax-optional-catch-binding": true } }, "@babel/preset-env>@babel/plugin-transform-optional-chaining": { "packages": { "@babel/core": true, "@babel/preset-env>@babel/helper-plugin-utils": true, - "@babel/preset-env>@babel/plugin-transform-for-of>@babel/helper-skip-transparent-expression-wrappers": true + "@babel/preset-env>@babel/plugin-syntax-optional-chaining": true, + "@babel/preset-env>@babel/plugin-transform-spread>@babel/helper-skip-transparent-expression-wrappers": true } }, "@babel/preset-env>@babel/plugin-transform-parameters": { @@ -736,11 +821,11 @@ "packages": { "@babel/core": true, "@babel/preset-env>@babel/plugin-transform-classes>@babel/helper-annotate-as-pure": true, + "@babel/preset-env>@babel/plugin-transform-classes>@babel/helper-optimise-call-expression": true, "@babel/preset-env>@babel/plugin-transform-classes>@babel/helper-replace-supers": true, "@babel/preset-env>@babel/plugin-transform-classes>@babel/helper-replace-supers>@babel/helper-member-expression-to-functions": true, - "@babel/preset-env>@babel/plugin-transform-classes>@babel/helper-replace-supers>@babel/helper-optimise-call-expression": true, - "@babel/preset-env>@babel/plugin-transform-for-of>@babel/helper-skip-transparent-expression-wrappers": true, "@babel/preset-env>@babel/plugin-transform-private-methods>@babel/helper-create-class-features-plugin>semver": true, + "@babel/preset-env>@babel/plugin-transform-spread>@babel/helper-skip-transparent-expression-wrappers": true, "depcheck>@babel/traverse": true } }, @@ -753,6 +838,7 @@ "@babel/preset-env>@babel/plugin-transform-private-property-in-object": { "packages": { "@babel/preset-env>@babel/helper-plugin-utils": true, + "@babel/preset-env>@babel/plugin-syntax-private-property-in-object": true, "@babel/preset-env>@babel/plugin-transform-classes>@babel/helper-annotate-as-pure": true, "@babel/preset-env>@babel/plugin-transform-private-methods>@babel/helper-create-class-features-plugin": true } @@ -794,7 +880,12 @@ "packages": { "@babel/core": true, "@babel/preset-env>@babel/helper-plugin-utils": true, - "@babel/preset-env>@babel/plugin-transform-for-of>@babel/helper-skip-transparent-expression-wrappers": true + "@babel/preset-env>@babel/plugin-transform-spread>@babel/helper-skip-transparent-expression-wrappers": true + } + }, + "@babel/preset-env>@babel/plugin-transform-spread>@babel/helper-skip-transparent-expression-wrappers": { + "packages": { + "@babel/core>@babel/types": true } }, "@babel/preset-env>@babel/plugin-transform-sticky-regex": { @@ -963,8 +1054,8 @@ "@babel/core": true, "@babel/preset-env>@babel/helper-plugin-utils": true, "@babel/preset-env>@babel/plugin-transform-classes>@babel/helper-annotate-as-pure": true, - "@babel/preset-env>@babel/plugin-transform-for-of>@babel/helper-skip-transparent-expression-wrappers": true, "@babel/preset-env>@babel/plugin-transform-private-methods>@babel/helper-create-class-features-plugin": true, + "@babel/preset-env>@babel/plugin-transform-spread>@babel/helper-skip-transparent-expression-wrappers": true, "@babel/preset-typescript>@babel/plugin-transform-typescript>@babel/plugin-syntax-typescript": true } }, @@ -6237,23 +6328,24 @@ "packages": { "@babel/code-frame": true, "@babel/core>@babel/generator": true, + "@babel/preset-env>@babel/plugin-transform-classes>@babel/helper-environment-visitor": true, + "@babel/preset-env>@babel/plugin-transform-classes>@babel/helper-function-name": true, + "@babel/preset-env>@babel/plugin-transform-classes>@babel/helper-split-export-declaration": true, + "@babel/preset-env>@babel/plugin-transform-modules-systemjs>@babel/helper-hoist-variables": true, "lavamoat-viz>lavamoat-core>lavamoat-tofu>@babel/traverse>@babel/parser": true, "lavamoat-viz>lavamoat-core>lavamoat-tofu>@babel/traverse>@babel/types": true, "lavamoat-viz>lavamoat-core>lavamoat-tofu>@babel/traverse>globals": true, - "lavamoat>lavamoat-tofu>@babel/traverse>@babel/helper-environment-visitor": true, - "lavamoat>lavamoat-tofu>@babel/traverse>@babel/helper-function-name": true, - "lavamoat>lavamoat-tofu>@babel/traverse>@babel/helper-hoist-variables": true, - "lavamoat>lavamoat-tofu>@babel/traverse>@babel/helper-split-export-declaration": true, "nock>debug": true } }, "lavamoat-viz>lavamoat-core>lavamoat-tofu>@babel/traverse>@babel/types": { "globals": { "console.warn": true, - "process.env": true + "process.env.BABEL_TYPES_8_BREAKING": true }, "packages": { "@babel/core>@babel/types>@babel/helper-string-parser": true, + "@babel/core>@babel/types>to-fast-properties": true, "lavamoat>@babel/highlight>@babel/helper-validator-identifier": true } }, @@ -6281,9 +6373,9 @@ "packages": { "@babel/register>clone-deep>is-plain-object": true, "lavamoat>lavamoat-core>merge-deep>clone-deep>for-own": true, + "lavamoat>lavamoat-core>merge-deep>clone-deep>kind-of": true, "lavamoat>lavamoat-core>merge-deep>clone-deep>lazy-cache": true, - "lavamoat>lavamoat-core>merge-deep>clone-deep>shallow-clone": true, - "lavamoat>lavamoat-core>merge-deep>kind-of": true + "lavamoat>lavamoat-core>merge-deep>clone-deep>shallow-clone": true } }, "lavamoat>lavamoat-core>merge-deep>clone-deep>for-own": { @@ -6291,6 +6383,11 @@ "gulp>undertaker>object.reduce>for-own>for-in": true } }, + "lavamoat>lavamoat-core>merge-deep>clone-deep>kind-of": { + "packages": { + "browserify>insert-module-globals>is-buffer": true + } + }, "lavamoat>lavamoat-core>merge-deep>clone-deep>lazy-cache": { "globals": { "process.env.TRAVIS": true, @@ -6329,22 +6426,6 @@ "browserify>insert-module-globals>is-buffer": true } }, - "lavamoat>lavamoat-tofu>@babel/traverse>@babel/helper-function-name": { - "packages": { - "@babel/core>@babel/template": true, - "@babel/core>@babel/types": true - } - }, - "lavamoat>lavamoat-tofu>@babel/traverse>@babel/helper-hoist-variables": { - "packages": { - "@babel/core>@babel/types": true - } - }, - "lavamoat>lavamoat-tofu>@babel/traverse>@babel/helper-split-export-declaration": { - "packages": { - "@babel/core>@babel/types": true - } - }, "lodash": { "globals": { "define": true @@ -6605,8 +6686,13 @@ } }, "postcss>picocolors": { + "builtin": { + "tty.isatty": true + }, "globals": { - "process": true + "process.argv.includes": true, + "process.env": true, + "process.platform": true } }, "postcss>source-map-js": { diff --git a/package.json b/package.json index f7900e85d536..806231305329 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "metamask-crx", - "version": "12.7.0", + "version": "12.6.2", "private": true, "repository": { "type": "git", @@ -48,8 +48,8 @@ "test:unit:coverage": "jest --coverage", "test:unit:webpack": "tsx --test development/webpack/test/*.test.ts", "test:unit:webpack:coverage": "nyc --reporter=html --reporter=json --reporter=text --report-dir=./coverage/webpack tsx --test development/webpack/test/*.test.ts", - "test:integration": "npx webpack build --config ./development/webpack/webpack.integration.tests.config.ts && jest --config jest.integration.config.js", - "test:integration:coverage": "yarn test:integration --coverage", + "test:integration": "jest --config jest.integration.config.js", + "test:integration:coverage": "jest --config jest.integration.config.js --coverage", "test:e2e:chrome": "SELENIUM_BROWSER=chrome node test/e2e/run-all.js", "test:e2e:chrome:mmi": "SELENIUM_BROWSER=chrome node test/e2e/run-all.js --mmi", "test:e2e:chrome:flask": "SELENIUM_BROWSER=chrome node test/e2e/run-all.js --build-type flask", @@ -132,12 +132,8 @@ }, "resolutions": { "chokidar": "^3.6.0", - "gridplus-sdk/elliptic": "^6.5.7", - "gridplus-sdk/secp256k1": "^5.0.1", - "eth-lattice-keyring/@ethereumjs/tx": "^4.2.0", - "@ethersproject/signing-key/elliptic": "^6.5.7", - "ganache/secp256k1": "^4.0.4", "simple-update-notifier@^1.0.0": "^2.0.0", + "@babel/core": "patch:@babel/core@npm%3A7.23.2#~/.yarn/patches/@babel-core-npm-7.23.2-b93f586907.patch", "@types/react": "^16.9.53", "analytics-node/axios": "^0.21.2", "bn.js": "^5.2.1", @@ -235,8 +231,26 @@ "lavamoat-core@npm:^15.1.1": "patch:lavamoat-core@npm%3A15.1.1#~/.yarn/patches/lavamoat-core-npm-15.1.1-51fbe39988.patch", "@metamask/snaps-sdk": "^6.9.0", "@swc/types@0.1.5": "^0.1.6", - "@babel/core": "patch:@babel/core@npm%3A7.25.9#~/.yarn/patches/@babel-core-npm-7.25.9-4ae3bff7f3.patch", - "@babel/runtime": "patch:@babel/runtime@npm%3A7.25.9#~/.yarn/patches/@babel-runtime-npm-7.25.9-fe8c62510a.patch", + "@babel/runtime@npm:^7.7.6": "patch:@babel/runtime@npm%3A7.24.0#~/.yarn/patches/@babel-runtime-npm-7.24.0-7eb1dd11a2.patch", + "@babel/runtime@npm:^7.9.2": "patch:@babel/runtime@npm%3A7.24.0#~/.yarn/patches/@babel-runtime-npm-7.24.0-7eb1dd11a2.patch", + "@babel/runtime@npm:^7.12.5": "patch:@babel/runtime@npm%3A7.24.0#~/.yarn/patches/@babel-runtime-npm-7.24.0-7eb1dd11a2.patch", + "@babel/runtime@npm:^7.10.3": "patch:@babel/runtime@npm%3A7.24.0#~/.yarn/patches/@babel-runtime-npm-7.24.0-7eb1dd11a2.patch", + "@babel/runtime@npm:^7.5.5": "patch:@babel/runtime@npm%3A7.24.0#~/.yarn/patches/@babel-runtime-npm-7.24.0-7eb1dd11a2.patch", + "@babel/runtime@npm:^7.3.1": "patch:@babel/runtime@npm%3A7.24.0#~/.yarn/patches/@babel-runtime-npm-7.24.0-7eb1dd11a2.patch", + "@babel/runtime@npm:^7.0.0": "patch:@babel/runtime@npm%3A7.24.0#~/.yarn/patches/@babel-runtime-npm-7.24.0-7eb1dd11a2.patch", + "@babel/runtime@npm:^7.1.2": "patch:@babel/runtime@npm%3A7.24.0#~/.yarn/patches/@babel-runtime-npm-7.24.0-7eb1dd11a2.patch", + "@babel/runtime@npm:^7.12.13": "patch:@babel/runtime@npm%3A7.24.0#~/.yarn/patches/@babel-runtime-npm-7.24.0-7eb1dd11a2.patch", + "@babel/runtime@npm:^7.4.4": "patch:@babel/runtime@npm%3A7.24.0#~/.yarn/patches/@babel-runtime-npm-7.24.0-7eb1dd11a2.patch", + "@babel/runtime@npm:^7.10.2": "patch:@babel/runtime@npm%3A7.24.0#~/.yarn/patches/@babel-runtime-npm-7.24.0-7eb1dd11a2.patch", + "@babel/runtime@npm:^7.21.0": "patch:@babel/runtime@npm%3A7.24.0#~/.yarn/patches/@babel-runtime-npm-7.24.0-7eb1dd11a2.patch", + "@babel/runtime@npm:^7.17.8": "patch:@babel/runtime@npm%3A7.24.0#~/.yarn/patches/@babel-runtime-npm-7.24.0-7eb1dd11a2.patch", + "@babel/runtime@npm:^7.13.10": "patch:@babel/runtime@npm%3A7.24.0#~/.yarn/patches/@babel-runtime-npm-7.24.0-7eb1dd11a2.patch", + "@babel/runtime@npm:^7.12.0": "patch:@babel/runtime@npm%3A7.24.0#~/.yarn/patches/@babel-runtime-npm-7.24.0-7eb1dd11a2.patch", + "@babel/runtime@npm:^7.8.7": "patch:@babel/runtime@npm%3A7.24.0#~/.yarn/patches/@babel-runtime-npm-7.24.0-7eb1dd11a2.patch", + "@babel/runtime@npm:^7.4.0": "patch:@babel/runtime@npm%3A7.24.0#~/.yarn/patches/@babel-runtime-npm-7.24.0-7eb1dd11a2.patch", + "@babel/runtime@npm:^7.18.3": "patch:@babel/runtime@npm%3A7.24.0#~/.yarn/patches/@babel-runtime-npm-7.24.0-7eb1dd11a2.patch", + "@babel/runtime@npm:^7.8.3": "patch:@babel/runtime@npm%3A7.24.0#~/.yarn/patches/@babel-runtime-npm-7.24.0-7eb1dd11a2.patch", + "@babel/runtime@npm:^7.8.4": "patch:@babel/runtime@npm%3A7.24.0#~/.yarn/patches/@babel-runtime-npm-7.24.0-7eb1dd11a2.patch", "@spruceid/siwe-parser@npm:1.1.3": "patch:@spruceid/siwe-parser@npm%3A2.1.0#~/.yarn/patches/@spruceid-siwe-parser-npm-2.1.0-060b7ede7a.patch", "@spruceid/siwe-parser@npm:2.1.0": "patch:@spruceid/siwe-parser@npm%3A2.1.0#~/.yarn/patches/@spruceid-siwe-parser-npm-2.1.0-060b7ede7a.patch", "ts-mixer@npm:^6.0.3": "patch:ts-mixer@npm%3A6.0.4#~/.yarn/patches/ts-mixer-npm-6.0.4-5d9747bdf5.patch", @@ -251,14 +265,14 @@ "@metamask/network-controller@npm:^19.0.0": "patch:@metamask/network-controller@npm%3A21.0.0#~/.yarn/patches/@metamask-network-controller-npm-21.0.0-559aa8e395.patch", "@metamask/network-controller@npm:^20.0.0": "patch:@metamask/network-controller@npm%3A21.0.0#~/.yarn/patches/@metamask-network-controller-npm-21.0.0-559aa8e395.patch", "path-to-regexp": "1.9.0", - "@metamask/snaps-utils@npm:^8.4.1": "patch:@metamask/snaps-utils@npm%3A8.4.1#~/.yarn/patches/@metamask-snaps-utils-npm-8.4.1-90481bac4b.patch", - "@metamask/snaps-utils@npm:^8.3.0": "patch:@metamask/snaps-utils@npm%3A8.4.1#~/.yarn/patches/@metamask-snaps-utils-npm-8.4.1-90481bac4b.patch", - "@metamask/snaps-utils@npm:^8.1.1": "patch:@metamask/snaps-utils@npm%3A8.4.1#~/.yarn/patches/@metamask-snaps-utils-npm-8.4.1-90481bac4b.patch", - "@metamask/snaps-utils@npm:^8.4.0": "patch:@metamask/snaps-utils@npm%3A8.4.1#~/.yarn/patches/@metamask-snaps-utils-npm-8.4.1-90481bac4b.patch", - "@metamask/snaps-utils@npm:^7.4.0": "patch:@metamask/snaps-utils@npm%3A8.4.1#~/.yarn/patches/@metamask-snaps-utils-npm-8.4.1-90481bac4b.patch" + "secp256k1@npm:^4.0.0": "4.0.4", + "secp256k1@npm:^4.0.1": "4.0.4", + "secp256k1@npm:4.0.2": "4.0.4", + "secp256k1@npm:4.0.3": "4.0.4", + "cross-spawn@npm:^5.0.1": "^7.0.5" }, "dependencies": { - "@babel/runtime": "patch:@babel/runtime@npm%3A7.25.9#~/.yarn/patches/@babel-runtime-npm-7.25.9-fe8c62510a.patch", + "@babel/runtime": "patch:@babel/runtime@npm%3A7.24.0#~/.yarn/patches/@babel-runtime-npm-7.24.0-7eb1dd11a2.patch", "@blockaid/ppom_release": "^1.5.3", "@ensdomains/content-hash": "^2.5.7", "@ethereumjs/tx": "^4.1.1", @@ -272,12 +286,12 @@ "@ethersproject/wallet": "^5.7.0", "@fortawesome/fontawesome-free": "^5.13.0", "@keystonehq/bc-ur-registry-eth": "^0.19.1", - "@keystonehq/metamask-airgapped-keyring": "^0.14.1", + "@keystonehq/metamask-airgapped-keyring": "^0.13.1", "@lavamoat/lavadome-react": "0.0.17", "@lavamoat/snow": "^2.0.2", "@material-ui/core": "^4.11.0", "@metamask-institutional/custody-controller": "^0.3.0", - "@metamask-institutional/custody-keyring": "^2.1.1", + "@metamask-institutional/custody-keyring": "^2.1.0", "@metamask-institutional/extension": "^0.3.28", "@metamask-institutional/institutional-features": "^1.3.6", "@metamask-institutional/portfolio-dashboard": "^1.4.1", @@ -291,16 +305,16 @@ "@metamask/address-book-controller": "^6.0.0", "@metamask/announcement-controller": "^7.0.0", "@metamask/approval-controller": "^7.0.0", - "@metamask/assets-controllers": "patch:@metamask/assets-controllers@npm%3A41.0.0#~/.yarn/patches/@metamask-assets-controllers-npm-41.0.0-57b3d695bb.patch", + "@metamask/assets-controllers": "patch:@metamask/assets-controllers@npm%3A38.3.0#~/.yarn/patches/@metamask-assets-controllers-npm-38.3.0-57b3d695bb.patch", "@metamask/base-controller": "^7.0.0", "@metamask/bitcoin-wallet-snap": "^0.8.2", "@metamask/browser-passworder": "^4.3.0", "@metamask/contract-metadata": "^2.5.0", - "@metamask/controller-utils": "^11.4.0", + "@metamask/controller-utils": "^11.2.0", "@metamask/design-tokens": "^4.0.0", "@metamask/ens-controller": "^13.0.0", "@metamask/ens-resolver-snap": "^0.1.2", - "@metamask/eth-json-rpc-filters": "^9.0.0", + "@metamask/eth-json-rpc-filters": "^7.0.0", "@metamask/eth-json-rpc-middleware": "patch:@metamask/eth-json-rpc-middleware@npm%3A14.0.1#~/.yarn/patches/@metamask-eth-json-rpc-middleware-npm-14.0.1-b6c2ccbe8c.patch", "@metamask/eth-ledger-bridge-keyring": "^3.0.1", "@metamask/eth-query": "^4.0.0", @@ -315,7 +329,6 @@ "@metamask/gas-fee-controller": "^18.0.0", "@metamask/jazzicon": "^2.0.0", "@metamask/json-rpc-engine": "^10.0.0", - "@metamask/json-rpc-middleware-stream": "^8.0.4", "@metamask/keyring-api": "^8.1.3", "@metamask/keyring-controller": "^17.2.2", "@metamask/logging-controller": "^6.0.0", @@ -326,31 +339,30 @@ "@metamask/name-controller": "^8.0.0", "@metamask/network-controller": "patch:@metamask/network-controller@npm%3A21.0.0#~/.yarn/patches/@metamask-network-controller-npm-21.0.0-559aa8e395.patch", "@metamask/notification-controller": "^6.0.0", - "@metamask/notification-services-controller": "^0.11.0", + "@metamask/notification-services-controller": "^0.7.0", "@metamask/object-multiplex": "^2.0.0", "@metamask/obs-store": "^9.0.0", "@metamask/permission-controller": "^10.0.0", "@metamask/permission-log-controller": "^2.0.1", "@metamask/phishing-controller": "^12.3.0", - "@metamask/polling-controller": "^10.0.1", "@metamask/post-message-stream": "^8.0.0", "@metamask/ppom-validator": "0.35.1", "@metamask/preinstalled-example-snap": "^0.2.0", "@metamask/profile-sync-controller": "^0.9.7", "@metamask/providers": "^14.0.2", - "@metamask/queued-request-controller": "^7.0.1", + "@metamask/queued-request-controller": "^7.0.0", "@metamask/rate-limit-controller": "^6.0.0", "@metamask/rpc-errors": "^7.0.0", "@metamask/safe-event-emitter": "^3.1.1", "@metamask/scure-bip39": "^2.0.3", "@metamask/selected-network-controller": "^18.0.2", - "@metamask/signature-controller": "^21.0.0", + "@metamask/signature-controller": "^20.0.0", "@metamask/smart-transactions-controller": "^13.0.0", "@metamask/snaps-controllers": "^9.11.1", "@metamask/snaps-execution-environments": "^6.9.1", "@metamask/snaps-rpc-methods": "^11.5.0", "@metamask/snaps-sdk": "^6.9.0", - "@metamask/snaps-utils": "patch:@metamask/snaps-utils@npm%3A8.4.1#~/.yarn/patches/@metamask-snaps-utils-npm-8.4.1-90481bac4b.patch", + "@metamask/snaps-utils": "^8.4.1", "@metamask/transaction-controller": "^38.3.0", "@metamask/user-operation-controller": "^13.0.0", "@metamask/utils": "^9.3.0", @@ -395,6 +407,7 @@ "immer": "^9.0.6", "is-retry-allowed": "^2.2.0", "jest-junit": "^14.0.1", + "json-rpc-middleware-stream": "^5.0.1", "labeled-stream-splicer": "^2.0.2", "localforage": "^1.9.0", "lodash": "^4.17.21", @@ -443,15 +456,15 @@ "devDependencies": { "@actions/core": "^1.10.0", "@actions/github": "^5.1.1", - "@babel/code-frame": "^7.25.9", - "@babel/core": "patch:@babel/core@npm%3A7.25.9#~/.yarn/patches/@babel-core-npm-7.25.9-4ae3bff7f3.patch", - "@babel/eslint-parser": "^7.25.9", - "@babel/eslint-plugin": "^7.25.9", - "@babel/plugin-transform-logical-assignment-operators": "^7.25.9", - "@babel/preset-env": "^7.25.9", - "@babel/preset-react": "^7.25.9", - "@babel/preset-typescript": "^7.25.9", - "@babel/register": "^7.25.9", + "@babel/code-frame": "^7.22.13", + "@babel/core": "^7.23.2", + "@babel/eslint-parser": "^7.23.10", + "@babel/eslint-plugin": "^7.23.5", + "@babel/plugin-transform-logical-assignment-operators": "^7.23.4", + "@babel/preset-env": "^7.23.2", + "@babel/preset-react": "^7.22.15", + "@babel/preset-typescript": "^7.23.2", + "@babel/register": "^7.22.15", "@jest/globals": "^29.7.0", "@lavamoat/allow-scripts": "^3.0.4", "@lavamoat/lavadome-core": "0.0.10", @@ -468,7 +481,7 @@ "@metamask/eslint-config-typescript": "^9.0.1", "@metamask/eslint-plugin-design-tokens": "^1.1.0", "@metamask/forwarder": "^1.1.0", - "@metamask/phishing-warning": "^4.1.0", + "@metamask/phishing-warning": "^4.0.0", "@metamask/preferences-controller": "^13.0.2", "@metamask/test-bundler": "^1.0.0", "@metamask/test-dapp": "8.7.0", @@ -556,7 +569,7 @@ "concurrently": "^8.2.2", "copy-webpack-plugin": "^12.0.2", "core-js-pure": "^3.38.0", - "cross-spawn": "^7.0.3", + "cross-spawn": "^7.0.5", "crypto-browserify": "^3.12.0", "css-loader": "^6.10.0", "css-to-xpath": "^0.1.0", @@ -607,7 +620,6 @@ "jest": "^29.7.0", "jest-canvas-mock": "^2.3.1", "jest-environment-jsdom": "patch:jest-environment-jsdom@npm%3A29.7.0#~/.yarn/patches/jest-environment-jsdom-npm-29.7.0-0b72dd0e0b.patch", - "jest-preview": "^0.3.1", "jsdom": "^16.7.0", "json-schema-to-ts": "^3.0.1", "koa": "^2.7.0", @@ -617,7 +629,6 @@ "level": "^8.0.1", "lockfile-lint": "^4.10.6", "loose-envify": "^1.4.0", - "mini-css-extract-plugin": "^2.9.1", "mocha": "^10.2.0", "mocha-junit-reporter": "^2.2.1", "mockttp": "^3.10.1", @@ -675,7 +686,6 @@ "watchify": "^4.0.0", "webextension-polyfill": "^0.8.0", "webpack": "^5.91.0", - "webpack-cli": "^5.1.4", "webpack-dev-server": "^5.0.3", "ws": "^8.17.1", "yaml": "^2.4.1", @@ -693,10 +703,17 @@ "@eth-optimism/contracts>@ethersproject/hardware-wallets>@ledgerhq/hw-transport-node-hid>@ledgerhq/hw-transport-node-hid-noevents>node-hid": false, "@eth-optimism/contracts>@ethersproject/hardware-wallets>@ledgerhq/hw-transport-node-hid>node-hid": false, "@eth-optimism/contracts>@ethersproject/hardware-wallets>@ledgerhq/hw-transport-node-hid>usb": false, + "@metamask/controllers>web3-provider-engine>ethereumjs-util>keccak": false, + "@metamask/controllers>web3-provider-engine>ethereumjs-util>secp256k1": false, + "@metamask/controllers>web3-provider-engine>ethereumjs-vm>merkle-patricia-tree>ethereumjs-util>keccak": false, + "@metamask/controllers>web3-provider-engine>ethereumjs-vm>merkle-patricia-tree>ethereumjs-util>secp256k1": false, + "@metamask/eth-ledger-bridge-keyring>hdkey>secp256k1": false, "@storybook/api>core-js": false, "@storybook/core>@storybook/core-client>@storybook/ui>core-js-pure": false, "@storybook/test-runner>@storybook/core-common>esbuild": false, - "eth-lattice-keyring>gridplus-sdk": true, + "eth-json-rpc-filters>eth-json-rpc-middleware>ethereumjs-util>keccak": false, + "eth-json-rpc-filters>eth-json-rpc-middleware>ethereumjs-util>secp256k1": false, + "eth-lattice-keyring>gridplus-sdk": false, "ethereumjs-util>ethereum-cryptography>keccak": false, "ganache>@trufflesuite/bigint-buffer": false, "ganache>@trufflesuite/uws-js-unofficial>bufferutil": false, @@ -706,10 +723,13 @@ "ganache>leveldown": false, "ganache>secp256k1": false, "ganache>utf-8-validate": false, + "ethereumjs-util>ethereum-cryptography>secp256k1": false, "gulp-watch>chokidar>fsevents": false, "gulp>glob-watcher>chokidar>fsevents": false, "webpack>watchpack>watchpack-chokidar2>chokidar>fsevents": false, + "@keystonehq/bc-ur-registry-eth>hdkey>secp256k1": false, "eth-lattice-keyring>gridplus-sdk>secp256k1": false, + "eth-lattice-keyring>secp256k1": false, "@storybook/react>@pmmmwh/react-refresh-webpack-plugin>core-js-pure": false, "@testing-library/jest-dom>aria-query>@babel/runtime-corejs3>core-js-pure": false, "web3": false, @@ -718,6 +738,7 @@ "web3>web3-core>web3-core-requestmanager>web3-providers-ws>websocket>es5-ext": false, "web3>web3-core>web3-core-requestmanager>web3-providers-ws>websocket>utf-8-validate": false, "web3>web3-shh": false, + "@keystonehq/metamask-airgapped-keyring>@keystonehq/base-eth-keyring>hdkey>secp256k1": false, "@metamask/base-controller>simple-git-hooks": false, "@storybook/core>@storybook/core-server>webpack>watchpack>watchpack-chokidar2>chokidar>fsevents": false, "resolve-url-loader>es6-iterator>es5-ext": false, @@ -749,8 +770,7 @@ "core-js-pure": true, "resolve-url-loader>es6-iterator>d>es5-ext": false, "resolve-url-loader>es6-iterator>d>es5-ext>esniff>es5-ext": false, - "level>classic-level": false, - "jest-preview": false + "level>classic-level": false } }, "packageManager": "yarn@4.4.1" diff --git a/privacy-snapshot.json b/privacy-snapshot.json index 589504ea2cc7..41b04a9b5210 100644 --- a/privacy-snapshot.json +++ b/privacy-snapshot.json @@ -48,8 +48,6 @@ "raw.githubusercontent.com", "registry.npmjs.org", "responsive-rpc.test", - "security-alerts.api.cx.metamask.io", - "security-alerts.dev-api.cx.metamask.io", "sentry.io", "snaps.metamask.io", "sourcify.dev", diff --git a/shared/constants/metametrics.ts b/shared/constants/metametrics.ts index 615c562e21ad..8107a1040127 100644 --- a/shared/constants/metametrics.ts +++ b/shared/constants/metametrics.ts @@ -39,7 +39,7 @@ export type MetaMetricsReferrerObject = { * function, but still provides the consumer a way to override these values if * necessary. */ -export type MetaMetricsContext = { +type MetaMetricsContext = { /** * Application metadata. */ @@ -65,10 +65,6 @@ export type MetaMetricsContext = { * The dapp that triggered an interaction (MetaMask only). */ referrer?: MetaMetricsReferrerObject; - /** - * The marketing campaign cookie ID. - */ - marketingCampaignCookieId?: string | null; }; export type MetaMetricsEventPayload = { @@ -83,7 +79,7 @@ export type MetaMetricsEventPayload = { /** * The action ID to deduplicate event requests from the UI. */ - actionId?: string; + actionId?: number; /** * The type of environment this event occurred in. Defaults to the background * process type. @@ -120,14 +116,6 @@ export type MetaMetricsEventPayload = { * The origin of the dapp that triggered this event. */ referrer?: MetaMetricsReferrerObject; - /* - * The unique identifier for the event. - */ - uniqueIdentifier?: string; - /** - * Whether the event is a duplicate of an anonymized event. - */ - isDuplicateAnonymizedEvent?: boolean; }; export type MetaMetricsEventOptions = { @@ -235,25 +223,6 @@ export type MetaMetricsEventFragment = { * to avoid unnecessary lookups and reduce accidental duplication. */ uniqueIdentifier?: string; - /* - * The event id. - */ - id: string; - /* - * The environment type. - */ - environmentType?: string; - /* - * The event name. - */ - event?: string; - - /** - * HACK: "transaction-submitted-" fragment hack - * If this is true and the fragment is found as an abandoned fragment, - * then delete the fragment instead of finalizing it. - */ - canDeleteIfAbandoned?: boolean; }; /** @@ -276,38 +245,11 @@ export type SegmentEventPayload = { /** * Properties to attach to the event. */ - properties: { - params?: Record; - legacy_event?: boolean; - locale: string; - chain_id: string; - environment_type?: string; - revenue?: number; - value?: number; - currency?: string; - category?: string; - }; + properties: object; /** * The context the event occurred in. */ context: MetaMetricsContext; - /** - * The message id - */ - messageId?: string; - - /** - * The timestamp of the event. - */ - timestamp?: string; - /* - * The event name. - */ - name?: string; - /* - * The user trais - */ - traits?: MetaMetricsUserTraits; }; /** @@ -317,18 +259,18 @@ export type MetaMetricsPagePayload = { /** * The name of the page that was viewed. */ - name?: string; + name: string; /** * The variadic parts of the page URL. * * Example: If the route is `/asset/:asset` and the path is `/asset/ETH`, * the `params` property would be `{ asset: 'ETH' }`. */ - params?: Record; + params?: object; /** * The environment type that the page was viewed in. */ - environmentType?: EnvironmentType; + environmentType: EnvironmentType; /** * The details of the page. */ @@ -337,10 +279,6 @@ export type MetaMetricsPagePayload = { * The dapp that triggered the page view. */ referrer?: MetaMetricsReferrerObject; - /** - * The action ID of the page view. - */ - actionId?: string; }; export type MetaMetricsPageOptions = { @@ -377,7 +315,7 @@ export type MetaMetricsUserTraits = { /** * Does the user have the Autodetect NFTs feature enabled? */ - nft_autodetection_enabled?: boolean; + nft_autodetection_enabled?: number; /** * A number representing the number of identities (accounts) added to the * user's wallet. @@ -416,30 +354,10 @@ export type MetaMetricsUserTraits = { * Does the user have token detection enabled? */ token_detection_enabled?: boolean; - /** - * Does the user have a selected currency in the settings - */ - current_currency?: string; - /** - * Does the user have show native token as main balance enabled. - */ - show_native_token_as_main_balance?: boolean; /** * Does the user have native currency enabled? */ use_native_as_primary_currency?: boolean; - /** - * Does the user opt in for metrics - */ - is_metrics_opted_in?: boolean; - /** - * Does the user accepted marketing consent - */ - has_marketing_consent?: boolean; - /** - * The date the extension was installed. - */ - install_date_ext?: string; /** * Whether the security provider feature has been enabled. */ @@ -448,7 +366,7 @@ export type MetaMetricsUserTraits = { /** * The address of the MMI account in question */ - mmi_account_address?: string | null; + mmi_account_address?: string; /** * What is the MMI extension ID */ @@ -458,14 +376,6 @@ export type MetaMetricsUserTraits = { */ mmi_is_custodian?: boolean; ///: END:ONLY_INCLUDE_IF - /** - * Does the user change the token sort order on the asset list - */ - token_sort_preference?: string; - /** - * The number of petname addresses - */ - petname_addresses_count?: number; }; export enum MetaMetricsUserTrait { @@ -691,7 +601,6 @@ export enum MetaMetricsEventName { PetnameModalOpened = 'Petname Modal Opened', PetnameUpdated = 'Petname Updated', PhishingPageDisplayed = 'Phishing Page Displayed', - ProceedAnywayClicked = 'Proceed Anyway Clicked', PortfolioLinkClicked = 'Portfolio Link Clicked', ProviderMethodCalled = 'Provider Method Called', PublicAddressCopied = 'Public Address Copied', diff --git a/shared/constants/network.test.ts b/shared/constants/network.test.ts index 9d9ca72b46cd..20a13ccfb273 100644 --- a/shared/constants/network.test.ts +++ b/shared/constants/network.test.ts @@ -35,7 +35,6 @@ describe('NetworkConstants', () => { 'Polygon Mainnet': CHAIN_IDS.POLYGON, 'zkSync Era Mainnet': CHAIN_IDS.ZKSYNC_ERA, 'Base Mainnet': CHAIN_IDS.BASE, - 'Linea Mainnet': CHAIN_IDS.LINEA_MAINNET, }; FEATURED_RPCS.forEach((rpc) => { diff --git a/shared/constants/network.ts b/shared/constants/network.ts index 64d330b73b2c..e911ce1aabf5 100644 --- a/shared/constants/network.ts +++ b/shared/constants/network.ts @@ -147,7 +147,6 @@ export const CHAIN_IDS = { NUMBERS: '0x290b', SEI: '0x531', APE_TESTNET: '0x8157', - APE_MAINNET: '0x8173', BERACHAIN: '0x138d5', METACHAIN_ONE: '0x1b6e6', ARBITRUM_SEPOLIA: '0x66eee', @@ -208,7 +207,6 @@ export const CHAINLIST_CHAIN_IDS_MAP = { ZORA_MAINNET: '0x76adf1', FILECOIN: '0x13a', NUMBERS: '0x290b', - APE: '0x8173', } as const; // To add a deprecation warning to a network, add it to the array @@ -373,7 +371,6 @@ const CHAINLIST_CURRENCY_SYMBOLS_MAP = { HUOBI_ECO_CHAIN_MAINNET: 'HT', ACALA_NETWORK: 'ACA', IOTEX_MAINNET: 'IOTX', - APE: 'APE', } as const; export const CHAINLIST_CURRENCY_SYMBOLS_MAP_NETWORK_COLLISION = { @@ -419,7 +416,6 @@ export const FUSE_GOLD_MAINNET_IMAGE_URL = './images/fuse-mainnet.jpg'; export const HAQQ_NETWORK_IMAGE_URL = './images/haqq.svg'; export const IOTEX_MAINNET_IMAGE_URL = './images/iotex.svg'; export const IOTEX_TOKEN_IMAGE_URL = './images/iotex-token.svg'; -export const APE_TOKEN_IMAGE_URL = './images/ape-token.svg'; export const KCC_MAINNET_IMAGE_URL = './images/kcc-mainnet.svg'; export const KLAYTN_MAINNET_IMAGE_URL = './images/klaytn.svg'; export const KROMA_MAINNET_IMAGE_URL = './images/kroma.svg'; @@ -453,7 +449,7 @@ export const NUMBERS_MAINNET_IMAGE_URL = './images/numbers-mainnet.svg'; export const NUMBERS_TOKEN_IMAGE_URL = './images/numbers-token.png'; export const SEI_IMAGE_URL = './images/sei.svg'; export const NEAR_IMAGE_URL = './images/near.svg'; -export const APE_IMAGE_URL = './images/ape.svg'; +export const APE_TESTNET_IMAGE_URL = './images/ape.svg'; export const INFURA_PROVIDER_TYPES = [ NETWORK_TYPES.MAINNET, @@ -568,7 +564,6 @@ export const NETWORK_TO_NAME_MAP = { export const CHAIN_ID_TO_CURRENCY_SYMBOL_MAP = { [CHAINLIST_CHAIN_IDS_MAP.AVALANCHE]: CHAINLIST_CURRENCY_SYMBOLS_MAP.AVALANCHE, - [CHAINLIST_CHAIN_IDS_MAP.APE]: CHAINLIST_CURRENCY_SYMBOLS_MAP.APE, [CHAINLIST_CHAIN_IDS_MAP.BSC]: CHAINLIST_CURRENCY_SYMBOLS_MAP.BNB, [CHAINLIST_CHAIN_IDS_MAP.BASE]: CHAINLIST_CURRENCY_SYMBOLS_MAP.BASE, [CHAINLIST_CHAIN_IDS_MAP.ARBITRUM]: CHAINLIST_CURRENCY_SYMBOLS_MAP.ARBITRUM, @@ -787,8 +782,7 @@ export const CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP = { [CHAINLIST_CHAIN_IDS_MAP.ZKATANA]: ZKATANA_MAINNET_IMAGE_URL, [CHAINLIST_CHAIN_IDS_MAP.ZORA_MAINNET]: ZORA_MAINNET_IMAGE_URL, [CHAINLIST_CHAIN_IDS_MAP.FILECOIN]: FILECOIN_MAINNET_IMAGE_URL, - [CHAINLIST_CHAIN_IDS_MAP.APE_TESTNET]: APE_IMAGE_URL, - [CHAINLIST_CHAIN_IDS_MAP.APE_MAINNET]: APE_IMAGE_URL, + [CHAINLIST_CHAIN_IDS_MAP.APE_TESTNET]: APE_TESTNET_IMAGE_URL, [CHAINLIST_CHAIN_IDS_MAP.BASE]: BASE_TOKEN_IMAGE_URL, [CHAINLIST_CHAIN_IDS_MAP.NUMBERS]: NUMBERS_MAINNET_IMAGE_URL, [CHAINLIST_CHAIN_IDS_MAP.SEI]: SEI_IMAGE_URL, @@ -823,7 +817,6 @@ export const CHAIN_ID_TOKEN_IMAGE_MAP = { [CHAIN_IDS.MOONRIVER]: MOONRIVER_TOKEN_IMAGE_URL, [CHAIN_IDS.MOONBEAM]: MOONBEAM_TOKEN_IMAGE_URL, [CHAINLIST_CHAIN_IDS_MAP.IOTEX_MAINNET]: IOTEX_TOKEN_IMAGE_URL, - [CHAINLIST_CHAIN_IDS_MAP.APE_MAINNET]: APE_TOKEN_IMAGE_URL, } as const; export const INFURA_BLOCKED_KEY = 'countryBlocked'; @@ -939,20 +932,6 @@ export const UNSUPPORTED_RPC_METHODS = new Set([ export const IPFS_DEFAULT_GATEWAY_URL = 'dweb.link'; export const FEATURED_RPCS: AddNetworkFields[] = [ - { - chainId: CHAIN_IDS.LINEA_MAINNET, - name: LINEA_MAINNET_DISPLAY_NAME, - nativeCurrency: CURRENCY_SYMBOLS.ETH, - rpcEndpoints: [ - { - url: `https://linea-mainnet.infura.io/v3/${infuraProjectId}`, - type: RpcEndpointType.Custom, - }, - ], - defaultRpcEndpointIndex: 0, - blockExplorerUrls: ['https://lineascan.build/'], - defaultBlockExplorerUrlIndex: 0, - }, { chainId: CHAIN_IDS.ARBITRUM, name: ARBITRUM_DISPLAY_NAME, diff --git a/shared/constants/security-provider.ts b/shared/constants/security-provider.ts index 2864fe0339a9..e6fff53ee28a 100644 --- a/shared/constants/security-provider.ts +++ b/shared/constants/security-provider.ts @@ -32,7 +32,7 @@ export enum BlockaidReason { approvalFarming = 'approval_farming', /** Malicious signature on Blur order */ blurFarming = 'blur_farming', - /** A known malicious site invoked that transaction */ + /** A known malicous site invoked that transaction */ maliciousDomain = 'malicious_domain', /** Malicious signature on a Permit order */ permitFarming = 'permit_farming', @@ -57,8 +57,6 @@ export enum BlockaidReason { errored = 'Error', notApplicable = 'NotApplicable', inProgress = 'validation_in_progress', - checkingChain = 'CheckingChain', - chainNotSupported = 'ChainNotSupported', } export enum BlockaidResultType { @@ -119,17 +117,6 @@ export const LOADING_SECURITY_ALERT_RESPONSE: SecurityAlertResponse = { reason: BlockaidReason.inProgress, }; -export const SECURITY_ALERT_RESPONSE_CHECKING_CHAIN: SecurityAlertResponse = { - result_type: BlockaidResultType.Loading, - reason: BlockaidReason.checkingChain, -}; - -export const SECURITY_ALERT_RESPONSE_CHAIN_NOT_SUPPORTED: SecurityAlertResponse = - { - result_type: BlockaidResultType.Benign, - reason: BlockaidReason.chainNotSupported, - }; - export enum SecurityAlertSource { /** Validation performed remotely using the Security Alerts API. */ API = 'api', diff --git a/test/data/bridge/mock-quotes-erc20-erc20.json b/test/data/bridge/mock-quotes-erc20-erc20.json deleted file mode 100644 index 8b589aa85e1b..000000000000 --- a/test/data/bridge/mock-quotes-erc20-erc20.json +++ /dev/null @@ -1,248 +0,0 @@ -[ - { - "quote": { - "requestId": "90ae8e69-f03a-4cf6-bab7-ed4e3431eb37", - "srcChainId": 10, - "srcAsset": { - "chainId": 10, - "address": "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85", - "symbol": "USDC", - "name": "USD Coin", - "decimals": 6, - "icon": "https://media.socket.tech/tokens/all/USDC", - "logoURI": "https://media.socket.tech/tokens/all/USDC", - "chainAgnosticId": null - }, - "srcTokenAmount": "14000000", - "destChainId": 137, - "destAsset": { - "chainId": 137, - "address": "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359", - "symbol": "USDC", - "name": "Native USD Coin (POS)", - "decimals": 6, - "icon": "https://media.socket.tech/tokens/all/USDC", - "logoURI": "https://media.socket.tech/tokens/all/USDC", - "chainAgnosticId": "USDC" - }, - "destTokenAmount": "13984280", - "feeData": { - "metabridge": { - "amount": "0", - "asset": { - "chainId": 10, - "address": "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85", - "symbol": "USDC", - "name": "USD Coin", - "decimals": 6, - "icon": "https://media.socket.tech/tokens/all/USDC", - "logoURI": "https://media.socket.tech/tokens/all/USDC", - "chainAgnosticId": null - } - } - }, - "bridgeId": "socket", - "bridges": ["across"], - "steps": [ - { - "action": "bridge", - "srcChainId": 10, - "destChainId": 137, - "protocol": { - "name": "across", - "displayName": "Across", - "icon": "https://miro.medium.com/max/800/1*PN_F5yW4VMBgs_xX-fsyzQ.png" - }, - "srcAsset": { - "chainId": 10, - "address": "0x0b2c639c533813f4aa9d7837caf62653d097ff85", - "symbol": "USDC", - "name": "USD Coin", - "decimals": 6, - "icon": "https://assets.polygon.technology/tokenAssets/usdc.svg", - "logoURI": "https://assets.polygon.technology/tokenAssets/usdc.svg", - "chainAgnosticId": null - }, - "destAsset": { - "chainId": 137, - "address": "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359", - "symbol": "USDC", - "name": "Native USD Coin (POS)", - "decimals": 6, - "icon": "https://assets.polygon.technology/tokenAssets/usdc.svg", - "logoURI": "https://assets.polygon.technology/tokenAssets/usdc.svg", - "chainAgnosticId": "USDC" - }, - "srcAmount": "14000000", - "destAmount": "13984280" - } - ], - "refuel": { - "action": "refuel", - "srcChainId": 10, - "destChainId": 137, - "protocol": { - "name": "refuel", - "displayName": "Refuel", - "icon": "" - }, - "srcAsset": { - "chainId": 10, - "address": "0x0000000000000000000000000000000000000000", - "symbol": "ETH", - "name": "Ether", - "decimals": 18 - }, - "destAsset": { - "chainId": 137, - "address": "0x0000000000000000000000000000000000000000", - "symbol": "MATIC", - "name": "Matic", - "decimals": 18 - }, - "srcAmount": "1000000000000000", - "destAmount": "4405865573929566208" - } - }, - "approval": { - "chainId": 10, - "to": "0x0b2c639c533813f4aa9d7837caf62653d097ff85", - "from": "0x141d32a89a1e0a5ef360034a2f60a4b917c18838", - "value": "0x00", - "data": "0x095ea7b3000000000000000000000000b90357f2b86dbfd59c3502215d4060f71df8ca0e0000000000000000000000000000000000000000000000000000000000d59f80", - "gasLimit": 61865 - }, - "trade": { - "chainId": 10, - "to": "0xB90357f2b86dbfD59c3502215d4060f71DF8ca0e", - "from": "0x141d32a89a1e0a5ef360034a2f60a4b917c18838", - "value": "0x038d7ea4c68000", - "data": "0x3ce33bff00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000b2c639c533813f4aa9d7837caf62653d097ff850000000000000000000000000000000000000000000000000000000000d59f8000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000f736f636b6574416461707465725632000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005e00000000000000000000000003a23f943181408eac424116af7b7790c94cb97a50000000000000000000000003a23f943181408eac424116af7b7790c94cb97a500000000000000000000000000000000000000000000000000000000000000890000000000000000000000000b2c639c533813f4aa9d7837caf62653d097ff850000000000000000000000003c499c542cef5e3811e1192ce70d8cc03d5c33590000000000000000000000000000000000000000000000000000000000d59f8000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000716a8b9dd056055c84b7a2ba0a016099465a518700000000000000000000000000000000000000000000000000000000000004a0c3540448000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000019d0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000084ad69fa4f00000000000000000000000000000000000000000000000000038d7ea4c68000000000000000000000000000141d32a89a1e0a5ef360034a2f60a4b917c1883800000000000000000000000000000000000000000000000000000000000000890000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000284792ebcb90000000000000000000000000000000000000000000000000000000000d59f80000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000454000000000000000000000000000000000000000000000000000000000000000c40000000000000000000000000000000000000000000000000000000000000002000000000000000000000000141d32a89a1e0a5ef360034a2f60a4b917c18838000000000000000000000000141d32a89a1e0a5ef360034a2f60a4b917c1883800000000000000000000000000000000000000000000000000000000000000020000000000000000000000000b2c639c533813f4aa9d7837caf62653d097ff850000000000000000000000003c499c542cef5e3811e1192ce70d8cc03d5c335900000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000d55a40000000000000000000000000000000000000000000000000000000000000008900000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000067041c47000000000000000000000000000000000000000000000000000000006704704d00000000000000000000000000000000000000000000000000000000d00dfeeddeadbeef765753be7f7a64d5509974b0d678e1e3149b02f42c7402906f9888136205038026f20b3f6df2899044cab41d632bc7a6c35debd40516df85de6f194aeb05b72cb9ea4d5ce0f7c56c91a79536331112f1a846dc641c", - "gasLimit": 287227 - }, - "estimatedProcessingTimeInSeconds": 60 - }, - { - "quote": { - "requestId": "0b6caac9-456d-47e6-8982-1945ae81ae82", - "srcChainId": 10, - "srcAsset": { - "chainId": 10, - "address": "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85", - "symbol": "USDC", - "name": "USD Coin", - "decimals": 6, - "icon": "https://media.socket.tech/tokens/all/USDC", - "logoURI": "https://media.socket.tech/tokens/all/USDC", - "chainAgnosticId": null - }, - "srcTokenAmount": "14000000", - "destChainId": 137, - "destAsset": { - "chainId": 137, - "address": "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359", - "symbol": "USDC", - "name": "Native USD Coin (POS)", - "decimals": 6, - "icon": "https://media.socket.tech/tokens/all/USDC", - "logoURI": "https://media.socket.tech/tokens/all/USDC", - "chainAgnosticId": "USDC" - }, - "destTokenAmount": "13800000", - "feeData": { - "metabridge": { - "amount": "0", - "asset": { - "chainId": 10, - "address": "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85", - "symbol": "USDC", - "name": "USD Coin", - "decimals": 6, - "icon": "https://media.socket.tech/tokens/all/USDC", - "logoURI": "https://media.socket.tech/tokens/all/USDC", - "chainAgnosticId": null - } - } - }, - "bridgeId": "socket", - "bridges": ["celercircle"], - "steps": [ - { - "action": "bridge", - "srcChainId": 10, - "destChainId": 137, - "protocol": { - "name": "cctp", - "displayName": "Circle CCTP", - "icon": "https://movricons.s3.ap-south-1.amazonaws.com/CCTP.svg" - }, - "srcAsset": { - "chainId": 10, - "address": "0x0b2c639c533813f4aa9d7837caf62653d097ff85", - "symbol": "USDC", - "name": "USD Coin", - "decimals": 6, - "icon": "https://assets.polygon.technology/tokenAssets/usdc.svg", - "logoURI": "https://assets.polygon.technology/tokenAssets/usdc.svg", - "chainAgnosticId": null - }, - "destAsset": { - "chainId": 137, - "address": "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359", - "symbol": "USDC", - "name": "Native USD Coin (POS)", - "decimals": 6, - "icon": "https://assets.polygon.technology/tokenAssets/usdc.svg", - "logoURI": "https://assets.polygon.technology/tokenAssets/usdc.svg", - "chainAgnosticId": "USDC" - }, - "srcAmount": "14000000", - "destAmount": "13800000" - } - ], - "refuel": { - "action": "refuel", - "srcChainId": 10, - "destChainId": 137, - "protocol": { - "name": "refuel", - "displayName": "Refuel", - "icon": "" - }, - "srcAsset": { - "chainId": 10, - "address": "0x0000000000000000000000000000000000000000", - "symbol": "ETH", - "name": "Ether", - "decimals": 18 - }, - "destAsset": { - "chainId": 137, - "address": "0x0000000000000000000000000000000000000000", - "symbol": "MATIC", - "name": "Matic", - "decimals": 18 - }, - "srcAmount": "1000000000000000", - "destAmount": "4405865573929566208" - } - }, - "approval": { - "chainId": 10, - "to": "0x0b2c639c533813f4aa9d7837caf62653d097ff85", - "from": "0x141d32a89a1e0a5ef360034a2f60a4b917c18838", - "value": "0x00", - "data": "0x095ea7b3000000000000000000000000b90357f2b86dbfd59c3502215d4060f71df8ca0e0000000000000000000000000000000000000000000000000000000000d59f80", - "gasLimit": 61865 - }, - "trade": { - "chainId": 10, - "to": "0xB90357f2b86dbfD59c3502215d4060f71DF8ca0e", - "from": "0x141d32a89a1e0a5ef360034a2f60a4b917c18838", - "value": "0x038d7ea4c68000", - "data": "0x3ce33bff00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000b2c639c533813f4aa9d7837caf62653d097ff850000000000000000000000000000000000000000000000000000000000d59f8000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000f736f636b6574416461707465725632000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004400000000000000000000000003a23f943181408eac424116af7b7790c94cb97a50000000000000000000000003a23f943181408eac424116af7b7790c94cb97a500000000000000000000000000000000000000000000000000000000000000890000000000000000000000000b2c639c533813f4aa9d7837caf62653d097ff850000000000000000000000003c499c542cef5e3811e1192ce70d8cc03d5c33590000000000000000000000000000000000000000000000000000000000d59f8000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000716a8b9dd056055c84b7a2ba0a016099465a518700000000000000000000000000000000000000000000000000000000000002e4c3540448000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000018c0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000084ad69fa4f00000000000000000000000000000000000000000000000000038d7ea4c68000000000000000000000000000141d32a89a1e0a5ef360034a2f60a4b917c18838000000000000000000000000000000000000000000000000000000000000008900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e4b7dfe9d00000000000000000000000000000000000000000000000000000000000d59f8000000000000000000000000000000000000000000000000000000000000000c4000000000000000000000000141d32a89a1e0a5ef360034a2f60a4b917c188380000000000000000000000000b2c639c533813f4aa9d7837caf62653d097ff85000000000000000000000000000000000000000000000000000000000000008900000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000030d400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000138bc5930d51a475e4669db259f69e61ca33803675e76540f062a76af8cbaef4672c9926e56d6a8c29a263de3ee8f734ad760461c448f82fdccdd8c2360fffba1b", - "gasLimit": 343079 - }, - "estimatedProcessingTimeInSeconds": 1560 - } -] diff --git a/test/data/bridge/mock-quotes-native-erc20.json b/test/data/bridge/mock-quotes-native-erc20.json deleted file mode 100644 index fb6ecfcc0b73..000000000000 --- a/test/data/bridge/mock-quotes-native-erc20.json +++ /dev/null @@ -1,294 +0,0 @@ -[ - { - "quote": { - "requestId": "381c23bc-e3e4-48fe-bc53-257471e388ad", - "srcChainId": 10, - "srcAsset": { - "chainId": 10, - "address": "0x0000000000000000000000000000000000000000", - "symbol": "ETH", - "name": "Ethereum", - "decimals": 18, - "icon": "https://media.socket.tech/tokens/all/ETH", - "logoURI": "https://media.socket.tech/tokens/all/ETH", - "chainAgnosticId": null - }, - "srcTokenAmount": "9912500000000000", - "destChainId": 137, - "destAsset": { - "chainId": 137, - "address": "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359", - "symbol": "USDC", - "name": "Native USD Coin (POS)", - "decimals": 6, - "icon": "https://media.socket.tech/tokens/all/USDC", - "logoURI": "https://media.socket.tech/tokens/all/USDC", - "chainAgnosticId": "USDC" - }, - "destTokenAmount": "24438902", - "feeData": { - "metabridge": { - "amount": "87500000000000", - "asset": { - "chainId": 10, - "address": "0x0000000000000000000000000000000000000000", - "symbol": "ETH", - "name": "Ethereum", - "decimals": 18, - "icon": "https://media.socket.tech/tokens/all/ETH", - "logoURI": "https://media.socket.tech/tokens/all/ETH", - "chainAgnosticId": null - } - } - }, - "bridgeId": "socket", - "bridges": ["across"], - "steps": [ - { - "action": "swap", - "srcChainId": 10, - "protocol": { - "name": "zerox", - "displayName": "0x", - "icon": "https://media.socket.tech/dexes/0x.svg" - }, - "srcAsset": { - "chainId": 10, - "address": "0x0000000000000000000000000000000000000000", - "symbol": "ETH", - "name": "Ethereum", - "decimals": 18, - "icon": "https://assets.polygon.technology/tokenAssets/eth.svg", - "logoURI": "https://assets.polygon.technology/tokenAssets/eth.svg", - "chainAgnosticId": null - }, - "destAsset": { - "chainId": 10, - "address": "0x0b2c639c533813f4aa9d7837caf62653d097ff85", - "symbol": "USDC", - "name": "USD Coin", - "decimals": 6, - "icon": "https://assets.polygon.technology/tokenAssets/usdc.svg", - "logoURI": "https://assets.polygon.technology/tokenAssets/usdc.svg", - "chainAgnosticId": null - }, - "srcAmount": "9912500000000000", - "destAmount": "24456223" - }, - { - "action": "bridge", - "srcChainId": 10, - "destChainId": 137, - "protocol": { - "name": "across", - "displayName": "Across", - "icon": "https://miro.medium.com/max/800/1*PN_F5yW4VMBgs_xX-fsyzQ.png" - }, - "srcAsset": { - "chainId": 10, - "address": "0x0b2c639c533813f4aa9d7837caf62653d097ff85", - "symbol": "USDC", - "name": "USD Coin", - "decimals": 6, - "icon": "https://assets.polygon.technology/tokenAssets/usdc.svg", - "logoURI": "https://assets.polygon.technology/tokenAssets/usdc.svg", - "chainAgnosticId": null - }, - "destAsset": { - "chainId": 137, - "address": "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359", - "symbol": "USDC", - "name": "Native USD Coin (POS)", - "decimals": 6, - "icon": "https://assets.polygon.technology/tokenAssets/usdc.svg", - "logoURI": "https://assets.polygon.technology/tokenAssets/usdc.svg", - "chainAgnosticId": "USDC" - }, - "srcAmount": "24456223", - "destAmount": "24438902" - } - ], - "refuel": { - "action": "refuel", - "srcChainId": 10, - "destChainId": 137, - "protocol": { - "name": "refuel", - "displayName": "Refuel", - "icon": "" - }, - "srcAsset": { - "chainId": 10, - "address": "0x0000000000000000000000000000000000000000", - "symbol": "ETH", - "name": "Ether", - "decimals": 18 - }, - "destAsset": { - "chainId": 137, - "address": "0x0000000000000000000000000000000000000000", - "symbol": "MATIC", - "name": "Matic", - "decimals": 18 - }, - "srcAmount": "1000000000000000", - "destAmount": "4405865573929566208" - } - }, - "trade": { - "chainId": 10, - "to": "0xB90357f2b86dbfD59c3502215d4060f71DF8ca0e", - "from": "0x141d32a89a1e0a5ef360034a2f60a4b917c18838", - "value": "0x27147114878000", - "data": "0x3ce33bff00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002714711487800000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000f736f636b657441646170746572563200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f600000000000000000000000003a23f943181408eac424116af7b7790c94cb97a50000000000000000000000003a23f943181408eac424116af7b7790c94cb97a5000000000000000000000000000000000000000000000000000000000000008900000000000000000000000000000000000000000000000000000000000000000000000000000000000000003c499c542cef5e3811e1192ce70d8cc03d5c33590000000000000000000000000000000000000000000000000023375dc1560800000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000004f94ae6af800000000000000000000000000716a8b9dd056055c84b7a2ba0a016099465a51870000000000000000000000000000000000000000000000000000000000000e2037c6145a0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000d64123506490000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001960000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000019d0000000000000000000000000000000000000000000000000000000000000ac00000000000000000000000000000000000000000000000000000000000000084ad69fa4f00000000000000000000000000000000000000000000000000038d7ea4c68000000000000000000000000000141d32a89a1e0a5ef360034a2f60a4b917c1883800000000000000000000000000000000000000000000000000000000000000890000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000904ee8f0b86000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee0000000000000000000000000b2c639c533813f4aa9d7837caf62653d097ff850000000000000000000000000000000000000000000000000023375dc156080000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000828415565b0000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee0000000000000000000000000b2c639c533813f4aa9d7837caf62653d097ff850000000000000000000000000000000000000000000000000023375dc15608000000000000000000000000000000000000000000000000000000000001734d0800000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000004e000000000000000000000000000000000000000000000000000000000000005e0000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee0000000000000000000000000000000000000000000000000023375dc15608000000000000000000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000042000000000000000000000000000000000000060000000000000000000000000b2c639c533813f4aa9d7837caf62653d097ff8500000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000032000000000000000000000000000000000000000000000000000000000000002e00000000000000000000000000000000000000000000000000023375dc1560800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000012556e69737761705633000000000000000000000000000000000000000000000000000000000000000023375dc1560800000000000000000000000000000000000000000000000000000000000173dbd3000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000e592427a0aece92de3edee1f18e0157c0586156400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002b42000000000000000000000000000000000000060001f40b2c639c533813f4aa9d7837caf62653d097ff85000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000b2c639c533813f4aa9d7837caf62653d097ff850000000000000000000000000000000000000000000000000000000000008ecb000000000000000000000000ad01c20d5886137e056775af56915de824c8fce5000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000020000000000000000000000004200000000000000000000000000000000000006000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee0000000000000000000000000000000000000000000000000000000000000000869584cd0000000000000000000000001000000000000000000000000000000000000011000000000000000000000000000000000000000021582def464917822ff6092c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000043a900000000000000000000000000000000000000000000000000000000000000c40000000000000000000000000000000000000000000000000000000000000002000000000000000000000000141d32a89a1e0a5ef360034a2f60a4b917c18838000000000000000000000000141d32a89a1e0a5ef360034a2f60a4b917c1883800000000000000000000000000000000000000000000000000000000000000020000000000000000000000000b2c639c533813f4aa9d7837caf62653d097ff850000000000000000000000003c499c542cef5e3811e1192ce70d8cc03d5c33590000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000174e7be000000000000000000000000000000000000000000000000000000000000008900000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000067041c47000000000000000000000000000000000000000000000000000000006704704d00000000000000000000000000000000000000000000000000000000d00dfeeddeadbeef765753be7f7a64d5509974b0d678e1e3149b02f41fec59a4aef7d9ac92ee5eeaf293cb28c2261e7fd322723a97cb83762f7302296636026e52849fdad0f9db6e1640f914660e6b13f5b1a29345344c8c5687abbf1b", - "gasLimit": 610414 - }, - "estimatedProcessingTimeInSeconds": 60 - }, - { - "quote": { - "requestId": "4277a368-40d7-4e82-aa67-74f29dc5f98a", - "srcChainId": 10, - "srcAsset": { - "chainId": 10, - "address": "0x0000000000000000000000000000000000000000", - "symbol": "ETH", - "name": "Ethereum", - "decimals": 18, - "icon": "https://media.socket.tech/tokens/all/ETH", - "logoURI": "https://media.socket.tech/tokens/all/ETH", - "chainAgnosticId": null - }, - "srcTokenAmount": "9912500000000000", - "destChainId": 137, - "destAsset": { - "chainId": 137, - "address": "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359", - "symbol": "USDC", - "name": "Native USD Coin (POS)", - "decimals": 6, - "icon": "https://media.socket.tech/tokens/all/USDC", - "logoURI": "https://media.socket.tech/tokens/all/USDC", - "chainAgnosticId": "USDC" - }, - "destTokenAmount": "24256223", - "feeData": { - "metabridge": { - "amount": "87500000000000", - "asset": { - "chainId": 10, - "address": "0x0000000000000000000000000000000000000000", - "symbol": "ETH", - "name": "Ethereum", - "decimals": 18, - "icon": "https://media.socket.tech/tokens/all/ETH", - "logoURI": "https://media.socket.tech/tokens/all/ETH", - "chainAgnosticId": null - } - } - }, - "bridgeId": "socket", - "bridges": ["celercircle"], - "steps": [ - { - "action": "swap", - "srcChainId": 10, - "protocol": { - "name": "zerox", - "displayName": "0x", - "icon": "https://media.socket.tech/dexes/0x.svg" - }, - "srcAsset": { - "chainId": 10, - "address": "0x0000000000000000000000000000000000000000", - "symbol": "ETH", - "name": "Ethereum", - "decimals": 18, - "icon": "https://assets.polygon.technology/tokenAssets/eth.svg", - "logoURI": "https://assets.polygon.technology/tokenAssets/eth.svg", - "chainAgnosticId": null - }, - "destAsset": { - "chainId": 10, - "address": "0x0b2c639c533813f4aa9d7837caf62653d097ff85", - "symbol": "USDC", - "name": "USD Coin", - "decimals": 6, - "icon": "https://assets.polygon.technology/tokenAssets/usdc.svg", - "logoURI": "https://assets.polygon.technology/tokenAssets/usdc.svg", - "chainAgnosticId": null - }, - "srcAmount": "9912500000000000", - "destAmount": "24456223" - }, - { - "action": "bridge", - "srcChainId": 10, - "destChainId": 137, - "protocol": { - "name": "cctp", - "displayName": "Circle CCTP", - "icon": "https://movricons.s3.ap-south-1.amazonaws.com/CCTP.svg" - }, - "srcAsset": { - "chainId": 10, - "address": "0x0b2c639c533813f4aa9d7837caf62653d097ff85", - "symbol": "USDC", - "name": "USD Coin", - "decimals": 6, - "icon": "https://assets.polygon.technology/tokenAssets/usdc.svg", - "logoURI": "https://assets.polygon.technology/tokenAssets/usdc.svg", - "chainAgnosticId": null - }, - "destAsset": { - "chainId": 137, - "address": "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359", - "symbol": "USDC", - "name": "Native USD Coin (POS)", - "decimals": 6, - "icon": "https://assets.polygon.technology/tokenAssets/usdc.svg", - "logoURI": "https://assets.polygon.technology/tokenAssets/usdc.svg", - "chainAgnosticId": "USDC" - }, - "srcAmount": "24456223", - "destAmount": "24256223" - } - ], - "refuel": { - "action": "refuel", - "srcChainId": 10, - "destChainId": 137, - "protocol": { - "name": "refuel", - "displayName": "Refuel", - "icon": "" - }, - "srcAsset": { - "chainId": 10, - "address": "0x0000000000000000000000000000000000000000", - "symbol": "ETH", - "name": "Ether", - "decimals": 18 - }, - "destAsset": { - "chainId": 137, - "address": "0x0000000000000000000000000000000000000000", - "symbol": "MATIC", - "name": "Matic", - "decimals": 18 - }, - "srcAmount": "1000000000000000", - "destAmount": "4405865573929566208" - } - }, - "trade": { - "chainId": 10, - "to": "0xB90357f2b86dbfD59c3502215d4060f71DF8ca0e", - "from": "0x141d32a89a1e0a5ef360034a2f60a4b917c18838", - "value": "0x27147114878000", - "data": "0x3ce33bff00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002714711487800000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000f736f636b657441646170746572563200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dc00000000000000000000000003a23f943181408eac424116af7b7790c94cb97a50000000000000000000000003a23f943181408eac424116af7b7790c94cb97a5000000000000000000000000000000000000000000000000000000000000008900000000000000000000000000000000000000000000000000000000000000000000000000000000000000003c499c542cef5e3811e1192ce70d8cc03d5c33590000000000000000000000000000000000000000000000000023375dc1560800000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000004f94ae6af800000000000000000000000000716a8b9dd056055c84b7a2ba0a016099465a51870000000000000000000000000000000000000000000000000000000000000c6437c6145a0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000bc4123506490000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001960000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000018c0000000000000000000000000000000000000000000000000000000000000ac00000000000000000000000000000000000000000000000000000000000000084ad69fa4f00000000000000000000000000000000000000000000000000038d7ea4c68000000000000000000000000000141d32a89a1e0a5ef360034a2f60a4b917c1883800000000000000000000000000000000000000000000000000000000000000890000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000904ee8f0b86000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee0000000000000000000000000b2c639c533813f4aa9d7837caf62653d097ff850000000000000000000000000000000000000000000000000023375dc156080000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000828415565b0000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee0000000000000000000000000b2c639c533813f4aa9d7837caf62653d097ff850000000000000000000000000000000000000000000000000023375dc15608000000000000000000000000000000000000000000000000000000000001734d0800000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000004e000000000000000000000000000000000000000000000000000000000000005e0000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee0000000000000000000000000000000000000000000000000023375dc15608000000000000000000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000042000000000000000000000000000000000000060000000000000000000000000b2c639c533813f4aa9d7837caf62653d097ff8500000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000032000000000000000000000000000000000000000000000000000000000000002e00000000000000000000000000000000000000000000000000023375dc1560800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000012556e69737761705633000000000000000000000000000000000000000000000000000000000000000023375dc1560800000000000000000000000000000000000000000000000000000000000173dbd3000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000e592427a0aece92de3edee1f18e0157c0586156400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002b42000000000000000000000000000000000000060001f40b2c639c533813f4aa9d7837caf62653d097ff85000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000b2c639c533813f4aa9d7837caf62653d097ff850000000000000000000000000000000000000000000000000000000000008ecb000000000000000000000000ad01c20d5886137e056775af56915de824c8fce5000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000020000000000000000000000004200000000000000000000000000000000000006000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee0000000000000000000000000000000000000000000000000000000000000000869584cd00000000000000000000000010000000000000000000000000000000000000110000000000000000000000000000000000000000974132b87a5cb75e32f034280000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000b2c639c533813f4aa9d7837caf62653d097ff85000000000000000000000000141d32a89a1e0a5ef360034a2f60a4b917c18838000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000890000000000000000000000000000000000000000000000000000000000030d4000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003f9e43204a24f476db20f2518722627a122d31a1bc7c63fc15412e6a327295a9460b76bea5bb53b1f73fa6a15811055f6bada592d2e9e6c8cf48a855ce6968951c", - "gasLimit": 664389 - }, - "estimatedProcessingTimeInSeconds": 1560 - } -] diff --git a/test/data/confirmations/helper.ts b/test/data/confirmations/helper.ts index b8bd8a634588..6669c043d0ea 100644 --- a/test/data/confirmations/helper.ts +++ b/test/data/confirmations/helper.ts @@ -133,7 +133,6 @@ export const getMockConfirmState = (args: RootState = { metamask: {} }) => ({ ...args.metamask, preferences: { ...mockState.metamask.preferences, - ...(args.metamask?.preferences as Record), redesignedTransactionsEnabled: true, redesignedConfirmationsEnabled: true, isRedesignedConfirmationsDeveloperEnabled: true, diff --git a/test/data/mock-state.json b/test/data/mock-state.json index 2865478912f3..654e915a1305 100644 --- a/test/data/mock-state.json +++ b/test/data/mock-state.json @@ -420,7 +420,6 @@ "address": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc", "id": "cf8dace4-9439-4bd4-b3a8-88c821c8fcb3", "metadata": { - "importTime": 0, "name": "Test Account", "keyring": { "type": "HD Key Tree" @@ -440,7 +439,6 @@ "address": "0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b", "id": "07c2cfec-36c9-46c4-8115-3836d3ac9047", "metadata": { - "importTime": 0, "name": "Test Account 2", "keyring": { "type": "HD Key Tree" @@ -460,7 +458,6 @@ "address": "0xc42edfcc21ed14dda456aa0756c153f7985d8813", "id": "15e69915-2a1a-4019-93b3-916e11fd432f", "metadata": { - "importTime": 0, "name": "Ledger Hardware 2", "keyring": { "type": "Ledger Hardware" @@ -480,7 +477,6 @@ "address": "0xeb9e64b93097bc15f01f13eae97015c57ab64823", "id": "784225f4-d30b-4e77-a900-c8bbce735b88", "metadata": { - "importTime": 0, "name": "Test Account 3", "keyring": { "type": "HD Key Tree" @@ -500,7 +496,6 @@ "address": "0xca8f1F0245530118D0cf14a06b01Daf8f76Cf281", "id": "694225f4-d30b-4e77-a900-c8bbce735b42", "metadata": { - "importTime": 0, "name": "Test Account 4", "keyring": { "type": "Custody test" @@ -520,13 +515,11 @@ "address": "0xb552685e3d2790efd64a175b00d51f02cdafee5d", "id": "c3deeb99-ba0d-4a4e-a0aa-033fc1f79ae3", "metadata": { - "importTime": 0, "name": "Snap Account 1", "keyring": { "type": "Snap Keyring" }, "snap": { - "enabled": true, "id": "snap-id", "name": "snap-name" } diff --git a/test/e2e/constants.ts b/test/e2e/constants.ts index 8bf39d261bcb..c3957cb6fbbf 100644 --- a/test/e2e/constants.ts +++ b/test/e2e/constants.ts @@ -41,7 +41,6 @@ export const DEFAULT_GANACHE_ETH_BALANCE_DEC = '25'; /* Dapp host addresses and URL*/ export const DAPP_HOST_ADDRESS = '127.0.0.1:8080'; -export const DAPP_URL_LOCALHOST = 'http://localhost:8080'; export const DAPP_URL = `http://${DAPP_HOST_ADDRESS}`; export const DAPP_ONE_URL = 'http://127.0.0.1:8081'; diff --git a/test/e2e/fixture-builder.js b/test/e2e/fixture-builder.js index 4d7e1873bff4..d73b959946c2 100644 --- a/test/e2e/fixture-builder.js +++ b/test/e2e/fixture-builder.js @@ -13,7 +13,6 @@ const { CHAIN_IDS } = require('../../shared/constants/network'); const { SMART_CONTRACTS } = require('./seeder/smart-contracts'); const { DAPP_URL, - DAPP_URL_LOCALHOST, DAPP_ONE_URL, DEFAULT_FIXTURE_ACCOUNT, ERC_4337_ACCOUNT, @@ -58,7 +57,6 @@ function onboardingFixture() { }), providerConfig: { id: 'networkConfigurationId' }, }, - NotificationServicesController: {}, PreferencesController: { advancedGasFee: {}, currentLocale: 'en', @@ -75,7 +73,6 @@ function onboardingFixture() { hideZeroBalanceTokens: false, showExtensionInFullSizeView: false, showFiatInTestnets: false, - privacyMode: false, showTestNetworks: false, smartTransactionsOptInStatus: false, showNativeTokenAsMainBalance: true, @@ -141,7 +138,6 @@ function onboardingFixture() { }, }, }, - UserStorageController: {}, TokensController: { allDetectedTokens: {}, allIgnoredTokens: {}, @@ -449,13 +445,12 @@ class FixtureBuilder { withPermissionControllerConnectedToTestDapp({ restrictReturnedAccounts = true, account = '', - useLocalhostHostname = false, } = {}) { const selectedAccount = account || DEFAULT_FIXTURE_ACCOUNT; return this.withPermissionController({ subjects: { - [useLocalhostHostname ? DAPP_URL_LOCALHOST : DAPP_URL]: { - origin: useLocalhostHostname ? DAPP_URL_LOCALHOST : DAPP_URL, + [DAPP_URL]: { + origin: DAPP_URL, permissions: { eth_accounts: { id: 'ZaqPEWxyhNCJYACFw93jE', diff --git a/test/e2e/flask/btc/btc-account-overview.spec.ts b/test/e2e/flask/btc/btc-account-overview.spec.ts index 418c9d736078..f32a48d9c4a8 100644 --- a/test/e2e/flask/btc/btc-account-overview.spec.ts +++ b/test/e2e/flask/btc/btc-account-overview.spec.ts @@ -1,3 +1,4 @@ +import { strict as assert } from 'assert'; import { Suite } from 'mocha'; import { DEFAULT_BTC_BALANCE } from '../../constants'; import { withBtcAccountSnap } from './common-btc'; @@ -45,19 +46,17 @@ describe('BTC Account - Overview', function (this: Suite) { await withBtcAccountSnap( { title: this.test?.fullTitle() }, async (driver) => { - await driver.waitForSelector({ - testId: 'account-value-and-suffix', - text: `${DEFAULT_BTC_BALANCE}`, - }); - await driver.waitForSelector({ - css: '.currency-display-component__suffix', - text: 'BTC', - }); + // Wait for the balance to load up + await driver.delay(2000); - await driver.waitForSelector({ - tag: 'p', - text: `${DEFAULT_BTC_BALANCE} BTC`, - }); + const balanceElement = await driver.findElement( + '.coin-overview__balance', + ); + const balanceText = await balanceElement.getText(); + + const [balance, unit] = balanceText.split('\n'); + assert(Number(balance) === DEFAULT_BTC_BALANCE); + assert(unit === 'BTC'); }, ); }); diff --git a/test/e2e/helpers.js b/test/e2e/helpers.js index 5eaf14b8360b..6d2ccebeb7c7 100644 --- a/test/e2e/helpers.js +++ b/test/e2e/helpers.js @@ -435,7 +435,6 @@ const completeImportSRPOnboardingFlowWordByWord = async ( await driver.clickElement('[data-testid="onboarding-import-wallet"]'); // metrics - await driver.clickElement('[data-testid="metametrics-no-thanks"]'); // import with recovery phrase, word by word @@ -565,40 +564,9 @@ const onboardingPinExtension = async (driver) => { await driver.clickElement('[data-testid="pin-extension-done"]'); }; -/** - * Completes the onboarding flow with optional opt-out settings for wallet creation. - * - * This function navigates through the onboarding process, allowing for opt-out of certain features. - * It waits for the appropriate heading to appear, then proceeds to opt-out of third-party API - * integration for general and assets sections if specified in the optOutOptions. - * - * @param {WebDriver} driver - The Selenium WebDriver instance. - * @param {object} optOutOptions - Optional. An object specifying which features to opt-out of. - * @param {boolean} optOutOptions.basicFunctionality - Optional. Defaults to true. Opt-out of basic functionality. - * @param {boolean} optOutOptions.profileSync - Optional. Defaults to true. Opt-out of profile sync. - * @param {boolean} optOutOptions.assets - Optional. Defaults to true. Opt-out of assets options. - * @param {boolean} optOutOptions.isNewWallet - Optional. Defaults to true. Indicates if this is a new wallet creation. - */ -const onboardingCompleteWalletCreationWithOptOut = async ( - driver, - optOutOptions = {}, -) => { - const defaultOptOutOptions = { - basicFunctionality: true, - profileSync: true, - assets: true, - isNewWallet: true, - }; - - const optOutOptionsToUse = { ...defaultOptOutOptions, ...optOutOptions }; - +const onboardingCompleteWalletCreationWithOptOut = async (driver) => { // wait for h2 to appear - await driver.findElement({ - text: optOutOptionsToUse.isNewWallet - ? 'Congratulations' - : 'Your wallet is ready', - tag: 'h2', - }); + await driver.findElement({ text: 'Congratulations!', tag: 'h2' }); // opt-out from third party API on general section await driver.clickElementAndWaitToDisappear({ @@ -606,46 +574,26 @@ const onboardingCompleteWalletCreationWithOptOut = async ( tag: 'button', }); await driver.clickElement({ text: 'General', tag: 'p' }); + await driver.clickElement( + '[data-testid="basic-functionality-toggle"] .toggle-button', + ); + await driver.clickElement('[id="basic-configuration-checkbox"]'); + await driver.clickElementAndWaitToDisappear({ + tag: 'button', + text: 'Turn off', + }); - if (optOutOptionsToUse.basicFunctionality) { - await driver.clickElement( - '[data-testid="basic-functionality-toggle"] .toggle-button', - ); - await driver.clickElement('[id="basic-configuration-checkbox"]'); - await driver.clickElementAndWaitToDisappear({ - tag: 'button', - text: 'Turn off', - }); - } - - if ( - optOutOptionsToUse.profileSync && - !optOutOptionsToUse.basicFunctionality - ) { - await driver.clickElement( - '[data-testid="profile-sync-toggle"] .toggle-button', - ); - await driver.clickElementAndWaitToDisappear({ - tag: 'button', - text: 'Turn off', - }); - } - + // opt-out from third party API on assets section + await driver.clickElement('[data-testid="category-back-button"]'); + await driver.clickElement({ text: 'Assets', tag: 'p' }); + await Promise.all( + ( + await driver.findClickableElements( + '.toggle-button.toggle-button--on:not([data-testid="basic-functionality-toggle"] .toggle-button)', + ) + ).map((toggle) => toggle.click()), + ); await driver.clickElement('[data-testid="category-back-button"]'); - - if (optOutOptionsToUse.assets) { - // opt-out from third party API on assets section - await driver.clickElement({ text: 'Assets', tag: 'p' }); - await Promise.all( - ( - await driver.findClickableElements( - '.toggle-button.toggle-button--on:not([data-testid="basic-functionality-toggle"] .toggle-button)', - ) - ).map((toggle) => toggle.click()), - ); - - await driver.clickElement('[data-testid="category-back-button"]'); - } // Wait until the onboarding carousel has stopped moving // otherwise the click has no effect. @@ -662,30 +610,15 @@ const onboardingCompleteWalletCreationWithOptOut = async ( await onboardingPinExtension(driver); }; -/** - * Completes the onboarding flow for creating a new wallet with opt-out options. - * - * This function guides the user through the onboarding process of creating a new wallet, - * including opting out of certain features as specified by the `optOutOptions` parameter. - * - * @param {object} driver - The Selenium driver instance. - * @param {string} password - The password to use for the new wallet. - * @param {object} optOutOptions - An object specifying the features to opt out of. - * @param {boolean} optOutOptions.isNewWallet - Indicates if this is a new wallet creation. - * @param {boolean} optOutOptions.basicFunctionality - Indicates if basic functionality should be opted out. - * @param {boolean} optOutOptions.profileSync - Indicates if profile sync should be opted out. - * @param {boolean} optOutOptions.assets - Indicates if assets should be opted out. - */ const completeCreateNewWalletOnboardingFlowWithOptOut = async ( driver, password, - optOutOptions, ) => { await onboardingBeginCreateNewWallet(driver); await onboardingChooseMetametricsOption(driver, false); await onboardingCreatePassword(driver, password); await onboardingRevealAndConfirmSRP(driver); - await onboardingCompleteWalletCreationWithOptOut(driver, optOutOptions); + await onboardingCompleteWalletCreationWithOptOut(driver); }; const completeCreateNewWalletOnboardingFlow = async (driver, password) => { @@ -697,6 +630,45 @@ const completeCreateNewWalletOnboardingFlow = async (driver, password) => { await onboardingPinExtension(driver); }; +const importWrongSRPOnboardingFlow = async (driver, seedPhrase) => { + // agree to terms of use + await driver.clickElement('[data-testid="onboarding-terms-checkbox"]'); + + // welcome + await driver.clickElement('[data-testid="onboarding-import-wallet"]'); + + // metrics + await driver.clickElement('[data-testid="metametrics-no-thanks"]'); + + // import with recovery phrase + await driver.pasteIntoField( + '[data-testid="import-srp__srp-word-0"]', + seedPhrase, + ); + + const warningText = 'Invalid Secret Recovery Phrase'; + const warnings = await driver.findElements('.import-srp__banner-alert-text'); + const warning = warnings[1]; + + assert.equal(await warning.getText(), warningText); +}; + +const selectDropdownByNum = async (elements, index) => { + await elements[index].click(); +}; + +const testSRPDropdownIterations = async (options, driver, iterations) => { + for (let i = 0; i < iterations; i++) { + await selectDropdownByNum(options, i); + await new Promise((resolve) => setTimeout(resolve, 1000)); + + const formFields = await driver.findElements('.import-srp__srp-word-label'); + const expectedNumFields = 12 + i * 3; + const actualNumFields = formFields.length; + assert.equal(actualNumFields, expectedNumFields); + } +}; + const openSRPRevealQuiz = async (driver) => { // navigate settings to reveal SRP await driver.clickElement('[data-testid="account-options-menu-button"]'); @@ -909,8 +881,7 @@ const sendScreenToConfirmScreen = async ( quantity, ) => { await openActionMenuAndStartSendFlow(driver); - await driver.waitForSelector('[data-testid="ens-input"]'); - await driver.pasteIntoField('[data-testid="ens-input"]', recipientAddress); + await driver.fill('[data-testid="ens-input"]', recipientAddress); await driver.fill('.unit-input__input', quantity); // check if element exists and click it @@ -929,8 +900,7 @@ const sendTransaction = async ( isAsyncFlow = false, ) => { await openActionMenuAndStartSendFlow(driver); - await driver.waitForSelector('[data-testid="ens-input"]'); - await driver.pasteIntoField('[data-testid="ens-input"]', recipientAddress); + await driver.fill('[data-testid="ens-input"]', recipientAddress); await driver.fill('.unit-input__input', quantity); await driver.clickElement({ @@ -1311,6 +1281,8 @@ module.exports = { closeSRPReveal, tapAndHoldToRevealSRP, createDownloadFolder, + importWrongSRPOnboardingFlow, + testSRPDropdownIterations, openDapp, openDappConnectionsPage, createDappTransaction, @@ -1341,7 +1313,6 @@ module.exports = { onboardingCreatePassword, onboardingRevealAndConfirmSRP, onboardingCompleteWalletCreation, - onboardingCompleteWalletCreationWithOptOut, onboardingPinExtension, assertInAnyOrder, genRandInitBal, diff --git a/test/e2e/helpers/user-storage/userStorageMockttpController.test.ts b/test/e2e/helpers/user-storage/userStorageMockttpController.test.ts deleted file mode 100644 index 1b6591899c0e..000000000000 --- a/test/e2e/helpers/user-storage/userStorageMockttpController.test.ts +++ /dev/null @@ -1,304 +0,0 @@ -import * as mockttp from 'mockttp'; -import { UserStorageMockttpController } from './userStorageMockttpController'; - -describe('UserStorageMockttpController', () => { - let mockServer: mockttp.Mockttp; - - const baseUrl = 'https://user-storage.api.cx.metamask.io/api/v1/userstorage'; - - describe('mimics user storage behaviour', () => { - mockServer = mockttp.getLocal({ cors: true }); - - it('handles GET requests that have empty response', async () => { - const controller = new UserStorageMockttpController(); - - controller.setupPath('accounts', mockServer); - - const request = await controller.onGet('accounts', { - path: `${baseUrl}/accounts`, - }); - - expect(request.json).toEqual(null); - }); - - it('handles GET requests that have a pre-defined response', async () => { - const controller = new UserStorageMockttpController(); - const mockedData = [ - { - HashedKey: - '7f8a7963423985c50f75f6ad42a6cf4e7eac43a6c55e3c6fcd49d73f01c1471b', - Data: 'data1', - }, - { - HashedKey: - 'c236b92ea7d513b2beda062cb546986961dfa7ca4334a2913f7837e43d050468', - Data: 'data2', - }, - ]; - - controller.setupPath('accounts', mockServer, { - getResponse: mockedData, - }); - - const request = await controller.onGet('accounts', { - path: `${baseUrl}/accounts`, - }); - - expect(request.json).toEqual(mockedData); - }); - - it('handles batch GET requests', async () => { - const controller = new UserStorageMockttpController(); - const mockedData = [ - { - HashedKey: - '7f8a7963423985c50f75f6ad42a6cf4e7eac43a6c55e3c6fcd49d73f01c1471b', - Data: 'data1', - }, - { - HashedKey: - 'c236b92ea7d513b2beda062cb546986961dfa7ca4334a2913f7837e43d050468', - Data: 'data2', - }, - ]; - - controller.setupPath('accounts', mockServer, { - getResponse: mockedData, - }); - - const request = await controller.onGet('accounts', { - path: `${baseUrl}/accounts`, - }); - - expect(request.json).toEqual(mockedData); - }); - - it('handles GET requests for feature entries', async () => { - const controller = new UserStorageMockttpController(); - const mockedData = [ - { - HashedKey: - '7f8a7963423985c50f75f6ad42a6cf4e7eac43a6c55e3c6fcd49d73f01c1471b', - Data: 'data1', - }, - { - HashedKey: - 'c236b92ea7d513b2beda062cb546986961dfa7ca4334a2913f7837e43d050468', - Data: 'data2', - }, - ]; - - controller.setupPath('accounts', mockServer, { - getResponse: mockedData, - }); - - const request = await controller.onGet('accounts', { - path: `${baseUrl}/accounts/7f8a7963423985c50f75f6ad42a6cf4e7eac43a6c55e3c6fcd49d73f01c1471b`, - }); - - expect(request.json).toEqual(mockedData[0]); - }); - - it('handles PUT requests to create new entries', async () => { - const controller = new UserStorageMockttpController(); - const mockedData = [ - { - HashedKey: - '7f8a7963423985c50f75f6ad42a6cf4e7eac43a6c55e3c6fcd49d73f01c1471b', - Data: 'data1', - }, - { - HashedKey: - 'c236b92ea7d513b2beda062cb546986961dfa7ca4334a2913f7837e43d050468', - Data: 'data2', - }, - ]; - const mockedAddedData = { - HashedKey: - '6afbe024087495b4e0d56c4bdfc981c84eba44a7c284d4f455b5db4fcabc2173', - Data: 'data3', - }; - - controller.setupPath('accounts', mockServer, { - getResponse: mockedData, - }); - - const putRequest = await controller.onPut('accounts', { - path: `${baseUrl}/accounts/6afbe024087495b4e0d56c4bdfc981c84eba44a7c284d4f455b5db4fcabc2173`, - body: { - getJson: async () => ({ - data: mockedAddedData.Data, - }), - } as unknown as mockttp.CompletedBody, - }); - - expect(putRequest.statusCode).toEqual(204); - - const getRequest = await controller.onGet('accounts', { - path: `${baseUrl}/accounts`, - }); - - expect(getRequest.json).toEqual([...mockedData, mockedAddedData]); - }); - - it('handles PUT requests to update existing entries', async () => { - const controller = new UserStorageMockttpController(); - const mockedData = [ - { - HashedKey: - '7f8a7963423985c50f75f6ad42a6cf4e7eac43a6c55e3c6fcd49d73f01c1471b', - Data: 'data1', - }, - { - HashedKey: - 'c236b92ea7d513b2beda062cb546986961dfa7ca4334a2913f7837e43d050468', - Data: 'data2', - }, - ]; - const mockedUpdatedData = { - HashedKey: - 'c236b92ea7d513b2beda062cb546986961dfa7ca4334a2913f7837e43d050468', - Data: 'data3', - }; - - controller.setupPath('accounts', mockServer, { - getResponse: mockedData, - }); - - const putRequest = await controller.onPut('accounts', { - path: `${baseUrl}/accounts/c236b92ea7d513b2beda062cb546986961dfa7ca4334a2913f7837e43d050468`, - body: { - getJson: async () => ({ - data: mockedUpdatedData.Data, - }), - } as unknown as mockttp.CompletedBody, - }); - - expect(putRequest.statusCode).toEqual(204); - - const getRequest = await controller.onGet('accounts', { - path: `${baseUrl}/accounts`, - }); - - expect(getRequest.json).toEqual([mockedData[0], mockedUpdatedData]); - }); - - it('handles batch PUT requests', async () => { - const controller = new UserStorageMockttpController(); - const mockedData = [ - { - HashedKey: - '7f8a7963423985c50f75f6ad42a6cf4e7eac43a6c55e3c6fcd49d73f01c1471b', - Data: 'data1', - }, - { - HashedKey: - 'c236b92ea7d513b2beda062cb546986961dfa7ca4334a2913f7837e43d050468', - Data: 'data2', - }, - ]; - const mockedUpdatedData = [ - { - HashedKey: - '7f8a7963423985c50f75f6ad42a6cf4e7eac43a6c55e3c6fcd49d73f01c1471b', - Data: 'data3', - }, - { - HashedKey: - 'c236b92ea7d513b2beda062cb546986961dfa7ca4334a2913f7837e43d050468', - Data: 'data4', - }, - ]; - - controller.setupPath('accounts', mockServer, { - getResponse: mockedData, - }); - - const putData = {} as { [key: string]: string }; - mockedUpdatedData.forEach((entry) => { - putData[entry.HashedKey] = entry.Data; - }); - - const putRequest = await controller.onPut('accounts', { - path: `${baseUrl}/accounts`, - body: { - getJson: async () => ({ - data: putData, - }), - } as unknown as mockttp.CompletedBody, - }); - - expect(putRequest.statusCode).toEqual(204); - - const getRequest = await controller.onGet('accounts', { - path: `${baseUrl}/accounts`, - }); - - expect(getRequest.json).toEqual(mockedUpdatedData); - }); - - it('handles DELETE requests', async () => { - const controller = new UserStorageMockttpController(); - const mockedData = [ - { - HashedKey: - '7f8a7963423985c50f75f6ad42a6cf4e7eac43a6c55e3c6fcd49d73f01c1471b', - Data: 'data1', - }, - { - HashedKey: - 'c236b92ea7d513b2beda062cb546986961dfa7ca4334a2913f7837e43d050468', - Data: 'data2', - }, - ]; - - controller.setupPath('accounts', mockServer, { - getResponse: mockedData, - }); - - const deleteRequest = await controller.onDelete('accounts', { - path: `${baseUrl}/accounts/c236b92ea7d513b2beda062cb546986961dfa7ca4334a2913f7837e43d050468`, - }); - - expect(deleteRequest.statusCode).toEqual(204); - - const getRequest = await controller.onGet('accounts', { - path: `${baseUrl}/accounts`, - }); - - expect(getRequest.json).toEqual([mockedData[0]]); - }); - - it('handles batch DELETE requests', async () => { - const controller = new UserStorageMockttpController(); - const mockedData = [ - { - HashedKey: - '7f8a7963423985c50f75f6ad42a6cf4e7eac43a6c55e3c6fcd49d73f01c1471b', - Data: 'data1', - }, - { - HashedKey: - 'c236b92ea7d513b2beda062cb546986961dfa7ca4334a2913f7837e43d050468', - Data: 'data2', - }, - ]; - - controller.setupPath('accounts', mockServer, { - getResponse: mockedData, - }); - - const deleteRequest = await controller.onDelete('accounts', { - path: `${baseUrl}/accounts`, - }); - - expect(deleteRequest.statusCode).toEqual(204); - - const getRequest = await controller.onGet('accounts', { - path: `${baseUrl}/accounts`, - }); - - expect(getRequest.json).toEqual(null); - }); - }); -}); diff --git a/test/e2e/helpers/user-storage/userStorageMockttpController.ts b/test/e2e/helpers/user-storage/userStorageMockttpController.ts deleted file mode 100644 index 970a10d11120..000000000000 --- a/test/e2e/helpers/user-storage/userStorageMockttpController.ts +++ /dev/null @@ -1,196 +0,0 @@ -import { CompletedRequest, Mockttp } from 'mockttp'; - -// TODO: Export user storage schema from @metamask/profile-sync-controller -export const pathRegexps = { - accounts: - /https:\/\/user-storage\.api\.cx\.metamask\.io\/api\/v1\/userstorage\/accounts/u, - networks: - /https:\/\/user-storage\.api\.cx\.metamask\.io\/api\/v1\/userstorage\/networks/u, - notifications: - /https:\/\/user-storage\.api\.cx\.metamask\.io\/api\/v1\/userstorage\/notifications/u, -}; - -type UserStorageResponseData = { HashedKey: string; Data: string }; - -const determineIfFeatureEntryFromURL = (url: string) => - url.substring(url.lastIndexOf('userstorage') + 12).split('/').length === 2; - -export class UserStorageMockttpController { - paths: Map< - keyof typeof pathRegexps, - { - response: UserStorageResponseData[]; - server: Mockttp; - } - > = new Map(); - - readonly onGet = async ( - path: keyof typeof pathRegexps, - request: Pick, - statusCode: number = 200, - ) => { - const internalPathData = this.paths.get(path); - - if (!internalPathData) { - return { - statusCode, - json: null, - }; - } - - const isFeatureEntry = determineIfFeatureEntryFromURL(request.path); - - if (isFeatureEntry) { - const json = - internalPathData.response?.find( - (entry) => entry.HashedKey === request.path.split('/').pop(), - ) || null; - - return { - statusCode, - json, - }; - } - - const json = internalPathData?.response.length - ? internalPathData.response - : null; - - return { - statusCode, - json, - }; - }; - - readonly onPut = async ( - path: keyof typeof pathRegexps, - request: Pick, - statusCode: number = 204, - ) => { - const isFeatureEntry = determineIfFeatureEntryFromURL(request.path); - - const data = (await request.body.getJson()) as { - data: string | { [key: string]: string }; - }; - - const newOrUpdatedSingleOrBatchEntries = - isFeatureEntry && typeof data?.data === 'string' - ? [ - { - HashedKey: request.path.split('/').pop() as string, - Data: data?.data, - }, - ] - : Object.entries(data?.data).map(([key, value]) => ({ - HashedKey: key, - Data: value, - })); - - newOrUpdatedSingleOrBatchEntries.forEach((entry) => { - const internalPathData = this.paths.get(path); - - if (!internalPathData) { - return; - } - - const doesThisEntryExist = internalPathData.response?.find( - (existingEntry) => existingEntry.HashedKey === entry.HashedKey, - ); - - if (doesThisEntryExist) { - this.paths.set(path, { - ...internalPathData, - response: internalPathData.response.map((existingEntry) => - existingEntry.HashedKey === entry.HashedKey ? entry : existingEntry, - ), - }); - } else { - this.paths.set(path, { - ...internalPathData, - response: [ - ...(internalPathData?.response || []), - entry as { HashedKey: string; Data: string }, - ], - }); - } - }); - - return { - statusCode, - }; - }; - - readonly onDelete = async ( - path: keyof typeof pathRegexps, - request: Pick, - statusCode: number = 204, - ) => { - const internalPathData = this.paths.get(path); - - if (!internalPathData) { - return { - statusCode, - }; - } - - const isFeatureEntry = determineIfFeatureEntryFromURL(request.path); - - if (isFeatureEntry) { - this.paths.set(path, { - ...internalPathData, - response: internalPathData?.response.filter( - (entry) => entry.HashedKey !== request.path.split('/').pop(), - ), - }); - } else { - this.paths.set(path, { - ...internalPathData, - response: [], - }); - } - - return { - statusCode, - }; - }; - - setupPath = ( - path: keyof typeof pathRegexps, - server: Mockttp, - overrides?: { - getResponse?: UserStorageResponseData[]; - getStatusCode?: number; - putStatusCode?: number; - deleteStatusCode?: number; - }, - ) => { - const previouslySetupPath = this.paths.get(path); - - this.paths.set(path, { - response: overrides?.getResponse || previouslySetupPath?.response || [], - server, - }); - - this.paths - .get(path) - ?.server.forGet(pathRegexps[path]) - .always() - .thenCallback((request) => - this.onGet(path, request, overrides?.getStatusCode), - ); - this.paths - .get(path) - ?.server.forPut(pathRegexps[path]) - .always() - .thenCallback((request) => - this.onPut(path, request, overrides?.putStatusCode), - ); - this.paths - .get(path) - ?.server.forDelete(pathRegexps[path]) - .always() - .thenCallback((request) => - this.onDelete(path, request, overrides?.deleteStatusCode), - ); - }; -} diff --git a/test/e2e/mock-cdn/cdn-config-res-headers.json b/test/e2e/mock-cdn/cdn-config-res-headers.json index 02096db176b3..7b0e37a92449 100644 --- a/test/e2e/mock-cdn/cdn-config-res-headers.json +++ b/test/e2e/mock-cdn/cdn-config-res-headers.json @@ -1,4 +1,3 @@ { - "Content-Type": "text/plain", - "Etag": "\"db8ccd7f11424082a7cea67466129aed\"" + "Etag": "bb28e40153ff052671b8ad835d368d89" } diff --git a/test/e2e/mock-cdn/cdn-config.txt b/test/e2e/mock-cdn/cdn-config.txt index edd8280e3a8e..b05273585ff5 100644 Binary files a/test/e2e/mock-cdn/cdn-config.txt and b/test/e2e/mock-cdn/cdn-config.txt differ diff --git a/test/e2e/mock-cdn/cdn-stale-diff-res-headers.json b/test/e2e/mock-cdn/cdn-stale-diff-res-headers.json index 0fb0ec0f7d89..b3c558ff1cdd 100644 --- a/test/e2e/mock-cdn/cdn-stale-diff-res-headers.json +++ b/test/e2e/mock-cdn/cdn-stale-diff-res-headers.json @@ -1,4 +1,3 @@ { - "Content-Type": "text/plain", - "Etag": "W/\"5ae8a43f84ccd89e8ddc79b1dfed0035\"" + "Etag": "W/\"ece7f5f533b8978063633ea5b1f8a0fc\"" } diff --git a/test/e2e/mock-cdn/cdn-stale-diff.txt b/test/e2e/mock-cdn/cdn-stale-diff.txt index 44eb67f85fa4..02e2bae35ce5 100644 Binary files a/test/e2e/mock-cdn/cdn-stale-diff.txt and b/test/e2e/mock-cdn/cdn-stale-diff.txt differ diff --git a/test/e2e/mock-cdn/cdn-stale-res-headers.json b/test/e2e/mock-cdn/cdn-stale-res-headers.json index b9f7bf79559f..bb2df028661c 100644 --- a/test/e2e/mock-cdn/cdn-stale-res-headers.json +++ b/test/e2e/mock-cdn/cdn-stale-res-headers.json @@ -1,4 +1,3 @@ { - "Content-Type": "text/plain", - "Etag": "W/\"ab6bc9d599f83e04ae71f6ea957414f0\"" + "Etag": "W/\"b89ab99b0801b5d64acb27893a2b31ca\"" } diff --git a/test/e2e/mock-cdn/cdn-stale.txt b/test/e2e/mock-cdn/cdn-stale.txt index 42efc2a8ba97..39e3f2b9ea1b 100644 Binary files a/test/e2e/mock-cdn/cdn-stale.txt and b/test/e2e/mock-cdn/cdn-stale.txt differ diff --git a/test/e2e/mock-cdn/ppom-version-headers.json b/test/e2e/mock-cdn/ppom-version-headers.json index ad50d161d1dd..a29a05e8c360 100644 --- a/test/e2e/mock-cdn/ppom-version-headers.json +++ b/test/e2e/mock-cdn/ppom-version-headers.json @@ -1,3 +1,3 @@ { - "Etag": "W/\"7aa74f7c18a5cb2601e4fc6afcadc9cc\"" + "Etag": "W/\"9f5df4118b061a89ac013422f809de72\"" } diff --git a/test/e2e/mock-cdn/ppom-version.json b/test/e2e/mock-cdn/ppom-version.json index b529f71a0f1c..e06e6705218b 100644 --- a/test/e2e/mock-cdn/ppom-version.json +++ b/test/e2e/mock-cdn/ppom-version.json @@ -1,512 +1,302 @@ [ + { + "name": "stale", + "chainId": "0x144", + "version": "0.0.11", + "checksum": "b4731bb258fec747bf9394d4c21096dd27d498e6ada6c1a871d0407f63f9c2d3", + "signature": "49fa6b11db8114a4520343544d829753c0eedd156f15c168dd8e31a8ddc25c10c16d9203bdd5d0872610a805d7e37a26b79bf399d1c2d5037f6ebd02ac6d0306", + "hashSignature": "7a7d72a4214317738b3b91c3245a8ef8ac0f5bac247c369212c79db763ef78b1a5001f892edf41c2b619f28c326541ecd76e9bbe7db444a6d2898ab408832507", + "filePath": "stale/0x144/0.0.11", + "timestamp": "2024-03-06T11:05:22.120889" + }, + { + "name": "stale_diff", + "chainId": "0x144", + "version": "0.0.77", + "checksum": "69b726f5ae8567cd566c6bf30dea3692ffb159d1d61dcdf081298c7023635be9", + "signature": "5a5110480d0d63e35900a1e5e09d253f644a1b481c3461e0ffc8dc614dca67838cc7051e304e23ad8cf9e2b74b9e129724253da4f1239140d8474d59400b7502", + "hashSignature": "846d71a01cc094b1940cdedba0cf1e5b2d324eaca41a55d9825f759f67572f43fcc2d29bd894f66d906eebae0fe49922db5d02948748880e9ecb7a884a17b901", + "filePath": "stale_diff/0x144/0.0.77", + "timestamp": "2024-03-10T11:34:07.732508" + }, + { + "name": "config", + "chainId": "0x144", + "version": "0.0.77", + "checksum": "3e1772693c4e2fa91ae00c4b79d546f36e97525daa60baab372c145e979e19f4", + "signature": "f56c9387e07892aceb1018db6dc7e32cce0528c131b6ac1822e12e87dd43d40fdb9f3e17a9c6d7fd25a095e000db19acaf3f93c42142c48f35b06f07998f0f0e", + "hashSignature": "ae3fbbe3f87e48e537e9c1c9601fa5706ed72145250e5f4a2d7a4902d59e2c770c7c1ef88bde15505fa6bcbf24f85ef1b5ad48b76447a3479f7f7a462f082302", + "filePath": "config/0x144/0.0.77", + "timestamp": "2024-03-10T11:34:07.732733" + }, { "name": "stale", "chainId": "0x38", - "version": "0.0.66", - "checksum": "939cfdb932942bc70f9073fd28bbfc40d1d3d5852d83818995e9e22043c2f43f", - "signature": "8ff3884b8fd03330fc507bb71fdf979d3cdce74dc99b75e23a32e7c3df0a8ca938f6f32193690d96f97801d1aad220fcf7a2c9d4ff4e3244c4fb004cea183a02", - "hashSignature": "0076f738508122fce625dfbf5adb5f5068de6484a8bec018d0b6ce9f5818b528a42ef34905e91b30e7864384b3199058a85ef26f0a6a753127eeddad07293f0b", - "filePath": "stale/0x38/0.0.66", - "timestamp": "2024-10-27T12:35:14.835435" + "version": "0.0.38", + "checksum": "99776fbf3527ad357f0772a48585750aa9dfe3ff006a4eb8788ca5cde3dfa6e9", + "signature": "e592080cca99f769f602d94b774fe7905c9e53e9be3f2bc172c0bfd9d0a641083fb735add7cfe5734d3252e217ca63a0b4fbcde6b4376c0ee6a21ce9d07ee700", + "hashSignature": "def291f8e687284afec51cfe8e4ebd9f260078f4ac0804742c31f7444a6d7ba055ab485d227707826761d8c97cba1f6bc08cb5e8fb7e9fb5fdcf1aa8190cc901", + "filePath": "stale/0x38/0.0.38", + "timestamp": "2024-03-20T21:00:30.533909" }, { "name": "stale", "chainId": "0x1", - "version": "0.0.80", - "checksum": "f1750994bf1daa849d8e65b4ebfb3aca09aee65dc96343ddf96ccf9036392a10", - "signature": "1f72981666b660d281b9a92810da45ddef5828240084309fdb8dcc47ce5cdcb38a4369b18fa55331c3dddf39a47bacd9595bb868eca6fc795f11536b8adb1b08", - "hashSignature": "37fff50fd8eaf56d6a7bdc755a8dd13fc654dc9271214cdf419276ecf06b457416f17b8e76640598a3e7a3345eba3df990ddbbfbd974937912901063f22be106", - "filePath": "stale/0x1/0.0.80", - "timestamp": "2024-10-27T12:35:35.786460" + "version": "0.0.52", + "checksum": "20c1449b660f32d7c8e03b0170c79878879cd9ba40fd84374bb7d86cd82a8e6b", + "signature": "9d5f4e433156ddaec3d34361b3bef4c0147c464e6810c6d5c45a0b179227f97d8ed03bc847330eb7d529cc4b5320a8a3602522820d2b44ec2928c227ed7cb700", + "hashSignature": "fd98b80b18a774c64f8bc3a48cbbd1fdd948af320dd14e229c1c2587c5ca6199cb7782989f3edc783e1534b8db2bd758b83898c12899aaf43cf319f112a07e05", + "filePath": "stale/0x1/0.0.52", + "timestamp": "2024-03-20T21:00:43.282213" }, { "name": "stale", "chainId": "0x89", - "version": "0.0.66", - "checksum": "ec799a6a914e3c471edaa0d2d7a6676c32c8cd2bdb00c9ef23f6075595a39e8f", - "signature": "3718817a7eee0c190888e1046346ad865497f0785c8ff0f93fee32bb1a67a0d93890ebcf08cc2930237095b1296ff2a885d8ad6e25ac3b0803d8a879db19d10f", - "hashSignature": "00b5b427d6618c2c7b8a603e95c126715489119611266ba7c33f9cc6d3a15b86617bf11c629534d49500329eda6bc3576c688163ea838cdc48caa41921d7610b", - "filePath": "stale/0x89/0.0.66", - "timestamp": "2024-10-27T12:35:55.378375" + "version": "0.0.38", + "checksum": "b484bd673cd5eec0724fb22c36ba39c9ccc9721401be07fb273f27ec020bfb4a", + "signature": "7392aeb07bba7034fffe308e316f424c48bf868051b655114b63cb36038d4495d190c8daadf33b815bee9bced838aadcf2eb49cbf177d6ab38ae97b6475f7f03", + "hashSignature": "48b8e01132ffdbdd01439dd0e1d8c443bb4c2b88c29d1b9132bb107b0890d246ea73fd55ac2be3bdd0b4922400f5930d3335aafadd2141b6049f1caa1ec59d00", + "filePath": "stale/0x89/0.0.38", + "timestamp": "2024-03-20T21:00:56.806406" }, { "name": "stale", "chainId": "0xa", - "version": "0.0.66", - "checksum": "5ca4a3f1fdd546e8c5ab7553db92892bccc8e143956a922e3080455b7368ed74", - "signature": "9fd80cfb4103e55f9848ecb751596a81faba3528b3ca881f379c1919564ea8ba7ca196025dd068b8ea1aa431d61dad79a2f5edd61f8f9edd2da3fa21b4e7a902", - "hashSignature": "b5aa4508c58cbee23e8d44ca8024e4a72329de93807135c937cf8ce85ab1e8d49a8a0a6cfffaf3734a3a79ea9a57fa9448fab79987d41d58a315aeab5b7f0404", - "filePath": "stale/0xa/0.0.66", - "timestamp": "2024-10-27T12:36:13.196098" + "version": "0.0.38", + "checksum": "7408b4f44e86e19025549c3e165f7d528f856ed3473a6efddf3d2577251d3544", + "signature": "fd0e9a82564802155a6bc13f34363dddc99ca2a3468e3f0e7b00360ee5008f6f2a30dd47771b69352fa1c4323deae756c46fc03508dc39ccccda3fb8678d7f09", + "hashSignature": "9aab8ca37a8cf0797d55c0b655e280e527751a9739766e8d2edd6c45b18dabe09f0ee66518f59a4112b45e74d5c047af7b39380a0e3f700a41d1680f24b6ad06", + "filePath": "stale/0xa/0.0.38", + "timestamp": "2024-03-20T21:01:06.639827" }, { "name": "stale", "chainId": "0xa4b1", - "version": "0.0.66", - "checksum": "878a437412a4399852866cf6f6237e1438b29d3e199ee07365541bfe0e292474", - "signature": "88a9aa564a2bc74929767aca6e3f9c118beb95790d8abb909f6bdb14a1ef83030adca0a030be5cc200fca01ea48f717c5d128deb552320a8fd7c6a1063d55c0c", - "hashSignature": "9e66daf2ecb7d0f31eeba688fe36b3a31e2076f956e9a8da30d619063905364fc61901111c70b3adc01234ecd60873edacd03e49c0f553b14ddc90686c6f350a", - "filePath": "stale/0xa4b1/0.0.66", - "timestamp": "2024-10-27T12:36:31.787509" + "version": "0.0.38", + "checksum": "642573df1c81669619be1bda27c1659bb730a201824f0470495d38c08acabd70", + "signature": "4d8b6c51d8f9205ce173cde3dab150ad6653f48dc2ca59c3f1beb5e574430404f8b9c03f701dc9160a56f93a35560cd57b49edef6e3f65ea71ea6bfbf21c2b0b", + "hashSignature": "4e07a1c1b15353e2a5f2c7bd35525f47cd9405f740a89a2faa5ea7096edc7278a272aed383341eaee413673a45cd8d6e042fd784493cafee2699fe01229a0b04", + "filePath": "stale/0xa4b1/0.0.38", + "timestamp": "2024-03-20T21:01:16.670454" }, { "name": "stale", "chainId": "0xa86a", - "version": "0.0.66", - "checksum": "936bb4a00c2d272fe165193dbfce43ff93a08a833607caa941079938164441c0", - "signature": "45080ba868da6561104baaf6fb6fe6b1f33c8d5c616cbb469cd09eec03bb77b55705593458d24c4dadfb08f3c4d25ba91884ded6cd2964c4f3aa97620e8f9401", - "hashSignature": "a956eb9ce828f4a3b4b3ba9bf758650a26a19f583dec81eaf69324491e4ed506f71358aced8c60f6d00fe0cb7ff4ae85395ab234cd763556b43fe66da4cd8409", - "filePath": "stale/0xa86a/0.0.66", - "timestamp": "2024-10-27T12:36:50.561386" + "version": "0.0.38", + "checksum": "94982df19575b44a03f3f620bb75fb673b35535e6fede1e542dfbc2166984e5c", + "signature": "d59c6d65721e7003b83870af71152559c5170fff2c81b073faf3618c344063079d2551d5d2dcd310af58a840120aa6dc1e8eba2d83c7a6eb1fd61e58999b900f", + "hashSignature": "22c6d339c1957909b68922c31c015852175e494b6db191b2d1e52612c492ec22d25dfe111eb8cd99131ae245b36aa9f9dfa989cc4d437940893c0c8d2157580a", + "filePath": "stale/0xa86a/0.0.38", + "timestamp": "2024-03-20T21:01:28.762015" }, { "name": "stale", "chainId": "0xe708", - "version": "0.0.56", - "checksum": "5c04ebb4f2b6866f4bd8975bcd9b47a9ebeff17905ef0dfc3f09dcf6d91e7013", - "signature": "84a5528810b64f7e5eea57e0f831e07d2e72c3b2f2f1b694275660669bbe551f4dfe040d5de5a2f0434c4950c4cb76147c727f1bcbd299d5e687adc111c1a80d", - "hashSignature": "944cd65df550f83e9eb227d3b98c0d28cc0b415afba755106f3da85c72cde4acdfdd0d37e72b9bc261791d270cf66dd6fa5e6b463bcbf28239ff032d0edd1105", - "filePath": "stale/0xe708/0.0.56", - "timestamp": "2024-10-27T12:37:32.069927" + "version": "0.0.28", + "checksum": "a05a57016325ea3fd2663ec61e2d2a28fff07c7cc1fd653fb0d38867b4a40b6c", + "signature": "079268869c98b0552129a9aaadb08dd4ff2cc828344365eab8bdb579f6f204cc51515d4eacc034f18fab2df64c82f7d84bec668e80a10e5b4e389eabbf8b3e03", + "hashSignature": "175b783790515ccd4446cd17c44c4877fd48a54b51d0da44fc7f861eedad275f87c425f6dcf9a1e6061c0d56eafe118e6332ce3dedf9ed4ae6951a016a392600", + "filePath": "stale/0xe708/0.0.28", + "timestamp": "2024-03-20T21:01:38.871229" }, { "name": "stale", "chainId": "0x2105", - "version": "0.0.44", - "checksum": "254ba4141c822fda5e1fad71eb16b8280af420ed3fea70c9865d619014e2e036", - "signature": "c291aa42392ca17df1c26c301631d8be2e0d69ce3e63cf131153007b4a4f3d59b8629458b36ac1a73a5a9f129b0a1edf2861eba97c1415c5a7cb2eea5e847b03", - "hashSignature": "9eea3aa55dbbfeeadc35449781f19f3e7f52e97015d7767951c96be629a3a6f03487a87ba1993fc724f414e40bca6a4fa4f19a838328d87004214dc48f246301", - "filePath": "stale/0x2105/0.0.44", - "timestamp": "2024-10-27T12:37:11.181669" + "version": "0.0.16", + "checksum": "d92e7360c8504e6be5b124ba6f53030b286d64ccb050252589324ea49060ef60", + "signature": "85c7c0ad4a293e64738c722e3fe52d7d59c35c7d6cb306c54797767664aa7e47fbc9f52b4dfdf25495fe4e22acf399feacabbc8a2b9dd4eb0a0e8855ee9af607", + "hashSignature": "c93d06cea4f28a602c7871e0b946b528835900aac603187c877153dbc31aceb7fe6cdb17e03558718e62b7a001cc71aef4508098a808bc83b889e91d0fda0501", + "filePath": "stale/0x2105/0.0.16", + "timestamp": "2024-03-20T21:01:47.120043" }, { "name": "stale", "chainId": "0xaa36a7", - "version": "0.0.33", - "checksum": "f01b489470ec41ece8b65ee23e2b092e77046eb9f21a54a9c39ef2399ecc721f", - "signature": "cf94226430160273f94699041b56e9edfc603023ba4b653cf9c555a9f21065078774e1d554eae602d4a5e7bba02e0a76047a42e9a0a89acb1c2069a28c9f6f02", - "hashSignature": "939dd7022011d78cfd935ce88ca041660df72480ce8d9ec84a66066e54e030eb4abb2beadaad8ead4db192c1d0558ac1faa6703b8dda2101481accaf5f6f3005", - "filePath": "stale/0xaa36a7/0.0.33", - "timestamp": "2024-10-27T12:37:51.369806" - }, - { - "name": "stale", - "chainId": "0xcc", - "version": "0.0.26", - "checksum": "f01b489470ec41ece8b65ee23e2b092e77046eb9f21a54a9c39ef2399ecc721f", - "signature": "cf94226430160273f94699041b56e9edfc603023ba4b653cf9c555a9f21065078774e1d554eae602d4a5e7bba02e0a76047a42e9a0a89acb1c2069a28c9f6f02", - "hashSignature": "939dd7022011d78cfd935ce88ca041660df72480ce8d9ec84a66066e54e030eb4abb2beadaad8ead4db192c1d0558ac1faa6703b8dda2101481accaf5f6f3005", - "filePath": "stale/0xcc/0.0.26", - "timestamp": "2024-10-27T12:38:12.484316" - }, - { - "name": "stale", - "chainId": "0x0", - "version": "0.0.20", - "checksum": "f01b489470ec41ece8b65ee23e2b092e77046eb9f21a54a9c39ef2399ecc721f", - "signature": "cf94226430160273f94699041b56e9edfc603023ba4b653cf9c555a9f21065078774e1d554eae602d4a5e7bba02e0a76047a42e9a0a89acb1c2069a28c9f6f02", - "hashSignature": "939dd7022011d78cfd935ce88ca041660df72480ce8d9ec84a66066e54e030eb4abb2beadaad8ead4db192c1d0558ac1faa6703b8dda2101481accaf5f6f3005", - "filePath": "stale/0x0/0.0.20", - "timestamp": "2024-10-27T12:38:30.230565" - }, - { - "name": "stale", - "chainId": "0x144", - "version": "0.0.33", - "checksum": "f01b489470ec41ece8b65ee23e2b092e77046eb9f21a54a9c39ef2399ecc721f", - "signature": "cf94226430160273f94699041b56e9edfc603023ba4b653cf9c555a9f21065078774e1d554eae602d4a5e7bba02e0a76047a42e9a0a89acb1c2069a28c9f6f02", - "hashSignature": "939dd7022011d78cfd935ce88ca041660df72480ce8d9ec84a66066e54e030eb4abb2beadaad8ead4db192c1d0558ac1faa6703b8dda2101481accaf5f6f3005", - "filePath": "stale/0x144/0.0.33", - "timestamp": "2024-10-27T12:38:50.380511" - }, - { - "name": "stale", - "chainId": "0x82750", - "version": "0.0.21", - "checksum": "f01b489470ec41ece8b65ee23e2b092e77046eb9f21a54a9c39ef2399ecc721f", - "signature": "cf94226430160273f94699041b56e9edfc603023ba4b653cf9c555a9f21065078774e1d554eae602d4a5e7bba02e0a76047a42e9a0a89acb1c2069a28c9f6f02", - "hashSignature": "939dd7022011d78cfd935ce88ca041660df72480ce8d9ec84a66066e54e030eb4abb2beadaad8ead4db192c1d0558ac1faa6703b8dda2101481accaf5f6f3005", - "filePath": "stale/0x82750/0.0.21", - "timestamp": "2024-10-27T12:39:09.335209" - }, - { - "name": "stale", - "chainId": "0x1b58", - "version": "0.0.21", - "checksum": "f01b489470ec41ece8b65ee23e2b092e77046eb9f21a54a9c39ef2399ecc721f", - "signature": "cf94226430160273f94699041b56e9edfc603023ba4b653cf9c555a9f21065078774e1d554eae602d4a5e7bba02e0a76047a42e9a0a89acb1c2069a28c9f6f02", - "hashSignature": "939dd7022011d78cfd935ce88ca041660df72480ce8d9ec84a66066e54e030eb4abb2beadaad8ead4db192c1d0558ac1faa6703b8dda2101481accaf5f6f3005", - "filePath": "stale/0x1b58/0.0.21", - "timestamp": "2024-10-27T12:39:26.466030" - }, - { - "name": "stale", - "chainId": "0x138d5", - "version": "0.0.21", - "checksum": "f01b489470ec41ece8b65ee23e2b092e77046eb9f21a54a9c39ef2399ecc721f", - "signature": "cf94226430160273f94699041b56e9edfc603023ba4b653cf9c555a9f21065078774e1d554eae602d4a5e7bba02e0a76047a42e9a0a89acb1c2069a28c9f6f02", - "hashSignature": "939dd7022011d78cfd935ce88ca041660df72480ce8d9ec84a66066e54e030eb4abb2beadaad8ead4db192c1d0558ac1faa6703b8dda2101481accaf5f6f3005", - "filePath": "stale/0x138d5/0.0.21", - "timestamp": "2024-10-27T12:39:42.956981" + "version": "0.0.5", + "checksum": "fa8f9b03fb688da8dc98c0e38f49f05ca1a644609742d7e2b37373d4fa56b961", + "signature": "a9473d0b8659be8332f7b2e04c530bdef5f52a24c5aeb8cdbbe8ed83daa50e97878cebd4db0280b715d8c9a4c23390e30edf2bda990a699b52dbb3514ac2e805", + "hashSignature": "a8ef8f5ccff133430cf2a660c6a9522c674cc62aade0326d71063b5d43480d05c31780cbc027e2eda281e29cf0f3b94188c9584e5e92ba21d91b9ae27056040d", + "filePath": "stale/0xaa36a7/0.0.5", + "timestamp": "2024-03-20T21:01:55.297706" }, { "name": "stale_diff", "chainId": "0x38", - "version": "0.0.482", - "checksum": "3bab009ec87420bb6de56069cb3b76614c891688beb233bfe408aa60757478fb", - "signature": "481f19700f7399df4eefc8162e0229ea2a81fbfcbd6c327fac4ad79aaa7ac3aa8215b36bbdc74986c008cd60ffec428aa7ea04cd166faad938e1f9f4cef4b804", - "hashSignature": "f4991d29cefb09343ed5b3332416e2bab7428499373ea4bb0f6dd9bc1955eed33f80f383f70350a74ccddbea07e52eb4a144559626214a6c0f8c726b28be2f03", - "filePath": "stale_diff/0x38/0.0.482", - "timestamp": "2024-10-27T12:49:57.410484" + "version": "0.0.217", + "checksum": "5a73bfc69701257e3e56a8d4b47d0f17888aaa5a615ce383ad5119d6766d9133", + "signature": "59f489f4680ce4782f68e3c00a011346a366d6bd1b2e5d3de5147680317fe40d05160ffd4976b021ad89c20bc3ef4b4212a0ce70d3859dd281bdeded42204a05", + "hashSignature": "85a82f9d2adf7dd9c9a186ab936c84a71d79818f4612d8410c04670396d62118b313f9914da17bc8299c8617dcd301a38e2dafe086943f1d5eca4622a466e50c", + "filePath": "stale_diff/0x38/0.0.217", + "timestamp": "2024-03-21T12:13:14.798558" }, { "name": "config", "chainId": "0x38", - "version": "0.0.482", - "checksum": "e29783f8be2392ee395055382aa7b19a78564f99915bb749492577712c18c6f0", - "signature": "0e4e28c1e10d8e0243fd6009787dc5742a76cc5460c2565459094aa112fe2cbddb62001b8d5771ed75e836d842f87baf2a8bdc099003797ebc6c7a80079f4701", - "hashSignature": "ac2ba4a855e4e271f1ce7f4f6b67ac1b10a56378ab3f31a638aff4d5f5ccccc9385d976eec2fb304427810995ed22bd755adac62d15d4fcf6fd4934ee3883d00", - "filePath": "config/0x38/0.0.482", - "timestamp": "2024-10-27T12:49:57.410737" + "version": "0.0.217", + "checksum": "3e1772693c4e2fa91ae00c4b79d546f36e97525daa60baab372c145e979e19f4", + "signature": "f56c9387e07892aceb1018db6dc7e32cce0528c131b6ac1822e12e87dd43d40fdb9f3e17a9c6d7fd25a095e000db19acaf3f93c42142c48f35b06f07998f0f0e", + "hashSignature": "ae3fbbe3f87e48e537e9c1c9601fa5706ed72145250e5f4a2d7a4902d59e2c770c7c1ef88bde15505fa6bcbf24f85ef1b5ad48b76447a3479f7f7a462f082302", + "filePath": "config/0x38/0.0.217", + "timestamp": "2024-03-21T12:13:14.798803" }, { "name": "stale_diff", "chainId": "0x1", - "version": "0.0.525", - "checksum": "ae3059765d220e8cda72aa43638916b9baac84f264a39a1d147a5b144378df62", - "signature": "3ed03cfa8bee704816a9ceb270fda86d7b007f0fe510d6acc40f96b15819c114fbd768d8845d75ab803c981eb151b4b0a24af705a27e1f96381bdc6dc5e3b50f", - "hashSignature": "9394bc16a948ab41ee74c0270a900092cbb8707fe72d3576fd75f0b87c698089c0a10b45a20ea47691a90cee427e605f81838b87424291902a9b54fec19e0709", - "filePath": "stale_diff/0x1/0.0.525", - "timestamp": "2024-10-27T12:50:03.871663" + "version": "0.0.260", + "checksum": "fdfde4e2d19c7cbfc307924ee49b277888bfe644dadcf2dc646e4828739adda0", + "signature": "e54483a98adf96e5f16134400a2bb7dc23693de2509a878c50e6fd0ff531f1c13e2f8fdeb58e63f5e47c2497ae5dd8c904c7456456e05bad37758a40d356710f", + "hashSignature": "7f047374e55203d2e0e52257fa9b8fa2c455e27a01f131956063156335476bd0c925a8f60506820861b4a18335835bdddf1be99486aa45df331514d72b22aa0c", + "filePath": "stale_diff/0x1/0.0.260", + "timestamp": "2024-03-21T12:13:18.827919" }, { "name": "config", "chainId": "0x1", - "version": "0.0.525", - "checksum": "abe69e1c8f6084d26b1d556bb3ae4919628ac4bf3b468bea95e3c14254bdf290", - "signature": "52ffaf9e1a543f8164ea93558467f7f4e02c15650daf92f1a1e374848c53b91dcca96037fd6d7bd63b13e7fcf88a1bcc9fe7c7915d8d6949bd153e6bf6b1a403", - "hashSignature": "83c1edb28635049e4c99d8610782506818ef071de95df49f9242f229991924b4ea829782b0ac125de3f763fc7415baaebf3732a920fb4d01861e1fdd5cb86207", - "filePath": "config/0x1/0.0.525", - "timestamp": "2024-10-27T12:50:03.871914" + "version": "0.0.260", + "checksum": "29771bc6544e0d66898eb727ed1b4db478b33e8e45be560de84880c2433ebca2", + "signature": "1a501372b5bd9ac95accd6bf8caeec08425f3e826f100e3ca9df1dff8a861d713207e387676ed64df920ac4682888da76bde534157d71ec270e28e66b033290e", + "hashSignature": "a1cb93bea92cfbe79dd2d9023e0f7b748f0f370c97f2eabdb00a215b39dcac7a32614aa2729dcefe2a7c57a6bce78c934187a3ea443944b13b4da2fa7ee5ac0a", + "filePath": "config/0x1/0.0.260", + "timestamp": "2024-03-21T12:13:18.828225" }, { "name": "stale_diff", "chainId": "0x89", - "version": "0.0.481", - "checksum": "3bab009ec87420bb6de56069cb3b76614c891688beb233bfe408aa60757478fb", - "signature": "481f19700f7399df4eefc8162e0229ea2a81fbfcbd6c327fac4ad79aaa7ac3aa8215b36bbdc74986c008cd60ffec428aa7ea04cd166faad938e1f9f4cef4b804", - "hashSignature": "f4991d29cefb09343ed5b3332416e2bab7428499373ea4bb0f6dd9bc1955eed33f80f383f70350a74ccddbea07e52eb4a144559626214a6c0f8c726b28be2f03", - "filePath": "stale_diff/0x89/0.0.481", - "timestamp": "2024-10-27T12:50:09.561729" + "version": "0.0.216", + "checksum": "d38399d82b0615fbada643872c7dfc7debf183cc1634d643ce608f8c8ffc5d20", + "signature": "89506ef81561309831f4a27cac0d330c8d14607fd646906391c73fccecb4b399931b81e29c2358747414fccee9cb774d936c8d32d8b55f3f3f0adca750f8e805", + "hashSignature": "72cdb5e05f4b1da1ed80140457e7d130beaf8eb6be864847e01befecc6bd619db5ff924578d4b127f82548766ee4b7ac49256026b2012f923fceb0bef0e6300e", + "filePath": "stale_diff/0x89/0.0.216", + "timestamp": "2024-03-21T12:13:22.608506" }, { "name": "config", "chainId": "0x89", - "version": "0.0.481", - "checksum": "eb3b41ae8c3bbf9595dcded8e1b9090c8ed2427e236375652f6fd701c6e134b4", - "signature": "79552c7fa525e05c9c086fe8d8ecb49375be796176877e17332fa1137d4c653f224873bdac2b6fd4fe63fcfe6d404778684116e98fdb0563f63ae1efcfafe60d", - "hashSignature": "9c8a84e430578290eceea60ba7dec3695e0cfd343f7ac3e2667c8634afa3dd65d6ec25b112ac8260dbe3e6bc9e00ba906c4212975652354b1423f6ee0ec7b20e", - "filePath": "config/0x89/0.0.481", - "timestamp": "2024-10-27T12:50:09.561989" + "version": "0.0.216", + "checksum": "d75160f71081e7fe387344f51fb48251b8e7a91e7690be97ee845967494dfd86", + "signature": "78a82de98e2ac84c47d392296625679504f327263b3ace3b96686be3a443b76a0ae4e5cfb38962d3024e73250fe8479b423af21cc28b09defbc20f0285d60e04", + "hashSignature": "16840dddb6f35de35dd57159056217d947d7bc242cedc60eff3ccad664ed952f2287f19ee6a0413c929299afcd04447c217494c9bb2c433879f6c2ea66e69c03", + "filePath": "config/0x89/0.0.216", + "timestamp": "2024-03-21T12:13:22.608797" }, { "name": "stale_diff", "chainId": "0xa", - "version": "0.0.481", - "checksum": "3bab009ec87420bb6de56069cb3b76614c891688beb233bfe408aa60757478fb", - "signature": "481f19700f7399df4eefc8162e0229ea2a81fbfcbd6c327fac4ad79aaa7ac3aa8215b36bbdc74986c008cd60ffec428aa7ea04cd166faad938e1f9f4cef4b804", - "hashSignature": "f4991d29cefb09343ed5b3332416e2bab7428499373ea4bb0f6dd9bc1955eed33f80f383f70350a74ccddbea07e52eb4a144559626214a6c0f8c726b28be2f03", - "filePath": "stale_diff/0xa/0.0.481", - "timestamp": "2024-10-27T12:50:15.189820" + "version": "0.0.216", + "checksum": "4d44ce4c9c9e8b12ee5cc696b21f6409429d9f55cdc703c5acf799836c80ad8a", + "signature": "679813ccd33b254e88c19421e95a93e16c1db4fa473b8f9a510df7fe9ca56c7c0f21b303660d2e1175b1dc47c40aaa4fa989e710cfc2a432ad65cb7d0a522c0a", + "hashSignature": "ab86ad1046968599d8d85e0417e504c05f8bd87051ab4edee46a7946699f21e827bce2093dc94aee7f6746bb25413f819b4ca19950022fa0f8ed80efde0d2902", + "filePath": "stale_diff/0xa/0.0.216", + "timestamp": "2024-03-21T12:13:26.693599" }, { "name": "config", "chainId": "0xa", - "version": "0.0.481", - "checksum": "eb3b41ae8c3bbf9595dcded8e1b9090c8ed2427e236375652f6fd701c6e134b4", - "signature": "79552c7fa525e05c9c086fe8d8ecb49375be796176877e17332fa1137d4c653f224873bdac2b6fd4fe63fcfe6d404778684116e98fdb0563f63ae1efcfafe60d", - "hashSignature": "9c8a84e430578290eceea60ba7dec3695e0cfd343f7ac3e2667c8634afa3dd65d6ec25b112ac8260dbe3e6bc9e00ba906c4212975652354b1423f6ee0ec7b20e", - "filePath": "config/0xa/0.0.481", - "timestamp": "2024-10-27T12:50:15.190077" + "version": "0.0.216", + "checksum": "3e1772693c4e2fa91ae00c4b79d546f36e97525daa60baab372c145e979e19f4", + "signature": "f56c9387e07892aceb1018db6dc7e32cce0528c131b6ac1822e12e87dd43d40fdb9f3e17a9c6d7fd25a095e000db19acaf3f93c42142c48f35b06f07998f0f0e", + "hashSignature": "ae3fbbe3f87e48e537e9c1c9601fa5706ed72145250e5f4a2d7a4902d59e2c770c7c1ef88bde15505fa6bcbf24f85ef1b5ad48b76447a3479f7f7a462f082302", + "filePath": "config/0xa/0.0.216", + "timestamp": "2024-03-21T12:13:26.693846" }, { "name": "stale_diff", "chainId": "0xa4b1", - "version": "0.0.481", - "checksum": "3bab009ec87420bb6de56069cb3b76614c891688beb233bfe408aa60757478fb", - "signature": "481f19700f7399df4eefc8162e0229ea2a81fbfcbd6c327fac4ad79aaa7ac3aa8215b36bbdc74986c008cd60ffec428aa7ea04cd166faad938e1f9f4cef4b804", - "hashSignature": "f4991d29cefb09343ed5b3332416e2bab7428499373ea4bb0f6dd9bc1955eed33f80f383f70350a74ccddbea07e52eb4a144559626214a6c0f8c726b28be2f03", - "filePath": "stale_diff/0xa4b1/0.0.481", - "timestamp": "2024-10-27T12:50:21.791169" + "version": "0.0.216", + "checksum": "837ea3eef03c7a5a9975e5435581d6d051c5b4bd9a60e726ab044c8f7d911c7c", + "signature": "1c8252591e29761981ca792e2fb5e8c611906a7a136f8ac09e5fe0290ac3106cf425f0d38bc579739f09dba6ea4b9de50a77202f6d11e6b24e1f4d242af83603", + "hashSignature": "ffb5d7f443682e36b2928bccfe8919ecfb591b2cbf8cda82440332aec5927d39a3521c93fb7a39db20516a70a5dc101fddbc4548dabd1ee221fc754ca4c5040c", + "filePath": "stale_diff/0xa4b1/0.0.216", + "timestamp": "2024-03-21T12:13:31.844985" }, { "name": "config", "chainId": "0xa4b1", - "version": "0.0.481", - "checksum": "eb3b41ae8c3bbf9595dcded8e1b9090c8ed2427e236375652f6fd701c6e134b4", - "signature": "79552c7fa525e05c9c086fe8d8ecb49375be796176877e17332fa1137d4c653f224873bdac2b6fd4fe63fcfe6d404778684116e98fdb0563f63ae1efcfafe60d", - "hashSignature": "9c8a84e430578290eceea60ba7dec3695e0cfd343f7ac3e2667c8634afa3dd65d6ec25b112ac8260dbe3e6bc9e00ba906c4212975652354b1423f6ee0ec7b20e", - "filePath": "config/0xa4b1/0.0.481", - "timestamp": "2024-10-27T12:50:21.791427" + "version": "0.0.216", + "checksum": "3e1772693c4e2fa91ae00c4b79d546f36e97525daa60baab372c145e979e19f4", + "signature": "f56c9387e07892aceb1018db6dc7e32cce0528c131b6ac1822e12e87dd43d40fdb9f3e17a9c6d7fd25a095e000db19acaf3f93c42142c48f35b06f07998f0f0e", + "hashSignature": "ae3fbbe3f87e48e537e9c1c9601fa5706ed72145250e5f4a2d7a4902d59e2c770c7c1ef88bde15505fa6bcbf24f85ef1b5ad48b76447a3479f7f7a462f082302", + "filePath": "config/0xa4b1/0.0.216", + "timestamp": "2024-03-21T12:13:31.845230" }, { "name": "stale_diff", "chainId": "0xa86a", - "version": "0.0.481", - "checksum": "3bab009ec87420bb6de56069cb3b76614c891688beb233bfe408aa60757478fb", - "signature": "481f19700f7399df4eefc8162e0229ea2a81fbfcbd6c327fac4ad79aaa7ac3aa8215b36bbdc74986c008cd60ffec428aa7ea04cd166faad938e1f9f4cef4b804", - "hashSignature": "f4991d29cefb09343ed5b3332416e2bab7428499373ea4bb0f6dd9bc1955eed33f80f383f70350a74ccddbea07e52eb4a144559626214a6c0f8c726b28be2f03", - "filePath": "stale_diff/0xa86a/0.0.481", - "timestamp": "2024-10-27T12:50:27.602732" + "version": "0.0.216", + "checksum": "a4b8a87b84c93d03c1e51e83046edcda608be70d32924bad82aa3f93a0633f0c", + "signature": "be4cf087a3491184a4702cb9d4368775df68651735e050e2f2b16845376b87986ed24b00cde91a0d1a5739ddde513d9c9e97949b26ce96cb71b657c7e4d24a04", + "hashSignature": "0a74a14fa27289d347db514bfd11d2edb281cc6cf1bd7917705430dd7b4d97b245daa78bdcc07b74bd519619518432c7658d9c3e17b7def5e948655c9222f606", + "filePath": "stale_diff/0xa86a/0.0.216", + "timestamp": "2024-03-21T12:13:36.520723" }, { "name": "config", "chainId": "0xa86a", - "version": "0.0.481", + "version": "0.0.216", "checksum": "3e1772693c4e2fa91ae00c4b79d546f36e97525daa60baab372c145e979e19f4", "signature": "f56c9387e07892aceb1018db6dc7e32cce0528c131b6ac1822e12e87dd43d40fdb9f3e17a9c6d7fd25a095e000db19acaf3f93c42142c48f35b06f07998f0f0e", "hashSignature": "ae3fbbe3f87e48e537e9c1c9601fa5706ed72145250e5f4a2d7a4902d59e2c770c7c1ef88bde15505fa6bcbf24f85ef1b5ad48b76447a3479f7f7a462f082302", - "filePath": "config/0xa86a/0.0.481", - "timestamp": "2024-10-27T12:50:27.602991" + "filePath": "config/0xa86a/0.0.216", + "timestamp": "2024-03-21T12:13:36.520972" }, { "name": "stale_diff", "chainId": "0xe708", - "version": "0.0.435", - "checksum": "3bab009ec87420bb6de56069cb3b76614c891688beb233bfe408aa60757478fb", - "signature": "481f19700f7399df4eefc8162e0229ea2a81fbfcbd6c327fac4ad79aaa7ac3aa8215b36bbdc74986c008cd60ffec428aa7ea04cd166faad938e1f9f4cef4b804", - "hashSignature": "f4991d29cefb09343ed5b3332416e2bab7428499373ea4bb0f6dd9bc1955eed33f80f383f70350a74ccddbea07e52eb4a144559626214a6c0f8c726b28be2f03", - "filePath": "stale_diff/0xe708/0.0.435", - "timestamp": "2024-10-27T12:50:39.077328" + "version": "0.0.170", + "checksum": "79ed396331e95cfb34b5e14fce09b23c2059d78ada026cc38dc8f1119b793cee", + "signature": "8ae4be3b31e45eab20c4c0a177cd68ccba4293f790702392da02f840398e8da3525e69fb129c3c600c28c6dd773abfbe368a9b02c37076e82a67a906fc712e09", + "hashSignature": "9b3bacc5d3e9c50e711b8b0fd9e50dde38cc93c0fcde468efe8cac03e3b3b3fe818cfa105029b0fcac8ecc9c03c4c4833ad5da0fce9ed2b80e9d762640a2410b", + "filePath": "stale_diff/0xe708/0.0.170", + "timestamp": "2024-03-21T12:13:41.925924" }, { "name": "config", "chainId": "0xe708", - "version": "0.0.435", + "version": "0.0.170", "checksum": "3e1772693c4e2fa91ae00c4b79d546f36e97525daa60baab372c145e979e19f4", "signature": "f56c9387e07892aceb1018db6dc7e32cce0528c131b6ac1822e12e87dd43d40fdb9f3e17a9c6d7fd25a095e000db19acaf3f93c42142c48f35b06f07998f0f0e", "hashSignature": "ae3fbbe3f87e48e537e9c1c9601fa5706ed72145250e5f4a2d7a4902d59e2c770c7c1ef88bde15505fa6bcbf24f85ef1b5ad48b76447a3479f7f7a462f082302", - "filePath": "config/0xe708/0.0.435", - "timestamp": "2024-10-27T12:50:39.077595" + "filePath": "config/0xe708/0.0.170", + "timestamp": "2024-03-21T12:13:41.926309" }, { "name": "stale_diff", "chainId": "0x2105", - "version": "0.0.370", - "checksum": "3bab009ec87420bb6de56069cb3b76614c891688beb233bfe408aa60757478fb", - "signature": "481f19700f7399df4eefc8162e0229ea2a81fbfcbd6c327fac4ad79aaa7ac3aa8215b36bbdc74986c008cd60ffec428aa7ea04cd166faad938e1f9f4cef4b804", - "hashSignature": "f4991d29cefb09343ed5b3332416e2bab7428499373ea4bb0f6dd9bc1955eed33f80f383f70350a74ccddbea07e52eb4a144559626214a6c0f8c726b28be2f03", - "filePath": "stale_diff/0x2105/0.0.370", - "timestamp": "2024-10-27T12:50:33.715820" + "version": "0.0.105", + "checksum": "e9135ad2c08b3b0349db7e2decb871598251680d81c320718ac59de91b0cfcc8", + "signature": "e47d5f9afdd1c0557cdc020692d80bc844477c8407392ab860ce8b1efb95e2c303695699d6a464dfe071685d87990bbfceb1ab166bffff795c883444543b9404", + "hashSignature": "58224a7946eea888848a9305189c080f4eab04aca4be0bd69b08a799c7cb3c8595dcd354e021fccfd4c304465d94819dd1978e8dd898938d1391dfb508735e08", + "filePath": "stale_diff/0x2105/0.0.105", + "timestamp": "2024-03-21T12:13:47.580538" }, { "name": "config", "chainId": "0x2105", - "version": "0.0.370", - "checksum": "eb3b41ae8c3bbf9595dcded8e1b9090c8ed2427e236375652f6fd701c6e134b4", - "signature": "79552c7fa525e05c9c086fe8d8ecb49375be796176877e17332fa1137d4c653f224873bdac2b6fd4fe63fcfe6d404778684116e98fdb0563f63ae1efcfafe60d", - "hashSignature": "9c8a84e430578290eceea60ba7dec3695e0cfd343f7ac3e2667c8634afa3dd65d6ec25b112ac8260dbe3e6bc9e00ba906c4212975652354b1423f6ee0ec7b20e", - "filePath": "config/0x2105/0.0.370", - "timestamp": "2024-10-27T12:50:33.716077" - }, - { - "name": "stale_diff", - "chainId": "0xaa36a7", - "version": "0.0.289", - "checksum": "3bab009ec87420bb6de56069cb3b76614c891688beb233bfe408aa60757478fb", - "signature": "481f19700f7399df4eefc8162e0229ea2a81fbfcbd6c327fac4ad79aaa7ac3aa8215b36bbdc74986c008cd60ffec428aa7ea04cd166faad938e1f9f4cef4b804", - "hashSignature": "f4991d29cefb09343ed5b3332416e2bab7428499373ea4bb0f6dd9bc1955eed33f80f383f70350a74ccddbea07e52eb4a144559626214a6c0f8c726b28be2f03", - "filePath": "stale_diff/0xaa36a7/0.0.289", - "timestamp": "2024-10-27T12:50:44.187191" - }, - { - "name": "config", - "chainId": "0xaa36a7", - "version": "0.0.289", - "checksum": "3e1772693c4e2fa91ae00c4b79d546f36e97525daa60baab372c145e979e19f4", - "signature": "f56c9387e07892aceb1018db6dc7e32cce0528c131b6ac1822e12e87dd43d40fdb9f3e17a9c6d7fd25a095e000db19acaf3f93c42142c48f35b06f07998f0f0e", - "hashSignature": "ae3fbbe3f87e48e537e9c1c9601fa5706ed72145250e5f4a2d7a4902d59e2c770c7c1ef88bde15505fa6bcbf24f85ef1b5ad48b76447a3479f7f7a462f082302", - "filePath": "config/0xaa36a7/0.0.289", - "timestamp": "2024-10-27T12:50:44.187491" - }, - { - "name": "stale_diff", - "chainId": "0xcc", - "version": "0.0.238", - "checksum": "3bab009ec87420bb6de56069cb3b76614c891688beb233bfe408aa60757478fb", - "signature": "481f19700f7399df4eefc8162e0229ea2a81fbfcbd6c327fac4ad79aaa7ac3aa8215b36bbdc74986c008cd60ffec428aa7ea04cd166faad938e1f9f4cef4b804", - "hashSignature": "f4991d29cefb09343ed5b3332416e2bab7428499373ea4bb0f6dd9bc1955eed33f80f383f70350a74ccddbea07e52eb4a144559626214a6c0f8c726b28be2f03", - "filePath": "stale_diff/0xcc/0.0.238", - "timestamp": "2024-10-27T12:50:49.423599" - }, - { - "name": "config", - "chainId": "0xcc", - "version": "0.0.238", - "checksum": "3e1772693c4e2fa91ae00c4b79d546f36e97525daa60baab372c145e979e19f4", - "signature": "f56c9387e07892aceb1018db6dc7e32cce0528c131b6ac1822e12e87dd43d40fdb9f3e17a9c6d7fd25a095e000db19acaf3f93c42142c48f35b06f07998f0f0e", - "hashSignature": "ae3fbbe3f87e48e537e9c1c9601fa5706ed72145250e5f4a2d7a4902d59e2c770c7c1ef88bde15505fa6bcbf24f85ef1b5ad48b76447a3479f7f7a462f082302", - "filePath": "config/0xcc/0.0.238", - "timestamp": "2024-10-27T12:50:49.423903" - }, - { - "name": "stale_diff", - "chainId": "0x0", - "version": "0.0.179", - "checksum": "3bab009ec87420bb6de56069cb3b76614c891688beb233bfe408aa60757478fb", - "signature": "481f19700f7399df4eefc8162e0229ea2a81fbfcbd6c327fac4ad79aaa7ac3aa8215b36bbdc74986c008cd60ffec428aa7ea04cd166faad938e1f9f4cef4b804", - "hashSignature": "f4991d29cefb09343ed5b3332416e2bab7428499373ea4bb0f6dd9bc1955eed33f80f383f70350a74ccddbea07e52eb4a144559626214a6c0f8c726b28be2f03", - "filePath": "stale_diff/0x0/0.0.179", - "timestamp": "2024-10-27T12:50:57.113651" - }, - { - "name": "config", - "chainId": "0x0", - "version": "0.0.179", - "checksum": "3e1772693c4e2fa91ae00c4b79d546f36e97525daa60baab372c145e979e19f4", - "signature": "f56c9387e07892aceb1018db6dc7e32cce0528c131b6ac1822e12e87dd43d40fdb9f3e17a9c6d7fd25a095e000db19acaf3f93c42142c48f35b06f07998f0f0e", - "hashSignature": "ae3fbbe3f87e48e537e9c1c9601fa5706ed72145250e5f4a2d7a4902d59e2c770c7c1ef88bde15505fa6bcbf24f85ef1b5ad48b76447a3479f7f7a462f082302", - "filePath": "config/0x0/0.0.179", - "timestamp": "2024-10-27T12:50:57.113910" - }, - { - "name": "stale_diff", - "chainId": "0x144", - "version": "0.0.258", - "checksum": "3bab009ec87420bb6de56069cb3b76614c891688beb233bfe408aa60757478fb", - "signature": "481f19700f7399df4eefc8162e0229ea2a81fbfcbd6c327fac4ad79aaa7ac3aa8215b36bbdc74986c008cd60ffec428aa7ea04cd166faad938e1f9f4cef4b804", - "hashSignature": "f4991d29cefb09343ed5b3332416e2bab7428499373ea4bb0f6dd9bc1955eed33f80f383f70350a74ccddbea07e52eb4a144559626214a6c0f8c726b28be2f03", - "filePath": "stale_diff/0x144/0.0.258", - "timestamp": "2024-10-27T12:51:10.842031" - }, - { - "name": "config", - "chainId": "0x144", - "version": "0.0.258", - "checksum": "3e1772693c4e2fa91ae00c4b79d546f36e97525daa60baab372c145e979e19f4", - "signature": "f56c9387e07892aceb1018db6dc7e32cce0528c131b6ac1822e12e87dd43d40fdb9f3e17a9c6d7fd25a095e000db19acaf3f93c42142c48f35b06f07998f0f0e", - "hashSignature": "ae3fbbe3f87e48e537e9c1c9601fa5706ed72145250e5f4a2d7a4902d59e2c770c7c1ef88bde15505fa6bcbf24f85ef1b5ad48b76447a3479f7f7a462f082302", - "filePath": "config/0x144/0.0.258", - "timestamp": "2024-10-27T12:51:10.842292" - }, - { - "name": "stale_diff", - "chainId": "0x82750", - "version": "0.0.180", - "checksum": "3bab009ec87420bb6de56069cb3b76614c891688beb233bfe408aa60757478fb", - "signature": "481f19700f7399df4eefc8162e0229ea2a81fbfcbd6c327fac4ad79aaa7ac3aa8215b36bbdc74986c008cd60ffec428aa7ea04cd166faad938e1f9f4cef4b804", - "hashSignature": "f4991d29cefb09343ed5b3332416e2bab7428499373ea4bb0f6dd9bc1955eed33f80f383f70350a74ccddbea07e52eb4a144559626214a6c0f8c726b28be2f03", - "filePath": "stale_diff/0x82750/0.0.180", - "timestamp": "2024-10-27T12:51:16.756456" - }, - { - "name": "config", - "chainId": "0x82750", - "version": "0.0.180", - "checksum": "3e1772693c4e2fa91ae00c4b79d546f36e97525daa60baab372c145e979e19f4", - "signature": "f56c9387e07892aceb1018db6dc7e32cce0528c131b6ac1822e12e87dd43d40fdb9f3e17a9c6d7fd25a095e000db19acaf3f93c42142c48f35b06f07998f0f0e", - "hashSignature": "ae3fbbe3f87e48e537e9c1c9601fa5706ed72145250e5f4a2d7a4902d59e2c770c7c1ef88bde15505fa6bcbf24f85ef1b5ad48b76447a3479f7f7a462f082302", - "filePath": "config/0x82750/0.0.180", - "timestamp": "2024-10-27T12:51:16.756829" - }, - { - "name": "stale_diff", - "chainId": "0x1b58", - "version": "0.0.180", - "checksum": "3bab009ec87420bb6de56069cb3b76614c891688beb233bfe408aa60757478fb", - "signature": "481f19700f7399df4eefc8162e0229ea2a81fbfcbd6c327fac4ad79aaa7ac3aa8215b36bbdc74986c008cd60ffec428aa7ea04cd166faad938e1f9f4cef4b804", - "hashSignature": "f4991d29cefb09343ed5b3332416e2bab7428499373ea4bb0f6dd9bc1955eed33f80f383f70350a74ccddbea07e52eb4a144559626214a6c0f8c726b28be2f03", - "filePath": "stale_diff/0x1b58/0.0.180", - "timestamp": "2024-10-27T12:51:23.355191" - }, - { - "name": "config", - "chainId": "0x1b58", - "version": "0.0.180", + "version": "0.0.105", "checksum": "3e1772693c4e2fa91ae00c4b79d546f36e97525daa60baab372c145e979e19f4", "signature": "f56c9387e07892aceb1018db6dc7e32cce0528c131b6ac1822e12e87dd43d40fdb9f3e17a9c6d7fd25a095e000db19acaf3f93c42142c48f35b06f07998f0f0e", "hashSignature": "ae3fbbe3f87e48e537e9c1c9601fa5706ed72145250e5f4a2d7a4902d59e2c770c7c1ef88bde15505fa6bcbf24f85ef1b5ad48b76447a3479f7f7a462f082302", - "filePath": "config/0x1b58/0.0.180", - "timestamp": "2024-10-27T12:51:23.355470" + "filePath": "config/0x2105/0.0.105", + "timestamp": "2024-03-21T12:13:47.580833" }, { "name": "stale_diff", - "chainId": "0x138d5", - "version": "0.0.180", - "checksum": "3bab009ec87420bb6de56069cb3b76614c891688beb233bfe408aa60757478fb", - "signature": "481f19700f7399df4eefc8162e0229ea2a81fbfcbd6c327fac4ad79aaa7ac3aa8215b36bbdc74986c008cd60ffec428aa7ea04cd166faad938e1f9f4cef4b804", - "hashSignature": "f4991d29cefb09343ed5b3332416e2bab7428499373ea4bb0f6dd9bc1955eed33f80f383f70350a74ccddbea07e52eb4a144559626214a6c0f8c726b28be2f03", - "filePath": "stale_diff/0x138d5/0.0.180", - "timestamp": "2024-10-27T12:51:29.701505" - }, - { - "name": "config", - "chainId": "0x138d5", - "version": "0.0.180", - "checksum": "3e1772693c4e2fa91ae00c4b79d546f36e97525daa60baab372c145e979e19f4", - "signature": "f56c9387e07892aceb1018db6dc7e32cce0528c131b6ac1822e12e87dd43d40fdb9f3e17a9c6d7fd25a095e000db19acaf3f93c42142c48f35b06f07998f0f0e", - "hashSignature": "ae3fbbe3f87e48e537e9c1c9601fa5706ed72145250e5f4a2d7a4902d59e2c770c7c1ef88bde15505fa6bcbf24f85ef1b5ad48b76447a3479f7f7a462f082302", - "filePath": "config/0x138d5/0.0.180", - "timestamp": "2024-10-27T12:51:29.701771" - }, - { - "name": "stale", - "chainId": "0x1b6e6", - "version": "0.0.13", - "checksum": "f01b489470ec41ece8b65ee23e2b092e77046eb9f21a54a9c39ef2399ecc721f", - "signature": "cf94226430160273f94699041b56e9edfc603023ba4b653cf9c555a9f21065078774e1d554eae602d4a5e7bba02e0a76047a42e9a0a89acb1c2069a28c9f6f02", - "hashSignature": "939dd7022011d78cfd935ce88ca041660df72480ce8d9ec84a66066e54e030eb4abb2beadaad8ead4db192c1d0558ac1faa6703b8dda2101481accaf5f6f3005", - "filePath": "stale/0x1b6e6/0.0.13", - "timestamp": "2024-10-27T12:40:16.522556" - }, - { - "name": "stale_diff", - "chainId": "0x1b6e6", - "version": "0.0.114", - "checksum": "3bab009ec87420bb6de56069cb3b76614c891688beb233bfe408aa60757478fb", - "signature": "481f19700f7399df4eefc8162e0229ea2a81fbfcbd6c327fac4ad79aaa7ac3aa8215b36bbdc74986c008cd60ffec428aa7ea04cd166faad938e1f9f4cef4b804", - "hashSignature": "f4991d29cefb09343ed5b3332416e2bab7428499373ea4bb0f6dd9bc1955eed33f80f383f70350a74ccddbea07e52eb4a144559626214a6c0f8c726b28be2f03", - "filePath": "stale_diff/0x1b6e6/0.0.114", - "timestamp": "2024-10-27T12:51:43.249992" - }, - { - "name": "config", - "chainId": "0x1b6e6", - "version": "0.0.114", - "checksum": "3e1772693c4e2fa91ae00c4b79d546f36e97525daa60baab372c145e979e19f4", - "signature": "f56c9387e07892aceb1018db6dc7e32cce0528c131b6ac1822e12e87dd43d40fdb9f3e17a9c6d7fd25a095e000db19acaf3f93c42142c48f35b06f07998f0f0e", - "hashSignature": "ae3fbbe3f87e48e537e9c1c9601fa5706ed72145250e5f4a2d7a4902d59e2c770c7c1ef88bde15505fa6bcbf24f85ef1b5ad48b76447a3479f7f7a462f082302", - "filePath": "config/0x1b6e6/0.0.114", - "timestamp": "2024-10-27T12:51:43.250277" - }, - { - "name": "stale_diff", - "chainId": "0x138d4", - "version": "0.0.75", - "checksum": "3bab009ec87420bb6de56069cb3b76614c891688beb233bfe408aa60757478fb", - "signature": "481f19700f7399df4eefc8162e0229ea2a81fbfcbd6c327fac4ad79aaa7ac3aa8215b36bbdc74986c008cd60ffec428aa7ea04cd166faad938e1f9f4cef4b804", - "hashSignature": "f4991d29cefb09343ed5b3332416e2bab7428499373ea4bb0f6dd9bc1955eed33f80f383f70350a74ccddbea07e52eb4a144559626214a6c0f8c726b28be2f03", - "filePath": "stale_diff/0x138d4/0.0.75", - "timestamp": "2024-10-27T12:51:36.220430" + "chainId": "0xaa36a7", + "version": "0.0.24", + "checksum": "501048a17060b390dd5f6d3556dfae356299f2e1761170c5e77f71ae304b38a5", + "signature": "453422760071953f014675c10e7b540474847a7901d078aa892c1ea6cde2f669209772042b73573943cb3b123ae4ea4c48b3bda285a13262ec93e4acffe51e07", + "hashSignature": "4e1a3c0e259dfa4ee5bdfc9580a3d53c817229bb076b2ff90ad63cf7db5444073e1257a27f8be0cce7825f8824fe6a3f698c77f4f24d156b52d019bec155460c", + "filePath": "stale_diff/0xaa36a7/0.0.24", + "timestamp": "2024-03-21T12:13:51.767514" }, { "name": "config", - "chainId": "0x138d4", - "version": "0.0.75", + "chainId": "0xaa36a7", + "version": "0.0.24", "checksum": "3e1772693c4e2fa91ae00c4b79d546f36e97525daa60baab372c145e979e19f4", "signature": "f56c9387e07892aceb1018db6dc7e32cce0528c131b6ac1822e12e87dd43d40fdb9f3e17a9c6d7fd25a095e000db19acaf3f93c42142c48f35b06f07998f0f0e", "hashSignature": "ae3fbbe3f87e48e537e9c1c9601fa5706ed72145250e5f4a2d7a4902d59e2c770c7c1ef88bde15505fa6bcbf24f85ef1b5ad48b76447a3479f7f7a462f082302", - "filePath": "config/0x138d4/0.0.75", - "timestamp": "2024-10-27T12:51:36.220693" - }, - { - "name": "stale", - "chainId": "0x138d4", - "version": "0.0.7", - "checksum": "f01b489470ec41ece8b65ee23e2b092e77046eb9f21a54a9c39ef2399ecc721f", - "signature": "cf94226430160273f94699041b56e9edfc603023ba4b653cf9c555a9f21065078774e1d554eae602d4a5e7bba02e0a76047a42e9a0a89acb1c2069a28c9f6f02", - "hashSignature": "939dd7022011d78cfd935ce88ca041660df72480ce8d9ec84a66066e54e030eb4abb2beadaad8ead4db192c1d0558ac1faa6703b8dda2101481accaf5f6f3005", - "filePath": "stale/0x138d4/0.0.7", - "timestamp": "2024-10-27T12:40:00.249463" + "filePath": "config/0xaa36a7/0.0.24", + "timestamp": "2024-03-21T12:13:51.767758" } ] diff --git a/test/e2e/mock-cdn/update-mock-cdn-files.js b/test/e2e/mock-cdn/update-mock-cdn-files.js index 139c60c23cef..5fa2d7cc51a5 100644 --- a/test/e2e/mock-cdn/update-mock-cdn-files.js +++ b/test/e2e/mock-cdn/update-mock-cdn-files.js @@ -65,11 +65,6 @@ async function updateMockCdnFiles() { const { mainnetConfigVersion, mainnetStaleVersion, mainnetStaleDiffVersion } = await getFileVersions(); - // Function to create header object with Etag and Content-Type - const createHeaderObject = (etag) => ({ - Etag: etag, - 'Content-Type': 'text/plain', - }); // updating cdn-config-res-headers.json file const configResponse = await fetch( `${PPOM_CONFIG_URL}${mainnetConfigVersion}`, @@ -81,7 +76,7 @@ async function updateMockCdnFiles() { const configHeaders = configResponse.headers; const etagConfig = configHeaders.get('etag'); - const etagConfigObject = createHeaderObject(etagConfig); + const etagConfigObject = { Etag: etagConfig }; writeFileSync( `${MOCK_CDN_FOLDER_URL}cdn-config-res-headers.json`, @@ -96,7 +91,7 @@ async function updateMockCdnFiles() { const staleHeaders = staleResponse.headers; const etagStale = staleHeaders.get('etag'); - const etagStaleObject = createHeaderObject(etagStale); + const etagStaleObject = { Etag: etagStale }; writeFileSync( `${MOCK_CDN_FOLDER_URL}cdn-stale-res-headers.json`, @@ -114,7 +109,7 @@ async function updateMockCdnFiles() { const staleDiffHeaders = staleDiffResponse.headers; const etagStaleDiff = staleDiffHeaders.get('etag'); - const etagStaleDiffObject = createHeaderObject(etagStaleDiff); + const etagStaleDiffObject = { Etag: etagStaleDiff }; writeFileSync( `${MOCK_CDN_FOLDER_URL}cdn-stale-diff-res-headers.json`, diff --git a/test/e2e/mock-e2e.js b/test/e2e/mock-e2e.js index 85636fcb9089..12d0fb293e15 100644 --- a/test/e2e/mock-e2e.js +++ b/test/e2e/mock-e2e.js @@ -1,8 +1,5 @@ const fs = require('fs'); -const { - SECURITY_PROVIDER_SUPPORTED_CHAIN_IDS, -} = require('../../shared/constants/security-provider'); const { BRIDGE_DEV_API_BASE_URL, BRIDGE_PROD_API_BASE_URL, @@ -16,7 +13,6 @@ const { SWAPS_API_V2_BASE_URL, TOKEN_API_BASE_URL, } = require('../../shared/constants/swaps'); -const { SECURITY_ALERTS_PROD_API_BASE_URL } = require('./tests/ppom/constants'); const { DEFAULT_FEATURE_FLAGS_RESPONSE: BRIDGE_DEFAULT_FEATURE_FLAGS_RESPONSE, } = require('./tests/bridge/constants'); @@ -107,7 +103,7 @@ const privateHostMatchers = [ async function setupMocking( server, testSpecificMock, - { chainId, ethConversionInUsd = 1700 }, + { chainId, ethConversionInUsd = '1700' }, ) { const privacyReport = new Set(); await server.forAnyRequest().thenPassThrough({ @@ -155,30 +151,6 @@ async function setupMocking( }; }); - await server - .forGet(`${SECURITY_ALERTS_PROD_API_BASE_URL}/supportedChains`) - .thenCallback(() => { - return { - statusCode: 200, - json: SECURITY_PROVIDER_SUPPORTED_CHAIN_IDS, - }; - }); - - await server - .forPost(`${SECURITY_ALERTS_PROD_API_BASE_URL}/validate/${chainId}`) - .thenCallback(() => { - return { - statusCode: 200, - json: { - block: 20733513, - result_type: 'Benign', - reason: '', - description: '', - features: [], - }, - }; - }); - await server .forPost( 'https://arbitrum-mainnet.infura.io/v3/00000000000000000000000000000000', @@ -616,15 +588,13 @@ async function setupMocking( }); await server - .forGet('https://min-api.cryptocompare.com/data/pricemulti') - .withQuery({ fsyms: 'ETH', tsyms: 'usd' }) + .forGet('https://min-api.cryptocompare.com/data/price') + .withQuery({ fsym: 'ETH', tsyms: 'USD' }) .thenCallback(() => { return { statusCode: 200, json: { - ETH: { - USD: ethConversionInUsd, - }, + USD: ethConversionInUsd, }, }; }); diff --git a/test/e2e/page-objects/flows/login.flow.ts b/test/e2e/page-objects/flows/login.flow.ts index fcd0bcb22d8a..87239e3f19f1 100644 --- a/test/e2e/page-objects/flows/login.flow.ts +++ b/test/e2e/page-objects/flows/login.flow.ts @@ -18,6 +18,10 @@ export const loginWithoutBalanceValidation = async ( const loginPage = new LoginPage(driver); await loginPage.check_pageIsLoaded(); await loginPage.loginToHomepage(password); + + // user should land on homepage after successfully logging in with password + const homePage = new HomePage(driver); + await homePage.check_pageIsLoaded(); }; /** @@ -33,14 +37,10 @@ export const loginWithBalanceValidation = async ( password?: string, ) => { await loginWithoutBalanceValidation(driver, password); - // user should land on homepage after successfully logging in with password - const homePage = new HomePage(driver); - await homePage.check_pageIsLoaded(); - // Verify the expected balance on the homepage if (ganacheServer) { - await homePage.check_ganacheBalanceIsDisplayed(ganacheServer); + await new HomePage(driver).check_ganacheBalanceIsDisplayed(ganacheServer); } else { - await homePage.check_expectedBalanceIsDisplayed(); + await new HomePage(driver).check_expectedBalanceIsDisplayed(); } }; diff --git a/test/e2e/page-objects/flows/network.flow.ts b/test/e2e/page-objects/flows/network.flow.ts deleted file mode 100644 index fc77db8895bd..000000000000 --- a/test/e2e/page-objects/flows/network.flow.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { Driver } from '../../webdriver/driver'; -import HeaderNavbar from '../pages/header-navbar'; -import SelectNetwork from '../pages/dialog/select-network'; -import NetworkSwitchModalConfirmation from '../pages/dialog/network-switch-modal-confirmation'; - -/** - * Switches to a specified network in the header bar. - * - * @param driver - * @param networkName - The name of the network to switch to. - * @param toggleShowTestNetwork - A boolean indicating whether to toggle the display of test networks. Defaults to false. - */ -export const switchToNetworkFlow = async ( - driver: Driver, - networkName: string, - toggleShowTestNetwork: boolean = false, -) => { - console.log(`Switch to network ${networkName} in header bar`); - const headerNavbar = new HeaderNavbar(driver); - await headerNavbar.check_pageIsLoaded(); - await headerNavbar.clickSwitchNetworkDropDown(); - - const selectNetworkDialog = new SelectNetwork(driver); - await selectNetworkDialog.check_pageIsLoaded(); - if (toggleShowTestNetwork) { - await selectNetworkDialog.toggleShowTestNetwork(); - } - await selectNetworkDialog.selectNetworkName(networkName); - await headerNavbar.check_currentSelectedNetwork(networkName); -}; - -/** - * Search for a network in the select network dialog and switches to it. - * - * @param driver - * @param networkName - The name of the network to search for and switch to. - */ -export const searchAndSwitchToNetworkFlow = async ( - driver: Driver, - networkName: string, -) => { - console.log( - `Search in select network dialog and switch to network ${networkName}`, - ); - const headerNavbar = new HeaderNavbar(driver); - await headerNavbar.check_pageIsLoaded(); - await headerNavbar.clickSwitchNetworkDropDown(); - - const selectNetworkDialog = new SelectNetwork(driver); - await selectNetworkDialog.check_pageIsLoaded(); - await selectNetworkDialog.fillNetworkSearchInput(networkName); - await selectNetworkDialog.clickAddButton(); - - const networkSwitchModalConfirmation = new NetworkSwitchModalConfirmation( - driver, - ); - await networkSwitchModalConfirmation.check_pageIsLoaded(); - await networkSwitchModalConfirmation.clickApproveButton(); - await headerNavbar.check_currentSelectedNetwork(networkName); -}; diff --git a/test/e2e/page-objects/flows/onboarding.flow.ts b/test/e2e/page-objects/flows/onboarding.flow.ts deleted file mode 100644 index b5fda9e0c276..000000000000 --- a/test/e2e/page-objects/flows/onboarding.flow.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { Driver } from '../../webdriver/driver'; -import OnboardingMetricsPage from '../pages/onboarding/onboarding-metrics-page'; -import OnboardingPasswordPage from '../pages/onboarding/onboarding-password-page'; -import OnboardingSrpPage from '../pages/onboarding/onboarding-srp-page'; -import StartOnboardingPage from '../pages/onboarding/start-onboarding-page'; -import SecureWalletPage from '../pages/onboarding/secure-wallet-page'; -import OnboardingCompletePage from '../pages/onboarding/onboarding-complete-page'; - -export const createNewWalletOnboardingFlow = async (driver: Driver) => { - console.log('Starting the creation of a new wallet onboarding flow'); - await driver.navigate(); - const startOnboardingPage = new StartOnboardingPage(driver); - await startOnboardingPage.check_pageIsLoaded(); - await startOnboardingPage.checkTermsCheckbox(); - await startOnboardingPage.clickCreateWalletButton(); - - const onboardingMetricsPage = new OnboardingMetricsPage(driver); - await onboardingMetricsPage.check_pageIsLoaded(); - await onboardingMetricsPage.clickNoThanksButton(); - - const onboardingPasswordPage = new OnboardingPasswordPage(driver); - await onboardingPasswordPage.check_pageIsLoaded(); - await onboardingPasswordPage.createWalletPassword(); - - const secureWalletPage = new SecureWalletPage(driver); - await secureWalletPage.check_pageIsLoaded(); - await secureWalletPage.revealAndConfirmSRP(); -}; - -export const importSRPOnboardingFlow = async (driver: Driver) => { - console.log('Starting the import of SRP onboarding flow'); - await driver.navigate(); - const startOnboardingPage = new StartOnboardingPage(driver); - await startOnboardingPage.check_pageIsLoaded(); - await startOnboardingPage.checkTermsCheckbox(); - await startOnboardingPage.clickImportWalletButton(); - - const onboardingMetricsPage = new OnboardingMetricsPage(driver); - await onboardingMetricsPage.check_pageIsLoaded(); - await onboardingMetricsPage.clickNoThanksButton(); - - const onboardingSrpPage = new OnboardingSrpPage(driver); - await onboardingSrpPage.check_pageIsLoaded(); - await onboardingSrpPage.fillSrp(); - await onboardingSrpPage.clickConfirmButton(); - - const onboardingPasswordPage = new OnboardingPasswordPage(driver); - await onboardingPasswordPage.check_pageIsLoaded(); - await onboardingPasswordPage.createImportedWalletPassword(); -}; - -export const completeCreateNewWalletOnboardingFlow = async (driver: Driver) => { - console.log('start to complete create new wallet onboarding flow '); - await createNewWalletOnboardingFlow(driver); - const onboardingCompletePage = new OnboardingCompletePage(driver); - await onboardingCompletePage.check_pageIsLoaded(); - await onboardingCompletePage.check_congratulationsMessageIsDisplayed(); - await onboardingCompletePage.completeOnboarding(); -}; - -export const completeImportSRPOnboardingFlow = async (driver: Driver) => { - console.log('start to complete import srp onboarding flow '); - await importSRPOnboardingFlow(driver); - const onboardingCompletePage = new OnboardingCompletePage(driver); - await onboardingCompletePage.check_pageIsLoaded(); - await onboardingCompletePage.check_walletReadyMessageIsDisplayed(); - await onboardingCompletePage.completeOnboarding(); -}; diff --git a/test/e2e/page-objects/pages/account-list-page.ts b/test/e2e/page-objects/pages/account-list-page.ts index ae02973b6ae5..7218c727a929 100644 --- a/test/e2e/page-objects/pages/account-list-page.ts +++ b/test/e2e/page-objects/pages/account-list-page.ts @@ -20,15 +20,9 @@ class AccountListPage { private readonly addAccountConfirmButton = '[data-testid="submit-add-account-with-name"]'; - private readonly importAccountConfirmButton = - '[data-testid="import-account-confirm-button"]'; - private readonly addEthereumAccountButton = '[data-testid="multichain-account-menu-popover-add-account"]'; - private readonly addImportedAccountButton = - '[data-testid="multichain-account-menu-popover-add-imported-account"]'; - private readonly addSnapAccountButton = { text: 'Add account Snap', tag: 'button', @@ -60,8 +54,6 @@ class AccountListPage { private readonly saveAccountLabelButton = '[data-testid="save-account-label-input"]'; - private readonly importAccountPrivateKeyInput = '#private-key-box'; - constructor(driver: Driver) { this.driver = driver; } @@ -94,34 +86,6 @@ class AccountListPage { ); } - /** - * Adds a new account with default next available name. - * - */ - async addNewAccountWithDefaultName(): Promise { - console.log(`Adding new account with next available name`); - await this.driver.clickElement(this.createAccountButton); - await this.driver.clickElement(this.addEthereumAccountButton); - await this.driver.clickElementAndWaitToDisappear( - this.addAccountConfirmButton, - ); - } - - /** - * Adds a new account with a custom label. - * - * @param privateKey - Private key of the account - */ - async addNewImportedAccount(privateKey: string): Promise { - console.log(`Adding new imported account`); - await this.driver.clickElement(this.createAccountButton); - await this.driver.clickElement(this.addImportedAccountButton); - await this.driver.fill(this.importAccountPrivateKeyInput, privateKey); - await this.driver.clickElementAndWaitToDisappear( - this.importAccountConfirmButton, - ); - } - /** * Changes the label of the current account. * @@ -263,25 +227,6 @@ class AccountListPage { console.log(`Check that hidden accounts list is displayed in account list`); await this.driver.waitForSelector(this.hiddenAccountsList); } - - /** - * Verifies number of accounts currently showing in the accounts menu. - * - * @param expectedNumberOfAccounts - The expected number of accounts showing. - */ - async check_numberOfAvailableAccounts( - expectedNumberOfAccounts: number, - ): Promise { - console.log( - `Verify the number of accounts in the account menu is: ${expectedNumberOfAccounts}`, - ); - await this.driver.wait(async () => { - const internalAccounts = await this.driver.findElements( - this.accountListItem, - ); - return internalAccounts.length === expectedNumberOfAccounts; - }, 20000); - } } export default AccountListPage; diff --git a/test/e2e/page-objects/pages/dialog/network-switch-modal-confirmation.ts b/test/e2e/page-objects/pages/dialog/network-switch-modal-confirmation.ts deleted file mode 100644 index 8a51194605f7..000000000000 --- a/test/e2e/page-objects/pages/dialog/network-switch-modal-confirmation.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { Driver } from '../../../webdriver/driver'; - -class NetworkSwitchModalConfirmation { - private driver: Driver; - - private readonly submitButton = '[data-testid="confirmation-submit-button"]'; - - private readonly addNetworkMessage = { - text: 'Want to add this network?', - tag: 'h3', - }; - - constructor(driver: Driver) { - this.driver = driver; - } - - async check_pageIsLoaded(): Promise { - try { - await this.driver.waitForMultipleSelectors([ - this.addNetworkMessage, - this.submitButton, - ]); - } catch (e) { - console.log( - 'Timeout while waiting for add network confirmation modal to be loaded', - e, - ); - throw e; - } - console.log('Add network confirmation modal is loaded'); - } - - async clickApproveButton(): Promise { - console.log('Click Approve Button'); - await this.driver.clickElementAndWaitToDisappear(this.submitButton); - } -} - -export default NetworkSwitchModalConfirmation; diff --git a/test/e2e/page-objects/pages/dialog/select-network.ts b/test/e2e/page-objects/pages/dialog/select-network.ts deleted file mode 100644 index 2c399a4118d8..000000000000 --- a/test/e2e/page-objects/pages/dialog/select-network.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { Driver } from '../../../webdriver/driver'; - -class SelectNetwork { - private driver: Driver; - - private networkName: string | undefined; - - private readonly addNetworkButton = { - tag: 'button', - text: 'Add a custom network', - }; - - private readonly closeButton = 'button[aria-label="Close"]'; - - private readonly searchInput = - '[data-testid="network-redesign-modal-search-input"]'; - - private readonly selectNetworkMessage = { - text: 'Select a network', - tag: 'h4', - }; - - private readonly toggleButton = '.toggle-button > div'; - - constructor(driver: Driver) { - this.driver = driver; - } - - async check_pageIsLoaded(): Promise { - try { - await this.driver.waitForMultipleSelectors([ - this.selectNetworkMessage, - this.searchInput, - ]); - } catch (e) { - console.log( - 'Timeout while waiting for select network dialog to be loaded', - e, - ); - throw e; - } - console.log('Select network dialog is loaded'); - } - - async selectNetworkName(networkName: string): Promise { - console.log(`Click ${networkName}`); - this.networkName = `[data-testid="${networkName}"]`; - await this.driver.clickElementAndWaitToDisappear(this.networkName); - } - - async addNewNetwork(): Promise { - console.log('Click Add network'); - await this.driver.clickElement(this.addNetworkButton); - } - - async clickCloseButton(): Promise { - console.log('Click Close Button'); - await this.driver.clickElementAndWaitToDisappear(this.closeButton); - } - - async toggleShowTestNetwork(): Promise { - console.log('Toggle show test network in select network dialog'); - await this.driver.clickElement(this.toggleButton); - } - - async fillNetworkSearchInput(networkName: string): Promise { - console.log(`Fill network search input with ${networkName}`); - await this.driver.fill(this.searchInput, networkName); - } - - async clickAddButton(): Promise { - console.log('Click Add Button'); - await this.driver.clickElementAndWaitToDisappear( - '[data-testid="test-add-button"]', - ); - } -} - -export default SelectNetwork; diff --git a/test/e2e/page-objects/pages/header-navbar.ts b/test/e2e/page-objects/pages/header-navbar.ts index 100c23b851e4..8bd29ea8c602 100644 --- a/test/e2e/page-objects/pages/header-navbar.ts +++ b/test/e2e/page-objects/pages/header-navbar.ts @@ -5,7 +5,7 @@ class HeaderNavbar { private readonly accountMenuButton = '[data-testid="account-menu-icon"]'; - private readonly threeDotMenuButton = + private readonly accountOptionMenu = '[data-testid="account-options-menu-button"]'; private readonly accountSnapButton = { text: 'Snaps', tag: 'div' }; @@ -17,8 +17,6 @@ class HeaderNavbar { private readonly settingsButton = '[data-testid="global-menu-settings"]'; - private readonly switchNetworkDropDown = '[data-testid="network-display"]'; - constructor(driver: Driver) { this.driver = driver; } @@ -27,7 +25,7 @@ class HeaderNavbar { try { await this.driver.waitForMultipleSelectors([ this.accountMenuButton, - this.threeDotMenuButton, + this.accountOptionMenu, ]); } catch (e) { console.log('Timeout while waiting for header navbar to be loaded', e); @@ -37,7 +35,11 @@ class HeaderNavbar { } async lockMetaMask(): Promise { - await this.openThreeDotMenu(); + await this.driver.clickElement(this.accountOptionMenu); + // fix race condition with mmi build + if (process.env.MMI) { + await this.driver.waitForSelector(this.mmiPortfolioButton); + } await this.driver.clickElement(this.lockMetaMaskButton); } @@ -45,39 +47,22 @@ class HeaderNavbar { await this.driver.clickElement(this.accountMenuButton); } - async openThreeDotMenu(): Promise { - console.log('Open account options menu'); - await this.driver.clickElement(this.threeDotMenuButton); - // fix race condition with mmi build - if (process.env.MMI) { - await this.driver.waitForSelector(this.mmiPortfolioButton); - } - } - async openSnapListPage(): Promise { console.log('Open account snap page'); - await this.openThreeDotMenu(); + await this.driver.clickElement(this.accountOptionMenu); await this.driver.clickElement(this.accountSnapButton); } async openSettingsPage(): Promise { console.log('Open settings page'); - await this.openThreeDotMenu(); + await this.driver.clickElement(this.accountOptionMenu); + // fix race condition with mmi build + if (process.env.MMI) { + await this.driver.waitForSelector(this.mmiPortfolioButton); + } await this.driver.clickElement(this.settingsButton); } - async clickSwitchNetworkDropDown(): Promise { - console.log(`Click switch network menu`); - await this.driver.clickElement(this.switchNetworkDropDown); - } - - async check_currentSelectedNetwork(networkName: string): Promise { - console.log(`Validate the Switch network to ${networkName}`); - await this.driver.waitForSelector( - `button[data-testid="network-display"][aria-label="Network Menu ${networkName}"]`, - ); - } - /** * Verifies that the displayed account label in header matches the expected label. * diff --git a/test/e2e/page-objects/pages/homepage.ts b/test/e2e/page-objects/pages/homepage.ts index 7c322b0f2cbb..326ecc3188b7 100644 --- a/test/e2e/page-objects/pages/homepage.ts +++ b/test/e2e/page-objects/pages/homepage.ts @@ -11,17 +11,8 @@ class HomePage { private readonly activityTab = '[data-testid="account-overview__activity-tab"]'; - private readonly nftTab = '[data-testid="account-overview__nfts-tab"]'; - - private readonly nftIconOnActivityList = '[data-testid="nft-item"]'; - private readonly balance = '[data-testid="eth-overview__primary-currency"]'; - private readonly basicFunctionalityOffWarningMessage = { - text: 'Basic functionality is off', - css: '.mm-banner-alert', - }; - private readonly completedTransactions = '[data-testid="activity-list-item"]'; private readonly confirmedTransactions = { @@ -69,37 +60,6 @@ class HomePage { await this.driver.clickElement(this.activityTab); } - async check_basicFunctionalityOffWarnigMessageIsDisplayed(): Promise { - console.log( - 'Check if basic functionality off warning message is displayed on homepage', - ); - await this.driver.waitForSelector(this.basicFunctionalityOffWarningMessage); - } - - async goToNFTList(): Promise { - console.log(`Open NFT tab on homepage`); - await this.driver.clickElement(this.nftTab); - } - - async clickNFTIconOnActivityList() { - await this.driver.clickElement(this.nftIconOnActivityList); - } - - /** - * Checks if the toaster message for adding a network is displayed on the homepage. - * - * @param networkName - The name of the network that was added. - */ - async check_addNetworkMessageIsDisplayed(networkName: string): Promise { - console.log( - `Check the toaster message for adding network ${networkName} is displayed on homepage`, - ); - await this.driver.waitForSelector({ - tag: 'h6', - text: `“${networkName}” was successfully added!`, - }); - } - /** * This function checks if the specified number of confirmed transactions are displayed in the activity list on homepage. * It waits up to 10 seconds for the expected number of confirmed transactions to be visible. diff --git a/test/e2e/page-objects/pages/nft-details-page.ts b/test/e2e/page-objects/pages/nft-details-page.ts deleted file mode 100644 index b95477f9024d..000000000000 --- a/test/e2e/page-objects/pages/nft-details-page.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Driver } from '../../webdriver/driver'; - -class NFTDetailsPage { - private driver: Driver; - - private readonly nftSendButton = '[data-testid="nft-send-button"]'; - - constructor(driver: Driver) { - this.driver = driver; - } - - async clickNFTSendButton() { - await this.driver.clickElement(this.nftSendButton); - } -} - -export default NFTDetailsPage; diff --git a/test/e2e/page-objects/pages/onboarding/onboarding-complete-page.ts b/test/e2e/page-objects/pages/onboarding/onboarding-complete-page.ts deleted file mode 100644 index 827f89899bad..000000000000 --- a/test/e2e/page-objects/pages/onboarding/onboarding-complete-page.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { Driver } from '../../../webdriver/driver'; - -class OnboardingCompletePage { - private driver: Driver; - - private readonly congratulationsMessage = { - text: 'Congratulations!', - tag: 'h2', - }; - - private readonly defaultPrivacySettingsButton = { - text: 'Manage default privacy settings', - tag: 'button', - }; - - private readonly installCompleteMessage = { - text: 'Your MetaMask install is complete!', - tag: 'h2', - }; - - private readonly onboardingCompleteDoneButton = - '[data-testid="onboarding-complete-done"]'; - - private readonly pinExtensionDoneButton = - '[data-testid="pin-extension-done"]'; - - private readonly pinExtensionMessage = { - text: 'Click browser extension icon to access it instantly', - tag: 'p', - }; - - private readonly pinExtensionNextButton = - '[data-testid="pin-extension-next"]'; - - private readonly walletReadyMessage = { - text: 'Your wallet is ready', - tag: 'h2', - }; - - constructor(driver: Driver) { - this.driver = driver; - } - - async check_pageIsLoaded(): Promise { - try { - await this.driver.waitForMultipleSelectors([ - this.defaultPrivacySettingsButton, - this.onboardingCompleteDoneButton, - ]); - } catch (e) { - console.log( - 'Timeout while waiting for onboarding wallet creation complete page to be loaded', - e, - ); - throw e; - } - console.log('Onboarding wallet creation complete page is loaded'); - } - - async clickCreateWalletDoneButton(): Promise { - await this.driver.clickElementAndWaitToDisappear( - this.onboardingCompleteDoneButton, - ); - } - - async completeOnboarding(): Promise { - console.log('Complete onboarding'); - await this.clickCreateWalletDoneButton(); - await this.driver.waitForSelector(this.installCompleteMessage); - await this.driver.clickElement(this.pinExtensionNextButton); - - // Wait until the onboarding carousel has stopped moving otherwise the click has no effect. - await this.driver.waitForSelector(this.pinExtensionMessage); - await this.driver.waitForElementToStopMoving(this.pinExtensionDoneButton); - await this.driver.clickElementAndWaitToDisappear( - this.pinExtensionDoneButton, - ); - } - - async navigateToDefaultPrivacySettings(): Promise { - await this.driver.clickElementAndWaitToDisappear( - this.defaultPrivacySettingsButton, - ); - } - - async check_congratulationsMessageIsDisplayed(): Promise { - await this.driver.waitForSelector(this.congratulationsMessage); - } - - async check_walletReadyMessageIsDisplayed(): Promise { - await this.driver.waitForSelector(this.walletReadyMessage); - } -} - -export default OnboardingCompletePage; diff --git a/test/e2e/page-objects/pages/onboarding/onboarding-metrics-page.ts b/test/e2e/page-objects/pages/onboarding/onboarding-metrics-page.ts deleted file mode 100644 index 2982acaa40c0..000000000000 --- a/test/e2e/page-objects/pages/onboarding/onboarding-metrics-page.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { Driver } from '../../../webdriver/driver'; - -class OnboardingMetricsPage { - private driver: Driver; - - private readonly metametricsMessage = { - text: 'Help us improve MetaMask', - tag: 'h2', - }; - - private readonly noThanksButton = '[data-testid="metametrics-no-thanks"]'; - - constructor(driver: Driver) { - this.driver = driver; - } - - async check_pageIsLoaded(): Promise { - try { - await this.driver.waitForMultipleSelectors([ - this.metametricsMessage, - this.noThanksButton, - ]); - } catch (e) { - console.log( - 'Timeout while waiting for onboarding metametrics page to be loaded', - e, - ); - throw e; - } - console.log('Onboarding metametrics page is loaded'); - } - - async clickNoThanksButton(): Promise { - await this.driver.clickElementAndWaitToDisappear(this.noThanksButton); - } -} - -export default OnboardingMetricsPage; diff --git a/test/e2e/page-objects/pages/onboarding/onboarding-password-page.ts b/test/e2e/page-objects/pages/onboarding/onboarding-password-page.ts deleted file mode 100644 index 81c2d21aceb6..000000000000 --- a/test/e2e/page-objects/pages/onboarding/onboarding-password-page.ts +++ /dev/null @@ -1,112 +0,0 @@ -import { strict as assert } from 'assert'; -import { Driver } from '../../../webdriver/driver'; -import { WALLET_PASSWORD } from '../../../helpers'; - -class OnboardingPasswordPage { - private driver: Driver; - - private readonly confirmPasswordInput = - '[data-testid="create-password-confirm"]'; - - private readonly createPasswordMessage = { - text: 'Create password', - tag: 'h2', - }; - - private readonly createWalletButton = - '[data-testid="create-password-wallet"]'; - - private readonly importWalletButton = - '[data-testid="create-password-import"]'; - - private readonly incorrectPasswordWarningMessage = { - text: "Passwords don't match", - tag: 'h6', - }; - - private readonly newPasswordInput = '[data-testid="create-password-new"]'; - - private readonly passwordTerms = '[data-testid="create-password-terms"]'; - - constructor(driver: Driver) { - this.driver = driver; - } - - async check_pageIsLoaded(): Promise { - try { - await this.driver.waitForMultipleSelectors([ - this.createPasswordMessage, - this.newPasswordInput, - this.confirmPasswordInput, - ]); - } catch (e) { - console.log( - 'Timeout while waiting for create password page to be loaded', - e, - ); - throw e; - } - console.log('Onboarding password page is loaded'); - } - - /** - * Create a password for new imported wallet - * - * @param newPassword - The new password to create. Defaults to WALLET_PASSWORD. - * @param confirmPassword - The confirm password to create. Defaults to WALLET_PASSWORD. - */ - async createImportedWalletPassword( - newPassword: string = WALLET_PASSWORD, - confirmPassword: string = WALLET_PASSWORD, - ): Promise { - console.log('Create password for new imported wallet'); - await this.fillWalletPassword(newPassword, confirmPassword); - await this.driver.clickElementAndWaitToDisappear(this.importWalletButton); - } - - /** - * Create a password for new created wallet - * - * @param newPassword - The new password to create. Defaults to WALLET_PASSWORD. - * @param confirmPassword - The confirm password to create. Defaults to WALLET_PASSWORD. - */ - async createWalletPassword( - newPassword: string = WALLET_PASSWORD, - confirmPassword: string = WALLET_PASSWORD, - ): Promise { - console.log('Create password for new created wallet'); - await this.fillWalletPassword(newPassword, confirmPassword); - await this.driver.clickElementAndWaitToDisappear(this.createWalletButton); - } - - /** - * Fill the wallet password fields - * - * @param newPassword - The new password to fill. - * @param confirmPassword - The confirm password to fill. - */ - async fillWalletPassword( - newPassword: string, - confirmPassword: string, - ): Promise { - console.log('Fill the wallet password fields'); - await this.driver.fill(this.newPasswordInput, newPassword); - await this.driver.fill(this.confirmPasswordInput, confirmPassword); - await this.driver.clickElement(this.passwordTerms); - } - - async check_confirmPasswordButtonIsDisabled(): Promise { - console.log('Check the confirm password button is disabled'); - const confirmPasswordButton = await this.driver.findElement( - this.createWalletButton, - ); - assert.equal(await confirmPasswordButton.isEnabled(), false); - } - - async check_incorrectPasswordWarningMessageIsDisplayed(): Promise { - console.log('Check the incorrect password warning message is displayed'); - await this.driver.waitForSelector(this.incorrectPasswordWarningMessage); - } -} - -export default OnboardingPasswordPage; diff --git a/test/e2e/page-objects/pages/onboarding/onboarding-privacy-settings-page.ts b/test/e2e/page-objects/pages/onboarding/onboarding-privacy-settings-page.ts deleted file mode 100644 index dac2ab447710..000000000000 --- a/test/e2e/page-objects/pages/onboarding/onboarding-privacy-settings-page.ts +++ /dev/null @@ -1,194 +0,0 @@ -import { Driver } from '../../../webdriver/driver'; - -class OnboardingPrivacySettingsPage { - private driver: Driver; - - private readonly assetsSettings = '[data-testid="category-item-Assets"]'; - - private readonly categoryBackButton = '[data-testid="category-back-button"]'; - - private readonly generalSettings = '[data-testid="category-item-General"]'; - - private readonly privacySettingsBackButton = - '[data-testid="privacy-settings-back-button"]'; - - private readonly securitySettings = '[data-testid="category-item-Security"]'; - - // General settings - private readonly basicFunctionalityCheckbox = - '[id="basic-configuration-checkbox"]'; - - private readonly basicFunctionalityToggle = - '[data-testid="basic-functionality-toggle"] .toggle-button'; - - private readonly basicFunctionalityTurnOffButton = { - text: 'Turn off', - tag: 'button', - }; - - private readonly basicFunctionalityTurnOffMessage = { - text: 'Turn off basic functionality', - tag: 'h4', - }; - - private readonly generalSettingsMessage = { text: 'General', tag: 'h2' }; - - // General settings - add custom network section - private readonly addCustomNetworkButton = { - text: 'Add a network', - tag: 'p', - }; - - private readonly addCustomNetworkFormMessage = { - text: 'Add a custom network', - tag: 'h4', - }; - - private readonly addRpcUrlButton = { - text: 'Add RPC URL', - tag: 'button', - }; - - private readonly addRpcUrlDialogMessage = { - text: 'Add RPC URL', - tag: 'h4', - }; - - private readonly addRpcUrlDropDown = '[data-testid="test-add-rpc-drop-down"]'; - - private readonly chainIdInput = '[data-testid="network-form-chain-id"]'; - - private readonly confirmAddCustomNetworkButton = { - text: 'Save', - tag: 'button', - }; - - private readonly confirmAddRpcUrlButton = { - text: 'Add URL', - tag: 'button', - }; - - private readonly currencySymbolInput = - '[data-testid="network-form-ticker-input"]'; - - private readonly networkNameInput = - '[data-testid="network-form-network-name"]'; - - private readonly rpcUrlInput = '[data-testid="rpc-url-input-test"]'; - - // Assets settings - private readonly assetsPrivacyToggle = '.toggle-button.toggle-button--on'; - - private readonly assetsSettingsMessage = { text: 'Assets', tag: 'h2' }; - - constructor(driver: Driver) { - this.driver = driver; - } - - async check_pageIsLoaded(): Promise { - try { - await this.driver.waitForMultipleSelectors([ - this.generalSettings, - this.assetsSettings, - this.securitySettings, - ]); - } catch (e) { - console.log( - 'Timeout while waiting for onboarding privacy settings page to be loaded', - e, - ); - throw e; - } - console.log('Onboarding privacy settings page is loaded'); - } - - /** - * Adds a custom network to MetaMask during the onboarding process. - * - * @param networkName - The name of the custom network. - * @param chainId - The chain ID of the custom network. - * @param currencySymbol - The currency symbol for the custom network. - * @param networkUrl - The RPC URL for the custom network. - * @returns A promise that resolves when the custom network has been added. - */ - async addCustomNetwork( - networkName: string, - chainId: number, - currencySymbol: string, - networkUrl: string, - ): Promise { - await this.navigateToGeneralSettings(); - console.log('Adding custom network'); - await this.driver.clickElement(this.addCustomNetworkButton); - await this.driver.waitForSelector(this.addCustomNetworkFormMessage); - await this.driver.fill(this.networkNameInput, networkName); - await this.driver.fill(this.chainIdInput, chainId.toString()); - await this.driver.fill(this.currencySymbolInput, currencySymbol); - // Add rpc url - await this.driver.clickElement(this.addRpcUrlDropDown); - await this.driver.clickElement(this.addRpcUrlButton); - await this.driver.waitForSelector(this.addRpcUrlDialogMessage); - await this.driver.fill(this.rpcUrlInput, networkUrl); - await this.driver.clickElement(this.confirmAddRpcUrlButton); - await this.driver.clickElementAndWaitToDisappear( - this.confirmAddCustomNetworkButton, - ); - // Navigate back to default privacy settings - await this.driver.clickElement(this.categoryBackButton); - await this.driver.waitForElementToStopMoving(this.categoryBackButton); - } - - /** - * Navigate back to the onboarding complete page. - */ - async navigateBackToOnboardingCompletePage(): Promise { - console.log('Navigate back to onboarding complete page'); - // Wait until the onboarding carousel has stopped moving otherwise the click has no effect. - await this.driver.waitForElementToStopMoving( - this.privacySettingsBackButton, - ); - await this.driver.clickElementAndWaitToDisappear( - this.privacySettingsBackButton, - ); - } - - async navigateToGeneralSettings(): Promise { - console.log('Navigate to general settings'); - await this.check_pageIsLoaded(); - await this.driver.clickElement(this.generalSettings); - await this.driver.waitForSelector(this.generalSettingsMessage); - } - - /** - * Go to assets settings and toggle options, then navigate back. - */ - async toggleAssetsSettings(): Promise { - console.log('Toggle advanced assets settings in privacy settings'); - await this.check_pageIsLoaded(); - await this.driver.clickElement(this.assetsSettings); - await this.driver.waitForSelector(this.assetsSettingsMessage); - await Promise.all( - ( - await this.driver.findClickableElements(this.assetsPrivacyToggle) - ).map((toggle) => toggle.click()), - ); - await this.driver.clickElement(this.categoryBackButton); - await this.driver.waitForElementToStopMoving(this.categoryBackButton); - } - - /** - * Go to general settings and toggle options, then navigate back. - */ - async toggleBasicFunctionalitySettings(): Promise { - console.log('Toggle basic functionality settings in privacy settings'); - await this.navigateToGeneralSettings(); - await this.driver.clickElement(this.basicFunctionalityToggle); - await this.driver.waitForSelector(this.basicFunctionalityTurnOffMessage); - await this.driver.clickElement(this.basicFunctionalityCheckbox); - await this.driver.clickElement(this.basicFunctionalityTurnOffButton); - await this.driver.clickElement(this.categoryBackButton); - await this.driver.waitForElementToStopMoving(this.categoryBackButton); - } -} - -export default OnboardingPrivacySettingsPage; diff --git a/test/e2e/page-objects/pages/onboarding/onboarding-srp-page.ts b/test/e2e/page-objects/pages/onboarding/onboarding-srp-page.ts deleted file mode 100644 index da3e74153c67..000000000000 --- a/test/e2e/page-objects/pages/onboarding/onboarding-srp-page.ts +++ /dev/null @@ -1,105 +0,0 @@ -import { strict as assert } from 'assert'; -import { Driver } from '../../../webdriver/driver'; -import { TEST_SEED_PHRASE } from '../../../helpers'; - -class OnboardingSrpPage { - private driver: Driver; - - private readonly srpConfirmButton = '[data-testid="import-srp-confirm"]'; - - private readonly srpDropdown = '.import-srp__number-of-words-dropdown'; - - private readonly srpDropdownOptions = - '.import-srp__number-of-words-dropdown option'; - - private readonly srpMessage = { - text: 'Access your wallet with your Secret Recovery Phrase', - tag: 'h2', - }; - - private readonly srpWord0 = '[data-testid="import-srp__srp-word-0"]'; - - private readonly srpWords = '.import-srp__srp-word'; - - private readonly wrongSrpWarningMessage = { - text: 'Invalid Secret Recovery Phrase', - css: '.import-srp__banner-alert-text', - }; - - constructor(driver: Driver) { - this.driver = driver; - } - - async check_pageIsLoaded(): Promise { - try { - await this.driver.waitForMultipleSelectors([ - this.srpMessage, - this.srpWord0, - ]); - } catch (e) { - console.log( - 'Timeout while waiting for onboarding srp page to be loaded', - e, - ); - throw e; - } - console.log('Onboarding srp page is loaded'); - } - - async clickConfirmButton(): Promise { - await this.driver.clickElementAndWaitToDisappear(this.srpConfirmButton); - } - - /** - * Fill the SRP words with the provided seed phrase - * - * @param seedPhrase - The seed phrase to fill. Defaults to TEST_SEED_PHRASE. - */ - async fillSrp(seedPhrase: string = TEST_SEED_PHRASE): Promise { - await this.driver.pasteIntoField(this.srpWord0, seedPhrase); - } - - async check_confirmSrpButtonIsDisabled(): Promise { - console.log('Check that confirm SRP button is disabled'); - const confirmSeedPhrase = await this.driver.findElement( - this.srpConfirmButton, - ); - assert.equal(await confirmSeedPhrase.isEnabled(), false); - } - - /** - * Check the SRP dropdown iterates through each option - * - * @param numOptions - The number of options to check. Defaults to 5. - */ - async check_srpDropdownIterations(numOptions: number = 5) { - console.log( - `Check the SRP dropdown iterates through ${numOptions} options`, - ); - await this.driver.clickElement(this.srpDropdown); - await this.driver.wait(async () => { - const options = await this.driver.findElements(this.srpDropdownOptions); - return options.length === numOptions; - }, this.driver.timeout); - - const options = await this.driver.findElements(this.srpDropdownOptions); - for (let i = 0; i < options.length; i++) { - if (i !== 0) { - await this.driver.clickElement(this.srpDropdown); - } - await options[i].click(); - const expectedNumFields = 12 + i * 3; - await this.driver.wait(async () => { - const srpWordsFields = await this.driver.findElements(this.srpWords); - return expectedNumFields === srpWordsFields.length; - }, this.driver.timeout); - } - } - - async check_wrongSrpWarningMessage(): Promise { - console.log('Check that wrong SRP warning message is displayed'); - await this.driver.waitForSelector(this.wrongSrpWarningMessage); - } -} - -export default OnboardingSrpPage; diff --git a/test/e2e/page-objects/pages/onboarding/secure-wallet-page.ts b/test/e2e/page-objects/pages/onboarding/secure-wallet-page.ts deleted file mode 100644 index cff7549a0f75..000000000000 --- a/test/e2e/page-objects/pages/onboarding/secure-wallet-page.ts +++ /dev/null @@ -1,107 +0,0 @@ -import { Driver } from '../../../webdriver/driver'; - -class SecureWalletPage { - private driver: Driver; - - private readonly confirmRecoveryPhraseButton = - '[data-testid="recovery-phrase-confirm"]'; - - private readonly confirmSecretRecoveryPhraseMessage = { - text: 'Confirm Secret Recovery Phrase', - tag: 'h2', - }; - - private readonly recoveryPhraseChips = - '[data-testid="recovery-phrase-chips"]'; - - private readonly recoveryPhraseInputIndex2 = - '[data-testid="recovery-phrase-input-2"]'; - - private readonly recoveryPhraseInputIndex3 = - '[data-testid="recovery-phrase-input-3"]'; - - private readonly recoveryPhraseInputIndex7 = - '[data-testid="recovery-phrase-input-7"]'; - - private readonly recoveryPhraseNextButton = - '[data-testid="recovery-phrase-next"]'; - - private readonly revealSecretRecoveryPhraseButton = - '[data-testid="recovery-phrase-reveal"]'; - - private readonly secureWalletButton = - '[data-testid="secure-wallet-recommended"]'; - - private readonly secureWalletLaterButton = - '[data-testid="secure-wallet-later"]'; - - private readonly secureWalletMessage = { - text: 'Secure your wallet', - tag: 'h2', - }; - - private readonly writeDownSecretRecoveryPhraseMessage = { - text: 'Write down your Secret Recovery Phrase', - tag: 'h2', - }; - - constructor(driver: Driver) { - this.driver = driver; - } - - async check_pageIsLoaded(): Promise { - try { - await this.driver.waitForMultipleSelectors([ - this.secureWalletMessage, - this.secureWalletButton, - this.secureWalletLaterButton, - ]); - } catch (e) { - console.log( - 'Timeout while waiting for secure wallet page to be loaded', - e, - ); - throw e; - } - console.log('Secure wallet page is loaded'); - } - - async revealAndConfirmSRP(): Promise { - console.log( - 'Reveal and confirm SRP on secure wallet page during onboarding', - ); - // click secure my wallet button to reveal SRP - await this.driver.clickElement(this.secureWalletButton); - await this.driver.waitForMultipleSelectors([ - this.writeDownSecretRecoveryPhraseMessage, - this.revealSecretRecoveryPhraseButton, - ]); - - // click reveal button to reveal SRP - await this.driver.clickElement(this.revealSecretRecoveryPhraseButton); - await this.driver.waitForSelector(this.recoveryPhraseChips); - - let finalWords: string[] = []; - await this.driver.wait(async () => { - const recoveryPhraseChips = await this.driver.findElement( - this.recoveryPhraseChips, - ); - const recoveryPhrase = await recoveryPhraseChips.getText(); - const words = recoveryPhrase.split(/\s*(?:[0-9)]+|\n|\.|^$|$)\s*/u); - finalWords = words.filter((str) => str !== ''); - return finalWords.length === 12; - }, this.driver.timeout); - await this.driver.clickElement(this.recoveryPhraseNextButton); - - // confirm SRP - await this.driver.waitForSelector(this.confirmSecretRecoveryPhraseMessage); - await this.driver.fill(this.recoveryPhraseInputIndex2, finalWords[2]); - await this.driver.fill(this.recoveryPhraseInputIndex3, finalWords[3]); - await this.driver.fill(this.recoveryPhraseInputIndex7, finalWords[7]); - await this.driver.clickElementAndWaitToDisappear( - this.confirmRecoveryPhraseButton, - ); - } -} - -export default SecureWalletPage; diff --git a/test/e2e/page-objects/pages/onboarding/start-onboarding-page.ts b/test/e2e/page-objects/pages/onboarding/start-onboarding-page.ts deleted file mode 100644 index 47c0b53a2b2f..000000000000 --- a/test/e2e/page-objects/pages/onboarding/start-onboarding-page.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { Driver } from '../../../webdriver/driver'; - -class StartOnboardingPage { - private driver: Driver; - - private readonly createWalletButton = - '[data-testid="onboarding-create-wallet"]'; - - private readonly importWalletButton = - '[data-testid="onboarding-import-wallet"]'; - - private readonly startMessage = { - text: "Let's get started", - tag: 'h2', - }; - - private readonly termsCheckbox = '[data-testid="onboarding-terms-checkbox"]'; - - constructor(driver: Driver) { - this.driver = driver; - } - - async check_pageIsLoaded(): Promise { - try { - await this.driver.waitForMultipleSelectors([ - this.startMessage, - this.termsCheckbox, - ]); - } catch (e) { - console.log( - 'Timeout while waiting for start onboarding page to be loaded', - e, - ); - throw e; - } - console.log('Start onboarding page is loaded'); - } - - async checkTermsCheckbox(): Promise { - await this.driver.clickElement(this.termsCheckbox); - } - - async clickCreateWalletButton(): Promise { - await this.driver.clickElementAndWaitToDisappear(this.createWalletButton); - } - - async clickImportWalletButton(): Promise { - await this.driver.clickElementAndWaitToDisappear(this.importWalletButton); - } -} - -export default StartOnboardingPage; diff --git a/test/e2e/page-objects/pages/send/send-token-page.ts b/test/e2e/page-objects/pages/send/send-token-page.ts index 60ffd86c6cdd..728afbfdd4df 100644 --- a/test/e2e/page-objects/pages/send/send-token-page.ts +++ b/test/e2e/page-objects/pages/send/send-token-page.ts @@ -9,8 +9,6 @@ class SendTokenPage { private inputAmount: string; - private inputNFTAmount: string; - private scanButton: string; private continueButton: object; @@ -28,7 +26,6 @@ class SendTokenPage { constructor(driver: Driver) { this.driver = driver; this.inputAmount = '[data-testid="currency-input"]'; - this.inputNFTAmount = '[data-testid="nft-input"]'; this.inputRecipient = '[data-testid="ens-input"]'; this.scanButton = '[data-testid="ens-qr-scan-button"]'; this.ensResolvedName = @@ -82,10 +79,6 @@ class SendTokenPage { ); } - async fillNFTAmount(amount: string) { - await this.driver.pasteIntoField(this.inputNFTAmount, amount); - } - async goToNextScreen(): Promise { await this.driver.clickElement(this.continueButton); } diff --git a/test/e2e/page-objects/pages/test-dapp.ts b/test/e2e/page-objects/pages/test-dapp.ts index afff2f37e57e..b9487ee599b9 100644 --- a/test/e2e/page-objects/pages/test-dapp.ts +++ b/test/e2e/page-objects/pages/test-dapp.ts @@ -34,20 +34,6 @@ class TestDapp { tag: 'button', }; - private readonly simpleSendButton = '#sendButton'; - - private readonly erc721MintButton = '#mintButton'; - - private readonly erc721TransferFromButton = '#transferFromButton'; - - private readonly erc1155TokenIDInput = '#batchMintTokenIds'; - - private readonly erc1155TokenAmountInput = '#batchMintIdAmounts'; - - private readonly erc1155MintButton = '#batchMintButton'; - - private readonly erc1155WatchButton = '#watchAssetButton'; - private readonly erc1155RevokeSetApprovalForAllButton = '#revokeERC1155Button'; @@ -188,34 +174,6 @@ class TestDapp { }); } - async clickSimpleSendButton() { - await this.driver.clickElement(this.simpleSendButton); - } - - async clickERC721MintButton() { - await this.driver.clickElement(this.erc721MintButton); - } - - async clickERC721TransferFromButton() { - await this.driver.clickElement(this.erc721TransferFromButton); - } - - async fillERC1155TokenID(tokenID: string) { - await this.driver.pasteIntoField(this.erc1155TokenIDInput, tokenID); - } - - async fillERC1155TokenAmount(amount: string) { - await this.driver.pasteIntoField(this.erc1155TokenAmountInput, amount); - } - - async clickERC1155MintButton() { - await this.driver.clickElement(this.erc1155MintButton); - } - - async clickERC1155WatchButton() { - await this.driver.clickElement(this.erc1155WatchButton); - } - async clickERC721SetApprovalForAllButton() { await this.driver.clickElement(this.erc721SetApprovalForAllButton); } diff --git a/test/e2e/snaps/test-snap-bip-32.spec.js b/test/e2e/snaps/test-snap-bip-32.spec.js index d52b92f0fe97..59a9c14833e8 100644 --- a/test/e2e/snaps/test-snap-bip-32.spec.js +++ b/test/e2e/snaps/test-snap-bip-32.spec.js @@ -2,6 +2,7 @@ const { defaultGanacheOptions, withFixtures, unlockWallet, + switchToNotificationWindow, WINDOW_TITLES, } = require('../helpers'); const FixtureBuilder = require('../fixture-builder'); @@ -27,19 +28,14 @@ describe('Test Snap bip-32', function () { tag: 'h2', }); - // find and scroll to the bip32 snap + // find and scroll to the bip32 test and connect const snapButton1 = await driver.findElement('#connectbip32'); await driver.scrollToElement(snapButton1); - - // added delay for firefox (deflake) - await driver.delayFirefox(3000); - - // wait for and click connect to bip-32 await driver.waitForSelector('#connectbip32'); await driver.clickElement('#connectbip32'); // switch to metamask extension and click connect - await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); + await switchToNotificationWindow(driver, 2); await driver.waitForSelector({ text: 'Connect', tag: 'button', @@ -49,13 +45,10 @@ describe('Test Snap bip-32', function () { tag: 'button', }); - // wait for confirm to appear await driver.waitForSelector({ text: 'Confirm' }); - // click and dismiss possible scroll element await driver.clickElementSafe('[data-testid="snap-install-scroll"]'); - // click confirm await driver.clickElement({ text: 'Confirm', tag: 'button', @@ -71,9 +64,8 @@ describe('Test Snap bip-32', function () { '[data-testid="snap-install-warning-modal-confirm"]', ); - // wait for and click OK and wait for window to close await driver.waitForSelector({ text: 'OK' }); - await driver.clickElementAndWaitForWindowToClose({ + await driver.clickElement({ text: 'OK', tag: 'button', }); @@ -111,15 +103,20 @@ describe('Test Snap bip-32', function () { await driver.fill('#bip32Message-secp256k1', 'foo bar'); await driver.clickElement('#sendBip32-secp256k1'); - // hit 'approve' on the signature confirmation and wait for window to close - await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - await driver.clickElementAndWaitForWindowToClose({ + // hit 'approve' on the signature confirmation + await switchToNotificationWindow(driver, 2); + await driver.clickElement({ text: 'Approve', tag: 'button', }); // switch back to the test-snaps window - await driver.switchToWindowWithTitle(WINDOW_TITLES.TestSnaps); + let windowHandles = await driver.waitUntilXWindowHandles( + 1, + 1000, + 10000, + ); + await driver.switchToWindow(windowHandles[0]); // check results of the secp256k1 signature with waitForSelector await driver.waitForSelector({ @@ -136,21 +133,15 @@ describe('Test Snap bip-32', function () { await driver.fill('#bip32Message-ed25519', 'foo bar'); await driver.clickElement('#sendBip32-ed25519'); - // switch to dialog window - await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - - // wait for and click 'approve' and wait for window to close - await driver.waitForSelector({ - text: 'Approve', - tag: 'button', - }); - await driver.clickElementAndWaitForWindowToClose({ + // hit 'approve' on the custom confirm + await switchToNotificationWindow(driver, 2); + await driver.clickElement({ text: 'Approve', tag: 'button', }); - // switch back to test-snaps window - await driver.switchToWindowWithTitle(WINDOW_TITLES.TestSnaps); + windowHandles = await driver.waitUntilXWindowHandles(1, 1000, 10000); + await driver.switchToWindow(windowHandles[0]); // check results of ed25519 signature with waitForSelector await driver.waitForSelector({ diff --git a/test/e2e/snaps/test-snap-bip-44.spec.js b/test/e2e/snaps/test-snap-bip-44.spec.js index 8efb626045a8..ef70e6fc193a 100644 --- a/test/e2e/snaps/test-snap-bip-44.spec.js +++ b/test/e2e/snaps/test-snap-bip-44.spec.js @@ -2,6 +2,7 @@ const { defaultGanacheOptions, withFixtures, unlockWallet, + switchToNotificationWindow, WINDOW_TITLES, } = require('../helpers'); const FixtureBuilder = require('../fixture-builder'); @@ -27,19 +28,14 @@ describe('Test Snap bip-44', function () { tag: 'h2', }); - // find and scroll to the bip44 snap + // find and scroll to the bip44 test and connect const snapButton1 = await driver.findElement('#connectbip44'); await driver.scrollToElement(snapButton1); - - // added delay for firefox (deflake) - await driver.delayFirefox(1000); - - // wait for and click connect await driver.waitForSelector('#connectbip44'); await driver.clickElement('#connectbip44'); // switch to metamask extension and click connect and approve - await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); + await switchToNotificationWindow(driver, 2); await driver.clickElement({ text: 'Connect', tag: 'button', @@ -63,9 +59,9 @@ describe('Test Snap bip-44', function () { '[data-testid="snap-install-warning-modal-confirm"]', ); - // wait for and click ok and wait for window to close + // deal with OK button await driver.waitForSelector({ text: 'OK' }); - await driver.clickElementAndWaitForWindowToClose({ + await driver.clickElement({ text: 'OK', tag: 'button', }); @@ -95,21 +91,20 @@ describe('Test Snap bip-44', function () { await driver.waitForSelector('#signBip44Message'); await driver.clickElement('#signBip44Message'); - // Switch to approve signature message window - await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - - // wait for and click approve and wait for window to close - await driver.waitForSelector({ - text: 'Approve', - tag: 'button', - }); - await driver.clickElementAndWaitForWindowToClose({ + // Switch to approve signature message window and approve + await switchToNotificationWindow(driver, 2); + await driver.clickElement({ text: 'Approve', tag: 'button', }); // switch back to test-snaps page - await driver.switchToWindowWithTitle(WINDOW_TITLES.TestSnaps); + const windowHandles = await driver.waitUntilXWindowHandles( + 1, + 1000, + 10000, + ); + await driver.switchToWindow(windowHandles[0]); // check the results of the message signature using waitForSelector await driver.waitForSelector({ diff --git a/test/e2e/snaps/test-snap-clientstatus.spec.js b/test/e2e/snaps/test-snap-clientstatus.spec.js index 2057f7499546..edcc48b9b137 100644 --- a/test/e2e/snaps/test-snap-clientstatus.spec.js +++ b/test/e2e/snaps/test-snap-clientstatus.spec.js @@ -2,6 +2,7 @@ const { defaultGanacheOptions, withFixtures, unlockWallet, + switchToNotificationWindow, WINDOW_TITLES, } = require('../helpers'); const FixtureBuilder = require('../fixture-builder'); @@ -27,40 +28,28 @@ describe('Test Snap Client Status', function () { tag: 'h2', }); - // scroll to and click connect to client-status snap const snapButton = await driver.findElement('#connectclient-status'); await driver.scrollToElement(snapButton); - - // added delay for firefox (deflake) - await driver.delayFirefox(1000); - - // wait for and click connect - await driver.waitForSelector('#connectclient-status'); + await driver.delay(1000); await driver.clickElement('#connectclient-status'); - // switch to metamask extension - await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - - // wait for and click connect - await driver.waitForSelector({ - text: 'Connect', - tag: 'button', - }); + // switch to metamask extension and click connect + await switchToNotificationWindow(driver); await driver.clickElement({ text: 'Connect', tag: 'button', }); - // wait for and click confirm await driver.waitForSelector({ text: 'Confirm' }); + await driver.clickElement({ text: 'Confirm', tag: 'button', }); - // wait for and click ok and wait for window to close await driver.waitForSelector({ text: 'OK' }); - await driver.clickElementAndWaitForWindowToClose({ + + await driver.clickElement({ text: 'OK', tag: 'button', }); @@ -88,7 +77,7 @@ describe('Test Snap Client Status', function () { WINDOW_TITLES.ExtensionInFullScreenView, ); - // wait for and click on the global action menu + // click on the global action menu await driver.waitForSelector( '[data-testid="account-options-menu-button"]', ); diff --git a/test/e2e/snaps/test-snap-cronjob.spec.js b/test/e2e/snaps/test-snap-cronjob.spec.js index 6f4e05883943..b19de4246ac3 100644 --- a/test/e2e/snaps/test-snap-cronjob.spec.js +++ b/test/e2e/snaps/test-snap-cronjob.spec.js @@ -2,6 +2,7 @@ const { defaultGanacheOptions, withFixtures, unlockWallet, + switchToNotificationWindow, WINDOW_TITLES, } = require('../helpers'); const FixtureBuilder = require('../fixture-builder'); @@ -27,40 +28,28 @@ describe('Test Snap Cronjob', function () { tag: 'h2', }); - // scroll to and connect to cronjobs snap const snapButton = await driver.findElement('#connectcronjobs'); await driver.scrollToElement(snapButton); - - // added delay for firefox (deflake) - await driver.delayFirefox(1000); - - // wait for and click connect - await driver.waitForSelector('#connectcronjobs'); + await driver.delay(500); await driver.clickElement('#connectcronjobs'); - // switch to metamask extension - await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - - // wait for and click connect - await driver.waitForSelector({ - text: 'Connect', - tag: 'button', - }); + // switch to metamask extension and click connect + await switchToNotificationWindow(driver); await driver.clickElement({ text: 'Connect', tag: 'button', }); - // wait for and click confirm await driver.waitForSelector({ text: 'Confirm' }); + await driver.clickElement({ text: 'Confirm', tag: 'button', }); - // wait for and click ok and wait for window to close await driver.waitForSelector({ text: 'OK' }); - await driver.clickElementAndWaitForWindowToClose({ + + await driver.clickElement({ text: 'OK', tag: 'button', }); @@ -75,7 +64,7 @@ describe('Test Snap Cronjob', function () { }); // switch to dialog popup, wait for a maximum of 65 seconds - await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); + await switchToNotificationWindow(driver); // look for the dialog popup to verify cronjob fired await driver.waitForSelector({ @@ -83,8 +72,8 @@ describe('Test Snap Cronjob', function () { text: 'This dialog was triggered by a cronjob', }); - // try to click on the Ok button and pass test if window closes - await driver.clickElementAndWaitForWindowToClose({ + // try to click on the Ok button and pass test if it works + await driver.clickElement({ text: 'OK', tag: 'button', }); diff --git a/test/e2e/snaps/test-snap-dialog.spec.js b/test/e2e/snaps/test-snap-dialog.spec.js index 36c3965aaca3..3b82781321da 100644 --- a/test/e2e/snaps/test-snap-dialog.spec.js +++ b/test/e2e/snaps/test-snap-dialog.spec.js @@ -27,39 +27,27 @@ describe('Test Snap Dialog', function () { tag: 'h2', }); - // scroll to connect dialogs snap const dialogButton = await driver.findElement('#connectdialogs'); await driver.scrollToElement(dialogButton); - - // added delay for firefox (deflake) - await driver.delayFirefox(1000); - - // wait for and click connect - await driver.waitForSelector('#connectdialogs'); + await driver.delay(500); await driver.clickElement('#connectdialogs'); - // switch to metamask extension + // switch to metamask extension and click connect await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - - // wait for and click connect - await driver.waitForSelector({ - text: 'Connect', - tag: 'button', - }); await driver.clickElement({ text: 'Connect', tag: 'button', }); - // wait for and click confirm await driver.waitForSelector({ text: 'Confirm' }); + await driver.clickElement({ text: 'Confirm', tag: 'button', }); - // wait for and click ok and wait for window to close await driver.waitForSelector({ text: 'OK' }); + await driver.clickElementAndWaitForWindowToClose({ text: 'OK', tag: 'button', @@ -89,7 +77,7 @@ describe('Test Snap Dialog', function () { text: 'It has a single button: "OK"', }); - // click ok button and wait for window to close + // click ok button await driver.clickElementAndWaitForWindowToClose({ text: 'OK', tag: 'button', @@ -111,7 +99,7 @@ describe('Test Snap Dialog', function () { // switch to dialog popup await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - // click reject and wait for window to close + // click reject await driver.clickElementAndWaitForWindowToClose({ text: 'Reject', tag: 'button', @@ -132,7 +120,7 @@ describe('Test Snap Dialog', function () { // switch to dialog popup await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - // click accept and wait for window to close + // click accept await driver.clickElementAndWaitForWindowToClose({ text: 'Approve', tag: 'button', @@ -154,7 +142,7 @@ describe('Test Snap Dialog', function () { // switch to dialog popup await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - // click cancel button and wait for window to close + // click cancel button await driver.clickElementAndWaitForWindowToClose({ text: 'Cancel', tag: 'button', @@ -178,7 +166,7 @@ describe('Test Snap Dialog', function () { // fill '2323' in form field await driver.pasteIntoField('.mm-input', '2323'); - // click submit button and wait for window to close + // click submit button await driver.clickElementAndWaitForWindowToClose({ text: 'Submit', tag: 'button', @@ -200,7 +188,7 @@ describe('Test Snap Dialog', function () { // switch to dialog popup await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - // click cancel button and wait for window to close + // click cancel button await driver.clickElementAndWaitForWindowToClose({ text: 'Cancel', tag: 'span', @@ -224,7 +212,7 @@ describe('Test Snap Dialog', function () { // fill '2323' in form field await driver.pasteIntoField('#custom-input', '2323'); - // click confirm button and wait for window to close + // click confirm button await driver.clickElementAndWaitForWindowToClose({ text: 'Confirm', tag: 'span', diff --git a/test/e2e/snaps/test-snap-ethprovider.spec.js b/test/e2e/snaps/test-snap-ethprovider.spec.js index 0e5ea68a09eb..feee8c76783a 100644 --- a/test/e2e/snaps/test-snap-ethprovider.spec.js +++ b/test/e2e/snaps/test-snap-ethprovider.spec.js @@ -2,6 +2,7 @@ const { defaultGanacheOptions, withFixtures, unlockWallet, + switchToNotificationWindow, WINDOW_TITLES, } = require('../helpers'); const FixtureBuilder = require('../fixture-builder'); @@ -27,42 +28,30 @@ describe('Test Snap ethereum_provider', function () { tag: 'h2', }); - // scroll to ethereum provider snap const snapButton = await driver.findElement( '#connectethereum-provider', ); await driver.scrollToElement(snapButton); - - // added delay for firefox (deflake) - await driver.delayFirefox(1000); - - // wait for and click connect - await driver.waitForSelector('#connectethereum-provider'); + await driver.delay(1000); await driver.clickElement('#connectethereum-provider'); - // switch to metamask extension - await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - - // wait for and click connect - await driver.waitForSelector({ - text: 'Connect', - tag: 'button', - }); + // switch to metamask extension and click connect + await switchToNotificationWindow(driver, 2); await driver.clickElement({ text: 'Connect', tag: 'button', }); - // wait for and click confirm await driver.waitForSelector({ text: 'Confirm' }); + await driver.clickElement({ text: 'Confirm', tag: 'button', }); - // wait for and click ok and wait for window to close await driver.waitForSelector({ text: 'OK' }); - await driver.clickElementAndWaitForWindowToClose({ + + await driver.clickElement({ text: 'OK', tag: 'button', }); @@ -96,19 +85,17 @@ describe('Test Snap ethereum_provider', function () { await driver.delay(500); await driver.clickElement('#sendEthproviderAccounts'); - // switch to metamask window - await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); + // switch to metamask window and click through confirmations + await switchToNotificationWindow(driver, 2); await driver.clickElement({ text: 'Next', tag: 'button', }); - - // wait for and click confirm and wait for window to close await driver.waitForSelector({ text: 'Confirm', tag: 'button', }); - await driver.clickElementAndWaitForWindowToClose({ + await driver.clickElement({ text: 'Confirm', tag: 'button', }); diff --git a/test/e2e/snaps/test-snap-get-file.spec.js b/test/e2e/snaps/test-snap-get-file.spec.js index 386df0fa56ad..ce7f29fe3bf2 100644 --- a/test/e2e/snaps/test-snap-get-file.spec.js +++ b/test/e2e/snaps/test-snap-get-file.spec.js @@ -2,6 +2,7 @@ const { defaultGanacheOptions, withFixtures, unlockWallet, + switchToNotificationWindow, WINDOW_TITLES, } = require('../helpers'); const FixtureBuilder = require('../fixture-builder'); @@ -27,40 +28,28 @@ describe('Test Snap Get File', function () { tag: 'h2', }); - // scroll to and wait for connect to get file button - const snapButton = await driver.findElement('#connectgetfile'); - await driver.scrollToElement(snapButton); - - // added delay for firefox (deflake) - await driver.delayFirefox(1000); - - // wait for and click connect - await driver.waitForSelector('#connectgetfile'); + const dialogButton = await driver.findElement('#connectgetfile'); + await driver.scrollToElement(dialogButton); + await driver.delay(1000); await driver.clickElement('#connectgetfile'); - // switch to metamask extension - await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - - // wait for and click connect - await driver.waitForSelector({ - text: 'Connect', - tag: 'button', - }); + // switch to metamask extension and click connect + await switchToNotificationWindow(driver); await driver.clickElement({ text: 'Connect', tag: 'button', }); - // wait for and click confirm await driver.waitForSelector({ text: 'Confirm' }); + await driver.clickElement({ text: 'Confirm', tag: 'button', }); - // wait for and click ok and wait for window to close await driver.waitForSelector({ text: 'OK' }); - await driver.clickElementAndWaitForWindowToClose({ + + await driver.clickElement({ text: 'OK', tag: 'button', }); diff --git a/test/e2e/snaps/test-snap-get-locale.spec.js b/test/e2e/snaps/test-snap-get-locale.spec.js index a87d965c6170..9ccd4f3c141f 100644 --- a/test/e2e/snaps/test-snap-get-locale.spec.js +++ b/test/e2e/snaps/test-snap-get-locale.spec.js @@ -2,6 +2,7 @@ const { defaultGanacheOptions, withFixtures, unlockWallet, + switchToNotificationWindow, WINDOW_TITLES, } = require('../helpers'); const FixtureBuilder = require('../fixture-builder'); @@ -27,25 +28,13 @@ describe('Test Snap Get Locale', function () { tag: 'h2', }); - // scroll to dialog snap const dialogButton = await driver.findElement('#connectgetlocale'); await driver.scrollToElement(dialogButton); - - // added delay for firefox (deflake) - await driver.delayFirefox(1000); - - // wait for and click connect - await driver.waitForSelector('#connectgetlocale'); + await driver.delay(1000); await driver.clickElement('#connectgetlocale'); - // switch to metamask extension - await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - - // wait for and click connect - await driver.waitForSelector({ - text: 'Connect', - tag: 'button', - }); + // switch to metamask extension and click connect + await switchToNotificationWindow(driver); await driver.clickElement({ text: 'Connect', tag: 'button', @@ -57,21 +46,18 @@ describe('Test Snap Get Locale', function () { tag: 'p', }); - // wait for confirm await driver.waitForSelector({ text: 'Confirm' }); - // click and dismiss possible scroll element await driver.clickElementSafe('[data-testid="snap-install-scroll"]'); - // click confirm await driver.clickElement({ text: 'Confirm', tag: 'button', }); - // wait for and click ok and wait for window to close await driver.waitForSelector({ text: 'OK' }); - await driver.clickElementAndWaitForWindowToClose({ + + await driver.clickElement({ text: 'OK', tag: 'button', }); diff --git a/test/e2e/snaps/test-snap-getentropy.spec.js b/test/e2e/snaps/test-snap-getentropy.spec.js index b25fcfbc10f6..7815f7329878 100644 --- a/test/e2e/snaps/test-snap-getentropy.spec.js +++ b/test/e2e/snaps/test-snap-getentropy.spec.js @@ -2,6 +2,7 @@ const { defaultGanacheOptions, withFixtures, unlockWallet, + switchToNotificationWindow, WINDOW_TITLES, } = require('../helpers'); const FixtureBuilder = require('../fixture-builder'); @@ -27,45 +28,30 @@ describe('Test Snap getEntropy', function () { tag: 'h2', }); - // scroll to get entropy snap const snapButton = await driver.findElement('#connectGetEntropySnap'); await driver.scrollToElement(snapButton); - - // added delay for firefox (deflake) - await driver.delayFirefox(1000); - - // wait for and click connect - await driver.waitForSelector('#connectGetEntropySnap'); + await driver.delay(1000); await driver.clickElement('#connectGetEntropySnap'); - // switch to metamask extension - await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - - // wait for and click connect - await driver.waitForSelector({ - text: 'Connect', - tag: 'button', - }); + // switch to metamask extension and click connect + await switchToNotificationWindow(driver, 2); await driver.clickElement({ text: 'Connect', tag: 'button', }); - // wait for confirm selector await driver.waitForSelector({ text: 'Confirm' }); - // dismiss possible scroll element await driver.clickElementSafe('[data-testid="snap-install-scroll"]'); - // click confirm button await driver.clickElement({ text: 'Confirm', tag: 'button', }); - // wait for and click ok and wait for window to close await driver.waitForSelector({ text: 'OK' }); - await driver.clickElementAndWaitForWindowToClose({ + + await driver.clickElement({ text: 'OK', tag: 'button', }); @@ -86,15 +72,9 @@ describe('Test Snap getEntropy', function () { await driver.delay(500); await driver.clickElement('#signEntropyMessage'); - // Switch to approve signature message window - await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - - // wait for and click on approve and wait for window to close - await driver.waitForSelector({ - text: 'Approve', - tag: 'button', - }); - await driver.clickElementAndWaitForWindowToClose({ + // Switch to approve signature message window and approve + await switchToNotificationWindow(driver, 2); + await driver.clickElement({ text: 'Approve', tag: 'button', }); diff --git a/test/e2e/snaps/test-snap-homepage.spec.js b/test/e2e/snaps/test-snap-homepage.spec.js index fb5dc575a0de..8f1e851d0a0d 100644 --- a/test/e2e/snaps/test-snap-homepage.spec.js +++ b/test/e2e/snaps/test-snap-homepage.spec.js @@ -2,6 +2,7 @@ const { defaultGanacheOptions, withFixtures, unlockWallet, + switchToNotificationWindow, WINDOW_TITLES, } = require('../helpers'); const FixtureBuilder = require('../fixture-builder'); @@ -27,40 +28,29 @@ describe('Test Snap Homepage', function () { tag: 'h2', }); - // find and scroll to the homepage snap + // find and scroll to the honmepage test and connect const snapButton1 = await driver.findElement('#connecthomepage'); await driver.scrollToElement(snapButton1); - - // added delay for firefox (deflake) - await driver.delayFirefox(1000); - - // wait for and click connect - await driver.waitForSelector('#connecthomepage'); + await driver.delay(1000); await driver.clickElement('#connecthomepage'); - // switch to metamask extension - await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - - // wait for and click connect - await driver.waitForSelector({ - text: 'Connect', - tag: 'button', - }); + // switch to metamask extension and click connect + await switchToNotificationWindow(driver); await driver.clickElement({ text: 'Connect', tag: 'button', }); - // wait for and click confirm await driver.waitForSelector({ text: 'Confirm' }); + await driver.clickElement({ text: 'Confirm', tag: 'button', }); - // wait for and click ok and wait for windows to close await driver.waitForSelector({ text: 'OK' }); - await driver.clickElementAndWaitForWindowToClose({ + + await driver.clickElement({ text: 'OK', tag: 'button', }); diff --git a/test/e2e/snaps/test-snap-installed.spec.js b/test/e2e/snaps/test-snap-installed.spec.js index 11325920c723..5c7a3394966f 100644 --- a/test/e2e/snaps/test-snap-installed.spec.js +++ b/test/e2e/snaps/test-snap-installed.spec.js @@ -2,6 +2,7 @@ const { defaultGanacheOptions, withFixtures, unlockWallet, + switchToNotificationWindow, WINDOW_TITLES, } = require('../helpers'); const FixtureBuilder = require('../fixture-builder'); @@ -32,40 +33,28 @@ describe('Test Snap Installed', function () { tag: 'h2', }); - // scroll to dialogs snap const confirmButton = await driver.findElement('#connectdialogs'); await driver.scrollToElement(confirmButton); - - // added delay for firefox (deflake) - await driver.delayFirefox(1000); - - // wait for and click connect - await driver.waitForSelector('#connectdialogs'); + await driver.delay(1000); await driver.clickElement('#connectdialogs'); - // switch to metamask extension - await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - - // wait for and click connect - await driver.waitForSelector({ - text: 'Connect', - tag: 'button', - }); + // switch to metamask extension and click connect + await switchToNotificationWindow(driver); await driver.clickElement({ text: 'Connect', tag: 'button', }); - // wait for and click confirm await driver.waitForSelector({ text: 'Confirm' }); + await driver.clickElement({ text: 'Confirm', tag: 'button', }); - // wait for and click ok and wait for window to close await driver.waitForSelector({ text: 'OK' }); - await driver.clickElementAndWaitForWindowToClose({ + + await driver.clickElement({ text: 'OK', tag: 'button', }); @@ -82,37 +71,26 @@ describe('Test Snap Installed', function () { // click to connect to errors snap const errorButton = await driver.findElement('#connecterrors'); await driver.scrollToElement(errorButton); - - // added delay for firefox (deflake) - await driver.delayFirefox(1000); - - // wait for and click connect - await driver.waitForSelector('#connecterrors'); + await driver.delay(500); await driver.clickElement('#connecterrors'); - // switch to metamask extension - await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - - // wait for and click connect - await driver.waitForSelector({ - text: 'Connect', - tag: 'button', - }); + // switch to metamask extension and click connect + await switchToNotificationWindow(driver); await driver.clickElement({ text: 'Connect', tag: 'button', }); - // wait for and click confirm await driver.waitForSelector({ text: 'Confirm' }); + await driver.clickElement({ text: 'Confirm', tag: 'button', }); - // wait for and click ok and wait for window to close await driver.waitForSelector({ text: 'OK' }); - await driver.clickElementAndWaitForWindowToClose({ + + await driver.clickElement({ text: 'OK', tag: 'button', }); diff --git a/test/e2e/snaps/test-snap-interactive-ui.spec.js b/test/e2e/snaps/test-snap-interactive-ui.spec.js index a2d00b1348cc..200c1916ddaa 100644 --- a/test/e2e/snaps/test-snap-interactive-ui.spec.js +++ b/test/e2e/snaps/test-snap-interactive-ui.spec.js @@ -21,32 +21,14 @@ describe('Test Snap Interactive UI', function () { // navigate to test snaps page and connect to interactive ui snap await driver.openNewPage(TEST_SNAPS_WEBSITE_URL); - - // wait for page to load - await driver.waitForSelector({ - text: 'Installed Snaps', - tag: 'h2', - }); - - // scroll to interactive-ui snap + await driver.delay(1000); const dialogButton = await driver.findElement('#connectinteractive-ui'); await driver.scrollToElement(dialogButton); - - // added delay for firefox (deflake) - await driver.delayFirefox(1000); - - // wait for and click connect - await driver.waitForSelector('#connectinteractive-ui'); + await driver.delay(1000); await driver.clickElement('#connectinteractive-ui'); - // switch to metamask extension + // switch to metamask extension and click connect await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - - // wait for and click connect - await driver.waitForSelector({ - text: 'Connect', - tag: 'button', - }); await driver.clickElement({ text: 'Connect', tag: 'button', @@ -65,9 +47,9 @@ describe('Test Snap Interactive UI', function () { tag: 'button', }); - // wait for and click OK and wait for window to close + // wait for anc click OK await driver.waitForSelector({ text: 'OK' }); - await driver.clickElementAndWaitForWindowToClose({ + await driver.clickElement({ text: 'OK', tag: 'button', }); @@ -111,10 +93,8 @@ describe('Test Snap Interactive UI', function () { await driver.waitForSelector({ text: 'option3', tag: 'p' }); await driver.waitForSelector({ text: 'true', tag: 'p' }); - // click on approve and wait for window to close - await driver.clickElementAndWaitForWindowToClose( - '[data-testid="confirmation-submit-button"]', - ); + // try to click on approve + await driver.clickElement('[data-testid="confirmation-submit-button"]'); // switch to test snaps tab await driver.switchToWindowWithTitle(WINDOW_TITLES.TestSnaps); diff --git a/test/e2e/snaps/test-snap-jsx.spec.js b/test/e2e/snaps/test-snap-jsx.spec.js index 4bdb94990df7..b5f651125232 100644 --- a/test/e2e/snaps/test-snap-jsx.spec.js +++ b/test/e2e/snaps/test-snap-jsx.spec.js @@ -27,21 +27,15 @@ describe('Test Snap JSX', function () { tag: 'h2', }); - // find and scroll to the jsx test + // find and scroll to the jsx test and connect const snapButton = await driver.findElement('#connectjsx'); await driver.scrollToElement(snapButton); - - // added delay for firefox (deflake) - await driver.delayFirefox(1000); - - // wait for and click connect + await driver.delay(1000); await driver.waitForSelector('#connectjsx'); await driver.clickElement('#connectjsx'); - // switch to dialog window + // switch to dialog window and click connect await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - - // wait for and click connect await driver.waitForSelector({ text: 'Connect', tag: 'button', @@ -51,20 +45,17 @@ describe('Test Snap JSX', function () { tag: 'button', }); - // wait for confirm button await driver.waitForSelector({ text: 'Confirm' }); - // click and dismiss possible scroll element await driver.clickElementSafe('[data-testid="snap-install-scroll"]'); - // click confirm await driver.clickElement({ text: 'Confirm', tag: 'button', }); - // wait for and click ok await driver.waitForSelector({ text: 'OK' }); + await driver.clickElementAndWaitForWindowToClose({ text: 'OK', tag: 'button', diff --git a/test/e2e/snaps/test-snap-lifecycle.spec.js b/test/e2e/snaps/test-snap-lifecycle.spec.js index e84845ec2553..40387376557c 100644 --- a/test/e2e/snaps/test-snap-lifecycle.spec.js +++ b/test/e2e/snaps/test-snap-lifecycle.spec.js @@ -2,6 +2,7 @@ const { defaultGanacheOptions, withFixtures, unlockWallet, + switchToNotificationWindow, WINDOW_TITLES, } = require('../helpers'); const FixtureBuilder = require('../fixture-builder'); @@ -27,39 +28,27 @@ describe('Test Snap Lifecycle Hooks', function () { tag: 'h2', }); - // scroll to lifecycle hooks snap const snapButton = await driver.findElement('#connectlifecycle-hooks'); await driver.scrollToElement(snapButton); - - // added delay for firefox (deflake) - await driver.delayFirefox(1000); - - // wait for and click connect - await driver.waitForSelector('#connectlifecycle-hooks'); + await driver.delay(1000); await driver.clickElement('#connectlifecycle-hooks'); - // switch to metamask extension - await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - - // wait for and click connect - await driver.waitForSelector({ - text: 'Connect', - tag: 'button', - }); + // switch to metamask extension and click connect + await switchToNotificationWindow(driver); await driver.clickElement({ text: 'Connect', tag: 'button', }); - // wait for and click confirm await driver.waitForSelector({ text: 'Confirm' }); + await driver.clickElement({ text: 'Confirm', tag: 'button', }); - // wait for and click ok - await driver.waitForSelector({ text: 'OK', tag: 'button' }); + await driver.waitForSelector({ text: 'OK' }); + await driver.clickElement({ text: 'OK', tag: 'button', @@ -75,7 +64,7 @@ describe('Test Snap Lifecycle Hooks', function () { }); // switch to dialog popup - await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); + await switchToNotificationWindow(driver); // check dialog contents const result = await driver.findElement('.snap-ui-renderer__panel'); diff --git a/test/e2e/snaps/test-snap-management.spec.js b/test/e2e/snaps/test-snap-management.spec.js index fd11e801a061..7e62311af6bc 100644 --- a/test/e2e/snaps/test-snap-management.spec.js +++ b/test/e2e/snaps/test-snap-management.spec.js @@ -2,6 +2,7 @@ const { defaultGanacheOptions, withFixtures, unlockWallet, + switchToNotificationWindow, WINDOW_TITLES, } = require('../helpers'); const FixtureBuilder = require('../fixture-builder'); @@ -27,40 +28,29 @@ describe('Test Snap Management', function () { tag: 'h2', }); - // find and scroll to the notifications snap + // find and scroll to the notifications card and click first const snapButton = await driver.findElement('#connectnotifications'); await driver.scrollToElement(snapButton); - - // added delay for firefox (deflake) - await driver.delayFirefox(1000); - - // wait for and click connect - await driver.waitForSelector('#connectnotifications'); + await driver.delay(1000); await driver.clickElement('#connectnotifications'); - // switch to metamask extension - await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - - // wait for and click connect - await driver.waitForSelector({ - text: 'Connect', - tag: 'button', - }); + // switch to metamask extension and click connect + await switchToNotificationWindow(driver); await driver.clickElement({ text: 'Connect', tag: 'button', }); - // wait for and click confirm await driver.waitForSelector({ text: 'Confirm' }); + await driver.clickElement({ text: 'Confirm', tag: 'button', }); - // wait for and click ok and wait for window to close await driver.waitForSelector({ text: 'OK' }); - await driver.clickElementAndWaitForWindowToClose({ + + await driver.clickElement({ text: 'OK', tag: 'button', }); diff --git a/test/e2e/snaps/test-snap-managestate.spec.js b/test/e2e/snaps/test-snap-managestate.spec.js index 4d6a58faa678..b7bfdb7678d4 100644 --- a/test/e2e/snaps/test-snap-managestate.spec.js +++ b/test/e2e/snaps/test-snap-managestate.spec.js @@ -2,6 +2,7 @@ const { defaultGanacheOptions, withFixtures, unlockWallet, + switchToNotificationWindow, WINDOW_TITLES, } = require('../helpers'); const FixtureBuilder = require('../fixture-builder'); @@ -27,40 +28,29 @@ describe('Test Snap manageState', function () { tag: 'h2', }); - // scroll to manage-state snap + // navigate to test snaps page and connect to manage-state snap const snapButton1 = await driver.findElement('#connectmanage-state'); await driver.scrollToElement(snapButton1); - - // added delay for firefox (deflake) - await driver.delayFirefox(1000); - - // wait for and click connect - await driver.waitForSelector('#connectmanage-state'); + await driver.delay(1000); await driver.clickElement('#connectmanage-state'); - // switch to metamask extension - await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - - // wait for and click connect - await driver.waitForSelector({ - text: 'Connect', - tag: 'button', - }); + // switch to metamask extension and click connect + await switchToNotificationWindow(driver, 2); await driver.clickElement({ text: 'Connect', tag: 'button', }); - // wait for and click confirm await driver.waitForSelector({ text: 'Confirm' }); + await driver.clickElement({ text: 'Confirm', tag: 'button', }); - // wait for and click ok and wait for window to close await driver.waitForSelector({ text: 'OK' }); - await driver.clickElementAndWaitForWindowToClose({ + + await driver.clickElement({ text: 'OK', tag: 'button', }); @@ -74,7 +64,6 @@ describe('Test Snap manageState', function () { text: 'Reconnect to Manage State Snap', }); - // enter data and click send managestate await driver.pasteIntoField('#dataManageState', '23'); const snapButton2 = await driver.findElement( '#retrieveManageStateResult', diff --git a/test/e2e/snaps/test-snap-metrics.spec.js b/test/e2e/snaps/test-snap-metrics.spec.js index 54ebd572d993..a26671ca4261 100644 --- a/test/e2e/snaps/test-snap-metrics.spec.js +++ b/test/e2e/snaps/test-snap-metrics.spec.js @@ -192,40 +192,29 @@ describe('Test Snap Metrics', function () { tag: 'h2', }); - // find and scroll to the notifications snap + // find and scroll to the notifications card and click first const snapButton = await driver.findElement('#connectnotifications'); await driver.scrollToElement(snapButton); - - // added delay for firefox (deflake) - await driver.delayFirefox(1000); - - // wait for and click connect - await driver.waitForSelector('#connectnotifications'); + await driver.delay(1000); await driver.clickElement('#connectnotifications'); - // switch to metamask extension + // switch to metamask popup and click connect await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - - // wait for and click connect - await driver.waitForSelector({ - text: 'Connect', - tag: 'button', - }); await driver.clickElement({ text: 'Connect', tag: 'button', }); - // wait for and click confirm await driver.waitForSelector({ text: 'Confirm' }); + await driver.clickElement({ text: 'Confirm', tag: 'button', }); - // wait for and click ok and wait for window to close await driver.waitForSelector({ text: 'OK' }); - await driver.clickElementAndWaitForWindowToClose({ + + await driver.clickElement({ text: 'OK', tag: 'button', }); @@ -288,31 +277,21 @@ describe('Test Snap Metrics', function () { tag: 'h2', }); - // find and scroll to the notifications snap + // find and scroll to the notifications card and click first const snapButton = await driver.findElement('#connectnotifications'); await driver.scrollToElement(snapButton); - - // added delay for firefox (deflake) - await driver.delayFirefox(1000); - - // wait for and click connect - await driver.waitForSelector('#connectnotifications'); + await driver.delay(1000); await driver.clickElement('#connectnotifications'); - // switch to metamask extension + // switch to metamask popup and click connect await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - - // wait for and click connect - await driver.waitForSelector({ - text: 'Connect', - tag: 'button', - }); await driver.clickElement({ text: 'Connect', tag: 'button', }); - // wait for and click confirm + await driver.waitForSelector({ text: 'Confirm' }); + await driver.clickElement({ text: 'Cancel', tag: 'button', @@ -376,31 +355,19 @@ describe('Test Snap Metrics', function () { tag: 'h2', }); - // find and scroll to the notifications snap + // find and scroll to the notifications card and click first const snapButton = await driver.findElement('#connectnotifications'); await driver.scrollToElement(snapButton); - - // added delay for firefox (deflake) - await driver.delayFirefox(1000); - - // wait for and click connect - await driver.waitForSelector('#connectnotifications'); + await driver.delay(1000); await driver.clickElement('#connectnotifications'); - // switch to metamask extension + // switch to metamask popup and click connect await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - - // wait for and click connect - await driver.waitForSelector({ - text: 'Connect', - tag: 'button', - }); await driver.clickElement({ text: 'Connect', tag: 'button', }); - // wait for connection failure await driver.waitForSelector({ text: 'Connection failed' }); // check that snap installed event metrics have been sent @@ -457,40 +424,29 @@ describe('Test Snap Metrics', function () { tag: 'h2', }); - // find and scroll to the notifications snap + // find and scroll to the notifications card and click first const snapButton = await driver.findElement('#connectnotifications'); await driver.scrollToElement(snapButton); - - // added delay for firefox (deflake) - await driver.delayFirefox(1000); - - // wait for and click connect - await driver.waitForSelector('#connectnotifications'); + await driver.delay(1000); await driver.clickElement('#connectnotifications'); - // switch to metamask extension + // switch to metamask popup and click connect await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - - // wait for and click connect - await driver.waitForSelector({ - text: 'Connect', - tag: 'button', - }); await driver.clickElement({ text: 'Connect', tag: 'button', }); - // wait for and click confirm await driver.waitForSelector({ text: 'Confirm' }); + await driver.clickElement({ text: 'Confirm', tag: 'button', }); - // wait for and click ok and wait for window to close await driver.waitForSelector({ text: 'OK' }); - await driver.clickElementAndWaitForWindowToClose({ + + await driver.clickElement({ text: 'OK', tag: 'button', }); @@ -586,31 +542,19 @@ describe('Test Snap Metrics', function () { tag: 'h2', }); - // find and scroll to the update snap + // find and scroll to the correct card and connect to update snap const snapButton = await driver.findElement('#connectUpdate'); await driver.scrollToElement(snapButton); - - // added delay for firefox (deflake) - await driver.delayFirefox(1000); - - // wait for and click connect - await driver.waitForSelector('#connectUpdate'); + await driver.delay(1000); await driver.clickElement('#connectUpdate'); - // switch to metamask extension + // switch to metamask popup and click connect await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - - // wait for and click connect - await driver.waitForSelector({ - text: 'Connect', - tag: 'button', - }); await driver.clickElement({ text: 'Connect', tag: 'button', }); - // wait for confirm button await driver.waitForSelector({ text: 'Confirm' }); // Wait for the permissions content to be rendered @@ -619,10 +563,8 @@ describe('Test Snap Metrics', function () { tag: 'span', }); - // click and dismiss possible scroll element await driver.clickElementSafe('[data-testid="snap-install-scroll"]'); - // click confirm button await driver.clickElement({ text: 'Confirm', tag: 'button', @@ -638,9 +580,9 @@ describe('Test Snap Metrics', function () { '[data-testid="snap-install-warning-modal-confirm"]', ); - // wait for and click ok and wait for window to close await driver.waitForSelector({ text: 'OK' }); - await driver.clickElementAndWaitForWindowToClose({ + + await driver.clickElement({ text: 'OK', tag: 'button', }); @@ -654,35 +596,27 @@ describe('Test Snap Metrics', function () { text: 'Reconnect to Update Snap', }); - // find and scroll to the update new button + // find and scroll to the correct card and click first const snapButton2 = await driver.findElement('#connectUpdateNew'); await driver.scrollToElement(snapButton2); - - // added delay for firefox (deflake) - await driver.delayFirefox(1000); - - // wait for and click connect - await driver.waitForSelector('#connectUpdateNew'); + await driver.delay(1000); await driver.clickElement('#connectUpdateNew'); // switch to metamask popup and update await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - // wait for confirm button await driver.waitForSelector({ text: 'Confirm' }); - // click and dismiss possible scroll element await driver.clickElementSafe('[data-testid="snap-update-scroll"]'); - // click confirm await driver.clickElement({ text: 'Confirm', tag: 'button', }); - // wait for and click ok and wait for window to close await driver.waitForSelector({ text: 'OK' }); - await driver.clickElementAndWaitForWindowToClose({ + + await driver.clickElement({ text: 'OK', tag: 'button', }); @@ -756,31 +690,19 @@ describe('Test Snap Metrics', function () { tag: 'h2', }); - // find and scroll to the update snap + // find and scroll to the correct card and connect to update snap const snapButton = await driver.findElement('#connectUpdate'); await driver.scrollToElement(snapButton); - - // added delay for firefox (deflake) - await driver.delayFirefox(1000); - - // wait for and click connect - await driver.waitForSelector('#connectUpdate'); + await driver.delay(1000); await driver.clickElement('#connectUpdate'); - // switch to metamask extension + // switch to metamask popup and click connect await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - - // wait for and click connect - await driver.waitForSelector({ - text: 'Connect', - tag: 'button', - }); await driver.clickElement({ text: 'Connect', tag: 'button', }); - // wait for confirm button await driver.waitForSelector({ text: 'Confirm' }); // Wait for the permissions content to be rendered @@ -789,10 +711,8 @@ describe('Test Snap Metrics', function () { tag: 'span', }); - // click and dismiss possible scroll element await driver.clickElementSafe('[data-testid="snap-install-scroll"]'); - // click confirm await driver.clickElement({ text: 'Confirm', tag: 'button', @@ -808,9 +728,9 @@ describe('Test Snap Metrics', function () { '[data-testid="snap-install-warning-modal-confirm"]', ); - // wait for and click ok and wait for window to close await driver.waitForSelector({ text: 'OK' }); - await driver.clickElementAndWaitForWindowToClose({ + + await driver.clickElement({ text: 'OK', tag: 'button', }); @@ -824,28 +744,20 @@ describe('Test Snap Metrics', function () { text: 'Reconnect to Update Snap', }); - // find and scroll to the update snap + // find and scroll to the correct card and click first const snapButton2 = await driver.findElement('#connectUpdateNew'); await driver.scrollToElement(snapButton2); - - // added delay for firefox (deflake) - await driver.delayFirefox(1000); - - // wait for and click connect new - await driver.waitForSelector('#connectUpdateNew'); + await driver.delay(1000); await driver.clickElement('#connectUpdateNew'); // switch to metamask popup and update await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - // wait for confirm button await driver.waitForSelector({ text: 'Confirm' }); - // click and dismiss possible scroll element await driver.clickElementSafe('[data-testid="snap-update-scroll"]'); - // click cancel and wait for window to close - await driver.clickElementAndWaitForWindowToClose({ + await driver.clickElement({ text: 'Cancel', tag: 'button', }); @@ -914,31 +826,19 @@ describe('Test Snap Metrics', function () { tag: 'h2', }); - // find and scroll to the update snap + // find and scroll to the correct card and connect to update snap const snapButton = await driver.findElement('#connectUpdate'); await driver.scrollToElement(snapButton); - - // added delay for firefox (deflake) - await driver.delayFirefox(1000); - - // wait for and click connect - await driver.waitForSelector('#connectUpdate'); + await driver.delay(1000); await driver.clickElement('#connectUpdate'); - // switch to metamask extension + // switch to metamask popup and click connect await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - - // wait for and click connect - await driver.waitForSelector({ - text: 'Connect', - tag: 'button', - }); await driver.clickElement({ text: 'Connect', tag: 'button', }); - // wait for confirm button await driver.waitForSelector({ text: 'Confirm' }); // Wait for the permissions content to be rendered @@ -947,10 +847,8 @@ describe('Test Snap Metrics', function () { tag: 'span', }); - // click and dismiss possible scroll element await driver.clickElementSafe('[data-testid="snap-install-scroll"]'); - // click confirm await driver.clickElement({ text: 'Confirm', tag: 'button', @@ -966,9 +864,9 @@ describe('Test Snap Metrics', function () { '[data-testid="snap-install-warning-modal-confirm"]', ); - // wait for and click ok and wait for window to close await driver.waitForSelector({ text: 'OK' }); - await driver.clickElementAndWaitForWindowToClose({ + + await driver.clickElement({ text: 'OK', tag: 'button', }); @@ -982,25 +880,18 @@ describe('Test Snap Metrics', function () { text: 'Reconnect to Update Snap', }); - // find and scroll to the update snap + // find and scroll to the correct card and click first const snapButton2 = await driver.findElement('#connectUpdateNew'); await driver.scrollToElement(snapButton2); - - // added delay for firefox (deflake) - await driver.delayFirefox(1000); - - // wait for and click update new - await driver.waitForSelector('#connectUpdateNew'); + await driver.delay(1000); await driver.clickElement('#connectUpdateNew'); - // wait for and close alert window await driver.delay(1000); await driver.closeAlertPopup(); // switch to metamask popup and update await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - // wait for failure message await driver.waitForSelector({ text: 'Update failed' }); // check that snap updated event metrics have been sent diff --git a/test/e2e/snaps/test-snap-multi-install.spec.js b/test/e2e/snaps/test-snap-multi-install.spec.js index 2b48a1bb86ed..d30b1307d216 100644 --- a/test/e2e/snaps/test-snap-multi-install.spec.js +++ b/test/e2e/snaps/test-snap-multi-install.spec.js @@ -21,32 +21,14 @@ describe('Test Snap Multi Install', function () { // navigate to test snaps page and multi-install snaps await driver.openNewPage(TEST_SNAPS_WEBSITE_URL); - - // wait for page to load - await driver.waitForSelector({ - text: 'Installed Snaps', - tag: 'h2', - }); - - // scroll to multi-install snap + await driver.delay(1000); const dialogButton = await driver.findElement('#multi-install-connect'); await driver.scrollToElement(dialogButton); - - // added delay for firefox (deflake) - await driver.delayFirefox(1000); - - // wait for and click connect - await driver.waitForSelector('#multi-install-connect'); + await driver.delay(1000); await driver.clickElement('#multi-install-connect'); - // switch to metamask extension + // switch to metamask extension and click connect await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - - // wait for and click connect - await driver.waitForSelector({ - text: 'Connect', - tag: 'button', - }); await driver.clickElement({ text: 'Connect', tag: 'button', @@ -75,7 +57,7 @@ describe('Test Snap Multi Install', function () { '[data-testid="snap-install-warning-modal-confirm"]', ); - // wait for and click OK + // wait for anc click OK await driver.waitForSelector({ text: 'OK' }); await driver.clickElement({ text: 'OK', @@ -108,7 +90,7 @@ describe('Test Snap Multi Install', function () { '[data-testid="snap-install-warning-modal-confirm"]', ); - // wait for and click OK + // wait for anc click OK await driver.waitForSelector({ text: 'OK' }); await driver.clickElement({ text: 'OK', diff --git a/test/e2e/snaps/test-snap-namelookup.spec.js b/test/e2e/snaps/test-snap-namelookup.spec.js index c7a61bb928fa..94af575deb9c 100644 --- a/test/e2e/snaps/test-snap-namelookup.spec.js +++ b/test/e2e/snaps/test-snap-namelookup.spec.js @@ -2,6 +2,7 @@ const { withFixtures, defaultGanacheOptions, unlockWallet, + switchToNotificationWindow, WINDOW_TITLES, } = require('../helpers'); const FixtureBuilder = require('../fixture-builder'); @@ -27,39 +28,28 @@ describe('Test Snap Name Lookup', function () { tag: 'h2', }); - // find and scroll to the namelookup test snap + // find and scroll to the namelookup test snap and connect const snapButton1 = await driver.findElement('#connectname-lookup'); await driver.scrollToElement(snapButton1); - - // added delay for firefox (deflake) - await driver.delayFirefox(1000); - - // wait for and click connect - await driver.waitForSelector('#connectname-lookup'); + await driver.delay(1000); await driver.clickElement('#connectname-lookup'); - // switch to metamask extension - await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - - // wait for and click connect - await driver.waitForSelector({ - text: 'Connect', - tag: 'button', - }); + // switch to metamask extension and click connect + await switchToNotificationWindow(driver); await driver.clickElement({ text: 'Connect', tag: 'button', }); - // wait for and click confirm await driver.waitForSelector({ text: 'Confirm' }); + await driver.clickElement({ text: 'Confirm', tag: 'button', }); - // wait for and click ok and wait for window to close await driver.waitForSelector({ text: 'OK' }); + await driver.clickElementAndWaitForWindowToClose({ text: 'OK', tag: 'button', diff --git a/test/e2e/snaps/test-snap-networkaccess.spec.js b/test/e2e/snaps/test-snap-networkaccess.spec.js index a8bfb9141394..e3fa954a79c2 100644 --- a/test/e2e/snaps/test-snap-networkaccess.spec.js +++ b/test/e2e/snaps/test-snap-networkaccess.spec.js @@ -2,6 +2,7 @@ const { defaultGanacheOptions, withFixtures, unlockWallet, + switchToNotificationWindow, WINDOW_TITLES, } = require('../helpers'); const FixtureBuilder = require('../fixture-builder'); @@ -27,40 +28,28 @@ describe('Test Snap networkAccess', function () { tag: 'h2', }); - // scroll to network access snap const dialogButton = await driver.findElement('#connectnetwork-access'); await driver.scrollToElement(dialogButton); - - // added delay for firefox (deflake) - await driver.delayFirefox(1000); - - // wait for and click connect - await driver.waitForSelector('#connectnetwork-access'); + await driver.delay(1000); await driver.clickElement('#connectnetwork-access'); - // switch to metamask extension - await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - - // wait for and click connect - await driver.waitForSelector({ - text: 'Connect', - tag: 'button', - }); + // switch to metamask extension and click connect + await switchToNotificationWindow(driver); await driver.clickElement({ text: 'Connect', tag: 'button', }); - // wait for and click confirm await driver.waitForSelector({ text: 'Confirm' }); + await driver.clickElement({ text: 'Confirm', tag: 'button', }); - // wait for and click ok and wait for window to close await driver.waitForSelector({ text: 'OK' }); - await driver.clickElementAndWaitForWindowToClose({ + + await driver.clickElement({ text: 'OK', tag: 'button', }); diff --git a/test/e2e/snaps/test-snap-notification.spec.js b/test/e2e/snaps/test-snap-notification.spec.js index c96c1bc96e3d..7c9fdede898d 100644 --- a/test/e2e/snaps/test-snap-notification.spec.js +++ b/test/e2e/snaps/test-snap-notification.spec.js @@ -2,13 +2,14 @@ const { defaultGanacheOptions, withFixtures, unlockWallet, + switchToNotificationWindow, WINDOW_TITLES, } = require('../helpers'); const FixtureBuilder = require('../fixture-builder'); const { TEST_SNAPS_WEBSITE_URL } = require('./enums'); describe('Test Snap Notification', function () { - it('can send 1 correctly read in-app notification', async function () { + it('can send 1 correctly read inapp notification', async function () { await withFixtures( { fixtures: new FixtureBuilder().build(), @@ -27,40 +28,29 @@ describe('Test Snap Notification', function () { tag: 'h2', }); - // scroll to notifications snap + // connect to notifications snap const snapButton = await driver.findElement('#connectnotifications'); await driver.scrollToElement(snapButton); - - // added delay for firefox (deflake) - await driver.delayFirefox(1000); - - // wait for and click connect - await driver.waitForSelector('#connectnotifications'); + await driver.delay(1000); await driver.clickElement('#connectnotifications'); - // switch to metamask extension - await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - - // wait for and click connect - await driver.waitForSelector({ - text: 'Connect', - tag: 'button', - }); + // switch to metamask extension and click connect + await switchToNotificationWindow(driver); await driver.clickElement({ text: 'Connect', tag: 'button', }); - // wait for and click confirm await driver.waitForSelector({ text: 'Confirm' }); + await driver.clickElement({ text: 'Confirm', tag: 'button', }); - // wait for and click ok and wait for window to cloe await driver.waitForSelector({ text: 'OK' }); - await driver.clickElementAndWaitForWindowToClose({ + + await driver.clickElement({ text: 'OK', tag: 'button', }); @@ -74,7 +64,6 @@ describe('Test Snap Notification', function () { text: 'Reconnect to Notifications Snap', }); - // click to send notification await driver.clickElement('#sendInAppNotification'); // switch back to the extension page diff --git a/test/e2e/snaps/test-snap-revoke-perm.spec.js b/test/e2e/snaps/test-snap-revoke-perm.spec.js index e61c1d831862..b70cc3ab4cc1 100644 --- a/test/e2e/snaps/test-snap-revoke-perm.spec.js +++ b/test/e2e/snaps/test-snap-revoke-perm.spec.js @@ -2,6 +2,7 @@ const { defaultGanacheOptions, withFixtures, WINDOW_TITLES, + switchToNotificationWindow, unlockWallet, } = require('../helpers'); const FixtureBuilder = require('../fixture-builder'); @@ -27,42 +28,30 @@ describe('Test Snap revoke permission', function () { tag: 'h2', }); - // scroll to ethereum-provider snap const snapButton = await driver.findElement( '#connectethereum-provider', ); await driver.scrollToElement(snapButton); - - // added delay for firefox (deflake) - await driver.delayFirefox(1000); - - // wait for and click connect - await driver.waitForSelector('#connectethereum-provider'); + await driver.delay(1000); await driver.clickElement('#connectethereum-provider'); - // switch to metamask extension - await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - - // wait for and click connect - await driver.waitForSelector({ - text: 'Connect', - tag: 'button', - }); + // switch to metamask extension and click connect + await switchToNotificationWindow(driver, 3); await driver.clickElement({ text: 'Connect', tag: 'button', }); - // wait for and click connect await driver.waitForSelector({ text: 'Confirm' }); + await driver.clickElement({ text: 'Confirm', tag: 'button', }); - // wait for and click ok and wait for window to close await driver.waitForSelector({ text: 'OK' }); - await driver.clickElementAndWaitForWindowToClose({ + + await driver.clickElement({ text: 'OK', tag: 'button', }); @@ -81,36 +70,17 @@ describe('Test Snap revoke permission', function () { '#sendEthproviderAccounts', ); await driver.scrollToElement(snapButton3); - - // added delay for firefox (deflake) - await driver.delayFirefox(1000); - - // wait for and click send - await driver.waitForSelector('#sendEthproviderAccounts'); + await driver.delay(500); await driver.clickElement('#sendEthproviderAccounts'); - // switch to metamask window - await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - - // wait for and click next - await driver.waitForSelector({ - text: 'Next', - tag: 'button', - }); + // switch to metamask window and click through confirmations + await switchToNotificationWindow(driver, 3); await driver.clickElement({ text: 'Next', tag: 'button', }); - - // delay added for rendering time (deflake) await driver.delay(500); - - // wait for and click confirm and wait for window to close - await driver.waitForSelector({ - text: 'Confirm', - tag: 'button', - }); - await driver.clickElementAndWaitForWindowToClose({ + await driver.clickElement({ text: 'Confirm', tag: 'button', }); @@ -128,8 +98,6 @@ describe('Test Snap revoke permission', function () { await driver.switchToWindowWithTitle( WINDOW_TITLES.ExtensionInFullScreenView, ); - - // added delay for rendering (deflake) await driver.delay(1000); // click on the global action menu @@ -166,39 +134,18 @@ describe('Test Snap revoke permission', function () { '#sendEthproviderAccounts', ); await driver.scrollToElement(snapButton4); - - // added delay for firefox (deflake) - await driver.delayFirefox(1000); - - // wait for and click connect - await driver.waitForSelector('#sendEthproviderAccounts'); + await driver.delay(500); await driver.clickElement('#sendEthproviderAccounts'); - // delay added for rendering time (deflake) + // switch to metamask window and click through confirmations await driver.delay(500); - - // switch to metamask dialog - await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - - // wait for and click next - await driver.waitForSelector({ - text: 'Next', - tag: 'button', - }); + await switchToNotificationWindow(driver, 3); await driver.clickElement({ text: 'Next', tag: 'button', }); - - // delay added for rendering time (deflake) await driver.delay(500); - - // wait for and click confirm and wait for window to close - await driver.waitForSelector({ - text: 'Confirm', - tag: 'button', - }); - await driver.clickElementAndWaitForWindowToClose({ + await driver.clickElement({ text: 'Confirm', tag: 'button', }); diff --git a/test/e2e/snaps/test-snap-rpc.spec.js b/test/e2e/snaps/test-snap-rpc.spec.js index 2b16a9f0929a..b6ad95c89c65 100644 --- a/test/e2e/snaps/test-snap-rpc.spec.js +++ b/test/e2e/snaps/test-snap-rpc.spec.js @@ -1,6 +1,7 @@ const { defaultGanacheOptions, withFixtures, + switchToNotificationWindow, unlockWallet, WINDOW_TITLES, } = require('../helpers'); @@ -27,37 +28,23 @@ describe('Test Snap RPC', function () { tag: 'h2', }); - // find and scroll to the bip32 test snap + // find and scroll to the bip32 test and connect const snapButton1 = await driver.findElement('#connectbip32'); await driver.scrollToElement(snapButton1); - - // added delay for firefox (deflake) - await driver.delayFirefox(1000); - - // wait for and click connect - await driver.waitForSelector('#connectbip32'); + await driver.delay(1000); await driver.clickElement('#connectbip32'); - // switch to metamask extension - await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - - // wait for and click connect - await driver.waitForSelector({ - text: 'Connect', - tag: 'button', - }); + // switch to metamask extension and click connect + await switchToNotificationWindow(driver, 2); await driver.clickElement({ text: 'Connect', tag: 'button', }); - // wait for confirm button await driver.waitForSelector({ text: 'Confirm' }); - // click and dismiss possible scroll element await driver.clickElementSafe('[data-testid="snap-install-scroll"]'); - // click confirm await driver.clickElement({ text: 'Confirm', tag: 'button', @@ -73,9 +60,10 @@ describe('Test Snap RPC', function () { '[data-testid="snap-install-warning-modal-confirm"]', ); - // wait for and click ok and wait for window to close + // deal with OK button await driver.waitForSelector({ text: 'OK' }); - await driver.clickElementAndWaitForWindowToClose({ + + await driver.clickElement({ text: 'OK', tag: 'button', }); @@ -83,45 +71,31 @@ describe('Test Snap RPC', function () { // switch back to test-snaps window await driver.switchToWindowWithTitle(WINDOW_TITLES.TestSnaps); - // scroll to json-rpc snap const snapButton2 = await driver.findElement('#connectjson-rpc'); await driver.scrollToElement(snapButton2); - - // added delay for firefox (deflake) - await driver.delayFirefox(1000); - - // wait for and click connect - await driver.waitForSelector('#connectjson-rpc'); + await driver.delay(1000); await driver.clickElement('#connectjson-rpc'); - // switch to metamask dialog - await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - - // wait for and click connect - await driver.waitForSelector({ - text: 'Connect', - tag: 'button', - }); + await switchToNotificationWindow(driver, 2); await driver.clickElement({ text: 'Connect', tag: 'button', }); - // wait for and click confirm await driver.waitForSelector({ text: 'Confirm' }); + await driver.clickElement({ text: 'Confirm', tag: 'button', }); - // wait for and click ok and wait for window to close await driver.waitForSelector({ text: 'OK' }); - await driver.clickElementAndWaitForWindowToClose({ + + await driver.clickElement({ text: 'OK', tag: 'button', }); - // switch to test snaps window await driver.switchToWindowWithTitle(WINDOW_TITLES.TestSnaps); // wait for npm installation success @@ -130,15 +104,10 @@ describe('Test Snap RPC', function () { text: 'Reconnect to JSON-RPC Snap', }); - // scroll to send rpc + // click send inputs on test snap page const snapButton3 = await driver.findElement('#sendRpc'); await driver.scrollToElement(snapButton3); - - // added delay for firefox (deflake) - await driver.delayFirefox(1000); - - // wait for and click send - await driver.waitForSelector('#sendRpc'); + await driver.delay(1000); await driver.clickElement('#sendRpc'); // check result with waitForSelector diff --git a/test/e2e/snaps/test-snap-siginsights.spec.js b/test/e2e/snaps/test-snap-siginsights.spec.js index 94237b99c7f9..b72d6e248ff0 100644 --- a/test/e2e/snaps/test-snap-siginsights.spec.js +++ b/test/e2e/snaps/test-snap-siginsights.spec.js @@ -27,48 +27,32 @@ describe('Test Snap Signature Insights', function () { // navigate to test snaps page and connect await driver.openNewPage(TEST_SNAPS_WEBSITE_URL); + await driver.delay(1000); - // wait for page to load - await driver.waitForSelector({ - text: 'Installed Snaps', - tag: 'h2', - }); - - // find and scroll to the transaction-insights snap + // find and scroll to the transaction-insights test and connect const snapButton1 = await driver.findElement( '#connectsignature-insights', ); await driver.scrollToElement(snapButton1); - - // added delay for firefox (deflake) - await driver.delayFirefox(1000); - - // wait for and click connect - await driver.waitForSelector('#connectsignature-insights'); + await driver.delay(1000); await driver.clickElement('#connectsignature-insights'); - // switch to metamask extension + // switch to metamask extension and click connect await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - - // wait for and click connect - await driver.waitForSelector({ - text: 'Connect', - tag: 'button', - }); await driver.clickElement({ text: 'Connect', tag: 'button', }); - // wait for and click confirm await driver.waitForSelector({ text: 'Confirm' }); + await driver.clickElement({ text: 'Confirm', tag: 'button', }); - // wait for and click ok and wait for window to close await driver.waitForSelector({ text: 'OK' }); + await driver.clickElementAndWaitForWindowToClose({ text: 'OK', tag: 'button', @@ -145,7 +129,7 @@ describe('Test Snap Signature Insights', function () { tag: 'p', }); - // click sign button and wait for window to close + // click sign button await driver.clickElementAndWaitForWindowToClose( '[data-testid="confirm-footer-button"]', ); @@ -239,7 +223,7 @@ describe('Test Snap Signature Insights', function () { tag: 'p', }); - // click sign button and wait for window to close + // click sign button await driver.clickElementAndWaitForWindowToClose( '[data-testid="confirm-footer-button"]', ); @@ -273,45 +257,32 @@ describe('Test Snap Signature Insights', function () { // navigate to test snaps page and connect await driver.openNewPage(TEST_SNAPS_WEBSITE_URL); - - // delay added for page render (deflake) await driver.delay(1000); - // find and scroll to the transaction-insights test snap + // find and scroll to the transaction-insights test and connect const snapButton1 = await driver.findElement( '#connectsignature-insights', ); await driver.scrollToElement(snapButton1); - - // added delay for firefox (deflake) - await driver.delayFirefox(1000); - - // wait for and click connect - await driver.waitForSelector('#connectsignature-insights'); + await driver.delay(1000); await driver.clickElement('#connectsignature-insights'); - // switch to metamask extension + // switch to metamask extension and click connect await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - - // wait for and click connect - await driver.waitForSelector({ - text: 'Connect', - tag: 'button', - }); await driver.clickElement({ text: 'Connect', tag: 'button', }); - // wait for and click confirm await driver.waitForSelector({ text: 'Confirm' }); + await driver.clickElement({ text: 'Confirm', tag: 'button', }); - // wait for and click ok and wait for window to close await driver.waitForSelector({ text: 'OK' }); + await driver.clickElementAndWaitForWindowToClose({ text: 'OK', tag: 'button', @@ -349,9 +320,10 @@ describe('Test Snap Signature Insights', function () { text: '127.0.0.1:8080', tag: 'span', }); + await driver.clickElement('.mm-checkbox__input-wrapper'); - // click sign button and wait for window to close + // click sign button await driver.clickElementAndWaitForWindowToClose( '[data-testid="snapInsightsButtonConfirm"]', ); @@ -391,9 +363,10 @@ describe('Test Snap Signature Insights', function () { text: '127.0.0.1:8080', tag: 'span', }); + await driver.clickElement('.mm-checkbox__input-wrapper'); - // click sign button and wait for window to close + // click sign button await driver.clickElementAndWaitForWindowToClose( '[data-testid="snapInsightsButtonConfirm"]', ); @@ -437,9 +410,10 @@ describe('Test Snap Signature Insights', function () { text: '127.0.0.1:8080', tag: 'span', }); + await driver.clickElement('.mm-checkbox__input-wrapper'); - // click sign button and wait for window to close + // click sign button await driver.clickElementAndWaitForWindowToClose( '[data-testid="snapInsightsButtonConfirm"]', ); @@ -483,9 +457,10 @@ describe('Test Snap Signature Insights', function () { text: '127.0.0.1:8080', tag: 'span', }); + await driver.clickElement('.mm-checkbox__input-wrapper'); - // click sign button and wait for window to close + // click sign button await driver.clickElementAndWaitForWindowToClose( '[data-testid="snapInsightsButtonConfirm"]', ); diff --git a/test/e2e/snaps/test-snap-txinsights-v2.spec.js b/test/e2e/snaps/test-snap-txinsights-v2.spec.js index a249e9daa79b..830629d1c43e 100644 --- a/test/e2e/snaps/test-snap-txinsights-v2.spec.js +++ b/test/e2e/snaps/test-snap-txinsights-v2.spec.js @@ -27,61 +27,37 @@ describe('Test Snap TxInsights-v2', function () { tag: 'h2', }); - // find and scroll to the transaction-insights test snap + // find and scroll to the transaction-insights test and connect const snapButton1 = await driver.findElement( '#connecttransaction-insights', ); await driver.scrollToElement(snapButton1); - - // added delay for firefox (deflake) - await driver.delayFirefox(1000); - - // wait for and click connect - await driver.waitForSelector('#connecttransaction-insights'); + await driver.delay(1000); await driver.clickElement('#connecttransaction-insights'); - // switch to metamask extension + // switch to metamask extension and click connect await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - - // wait for and click connect - await driver.waitForSelector({ - text: 'Connect', - tag: 'button', - }); await driver.clickElement({ text: 'Connect', tag: 'button', }); - // wait for and click connect - await driver.waitForSelector({ text: 'Confirm' }); await driver.clickElement({ text: 'Confirm', tag: 'button', }); - // wait for and click ok and wait for window to close - await driver.waitForSelector({ text: 'OK' }); await driver.clickElementAndWaitForWindowToClose({ text: 'OK', tag: 'button', }); - // switch to test-snaps page + // switch to test-snaps page and get accounts await driver.switchToWindowWithTitle(WINDOW_TITLES.TestSnaps); - - // wait for and click get accounts - await driver.waitForSelector('#getAccounts'); await driver.clickElement('#getAccounts'); - // switch back to MetaMask window + // switch back to MetaMask window and deal with dialogs await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - - // wait for and click confirm and wait for window to close - await driver.waitForSelector({ - text: 'Connect', - tag: 'button', - }); await driver.clickElementAndWaitForWindowToClose({ text: 'Connect', tag: 'button', @@ -91,19 +67,15 @@ describe('Test Snap TxInsights-v2', function () { await driver.switchToWindowWithTitle(WINDOW_TITLES.TestSnaps); await driver.clickElement('#sendInsights'); - // delay added for rendering (deflake) - await driver.delay(2000); - // switch back to MetaMask window and switch to tx insights pane + await driver.delay(2000); await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - // find confirm button await driver.findClickableElement({ text: 'Confirm', tag: 'button', }); - // wait for and click insights snap tab await driver.waitForSelector({ text: 'Insights Example Snap', tag: 'button', @@ -147,12 +119,10 @@ describe('Test Snap TxInsights-v2', function () { tag: 'button', }); - // switch back to MetaMask tab + // switch back to MetaMask tab and switch to activity pane await driver.switchToWindowWithTitle( WINDOW_TITLES.ExtensionInFullScreenView, ); - - // switch to activity pane await driver.clickElement({ tag: 'button', text: 'Activity', diff --git a/test/e2e/snaps/test-snap-txinsights.spec.js b/test/e2e/snaps/test-snap-txinsights.spec.js index 21feafd06cb9..7f6b7a3bec46 100644 --- a/test/e2e/snaps/test-snap-txinsights.spec.js +++ b/test/e2e/snaps/test-snap-txinsights.spec.js @@ -27,41 +27,26 @@ describe('Test Snap TxInsights', function () { tag: 'h2', }); - // find and scroll to the transaction-insights test snap + // find and scroll to the transaction-insights test and connect const snapButton1 = await driver.findElement( '#connecttransaction-insights', ); await driver.scrollToElement(snapButton1); - - // added delay for firefox (deflake) - await driver.delayFirefox(1000); - - // wait for and click connect - await driver.waitForSelector('#connecttransaction-insights'); + await driver.delay(1000); await driver.clickElement('#connecttransaction-insights'); - // switch to metamask extension + // switch to metamask extension and click connect await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - - // wait for and click connect - await driver.waitForSelector({ - text: 'Connect', - tag: 'button', - }); await driver.clickElement({ text: 'Connect', tag: 'button', }); - // wait for and click confirm - await driver.waitForSelector({ text: 'Confirm' }); await driver.clickElement({ text: 'Confirm', tag: 'button', }); - // wait for and click ok and wait for window to close - await driver.waitForSelector({ text: 'OK' }); await driver.clickElementAndWaitForWindowToClose({ text: 'OK', tag: 'button', @@ -69,40 +54,23 @@ describe('Test Snap TxInsights', function () { // switch to test-snaps page and get accounts await driver.switchToWindowWithTitle(WINDOW_TITLES.TestSnaps); - - // click get accounts await driver.clickElement('#getAccounts'); - // switch back to MetaMask window + // switch back to MetaMask window and deal with dialogs await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - - // wait for and click next and wait for window to close - await driver.waitForSelector({ - text: 'Connect', - tag: 'button', - }); await driver.clickElementAndWaitForWindowToClose({ text: 'Connect', tag: 'button', }); - // switch to test-snaps page + // switch to test-snaps page and send tx await driver.switchToWindowWithTitle(WINDOW_TITLES.TestSnaps); - - // click send tx await driver.clickElement('#sendInsights'); - // delay added for rendering (deflake) + // switch back to MetaMask window and switch to tx insights pane await driver.delay(2000); - - // switch back to MetaMask window await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - // wait for and switch to insight snap pane - await driver.waitForSelector({ - text: 'Insights Example Snap', - tag: 'button', - }); await driver.clickElement({ text: 'Insights Example Snap', tag: 'button', diff --git a/test/e2e/snaps/test-snap-ui-imgs.spec.js b/test/e2e/snaps/test-snap-ui-imgs.spec.js index 8a73c5723611..4d8f17ad852a 100644 --- a/test/e2e/snaps/test-snap-ui-imgs.spec.js +++ b/test/e2e/snaps/test-snap-ui-imgs.spec.js @@ -2,6 +2,7 @@ const { defaultGanacheOptions, withFixtures, unlockWallet, + switchToNotificationWindow, WINDOW_TITLES, } = require('../helpers'); const FixtureBuilder = require('../fixture-builder'); @@ -27,43 +28,29 @@ describe('Test Snap Images', function () { tag: 'h2', }); - // find and scroll to the images test snap + // find and scroll to the images test and connect const snapButton1 = await driver.findElement('#connectimages'); await driver.scrollToElement(snapButton1); - - // added delay for firefox (deflake) - await driver.delayFirefox(1000); - - // wait for and click connect + await driver.delay(1000); await driver.waitForSelector('#connectimages'); await driver.clickElement('#connectimages'); - // switch to metamask extension - await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - - // wait for and click connect - await driver.waitForSelector({ - text: 'Connect', - tag: 'button', - }); + // switch to metamask extension and click connect and approve + await switchToNotificationWindow(driver, 2); await driver.clickElement({ text: 'Connect', tag: 'button', }); - - // wait for confirm await driver.waitForSelector({ text: 'Confirm' }); - // click and dismiss possible scroll element await driver.clickElementSafe('[data-testid="snap-install-scroll"]'); - // click confirm await driver.clickElement({ text: 'Confirm', tag: 'button', }); - // wait for and click ok and wait for window to close + // deal with OK button await driver.waitForSelector({ text: 'OK' }); await driver.clickElementAndWaitForWindowToClose({ text: 'OK', @@ -88,7 +75,7 @@ describe('Test Snap Images', function () { // check snaps ui image using waitForSelector await driver.waitForSelector('[data-testid="snaps-ui-image"]'); - // click ok to close window and wait for window to close + // click ok to close window await driver.clickElementAndWaitForWindowToClose( '[data-testid="confirmation-submit-button"]', ); diff --git a/test/e2e/snaps/test-snap-uilinks.spec.js b/test/e2e/snaps/test-snap-uilinks.spec.js index 8c4c397f8196..4e75765cbac6 100644 --- a/test/e2e/snaps/test-snap-uilinks.spec.js +++ b/test/e2e/snaps/test-snap-uilinks.spec.js @@ -2,6 +2,7 @@ const { defaultGanacheOptions, withFixtures, unlockWallet, + switchToNotificationWindow, WINDOW_TITLES, } = require('../helpers'); const FixtureBuilder = require('../fixture-builder'); @@ -19,49 +20,31 @@ describe('Test Snap UI Links', function () { async ({ driver }) => { await unlockWallet(driver); - // navigate to test snaps page + // navigate to test snaps page and connect to dialog snap await driver.openNewPage(TEST_SNAPS_WEBSITE_URL); - - // wait for page to load - await driver.waitForSelector({ - text: 'Installed Snaps', - tag: 'h2', - }); - - // scroll to dialogs snap + await driver.delay(1000); const dialogButton = await driver.findElement('#connectdialogs'); await driver.scrollToElement(dialogButton); - - // added delay for firefox (deflake) - await driver.delayFirefox(1000); - - // wait for and click connect - await driver.waitForSelector('#connectdialogs'); + await driver.delay(1000); await driver.clickElement('#connectdialogs'); - // switch to metamask extension - await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - - // wait for and click connect - await driver.waitForSelector({ - text: 'Connect', - tag: 'button', - }); + // switch to metamask extension and click connect + await switchToNotificationWindow(driver); await driver.clickElement({ text: 'Connect', tag: 'button', }); - // wait for and click confirm await driver.waitForSelector({ text: 'Confirm' }); + await driver.clickElement({ text: 'Confirm', tag: 'button', }); - // wait for and click ok and wait for window to close await driver.waitForSelector({ text: 'OK' }); - await driver.clickElementAndWaitForWindowToClose({ + + await driver.clickElement({ text: 'OK', tag: 'button', }); @@ -77,14 +60,10 @@ describe('Test Snap UI Links', function () { // click conf button await driver.clickElement('#sendConfirmationButton'); - - // delay added for rendering (deflake) await driver.delay(500); // switch to dialog popup - await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - - // delay added for rendering (deflake) + await switchToNotificationWindow(driver); await driver.delay(500); // wait for link to appear and click it @@ -123,7 +102,7 @@ describe('Test Snap UI Links', function () { }); // switch back to metamask window - await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); + await switchToNotificationWindow(driver, 4); // wait for and click approve button await driver.waitForSelector({ diff --git a/test/e2e/snaps/test-snap-update-component.spec.js b/test/e2e/snaps/test-snap-update-component.spec.js index c65c9e047668..e471c76ad6e8 100644 --- a/test/e2e/snaps/test-snap-update-component.spec.js +++ b/test/e2e/snaps/test-snap-update-component.spec.js @@ -1,4 +1,9 @@ -const { withFixtures, unlockWallet, WINDOW_TITLES } = require('../helpers'); +const { + withFixtures, + switchToNotificationWindow, + unlockWallet, + WINDOW_TITLES, +} = require('../helpers'); const FixtureBuilder = require('../fixture-builder'); const { TEST_SNAPS_WEBSITE_URL } = require('./enums'); @@ -31,37 +36,23 @@ describe('Test Snap update via snaps component', function () { tag: 'h2', }); - // find and scroll to the update snap + // find and scroll to the correct card and connect to update snap const snapButton = await driver.findElement('#connectUpdate'); await driver.scrollToElement(snapButton); - - // added delay for firefox (deflake) - await driver.delayFirefox(1000); - - // wait for and click connect - await driver.waitForSelector('#connectUpdate'); + await driver.delay(1000); await driver.clickElement('#connectUpdate'); - // switch to metamask extension - await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - - // wait for and click connect - await driver.waitForSelector({ - text: 'Connect', - tag: 'button', - }); + // switch to metamask extension and click connect + await switchToNotificationWindow(driver, 3); await driver.clickElement({ text: 'Connect', tag: 'button', }); - // wait for confirm await driver.waitForSelector({ text: 'Confirm' }); - // click and dismiss possible scroll element await driver.clickElementSafe('[data-testid="snap-install-scroll"]'); - // click confirm await driver.clickElement({ text: 'Confirm', tag: 'button', @@ -77,9 +68,10 @@ describe('Test Snap update via snaps component', function () { '[data-testid="snap-install-warning-modal-confirm"]', ); - // wait for and click OK button and wait for window to close + // deal with OK button await driver.waitForSelector({ text: 'OK' }); - await driver.clickElementAndWaitForWindowToClose({ + + await driver.clickElement({ text: 'OK', tag: 'button', }); @@ -98,7 +90,7 @@ describe('Test Snap update via snaps component', function () { WINDOW_TITLES.ExtensionInFullScreenView, ); - // wait for and click on the global action menu + // click on the global action menu await driver.waitForSelector( '[data-testid="account-options-menu-button"]', ); @@ -132,25 +124,20 @@ describe('Test Snap update via snaps component', function () { tag: 'button', }); - // click and dismiss possible scroll element await driver.clickElementSafe('[data-testid="snap-update-scroll"]'); - // wait for confirm await driver.clickElement({ text: 'Confirm', tag: 'button', }); - // click checkbox await driver.clickElement('.mm-checkbox__input'); - - // click install warning confirm await driver.clickElement( '[data-testid="snap-install-warning-modal-confirm"]', ); - // wait for and click ok await driver.waitForSelector({ text: 'OK' }); + await driver.clickElement({ text: 'OK', tag: 'button', @@ -170,18 +157,17 @@ describe('Test Snap update via snaps component', function () { text: 'Snaps', tag: 'div', }); - - // wait for and click into snap view await driver.waitForSelector({ text: 'BIP-32 Example Snap', tag: 'p', }); + + // click into snap view and attempt to update the snap await driver.clickElement({ text: 'BIP-32 Example Snap', tag: 'p', }); - // make sure update button isn't present await driver.assertElementNotPresent( { css: '.mm-button-link', diff --git a/test/e2e/snaps/test-snap-update.spec.js b/test/e2e/snaps/test-snap-update.spec.js index ac35b8947787..6f5500d593a4 100644 --- a/test/e2e/snaps/test-snap-update.spec.js +++ b/test/e2e/snaps/test-snap-update.spec.js @@ -1,6 +1,7 @@ const { defaultGanacheOptions, withFixtures, + switchToNotificationWindow, unlockWallet, WINDOW_TITLES, } = require('../helpers'); @@ -27,37 +28,24 @@ describe('Test Snap update', function () { tag: 'h2', }); - // find and scroll to the update snap + // find and scroll to the correct card and connect to update snap const snapButton = await driver.findElement('#connectUpdate'); await driver.scrollToElement(snapButton); - - // added delay for firefox (deflake) - await driver.delayFirefox(1000); - - // wait for and click connect - await driver.waitForSelector('#connectUpdate'); + await driver.delay(1000); await driver.clickElement('#connectUpdate'); - // switch to metamask extension - await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - - // wait for and click connect - await driver.waitForSelector({ - text: 'Connect', - tag: 'button', - }); + // switch to metamask extension and click connect + await switchToNotificationWindow(driver, 2); await driver.clickElement({ text: 'Connect', tag: 'button', }); - // wait for confirm await driver.waitForSelector({ text: 'Confirm' }); - // click and dismiss possible scroll element + // scroll to bottom await driver.clickElementSafe('[data-testid="snap-install-scroll"]'); - // click confirm await driver.clickElement({ text: 'Confirm', tag: 'button', @@ -86,29 +74,22 @@ describe('Test Snap update', function () { text: 'Reconnect to Update Snap', }); - // find and scroll to the update snap + // find and scroll to the correct card and click first const snapButton2 = await driver.findElement('#connectUpdateNew'); await driver.scrollToElement(snapButton2); - - // added delay for firefox (deflake) - await driver.delayFirefox(1000); - - // wait for and click connect - await driver.waitForSelector('#connectUpdateNew'); + await driver.delay(1000); await driver.clickElement('#connectUpdateNew'); // switch to metamask extension and update - await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); + await switchToNotificationWindow(driver, 2); await driver.waitForSelector({ text: 'Update request' }); // Scroll to bottom of dialog await driver.clickElementSafe('[data-testid="snap-update-scroll"]'); - // Click confirm button await driver.clickElementAndWaitToDisappear( '[data-testid="page-container-footer-next"]', ); - // When it is confirmed, click okay button await driver.waitForSelector({ text: 'OK' }); await driver.clickElement('[data-testid="page-container-footer-next"]'); diff --git a/test/e2e/snaps/test-snap-wasm.spec.js b/test/e2e/snaps/test-snap-wasm.spec.js index 205300c570c0..ada7d604d97a 100644 --- a/test/e2e/snaps/test-snap-wasm.spec.js +++ b/test/e2e/snaps/test-snap-wasm.spec.js @@ -2,6 +2,7 @@ const { defaultGanacheOptions, withFixtures, unlockWallet, + switchToNotificationWindow, WINDOW_TITLES, } = require('../helpers'); const FixtureBuilder = require('../fixture-builder'); @@ -27,40 +28,28 @@ describe('Test Snap WASM', function () { tag: 'h2', }); - // scroll to wasm snap const snapButton = await driver.findElement('#connectwasm'); await driver.scrollToElement(snapButton); - - // added delay for firefox (deflake) - await driver.delayFirefox(1000); - - // wait for and click connect - await driver.waitForSelector('#connectwasm'); + await driver.delay(1000); await driver.clickElement('#connectwasm'); - // switch to metamask extension - await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - - // wait for and click connect - await driver.waitForSelector({ - text: 'Connect', - tag: 'button', - }); + // switch to metamask extension and click connect + await switchToNotificationWindow(driver); await driver.clickElement({ text: 'Connect', tag: 'button', }); - // wait for and click confirm await driver.waitForSelector({ text: 'Confirm' }); + await driver.clickElement({ text: 'Confirm', tag: 'button', }); - // wait for and click ok and wait for window to close await driver.waitForSelector({ text: 'OK' }); - await driver.clickElementAndWaitForWindowToClose({ + + await driver.clickElement({ text: 'OK', tag: 'button', }); diff --git a/test/e2e/tests/account/snap-account-signatures.spec.ts b/test/e2e/tests/account/snap-account-signatures.spec.ts index 0c528b1dc20c..fd2fe013c3c1 100644 --- a/test/e2e/tests/account/snap-account-signatures.spec.ts +++ b/test/e2e/tests/account/snap-account-signatures.spec.ts @@ -18,7 +18,7 @@ import SnapSimpleKeyringPage from '../../page-objects/pages/snap-simple-keyring- import TestDapp from '../../page-objects/pages/test-dapp'; describe('Snap Account Signatures @no-mmi', function (this: Suite) { - this.timeout(200000); // This test is very long, so we need an unusually high timeout + this.timeout(120000); // This test is very long, so we need an unusually high timeout // Run sync, async approve, and async reject flows // (in Jest we could do this with test.each, but that does not exist here) diff --git a/test/e2e/tests/privacy/account-tracker-api-usage.spec.ts b/test/e2e/tests/api-usage/account-tracker-api-usage.spec.ts similarity index 93% rename from test/e2e/tests/privacy/account-tracker-api-usage.spec.ts rename to test/e2e/tests/api-usage/account-tracker-api-usage.spec.ts index 24f2318fa13b..f0cd40cb7373 100644 --- a/test/e2e/tests/privacy/account-tracker-api-usage.spec.ts +++ b/test/e2e/tests/api-usage/account-tracker-api-usage.spec.ts @@ -4,12 +4,11 @@ import { MockedEndpoint } from 'mockttp'; import FixtureBuilder from '../../fixture-builder'; import { defaultGanacheOptions, + unlockWallet, veryLargeDelayMs, withFixtures, } from '../../helpers'; import { Mockttp } from '../../mock-e2e'; -import HomePage from '../../page-objects/pages/homepage'; -import { loginWithoutBalanceValidation } from '../../page-objects/flows/login.flow'; async function mockInfura(mockServer: Mockttp): Promise { const blockNumber = { value: 0 }; @@ -123,9 +122,7 @@ describe('Account Tracker API Usage', function () { )} request has been made to infura before opening the UI`, ); - await loginWithoutBalanceValidation(driver); - const homepage = new HomePage(driver); - await homepage.check_pageIsLoaded(); + await unlockWallet(driver); await driver.delay(veryLargeDelayMs); allInfuraJsonRpcRequests = await getAllInfuraJsonRpcRequests( @@ -161,9 +158,7 @@ describe('Account Tracker API Usage', function () { testSpecificMock: mockInfura, }, async ({ driver, mockedEndpoint }) => { - await loginWithoutBalanceValidation(driver); - const homepage = new HomePage(driver); - await homepage.check_pageIsLoaded(); + await unlockWallet(driver); await driver.delay(veryLargeDelayMs); const initialInfuraJsonRpcRequests = await getAllInfuraJsonRpcRequests( mockedEndpoint, diff --git a/test/e2e/tests/bridge/bridge-test-utils.ts b/test/e2e/tests/bridge/bridge-test-utils.ts index 930f0196673e..1f4a3e5cda79 100644 --- a/test/e2e/tests/bridge/bridge-test-utils.ts +++ b/test/e2e/tests/bridge/bridge-test-utils.ts @@ -11,8 +11,8 @@ import { import { SMART_CONTRACTS } from '../../seeder/smart-contracts'; import { CHAIN_IDS } from '../../../../shared/constants/network'; import { Driver } from '../../webdriver/driver'; +import { FeatureFlagResponse } from '../../../../ui/pages/bridge/bridge.util'; import { isManifestV3 } from '../../../../shared/modules/mv3.utils'; -import { FeatureFlagResponse } from '../../../../ui/pages/bridge/types'; import { DEFAULT_FEATURE_FLAGS_RESPONSE, ETH_CONVERSION_RATE_USD, diff --git a/test/e2e/tests/bridge/constants.ts b/test/e2e/tests/bridge/constants.ts index ae7fc37a62c6..e79a1dfe4553 100644 --- a/test/e2e/tests/bridge/constants.ts +++ b/test/e2e/tests/bridge/constants.ts @@ -1,10 +1,6 @@ -import { FeatureFlagResponse } from '../../../../ui/pages/bridge/types'; +import { FeatureFlagResponse } from '../../../../ui/pages/bridge/bridge.util'; export const DEFAULT_FEATURE_FLAGS_RESPONSE: FeatureFlagResponse = { - 'extension-config': { - refreshRate: 30, - maxRefreshCount: 5, - }, 'extension-support': false, 'src-network-allowlist': [1, 42161, 59144], 'dest-network-allowlist': [1, 42161, 59144], diff --git a/test/e2e/tests/confirmations/helpers.ts b/test/e2e/tests/confirmations/helpers.ts index 355f664ec61c..ff467f42c320 100644 --- a/test/e2e/tests/confirmations/helpers.ts +++ b/test/e2e/tests/confirmations/helpers.ts @@ -46,8 +46,8 @@ export function withRedesignConfirmationFixtures( transactionEnvelopeType === TransactionEnvelopeType.legacy ? defaultGanacheOptions : defaultGanacheOptionsForType2Transactions, - ...(smartContract && { smartContract }), - ...(mocks && { testSpecificMock: mocks }), + smartContract, + testSpecificMock: mocks, title, }, testFunction, diff --git a/test/e2e/tests/confirmations/navigation.spec.ts b/test/e2e/tests/confirmations/navigation.spec.ts index 38d29ad3ad77..747ba15872b3 100644 --- a/test/e2e/tests/confirmations/navigation.spec.ts +++ b/test/e2e/tests/confirmations/navigation.spec.ts @@ -66,15 +66,10 @@ describe('Navigation Signature - Different signature types', function (this: Sui '[data-testid="confirm-nav__next-confirmation"]', ); - // Verify simple send transaction is displayed - await driver.waitForSelector({ - tag: 'h3', - text: 'Transfer request', - }); + // Verify Transaction Sending ETH is displayed + await verifyTransaction(driver, 'Sending ETH'); - await driver.clickElement( - '[data-testid="confirm-nav__next-confirmation"]', - ); + await driver.clickElement('[data-testid="next-page"]'); // Verify Sign Typed Data v3 confirmation is displayed await verifySignedTypeV3Confirmation(driver); @@ -83,15 +78,10 @@ describe('Navigation Signature - Different signature types', function (this: Sui '[data-testid="confirm-nav__previous-confirmation"]', ); - // Verify simple send transaction is displayed - await driver.waitForSelector({ - tag: 'h3', - text: 'Transfer request', - }); + // Verify Sign Typed Data v3 confirmation is displayed + await verifyTransaction(driver, 'Sending ETH'); - await driver.clickElement( - '[data-testid="confirm-nav__previous-confirmation"]', - ); + await driver.clickElement('[data-testid="previous-page"]'); // Verify Sign Typed Data v3 confirmation is displayed await verifySignTypedData(driver); @@ -189,3 +179,13 @@ async function queueSignaturesAndTransactions(driver: Driver) { await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); await driver.waitForSelector(By.xpath("//div[normalize-space(.)='1 of 3']")); } + +async function verifyTransaction( + driver: Driver, + expectedTransactionType: string, +) { + await driver.waitForSelector({ + tag: 'span', + text: expectedTransactionType, + }); +} diff --git a/test/e2e/tests/confirmations/signatures/malicious-signatures.spec.ts b/test/e2e/tests/confirmations/signatures/malicious-signatures.spec.ts index 328ad7811e1b..fc8a6d0ab240 100644 --- a/test/e2e/tests/confirmations/signatures/malicious-signatures.spec.ts +++ b/test/e2e/tests/confirmations/signatures/malicious-signatures.spec.ts @@ -10,10 +10,6 @@ import { withRedesignConfirmationFixtures, } from '../helpers'; import { TestSuiteArguments } from '../transactions/shared'; -import { - BlockaidReason, - BlockaidResultType, -} from '../../../../../shared/constants/security-provider'; import { assertSignatureRejectedMetrics, openDappAndTriggerSignature, @@ -84,8 +80,6 @@ describe('Malicious Confirmation Signature - Bad Domain @no-mmi', function (this alert_visualized: [], alert_visualized_count: 0, }, - securityAlertReason: BlockaidReason.notApplicable, - securityAlertResponse: BlockaidResultType.NotApplicable, }); }, mockSignatureRejected, @@ -136,8 +130,6 @@ describe('Malicious Confirmation Signature - Bad Domain @no-mmi', function (this alert_visualized: ['requestFrom'], alert_visualized_count: 1, }, - securityAlertReason: BlockaidReason.notApplicable, - securityAlertResponse: BlockaidResultType.NotApplicable, }); }, mockSignatureRejected, diff --git a/test/e2e/tests/confirmations/signatures/permit.spec.ts b/test/e2e/tests/confirmations/signatures/permit.spec.ts index bc74b9fd2f5f..8da5e411a2f4 100644 --- a/test/e2e/tests/confirmations/signatures/permit.spec.ts +++ b/test/e2e/tests/confirmations/signatures/permit.spec.ts @@ -114,7 +114,6 @@ describe('Confirmation Signature - Permit @no-mmi', function (this: Suite) { }); async function assertInfoValues(driver: Driver) { - await driver.clickElement('[data-testid="sectionCollapseButton"]'); const origin = driver.findElement({ text: DAPP_HOST_ADDRESS }); const contractPetName = driver.findElement({ css: '.name__value', diff --git a/test/e2e/tests/confirmations/signatures/signature-helpers.ts b/test/e2e/tests/confirmations/signatures/signature-helpers.ts index 27cc92fe27cf..9b87e5b4e9cc 100644 --- a/test/e2e/tests/confirmations/signatures/signature-helpers.ts +++ b/test/e2e/tests/confirmations/signatures/signature-helpers.ts @@ -8,10 +8,6 @@ import { unlockWallet, } from '../../../helpers'; import { Driver } from '../../../webdriver/driver'; -import { - BlockaidReason, - BlockaidResultType, -} from '../../../../../shared/constants/security-provider'; export const WALLET_ADDRESS = '0x5CfE73b6021E818B776b421B1c4Db2474086a7e1'; export const WALLET_ETH_BALANCE = '25'; @@ -34,8 +30,6 @@ type AssertSignatureMetricsOptions = { location?: string; expectedProps?: Record; withAnonEvents?: boolean; - securityAlertReason?: string; - securityAlertResponse?: string; }; type SignatureEventProperty = { @@ -45,7 +39,7 @@ type SignatureEventProperty = { environment_type: 'background'; locale: 'en'; security_alert_reason: string; - security_alert_response: string; + security_alert_response: 'NotApplicable'; signature_type: string; eip712_primary_type?: string; ui_customizations?: string[]; @@ -64,15 +58,11 @@ const signatureAnonProperties = { * @param signatureType * @param primaryType * @param uiCustomizations - * @param securityAlertReason - * @param securityAlertResponse */ function getSignatureEventProperty( signatureType: string, primaryType: string, uiCustomizations: string[], - securityAlertReason: string = BlockaidReason.checkingChain, - securityAlertResponse: string = BlockaidResultType.Loading, ): SignatureEventProperty { const signatureEventProperty: SignatureEventProperty = { account_type: 'MetaMask', @@ -81,8 +71,8 @@ function getSignatureEventProperty( chain_id: '0x539', environment_type: 'background', locale: 'en', - security_alert_reason: securityAlertReason, - security_alert_response: securityAlertResponse, + security_alert_reason: 'NotApplicable', + security_alert_response: 'NotApplicable', ui_customizations: uiCustomizations, }; @@ -99,15 +89,15 @@ function assertSignatureRequestedMetrics( signatureEventProperty: SignatureEventProperty, withAnonEvents = false, ) { - assertEventPropertiesMatch( - events, - 'Signature Requested', - signatureEventProperty, - ); + assertEventPropertiesMatch(events, 'Signature Requested', { + ...signatureEventProperty, + security_alert_reason: 'NotApplicable', + }); if (withAnonEvents) { assertEventPropertiesMatch(events, 'Signature Requested Anon', { ...signatureEventProperty, + security_alert_reason: 'NotApplicable', ...signatureAnonProperties, }); } @@ -120,16 +110,12 @@ export async function assertSignatureConfirmedMetrics({ primaryType = '', uiCustomizations = ['redesigned_confirmation'], withAnonEvents = false, - securityAlertReason, - securityAlertResponse, }: AssertSignatureMetricsOptions) { const events = await getEventPayloads(driver, mockedEndpoints); const signatureEventProperty = getSignatureEventProperty( signatureType, primaryType, uiCustomizations, - securityAlertReason, - securityAlertResponse, ); assertSignatureRequestedMetrics( @@ -161,16 +147,12 @@ export async function assertSignatureRejectedMetrics({ location, expectedProps = {}, withAnonEvents = false, - securityAlertReason, - securityAlertResponse, }: AssertSignatureMetricsOptions) { const events = await getEventPayloads(driver, mockedEndpoints); const signatureEventProperty = getSignatureEventProperty( signatureType, primaryType, uiCustomizations, - securityAlertReason, - securityAlertResponse, ); assertSignatureRequestedMetrics( @@ -218,44 +200,14 @@ function assertEventPropertiesMatch( expectedProperties: object, ) { const event = events.find((e) => e.event === eventName); - - const actualProperties = { ...event.properties }; - const expectedProps = { ...expectedProperties }; - - compareSecurityAlertResponse(actualProperties, expectedProps, eventName); - assert(event, `${eventName} event not found`); assert.deepStrictEqual( - actualProperties, - expectedProps, + event.properties, + expectedProperties, `${eventName} event properties do not match`, ); } -function compareSecurityAlertResponse( - actualProperties: Record, - expectedProperties: Record, - eventName: string, -) { - if ( - expectedProperties.security_alert_response && - (expectedProperties.security_alert_response === 'loading' || - expectedProperties.security_alert_response === 'Benign') - ) { - if ( - actualProperties.security_alert_response !== 'loading' && - actualProperties.security_alert_response !== 'Benign' - ) { - assert.fail( - `${eventName} event properties do not match: security_alert_response is ${actualProperties.security_alert_response}`, - ); - } - // Remove the property from both objects to avoid comparison - delete actualProperties.security_alert_response; - delete expectedProperties.security_alert_response; - } -} - export async function clickHeaderInfoBtn(driver: Driver) { await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); diff --git a/test/e2e/tests/confirmations/signatures/siwe.spec.ts b/test/e2e/tests/confirmations/signatures/siwe.spec.ts index 889122eb22b8..1dd545034731 100644 --- a/test/e2e/tests/confirmations/signatures/siwe.spec.ts +++ b/test/e2e/tests/confirmations/signatures/siwe.spec.ts @@ -11,10 +11,6 @@ import { withRedesignConfirmationFixtures, } from '../helpers'; import { TestSuiteArguments } from '../transactions/shared'; -import { - BlockaidReason, - BlockaidResultType, -} from '../../../../../shared/constants/security-provider'; import { assertAccountDetailsMetrics, assertHeaderInfoBalance, @@ -64,8 +60,6 @@ describe('Confirmation Signature - SIWE @no-mmi', function (this: Suite) { 'redesigned_confirmation', 'sign_in_with_ethereum', ], - securityAlertReason: BlockaidReason.notApplicable, - securityAlertResponse: BlockaidResultType.NotApplicable, }); }, mockSignatureApproved, @@ -101,8 +95,6 @@ describe('Confirmation Signature - SIWE @no-mmi', function (this: Suite) { 'sign_in_with_ethereum', ], location: 'confirmation', - securityAlertReason: BlockaidReason.notApplicable, - securityAlertResponse: BlockaidResultType.NotApplicable, }); }, mockSignatureRejected, @@ -111,7 +103,6 @@ describe('Confirmation Signature - SIWE @no-mmi', function (this: Suite) { }); async function assertInfoValues(driver: Driver) { - await driver.clickElement('[data-testid="sectionCollapseButton"]'); const origin = driver.findElement({ text: DAPP_HOST_ADDRESS }); const message = driver.findElement({ text: 'I accept the MetaMask Terms of Service: https://community.metamask.io/tos', diff --git a/test/e2e/tests/confirmations/transactions/native-send-redesign.spec.ts b/test/e2e/tests/confirmations/transactions/native-send-redesign.spec.ts deleted file mode 100644 index e8226977d019..000000000000 --- a/test/e2e/tests/confirmations/transactions/native-send-redesign.spec.ts +++ /dev/null @@ -1,113 +0,0 @@ -/* eslint-disable @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires */ -import { TransactionEnvelopeType } from '@metamask/transaction-controller'; -import { DAPP_URL } from '../../../constants'; -import { - unlockWallet, - veryLargeDelayMs, - WINDOW_TITLES, -} from '../../../helpers'; -import TokenTransferTransactionConfirmation from '../../../page-objects/pages/confirmations/redesign/token-transfer-confirmation'; -import HomePage from '../../../page-objects/pages/homepage'; -import SendTokenPage from '../../../page-objects/pages/send/send-token-page'; -import TestDapp from '../../../page-objects/pages/test-dapp'; -import { Driver } from '../../../webdriver/driver'; -import { withRedesignConfirmationFixtures } from '../helpers'; -import { TestSuiteArguments } from './shared'; - -const TOKEN_RECIPIENT_ADDRESS = '0x2f318C334780961FB129D2a6c30D0763d9a5C970'; - -describe('Confirmation Redesign Native Send @no-mmi', function () { - describe('Wallet initiated', async function () { - it('Sends a type 0 transaction (Legacy)', async function () { - await withRedesignConfirmationFixtures( - this.test?.fullTitle(), - TransactionEnvelopeType.legacy, - async ({ driver }: TestSuiteArguments) => { - await createWalletInitiatedTransactionAndAssertDetails(driver); - }, - ); - }); - - it('Sends a type 2 transaction (EIP1559)', async function () { - await withRedesignConfirmationFixtures( - this.test?.fullTitle(), - TransactionEnvelopeType.feeMarket, - async ({ driver }: TestSuiteArguments) => { - await createWalletInitiatedTransactionAndAssertDetails(driver); - }, - ); - }); - }); - - describe('dApp initiated', async function () { - it('Sends a type 0 transaction (Legacy)', async function () { - await withRedesignConfirmationFixtures( - this.test?.fullTitle(), - TransactionEnvelopeType.legacy, - async ({ driver }: TestSuiteArguments) => { - await createDAppInitiatedTransactionAndAssertDetails(driver); - }, - ); - }); - - it('Sends a type 2 transaction (EIP1559)', async function () { - await withRedesignConfirmationFixtures( - this.test?.fullTitle(), - TransactionEnvelopeType.feeMarket, - async ({ driver }: TestSuiteArguments) => { - await createDAppInitiatedTransactionAndAssertDetails(driver); - }, - ); - }); - }); -}); - -async function createWalletInitiatedTransactionAndAssertDetails( - driver: Driver, -) { - await unlockWallet(driver); - - const testDapp = new TestDapp(driver); - - await testDapp.openTestDappPage({ contractAddress: null, url: DAPP_URL }); - - await driver.switchToWindowWithTitle(WINDOW_TITLES.ExtensionInFullScreenView); - const homePage = new HomePage(driver); - await homePage.startSendFlow(); - const sendToPage = new SendTokenPage(driver); - await sendToPage.check_pageIsLoaded(); - await sendToPage.fillRecipient(TOKEN_RECIPIENT_ADDRESS); - await sendToPage.fillAmount('1'); - await sendToPage.goToNextScreen(); - - const tokenTransferTransactionConfirmation = - new TokenTransferTransactionConfirmation(driver); - await tokenTransferTransactionConfirmation.check_walletInitiatedHeadingTitle(); - await tokenTransferTransactionConfirmation.check_networkParagraph(); - await tokenTransferTransactionConfirmation.check_networkFeeParagraph(); - - await tokenTransferTransactionConfirmation.clickFooterConfirmButton(); -} - -async function createDAppInitiatedTransactionAndAssertDetails(driver: Driver) { - await unlockWallet(driver); - - const testDapp = new TestDapp(driver); - - await testDapp.openTestDappPage({ contractAddress: null, url: DAPP_URL }); - - await driver.switchToWindowWithTitle(WINDOW_TITLES.TestDApp); - - await testDapp.clickSimpleSendButton(); - - await driver.delay(veryLargeDelayMs); - await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - const tokenTransferTransactionConfirmation = - new TokenTransferTransactionConfirmation(driver); - await tokenTransferTransactionConfirmation.check_dappInitiatedHeadingTitle(); - await tokenTransferTransactionConfirmation.check_networkParagraph(); - await tokenTransferTransactionConfirmation.check_networkFeeParagraph(); - - await tokenTransferTransactionConfirmation.clickScrollToBottomButton(); - await tokenTransferTransactionConfirmation.clickFooterConfirmButton(); -} diff --git a/test/e2e/tests/confirmations/transactions/nft-token-send-redesign.spec.ts b/test/e2e/tests/confirmations/transactions/nft-token-send-redesign.spec.ts deleted file mode 100644 index 4f6093e349f2..000000000000 --- a/test/e2e/tests/confirmations/transactions/nft-token-send-redesign.spec.ts +++ /dev/null @@ -1,320 +0,0 @@ -/* eslint-disable @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires */ -import { TransactionEnvelopeType } from '@metamask/transaction-controller'; -import { DAPP_URL } from '../../../constants'; -import { - unlockWallet, - veryLargeDelayMs, - WINDOW_TITLES, -} from '../../../helpers'; -import { Mockttp } from '../../../mock-e2e'; -import WatchAssetConfirmation from '../../../page-objects/pages/confirmations/legacy/watch-asset-confirmation'; -import TokenTransferTransactionConfirmation from '../../../page-objects/pages/confirmations/redesign/token-transfer-confirmation'; -import TransactionConfirmation from '../../../page-objects/pages/confirmations/redesign/transaction-confirmation'; -import HomePage from '../../../page-objects/pages/homepage'; -import NFTDetailsPage from '../../../page-objects/pages/nft-details-page'; -import SendTokenPage from '../../../page-objects/pages/send/send-token-page'; -import TestDapp from '../../../page-objects/pages/test-dapp'; -import GanacheContractAddressRegistry from '../../../seeder/ganache-contract-address-registry'; -import { Driver } from '../../../webdriver/driver'; -import { withRedesignConfirmationFixtures } from '../helpers'; -import { TestSuiteArguments } from './shared'; - -const { SMART_CONTRACTS } = require('../../../seeder/smart-contracts'); - -const TOKEN_RECIPIENT_ADDRESS = '0x2f318C334780961FB129D2a6c30D0763d9a5C970'; - -describe('Confirmation Redesign Token Send @no-mmi', function () { - describe('ERC721', function () { - describe('Wallet initiated', async function () { - it('Sends a type 0 transaction (Legacy)', async function () { - await withRedesignConfirmationFixtures( - this.test?.fullTitle(), - TransactionEnvelopeType.legacy, - async ({ driver, contractRegistry }: TestSuiteArguments) => { - await createERC721WalletInitiatedTransactionAndAssertDetails( - driver, - contractRegistry, - ); - }, - erc721Mocks, - SMART_CONTRACTS.NFTS, - ); - }); - - it('Sends a type 2 transaction (EIP1559)', async function () { - await withRedesignConfirmationFixtures( - this.test?.fullTitle(), - TransactionEnvelopeType.feeMarket, - async ({ driver, contractRegistry }: TestSuiteArguments) => { - await createERC721WalletInitiatedTransactionAndAssertDetails( - driver, - contractRegistry, - ); - }, - erc721Mocks, - SMART_CONTRACTS.NFTS, - ); - }); - }); - - describe('dApp initiated', async function () { - it('Sends a type 0 transaction (Legacy)', async function () { - await withRedesignConfirmationFixtures( - this.test?.fullTitle(), - TransactionEnvelopeType.legacy, - async ({ driver, contractRegistry }: TestSuiteArguments) => { - await createERC721DAppInitiatedTransactionAndAssertDetails( - driver, - contractRegistry, - ); - }, - erc721Mocks, - SMART_CONTRACTS.NFTS, - ); - }); - - it('Sends a type 2 transaction (EIP1559)', async function () { - await withRedesignConfirmationFixtures( - this.test?.fullTitle(), - TransactionEnvelopeType.feeMarket, - async ({ driver, contractRegistry }: TestSuiteArguments) => { - await createERC721DAppInitiatedTransactionAndAssertDetails( - driver, - contractRegistry, - ); - }, - erc721Mocks, - SMART_CONTRACTS.NFTS, - ); - }); - }); - }); - - describe('ERC1155', function () { - describe('Wallet initiated', async function () { - it('Sends a type 0 transaction (Legacy)', async function () { - await withRedesignConfirmationFixtures( - this.test?.fullTitle(), - TransactionEnvelopeType.legacy, - async ({ driver, contractRegistry }: TestSuiteArguments) => { - await createERC1155WalletInitiatedTransactionAndAssertDetails( - driver, - contractRegistry, - ); - }, - erc1155Mocks, - SMART_CONTRACTS.ERC1155, - ); - }); - - it('Sends a type 2 transaction (EIP1559)', async function () { - await withRedesignConfirmationFixtures( - this.test?.fullTitle(), - TransactionEnvelopeType.feeMarket, - async ({ driver, contractRegistry }: TestSuiteArguments) => { - await createERC1155WalletInitiatedTransactionAndAssertDetails( - driver, - contractRegistry, - ); - }, - erc1155Mocks, - SMART_CONTRACTS.ERC1155, - ); - }); - }); - }); -}); - -async function erc721Mocks(server: Mockttp) { - return [await mockedERC7214BytesNFTTokenSend(server)]; -} - -async function erc1155Mocks(server: Mockttp) { - return [await mockedERC11554BytesNFTTokenSend(server)]; -} - -export async function mockedERC7214BytesNFTTokenSend(mockServer: Mockttp) { - return await mockServer - .forGet('https://www.4byte.directory/api/v1/signatures/') - .withQuery({ hex_signature: '0x23b872dd' }) - .always() - .thenCallback(() => ({ - statusCode: 200, - json: { - count: 1, - next: null, - previous: null, - results: [ - { - bytes_signature: '#rÝ', - created_at: '2016-07-09T03:58:28.927638Z', - hex_signature: '0x23b872dd', - id: 147, - text_signature: 'transferFrom(address,address,uint256)', - }, - ], - }, - })); -} - -export async function mockedERC11554BytesNFTTokenSend(mockServer: Mockttp) { - return await mockServer - .forGet('https://www.4byte.directory/api/v1/signatures/') - .withQuery({ hex_signature: '0xf242432a' }) - .always() - .thenCallback(() => ({ - statusCode: 200, - json: { - count: 1, - next: null, - previous: null, - results: [ - { - bytes_signature: 'òBC*', - created_at: '2018-08-29T20:16:41.650553Z', - hex_signature: '0xf242432a', - id: 93843, - text_signature: - 'safeTransferFrom(address,address,uint256,uint256,bytes)', - }, - ], - }, - })); -} - -async function createERC721WalletInitiatedTransactionAndAssertDetails( - driver: Driver, - contractRegistry?: GanacheContractAddressRegistry, -) { - await unlockWallet(driver); - - const contractAddress = await ( - contractRegistry as GanacheContractAddressRegistry - ).getContractAddress(SMART_CONTRACTS.NFTS); - - const testDapp = new TestDapp(driver); - - await testDapp.openTestDappPage({ contractAddress, url: DAPP_URL }); - - await testDapp.clickERC721MintButton(); - - await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - - const mintConfirmation = new TransactionConfirmation(driver); - - await mintConfirmation.clickFooterConfirmButton(); - - await driver.switchToWindowWithTitle(WINDOW_TITLES.ExtensionInFullScreenView); - - const homePage = new HomePage(driver); - await homePage.goToNFTList(); - await homePage.clickNFTIconOnActivityList(); - - const nftDetailsPage = new NFTDetailsPage(driver); - await nftDetailsPage.clickNFTSendButton(); - - const sendToPage = new SendTokenPage(driver); - await sendToPage.check_pageIsLoaded(); - await sendToPage.fillRecipient(TOKEN_RECIPIENT_ADDRESS); - await sendToPage.goToNextScreen(); - - const tokenTransferTransactionConfirmation = - new TokenTransferTransactionConfirmation(driver); - await tokenTransferTransactionConfirmation.check_walletInitiatedHeadingTitle(); - await tokenTransferTransactionConfirmation.check_networkParagraph(); - await tokenTransferTransactionConfirmation.check_interactingWithParagraph(); - await tokenTransferTransactionConfirmation.check_networkFeeParagraph(); - - await tokenTransferTransactionConfirmation.clickScrollToBottomButton(); - await tokenTransferTransactionConfirmation.clickFooterConfirmButton(); -} - -async function createERC721DAppInitiatedTransactionAndAssertDetails( - driver: Driver, - contractRegistry?: GanacheContractAddressRegistry, -) { - await unlockWallet(driver); - - const contractAddress = await ( - contractRegistry as GanacheContractAddressRegistry - ).getContractAddress(SMART_CONTRACTS.NFTS); - - const testDapp = new TestDapp(driver); - await testDapp.openTestDappPage({ contractAddress, url: DAPP_URL }); - await testDapp.clickERC721MintButton(); - - await driver.delay(veryLargeDelayMs); - await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - const mintConfirmation = new TransactionConfirmation(driver); - await mintConfirmation.clickFooterConfirmButton(); - - await driver.switchToWindowWithTitle(WINDOW_TITLES.TestDApp); - await testDapp.clickERC721TransferFromButton(); - - await driver.delay(veryLargeDelayMs); - await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - const tokenTransferTransactionConfirmation = - new TokenTransferTransactionConfirmation(driver); - await tokenTransferTransactionConfirmation.check_dappInitiatedHeadingTitle(); - await tokenTransferTransactionConfirmation.check_networkParagraph(); - await tokenTransferTransactionConfirmation.check_interactingWithParagraph(); - await tokenTransferTransactionConfirmation.check_networkFeeParagraph(); - - await tokenTransferTransactionConfirmation.clickScrollToBottomButton(); - await tokenTransferTransactionConfirmation.clickFooterConfirmButton(); -} - -async function createERC1155WalletInitiatedTransactionAndAssertDetails( - driver: Driver, - contractRegistry?: GanacheContractAddressRegistry, -) { - await unlockWallet(driver); - - const contractAddress = await ( - contractRegistry as GanacheContractAddressRegistry - ).getContractAddress(SMART_CONTRACTS.ERC1155); - - const testDapp = new TestDapp(driver); - - await testDapp.openTestDappPage({ contractAddress, url: DAPP_URL }); - await testDapp.fillERC1155TokenID('1'); - await testDapp.fillERC1155TokenAmount('1'); - await testDapp.clickERC1155MintButton(); - - await driver.delay(veryLargeDelayMs); - await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - const mintConfirmation = new TransactionConfirmation(driver); - await mintConfirmation.clickFooterConfirmButton(); - - await driver.switchToWindowWithTitle(WINDOW_TITLES.TestDApp); - await testDapp.clickERC1155WatchButton(); - - await driver.delay(veryLargeDelayMs); - await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - const watchAssetConfirmation = new WatchAssetConfirmation(driver); - await watchAssetConfirmation.clickFooterConfirmButton(); - - await driver.switchToWindowWithTitle(WINDOW_TITLES.ExtensionInFullScreenView); - const homePage = new HomePage(driver); - await homePage.goToNFTList(); - await homePage.clickNFTIconOnActivityList(); - - const nftDetailsPage = new NFTDetailsPage(driver); - await nftDetailsPage.clickNFTSendButton(); - - const sendToPage = new SendTokenPage(driver); - await sendToPage.check_pageIsLoaded(); - await sendToPage.fillRecipient(TOKEN_RECIPIENT_ADDRESS); - await sendToPage.fillNFTAmount('1'); - await sendToPage.goToNextScreen(); - - const tokenTransferTransactionConfirmation = - new TokenTransferTransactionConfirmation(driver); - await tokenTransferTransactionConfirmation.check_walletInitiatedHeadingTitle(); - await tokenTransferTransactionConfirmation.check_networkParagraph(); - await tokenTransferTransactionConfirmation.check_interactingWithParagraph(); - await tokenTransferTransactionConfirmation.check_networkFeeParagraph(); - - await tokenTransferTransactionConfirmation.clickScrollToBottomButton(); - await tokenTransferTransactionConfirmation.clickFooterConfirmButton(); -} diff --git a/test/e2e/tests/dapp-interactions/dapp-interactions.spec.js b/test/e2e/tests/dapp-interactions/dapp-interactions.spec.js index 584408134f1a..b992925ffc7a 100644 --- a/test/e2e/tests/dapp-interactions/dapp-interactions.spec.js +++ b/test/e2e/tests/dapp-interactions/dapp-interactions.spec.js @@ -31,7 +31,7 @@ describe('Dapp interactions', function () { await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); await unlockWallet(driver); const notification = await driver.isElementPresent({ - text: 'Add Localhost 8546', + text: 'Allow this site to add a network?', tag: 'h3', }); diff --git a/test/e2e/tests/hardware-wallets/trezor-sign.spec.ts b/test/e2e/tests/hardware-wallets/trezor-sign.spec.ts index f4cbf87b9dd4..169897ed7b16 100644 --- a/test/e2e/tests/hardware-wallets/trezor-sign.spec.ts +++ b/test/e2e/tests/hardware-wallets/trezor-sign.spec.ts @@ -31,11 +31,9 @@ describe('Trezor Hardware Signatures', function (this: Suite) { await openDapp(driver); await driver.clickElement('#signTypedDataV4'); await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - await driver.delay(1000); - await driver.clickElementSafe('.confirm-scroll-to-bottom__button'); + await driver.clickElement('.confirm-scroll-to-bottom__button'); await driver.clickElement({ text: 'Confirm', tag: 'button' }); - await driver.delay(1000); await driver.waitUntilXWindowHandles(2); await driver.switchToWindowWithTitle(WINDOW_TITLES.TestDApp); diff --git a/test/e2e/tests/metrics/errors.spec.js b/test/e2e/tests/metrics/errors.spec.js index 3b003b044b5a..dfe77f758fcb 100644 --- a/test/e2e/tests/metrics/errors.spec.js +++ b/test/e2e/tests/metrics/errors.spec.js @@ -46,8 +46,6 @@ const maskedBackgroundFields = [ 'AppStateController.notificationGasPollTokens', 'AppStateController.popupGasPollTokens', 'CurrencyController.currencyRates.ETH.conversionDate', - 'CurrencyController.currencyRates.LineaETH.conversionDate', - 'CurrencyController.currencyRates.SepoliaETH.conversionDate', ]; const maskedUiFields = maskedBackgroundFields.map(backgroundToUiField); @@ -59,7 +57,6 @@ const removedBackgroundFields = [ 'AppStateController.currentPopupId', 'AppStateController.timeoutMinutes', 'AppStateController.lastInteractedConfirmationInfo', - 'BridgeController.bridgeState.quoteRequest.walletAddress', 'PPOMController.chainStatus.0x539.lastVisited', 'PPOMController.versionInfo', // This property is timing-dependent @@ -865,19 +862,6 @@ describe('Sentry errors', function () { it('should not have extra properties in UI state mask @no-mmi', async function () { const expectedMissingState = { - bridgeState: { - // This can get wiped out during initialization due to a bug in - // the "resetState" method - quoteRequest: { - destChainId: true, - destTokenAddress: true, - srcChainId: true, - srcTokenAmount: true, - walletAddress: false, - }, - quotesLastFetched: true, - quotesLoadingStatus: true, - }, currentPopupId: false, // Initialized as undefined // Part of transaction controller store, but missing from the initial // state @@ -885,7 +869,6 @@ describe('Sentry errors', function () { preferences: { autoLockTimeLimit: true, // Initialized as undefined showConfirmationAdvancedDetails: true, - privacyMode: false, }, smartTransactionsState: { fees: { diff --git a/test/e2e/tests/metrics/nft-detection-metrics.spec.js b/test/e2e/tests/metrics/nft-detection-metrics.spec.js index a0c901087425..3c77fdb66731 100644 --- a/test/e2e/tests/metrics/nft-detection-metrics.spec.js +++ b/test/e2e/tests/metrics/nft-detection-metrics.spec.js @@ -101,7 +101,7 @@ describe('Nft detection event @no-mmi', function () { locale: 'en', chain_id: '0x539', environment_type: 'fullscreen', - is_profile_syncing_enabled: true, + is_profile_syncing_enabled: null, }); assert.deepStrictEqual(events[2].properties, { nft_autodetection_enabled: true, diff --git a/test/e2e/tests/metrics/signature-approved.spec.js b/test/e2e/tests/metrics/signature-approved.spec.js index 2ea84d281b50..c6990820af8b 100644 --- a/test/e2e/tests/metrics/signature-approved.spec.js +++ b/test/e2e/tests/metrics/signature-approved.spec.js @@ -1,5 +1,4 @@ const { strict: assert } = require('assert'); - const { defaultGanacheOptions, switchToNotificationWindow, @@ -48,16 +47,6 @@ async function mockSegment(mockServer) { ]; } -const expectedEventPropertiesBase = { - account_type: 'MetaMask', - category: 'inpage_provider', - locale: 'en', - chain_id: '0x539', - environment_type: 'background', - security_alert_reason: 'CheckingChain', - security_alert_response: 'loading', -}; - describe('Signature Approved Event @no-mmi', function () { it('Successfully tracked for signTypedData_v4', async function () { await withFixtures( @@ -87,21 +76,31 @@ describe('Signature Approved Event @no-mmi', function () { const events = await getEventPayloads(driver, mockedEndpoints); assert.deepStrictEqual(events[0].properties, { - ...expectedEventPropertiesBase, + account_type: 'MetaMask', signature_type: 'eth_signTypedData_v4', + category: 'inpage_provider', + locale: 'en', + chain_id: '0x539', eip712_primary_type: 'Mail', + environment_type: 'background', + security_alert_reason: 'NotApplicable', + security_alert_response: 'NotApplicable', }); assert.deepStrictEqual(events[1].properties, { - ...expectedEventPropertiesBase, + account_type: 'MetaMask', signature_type: 'eth_signTypedData_v4', + category: 'inpage_provider', + locale: 'en', + chain_id: '0x539', eip712_primary_type: 'Mail', - security_alert_response: 'Benign', + environment_type: 'background', + security_alert_reason: 'NotApplicable', + security_alert_response: 'NotApplicable', }); }, ); }); - it('Successfully tracked for signTypedData_v3', async function () { await withFixtures( { @@ -128,21 +127,29 @@ describe('Signature Approved Event @no-mmi', function () { await validateContractDetails(driver); await clickSignOnSignatureConfirmation({ driver }); const events = await getEventPayloads(driver, mockedEndpoints); - assert.deepStrictEqual(events[0].properties, { - ...expectedEventPropertiesBase, + account_type: 'MetaMask', signature_type: 'eth_signTypedData_v3', + category: 'inpage_provider', + locale: 'en', + chain_id: '0x539', + environment_type: 'background', + security_alert_reason: 'NotApplicable', + security_alert_response: 'NotApplicable', }); - assert.deepStrictEqual(events[1].properties, { - ...expectedEventPropertiesBase, + account_type: 'MetaMask', signature_type: 'eth_signTypedData_v3', - security_alert_response: 'Benign', + category: 'inpage_provider', + locale: 'en', + chain_id: '0x539', + environment_type: 'background', + security_alert_reason: 'NotApplicable', + security_alert_response: 'NotApplicable', }); }, ); }); - it('Successfully tracked for signTypedData', async function () { await withFixtures( { @@ -168,21 +175,29 @@ describe('Signature Approved Event @no-mmi', function () { await switchToNotificationWindow(driver); await clickSignOnSignatureConfirmation({ driver }); const events = await getEventPayloads(driver, mockedEndpoints); - assert.deepStrictEqual(events[0].properties, { - ...expectedEventPropertiesBase, + account_type: 'MetaMask', signature_type: 'eth_signTypedData', + category: 'inpage_provider', + locale: 'en', + chain_id: '0x539', + environment_type: 'background', + security_alert_reason: 'NotApplicable', + security_alert_response: 'NotApplicable', }); - assert.deepStrictEqual(events[1].properties, { - ...expectedEventPropertiesBase, + account_type: 'MetaMask', signature_type: 'eth_signTypedData', - security_alert_response: 'Benign', + category: 'inpage_provider', + locale: 'en', + chain_id: '0x539', + environment_type: 'background', + security_alert_reason: 'NotApplicable', + security_alert_response: 'NotApplicable', }); }, ); }); - it('Successfully tracked for personalSign', async function () { await withFixtures( { @@ -208,16 +223,25 @@ describe('Signature Approved Event @no-mmi', function () { await switchToNotificationWindow(driver); await clickSignOnSignatureConfirmation({ driver }); const events = await getEventPayloads(driver, mockedEndpoints); - assert.deepStrictEqual(events[0].properties, { - ...expectedEventPropertiesBase, + account_type: 'MetaMask', signature_type: 'personal_sign', + category: 'inpage_provider', + locale: 'en', + chain_id: '0x539', + environment_type: 'background', + security_alert_reason: 'NotApplicable', + security_alert_response: 'NotApplicable', }); - assert.deepStrictEqual(events[1].properties, { - ...expectedEventPropertiesBase, + account_type: 'MetaMask', signature_type: 'personal_sign', - security_alert_response: 'Benign', + category: 'inpage_provider', + locale: 'en', + chain_id: '0x539', + environment_type: 'background', + security_alert_reason: 'NotApplicable', + security_alert_response: 'NotApplicable', }); }, ); diff --git a/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-background-state.json b/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-background-state.json index 6d77cd3ae351..f96d03d96da0 100644 --- a/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-background-state.json +++ b/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-background-state.json @@ -62,18 +62,12 @@ "BridgeController": { "bridgeState": { "bridgeFeatureFlags": { - "extensionConfig": "object", "extensionSupport": "boolean", "srcNetworkAllowlist": { "0": "string", "1": "string", "2": "string" }, "destNetworkAllowlist": { "0": "string", "1": "string", "2": "string" } }, "destTokens": {}, "destTopAssets": {}, - "quoteRequest": { - "slippage": 0.5, - "srcTokenAddress": "0x0000000000000000000000000000000000000000" - }, - "quotes": {}, "srcTokens": {}, "srcTopAssets": {} } @@ -85,16 +79,6 @@ "conversionDate": "number", "conversionRate": 1700, "usdConversionRate": 1700 - }, - "LineaETH": { - "conversionDate": "number", - "conversionRate": 1700, - "usdConversionRate": 1700 - }, - "SepoliaETH": { - "conversionDate": "number", - "conversionRate": 1700, - "usdConversionRate": 1700 } }, "currentCurrency": "usd" @@ -144,7 +128,7 @@ "MultichainBalancesController": { "balances": "object" }, "MultichainRatesController": { "fiatCurrency": "usd", - "rates": { "btc": { "conversionDate": 0, "conversionRate": 0 } }, + "rates": { "btc": { "conversionDate": 0, "conversionRate": "0" } }, "cryptocurrencies": ["btc"] }, "NameController": { "names": "object", "nameSources": "object" }, diff --git a/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-ui-state.json b/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-ui-state.json index e577bb71a6be..c4c4ea71609d 100644 --- a/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-ui-state.json +++ b/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-ui-state.json @@ -54,16 +54,6 @@ "conversionDate": "number", "conversionRate": 1700, "usdConversionRate": 1700 - }, - "LineaETH": { - "conversionDate": "number", - "conversionRate": 1700, - "usdConversionRate": 1700 - }, - "SepoliaETH": { - "conversionDate": "number", - "conversionRate": 1700, - "usdConversionRate": 1700 } }, "connectedStatusPopoverHasBeenShown": true, @@ -197,7 +187,7 @@ "lastFetchedBlockNumbers": "object", "submitHistory": "object", "fiatCurrency": "usd", - "rates": { "btc": { "conversionDate": 0, "conversionRate": 0 } }, + "rates": { "btc": { "conversionDate": 0, "conversionRate": "0" } }, "cryptocurrencies": ["btc"], "snaps": "object", "jobs": "object", @@ -213,6 +203,7 @@ "isSignedIn": "boolean", "isProfileSyncingEnabled": null, "isProfileSyncingUpdateLoading": "boolean", + "submitHistory": "object", "subscriptionAccountsSeen": "object", "isMetamaskNotificationsFeatureSeen": "boolean", "isNotificationServicesEnabled": "boolean", @@ -267,18 +258,12 @@ }, "bridgeState": { "bridgeFeatureFlags": { - "extensionConfig": "object", "extensionSupport": "boolean", "srcNetworkAllowlist": { "0": "string", "1": "string", "2": "string" }, "destNetworkAllowlist": { "0": "string", "1": "string", "2": "string" } }, "destTokens": {}, "destTopAssets": {}, - "quoteRequest": { - "slippage": 0.5, - "srcTokenAddress": "0x0000000000000000000000000000000000000000" - }, - "quotes": {}, "srcTokens": {}, "srcTopAssets": {} }, diff --git a/test/e2e/tests/metrics/token-detection-metrics.spec.js b/test/e2e/tests/metrics/token-detection-metrics.spec.js index 923f7c86a242..669aff0a9290 100644 --- a/test/e2e/tests/metrics/token-detection-metrics.spec.js +++ b/test/e2e/tests/metrics/token-detection-metrics.spec.js @@ -98,7 +98,7 @@ describe('Token detection event @no-mmi', function () { locale: 'en', chain_id: '0x539', environment_type: 'fullscreen', - is_profile_syncing_enabled: true, + is_profile_syncing_enabled: null, }); assert.deepStrictEqual(events[2].properties, { token_detection_enabled: true, diff --git a/test/e2e/tests/metrics/wallet-created.spec.js b/test/e2e/tests/metrics/wallet-created.spec.js index fbe80fb595dc..890ac9342a8a 100644 --- a/test/e2e/tests/metrics/wallet-created.spec.js +++ b/test/e2e/tests/metrics/wallet-created.spec.js @@ -87,7 +87,7 @@ describe('Wallet Created Events @no-mmi', function () { locale: 'en', chain_id: '0x539', environment_type: 'fullscreen', - is_profile_syncing_enabled: true, + is_profile_syncing_enabled: null, }); }, ); diff --git a/test/e2e/tests/network/add-custom-network.spec.js b/test/e2e/tests/network/add-custom-network.spec.js index d24beb431c1c..dc8f38e1168c 100644 --- a/test/e2e/tests/network/add-custom-network.spec.js +++ b/test/e2e/tests/network/add-custom-network.spec.js @@ -119,7 +119,9 @@ const inputData = { }; describe('Custom network', function () { + const chainID = '42161'; const networkURL = 'https://arbitrum-mainnet.infura.io'; + const networkNAME = 'Arbitrum One'; const currencySYMBOL = 'ETH'; const blockExplorerURL = 'https://explorer.arbitrum.io'; @@ -454,29 +456,52 @@ describe('Custom network', function () { text: 'Add', }); - const [currencySymbol, networkUrl] = await driver.findElements( - '.definition-list dd', + // verify network details + const title = await driver.findElement({ + tag: 'span', + text: 'Arbitrum One', + }); + + assert.equal( + await title.getText(), + 'Arbitrum One', + 'Title of popup should be selected network', ); + + const [networkName, networkUrl, chainIdElement, currencySymbol] = + await driver.findElements('.definition-list dd'); + assert.equal( - await currencySymbol.getText(), - currencySYMBOL, - 'Currency symbol is not correctly displayed', + await networkName.getText(), + networkNAME, + 'Network name is not correctly displayed', ); assert.equal( await networkUrl.getText(), networkURL, 'Network Url is not correctly displayed', ); + assert.equal( + await chainIdElement.getText(), + chainID.toString(), + 'Chain Id is not correctly displayed', + ); + assert.equal( + await currencySymbol.getText(), + currencySYMBOL, + 'Currency symbol is not correctly displayed', + ); - await driver.clickElement({ tag: 'a', text: 'See details' }); + await driver.clickElement({ tag: 'a', text: 'View all details' }); const networkDetailsLabels = await driver.findElements('dd'); assert.equal( - await networkDetailsLabels[4].getText(), + await networkDetailsLabels[8].getText(), blockExplorerURL, 'Block Explorer URL is not correct', ); + await driver.clickElement({ tag: 'button', text: 'Close' }); await driver.clickElement({ tag: 'button', text: 'Approve' }); // verify network switched diff --git a/test/e2e/tests/network/switch-network.spec.ts b/test/e2e/tests/network/switch-network.spec.ts deleted file mode 100644 index a45e634dbbec..000000000000 --- a/test/e2e/tests/network/switch-network.spec.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { Suite } from 'mocha'; -import { Driver } from '../../webdriver/driver'; -import { withFixtures, defaultGanacheOptions } from '../../helpers'; -import FixtureBuilder from '../../fixture-builder'; -import { Ganache } from '../../seeder/ganache'; -import { loginWithBalanceValidation } from '../../page-objects/flows/login.flow'; -import HomePage from '../../page-objects/pages/homepage'; -import HeaderNavbar from '../../page-objects/pages/header-navbar'; -import { - switchToNetworkFlow, - searchAndSwitchToNetworkFlow, -} from '../../page-objects/flows/network.flow'; - -describe('Switch network - ', function (this: Suite) { - it('Switch networks to existing and new networks', async function () { - await withFixtures( - { - fixtures: new FixtureBuilder().build(), - ganacheOptions: defaultGanacheOptions, - title: this.test?.fullTitle(), - }, - async ({ - driver, - ganacheServer, - }: { - driver: Driver; - ganacheServer?: Ganache; - }) => { - await loginWithBalanceValidation(driver, ganacheServer); - const homePage = new HomePage(driver); - - // Validate the default network is Localhost 8545 - await new HeaderNavbar(driver).check_currentSelectedNetwork( - 'Localhost 8545', - ); - - // Validate the switch network functionality to Ethereum Mainnet - await switchToNetworkFlow(driver, 'Ethereum Mainnet'); - await homePage.check_ganacheBalanceIsDisplayed(ganacheServer); - - // Validate the switch network functionality to test network - await switchToNetworkFlow(driver, 'Localhost 8545', true); - await homePage.check_ganacheBalanceIsDisplayed(ganacheServer); - - // Add Arbitrum network and perform the switch network functionality - await searchAndSwitchToNetworkFlow(driver, 'Arbitrum One'); - await homePage.check_ganacheBalanceIsDisplayed(ganacheServer); - - // Validate the switch network functionality back to Ethereum Mainnet - await switchToNetworkFlow(driver, 'Ethereum Mainnet'); - await homePage.check_ganacheBalanceIsDisplayed(ganacheServer); - }, - ); - }); -}); diff --git a/test/e2e/tests/notifications/account-syncing/helpers.ts b/test/e2e/tests/notifications/account-syncing/helpers.ts deleted file mode 100644 index 5e2694067eed..000000000000 --- a/test/e2e/tests/notifications/account-syncing/helpers.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { isManifestV3 } from '../../../../../shared/modules/mv3.utils'; -import { - completeSRPRevealQuiz, - openSRPRevealQuiz, - tapAndHoldToRevealSRP, -} from '../../../helpers'; -import { Driver } from '../../../webdriver/driver'; - -export const IS_ACCOUNT_SYNCING_ENABLED = isManifestV3; - -export const getSRP = async (driver: Driver, password: string) => { - await openSRPRevealQuiz(driver); - await completeSRPRevealQuiz(driver); - await driver.fill('[data-testid="input-password"]', password); - await driver.press('[data-testid="input-password"]', driver.Key.ENTER); - await tapAndHoldToRevealSRP(driver); - return (await driver.findElement('[data-testid="srp_text"]')).getText(); -}; diff --git a/test/e2e/tests/notifications/account-syncing/importing-private-key-account.spec.ts b/test/e2e/tests/notifications/account-syncing/importing-private-key-account.spec.ts deleted file mode 100644 index 1494d8b2ceb3..000000000000 --- a/test/e2e/tests/notifications/account-syncing/importing-private-key-account.spec.ts +++ /dev/null @@ -1,112 +0,0 @@ -import { Mockttp } from 'mockttp'; -import { - withFixtures, - defaultGanacheOptions, - completeImportSRPOnboardingFlow, -} from '../../../helpers'; -import FixtureBuilder from '../../../fixture-builder'; -import { mockNotificationServices } from '../mocks'; -import { - NOTIFICATIONS_TEAM_IMPORTED_PRIVATE_KEY, - NOTIFICATIONS_TEAM_PASSWORD, - NOTIFICATIONS_TEAM_SEED_PHRASE, -} from '../constants'; -import { UserStorageMockttpController } from '../../../helpers/user-storage/userStorageMockttpController'; -import HeaderNavbar from '../../../page-objects/pages/header-navbar'; -import AccountListPage from '../../../page-objects/pages/account-list-page'; -import { accountsSyncMockResponse } from './mockData'; -import { IS_ACCOUNT_SYNCING_ENABLED } from './helpers'; - -describe('Account syncing - Import With Private Key @no-mmi', function () { - if (!IS_ACCOUNT_SYNCING_ENABLED) { - return; - } - describe('from inside MetaMask', function () { - it('does not sync accounts imported with private keys', async function () { - const userStorageMockttpController = new UserStorageMockttpController(); - - await withFixtures( - { - fixtures: new FixtureBuilder({ onboarding: true }).build(), - ganacheOptions: defaultGanacheOptions, - title: this.test?.fullTitle(), - testSpecificMock: (server: Mockttp) => { - userStorageMockttpController.setupPath('accounts', server, { - getResponse: accountsSyncMockResponse, - }); - - return mockNotificationServices( - server, - userStorageMockttpController, - ); - }, - }, - async ({ driver }) => { - await driver.navigate(); - await completeImportSRPOnboardingFlow( - driver, - NOTIFICATIONS_TEAM_SEED_PHRASE, - NOTIFICATIONS_TEAM_PASSWORD, - ); - - const header = new HeaderNavbar(driver); - await header.check_pageIsLoaded(); - await header.openAccountMenu(); - - const accountListPage = new AccountListPage(driver); - await accountListPage.check_pageIsLoaded(); - await accountListPage.check_numberOfAvailableAccounts( - accountsSyncMockResponse.length, - ); - await accountListPage.check_accountDisplayedInAccountList( - 'My First Synced Account', - ); - await accountListPage.check_accountDisplayedInAccountList( - 'My Second Synced Account', - ); - await accountListPage.openAccountOptionsMenu(); - await accountListPage.addNewImportedAccount( - NOTIFICATIONS_TEAM_IMPORTED_PRIVATE_KEY, - ); - }, - ); - - await withFixtures( - { - fixtures: new FixtureBuilder({ onboarding: true }).build(), - ganacheOptions: defaultGanacheOptions, - title: this.test?.fullTitle(), - testSpecificMock: (server: Mockttp) => { - userStorageMockttpController.setupPath('accounts', server); - return mockNotificationServices( - server, - userStorageMockttpController, - ); - }, - }, - async ({ driver }) => { - await driver.navigate(); - await completeImportSRPOnboardingFlow( - driver, - NOTIFICATIONS_TEAM_SEED_PHRASE, - NOTIFICATIONS_TEAM_PASSWORD, - ); - - const header = new HeaderNavbar(driver); - await header.check_pageIsLoaded(); - await header.openAccountMenu(); - - const accountListPage = new AccountListPage(driver); - await accountListPage.check_pageIsLoaded(); - await accountListPage.check_numberOfAvailableAccounts(2); - await accountListPage.check_accountDisplayedInAccountList( - 'My First Synced Account', - ); - await accountListPage.check_accountDisplayedInAccountList( - 'My Second Synced Account', - ); - }, - ); - }); - }); -}); diff --git a/test/e2e/tests/notifications/account-syncing/mockData.ts b/test/e2e/tests/notifications/account-syncing/mockData.ts deleted file mode 100644 index 96e92ecd8491..000000000000 --- a/test/e2e/tests/notifications/account-syncing/mockData.ts +++ /dev/null @@ -1,12 +0,0 @@ -export const accountsSyncMockResponse = [ - { - HashedKey: - '997050281e559a2bb40d1c2e73d9f0887cbea1b81ff9dd7815917949e37f4f2f', - Data: '{"v":"1","t":"scrypt","d":"1yC/ZXarV57HbqEZ46nH0JWgXfPl86nTHD7kai2g5gm290FM9tw5QjOaAAwIuQESEE8TIM/J9pIj7nmlGi+BZrevTtK3DXWXwnUQsCP7amKd5Q4gs3EEQgXpA0W+WJUgyElj869rwIv/C6tl5E2pK4j/0EAjMSIm1TGoj9FPohyRgZsOIt8VhZfb7w0GODsjPwPIkN6zazvJ3gAFYFPh7yRtebFs86z3fzqCWZ9zakdCHntchC2oZiaApXR9yzaPlGgnPg==","o":{"N":131072,"r":8,"p":1,"dkLen":16},"saltLen":16}', - }, - { - HashedKey: - 'e53d8feb65b4cf0c339e57bee2a81b155e056622f9192c54b707f928c8a42a7a', - Data: '{"v":"1","t":"scrypt","d":"O7QEtUo7q/jG+UNkD/HOxQARGGRXsGPrLsDlkwDfgfoYlPI0To/M3pJRBlKD0RLEFIPHtHBEA5bv/2izB21VljvhMnhHfo0KgQ+e8Uq1t7grwa+r+ge3qbPNY+w78Xt8GtC+Hkrw5fORKvCn+xjzaCHYV6RxKYbp1TpyCJq7hDrr1XiyL8kqbpE0hAHALrrQOoV9/WXJi9pC5J118kquXx8CNA1P5wO/BXKp1AbryGR6kVW3lsp1sy3lYE/TApa5lTj+","o":{"N":131072,"r":8,"p":1,"dkLen":16},"saltLen":16}', - }, -]; diff --git a/test/e2e/tests/notifications/account-syncing/new-user-sync.spec.ts b/test/e2e/tests/notifications/account-syncing/new-user-sync.spec.ts deleted file mode 100644 index eb0c2c7b65e8..000000000000 --- a/test/e2e/tests/notifications/account-syncing/new-user-sync.spec.ts +++ /dev/null @@ -1,118 +0,0 @@ -import { Mockttp } from 'mockttp'; -import { - withFixtures, - defaultGanacheOptions, - completeImportSRPOnboardingFlow, - completeCreateNewWalletOnboardingFlow, -} from '../../../helpers'; -import FixtureBuilder from '../../../fixture-builder'; -import { mockNotificationServices } from '../mocks'; -import { NOTIFICATIONS_TEAM_PASSWORD } from '../constants'; -import { UserStorageMockttpController } from '../../../helpers/user-storage/userStorageMockttpController'; -import HeaderNavbar from '../../../page-objects/pages/header-navbar'; -import AccountListPage from '../../../page-objects/pages/account-list-page'; -import { getSRP, IS_ACCOUNT_SYNCING_ENABLED } from './helpers'; - -describe('Account syncing - New User @no-mmi', function () { - if (!IS_ACCOUNT_SYNCING_ENABLED) { - return; - } - - describe('from inside MetaMask', function () { - it('syncs after new wallet creation', async function () { - const userStorageMockttpController = new UserStorageMockttpController(); - let walletSrp: string; - - await withFixtures( - { - fixtures: new FixtureBuilder({ onboarding: true }).build(), - ganacheOptions: defaultGanacheOptions, - title: this.test?.fullTitle(), - testSpecificMock: (server: Mockttp) => { - userStorageMockttpController.setupPath('accounts', server); - - return mockNotificationServices( - server, - userStorageMockttpController, - ); - }, - }, - async ({ driver }) => { - await driver.navigate(); - - // Create a new wallet - await completeCreateNewWalletOnboardingFlow( - driver, - NOTIFICATIONS_TEAM_PASSWORD, - ); - - // Open account menu and validate 1 account is shown - const header = new HeaderNavbar(driver); - await header.check_pageIsLoaded(); - await header.openAccountMenu(); - - const accountListPage = new AccountListPage(driver); - await accountListPage.check_pageIsLoaded(); - await accountListPage.check_numberOfAvailableAccounts(1); - await accountListPage.check_accountDisplayedInAccountList( - 'Account 1', - ); - - // Add a second account - await accountListPage.openAccountOptionsMenu(); - await accountListPage.addNewAccountWithCustomLabel( - 'My Second Account', - ); - - // Set SRP to use for retreival - walletSrp = await getSRP(driver, NOTIFICATIONS_TEAM_PASSWORD); - if (!walletSrp) { - throw new Error('Wallet SRP was not set'); - } - }, - ); - - await withFixtures( - { - fixtures: new FixtureBuilder({ onboarding: true }).build(), - ganacheOptions: defaultGanacheOptions, - title: this.test?.fullTitle(), - testSpecificMock: (server: Mockttp) => { - userStorageMockttpController.setupPath('accounts', server); - return mockNotificationServices( - server, - userStorageMockttpController, - ); - }, - }, - async ({ driver }) => { - await driver.navigate(); - - // Onboard with import flow using SRP from new account created above - await completeImportSRPOnboardingFlow( - driver, - walletSrp, - NOTIFICATIONS_TEAM_PASSWORD, - ); - - // Open account menu and validate the 2 accounts have been retrieved - const header = new HeaderNavbar(driver); - await header.check_pageIsLoaded(); - await header.openAccountMenu(); - - const accountListPage = new AccountListPage(driver); - await accountListPage.check_pageIsLoaded(); - - await accountListPage.check_numberOfAvailableAccounts(2); - - await accountListPage.check_accountDisplayedInAccountList( - 'Account 1', - ); - await accountListPage.check_accountDisplayedInAccountList( - 'My Second Account', - ); - }, - ); - }); - }); -}); diff --git a/test/e2e/tests/notifications/account-syncing/onboarding-with-opt-out.spec.ts b/test/e2e/tests/notifications/account-syncing/onboarding-with-opt-out.spec.ts deleted file mode 100644 index 5cf0bb3c4d19..000000000000 --- a/test/e2e/tests/notifications/account-syncing/onboarding-with-opt-out.spec.ts +++ /dev/null @@ -1,168 +0,0 @@ -import { Mockttp } from 'mockttp'; -import { - withFixtures, - defaultGanacheOptions, - completeImportSRPOnboardingFlow, - importSRPOnboardingFlow, - onboardingCompleteWalletCreationWithOptOut, - completeCreateNewWalletOnboardingFlowWithOptOut, -} from '../../../helpers'; -import FixtureBuilder from '../../../fixture-builder'; -import { mockNotificationServices } from '../mocks'; -import { - NOTIFICATIONS_TEAM_PASSWORD, - NOTIFICATIONS_TEAM_SEED_PHRASE, -} from '../constants'; -import { UserStorageMockttpController } from '../../../helpers/user-storage/userStorageMockttpController'; -import HeaderNavbar from '../../../page-objects/pages/header-navbar'; -import AccountListPage from '../../../page-objects/pages/account-list-page'; -import { accountsSyncMockResponse } from './mockData'; -import { getSRP, IS_ACCOUNT_SYNCING_ENABLED } from './helpers'; - -describe('Account syncing - Opt-out Profile Sync @no-mmi', function () { - if (!IS_ACCOUNT_SYNCING_ENABLED) { - return; - } - describe('from inside MetaMask', function () { - let walletSrp: string; - it('does not sync when profile sync is turned off - previously synced account', async function () { - const userStorageMockttpController = new UserStorageMockttpController(); - - await withFixtures( - { - fixtures: new FixtureBuilder({ onboarding: true }).build(), - ganacheOptions: defaultGanacheOptions, - title: this.test?.fullTitle(), - testSpecificMock: (server: Mockttp) => { - // Mocks are still set up to ensure that requests are not matched - userStorageMockttpController.setupPath('accounts', server, { - getResponse: accountsSyncMockResponse, - }); - return mockNotificationServices( - server, - userStorageMockttpController, - ); - }, - }, - async ({ driver }) => { - await driver.navigate(); - await importSRPOnboardingFlow( - driver, - NOTIFICATIONS_TEAM_SEED_PHRASE, - NOTIFICATIONS_TEAM_PASSWORD, - ); - - await onboardingCompleteWalletCreationWithOptOut(driver, { - isNewWallet: false, - basicFunctionality: false, - profileSync: true, - assets: false, - }); - - const header = new HeaderNavbar(driver); - await header.check_pageIsLoaded(); - await header.openAccountMenu(); - - const accountListPage = new AccountListPage(driver); - await accountListPage.check_pageIsLoaded(); - await accountListPage.check_numberOfAvailableAccounts(1); - await accountListPage.check_accountIsNotDisplayedInAccountList( - 'My First Synced Account', - ); - await accountListPage.check_accountIsNotDisplayedInAccountList( - 'My Second Synced Account', - ); - await accountListPage.check_accountDisplayedInAccountList( - 'Account 1', - ); - }, - ); - }); - - it('does not sync when profile sync is turned off - new user', async function () { - const userStorageMockttpController = new UserStorageMockttpController(); - - await withFixtures( - { - fixtures: new FixtureBuilder({ onboarding: true }).build(), - ganacheOptions: defaultGanacheOptions, - title: this.test?.fullTitle(), - testSpecificMock: (server: Mockttp) => { - // Mocks are still set up to ensure that requests are not matched - userStorageMockttpController.setupPath('accounts', server); - return mockNotificationServices( - server, - userStorageMockttpController, - ); - }, - }, - async ({ driver }) => { - await driver.navigate(); - await completeCreateNewWalletOnboardingFlowWithOptOut( - driver, - NOTIFICATIONS_TEAM_PASSWORD, - { - isNewWallet: true, - basicFunctionality: false, - profileSync: true, - assets: false, - }, - ); - - const header = new HeaderNavbar(driver); - await header.check_pageIsLoaded(); - await header.openAccountMenu(); - - const accountListPage = new AccountListPage(driver); - await accountListPage.check_pageIsLoaded(); - await accountListPage.check_numberOfAvailableAccounts(1); - await accountListPage.check_accountDisplayedInAccountList( - 'Account 1', - ); - await accountListPage.addNewAccountWithCustomLabel('New Account'); - - // Set SRP to use for retreival - walletSrp = await getSRP(driver, NOTIFICATIONS_TEAM_PASSWORD); - if (!walletSrp) { - throw new Error('Wallet SRP was not set'); - } - }, - ); - - await withFixtures( - { - fixtures: new FixtureBuilder({ onboarding: true }).build(), - ganacheOptions: defaultGanacheOptions, - title: this.test?.fullTitle(), - testSpecificMock: (server: Mockttp) => { - // Mocks are still set up to ensure that requests are not matched - userStorageMockttpController.setupPath('accounts', server); - return mockNotificationServices( - server, - userStorageMockttpController, - ); - }, - }, - async ({ driver }) => { - await driver.navigate(); - await completeImportSRPOnboardingFlow( - driver, - walletSrp, - NOTIFICATIONS_TEAM_PASSWORD, - ); - - const header = new HeaderNavbar(driver); - await header.check_pageIsLoaded(); - await header.openAccountMenu(); - - const accountListPage = new AccountListPage(driver); - await accountListPage.check_pageIsLoaded(); - await accountListPage.check_numberOfAvailableAccounts(1); - await accountListPage.check_accountDisplayedInAccountList( - 'Account 1', - ); - }, - ); - }); - }); -}); diff --git a/test/e2e/tests/notifications/account-syncing/sync-after-adding-account.spec.ts b/test/e2e/tests/notifications/account-syncing/sync-after-adding-account.spec.ts deleted file mode 100644 index d6c0dc373f69..000000000000 --- a/test/e2e/tests/notifications/account-syncing/sync-after-adding-account.spec.ts +++ /dev/null @@ -1,212 +0,0 @@ -import { Mockttp } from 'mockttp'; -import { - withFixtures, - defaultGanacheOptions, - completeImportSRPOnboardingFlow, -} from '../../../helpers'; -import FixtureBuilder from '../../../fixture-builder'; -import { mockNotificationServices } from '../mocks'; -import { - NOTIFICATIONS_TEAM_PASSWORD, - NOTIFICATIONS_TEAM_SEED_PHRASE, -} from '../constants'; -import { UserStorageMockttpController } from '../../../helpers/user-storage/userStorageMockttpController'; -import HeaderNavbar from '../../../page-objects/pages/header-navbar'; -import AccountListPage from '../../../page-objects/pages/account-list-page'; -import { accountsSyncMockResponse } from './mockData'; -import { IS_ACCOUNT_SYNCING_ENABLED } from './helpers'; - -describe('Account syncing - Add Account @no-mmi', function () { - if (!IS_ACCOUNT_SYNCING_ENABLED) { - return; - } - describe('from inside MetaMask', function () { - it('syncs newly added accounts - custom name', async function () { - const userStorageMockttpController = new UserStorageMockttpController(); - - await withFixtures( - { - fixtures: new FixtureBuilder({ onboarding: true }).build(), - ganacheOptions: defaultGanacheOptions, - title: this.test?.fullTitle(), - testSpecificMock: (server: Mockttp) => { - userStorageMockttpController.setupPath('accounts', server, { - getResponse: accountsSyncMockResponse, - }); - - return mockNotificationServices( - server, - userStorageMockttpController, - ); - }, - }, - async ({ driver }) => { - await driver.navigate(); - await completeImportSRPOnboardingFlow( - driver, - NOTIFICATIONS_TEAM_SEED_PHRASE, - NOTIFICATIONS_TEAM_PASSWORD, - ); - - const header = new HeaderNavbar(driver); - await header.check_pageIsLoaded(); - await header.openAccountMenu(); - - const accountListPage = new AccountListPage(driver); - await accountListPage.check_pageIsLoaded(); - await accountListPage.check_numberOfAvailableAccounts( - accountsSyncMockResponse.length, - ); - await accountListPage.check_accountDisplayedInAccountList( - 'My First Synced Account', - ); - await accountListPage.check_accountDisplayedInAccountList( - 'My Second Synced Account', - ); - await accountListPage.addNewAccountWithCustomLabel( - 'My third account', - ); - }, - ); - - await withFixtures( - { - fixtures: new FixtureBuilder({ onboarding: true }).build(), - ganacheOptions: defaultGanacheOptions, - title: this.test?.fullTitle(), - testSpecificMock: (server: Mockttp) => { - userStorageMockttpController.setupPath('accounts', server); - return mockNotificationServices( - server, - userStorageMockttpController, - ); - }, - }, - async ({ driver }) => { - await driver.navigate(); - await completeImportSRPOnboardingFlow( - driver, - NOTIFICATIONS_TEAM_SEED_PHRASE, - NOTIFICATIONS_TEAM_PASSWORD, - ); - - const header = new HeaderNavbar(driver); - await header.check_pageIsLoaded(); - await header.openAccountMenu(); - - const accountListPage = new AccountListPage(driver); - await accountListPage.check_pageIsLoaded(); - - const accountSyncResponse = - userStorageMockttpController.paths.get('accounts')?.response; - - await accountListPage.check_numberOfAvailableAccounts( - accountSyncResponse?.length as number, - ); - await accountListPage.check_accountDisplayedInAccountList( - 'My First Synced Account', - ); - await accountListPage.check_accountDisplayedInAccountList( - 'My Second Synced Account', - ); - await accountListPage.check_accountDisplayedInAccountList( - 'My third account', - ); - }, - ); - }); - - it('syncs newly added accounts - default name', async function () { - const userStorageMockttpController = new UserStorageMockttpController(); - - await withFixtures( - { - fixtures: new FixtureBuilder({ onboarding: true }).build(), - ganacheOptions: defaultGanacheOptions, - title: this.test?.fullTitle(), - testSpecificMock: (server: Mockttp) => { - userStorageMockttpController.setupPath('accounts', server, { - getResponse: accountsSyncMockResponse, - }); - - return mockNotificationServices( - server, - userStorageMockttpController, - ); - }, - }, - async ({ driver }) => { - await driver.navigate(); - await completeImportSRPOnboardingFlow( - driver, - NOTIFICATIONS_TEAM_SEED_PHRASE, - NOTIFICATIONS_TEAM_PASSWORD, - ); - - const header = new HeaderNavbar(driver); - await header.check_pageIsLoaded(); - await header.openAccountMenu(); - - const accountListPage = new AccountListPage(driver); - await accountListPage.check_pageIsLoaded(); - await accountListPage.check_numberOfAvailableAccounts( - accountsSyncMockResponse.length, - ); - await accountListPage.check_accountDisplayedInAccountList( - 'My First Synced Account', - ); - await accountListPage.check_accountDisplayedInAccountList( - 'My Second Synced Account', - ); - await accountListPage.addNewAccountWithDefaultName(); - }, - ); - - await withFixtures( - { - fixtures: new FixtureBuilder({ onboarding: true }).build(), - ganacheOptions: defaultGanacheOptions, - title: this.test?.fullTitle(), - testSpecificMock: (server: Mockttp) => { - userStorageMockttpController.setupPath('accounts', server); - return mockNotificationServices( - server, - userStorageMockttpController, - ); - }, - }, - async ({ driver }) => { - await driver.navigate(); - await completeImportSRPOnboardingFlow( - driver, - NOTIFICATIONS_TEAM_SEED_PHRASE, - NOTIFICATIONS_TEAM_PASSWORD, - ); - - const header = new HeaderNavbar(driver); - await header.check_pageIsLoaded(); - await header.openAccountMenu(); - - const accountListPage = new AccountListPage(driver); - await accountListPage.check_pageIsLoaded(); - - const accountSyncResponse = - userStorageMockttpController.paths.get('accounts')?.response; - - await accountListPage.check_numberOfAvailableAccounts( - accountSyncResponse?.length as number, - ); - await accountListPage.check_accountDisplayedInAccountList( - 'My First Synced Account', - ); - await accountListPage.check_accountDisplayedInAccountList( - 'My Second Synced Account', - ); - await accountListPage.check_accountDisplayedInAccountList( - 'Account 3', - ); - }, - ); - }); - }); -}); diff --git a/test/e2e/tests/notifications/account-syncing/sync-after-modifying-account-name.spec.ts b/test/e2e/tests/notifications/account-syncing/sync-after-modifying-account-name.spec.ts deleted file mode 100644 index 418962b370de..000000000000 --- a/test/e2e/tests/notifications/account-syncing/sync-after-modifying-account-name.spec.ts +++ /dev/null @@ -1,114 +0,0 @@ -import { Mockttp } from 'mockttp'; -import { - withFixtures, - defaultGanacheOptions, - completeImportSRPOnboardingFlow, -} from '../../../helpers'; -import FixtureBuilder from '../../../fixture-builder'; -import { mockNotificationServices } from '../mocks'; -import { - NOTIFICATIONS_TEAM_PASSWORD, - NOTIFICATIONS_TEAM_SEED_PHRASE, -} from '../constants'; -import { UserStorageMockttpController } from '../../../helpers/user-storage/userStorageMockttpController'; -import HeaderNavbar from '../../../page-objects/pages/header-navbar'; -import AccountListPage from '../../../page-objects/pages/account-list-page'; -import { accountsSyncMockResponse } from './mockData'; -import { IS_ACCOUNT_SYNCING_ENABLED } from './helpers'; - -describe('Account syncing - Rename Accounts @no-mmi', function () { - if (!IS_ACCOUNT_SYNCING_ENABLED) { - return; - } - describe('from inside MetaMask', function () { - it('syncs renamed account names', async function () { - const userStorageMockttpController = new UserStorageMockttpController(); - - await withFixtures( - { - fixtures: new FixtureBuilder({ onboarding: true }).build(), - ganacheOptions: defaultGanacheOptions, - title: this.test?.fullTitle(), - testSpecificMock: (server: Mockttp) => { - userStorageMockttpController.setupPath('accounts', server, { - getResponse: accountsSyncMockResponse, - }); - - return mockNotificationServices( - server, - userStorageMockttpController, - ); - }, - }, - async ({ driver }) => { - await driver.navigate(); - await completeImportSRPOnboardingFlow( - driver, - NOTIFICATIONS_TEAM_SEED_PHRASE, - NOTIFICATIONS_TEAM_PASSWORD, - ); - - const header = new HeaderNavbar(driver); - await header.check_pageIsLoaded(); - await header.openAccountMenu(); - - const accountListPage = new AccountListPage(driver); - await accountListPage.check_pageIsLoaded(); - await accountListPage.check_numberOfAvailableAccounts( - accountsSyncMockResponse.length, - ); - await accountListPage.check_accountDisplayedInAccountList( - 'My First Synced Account', - ); - await accountListPage.check_accountDisplayedInAccountList( - 'My Second Synced Account', - ); - await accountListPage.openAccountOptionsMenu(); - await accountListPage.changeAccountLabel('My Renamed First Account'); - }, - ); - - await withFixtures( - { - fixtures: new FixtureBuilder({ onboarding: true }).build(), - ganacheOptions: defaultGanacheOptions, - title: this.test?.fullTitle(), - testSpecificMock: (server: Mockttp) => { - userStorageMockttpController.setupPath('accounts', server); - return mockNotificationServices( - server, - userStorageMockttpController, - ); - }, - }, - async ({ driver }) => { - await driver.navigate(); - await completeImportSRPOnboardingFlow( - driver, - NOTIFICATIONS_TEAM_SEED_PHRASE, - NOTIFICATIONS_TEAM_PASSWORD, - ); - - const header = new HeaderNavbar(driver); - await header.check_pageIsLoaded(); - await header.openAccountMenu(); - - const accountListPage = new AccountListPage(driver); - await accountListPage.check_pageIsLoaded(); - await accountListPage.check_numberOfAvailableAccounts( - accountsSyncMockResponse.length, - ); - await accountListPage.check_accountIsNotDisplayedInAccountList( - 'My First Synced Account', - ); - await accountListPage.check_accountDisplayedInAccountList( - 'My Renamed First Account', - ); - await accountListPage.check_accountDisplayedInAccountList( - 'My Second Synced Account', - ); - }, - ); - }); - }); -}); diff --git a/test/e2e/tests/notifications/account-syncing/sync-after-onboarding.spec.ts b/test/e2e/tests/notifications/account-syncing/sync-after-onboarding.spec.ts deleted file mode 100644 index 4ec904256525..000000000000 --- a/test/e2e/tests/notifications/account-syncing/sync-after-onboarding.spec.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { Mockttp } from 'mockttp'; -import { - withFixtures, - defaultGanacheOptions, - completeImportSRPOnboardingFlow, -} from '../../../helpers'; -import FixtureBuilder from '../../../fixture-builder'; -import { mockNotificationServices } from '../mocks'; -import { - NOTIFICATIONS_TEAM_PASSWORD, - NOTIFICATIONS_TEAM_SEED_PHRASE, -} from '../constants'; -import { UserStorageMockttpController } from '../../../helpers/user-storage/userStorageMockttpController'; -import HeaderNavbar from '../../../page-objects/pages/header-navbar'; -import AccountListPage from '../../../page-objects/pages/account-list-page'; -import { accountsSyncMockResponse } from './mockData'; -import { IS_ACCOUNT_SYNCING_ENABLED } from './helpers'; - -describe('Account syncing - Onboarding @no-mmi', function () { - if (!IS_ACCOUNT_SYNCING_ENABLED) { - return; - } - describe('from inside MetaMask', function () { - it('retrieves all previously synced accounts', async function () { - const userStorageMockttpController = new UserStorageMockttpController(); - - await withFixtures( - { - fixtures: new FixtureBuilder({ onboarding: true }).build(), - ganacheOptions: defaultGanacheOptions, - title: this.test?.fullTitle(), - testSpecificMock: (server: Mockttp) => { - userStorageMockttpController.setupPath('accounts', server, { - getResponse: accountsSyncMockResponse, - }); - return mockNotificationServices( - server, - userStorageMockttpController, - ); - }, - }, - async ({ driver }) => { - await driver.navigate(); - await completeImportSRPOnboardingFlow( - driver, - NOTIFICATIONS_TEAM_SEED_PHRASE, - NOTIFICATIONS_TEAM_PASSWORD, - ); - - const header = new HeaderNavbar(driver); - await header.check_pageIsLoaded(); - await header.openAccountMenu(); - - const accountListPage = new AccountListPage(driver); - await accountListPage.check_pageIsLoaded(); - await accountListPage.check_numberOfAvailableAccounts( - accountsSyncMockResponse.length, - ); - await accountListPage.check_accountDisplayedInAccountList( - 'My First Synced Account', - ); - await accountListPage.check_accountDisplayedInAccountList( - 'My Second Synced Account', - ); - }, - ); - }); - }); -}); diff --git a/test/e2e/tests/notifications/constants.ts b/test/e2e/tests/notifications/constants.ts deleted file mode 100644 index 557483174b08..000000000000 --- a/test/e2e/tests/notifications/constants.ts +++ /dev/null @@ -1,10 +0,0 @@ -// As we rely on profile syncing for most of our features, we need to use the same SRP for all of our tests -export const NOTIFICATIONS_TEAM_SEED_PHRASE = - 'leisure swallow trip elbow prison wait rely keep supply hole general mountain'; -export const NOTIFICATIONS_TEAM_PASSWORD = 'notify_password'; -// You can use the storage key below to generate mock data -export const NOTIFICATIONS_TEAM_STORAGE_KEY = - '0d55d30da233959674d14076737198c05ae3fb8631a17e20d3c28c60dddd82f7'; - -export const NOTIFICATIONS_TEAM_IMPORTED_PRIVATE_KEY = - '0179f7453ff337aba89d04b00085764092cf8c0cfe91d5614c39c2be902ad582'; diff --git a/test/e2e/tests/notifications/mocks.ts b/test/e2e/tests/notifications/mocks.ts index ce2ced3df210..9161faf2967f 100644 --- a/test/e2e/tests/notifications/mocks.ts +++ b/test/e2e/tests/notifications/mocks.ts @@ -1,12 +1,15 @@ import { Mockttp, RequestRuleBuilder } from 'mockttp'; -import { AuthenticationController } from '@metamask/profile-sync-controller'; +import { + AuthenticationController, + UserStorageController, +} from '@metamask/profile-sync-controller'; import { NotificationServicesController, NotificationServicesPushController, } from '@metamask/notification-services-controller'; -import { UserStorageMockttpController } from '../../helpers/user-storage/userStorageMockttpController'; const AuthMocks = AuthenticationController.Mocks; +const StorageMocks = UserStorageController.Mocks; const NotificationMocks = NotificationServicesController.Mocks; const PushMocks = NotificationServicesPushController.Mocks; @@ -17,30 +20,32 @@ type MockResponse = { }; /** - * E2E mock setup for notification APIs (Auth, UserStorage, Notifications, Push Notifications, Profile syncing) + * E2E mock setup for notification APIs (Auth, Storage, Notifications, Push Notifications, Profile syncing) * * @param server - server obj used to mock our endpoints - * @param userStorageMockttpControllerInstance - optional instance of UserStorageMockttpController, useful if you need persisted user storage between tests */ -export async function mockNotificationServices( - server: Mockttp, - userStorageMockttpControllerInstance: UserStorageMockttpController = new UserStorageMockttpController(), -) { +export async function mockNotificationServices(server: Mockttp) { // Auth mockAPICall(server, AuthMocks.getMockAuthNonceResponse()); mockAPICall(server, AuthMocks.getMockAuthLoginResponse()); mockAPICall(server, AuthMocks.getMockAuthAccessTokenResponse()); // Storage - if (!userStorageMockttpControllerInstance?.paths.get('accounts')) { - userStorageMockttpControllerInstance.setupPath('accounts', server); - } - if (!userStorageMockttpControllerInstance?.paths.get('networks')) { - userStorageMockttpControllerInstance.setupPath('networks', server); - } - if (!userStorageMockttpControllerInstance?.paths.get('notifications')) { - userStorageMockttpControllerInstance.setupPath('notifications', server); - } + mockAPICall(server, await StorageMocks.getMockUserStorageGetResponse()); + mockAPICall(server, await StorageMocks.getMockUserStoragePutResponse()); + + // TODO - add better mock responses for other Profile Sync features + // (Account Sync, Network Sync, ...) + server + .forGet(/https:\/\/user-storage\.api\.cx\.metamask\.io\/.*/gu) + ?.thenCallback(() => ({ + statusCode: 404, + })); + server + .forPut(/https:\/\/user-storage\.api\.cx\.metamask\.io\/.*/gu) + ?.thenCallback(() => ({ + statusCode: 204, + })); // Notifications mockAPICall(server, NotificationMocks.getMockFeatureAnnouncementResponse()); diff --git a/test/e2e/tests/onboarding/onboarding.spec.js b/test/e2e/tests/onboarding/onboarding.spec.js new file mode 100644 index 000000000000..de040f825ee6 --- /dev/null +++ b/test/e2e/tests/onboarding/onboarding.spec.js @@ -0,0 +1,748 @@ +const { strict: assert } = require('assert'); +const { toHex } = require('@metamask/controller-utils'); +const { By } = require('selenium-webdriver'); +const { + TEST_SEED_PHRASE, + convertToHexValue, + withFixtures, + completeCreateNewWalletOnboardingFlow, + completeImportSRPOnboardingFlow, + importSRPOnboardingFlow, + importWrongSRPOnboardingFlow, + testSRPDropdownIterations, + locateAccountBalanceDOM, + defaultGanacheOptions, + WALLET_PASSWORD, + onboardingBeginCreateNewWallet, + onboardingChooseMetametricsOption, + onboardingCreatePassword, + onboardingRevealAndConfirmSRP, + onboardingCompleteWalletCreation, + regularDelayMs, + unlockWallet, +} = require('../../helpers'); +const FixtureBuilder = require('../../fixture-builder'); +const { + FirstTimeFlowType, +} = require('../../../../shared/constants/onboarding'); + +describe('MetaMask onboarding @no-mmi', function () { + const wrongSeedPhrase = + 'test test test test test test test test test test test test'; + const wrongTestPassword = 'test test test test'; + + const ganacheOptions2 = { + accounts: [ + { + secretKey: + '0x53CB0AB5226EEBF4D872113D98332C1555DC304443BEE1CF759D15798D3C55A9', + balance: convertToHexValue(10000000000000000000), + }, + ], + }; + + it('Clicks create a new wallet, accepts a secure password, reveals the Secret Recovery Phrase, confirm SRP', async function () { + await withFixtures( + { + fixtures: new FixtureBuilder({ onboarding: true }).build(), + ganacheOptions: defaultGanacheOptions, + title: this.test.fullTitle(), + }, + async ({ driver }) => { + await driver.navigate(); + + await completeCreateNewWalletOnboardingFlow(driver, WALLET_PASSWORD); + + const homePage = await driver.findElement('.home__main-view'); + const homePageDisplayed = await homePage.isDisplayed(); + + assert.equal(homePageDisplayed, true); + }, + ); + }); + + it('Clicks import a new wallet, accepts a secure password, reveals the Secret Recovery Phrase, confirm SRP', async function () { + await withFixtures( + { + fixtures: new FixtureBuilder({ onboarding: true }).build(), + ganacheOptions: defaultGanacheOptions, + title: this.test.fullTitle(), + }, + async ({ driver }) => { + await driver.navigate(); + + await completeImportSRPOnboardingFlow( + driver, + TEST_SEED_PHRASE, + WALLET_PASSWORD, + ); + + const homePage = await driver.findElement('.home__main-view'); + const homePageDisplayed = await homePage.isDisplayed(); + + assert.equal(homePageDisplayed, true); + }, + ); + }); + + it('User import wrong Secret Recovery Phrase', async function () { + await withFixtures( + { + fixtures: new FixtureBuilder({ onboarding: true }).build(), + ganacheOptions: defaultGanacheOptions, + title: this.test.fullTitle(), + }, + async ({ driver }) => { + await driver.navigate(); + + await importWrongSRPOnboardingFlow(driver, wrongSeedPhrase); + + const confirmSeedPhrase = await driver.findElement( + '[data-testid="import-srp-confirm"]', + ); + + assert.equal(await confirmSeedPhrase.isEnabled(), false); + }, + ); + }); + + it('Check if user select different type of secret recovery phrase', async function () { + await withFixtures( + { + fixtures: new FixtureBuilder({ onboarding: true }).build(), + ganacheOptions: defaultGanacheOptions, + title: this.test.fullTitle(), + }, + async ({ driver }) => { + await driver.navigate(); + + // accept terms of use + await driver.clickElement('[data-testid="onboarding-terms-checkbox"]'); + + // welcome + await driver.clickElement('[data-testid="onboarding-import-wallet"]'); + + await driver.clickElement('[data-testid="metametrics-no-thanks"]'); + + const dropdownElement = await driver.findElement( + '.import-srp__number-of-words-dropdown', + ); + await dropdownElement.click(); + const options = await dropdownElement.findElements(By.css('option')); + + const iterations = options.length; + + await testSRPDropdownIterations(options, driver, iterations); + + const finalFormFields = await driver.findElements( + '.import-srp__srp-word-label', + ); + const expectedFinalNumFields = 24; // The last iteration will have 24 fields + const actualFinalNumFields = finalFormFields.length; + assert.equal(actualFinalNumFields, expectedFinalNumFields); + }, + ); + }); + + it('User enters the wrong password during password creation', async function () { + await withFixtures( + { + fixtures: new FixtureBuilder({ onboarding: true }).build(), + ganacheOptions: defaultGanacheOptions, + title: this.test.fullTitle(), + }, + async ({ driver }) => { + await driver.navigate(); + + await driver.clickElement('[data-testid="onboarding-terms-checkbox"]'); + await driver.clickElement('[data-testid="onboarding-create-wallet"]'); + + // metrics + await driver.clickElement('[data-testid="metametrics-no-thanks"]'); + + // Fill in confirm password field with incorrect password + await driver.fill( + '[data-testid="create-password-new"]', + WALLET_PASSWORD, + ); + await driver.fill( + '[data-testid="create-password-confirm"]', + wrongTestPassword, + ); + + // Check that the error message is displayed for the password fields + await driver.isElementPresent( + { text: "Passwords don't match", tag: 'h6' }, + true, + ); + + // Check that the "Confirm Password" button is disabled + const confirmPasswordButton = await driver.findElement( + '[data-testid="create-password-wallet"]', + ); + assert.equal(await confirmPasswordButton.isEnabled(), false); + }, + ); + }); + + it('Verify that the user has been redirected to the correct page after importing their wallet', async function () { + await withFixtures( + { + fixtures: new FixtureBuilder({ onboarding: true }).build(), + ganacheOptions: defaultGanacheOptions, + title: this.test.fullTitle(), + }, + async ({ driver }) => { + await driver.navigate(); + + await importSRPOnboardingFlow( + driver, + TEST_SEED_PHRASE, + WALLET_PASSWORD, + ); + // Verify site + assert.equal( + await driver.isElementPresent({ + text: 'Your wallet is ready', + tag: 'h2', + }), + true, + ); + }, + ); + }); + + it('Verify that the user has been redirected to the correct page after creating a password for their new wallet', async function () { + await withFixtures( + { + fixtures: new FixtureBuilder({ onboarding: true }).build(), + ganacheOptions: defaultGanacheOptions, + title: this.test.fullTitle(), + }, + async ({ driver }) => { + await driver.navigate(); + + await driver.clickElement('[data-testid="onboarding-terms-checkbox"]'); + await driver.clickElement('[data-testid="onboarding-create-wallet"]'); + + // metrics + await driver.clickElement('[data-testid="metametrics-no-thanks"]'); + + // Fill in confirm password field with correct password + await driver.fill( + '[data-testid="create-password-new"]', + WALLET_PASSWORD, + ); + await driver.fill( + '[data-testid="create-password-confirm"]', + WALLET_PASSWORD, + ); + await driver.clickElement('[data-testid="create-password-terms"]'); + await driver.clickElement('[data-testid="create-password-wallet"]'); + + // Verify site + assert.equal( + await driver.isElementPresent({ + text: 'Secure your wallet', + tag: 'h2', + }), + true, + ); + }, + ); + }); + + it('User can add custom network during onboarding', async function () { + const networkName = 'Localhost 8546'; + const networkUrl = 'http://127.0.0.1:8546'; + const currencySymbol = 'ETH'; + const port = 8546; + const chainId = 1338; + await withFixtures( + { + fixtures: new FixtureBuilder({ onboarding: true }).build(), + ganacheOptions: { + ...defaultGanacheOptions, + concurrent: [{ port, chainId, ganacheOptions2 }], + }, + title: this.test.fullTitle(), + }, + + async ({ driver, secondaryGanacheServer }) => { + try { + await driver.navigate(); + await importSRPOnboardingFlow( + driver, + TEST_SEED_PHRASE, + WALLET_PASSWORD, + ); + + await driver.clickElement({ + text: 'Manage default privacy settings', + tag: 'button', + }); + + await driver.clickElement({ + text: 'General', + }); + await driver.clickElement({ text: 'Add a network' }); + + await driver.waitForSelector( + '.multichain-network-list-menu-content-wrapper__dialog', + ); + + await driver.fill( + '[data-testid="network-form-network-name"]', + networkName, + ); + await driver.fill( + '[data-testid="network-form-chain-id"]', + chainId.toString(), + ); + await driver.fill( + '[data-testid="network-form-ticker-input"]', + currencySymbol, + ); + + // Add rpc url + const rpcUrlInputDropDown = await driver.waitForSelector( + '[data-testid="test-add-rpc-drop-down"]', + ); + await rpcUrlInputDropDown.click(); + await driver.clickElement({ + text: 'Add RPC URL', + tag: 'button', + }); + const rpcUrlInput = await driver.waitForSelector( + '[data-testid="rpc-url-input-test"]', + ); + await rpcUrlInput.clear(); + await rpcUrlInput.sendKeys(networkUrl); + await driver.clickElement({ + text: 'Add URL', + tag: 'button', + }); + + await driver.clickElementAndWaitToDisappear({ + tag: 'button', + text: 'Save', + }); + + await driver.clickElement('[data-testid="category-back-button"]'); + + // Wait until the onboarding carousel has stopped moving + // otherwise the click has no effect. + await driver.waitForElementToStopMoving( + '[data-testid="privacy-settings-back-button"]', + ); + + await driver.clickElement( + '[data-testid="privacy-settings-back-button"]', + ); + + await driver.clickElementAndWaitToDisappear({ + text: 'Done', + tag: 'button', + }); + + await driver.clickElement({ + text: 'Next', + tag: 'button', + }); + + // Wait until the onboarding carousel has stopped moving + // otherwise the click has no effect. + await driver.waitForElementToStopMoving({ + text: 'Done', + tag: 'button', + }); + + await driver.clickElementAndWaitToDisappear({ + text: 'Done', + tag: 'button', + }); + + await driver.clickElement('.mm-picker-network'); + await driver.clickElement( + `[data-rbd-draggable-id="${toHex(chainId)}"]`, + ); + // Check localhost 8546 is selected and its balance value is correct + await driver.findElement({ + css: '[data-testid="network-display"]', + text: networkName, + }); + + await locateAccountBalanceDOM(driver, secondaryGanacheServer[0]); + } catch (error) { + console.error('Error in test:', error); + throw error; + } + }, + ); + }); + + it('User can turn off basic functionality in default settings', async function () { + await withFixtures( + { + fixtures: new FixtureBuilder({ onboarding: true }).build(), + ganacheOptions: defaultGanacheOptions, + title: this.test.fullTitle(), + }, + async ({ driver }) => { + await driver.navigate(); + await importSRPOnboardingFlow( + driver, + TEST_SEED_PHRASE, + WALLET_PASSWORD, + ); + + await driver.clickElement({ + text: 'Manage default privacy settings', + tag: 'button', + }); + await driver.clickElement('[data-testid="category-item-General"]'); + await driver.clickElement( + '[data-testid="basic-functionality-toggle"] .toggle-button', + ); + await driver.clickElement('[id="basic-configuration-checkbox"]'); + await driver.clickElement({ text: 'Turn off', tag: 'button' }); + await driver.clickElement('[data-testid="category-back-button"]'); + + // Wait until the onboarding carousel has stopped moving + // otherwise the click has no effect. + await driver.waitForElementToStopMoving( + '[data-testid="privacy-settings-back-button"]', + ); + await driver.clickElement( + '[data-testid="privacy-settings-back-button"]', + ); + + await driver.clickElement('[data-testid="onboarding-complete-done"]'); + await driver.clickElement('[data-testid="pin-extension-next"]'); + await driver.clickElement('[data-testid="pin-extension-done"]'); + + // Check that the 'basic functionality is off' banner is displayed on the home screen after onboarding completion + await driver.waitForSelector({ + text: 'Basic functionality is off', + css: '.mm-banner-alert', + }); + }, + ); + }); + + it("doesn't make any network requests to infura before onboarding is completed", async function () { + async function mockInfura(mockServer) { + const infuraUrl = + 'https://mainnet.infura.io/v3/00000000000000000000000000000000'; + const sampleAddress = '1111111111111111111111111111111111111111'; + + return [ + await mockServer + .forPost(infuraUrl) + .withJsonBodyIncluding({ method: 'eth_blockNumber' }) + .thenCallback(() => { + return { + statusCode: 200, + json: { + jsonrpc: '2.0', + id: '1111111111111111', + result: '0x1', + }, + }; + }), + await mockServer + .forPost(infuraUrl) + .withJsonBodyIncluding({ method: 'eth_getBalance' }) + .thenCallback(() => { + return { + statusCode: 200, + json: { + jsonrpc: '2.0', + id: '1111111111111111', + result: '0x0', + }, + }; + }), + await mockServer + .forPost(infuraUrl) + .withJsonBodyIncluding({ method: 'eth_getBlockByNumber' }) + .thenCallback(() => { + return { + statusCode: 200, + json: { + jsonrpc: '2.0', + id: '1111111111111111', + result: {}, + }, + }; + }), + await mockServer + .forPost(infuraUrl) + .withJsonBodyIncluding({ method: 'eth_call' }) + .thenCallback(() => { + return { + statusCode: 200, + json: { + jsonrpc: '2.0', + id: '1111111111111111', + result: `0x000000000000000000000000${sampleAddress}`, + }, + }; + }), + await mockServer + .forPost(infuraUrl) + .withJsonBodyIncluding({ method: 'net_version' }) + .thenCallback(() => { + return { + statusCode: 200, + json: { id: 8262367391254633, jsonrpc: '2.0', result: '1337' }, + }; + }), + ]; + } + + await withFixtures( + { + fixtures: new FixtureBuilder({ onboarding: true }) + .withNetworkControllerOnMainnet() + .build(), + ganacheOptions: defaultGanacheOptions, + title: this.test.fullTitle(), + testSpecificMock: mockInfura, + }, + async ({ driver, mockedEndpoint: mockedEndpoints }) => { + const password = 'password'; + + await driver.navigate(); + + await onboardingBeginCreateNewWallet(driver); + await onboardingChooseMetametricsOption(driver, false); + await onboardingCreatePassword(driver, password); + await onboardingRevealAndConfirmSRP(driver); + await onboardingCompleteWalletCreation(driver); + + // pin extension walkthrough screen + await driver.clickElement('[data-testid="pin-extension-next"]'); + + for (let i = 0; i < mockedEndpoints.length; i += 1) { + const mockedEndpoint = await mockedEndpoints[i]; + const isPending = await mockedEndpoint.isPending(); + assert.equal( + isPending, + true, + `${mockedEndpoints[i]} mock should still be pending before onboarding`, + ); + const requests = await mockedEndpoint.getSeenRequests(); + + assert.equal( + requests.length, + 0, + `${mockedEndpoints[i]} should make no requests before onboarding`, + ); + } + + await driver.clickElement('[data-testid="pin-extension-done"]'); + // requests happen here! + + for (let i = 0; i < mockedEndpoints.length; i += 1) { + const mockedEndpoint = await mockedEndpoints[i]; + + await driver.wait(async () => { + const isPending = await mockedEndpoint.isPending(); + return isPending === false; + }, driver.timeout); + + const requests = await mockedEndpoint.getSeenRequests(); + + assert.equal( + requests.length > 0, + true, + `${mockedEndpoints[i]} should make requests after onboarding`, + ); + } + }, + ); + }); + + it("doesn't make any network requests to infura before onboarding by import is completed", async function () { + async function mockInfura(mockServer) { + const infuraUrl = + 'https://mainnet.infura.io/v3/00000000000000000000000000000000'; + const sampleAddress = '1111111111111111111111111111111111111111'; + + return [ + await mockServer + .forPost(infuraUrl) + .withJsonBodyIncluding({ method: 'eth_blockNumber' }) + .thenCallback(() => { + return { + statusCode: 200, + json: { + jsonrpc: '2.0', + id: '1111111111111111', + result: '0x1', + }, + }; + }), + await mockServer + .forPost(infuraUrl) + .withJsonBodyIncluding({ method: 'eth_getBalance' }) + .thenCallback(() => { + return { + statusCode: 200, + json: { + jsonrpc: '2.0', + id: '1111111111111111', + result: '0x0', + }, + }; + }), + await mockServer + .forPost(infuraUrl) + .withJsonBodyIncluding({ method: 'eth_getBlockByNumber' }) + .thenCallback(() => { + return { + statusCode: 200, + json: { + jsonrpc: '2.0', + id: '1111111111111111', + result: {}, + }, + }; + }), + await mockServer + .forPost(infuraUrl) + .withJsonBodyIncluding({ method: 'eth_call' }) + .thenCallback(() => { + return { + statusCode: 200, + json: { + jsonrpc: '2.0', + id: '1111111111111111', + result: `0x000000000000000000000000${sampleAddress}`, + }, + }; + }), + await mockServer + .forPost(infuraUrl) + .withJsonBodyIncluding({ method: 'net_version' }) + .thenCallback(() => { + return { + statusCode: 200, + json: { id: 8262367391254633, jsonrpc: '2.0', result: '1337' }, + }; + }), + ]; + } + + await withFixtures( + { + fixtures: new FixtureBuilder({ onboarding: true }) + .withNetworkControllerOnMainnet() + .build(), + ganacheOptions: defaultGanacheOptions, + title: this.test.fullTitle(), + testSpecificMock: mockInfura, + }, + async ({ driver, mockedEndpoint: mockedEndpoints }) => { + const password = 'password'; + + await driver.navigate(); + + await importSRPOnboardingFlow(driver, TEST_SEED_PHRASE, password); + + await driver.delay(regularDelayMs); + + for (let i = 0; i < mockedEndpoints.length; i += 1) { + const mockedEndpoint = await mockedEndpoints[i]; + const requests = await mockedEndpoint.getSeenRequests(); + + assert.equal( + requests.length, + 0, + `${mockedEndpoints[i]} should make no requests before onboarding`, + ); + } + + // complete + await driver.clickElement('[data-testid="onboarding-complete-done"]'); + + // pin extension + await driver.clickElement('[data-testid="pin-extension-next"]'); + await driver.clickElement('[data-testid="pin-extension-done"]'); + + // pin extension walkthrough screen + await driver.findElement('[data-testid="account-menu-icon"]'); + // requests happen here! + + for (let i = 0; i < mockedEndpoints.length; i += 1) { + const mockedEndpoint = await mockedEndpoints[i]; + + await driver.wait(async () => { + const isPending = await mockedEndpoint.isPending(); + return isPending === false; + }, driver.timeout); + + const requests = await mockedEndpoint.getSeenRequests(); + + assert.equal( + requests.length > 0, + true, + `${mockedEndpoints[i]} should make requests after onboarding`, + ); + } + }, + ); + }); + + it('Provides an onboarding path for a user who has restored their account from state persistence failure', async function () { + // We don't use onboarding:true here because we want there to be a vault, + // simulating what will happen when a user eventually restores their vault + // during a state persistence failure. Instead, we set the + // firstTimeFlowType to 'restore' and completedOnboarding to false. as well + // as some other first time state options to get us into an onboarding + // state similar to a new state tree. + await withFixtures( + { + fixtures: new FixtureBuilder() + .withOnboardingController({ + completedOnboarding: false, + firstTimeFlowType: FirstTimeFlowType.restore, + seedPhraseBackedUp: null, + }) + .withMetaMetricsController({ + participateInMetaMetrics: null, + metaMetricsId: null, + }) + .build(), + ganacheOptions: defaultGanacheOptions, + title: this.test.fullTitle(), + }, + async ({ driver }) => { + await unlockWallet(driver); + + // First screen we should be on is MetaMetrics + assert.equal( + await driver.isElementPresent({ + text: 'Help us improve MetaMask', + tag: 'h2', + }), + true, + 'First screen should be MetaMetrics', + ); + + // select no thanks + await driver.clickElement('[data-testid="metametrics-no-thanks"]'); + + // Next should be Secure your wallet screen + assert.equal( + await driver.isElementPresent({ + text: 'Secure your wallet', + tag: 'h2', + }), + true, + ); + }, + ); + }); +}); diff --git a/test/e2e/tests/onboarding/onboarding.spec.ts b/test/e2e/tests/onboarding/onboarding.spec.ts deleted file mode 100644 index aa04ca151f86..000000000000 --- a/test/e2e/tests/onboarding/onboarding.spec.ts +++ /dev/null @@ -1,271 +0,0 @@ -import { - convertToHexValue, - WALLET_PASSWORD, - withFixtures, -} from '../../helpers'; -import { Driver } from '../../webdriver/driver'; -import FixtureBuilder from '../../fixture-builder'; -import { FirstTimeFlowType } from '../../../../shared/constants/onboarding'; -import HomePage from '../../page-objects/pages/homepage'; -import OnboardingCompletePage from '../../page-objects/pages/onboarding/onboarding-complete-page'; -import OnboardingMetricsPage from '../../page-objects/pages/onboarding/onboarding-metrics-page'; -import OnboardingPasswordPage from '../../page-objects/pages/onboarding/onboarding-password-page'; -import OnboardingPrivacySettingsPage from '../../page-objects/pages/onboarding/onboarding-privacy-settings-page'; -import OnboardingSrpPage from '../../page-objects/pages/onboarding/onboarding-srp-page'; -import SecureWalletPage from '../../page-objects/pages/onboarding/secure-wallet-page'; -import StartOnboardingPage from '../../page-objects/pages/onboarding/start-onboarding-page'; -import { loginWithoutBalanceValidation } from '../../page-objects/flows/login.flow'; -import { - completeCreateNewWalletOnboardingFlow, - completeImportSRPOnboardingFlow, - importSRPOnboardingFlow, -} from '../../page-objects/flows/onboarding.flow'; -import { switchToNetworkFlow } from '../../page-objects/flows/network.flow'; - -describe('MetaMask onboarding @no-mmi', function () { - const ganacheOptions2 = { - accounts: [ - { - secretKey: - '0x53CB0AB5226EEBF4D872113D98332C1555DC304443BEE1CF759D15798D3C55A9', - balance: convertToHexValue(10000000000000000000), - }, - ], - }; - - it('Creates a new wallet, sets up a secure password, and completes the onboarding process', async function () { - await withFixtures( - { - fixtures: new FixtureBuilder({ onboarding: true }).build(), - title: this.test?.fullTitle(), - }, - async ({ driver }: { driver: Driver }) => { - await completeCreateNewWalletOnboardingFlow(driver); - const homePage = new HomePage(driver); - await homePage.check_pageIsLoaded(); - await homePage.check_expectedBalanceIsDisplayed(); - }, - ); - }); - - it('Imports an existing wallet, sets up a secure password, and completes the onboarding process', async function () { - await withFixtures( - { - fixtures: new FixtureBuilder({ onboarding: true }).build(), - title: this.test?.fullTitle(), - }, - async ({ driver }: { driver: Driver }) => { - await completeImportSRPOnboardingFlow(driver); - const homePage = new HomePage(driver); - await homePage.check_pageIsLoaded(); - await homePage.check_expectedBalanceIsDisplayed(); - }, - ); - }); - - it('Attempts to import a wallet with an incorrect Secret Recovery Phrase and verifies the error message', async function () { - await withFixtures( - { - fixtures: new FixtureBuilder({ onboarding: true }).build(), - title: this.test?.fullTitle(), - }, - async ({ driver }: { driver: Driver }) => { - const wrongSeedPhrase = - 'test test test test test test test test test test test test'; - await driver.navigate(); - const startOnboardingPage = new StartOnboardingPage(driver); - await startOnboardingPage.check_pageIsLoaded(); - await startOnboardingPage.checkTermsCheckbox(); - await startOnboardingPage.clickImportWalletButton(); - - const onboardingMetricsPage = new OnboardingMetricsPage(driver); - await onboardingMetricsPage.check_pageIsLoaded(); - await onboardingMetricsPage.clickNoThanksButton(); - - const onboardingSrpPage = new OnboardingSrpPage(driver); - await onboardingSrpPage.check_pageIsLoaded(); - await onboardingSrpPage.fillSrp(wrongSeedPhrase); - - // check the wrong SRP warning message is displayed - await onboardingSrpPage.check_wrongSrpWarningMessage(); - await onboardingSrpPage.check_confirmSrpButtonIsDisabled(); - }, - ); - }); - - it('Verifies the functionality of selecting different Secret Recovery Phrase word counts', async function () { - await withFixtures( - { - fixtures: new FixtureBuilder({ onboarding: true }).build(), - title: this.test?.fullTitle(), - }, - async ({ driver }: { driver: Driver }) => { - await driver.navigate(); - const startOnboardingPage = new StartOnboardingPage(driver); - await startOnboardingPage.check_pageIsLoaded(); - await startOnboardingPage.checkTermsCheckbox(); - await startOnboardingPage.clickImportWalletButton(); - - const onboardingMetricsPage = new OnboardingMetricsPage(driver); - await onboardingMetricsPage.check_pageIsLoaded(); - await onboardingMetricsPage.clickNoThanksButton(); - - const onboardingSrpPage = new OnboardingSrpPage(driver); - await onboardingSrpPage.check_pageIsLoaded(); - await onboardingSrpPage.check_srpDropdownIterations(); - }, - ); - }); - - it('Verifies error handling when entering an incorrect password during wallet creation', async function () { - await withFixtures( - { - fixtures: new FixtureBuilder({ onboarding: true }).build(), - title: this.test?.fullTitle(), - }, - async ({ driver }: { driver: Driver }) => { - const wrongTestPassword = 'test test test test'; - await driver.navigate(); - const startOnboardingPage = new StartOnboardingPage(driver); - await startOnboardingPage.check_pageIsLoaded(); - await startOnboardingPage.checkTermsCheckbox(); - await startOnboardingPage.clickCreateWalletButton(); - - const onboardingMetricsPage = new OnboardingMetricsPage(driver); - await onboardingMetricsPage.check_pageIsLoaded(); - await onboardingMetricsPage.clickNoThanksButton(); - - const onboardingPasswordPage = new OnboardingPasswordPage(driver); - await onboardingPasswordPage.check_pageIsLoaded(); - await onboardingPasswordPage.fillWalletPassword( - WALLET_PASSWORD, - wrongTestPassword, - ); - - // check the incorrect password warning message is displayed - await onboardingPasswordPage.check_incorrectPasswordWarningMessageIsDisplayed(); - await onboardingPasswordPage.check_confirmPasswordButtonIsDisabled(); - }, - ); - }); - - it('User can add custom network during onboarding', async function () { - const networkName = 'Localhost 8546'; - const networkUrl = 'http://127.0.0.1:8546'; - const currencySymbol = 'ETH'; - const port = 8546; - const chainId = 1338; - await withFixtures( - { - fixtures: new FixtureBuilder({ onboarding: true }).build(), - ganacheOptions: { - concurrent: [{ port, chainId, ganacheOptions2 }], - }, - title: this.test?.fullTitle(), - }, - async ({ driver, secondaryGanacheServer }) => { - await importSRPOnboardingFlow(driver); - - const onboardingCompletePage = new OnboardingCompletePage(driver); - await onboardingCompletePage.check_pageIsLoaded(); - await onboardingCompletePage.check_walletReadyMessageIsDisplayed(); - await onboardingCompletePage.navigateToDefaultPrivacySettings(); - - const onboardingPrivacySettingsPage = new OnboardingPrivacySettingsPage( - driver, - ); - await onboardingPrivacySettingsPage.addCustomNetwork( - networkName, - chainId, - currencySymbol, - networkUrl, - ); - await onboardingPrivacySettingsPage.navigateBackToOnboardingCompletePage(); - - await onboardingCompletePage.check_pageIsLoaded(); - await onboardingCompletePage.completeOnboarding(); - - const homePage = new HomePage(driver); - await homePage.check_pageIsLoaded(); - await switchToNetworkFlow(driver, networkName); - await homePage.check_addNetworkMessageIsDisplayed(networkName); - - // Check the correct balance for the custom network is displayed - if (secondaryGanacheServer && Array.isArray(secondaryGanacheServer)) { - await homePage.check_ganacheBalanceIsDisplayed( - secondaryGanacheServer[0], - ); - } else { - throw new Error('Custom network Ganache server not available'); - } - }, - ); - }); - - it('User can turn off basic functionality in default settings', async function () { - await withFixtures( - { - fixtures: new FixtureBuilder({ onboarding: true }).build(), - title: this.test?.fullTitle(), - }, - async ({ driver }) => { - await importSRPOnboardingFlow(driver); - - const onboardingCompletePage = new OnboardingCompletePage(driver); - await onboardingCompletePage.check_pageIsLoaded(); - await onboardingCompletePage.check_walletReadyMessageIsDisplayed(); - await onboardingCompletePage.navigateToDefaultPrivacySettings(); - - const onboardingPrivacySettingsPage = new OnboardingPrivacySettingsPage( - driver, - ); - await onboardingPrivacySettingsPage.toggleBasicFunctionalitySettings(); - await onboardingPrivacySettingsPage.navigateBackToOnboardingCompletePage(); - - await onboardingCompletePage.check_pageIsLoaded(); - await onboardingCompletePage.completeOnboarding(); - - const homePage = new HomePage(driver); - await homePage.check_pageIsLoaded(); - // check the basic functionality is off warning message is displayed - await homePage.check_basicFunctionalityOffWarnigMessageIsDisplayed(); - }, - ); - }); - - it('Provides an onboarding path for a user who has restored their account from state persistence failure', async function () { - // We don't use onboarding: true here because we want there to be a vault, - // simulating what will happen when a user eventually restores their vault - // during a state persistence failure. Instead, we set the - // firstTimeFlowType to 'restore' and completedOnboarding to false. as well - // as some other first time state options to get us into an onboarding - // state similar to a new state tree. - await withFixtures( - { - fixtures: new FixtureBuilder() - .withOnboardingController({ - completedOnboarding: false, - firstTimeFlowType: FirstTimeFlowType.restore, - seedPhraseBackedUp: null, - }) - .withMetaMetricsController({ - participateInMetaMetrics: null, - metaMetricsId: null, - }) - .build(), - title: this.test?.fullTitle(), - }, - async ({ driver }) => { - await loginWithoutBalanceValidation(driver); - // First screen we should be on is MetaMetrics - const onboardingMetricsPage = new OnboardingMetricsPage(driver); - await onboardingMetricsPage.check_pageIsLoaded(); - await onboardingMetricsPage.clickNoThanksButton(); - - // Next screen should be Secure your wallet screen - const secureWalletPage = new SecureWalletPage(driver); - await secureWalletPage.check_pageIsLoaded(); - }, - ); - }); -}); diff --git a/test/e2e/tests/petnames/petnames-signatures.spec.js b/test/e2e/tests/petnames/petnames-signatures.spec.js index 6c472901057e..ba6cf7642c59 100644 --- a/test/e2e/tests/petnames/petnames-signatures.spec.js +++ b/test/e2e/tests/petnames/petnames-signatures.spec.js @@ -46,7 +46,7 @@ async function installNameLookupSnap(driver) { // Confirm Install Modal await driver.clickElement({ - text: 'Confirm', + text: 'Install', tag: 'button', }); @@ -173,7 +173,9 @@ describe('Petnames - Signatures', function () { ); }); - it('can propose names using installed snaps', async function () { + // TODO(dbrans): Re-enable this test when name-lookup endowment is in stable. + // eslint-disable-next-line mocha/no-skipped-tests + it.skip('can propose names using installed snaps', async function () { await withFixtures( { dapp: true, diff --git a/test/e2e/tests/phishing-controller/phishing-detection.spec.js b/test/e2e/tests/phishing-controller/phishing-detection.spec.js index 67fb82f8fa55..ac9a6d8461d2 100644 --- a/test/e2e/tests/phishing-controller/phishing-detection.spec.js +++ b/test/e2e/tests/phishing-controller/phishing-detection.spec.js @@ -59,7 +59,7 @@ describe('Phishing Detection', function () { await openDapp(driver); await driver.switchToWindowWithTitle('MetaMask Phishing Detection'); await driver.clickElement({ - text: 'Proceed anyway', + text: 'continue to the site.', }); await driver.wait(until.titleIs(WINDOW_TITLES.TestDApp), 10000); }, @@ -104,7 +104,7 @@ describe('Phishing Detection', function () { await driver.switchToWindowWithTitle('MetaMask Phishing Detection'); await driver.clickElement({ - text: 'Proceed anyway', + text: 'continue to the site.', }); await driver.wait(until.titleIs(WINDOW_TITLES.TestDApp), 10000); @@ -170,7 +170,7 @@ describe('Phishing Detection', function () { }); await driver.switchToWindowWithTitle('MetaMask Phishing Detection'); await driver.clickElement({ - text: 'Proceed anyway', + text: 'continue to the site.', }); // We don't really know what we're going to see at this blocked site, so a waitAtLeast guard of 1000ms is the best choice @@ -253,7 +253,7 @@ describe('Phishing Detection', function () { ); }); - it('should open MetaMask Portfolio when clicking back to safety button', async function () { + it('should open a new extension expanded view when clicking back to safety button', async function () { await withFixtures( { fixtures: new FixtureBuilder().build(), @@ -290,10 +290,11 @@ describe('Phishing Detection', function () { text: 'Back to safety', }); - const currentUrl = await driver.getCurrentUrl(); - const expectedPortfolioUrl = `https://portfolio.metamask.io/?metamaskEntry=phishing_page_portfolio_button`; + // Ensure we're redirected to wallet home page + const homePage = await driver.findElement('.home__main-view'); + const homePageDisplayed = await homePage.isDisplayed(); - assert.equal(currentUrl, expectedPortfolioUrl); + assert.equal(homePageDisplayed, true); }, ); }); diff --git a/test/e2e/tests/ppom/constants.ts b/test/e2e/tests/ppom/constants.ts deleted file mode 100644 index 7794e8738a76..000000000000 --- a/test/e2e/tests/ppom/constants.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const SECURITY_ALERTS_DEV_API_BASE_URL = - 'https://security-alerts.dev-api.cx.metamask.io'; - -export const SECURITY_ALERTS_PROD_API_BASE_URL = - 'https://security-alerts.api.cx.metamask.io'; diff --git a/test/e2e/tests/ppom/ppom-blockaid-alert-erc20-transfer.spec.js b/test/e2e/tests/ppom/ppom-blockaid-alert-erc20-transfer.spec.js index 4f6fcf819f94..4122695dfb50 100644 --- a/test/e2e/tests/ppom/ppom-blockaid-alert-erc20-transfer.spec.js +++ b/test/e2e/tests/ppom/ppom-blockaid-alert-erc20-transfer.spec.js @@ -1,92 +1,190 @@ +const { strict: assert } = require('assert'); const FixtureBuilder = require('../../fixture-builder'); const { WINDOW_TITLES, defaultGanacheOptions, + openDapp, unlockWallet, withFixtures, } = require('../../helpers'); -const { SECURITY_ALERTS_PROD_API_BASE_URL } = require('./constants'); const { mockServerJsonRpc } = require('./mocks/mock-server-json-rpc'); -const SELECTED_ADDRESS = '0x5cfe73b6021e818b776b421b1c4db2474086a7e1'; +const bannerAlertSelector = '[data-testid="security-provider-banner-alert"]'; -const CONTRACT_ADDRESS_USDC = '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48'; +const selectedAddress = '0x5cfe73b6021e818b776b421b1c4db2474086a7e1'; +const selectedAddressWithoutPrefix = '5cfe73b6021e818b776b421b1c4db2474086a7e1'; + +const CONTRACT_ADDRESS = { + BalanceChecker: '0xb1f8e55c7f64d203c1400b9d8555d050f94adf39', + FiatTokenV2_1: '0xa2327a938febf5fec13bacfb16ae10ecbc4cbdcf', + OffChainOracle: '0x52cbe0f49ccdd4dc6e9c13bab024eabd2842045b', + USDC: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', +}; async function mockInfura(mockServer) { await mockServerJsonRpc(mockServer, [ ['eth_blockNumber'], - ['eth_call'], + [ + 'eth_call', + { + methodResultVariant: 'balanceChecker', + params: [{ to: CONTRACT_ADDRESS.BalanceChecker }], + }, + ], + [ + 'eth_call', + { + methodResultVariant: 'offchainOracle', + params: [{ to: CONTRACT_ADDRESS.OffChainOracle }], + }, + ], + [ + 'eth_call', + { + methodResultVariant: 'balance', + params: [ + { + accessList: [], + data: `0x70a08231000000000000000000000000${selectedAddressWithoutPrefix}`, + to: CONTRACT_ADDRESS.USDC, + }, + ], + }, + ], ['eth_estimateGas'], ['eth_feeHistory'], ['eth_gasPrice'], ['eth_getBalance'], ['eth_getBlockByNumber'], - ['eth_getCode'], + [ + 'eth_getCode', + { + methodResultVariant: 'USDC', + params: [CONTRACT_ADDRESS.USDC], + }, + ], ['eth_getTransactionCount'], ]); -} -const maliciousTransferAlert = { - block: 1, - result_type: 'Malicious', - reason: 'transfer_farming', - description: - 'Transfer to 0x5fbdb2315678afecb367f032d93f642f64180aa3, classification: A known malicious address is involved in the transaction', - features: ['A known malicious address is involved in the transaction'], -}; - -async function mockRequest(server, response) { - await server - .forPost(`${SECURITY_ALERTS_PROD_API_BASE_URL}/validate/0x1`) + await mockServer + .forPost() .withJsonBodyIncluding({ - method: 'eth_sendTransaction', - params: [ - { - from: SELECTED_ADDRESS, - data: '0xa9059cbb0000000000000000000000005fbdb2315678afecb367f032d93f642f64180aa30000000000000000000000000000000000000000000000000000000000000064', - to: CONTRACT_ADDRESS_USDC, - value: '0x0', - }, - ], + method: 'debug_traceCall', + params: [{ accessList: [], data: '0x00000000' }], }) - .thenJson(201, response); -} - -async function mockInfuraWithMaliciousResponses(mockServer) { - await mockInfura(mockServer); + .thenCallback(async (req) => { + return { + statusCode: 200, + json: { + jsonrpc: '2.0', + id: (await req.body.getJson()).id, + result: { + calls: [ + { + error: 'execution reverted', + from: CONTRACT_ADDRESS.USDC, + gas: '0x1d55c2c7', + gasUsed: '0xf0', + input: '0x00000000', + to: CONTRACT_ADDRESS.FiatTokenV2_1, + type: 'DELEGATECALL', + value: '0x0', + }, + ], + error: 'execution reverted', + from: '0x0000000000000000000000000000000000000000', + gas: '0x1dcd6500', + gasUsed: '0x6f79', + input: '0x00000000', + to: CONTRACT_ADDRESS.USDC, + type: 'CALL', + value: '0x0', + }, + }, + }; + }); - await mockRequest(mockServer, maliciousTransferAlert); + await mockServer + .forPost() + .withJsonBodyIncluding({ + method: 'debug_traceCall', + params: [{ from: selectedAddress }], + }) + .thenCallback(async (req) => { + const mockFakePhishingAddress = + '5fbdb2315678afecb367f032d93f642f64180aa3'; + + return { + statusCode: 200, + json: { + jsonrpc: '2.0', + id: (await req.body.getJson()).id, + result: { + calls: [ + { + from: CONTRACT_ADDRESS.USDC, + gas: '0x2923d', + gasUsed: '0x4cac', + input: `0xa9059cbb000000000000000000000000${mockFakePhishingAddress}0000000000000000000000000000000000000000000000000000000000000064`, + logs: [ + { + address: CONTRACT_ADDRESS.USDC, + data: '0x0000000000000000000000000000000000000000000000000000000000000064', + topics: [ + '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef', + `0x000000000000000000000000${selectedAddressWithoutPrefix}`, + `0x000000000000000000000000${mockFakePhishingAddress}`, + ], + }, + ], + output: + '0x0000000000000000000000000000000000000000000000000000000000000001', + to: CONTRACT_ADDRESS.FiatTokenV2_1, + type: 'DELEGATECALL', + value: '0x0', + }, + ], + from: selectedAddress, + gas: '0x30d40', + gasUsed: '0xbd69', + input: `0xa9059cbb000000000000000000000000${mockFakePhishingAddress}0000000000000000000000000000000000000000000000000000000000000064`, + output: + '0x0000000000000000000000000000000000000000000000000000000000000001', + to: CONTRACT_ADDRESS.USDC, + type: 'CALL', + value: '0x0', + }, + }, + }; + }); } describe('PPOM Blockaid Alert - Malicious ERC20 Transfer @no-mmi', function () { - it('should show banner alert', async function () { - // we need to use localhost instead of the ip - // see issue: https://github.com/MetaMask/MetaMask-planning/issues/3560 + // eslint-disable-next-line mocha/no-skipped-tests + it.skip('should show banner alert', async function () { await withFixtures( { dapp: true, fixtures: new FixtureBuilder() .withNetworkControllerOnMainnet() - .withPermissionControllerConnectedToTestDapp({ - useLocalhostHostname: true, - }) + .withPermissionControllerConnectedToTestDapp() .withPreferencesController({ securityAlertsEnabled: true, }) .build(), defaultGanacheOptions, - testSpecificMock: mockInfuraWithMaliciousResponses, + testSpecificMock: mockInfura, title: this.test.fullTitle(), }, async ({ driver }) => { const expectedTitle = 'This is a deceptive request'; const expectedDescription = - 'If you approve this request, a third party known for scams will take all your assets.'; + 'If you approve this request, you might lose your assets.'; await unlockWallet(driver); - await driver.openNewPage('http://localhost:8080'); + await openDapp(driver); // Click TestDapp button to send JSON-RPC request await driver.clickElement('#maliciousERC20TransferButton'); @@ -97,15 +195,20 @@ describe('PPOM Blockaid Alert - Malicious ERC20 Transfer @no-mmi', function () { await driver.assertElementNotPresent('.loading-indicator'); - await driver.waitForSelector({ - css: '.mm-text--body-lg-medium', + const bannerAlertFoundByTitle = await driver.findElement({ + css: bannerAlertSelector, text: expectedTitle, }); - - await driver.waitForSelector({ - css: '.mm-text--body-md', - text: expectedDescription, - }); + const bannerAlertText = await bannerAlertFoundByTitle.getText(); + + assert( + bannerAlertFoundByTitle, + `Banner alert not found. Expected Title: ${expectedTitle} \nExpected reason: transfer_farming\n`, + ); + assert( + bannerAlertText.includes(expectedDescription), + `Unexpected banner alert description. Expected: ${expectedDescription} \nExpected reason: transfer_farming\n`, + ); }, ); }); diff --git a/test/e2e/tests/ppom/ppom-blockaid-alert-networks-support.spec.js b/test/e2e/tests/ppom/ppom-blockaid-alert-networks-support.spec.js index f40fd68f9566..719f8cbdc16b 100644 --- a/test/e2e/tests/ppom/ppom-blockaid-alert-networks-support.spec.js +++ b/test/e2e/tests/ppom/ppom-blockaid-alert-networks-support.spec.js @@ -114,7 +114,7 @@ describe('PPOM Blockaid Alert - Multiple Networks Support @no-mmi', function () text: 'Add', }); - await driver.clickElement({ tag: 'a', text: 'See details' }); + await driver.clickElement({ tag: 'a', text: 'View all details' }); await driver.clickElement({ tag: 'button', text: 'Close' }); await driver.clickElement({ tag: 'button', text: 'Approve' }); diff --git a/test/e2e/tests/ppom/ppom-blockaid-alert-simple-send.spec.js b/test/e2e/tests/ppom/ppom-blockaid-alert-simple-send.spec.js index c1c7323671f5..8f3e7a657716 100644 --- a/test/e2e/tests/ppom/ppom-blockaid-alert-simple-send.spec.js +++ b/test/e2e/tests/ppom/ppom-blockaid-alert-simple-send.spec.js @@ -6,9 +6,7 @@ const { withFixtures, sendScreenToConfirmScreen, logInWithBalanceValidation, - WINDOW_TITLES, } = require('../../helpers'); -const { SECURITY_ALERTS_PROD_API_BASE_URL } = require('./constants'); const { mockServerJsonRpc } = require('./mocks/mock-server-json-rpc'); const bannerAlertSelector = '[data-testid="security-provider-banner-alert"]'; @@ -19,18 +17,6 @@ const expectedMaliciousTitle = 'This is a deceptive request'; const expectedMaliciousDescription = 'If you approve this request, a third party known for scams will take all your assets.'; -const SEND_REQUEST_BASE_MOCK = { - method: 'eth_sendTransaction', - params: [ - { - from: '0x5cfe73b6021e818b776b421b1c4db2474086a7e1', - data: '0x', - to: mockMaliciousAddress, - value: '0xde0b6b3a7640000', - }, - ], -}; - async function mockInfura(mockServer) { await mockServerJsonRpc(mockServer, [ ['eth_blockNumber'], @@ -45,63 +31,87 @@ async function mockInfura(mockServer) { ]); } -async function mockRequest(server, request, response) { - await server - .forPost(`${SECURITY_ALERTS_PROD_API_BASE_URL}/validate/0x1`) - .withJsonBodyIncluding(request) - .thenJson(response.statusCode ?? 201, response); -} - async function mockInfuraWithBenignResponses(mockServer) { await mockInfura(mockServer); - await mockRequest(mockServer, SEND_REQUEST_BASE_MOCK, { - block: 20733513, - result_type: 'Benign', - reason: '', - description: '', - features: [], - }); + await mockServer + .forPost() + .withJsonBodyIncluding({ + method: 'debug_traceCall', + }) + .thenCallback(async (req) => { + return { + statusCode: 200, + json: { + jsonrpc: '2.0', + id: (await req.body.getJson()).id, + result: { + type: 'CALL', + from: '0x0000000000000000000000000000000000000000', + to: '0xd46e8dd67c5d32be8058bb8eb970870f07244567', + value: '0xde0b6b3a7640000', + gas: '0x16c696eb7', + gasUsed: '0x0', + input: '0x', + output: '0x', + }, + }, + }; + }); } async function mockInfuraWithMaliciousResponses(mockServer) { await mockInfura(mockServer); - await mockRequest(mockServer, SEND_REQUEST_BASE_MOCK, { - block: 20733277, - result_type: 'Malicious', - reason: 'transfer_farming', - description: '', - features: ['Interaction with a known malicious address'], - }); + await mockServer + .forPost() + .withJsonBodyIncluding({ + method: 'debug_traceCall', + params: [{ accessList: [], data: '0x00000000' }], + }) + .thenCallback(async (req) => { + return { + statusCode: 200, + json: { + jsonrpc: '2.0', + id: (await req.body.getJson()).id, + result: { + calls: [ + { + error: 'execution reverted', + from: '0x0000000000000000000000000000000000000000', + gas: '0x1d55c2cb', + gasUsed: '0x39c', + input: '0x00000000', + to: mockMaliciousAddress, + type: 'DELEGATECALL', + value: '0x0', + }, + ], + error: 'execution reverted', + from: '0x0000000000000000000000000000000000000000', + gas: '0x1dcd6500', + gasUsed: '0x721e', + input: '0x00000000', + to: mockMaliciousAddress, + type: 'CALL', + value: '0x0', + }, + }, + }; + }); } async function mockInfuraWithFailedResponses(mockServer) { await mockInfura(mockServer); - await mockRequest( - mockServer, - { - ...SEND_REQUEST_BASE_MOCK, - params: [ - { - from: '0x5cfe73b6021e818b776b421b1c4db2474086a7e1', - data: '0x', - to: '0xb8c77482e45f1f44de1745f52c74426c631bdd52', - value: '0xf43fc2c04ee0000', - }, - ], - }, - { statusCode: 500, message: 'Internal server error' }, - ); - - // Retained this mock to support fallback to the local PPOM await mockServer - .forGet( - 'https://static.cx.metamask.io/api/v1/confirmations/ppom/ppom_version.json', - ) + .forPost() + .withJsonBodyIncluding({ + method: 'debug_traceCall', + params: [{ accessList: [], data: '0x00000000' }], + }) .thenCallback(() => { - console.log('mocked ppom_version.json'); return { statusCode: 500, }; @@ -135,7 +145,7 @@ describe('Simple Send Security Alert - Blockaid @no-mmi', function () { await logInWithBalanceValidation(driver); await sendScreenToConfirmScreen(driver, mockBenignAddress, '1'); - + // await driver.delay(100000) const isPresent = await driver.isElementPresent(bannerAlertSelector); assert.equal(isPresent, false, `Banner alert unexpectedly found.`); }, @@ -149,15 +159,10 @@ describe('Simple Send Security Alert - Blockaid @no-mmi', function () { */ it('should show security alerts for malicious requests', async function () { await withFixtures( - // we need to use localhost instead of the ip - // see issue: https://github.com/MetaMask/MetaMask-planning/issues/3560 { dapp: true, fixtures: new FixtureBuilder() .withNetworkControllerOnMainnet() - .withPermissionControllerConnectedToTestDapp({ - useLocalhostHostname: true, - }) .withPreferencesController({ securityAlertsEnabled: true, }) @@ -170,25 +175,29 @@ describe('Simple Send Security Alert - Blockaid @no-mmi', function () { async ({ driver }) => { await logInWithBalanceValidation(driver); - await driver.openNewPage('http://localhost:8080'); - - await driver.clickElement('#maliciousRawEthButton'); - await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); + await sendScreenToConfirmScreen(driver, mockMaliciousAddress, '1'); - await driver.waitForSelector({ - css: '.mm-text--body-lg-medium', + // Find element by title + const bannerAlertFoundByTitle = await driver.findElement({ + css: bannerAlertSelector, text: expectedMaliciousTitle, }); + const bannerAlertText = await bannerAlertFoundByTitle.getText(); - await driver.waitForSelector({ - css: '.mm-text--body-md', - text: expectedMaliciousDescription, - }); + assert( + bannerAlertFoundByTitle, + `Banner alert not found. Expected Title: ${expectedMaliciousTitle}`, + ); + assert( + bannerAlertText.includes(expectedMaliciousDescription), + `Unexpected banner alert description. Expected: ${expectedMaliciousDescription}`, + ); }, ); }); - it('should show "Be careful" if the PPOM request fails to check transaction', async function () { + // eslint-disable-next-line mocha/no-skipped-tests + it.skip('should show "Request may not be safe" if the PPOM request fails to check transaction', async function () { await withFixtures( { dapp: true, @@ -211,7 +220,8 @@ describe('Simple Send Security Alert - Blockaid @no-mmi', function () { '0xB8c77482e45F1F44dE1745F52C74426C631bDD52', '1.1', ); - const expectedTitle = 'Be careful'; + // await driver.delay(100000) + const expectedTitle = 'Request may not be safe'; const bannerAlert = await driver.findElement({ css: bannerAlertSelector, diff --git a/test/e2e/tests/ppom/ppom-blockaid-alert-trade-order-farming.spec.js b/test/e2e/tests/ppom/ppom-blockaid-alert-trade-order-farming.spec.js index ac17614bc5af..8f2debc7b4f2 100644 --- a/test/e2e/tests/ppom/ppom-blockaid-alert-trade-order-farming.spec.js +++ b/test/e2e/tests/ppom/ppom-blockaid-alert-trade-order-farming.spec.js @@ -1,13 +1,17 @@ +const { strict: assert } = require('assert'); const FixtureBuilder = require('../../fixture-builder'); const { WINDOW_TITLES, defaultGanacheOptions, + openDapp, unlockWallet, withFixtures, } = require('../../helpers'); const { mockServerJsonRpc } = require('./mocks/mock-server-json-rpc'); +const bannerAlertSelector = '[data-testid="security-provider-banner-alert"]'; + const CONTRACT_ADDRESS = { WrappedEther: 'c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', OffchainOracle: '0x52cbe0f49ccdd4dc6e9c13bab024eabd2842045b', @@ -86,17 +90,14 @@ async function mockInfura(mockServer) { } describe('PPOM Blockaid Alert - Set Trade farming order @no-mmi', function () { - it('should show banner alert', async function () { - // we need to use localhost instead of the ip - // see issue: https://github.com/MetaMask/MetaMask-planning/issues/3560 + // eslint-disable-next-line mocha/no-skipped-tests + it.skip('should show banner alert', async function () { await withFixtures( { dapp: true, fixtures: new FixtureBuilder() .withNetworkControllerOnMainnet() - .withPermissionControllerConnectedToTestDapp({ - useLocalhostHostname: true, - }) + .withPermissionControllerConnectedToTestDapp() .withPreferencesController({ securityAlertsEnabled: true, }) @@ -108,7 +109,7 @@ describe('PPOM Blockaid Alert - Set Trade farming order @no-mmi', function () { async ({ driver }) => { await unlockWallet(driver); - await driver.openNewPage('http://localhost:8080'); + await openDapp(driver); const expectedTitle = 'This is a deceptive request'; const expectedDescription = @@ -116,19 +117,27 @@ describe('PPOM Blockaid Alert - Set Trade farming order @no-mmi', function () { // Click TestDapp button to send JSON-RPC request await driver.clickElement('#maliciousTradeOrder'); + + // Wait for confirmation pop-up + await driver.waitUntilXWindowHandles(3); await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); await driver.assertElementNotPresent('.loading-indicator'); - await driver.waitForSelector({ - css: '.mm-text--body-lg-medium', + const bannerAlertFoundByTitle = await driver.findElement({ + css: bannerAlertSelector, text: expectedTitle, }); + const bannerAlertText = await bannerAlertFoundByTitle.getText(); - await driver.waitForSelector({ - css: '.mm-text--body-md', - text: expectedDescription, - }); + assert( + bannerAlertFoundByTitle, + `Banner alert not found. Expected Title: ${expectedTitle} \nExpected reason: approval_farming\n`, + ); + assert( + bannerAlertText.includes(expectedDescription), + `Unexpected banner alert description. Expected: ${expectedDescription} \nExpected reason: approval_farming\n`, + ); }, ); }); diff --git a/test/e2e/tests/privacy-mode/privacy-mode.spec.js b/test/e2e/tests/privacy-mode/privacy-mode.spec.js deleted file mode 100644 index a4d2c2245752..000000000000 --- a/test/e2e/tests/privacy-mode/privacy-mode.spec.js +++ /dev/null @@ -1,106 +0,0 @@ -const { strict: assert } = require('assert'); -const { - withFixtures, - unlockWallet, - defaultGanacheOptions, -} = require('../../helpers'); -const FixtureBuilder = require('../../fixture-builder'); - -describe('Privacy Mode', function () { - it('should activate privacy mode, then deactivate it', async function () { - await withFixtures( - { - dapp: true, - fixtures: new FixtureBuilder().withPreferencesController().build(), - ganacheOptions: defaultGanacheOptions, - title: this.test.fullTitle(), - }, - async ({ driver }) => { - async function checkForHeaderValue(value) { - const balanceElement = await driver.findElement( - '[data-testid="eth-overview__primary-currency"] .currency-display-component__text', - ); - const surveyText = await balanceElement.getText(); - assert.equal( - surveyText, - value, - `Header balance should be "${value}"`, - ); - } - - async function checkForTokenValue(value) { - const balanceElement = await driver.findElement( - '[data-testid="multichain-token-list-item-secondary-value"]', - ); - const surveyText = await balanceElement.getText(); - assert.equal(surveyText, value, `Token balance should be "${value}"`); - } - - async function checkForPrivacy() { - await checkForHeaderValue('••••••'); - await checkForTokenValue('•••••••••'); - } - - async function checkForNoPrivacy() { - await checkForHeaderValue('25'); - await checkForTokenValue('25 ETH'); - } - - async function togglePrivacy() { - const balanceElement = await driver.findElement( - '[data-testid="eth-overview__primary-currency"] .currency-display-component__text', - ); - const initialText = await balanceElement.getText(); - - await driver.clickElement('[data-testid="sensitive-toggle"]'); - await driver.wait(async () => { - const currentText = await balanceElement.getText(); - return currentText !== initialText; - }, 2e3); - } - - await unlockWallet(driver); - await checkForNoPrivacy(); - await togglePrivacy(); - await checkForPrivacy(); - await togglePrivacy(); - await checkForNoPrivacy(); - }, - ); - }); - - it('should hide fiat balance and token balance when privacy mode is activated', async function () { - await withFixtures( - { - fixtures: new FixtureBuilder().withPreferencesController().build(), - ganacheOptions: defaultGanacheOptions, - title: this.test.fullTitle(), - }, - async ({ driver }) => { - await unlockWallet(driver); - - async function togglePrivacy() { - const balanceElement = await driver.findElement( - '[data-testid="eth-overview__primary-currency"] .currency-display-component__text', - ); - const initialText = await balanceElement.getText(); - - await driver.clickElement('[data-testid="sensitive-toggle"]'); - await driver.wait(async () => { - const currentText = await balanceElement.getText(); - return currentText !== initialText; - }, 2e3); - } - - await togglePrivacy(); - await driver.clickElement('[data-testid="account-menu-icon"]'); - const valueText = await driver.findElement( - '[data-testid="account-value-and-suffix"]', - ); - const valueTextContent = await valueText.getText(); - - assert.equal(valueTextContent, '••••••'); - }, - ); - }); -}); diff --git a/test/e2e/tests/privacy/basic-functionality.spec.js b/test/e2e/tests/privacy/basic-functionality.spec.js index a945154f4bd3..674ba8772e29 100644 --- a/test/e2e/tests/privacy/basic-functionality.spec.js +++ b/test/e2e/tests/privacy/basic-functionality.spec.js @@ -26,8 +26,8 @@ async function mockApis(mockServer) { }; }), await mockServer - .forGet('https://min-api.cryptocompare.com/data/pricemulti') - .withQuery({ fsyms: 'ETH', tsyms: 'usd' }) + .forGet('https://min-api.cryptocompare.com/data/price') + .withQuery({ fsym: 'ETH', tsyms: 'USD' }) .thenCallback(() => { return { statusCode: 200, diff --git a/test/e2e/tests/privacy/onboarding-infura-call-privacy.spec.ts b/test/e2e/tests/privacy/onboarding-infura-call-privacy.spec.ts deleted file mode 100644 index b18d713d9474..000000000000 --- a/test/e2e/tests/privacy/onboarding-infura-call-privacy.spec.ts +++ /dev/null @@ -1,188 +0,0 @@ -import assert from 'assert'; -import { Mockttp, MockedEndpoint } from 'mockttp'; -import { withFixtures, regularDelayMs } from '../../helpers'; -import FixtureBuilder from '../../fixture-builder'; -import HomePage from '../../page-objects/pages/homepage'; -import OnboardingCompletePage from '../../page-objects/pages/onboarding/onboarding-complete-page'; -import { - importSRPOnboardingFlow, - createNewWalletOnboardingFlow, -} from '../../page-objects/flows/onboarding.flow'; - -// Mock function implementation for Infura requests -async function mockInfura(mockServer: Mockttp): Promise { - const infuraUrl = - 'https://mainnet.infura.io/v3/00000000000000000000000000000000'; - const sampleAddress = '1111111111111111111111111111111111111111'; - return [ - await mockServer - .forPost(infuraUrl) - .withJsonBodyIncluding({ method: 'eth_blockNumber' }) - .thenCallback(() => { - return { - statusCode: 200, - json: { - jsonrpc: '2.0', - id: '1111111111111111', - result: '0x1', - }, - }; - }), - await mockServer - .forPost(infuraUrl) - .withJsonBodyIncluding({ method: 'eth_getBalance' }) - .thenCallback(() => { - return { - statusCode: 200, - json: { - jsonrpc: '2.0', - id: '1111111111111111', - result: '0x0', - }, - }; - }), - await mockServer - .forPost(infuraUrl) - .withJsonBodyIncluding({ method: 'eth_getBlockByNumber' }) - .thenCallback(() => { - return { - statusCode: 200, - json: { - jsonrpc: '2.0', - id: '1111111111111111', - result: {}, - }, - }; - }), - await mockServer - .forPost(infuraUrl) - .withJsonBodyIncluding({ method: 'eth_call' }) - .thenCallback(() => { - return { - statusCode: 200, - json: { - jsonrpc: '2.0', - id: '1111111111111111', - result: `0x000000000000000000000000${sampleAddress}`, - }, - }; - }), - await mockServer - .forPost(infuraUrl) - .withJsonBodyIncluding({ method: 'net_version' }) - .thenCallback(() => { - return { - statusCode: 200, - json: { id: 8262367391254633, jsonrpc: '2.0', result: '1337' }, - }; - }), - ]; -} - -describe('MetaMask onboarding @no-mmi', function () { - it("doesn't make any network requests to infura before create new wallet onboarding is completed", async function () { - await withFixtures( - { - fixtures: new FixtureBuilder({ onboarding: true }) - .withNetworkControllerOnMainnet() - .build(), - title: this.test?.fullTitle(), - testSpecificMock: mockInfura, - }, - async ({ driver, mockedEndpoint: mockedEndpoints }) => { - await createNewWalletOnboardingFlow(driver); - - // Check no requests are made before completing creat new wallet onboarding - // Intended delay to ensure we cover at least 1 polling loop of time for the network request - await driver.delay(regularDelayMs); - for (const mockedEndpoint of mockedEndpoints) { - const isPending = await mockedEndpoint.isPending(); - assert.equal( - isPending, - true, - `${mockedEndpoint} mock should still be pending before onboarding`, - ); - const requests = await mockedEndpoint.getSeenRequests(); - assert.equal( - requests.length, - 0, - `${mockedEndpoint} should make no requests before onboarding`, - ); - } - - // complete create new wallet onboarding - const onboardingCompletePage = new OnboardingCompletePage(driver); - await onboardingCompletePage.check_pageIsLoaded(); - await onboardingCompletePage.completeOnboarding(); - const homePage = new HomePage(driver); - await homePage.check_pageIsLoaded(); - await homePage.check_expectedBalanceIsDisplayed(); - - // network requests happen here - for (const mockedEndpoint of mockedEndpoints) { - await driver.wait(async () => { - const isPending = await mockedEndpoint.isPending(); - return isPending === false; - }, driver.timeout); - - const requests = await mockedEndpoint.getSeenRequests(); - assert.equal( - requests.length > 0, - true, - `${mockedEndpoint} should make requests after onboarding`, - ); - } - }, - ); - }); - - it("doesn't make any network requests to infura before onboarding by import is completed", async function () { - await withFixtures( - { - fixtures: new FixtureBuilder({ onboarding: true }) - .withNetworkControllerOnMainnet() - .build(), - title: this.test?.fullTitle(), - testSpecificMock: mockInfura, - }, - async ({ driver, mockedEndpoint: mockedEndpoints }) => { - await importSRPOnboardingFlow(driver); - - // Check no requests before completing onboarding - // Intended delay to ensure we cover at least 1 polling loop of time for the network request - await driver.delay(regularDelayMs); - for (const mockedEndpoint of mockedEndpoints) { - const requests = await mockedEndpoint.getSeenRequests(); - assert.equal( - requests.length, - 0, - `${mockedEndpoint} should make no requests before import wallet onboarding complete`, - ); - } - - // complete import wallet onboarding - const onboardingCompletePage = new OnboardingCompletePage(driver); - await onboardingCompletePage.check_pageIsLoaded(); - await onboardingCompletePage.completeOnboarding(); - const homePage = new HomePage(driver); - await homePage.check_pageIsLoaded(); - await homePage.check_expectedBalanceIsDisplayed(); - - // requests happen here - for (const mockedEndpoint of mockedEndpoints) { - await driver.wait(async () => { - const isPending = await mockedEndpoint.isPending(); - return isPending === false; - }, driver.timeout); - - const requests = await mockedEndpoint.getSeenRequests(); - assert.equal( - requests.length > 0, - true, - `${mockedEndpoint} should make requests after onboarding`, - ); - } - }, - ); - }); -}); diff --git a/test/e2e/tests/request-queuing/batch-txs-per-dapp-same-network.spec.js b/test/e2e/tests/request-queuing/batch-txs-per-dapp-same-network.spec.js index c30d6a73c063..bd52558ec67f 100644 --- a/test/e2e/tests/request-queuing/batch-txs-per-dapp-same-network.spec.js +++ b/test/e2e/tests/request-queuing/batch-txs-per-dapp-same-network.spec.js @@ -1,4 +1,4 @@ -const { By } = require('selenium-webdriver'); +const { strict: assert } = require('assert'); const FixtureBuilder = require('../../fixture-builder'); const { withFixtures, @@ -10,6 +10,7 @@ const { WINDOW_TITLES, defaultGanacheOptions, largeDelayMs, + switchToNotificationWindow, } = require('../../helpers'); const { PAGES } = require('../../webdriver/driver'); @@ -58,7 +59,7 @@ describe('Request Queuing for Multiple Dapps and Txs on same networks', function await driver.delay(regularDelayMs); - await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); + await switchToNotificationWindow(driver); await driver.clickElement({ text: 'Connect', @@ -97,7 +98,7 @@ describe('Request Queuing for Multiple Dapps and Txs on same networks', function await driver.delay(regularDelayMs); - await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); + await switchToNotificationWindow(driver, 4); await driver.clickElement({ text: 'Connect', @@ -133,12 +134,16 @@ describe('Request Queuing for Multiple Dapps and Txs on same networks', function await driver.clickElement('#sendButton'); await driver.clickElement('#sendButton'); - await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); + await switchToNotificationWindow(driver, 4); - await driver.waitForSelector( - By.xpath("//div[normalize-space(.)='1 of 2']"), + let navigationElement = await driver.findElement( + '.confirm-page-container-navigation', ); + let navigationText = await navigationElement.getText(); + + assert.equal(navigationText.includes('1 of 2'), true); + // Check correct network on confirm tx. await driver.findElement({ css: '[data-testid="network-display"]', @@ -157,10 +162,14 @@ describe('Request Queuing for Multiple Dapps and Txs on same networks', function await driver.delay(largeDelayMs); await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - await driver.waitForSelector( - By.xpath("//div[normalize-space(.)='1 of 2']"), + navigationElement = await driver.findElement( + '.confirm-page-container-navigation', ); + navigationText = await navigationElement.getText(); + + assert.equal(navigationText.includes('1 of 2'), true); + // Check correct network on confirm tx. await driver.findElement({ css: '[data-testid="network-display"]', diff --git a/test/e2e/tests/request-queuing/dapp1-send-dapp2-signTypedData.spec.js b/test/e2e/tests/request-queuing/dapp1-send-dapp2-signTypedData.spec.js index 5814d8a60a2b..d52d45701563 100644 --- a/test/e2e/tests/request-queuing/dapp1-send-dapp2-signTypedData.spec.js +++ b/test/e2e/tests/request-queuing/dapp1-send-dapp2-signTypedData.spec.js @@ -9,7 +9,6 @@ const { defaultGanacheOptions, tempToggleSettingRedesignedConfirmations, WINDOW_TITLES, - largeDelayMs, } = require('../../helpers'); describe('Request Queuing Dapp 1, Switch Tx -> Dapp 2 Send Tx', function () { @@ -91,7 +90,7 @@ describe('Request Queuing Dapp 1, Switch Tx -> Dapp 2 Send Tx', function () { `window.ethereum.request(${switchEthereumChainRequest})`, ); - await driver.waitForSelector({ + await driver.findElement({ css: '[id="chainId"]', text: '0x53a', }); @@ -112,7 +111,7 @@ describe('Request Queuing Dapp 1, Switch Tx -> Dapp 2 Send Tx', function () { await driver.executeScript( `window.ethereum.request(${switchEthereumChainRequest})`, ); - await driver.waitForSelector({ + await driver.findElement({ css: '[id="chainId"]', text: '0x3e8', }); @@ -133,24 +132,21 @@ describe('Request Queuing Dapp 1, Switch Tx -> Dapp 2 Send Tx', function () { await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); // Check correct network on the send confirmation. - await driver.waitForSelector({ + await driver.findElement({ css: '[data-testid="network-display"]', text: 'Localhost 7777', }); await driver.clickElement({ text: 'Confirm', tag: 'button' }); - await driver.delay(largeDelayMs); await driver.waitUntilXWindowHandles(4); await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); // Check correct network on the signTypedData confirmation. - await driver.waitForSelector({ + await driver.findElement({ css: '[data-testid="signature-request-network-display"]', text: 'Localhost 8546', }); - - await driver.clickElement({ text: 'Reject', tag: 'button' }); }, ); }); diff --git a/test/e2e/tests/request-queuing/watchAsset-switchChain-watchAsset.spec.js b/test/e2e/tests/request-queuing/watchAsset-switchChain-watchAsset.spec.js index 64ac781a20e0..1c1baa17fb5a 100644 --- a/test/e2e/tests/request-queuing/watchAsset-switchChain-watchAsset.spec.js +++ b/test/e2e/tests/request-queuing/watchAsset-switchChain-watchAsset.spec.js @@ -7,6 +7,7 @@ const { DAPP_URL, regularDelayMs, WINDOW_TITLES, + switchToNotificationWindow, defaultGanacheOptions, } = require('../../helpers'); @@ -44,15 +45,19 @@ describe('Request Queue WatchAsset -> SwitchChain -> WatchAsset', function () { // Create Token await driver.clickElement({ text: 'Create Token', tag: 'button' }); - await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); + await switchToNotificationWindow(driver); + await driver.findClickableElement({ text: 'Confirm', tag: 'button' }); await driver.clickElement({ text: 'Confirm', tag: 'button' }); // Wait for token address to populate in dapp await driver.switchToWindowWithTitle(WINDOW_TITLES.TestDApp); - await driver.waitForSelector({ - css: '#tokenAddresses', - text: '0x581c3C1A2A4EBDE2A0Df29B5cf4c116E42945947', - }); + await driver.wait(async () => { + const tokenAddressesElement = await driver.findElement( + '#tokenAddresses', + ); + const tokenAddresses = await tokenAddressesElement.getText(); + return tokenAddresses !== ''; + }, 10000); // Watch Asset 1st call await driver.clickElement({ @@ -60,9 +65,11 @@ describe('Request Queue WatchAsset -> SwitchChain -> WatchAsset', function () { tag: 'button', }); + await driver.waitUntilXWindowHandles(3); await driver.switchToWindowWithTitle(WINDOW_TITLES.TestDApp); // Switch Ethereum Chain + await driver.findClickableElement('#switchEthereumChain'); await driver.clickElement('#switchEthereumChain'); await driver.switchToWindowWithTitle(WINDOW_TITLES.TestDApp); @@ -76,7 +83,7 @@ describe('Request Queue WatchAsset -> SwitchChain -> WatchAsset', function () { // Wait for token to show in list of tokens to watch await driver.delay(regularDelayMs); - await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); + await switchToNotificationWindow(driver); const multipleSuggestedtokens = await driver.findElements( '.confirm-add-suggested-token__token-list-item', @@ -85,7 +92,7 @@ describe('Request Queue WatchAsset -> SwitchChain -> WatchAsset', function () { // Confirm only 1 token is present in suggested token list assert.equal(multipleSuggestedtokens.length, 1); - await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); + await switchToNotificationWindow(driver); await driver.waitUntilXWindowHandles(2); diff --git a/test/e2e/tests/responsive-ui/metamask-responsive-ui.spec.js b/test/e2e/tests/responsive-ui/metamask-responsive-ui.spec.js index 958854a5252c..446d579630bf 100644 --- a/test/e2e/tests/responsive-ui/metamask-responsive-ui.spec.js +++ b/test/e2e/tests/responsive-ui/metamask-responsive-ui.spec.js @@ -72,10 +72,10 @@ describe('MetaMask Responsive UI', function () { await driver.clickElement('[data-testid="pin-extension-done"]'); await driver.assertElementNotPresent('.loading-overlay__spinner'); // assert balance - await driver.waitForSelector({ - css: '[data-testid="eth-overview__primary-currency"]', - text: '0', - }); + const balance = await driver.findElement( + '[data-testid="eth-overview__primary-currency"]', + ); + assert.ok(/^0\sETH$/u.test(await balance.getText())); }, ); }); @@ -93,14 +93,11 @@ describe('MetaMask Responsive UI', function () { await driver.navigate(); // Import Secret Recovery Phrase - await driver.waitForSelector({ - tag: 'span', - text: 'Localhost 8545', - }); - await driver.clickElement({ - css: '.unlock-page__link', - text: 'Forgot password?', - }); + const restoreSeedLink = await driver.findClickableElement( + '.unlock-page__link', + ); + assert.equal(await restoreSeedLink.getText(), 'Forgot password?'); + await restoreSeedLink.click(); await driver.pasteIntoField( '[data-testid="import-srp__srp-word-0"]', diff --git a/test/e2e/tests/settings/about-metamask-ui-validation.spec.ts b/test/e2e/tests/settings/about-metamask-ui-validation.spec.ts index ed2702fef413..abc3c4857957 100644 --- a/test/e2e/tests/settings/about-metamask-ui-validation.spec.ts +++ b/test/e2e/tests/settings/about-metamask-ui-validation.spec.ts @@ -68,11 +68,16 @@ describe('Setting - About MetaMask : @no-mmi', function (this: Suite) { ); // verify the version number of the MetaMask + const metaMaskVersion = await driver.findElement( + selectors.metaMaskVersion, + ); + const getVersionNumber = await metaMaskVersion.getText(); const { version } = packageJson; - await driver.waitForSelector({ - css: selectors.metaMaskVersion, - text: version, - }); + assert.equal( + getVersionNumber, + version, + 'Meta Mask version is incorrect in the about view section', + ); // Validating the header text const isHeaderTextPresent = await driver.isElementPresent( diff --git a/test/e2e/tests/settings/address-book.spec.js b/test/e2e/tests/settings/address-book.spec.js index b71f45c2cf21..e81bd7c544aa 100644 --- a/test/e2e/tests/settings/address-book.spec.js +++ b/test/e2e/tests/settings/address-book.spec.js @@ -39,11 +39,12 @@ describe('Address Book', function () { await driver.clickElement({ css: 'button', text: 'Contacts' }); - await driver.waitForSelector({ - css: '.address-list-item__label', - text: 'Test Name 1', - }); + const recipientTitle = await driver.findElement( + '.address-list-item__label', + ); + const recipientRowTitleString = await recipientTitle.getText(); + assert.equal(recipientRowTitleString, 'Test Name 1'); await driver.clickElement('.address-list-item__label'); await driver.fill('input[placeholder="0"]', '2'); @@ -69,44 +70,6 @@ describe('Address Book', function () { }, ); }); - - it('Adds a new contact to the address book', async function () { - await withFixtures( - { - fixtures: new FixtureBuilder().build(), - ganacheOptions: defaultGanacheOptions, - title: this.test.fullTitle(), - }, - async ({ driver }) => { - await unlockWallet(driver); - await openMenuSafe(driver); - - await driver.clickElement({ text: 'Settings', tag: 'div' }); - await driver.clickElement({ text: 'Contacts', tag: 'div' }); - - await driver.clickElement('.address-book__link'); - - await driver.fill('#nickname', 'Test User'); - - await driver.fill( - '[data-testid="ens-input"]', - '0x56A355d3427bC2B1E22c78197AF091230919Cc2A', - ); - - await driver.clickElement('[data-testid="page-container-footer-next"]'); - - await driver.waitForSelector({ - text: 'Test User', - css: '.address-list-item__label', - }); - await driver.waitForSelector({ - css: '[data-testid="address-list-item-address"]', - text: '0x56A35...9Cc2A', - }); - }, - ); - }); - it('Edit entry in address book', async function () { await withFixtures( { @@ -148,15 +111,25 @@ describe('Address Book', function () { await driver.clickElement('[data-testid="page-container-footer-next"]'); - await driver.waitForSelector({ + const recipientUsername = await driver.findElement({ text: 'Test Name Edit', css: '.address-list-item__label', }); - await driver.waitForSelector({ - css: '[data-testid="address-list-item-address"]', - text: shortenAddress('0x74cE91B75935D6Bedc27eE002DeFa566c5946f74'), - }); + assert.equal( + await recipientUsername.getText(), + 'Test Name Edit', + 'Username is not edited correctly', + ); + + const recipientAddress = await driver.findElement( + '[data-testid="address-list-item-address"]', + ); + assert.equal( + await recipientAddress.getText(), + shortenAddress('0x74cE91B75935D6Bedc27eE002DeFa566c5946f74'), + 'Recipient address is not edited correctly', + ); }, ); }); diff --git a/test/e2e/tests/settings/auto-lock.spec.js b/test/e2e/tests/settings/auto-lock.spec.js index 46021ed0bb46..7d7a159d4a1b 100644 --- a/test/e2e/tests/settings/auto-lock.spec.js +++ b/test/e2e/tests/settings/auto-lock.spec.js @@ -1,3 +1,4 @@ +const { strict: assert } = require('assert'); const { defaultGanacheOptions, openMenuSafe, @@ -40,11 +41,12 @@ describe('Auto-Lock Timer', function () { '[data-testid="advanced-setting-auto-lock"] button', ); // Verify the wallet is locked - await driver.waitForSelector({ - css: '[data-testid="unlock-page-title"]', - text: 'Welcome back!', - }); - await driver.waitForSelector('.unlock-page button'); + const pageTitle = await driver.findElement( + '[data-testid="unlock-page-title"]', + ); + const unlockButton = await driver.findElement('.unlock-page button'); + assert.equal(await pageTitle.getText(), 'Welcome back!'); + assert.equal(await unlockButton.isDisplayed(), true); }, ); }); diff --git a/test/e2e/tests/settings/localization.spec.js b/test/e2e/tests/settings/localization.spec.js index 229c385efbeb..57dbfd5f68cf 100644 --- a/test/e2e/tests/settings/localization.spec.js +++ b/test/e2e/tests/settings/localization.spec.js @@ -1,3 +1,4 @@ +const { strict: assert } = require('assert'); const { defaultGanacheOptions, withFixtures, @@ -5,23 +6,6 @@ const { } = require('../../helpers'); const FixtureBuilder = require('../../fixture-builder'); -async function mockPhpConversion(mockServer) { - return await mockServer - .forGet('https://min-api.cryptocompare.com/data/pricemulti') - .withQuery({ fsyms: 'ETH', tsyms: 'php,USD' }) - .thenCallback(() => { - return { - statusCode: 200, - json: { - ETH: { - PHP: '100000', - USD: '2500', - }, - }, - }; - }); -} - describe('Localization', function () { it('can correctly display Philippine peso symbol and code', async function () { await withFixtures( @@ -38,22 +22,18 @@ describe('Localization', function () { }) .build(), ganacheOptions: defaultGanacheOptions, - testSpecificMock: mockPhpConversion, title: this.test.fullTitle(), }, async ({ driver }) => { await unlockWallet(driver); // After the removal of displaying secondary currency in coin-overview.tsx, we will test localization on main balance with showNativeTokenAsMainBalance = false - await driver.waitForSelector({ - tag: 'span', - text: 'PHP', - }); - - await driver.waitForSelector({ - tag: 'span', - text: '₱2,500,000.00', - }); + const primaryBalance = await driver.findElement( + '[data-testid="eth-overview__primary-currency"]', + ); + const balanceText = await primaryBalance.getText(); + assert.ok(balanceText.startsWith('₱')); + assert.ok(balanceText.endsWith('PHP')); }, ); }); diff --git a/test/e2e/tests/settings/settings-general.spec.js b/test/e2e/tests/settings/settings-general.spec.js index ef07a53d01f2..5e75c857a7f8 100644 --- a/test/e2e/tests/settings/settings-general.spec.js +++ b/test/e2e/tests/settings/settings-general.spec.js @@ -1,3 +1,4 @@ +const { strict: assert } = require('assert'); const { defaultGanacheOptions, openMenuSafe, @@ -27,15 +28,25 @@ describe('Settings', function () { '[data-testid="jazz_icon"] .settings-page__content-item__identicon__item__icon--active', ); - await driver.waitForSelector({ + const jazziconText = await driver.findElement({ tag: 'h6', text: 'Jazzicons', }); + assert.equal( + await jazziconText.getText(), + 'Jazzicons', + 'Text for icon should be Jazzicons', + ); - await driver.waitForSelector({ + const blockiesText = await driver.findElement({ tag: 'h6', text: 'Blockies', }); + assert.equal( + await blockiesText.getText(), + 'Blockies', + 'Text for icon should be Blockies', + ); }, ); }); diff --git a/test/e2e/tests/settings/settings-security-reveal-srp.spec.js b/test/e2e/tests/settings/settings-security-reveal-srp.spec.js index 7f68f53f8dc7..7cbdaefe73ed 100644 --- a/test/e2e/tests/settings/settings-security-reveal-srp.spec.js +++ b/test/e2e/tests/settings/settings-security-reveal-srp.spec.js @@ -52,10 +52,10 @@ describe('Reveal SRP through settings', function () { await tapAndHoldToRevealSRP(driver); // confirm SRP text matches expected - await driver.waitForSelector({ - css: '[data-testid="srp_text"]', - text: E2E_SRP, - }); + const displayedSRP = await driver.findVisibleElement( + '[data-testid="srp_text"]', + ); + assert.equal(await displayedSRP.getText(), E2E_SRP); // copy SRP text to clipboard await driver.clickElement({ diff --git a/test/e2e/tests/settings/show-hex-data.spec.js b/test/e2e/tests/settings/show-hex-data.spec.js index 4bef79ca0a3b..5e0ae9a133a0 100644 --- a/test/e2e/tests/settings/show-hex-data.spec.js +++ b/test/e2e/tests/settings/show-hex-data.spec.js @@ -1,3 +1,4 @@ +const { strict: assert } = require('assert'); const { defaultGanacheOptions, withFixtures, @@ -83,10 +84,15 @@ describe('Check the toggle for hex data', function () { await sendTransactionAndVerifyHexData(driver); // Verify hex data in the container content - await driver.waitForSelector({ - tag: 'p', - text: '0x0abc', - }); + const pageContentContainer = await driver.findElement( + selectors.containerContent, + ); + const pageContentContainerText = await pageContentContainer.getText(); + assert.equal( + pageContentContainerText.includes(inputData.hexDataText), + true, + 'Hex data is incorrect', + ); }, ); }); diff --git a/test/e2e/tests/transaction/ens.spec.ts b/test/e2e/tests/transaction/ens.spec.ts index 47bae3e5e4cc..4700ab8fac3f 100644 --- a/test/e2e/tests/transaction/ens.spec.ts +++ b/test/e2e/tests/transaction/ens.spec.ts @@ -112,7 +112,6 @@ describe('ENS', function (this: Suite) { // click send button on homepage to start send flow const homepage = new HomePage(driver); - await homepage.check_pageIsLoaded(); await homepage.check_expectedBalanceIsDisplayed('<0.000001'); await homepage.startSendFlow(); diff --git a/test/e2e/webdriver/driver.js b/test/e2e/webdriver/driver.js index 1a10f7c0199d..813d00d5e0e8 100644 --- a/test/e2e/webdriver/driver.js +++ b/test/e2e/webdriver/driver.js @@ -184,8 +184,8 @@ class Driver { * * To target an element based on its attribute using a CSS selector, * use square brackets ([]) to specify the attribute name and its value. - * @example Example to locate the ‘Buy & Sell’ button using its unique attribute testId and its value on the overview screen - * await driver.findElement({testId: 'eth-overview-buy'}); + * @example Example to locate the ‘Buy & Sell’ button using its unique attribute data-testid and its value on the overview screen + * await driver.findElement('[data-testid="eth-overview-buy"]'); * * To locate an element by XPath locator strategy * @example Example to locate 'Confirm' button on the send transaction page @@ -204,11 +204,6 @@ class Driver { // xpath locator. return By.xpath(locator.xpath); } else if (locator.text) { - // If a testId prop was provided along with text, convert that to a css prop and continue - if (locator.testId) { - locator.css = `[data-testid="${locator.testId}"]`; - } - // Providing a text prop, and optionally a tag or css prop, will use // xpath to look for an element with the tag that has matching text. if (locator.css) { @@ -237,12 +232,7 @@ class Driver { const quoted = quoteXPathText(locator.text); // The tag prop is optional and further refines which elements match return By.xpath(`//${locator.tag ?? '*'}[contains(text(), ${quoted})]`); - } else if (locator.testId) { - // Providing a testId prop will use css to look for an element with the - // data-testid attribute that matches the testId provided. - return By.css(`[data-testid="${locator.testId}"]`); } - throw new Error( `The locator '${locator}' is not supported by the E2E test driver`, ); @@ -286,12 +276,6 @@ class Driver { await new Promise((resolve) => setTimeout(resolve, time)); } - async delayFirefox(time) { - if (process.env.SELENIUM_BROWSER === 'firefox') { - await new Promise((resolve) => setTimeout(resolve, time)); - } - } - /** * Function to wait for a specific condition to be met within a given timeout period, * with an option to catch and handle any errors that occur during the wait. diff --git a/test/env.js b/test/env.js index 268f01af0e3b..2dacb1d888ba 100644 --- a/test/env.js +++ b/test/env.js @@ -16,4 +16,3 @@ process.env.PUSH_NOTIFICATIONS_SERVICE_URL = process.env.PORTFOLIO_URL = 'https://portfolio.test'; process.env.METAMASK_VERSION = 'MOCK_VERSION'; process.env.ENABLE_CONFIRMATION_REDESIGN = 'true'; -process.env.TZ = 'UTC'; diff --git a/test/integration/config/setupAfter.js b/test/integration/config/setupAfter.js index ad9e49178094..39eba1e429a5 100644 --- a/test/integration/config/setupAfter.js +++ b/test/integration/config/setupAfter.js @@ -1,9 +1,2 @@ // This file is for Jest-specific setup only and runs before our Jest tests. -import { jestPreviewConfigure } from 'jest-preview'; -import '../config/assets/index.css'; import '../../helpers/setup-after-helper'; - -// Should be path from root of your project -jestPreviewConfigure({ - publicFolder: 'test/integration/config/assets', // No need to configure if `publicFolder` is `public` -}); diff --git a/test/integration/data/integration-init-state.json b/test/integration/data/integration-init-state.json index 7949e19cfa51..2d9e50002a18 100644 --- a/test/integration/data/integration-init-state.json +++ b/test/integration/data/integration-init-state.json @@ -783,8 +783,7 @@ "showTestNetworks": true, "smartTransactionsOptInStatus": false, "petnamesEnabled": false, - "showConfirmationAdvancedDetails": false, - "showMultiRpcModal": false + "showConfirmationAdvancedDetails": false }, "preventPollingOnNetworkRestart": true, "previousAppVersion": "11.14.4", diff --git a/test/integration/data/onboarding-completion-route.json b/test/integration/data/onboarding-completion-route.json index e47d1379b2eb..e651e9c2ce29 100644 --- a/test/integration/data/onboarding-completion-route.json +++ b/test/integration/data/onboarding-completion-route.json @@ -227,8 +227,7 @@ "hideZeroBalanceTokens": false, "petnamesEnabled": true, "redesignedConfirmationsEnabled": true, - "featureNotificationsEnabled": false, - "privacyMode": false + "featureNotificationsEnabled": false }, "preventPollingOnNetworkRestart": false, "previousAppVersion": "", diff --git a/test/integration/notifications&auth/data/notification-state.ts b/test/integration/notifications&auth/data/notification-state.ts deleted file mode 100644 index c58bf707f521..000000000000 --- a/test/integration/notifications&auth/data/notification-state.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { - INotification, - TRIGGER_TYPES, - processNotification, -} from '@metamask/notification-services-controller/notification-services'; -import { - createMockNotificationEthSent, - createMockFeatureAnnouncementRaw, -} from '@metamask/notification-services-controller/notification-services/mocks'; -import mockMetaMaskState from '../../data/integration-init-state.json'; - -const notificationsAccountAddress = - mockMetaMaskState.internalAccounts.accounts[ - mockMetaMaskState.internalAccounts - .selectedAccount as keyof typeof mockMetaMaskState.internalAccounts.accounts - ].address; - -export const ethSentNotification = processNotification( - createMockNotificationEthSent(), -) as Extract; - -if (ethSentNotification.type === TRIGGER_TYPES.ETH_SENT) { - ethSentNotification.address = notificationsAccountAddress; - ethSentNotification.data.from = notificationsAccountAddress; - ethSentNotification.isRead = true; -} - -export const featureNotification = processNotification( - createMockFeatureAnnouncementRaw(), -) as Extract; - -if (featureNotification.type === TRIGGER_TYPES.FEATURES_ANNOUNCEMENT) { - featureNotification.isRead = true; -} - -export const getMockedNotificationsState = () => { - return { - ...mockMetaMaskState, - isProfileSyncingEnabled: true, - isProfileSyncingUpdateLoading: false, - isMetamaskNotificationsFeatureSeen: true, - isNotificationServicesEnabled: true, - isFeatureAnnouncementsEnabled: true, - notifications: {}, - metamaskNotificationsReadList: [featureNotification.id], - metamaskNotificationsList: [featureNotification, ethSentNotification], - isUpdatingMetamaskNotifications: false, - isFetchingMetamaskNotifications: false, - isUpdatingMetamaskNotificationsAccount: [], - useExternalServices: true, - pendingApprovalCount: 0, - pendingApprovals: {}, - }; -}; diff --git a/test/integration/notifications&auth/notifications-activation.test.tsx b/test/integration/notifications&auth/notifications-activation.test.tsx deleted file mode 100644 index e11e58dad320..000000000000 --- a/test/integration/notifications&auth/notifications-activation.test.tsx +++ /dev/null @@ -1,196 +0,0 @@ -import { - act, - fireEvent, - waitFor, - screen, - within, -} from '@testing-library/react'; -import { integrationTestRender } from '../../lib/render-helpers'; -import * as backgroundConnection from '../../../ui/store/background-connection'; -import { createMockImplementation } from '../helpers'; -import { - MetaMetricsEventCategory, - MetaMetricsEventName, -} from '../../../shared/constants/metametrics'; -import { getMockedNotificationsState } from './data/notification-state'; - -jest.mock('../../../ui/store/background-connection', () => ({ - ...jest.requireActual('../../../ui/store/background-connection'), - submitRequestToBackground: jest.fn(), - callBackgroundMethod: jest.fn(), -})); - -const backgroundConnectionMocked = { - onNotification: jest.fn(), -}; - -const mockedBackgroundConnection = jest.mocked(backgroundConnection); - -const setupSubmitRequestToBackgroundMocks = ( - mockRequests?: Record, -) => { - mockedBackgroundConnection.submitRequestToBackground.mockImplementation( - createMockImplementation({ - ...(mockRequests ?? {}), - }), - ); -}; - -const trackNotificationsActivatedMetaMetricsEvent = async ( - actionType: string, - profileSyncEnabled: boolean, -) => { - const expectedCall = [ - 'trackMetaMetricsEvent', - [ - expect.objectContaining({ - event: MetaMetricsEventName.NotificationsActivated, - category: MetaMetricsEventCategory.NotificationsActivationFlow, - properties: { - action_type: actionType, - is_profile_syncing_enabled: profileSyncEnabled, - }, - }), - ], - ]; - - expect( - mockedBackgroundConnection.submitRequestToBackground.mock.calls, - ).toStrictEqual(expect.arrayContaining([expectedCall])); -}; -describe('Notifications Activation', () => { - beforeEach(() => { - jest.resetAllMocks(); - setupSubmitRequestToBackgroundMocks(); - }); - - afterEach(() => { - window.history.pushState({}, '', '/'); // return to homescreen - }); - - const clickElement = async (testId: string) => { - await act(async () => { - fireEvent.click(screen.getByTestId(testId)); - }); - }; - - const waitForElement = async (testId: string) => { - await waitFor(() => { - expect(screen.getByTestId(testId)).toBeInTheDocument(); - }); - }; - - it('should successfully activate notification for the first time', async () => { - const mockedState = getMockedNotificationsState(); - await act(async () => { - await integrationTestRender({ - preloadedState: { - ...mockedState, - isProfileSyncingEnabled: false, - isNotificationServicesEnabled: false, - isFeatureAnnouncementsEnabled: false, - isMetamaskNotificationsFeatureSeen: false, - }, - backgroundConnection: backgroundConnectionMocked, - }); - - await clickElement('account-options-menu-button'); - await waitForElement('notifications-menu-item'); - await clickElement('notifications-menu-item'); - - await waitFor(() => { - expect( - within(screen.getByRole('dialog')).getByText('Turn on'), - ).toBeInTheDocument(); - }); - - await act(async () => { - fireEvent.click(screen.getByText('Turn on')); - }); - - await waitFor(() => { - const createOnChainTriggersCall = - mockedBackgroundConnection.submitRequestToBackground.mock.calls?.find( - (call) => call[0] === 'createOnChainTriggers', - ); - - expect(createOnChainTriggersCall?.[0]).toBe('createOnChainTriggers'); - }); - - await trackNotificationsActivatedMetaMetricsEvent('started', false); - await trackNotificationsActivatedMetaMetricsEvent('activated', true); - }); - }); - - it('should successfully send correct metrics when notifications modal is dismissed', async () => { - const mockedState = getMockedNotificationsState(); - await act(async () => { - await integrationTestRender({ - preloadedState: { - ...mockedState, - isProfileSyncingEnabled: false, - isNotificationServicesEnabled: false, - isFeatureAnnouncementsEnabled: false, - isMetamaskNotificationsFeatureSeen: false, - }, - backgroundConnection: backgroundConnectionMocked, - }); - - await clickElement('account-options-menu-button'); - await waitForElement('notifications-menu-item'); - await clickElement('notifications-menu-item'); - - await waitFor(() => { - expect( - within(screen.getByRole('dialog')).getByText('Turn on'), - ).toBeInTheDocument(); - }); - - await act(async () => { - fireEvent.click( - within(screen.getByRole('dialog')).getByRole('button', { - name: 'Close', - }), - ); - }); - - await trackNotificationsActivatedMetaMetricsEvent('dismissed', false); - }); - }); - - it('should successfully send correct metrics when notifications modal is dismissed', async () => { - const mockedState = getMockedNotificationsState(); - await act(async () => { - await integrationTestRender({ - preloadedState: { - ...mockedState, - isProfileSyncingEnabled: false, - isNotificationServicesEnabled: false, - isFeatureAnnouncementsEnabled: false, - isMetamaskNotificationsFeatureSeen: false, - }, - backgroundConnection: backgroundConnectionMocked, - }); - - await clickElement('account-options-menu-button'); - await waitForElement('notifications-menu-item'); - await clickElement('notifications-menu-item'); - - await waitFor(() => { - expect( - within(screen.getByRole('dialog')).getByText('Turn on'), - ).toBeInTheDocument(); - }); - - await act(async () => { - fireEvent.click( - within(screen.getByRole('dialog')).getByRole('button', { - name: 'Close', - }), - ); - }); - - await trackNotificationsActivatedMetaMetricsEvent('dismissed', false); - }); - }); -}); diff --git a/test/integration/notifications&auth/notifications-list.test.tsx b/test/integration/notifications&auth/notifications-list.test.tsx deleted file mode 100644 index 4e17a53db107..000000000000 --- a/test/integration/notifications&auth/notifications-list.test.tsx +++ /dev/null @@ -1,246 +0,0 @@ -import { - act, - fireEvent, - waitFor, - within, - screen, -} from '@testing-library/react'; -import { integrationTestRender } from '../../lib/render-helpers'; -import * as backgroundConnection from '../../../ui/store/background-connection'; -import { createMockImplementation } from '../helpers'; -import { - MetaMetricsEventCategory, - MetaMetricsEventName, -} from '../../../shared/constants/metametrics'; -import { - ethSentNotification, - featureNotification, - getMockedNotificationsState, -} from './data/notification-state'; - -jest.mock('../../../ui/store/background-connection', () => ({ - ...jest.requireActual('../../../ui/store/background-connection'), - submitRequestToBackground: jest.fn(), - callBackgroundMethod: jest.fn(), -})); - -const backgroundConnectionMocked = { - onNotification: jest.fn(), -}; - -const mockedBackgroundConnection = jest.mocked(backgroundConnection); - -const setupSubmitRequestToBackgroundMocks = ( - mockRequests?: Record, -) => { - mockedBackgroundConnection.submitRequestToBackground.mockImplementation( - createMockImplementation({ - ...(mockRequests ?? {}), - }), - ); -}; - -const getStateWithTwoUnreadNotifications = () => { - const state = getMockedNotificationsState(); - return { - ...state, - metamaskNotificationsList: [ - { - ...state.metamaskNotificationsList[0], - isRead: false, - }, - { - ...state.metamaskNotificationsList[1], - isRead: false, - }, - ], - }; -}; - -describe('Notifications List', () => { - beforeEach(() => { - jest.resetAllMocks(); - setupSubmitRequestToBackgroundMocks(); - }); - - afterEach(() => { - window.history.pushState({}, '', '/'); // return to homescreen - }); - - it('should show the correct number of unread notifications on the badge', async () => { - const mockedState = getStateWithTwoUnreadNotifications(); - - await act(async () => { - await integrationTestRender({ - preloadedState: mockedState, - backgroundConnection: backgroundConnectionMocked, - }); - }); - - await waitFor(() => { - const unreadCount = screen.getByTestId( - 'notifications-tag-counter__unread-dot', - ); - expect(unreadCount).toBeInTheDocument(); - expect(unreadCount).toHaveTextContent('2'); - }); - }); - - it('should render notifications list and show correct details', async () => { - const mockedState = getStateWithTwoUnreadNotifications(); - - await act(async () => { - await integrationTestRender({ - preloadedState: mockedState, - backgroundConnection: backgroundConnectionMocked, - }); - }); - - fireEvent.click(screen.getByTestId('account-options-menu-button')); - - await waitFor(() => { - expect(screen.getByTestId('notifications-menu-item')).toBeInTheDocument(); - fireEvent.click(screen.getByTestId('notifications-menu-item')); - }); - - await waitFor(() => { - const notificationsList = screen.getByTestId('notifications-list'); - expect(notificationsList).toBeInTheDocument(); - expect(notificationsList.childElementCount).toBe(3); - - // Feature notification details - expect( - within(notificationsList).getByText(featureNotification.data.title), - ).toBeInTheDocument(); - expect( - within(notificationsList).getByText( - featureNotification.data.shortDescription, - ), - ).toBeInTheDocument(); - - // Eth sent notification details - const sentToElement = within(notificationsList).getByText('Sent to'); - expect(sentToElement).toBeInTheDocument(); - - const addressElement = sentToElement.nextElementSibling; - expect(addressElement).toHaveTextContent('0x881D4...D300D'); - - // Read all button - expect( - within(notificationsList).getByTestId( - 'notifications-list-read-all-button', - ), - ).toBeInTheDocument(); - - const unreadDot = screen.getAllByTestId('unread-dot'); - expect(unreadDot).toHaveLength(2); - }); - - await waitFor(() => { - const notificationsInteractionsEvent = - mockedBackgroundConnection.submitRequestToBackground.mock.calls?.find( - (call) => - call[0] === 'trackMetaMetricsEvent' && - call[1]?.[0].category === - MetaMetricsEventCategory.NotificationInteraction, - ); - - expect(notificationsInteractionsEvent?.[0]).toBe('trackMetaMetricsEvent'); - const [metricsEvent] = notificationsInteractionsEvent?.[1] as unknown as [ - { - event: string; - category: string; - properties: Record; - }, - ]; - - expect(metricsEvent?.event).toBe( - MetaMetricsEventName.NotificationsMenuOpened, - ); - - expect(metricsEvent?.category).toBe( - MetaMetricsEventCategory.NotificationInteraction, - ); - - expect(metricsEvent.properties).toMatchObject({ - unread_count: 2, - read_count: 0, - }); - }); - }); - - it('should not see mark all as read button if there are no unread notifications', async () => { - const mockedState = getMockedNotificationsState(); // all notifications are read by default - - await act(async () => { - await integrationTestRender({ - preloadedState: mockedState, - backgroundConnection: backgroundConnectionMocked, - }); - - fireEvent.click(screen.getByTestId('account-options-menu-button')); - - await waitFor(() => { - expect( - screen.getByTestId('notifications-menu-item'), - ).toBeInTheDocument(); - fireEvent.click(screen.getByTestId('notifications-menu-item')); - }); - - await waitFor(() => { - const notificationsList = screen.getByTestId('notifications-list'); - expect(notificationsList).toBeInTheDocument(); - - expect(notificationsList.childElementCount).toBe(2); - - expect( - screen.queryByTestId('notifications-list-read-all-button'), - ).not.toBeInTheDocument(); - - expect(screen.queryAllByTestId('unread-dot')).toHaveLength(0); - }); - }); - }); - - it('should send request for marking notifications as read to the background with the correct params', async () => { - const mockedState = getStateWithTwoUnreadNotifications(); - await act(async () => { - await integrationTestRender({ - preloadedState: mockedState, - backgroundConnection: backgroundConnectionMocked, - }); - }); - - fireEvent.click(screen.getByTestId('account-options-menu-button')); - - await waitFor(() => { - expect(screen.getByTestId('notifications-menu-item')).toBeInTheDocument(); - fireEvent.click(screen.getByTestId('notifications-menu-item')); - }); - - fireEvent.click(screen.getByTestId('notifications-list-read-all-button')); - - await waitFor(() => { - const markAllAsReadEvent = - mockedBackgroundConnection.submitRequestToBackground.mock.calls?.find( - (call) => call[0] === 'markMetamaskNotificationsAsRead', - ); - - expect(markAllAsReadEvent?.[0]).toBe('markMetamaskNotificationsAsRead'); - expect(markAllAsReadEvent?.[1]).toStrictEqual([ - [ - { - id: featureNotification.id, - type: featureNotification.type, - isRead: false, - }, - { - id: ethSentNotification.id, - type: ethSentNotification.type, - isRead: false, - }, - ], - ]); - }); - }); -}); diff --git a/test/integration/notifications&auth/notifications-toggle.test.tsx b/test/integration/notifications&auth/notifications-toggle.test.tsx deleted file mode 100644 index 8133e4c4bc3d..000000000000 --- a/test/integration/notifications&auth/notifications-toggle.test.tsx +++ /dev/null @@ -1,224 +0,0 @@ -import { - act, - fireEvent, - waitFor, - within, - screen, -} from '@testing-library/react'; -import { integrationTestRender } from '../../lib/render-helpers'; -import * as backgroundConnection from '../../../ui/store/background-connection'; -import { createMockImplementation } from '../helpers'; -import { - MetaMetricsEventCategory, - MetaMetricsEventName, -} from '../../../shared/constants/metametrics'; -import { getMockedNotificationsState } from './data/notification-state'; - -jest.mock('../../../ui/store/background-connection', () => ({ - ...jest.requireActual('../../../ui/store/background-connection'), - submitRequestToBackground: jest.fn(), - callBackgroundMethod: jest.fn(), -})); - -const backgroundConnectionMocked = { - onNotification: jest.fn(), -}; - -const mockedBackgroundConnection = jest.mocked(backgroundConnection); - -const setupSubmitRequestToBackgroundMocks = ( - mockRequests?: Record, -) => { - mockedBackgroundConnection.submitRequestToBackground.mockImplementation( - createMockImplementation({ - ...(mockRequests ?? {}), - }), - ); -}; - -describe('Notifications Toggle', () => { - beforeEach(() => { - jest.resetAllMocks(); - setupSubmitRequestToBackgroundMocks(); - }); - - afterEach(() => { - window.history.pushState({}, '', '/'); // return to homescreen - }); - - const clickElement = async (testId: string) => { - await act(async () => { - fireEvent.click(screen.getByTestId(testId)); - }); - }; - - const waitForElement = async (testId: string) => { - await waitFor(() => { - expect(screen.getByTestId(testId)).toBeInTheDocument(); - }); - }; - - it('disabling notifications from settings', async () => { - const mockedState = getMockedNotificationsState(); - await act(async () => { - await integrationTestRender({ - preloadedState: { ...mockedState }, - backgroundConnection: backgroundConnectionMocked, - }); - - await clickElement('account-options-menu-button'); - await waitForElement('notifications-menu-item'); - await clickElement('notifications-menu-item'); - await waitForElement('notifications-settings-button'); - await clickElement('notifications-settings-button'); - await waitForElement('notifications-settings-allow-notifications'); - - const toggleSection = screen.getByTestId( - 'notifications-settings-allow-notifications', - ); - - await act(async () => { - fireEvent.click(within(toggleSection).getByRole('checkbox')); - }); - - await waitFor(() => { - const disableNotificationsCall = - mockedBackgroundConnection.submitRequestToBackground.mock.calls?.find( - (call) => call[0] === 'disableMetamaskNotifications', - ); - - const fetchAndUpdateMetamaskNotificationsCall = - mockedBackgroundConnection.submitRequestToBackground.mock.calls?.find( - (call) => call[0] === 'fetchAndUpdateMetamaskNotifications', - ); - - expect(disableNotificationsCall?.[0]).toBe( - 'disableMetamaskNotifications', - ); - - expect(fetchAndUpdateMetamaskNotificationsCall?.[0]).toBe( - 'fetchAndUpdateMetamaskNotifications', - ); - }); - - await waitFor(() => { - const metametrics = - mockedBackgroundConnection.submitRequestToBackground.mock.calls?.find( - (call) => - call[0] === 'trackMetaMetricsEvent' && - call[1]?.[0].category === - MetaMetricsEventCategory.NotificationSettings, - ); - - expect(metametrics?.[0]).toBe('trackMetaMetricsEvent'); - - const [metricsEvent] = metametrics?.[1] as unknown as [ - { - event: string; - category: string; - properties: Record; - }, - ]; - - expect(metricsEvent?.event).toBe( - MetaMetricsEventName.NotificationsSettingsUpdated, - ); - - expect(metricsEvent?.category).toBe( - MetaMetricsEventCategory.NotificationSettings, - ); - - expect(metricsEvent?.properties).toMatchObject({ - settings_type: 'notifications', - was_profile_syncing_on: true, - old_value: true, - new_value: false, - }); - }); - }); - }); - - it('enabling product announcments from settings', async () => { - const mockedState = getMockedNotificationsState(); - await act(async () => { - await integrationTestRender({ - preloadedState: { - ...mockedState, - isProfileSyncingEnabled: false, - isNotificationServicesEnabled: true, - isFeatureAnnouncementsEnabled: false, - isMetamaskNotificationsFeatureSeen: true, - }, - backgroundConnection: backgroundConnectionMocked, - }); - - await clickElement('account-options-menu-button'); - await waitForElement('notifications-menu-item'); - await clickElement('notifications-menu-item'); - await waitForElement('notifications-settings-button'); - await clickElement('notifications-settings-button'); - await waitForElement('notifications-settings-allow-notifications'); - - const allToggles = screen.getAllByTestId('test-toggle'); - - await act(async () => { - fireEvent.click(allToggles[1]); - }); - - await waitFor(() => { - const enableFeatureNotifications = - mockedBackgroundConnection.submitRequestToBackground.mock.calls?.find( - (call) => call[0] === 'setFeatureAnnouncementsEnabled', - ); - - const fetchAndUpdateMetamaskNotificationsCall = - mockedBackgroundConnection.submitRequestToBackground.mock.calls?.find( - (call) => call[0] === 'fetchAndUpdateMetamaskNotifications', - ); - - expect(enableFeatureNotifications?.[0]).toBe( - 'setFeatureAnnouncementsEnabled', - ); - expect(enableFeatureNotifications?.[1]).toEqual([true]); - - expect(fetchAndUpdateMetamaskNotificationsCall?.[0]).toBe( - 'fetchAndUpdateMetamaskNotifications', - ); - }); - - await waitFor(() => { - const metametrics = - mockedBackgroundConnection.submitRequestToBackground.mock.calls?.find( - (call) => - call[0] === 'trackMetaMetricsEvent' && - call[1]?.[0].category === - MetaMetricsEventCategory.NotificationSettings, - ); - - expect(metametrics?.[0]).toBe('trackMetaMetricsEvent'); - - const [metricsEvent] = metametrics?.[1] as unknown as [ - { - event: string; - category: string; - properties: Record; - }, - ]; - - expect(metricsEvent?.event).toBe( - MetaMetricsEventName.NotificationsSettingsUpdated, - ); - - expect(metricsEvent?.category).toBe( - MetaMetricsEventCategory.NotificationSettings, - ); - - expect(metricsEvent?.properties).toMatchObject({ - settings_type: 'product_announcements', - old_value: false, - new_value: true, - }); - }); - }); - }); -}); diff --git a/test/lib/render-helpers.js b/test/lib/render-helpers.js index 574415f2f3c6..fa54ac1fb4ec 100644 --- a/test/lib/render-helpers.js +++ b/test/lib/render-helpers.js @@ -99,27 +99,6 @@ export function renderHookWithProvider(hook, state, pathname = '/', Container) { }; } -/** - * Renders a hook with a provider and optional container. - * - * @template {(...args: any) => any} Hook - * @template {Parameters} HookParams - * @template {ReturnType} HookReturn - * @template {import('@testing-library/react-hooks').RenderHookResult} RenderHookResult - * @template {import('history').History} History - * @param {Hook} hook - The hook to be rendered. - * @param [state] - The initial state for the store. - * @param [pathname] - The initial pathname for the history. - * @param [Container] - An optional container component. - * @returns {RenderHookResult & { history: History }} The result of the rendered hook and the history object. - */ -export const renderHookWithProviderTyped = ( - hook, - state, - pathname = '/', - Container, -) => renderHookWithProvider(hook, state, pathname, Container); - export function renderWithLocalization(component) { const Wrapper = ({ children }) => ( diff --git a/ui/__mocks__/useNftCollectionsMetadata.js b/ui/__mocks__/useNftCollectionsMetadata.js deleted file mode 100644 index fd99ff219364..000000000000 --- a/ui/__mocks__/useNftCollectionsMetadata.js +++ /dev/null @@ -1,14 +0,0 @@ -module.exports = { - useNftCollectionsMetadata: () => { - return { - '0x1': { - '0xc0ffee254729296a45a3885639ac7e10f9d54979': { - name: 'Everything I Own', - image: - 'https://img.reservoir.tools/images/v2/mainnet/z9JRSpLYGu7%2BCZoKWtAuAN%2F%2FMfWcOGcwki5%2FxXYtCb4OfGsOPvxN1LZHZ5%2BcuQGwJciTvgr58ThRjooWLMWehc1nSTXtbfFJ1TNtL%2FeIjglkPKsEG%2Fbem0E%2B3yo7tAUqlZ1ou0SMzGOfq%2FG1BHwIpgHQ524PRAlaynVkDcp8y58kALOPTQSDN1tgaqkZD%2FZiNBEaYq6Bp9XH8Vm8tMXsaQ%3D%3D?width=250', - isSpam: false, - }, - }, - }; - }, -}; diff --git a/ui/__mocks__/webextension-polyfill.js b/ui/__mocks__/webextension-polyfill.js index dbf15f9ea145..693368f15e0c 100644 --- a/ui/__mocks__/webextension-polyfill.js +++ b/ui/__mocks__/webextension-polyfill.js @@ -1,15 +1,3 @@ -const getManifest = () => ({ manifest_version: 3 }); - -// Polyfill chrome.runtime for environments that do not support it -// E.g. Storybook -global.chrome = { - ...global?.chrome, - runtime: { - ...global?.chrome?.runtime, - getManifest, - }, -}; - module.exports = { - runtime: { getManifest }, + runtime: { getManifest: () => ({ manifest_version: 3 }) }, }; diff --git a/ui/components/app/app-components.scss b/ui/components/app/app-components.scss index 9eefd48028ac..0995f4b52a4a 100644 --- a/ui/components/app/app-components.scss +++ b/ui/components/app/app-components.scss @@ -25,7 +25,6 @@ @import 'snaps/snap-ui-input/index'; @import 'snaps/snap-ui-file-input/index'; @import 'snaps/snap-ui-selector/index'; -@import 'snaps/snap-ui-link/index'; @import 'snaps/snap-delineator/index'; @import 'snaps/snap-home-menu/index'; @import 'snaps/snap-list-item/index'; diff --git a/ui/components/app/assets/asset-list/asset-list-control-bar/asset-list-control-bar.tsx b/ui/components/app/assets/asset-list/asset-list-control-bar/asset-list-control-bar.tsx index de771976e677..696c3ca7c89f 100644 --- a/ui/components/app/assets/asset-list/asset-list-control-bar/asset-list-control-bar.tsx +++ b/ui/components/app/assets/asset-list/asset-list-control-bar/asset-list-control-bar.tsx @@ -1,6 +1,4 @@ import React, { useRef, useState } from 'react'; -import { useSelector } from 'react-redux'; -import { getCurrentNetwork, getPreferences } from '../../../../../selectors'; import { Box, ButtonBase, @@ -27,7 +25,6 @@ import { ENVIRONMENT_TYPE_NOTIFICATION, ENVIRONMENT_TYPE_POPUP, } from '../../../../../../shared/constants/app'; -import NetworkFilter from '../network-filter'; type AssetListControlBarProps = { showTokensLinks?: boolean; @@ -35,116 +32,55 @@ type AssetListControlBarProps = { const AssetListControlBar = ({ showTokensLinks }: AssetListControlBarProps) => { const t = useI18nContext(); - const popoverRef = useRef(null); - const currentNetwork = useSelector(getCurrentNetwork); - const { tokenNetworkFilter } = useSelector(getPreferences); - const [isTokenSortPopoverOpen, setIsTokenSortPopoverOpen] = useState(false); - const [isNetworkFilterPopoverOpen, setIsNetworkFilterPopoverOpen] = - useState(false); - - const allNetworksFilterShown = Object.keys(tokenNetworkFilter ?? {}).length; + const controlBarRef = useRef(null); // Create a ref + const [isPopoverOpen, setIsPopoverOpen] = useState(false); const windowType = getEnvironmentType(); const isFullScreen = windowType !== ENVIRONMENT_TYPE_NOTIFICATION && windowType !== ENVIRONMENT_TYPE_POPUP; - const toggleTokenSortPopover = () => { - setIsNetworkFilterPopoverOpen(false); - setIsTokenSortPopoverOpen(!isTokenSortPopoverOpen); - }; - - const toggleNetworkFilterPopover = () => { - setIsTokenSortPopoverOpen(false); - setIsNetworkFilterPopoverOpen(!isNetworkFilterPopoverOpen); + const handleOpenPopover = () => { + setIsPopoverOpen(!isPopoverOpen); }; const closePopover = () => { - setIsTokenSortPopoverOpen(false); - setIsNetworkFilterPopoverOpen(false); + setIsPopoverOpen(false); }; return ( - - {process.env.FILTER_TOKENS_TOGGLE && ( - - {allNetworksFilterShown - ? currentNetwork?.nickname ?? t('currentNetwork') - : t('allNetworks')} - - )} - - - {t('sortBy')} - - - - - - - - + {t('sortBy')} + + { '0xc42edfcc21ed14dda456aa0756c153f7985d8813', '0x0', ); - expect(queryByText('Tips for using a wallet')).toBeInTheDocument(); + expect(queryByText('Fund your wallet')).toBeInTheDocument(); }); it('does not show the ramp card when the account has a balance', () => { const { queryByText } = render(); - expect(queryByText('Tips for using a wallet')).not.toBeInTheDocument(); + expect(queryByText('Fund your wallet')).not.toBeInTheDocument(); }); }); diff --git a/ui/components/app/assets/asset-list/asset-list.tsx b/ui/components/app/assets/asset-list/asset-list.tsx index 4cbe529e3df2..5cfeb6803875 100644 --- a/ui/components/app/assets/asset-list/asset-list.tsx +++ b/ui/components/app/assets/asset-list/asset-list.tsx @@ -164,7 +164,7 @@ const AssetList = ({ onClickAsset, showTokensLinks }: AssetListProps) => { setShowFundingMethodModal(false)} - title={t('fundingMethod')} + title={t('selectFundingMethod')} onClickReceive={onClickReceive} /> )} diff --git a/ui/components/app/assets/asset-list/native-token/native-token.tsx b/ui/components/app/assets/asset-list/native-token/native-token.tsx index e63a2902a552..cf0191b3de66 100644 --- a/ui/components/app/assets/asset-list/native-token/native-token.tsx +++ b/ui/components/app/assets/asset-list/native-token/native-token.tsx @@ -8,11 +8,11 @@ import { getMultichainIsMainnet, getMultichainSelectedAccountCachedBalance, } from '../../../../../selectors/multichain'; -import { getPreferences } from '../../../../../selectors'; import { TokenListItem } from '../../../../multichain'; import { useIsOriginalNativeTokenSymbol } from '../../../../../hooks/useIsOriginalNativeTokenSymbol'; import { AssetListProps } from '../asset-list'; import { useNativeTokenBalance } from './use-native-token-balance'; +// import { getPreferences } from '../../../../../selectors'; const NativeToken = ({ onClickAsset }: AssetListProps) => { const nativeCurrency = useSelector(getMultichainNativeCurrency); @@ -20,7 +20,6 @@ const NativeToken = ({ onClickAsset }: AssetListProps) => { const { chainId, ticker, type, rpcUrl } = useSelector( getMultichainCurrentNetwork, ); - const { privacyMode } = useSelector(getPreferences); const isOriginalNativeSymbol = useIsOriginalNativeTokenSymbol( chainId, ticker, @@ -53,7 +52,6 @@ const NativeToken = ({ onClickAsset }: AssetListProps) => { isNativeCurrency isStakeable={isStakeable} showPercentage - privacyMode={privacyMode} /> ); }; diff --git a/ui/components/app/assets/asset-list/network-filter/index.scss b/ui/components/app/assets/asset-list/network-filter/index.scss deleted file mode 100644 index 76e61c1025ae..000000000000 --- a/ui/components/app/assets/asset-list/network-filter/index.scss +++ /dev/null @@ -1,27 +0,0 @@ -.selectable-list-item-wrapper { - position: relative; -} - -.selectable-list-item { - cursor: pointer; - padding: 16px; - - &--selected { - background: var(--color-primary-muted); - } - - &:not(.selectable-list-item--selected) { - &:hover, - &:focus-within { - background: var(--color-background-default-hover); - } - } - - &__selected-indicator { - width: 4px; - height: calc(100% - 8px); - position: absolute; - top: 4px; - left: 4px; - } -} diff --git a/ui/components/app/assets/asset-list/network-filter/index.ts b/ui/components/app/assets/asset-list/network-filter/index.ts deleted file mode 100644 index 61bca0ca23e0..000000000000 --- a/ui/components/app/assets/asset-list/network-filter/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default } from './network-filter'; diff --git a/ui/components/app/assets/asset-list/network-filter/network-filter.tsx b/ui/components/app/assets/asset-list/network-filter/network-filter.tsx deleted file mode 100644 index cc2d0f38210e..000000000000 --- a/ui/components/app/assets/asset-list/network-filter/network-filter.tsx +++ /dev/null @@ -1,144 +0,0 @@ -import React from 'react'; -import { useDispatch, useSelector } from 'react-redux'; -import { setTokenNetworkFilter } from '../../../../../store/actions'; -import { - getCurrentChainId, - getCurrentNetwork, - getIsTestnet, - getPreferences, - getSelectedInternalAccount, - getShouldHideZeroBalanceTokens, - getNetworkConfigurationsByChainId, -} from '../../../../../selectors'; -import { useI18nContext } from '../../../../../hooks/useI18nContext'; -import { SelectableListItem } from '../sort-control/sort-control'; -import { useAccountTotalFiatBalance } from '../../../../../hooks/useAccountTotalFiatBalance'; -import { Text } from '../../../../component-library/text/text'; -import { - Display, - JustifyContent, - TextColor, - TextVariant, -} from '../../../../../helpers/constants/design-system'; -import { Box } from '../../../../component-library/box/box'; -import { AvatarNetwork } from '../../../../component-library'; -import UserPreferencedCurrencyDisplay from '../../../user-preferenced-currency-display'; -import { CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP } from '../../../../../../shared/constants/network'; - -type SortControlProps = { - handleClose: () => void; -}; - -const NetworkFilter = ({ handleClose }: SortControlProps) => { - const t = useI18nContext(); - const dispatch = useDispatch(); - const chainId = useSelector(getCurrentChainId); - const selectedAccount = useSelector(getSelectedInternalAccount); - const currentNetwork = useSelector(getCurrentNetwork); - const allNetworks = useSelector(getNetworkConfigurationsByChainId); - const isTestnet = useSelector(getIsTestnet); - const { tokenNetworkFilter, showNativeTokenAsMainBalance } = - useSelector(getPreferences); - const shouldHideZeroBalanceTokens = useSelector( - getShouldHideZeroBalanceTokens, - ); - - const { totalFiatBalance: selectedAccountBalance } = - useAccountTotalFiatBalance(selectedAccount, shouldHideZeroBalanceTokens); - - // TODO: fetch balances across networks - // const multiNetworkAccountBalance = useMultichainAccountBalance() - - const handleFilter = (chainFilters: Record) => { - dispatch(setTokenNetworkFilter(chainFilters)); - - // TODO Add metrics - handleClose(); - }; - - return ( - <> - handleFilter({})} - > - - - - {t('allNetworks')} - - - {/* TODO: Should query cross chain account balance */} - $1,000.00 - - - - {Object.values(allNetworks) - .slice(0, 5) // only show a max of 5 icons overlapping - .map((network, index) => { - const networkImageUrl = - CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP[ - network.chainId as keyof typeof CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP - ]; - return ( - - ); - })} - - - - handleFilter({ [chainId]: true })} - > - - - - {t('currentNetwork')} - - - - - - - - ); -}; - -export default NetworkFilter; diff --git a/ui/components/app/assets/asset-list/sort-control/sort-control.tsx b/ui/components/app/assets/asset-list/sort-control/sort-control.tsx index 8e216b5ed6c2..c45a5488f1a6 100644 --- a/ui/components/app/assets/asset-list/sort-control/sort-control.tsx +++ b/ui/components/app/assets/asset-list/sort-control/sort-control.tsx @@ -1,11 +1,13 @@ import React, { ReactNode, useContext } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import classnames from 'classnames'; -import { Box } from '../../../../component-library'; +import { Box, Text } from '../../../../component-library'; import { SortOrder, SortingCallbacksT } from '../../util/sort'; import { BackgroundColor, BorderRadius, + TextColor, + TextVariant, } from '../../../../../helpers/constants/design-system'; import { setTokenSortConfig } from '../../../../../store/actions'; import { MetaMetricsContext } from '../../../../../contexts/metametrics'; @@ -43,7 +45,9 @@ export const SelectableListItem = ({ })} onClick={onClick} > - {children} + + {children} + {isSelected && ( + { + ///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask) + showRampsCard ? ( + + ) : null + ///: END:ONLY_INCLUDE_IF + } {isMainnet && !useNftDetection ? ( diff --git a/ui/components/app/assets/nfts/nfts-tab/nfts-tab.test.js b/ui/components/app/assets/nfts/nfts-tab/nfts-tab.test.js index 52acdbcd84f4..85f92a5344db 100644 --- a/ui/components/app/assets/nfts/nfts-tab/nfts-tab.test.js +++ b/ui/components/app/assets/nfts/nfts-tab/nfts-tab.test.js @@ -248,6 +248,10 @@ describe('NFT Items', () => { jest.clearAllMocks(); }); + function delay(ms) { + return new Promise((resolve) => setTimeout(resolve, ms)); + } + describe('NFTs Detection Notice', () => { it('should render the NFTs Detection Notice when currently selected network is Mainnet and nft detection is set to false and user has nfts', () => { render({ @@ -370,4 +374,24 @@ describe('NFT Items', () => { expect(historyPushMock).toHaveBeenCalledWith(SECURITY_ROUTE); }); }); + + describe('NFT Tab Ramps Card', () => { + it('shows the ramp card when user balance is zero', async () => { + const { queryByText } = render({ + selectedAddress: ACCOUNT_1, + balance: '0x0', + }); + // wait for spinner to be removed + await delay(3000); + expect(queryByText('Get ETH to buy NFTs')).toBeInTheDocument(); + }); + + it('does not show the ramp card when the account has a balance', () => { + const { queryByText } = render({ + selectedAddress: ACCOUNT_1, + balance: ETH_BALANCE, + }); + expect(queryByText('Get ETH to buy NFTs')).not.toBeInTheDocument(); + }); + }); }); diff --git a/ui/components/app/assets/token-cell/token-cell.test.tsx b/ui/components/app/assets/token-cell/token-cell.test.tsx index 5cb4b30aea49..882c80964d5b 100644 --- a/ui/components/app/assets/token-cell/token-cell.test.tsx +++ b/ui/components/app/assets/token-cell/token-cell.test.tsx @@ -5,7 +5,7 @@ import { fireEvent } from '@testing-library/react'; import { useSelector } from 'react-redux'; import { renderWithProvider } from '../../../../../test/lib/render-helpers'; import { useTokenFiatAmount } from '../../../../hooks/useTokenFiatAmount'; -import { getTokenList, getPreferences } from '../../../../selectors'; +import { getTokenList } from '../../../../selectors'; import { getMultichainCurrentChainId, getMultichainIsEvm, @@ -98,9 +98,6 @@ describe('Token Cell', () => { }; const useSelectorMock = useSelector; (useSelectorMock as jest.Mock).mockImplementation((selector) => { - if (selector === getPreferences) { - return { privacyMode: false }; - } if (selector === getTokenList) { return MOCK_GET_TOKEN_LIST; } diff --git a/ui/components/app/assets/token-cell/token-cell.tsx b/ui/components/app/assets/token-cell/token-cell.tsx index 31bb388aa65b..5f5b43d6c098 100644 --- a/ui/components/app/assets/token-cell/token-cell.tsx +++ b/ui/components/app/assets/token-cell/token-cell.tsx @@ -12,7 +12,6 @@ type TokenCellProps = { symbol: string; string?: string; image: string; - privacyMode?: boolean; onClick?: (arg: string) => void; }; @@ -21,7 +20,6 @@ export default function TokenCell({ image, symbol, string, - privacyMode = false, onClick, }: TokenCellProps) { const tokenList = useSelector(getTokenList); @@ -53,7 +51,6 @@ export default function TokenCell({ isOriginalTokenSymbol={isOriginalTokenSymbol} address={address} showPercentage - privacyMode={privacyMode} /> ); } diff --git a/ui/components/app/assets/token-list/token-list.tsx b/ui/components/app/assets/token-list/token-list.tsx index f0b17d686026..8a107b154fb9 100644 --- a/ui/components/app/assets/token-list/token-list.tsx +++ b/ui/components/app/assets/token-list/token-list.tsx @@ -30,8 +30,7 @@ export default function TokenList({ nativeToken, }: TokenListProps) { const t = useI18nContext(); - const { tokenSortConfig, tokenNetworkFilter, privacyMode } = - useSelector(getPreferences); + const { tokenSortConfig } = useSelector(getPreferences); const selectedAccount = useSelector(getSelectedAccount); const conversionRate = useSelector(getConversionRate); const nativeTokenWithBalance = useNativeTokenBalance(); @@ -53,7 +52,6 @@ export default function TokenList({ }; const sortedTokens = useMemo(() => { - // TODO filter assets by networkTokenFilter before sorting return sortAssets( [nativeTokenWithBalance, ...tokensWithBalances], tokenSortConfig, @@ -61,7 +59,6 @@ export default function TokenList({ }, [ tokensWithBalances, tokenSortConfig, - tokenNetworkFilter, conversionRate, contractExchangeRates, ]); @@ -89,7 +86,6 @@ export default function TokenList({ ); diff --git a/ui/components/app/assets/util/filter.test.ts b/ui/components/app/assets/util/filter.test.ts deleted file mode 100644 index fd5a612d590b..000000000000 --- a/ui/components/app/assets/util/filter.test.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { filterAssets, FilterCriteria } from './filter'; - -describe('filterAssets function - balance and chainId filtering', () => { - type MockToken = { - name: string; - symbol: string; - chainId: string; // Updated to string (e.g., '0x01', '0x89') - balance: number; - }; - - const mockTokens: MockToken[] = [ - { name: 'Token1', symbol: 'T1', chainId: '0x01', balance: 100 }, - { name: 'Token2', symbol: 'T2', chainId: '0x02', balance: 50 }, - { name: 'Token3', symbol: 'T3', chainId: '0x01', balance: 200 }, - { name: 'Token4', symbol: 'T4', chainId: '0x89', balance: 150 }, - ]; - - test('filters by inclusive chainId', () => { - const criteria: FilterCriteria[] = [ - { - key: 'chainId', - opts: { '0x01': true, '0x89': true }, // ChainId must be '0x01' or '0x89' - filterCallback: 'inclusive', - }, - ]; - - const filtered = filterAssets(mockTokens, criteria); - - expect(filtered.length).toBe(3); // Should include 3 tokens with chainId '0x01' and '0x89' - expect(filtered.map((token) => token.chainId)).toEqual([ - '0x01', - '0x01', - '0x89', - ]); - }); - - test('filters tokens with balance between 100 and 150 inclusive', () => { - const criteria: FilterCriteria[] = [ - { - key: 'balance', - opts: { min: 100, max: 150 }, // Balance between 100 and 150 - filterCallback: 'range', - }, - ]; - - const filtered = filterAssets(mockTokens, criteria); - - expect(filtered.length).toBe(2); // Token1 and Token4 - expect(filtered.map((token) => token.balance)).toEqual([100, 150]); - }); - - test('filters by inclusive chainId and balance range', () => { - const criteria: FilterCriteria[] = [ - { - key: 'chainId', - opts: { '0x01': true, '0x89': true }, // ChainId must be '0x01' or '0x89' - filterCallback: 'inclusive', - }, - { - key: 'balance', - opts: { min: 100, max: 150 }, // Balance between 100 and 150 - filterCallback: 'range', - }, - ]; - - const filtered = filterAssets(mockTokens, criteria); - - expect(filtered.length).toBe(2); // Token1 and Token4 meet both criteria - }); - - test('returns no tokens if no chainId matches', () => { - const criteria: FilterCriteria[] = [ - { - key: 'chainId', - opts: { '0x04': true }, // No token with chainId '0x04' - filterCallback: 'inclusive', - }, - ]; - - const filtered = filterAssets(mockTokens, criteria); - - expect(filtered.length).toBe(0); // No matching tokens - }); - - test('returns no tokens if balance is not within range', () => { - const criteria: FilterCriteria[] = [ - { - key: 'balance', - opts: { min: 300, max: 400 }, // No token with balance between 300 and 400 - filterCallback: 'range', - }, - ]; - - const filtered = filterAssets(mockTokens, criteria); - - expect(filtered.length).toBe(0); // No matching tokens - }); -}); diff --git a/ui/components/app/assets/util/filter.ts b/ui/components/app/assets/util/filter.ts deleted file mode 100644 index 20ca7cebcc58..000000000000 --- a/ui/components/app/assets/util/filter.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { get } from 'lodash'; - -export type FilterCriteria = { - key: string; - opts: Record; // Use opts for range, inclusion, etc. - filterCallback: FilterCallbackKeys; // Specify the type of filter: 'range', 'inclusive', etc. -}; - -export type FilterType = string | number | boolean | Date; -type FilterCallbackKeys = keyof FilterCallbacksT; - -export type FilterCallbacksT = { - inclusive: (value: string, opts: Record) => boolean; - range: (value: number, opts: Record) => boolean; -}; - -const filterCallbacks: FilterCallbacksT = { - inclusive: (value: string, opts: Record) => { - if (Object.entries(opts).length === 0) { - return false; - } - return opts[value]; - }, - range: (value: number, opts: Record) => - value >= opts.min && value <= opts.max, -}; - -function getNestedValue(obj: T, keyPath: string): FilterType { - return get(obj, keyPath); -} - -export function filterAssets(assets: T[], criteria: FilterCriteria[]): T[] { - if (criteria.length === 0) { - return assets; - } - - return assets.filter((asset) => - criteria.every(({ key, opts, filterCallback }) => { - const nestedValue = getNestedValue(asset, key); - - // If there's no callback or options, exit early and don't filter based on this criterion. - if (!filterCallback || !opts) { - return true; - } - - switch (filterCallback) { - case 'inclusive': - return filterCallbacks.inclusive( - nestedValue as string, - opts as Record, - ); - case 'range': - return filterCallbacks.range( - nestedValue as number, - opts as { min: number; max: number }, - ); - default: - return true; - } - }), - ); -} diff --git a/ui/components/app/confirm/info/row/__snapshots__/copy-icon.test.tsx.snap b/ui/components/app/confirm/info/row/__snapshots__/copy-icon.test.tsx.snap index 947d2aae64a8..192eb016c478 100644 --- a/ui/components/app/confirm/info/row/__snapshots__/copy-icon.test.tsx.snap +++ b/ui/components/app/confirm/info/row/__snapshots__/copy-icon.test.tsx.snap @@ -2,15 +2,9 @@ exports[`CopyIcon should match snapshot 1`] = `
- +
`; diff --git a/ui/components/app/confirm/info/row/__snapshots__/currency.test.tsx.snap b/ui/components/app/confirm/info/row/__snapshots__/currency.test.tsx.snap index 9f7014dea03e..e98ec1921081 100644 --- a/ui/components/app/confirm/info/row/__snapshots__/currency.test.tsx.snap +++ b/ui/components/app/confirm/info/row/__snapshots__/currency.test.tsx.snap @@ -12,7 +12,6 @@ exports[`ConfirmInfoRowCurrency should display in currency passed 1`] = ` > $82.65 @@ -38,7 +37,6 @@ exports[`ConfirmInfoRowCurrency should display value in user preferred currency > 0.14861879 diff --git a/ui/components/app/confirm/info/row/__snapshots__/row.test.tsx.snap b/ui/components/app/confirm/info/row/__snapshots__/row.test.tsx.snap index 545d548fa3b7..dd6b6f568ef8 100644 --- a/ui/components/app/confirm/info/row/__snapshots__/row.test.tsx.snap +++ b/ui/components/app/confirm/info/row/__snapshots__/row.test.tsx.snap @@ -34,16 +34,10 @@ exports[`ConfirmInfoRow should match snapshot when copy is enabled 1`] = ` class="mm-box confirm-info-row mm-box--margin-top-2 mm-box--margin-bottom-2 mm-box--padding-right-5 mm-box--padding-left-2 mm-box--display-flex mm-box--flex-direction-row mm-box--flex-wrap-wrap mm-box--justify-content-space-between mm-box--align-items-center mm-box--color-text-default mm-box--rounded-lg" style="overflow-wrap: anywhere; min-height: 24px; position: relative;" > - +
diff --git a/ui/components/app/confirm/info/row/address.test.tsx b/ui/components/app/confirm/info/row/address.test.tsx index f27e067787b1..08a3561691ff 100644 --- a/ui/components/app/confirm/info/row/address.test.tsx +++ b/ui/components/app/confirm/info/row/address.test.tsx @@ -8,8 +8,6 @@ import { mockNetworkState } from '../../../../../../test/stub/networks'; import { ConfirmInfoRowAddress } from './address'; import { TEST_ADDRESS } from './constants'; -const CHAIN_ID_MOCK = CHAIN_IDS.MAINNET; - const render = ( // TODO: Replace `any` with type // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -21,10 +19,7 @@ const render = ( ...storeOverrides, }); - return renderWithProvider( - , - store, - ); + return renderWithProvider(, store); }; describe('ConfirmInfoRowAddress', () => { diff --git a/ui/components/app/confirm/info/row/address.tsx b/ui/components/app/confirm/info/row/address.tsx index 95fabf26652f..7d28851ece92 100644 --- a/ui/components/app/confirm/info/row/address.tsx +++ b/ui/components/app/confirm/info/row/address.tsx @@ -22,12 +22,11 @@ import { useFallbackDisplayName } from './hook'; export type ConfirmInfoRowAddressProps = { address: string; - chainId: string; isSnapUsingThis?: boolean; }; export const ConfirmInfoRowAddress = memo( - ({ address, chainId, isSnapUsingThis }: ConfirmInfoRowAddressProps) => { + ({ address, isSnapUsingThis }: ConfirmInfoRowAddressProps) => { const isPetNamesEnabled = useSelector(getPetnamesEnabled); const { displayName, hexAddress } = useFallbackDisplayName(address); const [isNicknamePopoverShown, setIsNicknamePopoverShown] = useState(false); @@ -49,7 +48,6 @@ export const ConfirmInfoRowAddress = memo( value={hexAddress} type={NameType.ETHEREUM_ADDRESS} preferContractSymbol - variation={chainId} /> ) : ( <> diff --git a/ui/components/app/confirm/info/row/copy-icon.tsx b/ui/components/app/confirm/info/row/copy-icon.tsx index 16f6604a53d4..ce349089dac3 100644 --- a/ui/components/app/confirm/info/row/copy-icon.tsx +++ b/ui/components/app/confirm/info/row/copy-icon.tsx @@ -1,20 +1,12 @@ -import React, { CSSProperties, useCallback } from 'react'; +import React, { useCallback } from 'react'; import { useCopyToClipboard } from '../../../../../hooks/useCopyToClipboard'; import { IconColor } from '../../../../../helpers/constants/design-system'; -import { - ButtonIcon, - ButtonIconSize, - IconName, -} from '../../../../component-library'; +import { Icon, IconName, IconSize } from '../../../../component-library'; type CopyCallback = (text: string) => void; -export const CopyIcon: React.FC<{ - copyText: string; - color?: IconColor; - style?: CSSProperties; -}> = ({ copyText, color, style = {} }) => { +export const CopyIcon: React.FC<{ copyText: string }> = ({ copyText }) => { const [copied, handleCopy] = useCopyToClipboard(); const handleClick = useCallback(async () => { @@ -22,19 +14,12 @@ export const CopyIcon: React.FC<{ }, [copyText]); return ( - ); }; diff --git a/ui/components/app/confirm/info/row/row.test.tsx b/ui/components/app/confirm/info/row/row.test.tsx index 8f0edab1be03..3a6a77e4b354 100644 --- a/ui/components/app/confirm/info/row/row.test.tsx +++ b/ui/components/app/confirm/info/row/row.test.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { fireEvent, render, screen } from '@testing-library/react'; +import { render } from '@testing-library/react'; import { Text } from '../../../../component-library'; import { ConfirmInfoRow } from './row'; @@ -22,20 +22,4 @@ describe('ConfirmInfoRow', () => { ); expect(container).toMatchSnapshot(); }); - - it('should be expandable when collapsed is true', () => { - render( - - Some text - , - ); - expect(screen.queryByText('Some text')).not.toBeInTheDocument(); - fireEvent.click(screen.getByTestId('sectionCollapseButton')); - expect(screen.queryByText('Some text')).toBeInTheDocument(); - }); }); diff --git a/ui/components/app/confirm/info/row/row.tsx b/ui/components/app/confirm/info/row/row.tsx index 55821f7080f5..7616ccae5f21 100644 --- a/ui/components/app/confirm/info/row/row.tsx +++ b/ui/components/app/confirm/info/row/row.tsx @@ -1,9 +1,7 @@ -import React, { createContext, useState } from 'react'; +import React, { createContext } from 'react'; import Tooltip from '../../../../ui/tooltip/tooltip'; import { Box, - ButtonIcon, - ButtonIconSize, Icon, IconName, IconSize, @@ -42,7 +40,6 @@ export type ConfirmInfoRowProps = { copyEnabled?: boolean; copyText?: string; 'data-testid'?: string; - collapsed?: boolean; }; const BACKGROUND_COLORS = { @@ -82,101 +79,71 @@ export const ConfirmInfoRow: React.FC = ({ labelChildren, color, copyEnabled = false, - copyText, + copyText = undefined, 'data-testid': dataTestId, - collapsed, -}) => { - const [expanded, setExpanded] = useState(!collapsed); - - const isCollapsible = collapsed !== undefined; - - return ( - +}) => ( + + + {copyEnabled && } - {copyEnabled && ( - - )} - {isCollapsible && ( - setExpanded(!expanded)} - data-testid="sectionCollapseButton" - ariaLabel="collapse-button" - /> - )} - - - - {label} - - {labelChildren} - {!labelChildren && tooltip?.length && ( - - - - )} - + + + {label} + + {labelChildren} + {!labelChildren && tooltip?.length && ( + + + + )} - {expanded && - (typeof children === 'string' ? ( - - {children} - - ) : ( - children - ))} - - ); -}; + {typeof children === 'string' ? ( + + {children} + + ) : ( + children + )} + + +); diff --git a/ui/components/app/currency-input/__snapshots__/currency-input.test.js.snap b/ui/components/app/currency-input/__snapshots__/currency-input.test.js.snap index 2482a916a5a9..f9823b5af9ac 100644 --- a/ui/components/app/currency-input/__snapshots__/currency-input.test.js.snap +++ b/ui/components/app/currency-input/__snapshots__/currency-input.test.js.snap @@ -36,7 +36,6 @@ exports[`CurrencyInput Component rendering should disable unit input 1`] = ` > $0.00 @@ -90,7 +89,6 @@ exports[`CurrencyInput Component rendering should render properly with a fiat va > 0.004327880204275946 @@ -185,7 +183,6 @@ exports[`CurrencyInput Component rendering should render properly with an ETH va > $231.06 @@ -240,7 +237,6 @@ exports[`CurrencyInput Component rendering should render properly without a suff > $0.00 diff --git a/ui/components/app/modals/cancel-transaction/cancel-transaction-gas-fee/__snapshots__/cancel-transaction-gas-fee.component.test.js.snap b/ui/components/app/modals/cancel-transaction/cancel-transaction-gas-fee/__snapshots__/cancel-transaction-gas-fee.component.test.js.snap index f98b3a231970..179a3821cad4 100644 --- a/ui/components/app/modals/cancel-transaction/cancel-transaction-gas-fee/__snapshots__/cancel-transaction-gas-fee.component.test.js.snap +++ b/ui/components/app/modals/cancel-transaction/cancel-transaction-gas-fee/__snapshots__/cancel-transaction-gas-fee.component.test.js.snap @@ -11,7 +11,6 @@ exports[`CancelTransactionGasFee Component should render 1`] = ` > <0.000001 @@ -27,7 +26,6 @@ exports[`CancelTransactionGasFee Component should render 1`] = ` > <0.000001 diff --git a/ui/components/app/modals/qr-scanner/qr-scanner.component.js b/ui/components/app/modals/qr-scanner/qr-scanner.component.js index 75e1a83417b9..51ae5a89a6ef 100644 --- a/ui/components/app/modals/qr-scanner/qr-scanner.component.js +++ b/ui/components/app/modals/qr-scanner/qr-scanner.component.js @@ -22,6 +22,10 @@ const READY_STATE = { READY: 'READY', }; +const ethereumPrefix = 'ethereum:'; +// A 0x-prefixed Ethereum address is 42 characters (2 prefix + 40 address) +const addressLength = 42; + const parseContent = (content) => { let type = 'unknown'; let values = {}; @@ -31,12 +35,18 @@ const parseContent = (content) => { // For ex. EIP-681 (https://eips.ethereum.org/EIPS/eip-681) // Ethereum address links - fox ex. ethereum:0x.....1111 - if (content.split('ethereum:').length > 1) { + if ( + content.split(ethereumPrefix).length > 1 && + content.length === ethereumPrefix.length + addressLength + ) { type = 'address'; - // uses regex capture groups to match and extract address while ignoring everything else + // uses regex capture groups to match and extract address values = { address: parseScanContent(content) }; // Regular ethereum addresses - fox ex. 0x.....1111 - } else if (content.substring(0, 2).toLowerCase() === '0x') { + } else if ( + content.substring(0, 2).toLowerCase() === '0x' && + content.length === addressLength + ) { type = 'address'; values = { address: content }; } diff --git a/ui/components/app/name/name-details/__snapshots__/name-details.test.tsx.snap b/ui/components/app/name/name-details/__snapshots__/name-details.test.tsx.snap index cfa08b3eee01..a6d0df79843d 100644 --- a/ui/components/app/name/name-details/__snapshots__/name-details.test.tsx.snap +++ b/ui/components/app/name/name-details/__snapshots__/name-details.test.tsx.snap @@ -706,12 +706,15 @@ exports[`NameDetails renders with recognized name 1`] = `
- + > + +

diff --git a/ui/components/app/name/name-details/name-details.test.tsx b/ui/components/app/name/name-details/name-details.test.tsx index 0f0df9f5b5f6..9e93384adcd6 100644 --- a/ui/components/app/name/name-details/name-details.test.tsx +++ b/ui/components/app/name/name-details/name-details.test.tsx @@ -11,8 +11,8 @@ import { MetaMetricsEventCategory, MetaMetricsEventName, } from '../../../../../shared/constants/metametrics'; -import { CHAIN_IDS } from '../../../../../shared/constants/network'; import { mockNetworkState } from '../../../../../test/stub/networks'; +import { CHAIN_IDS } from '../../../../../shared/constants/network'; import NameDetails from './name-details'; jest.mock('../../../../store/actions', () => ({ @@ -37,11 +37,11 @@ const SOURCE_ID_MOCK = 'ens'; const SOURCE_ID_2_MOCK = 'some_snap'; const PROPOSED_NAME_MOCK = 'TestProposedName'; const PROPOSED_NAME_2_MOCK = 'TestProposedName2'; -const VARIATION_MOCK = CHAIN_ID_MOCK; const STATE_MOCK = { metamask: { ...mockNetworkState({ chainId: CHAIN_IDS.MAINNET }), + nameSources: { [SOURCE_ID_2_MOCK]: { label: 'Super Name Resolution Snap' }, }, @@ -85,17 +85,13 @@ const STATE_MOCK = { }, }, useTokenDetection: true, - tokensChainsCache: { - [VARIATION_MOCK]: { - data: { - '0x0a3bb08b3a15a19b4de82f8acfc862606fb69a2d': { - address: '0x0a3bb08b3a15a19b4de82f8acfc862606fb69a2d', - symbol: 'IUSD', - name: 'iZUMi Bond USD', - iconUrl: - 'https://static.cx.metamask.io/api/v1/tokenIcons/1/0x0a3bb08b3a15a19b4de82f8acfc862606fb69a2d.png', - }, - }, + tokenList: { + '0x0a3bb08b3a15a19b4de82f8acfc862606fb69a2d': { + address: '0x0a3bb08b3a15a19b4de82f8acfc862606fb69a2d', + symbol: 'IUSD', + name: 'iZUMi Bond USD', + iconUrl: + 'https://static.cx.metamask.io/api/v1/tokenIcons/1/0x0a3bb08b3a15a19b4de82f8acfc862606fb69a2d.png', }, }, }, @@ -161,7 +157,6 @@ describe('NameDetails', () => { undefined} />, store, @@ -175,7 +170,6 @@ describe('NameDetails', () => { undefined} />, store, @@ -189,7 +183,6 @@ describe('NameDetails', () => { undefined} />, store, @@ -203,7 +196,6 @@ describe('NameDetails', () => { undefined} />, store, @@ -217,7 +209,6 @@ describe('NameDetails', () => { undefined} />, store, @@ -238,7 +229,6 @@ describe('NameDetails', () => { undefined} />, store, @@ -261,7 +251,6 @@ describe('NameDetails', () => { undefined} />, store, @@ -284,7 +273,6 @@ describe('NameDetails', () => { undefined} />, store, @@ -307,7 +295,6 @@ describe('NameDetails', () => { undefined} />, store, @@ -330,7 +317,6 @@ describe('NameDetails', () => { undefined} />, store, @@ -350,7 +336,6 @@ describe('NameDetails', () => { undefined} />, store, @@ -388,7 +373,6 @@ describe('NameDetails', () => { undefined} /> , @@ -415,7 +399,6 @@ describe('NameDetails', () => { undefined} /> , @@ -443,7 +426,6 @@ describe('NameDetails', () => { undefined} /> , @@ -472,7 +454,6 @@ describe('NameDetails', () => { undefined} /> , diff --git a/ui/components/app/name/name-details/name-details.tsx b/ui/components/app/name/name-details/name-details.tsx index 1bb2b1f1e478..22b0445a0ad5 100644 --- a/ui/components/app/name/name-details/name-details.tsx +++ b/ui/components/app/name/name-details/name-details.tsx @@ -46,7 +46,7 @@ import Name from '../name'; import FormComboField, { FormComboFieldOption, } from '../../../ui/form-combo-field/form-combo-field'; -import { getNameSources } from '../../../../selectors'; +import { getCurrentChainId, getNameSources } from '../../../../selectors'; import { setName as saveName, updateProposedNames, @@ -64,7 +64,6 @@ export type NameDetailsProps = { sourcePriority?: string[]; type: NameType; value: string; - variation: string; }; type ProposedNameOption = Required & { @@ -158,14 +157,12 @@ function getInitialSources( return [...resultSources, ...stateSources].sort(); } -function useProposedNames(value: string, type: NameType, variation: string) { +function useProposedNames(value: string, type: NameType, chainId: string) { const dispatch = useDispatch(); - const { proposedNames } = useName(value, type, variation); - + const { proposedNames } = useName(value, type); // TODO: Replace `any` with type // eslint-disable-next-line @typescript-eslint/no-explicit-any const updateInterval = useRef(); - const [initialSources, setInitialSources] = useState(); useEffect(() => { @@ -181,7 +178,7 @@ function useProposedNames(value: string, type: NameType, variation: string) { value, type, onlyUpdateAfterDelay: true, - variation, + variation: chainId, }), // TODO: Replace `any` with type // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -199,7 +196,7 @@ function useProposedNames(value: string, type: NameType, variation: string) { updateInterval.current = setInterval(update, UPDATE_DELAY); return reset; - }, [value, type, variation, dispatch, initialSources, setInitialSources]); + }, [value, type, chainId, dispatch, initialSources, setInitialSources]); return { proposedNames, initialSources }; } @@ -208,20 +205,13 @@ export default function NameDetails({ onClose, type, value, - variation, }: NameDetailsProps) { - const { name: savedPetname, sourceId: savedSourceId } = useName( + const chainId = useSelector(getCurrentChainId); + const { name: savedPetname, sourceId: savedSourceId } = useName(value, type); + const { name: displayName, hasPetname: hasSavedPetname } = useDisplayName( value, type, - variation, ); - - const { name: displayName, hasPetname: hasSavedPetname } = useDisplayName({ - value, - type, - variation, - }); - const nameSources = useSelector(getNameSources, isEqual); const [name, setName] = useState(''); const [openMetricSent, setOpenMetricSent] = useState(false); @@ -236,7 +226,7 @@ export default function NameDetails({ const { proposedNames, initialSources } = useProposedNames( value, type, - variation, + chainId, ); const [copiedAddress, handleCopyAddress] = useCopyToClipboard() as [ @@ -285,12 +275,12 @@ export default function NameDetails({ type, name: name?.length ? name : null, sourceId: selectedSourceId, - variation, + variation: chainId, }), ); onClose(); - }, [name, selectedSourceId, onClose, trackPetnamesSaveEvent, variation]); + }, [name, selectedSourceId, onClose, trackPetnamesSaveEvent, chainId]); const handleClose = useCallback(() => { onClose(); @@ -343,7 +333,6 @@ export default function NameDetails({ diff --git a/ui/components/app/name/name.stories.tsx b/ui/components/app/name/name.stories.tsx index 732c9059b530..fb23334a8776 100644 --- a/ui/components/app/name/name.stories.tsx +++ b/ui/components/app/name/name.stories.tsx @@ -3,75 +3,108 @@ import React from 'react'; import { NameType } from '@metamask/name-controller'; import { Provider } from 'react-redux'; import configureStore from '../../../store/store'; -import Name, { NameProps } from './name'; -import mockState from '../../../../test/data/mock-state.json'; -import { - EXPERIENCES_TYPE, - FIRST_PARTY_CONTRACT_NAMES, -} from '../../../../shared/constants/first-party-contracts'; -import { cloneDeep } from 'lodash'; +import Name from './name'; +import { mockNetworkState } from '../../../../test/stub/networks'; -const ADDRESS_MOCK = '0xc0ffee254729296a45a3885639ac7e10f9d54978'; -const ADDRESS_NFT_MOCK = '0xc0ffee254729296a45a3885639ac7e10f9d54979'; -const VARIATION_MOCK = '0x1'; -const NAME_MOCK = 'Saved Name'; +const addressNoSavedNameMock = '0xc0ffee254729296a45a3885639ac7e10f9d54978'; +const addressSavedNameMock = '0xc0ffee254729296a45a3885639ac7e10f9d54977'; +const addressSavedTokenMock = '0x0a3bb08b3a15a19b4de82f8acfc862606fb69a2d'; +const addressUnsavedTokenMock = '0x0a5e677a6a24b2f1a2bf4f3bffc443231d2fdec8'; +const chainIdMock = '0x1'; -const ADDRESS_FIRST_PARTY_MOCK = - FIRST_PARTY_CONTRACT_NAMES[EXPERIENCES_TYPE.METAMASK_BRIDGE][ - VARIATION_MOCK - ].toLowerCase(); - -const PROPOSED_NAMES_MOCK = { - ens: { - proposedNames: ['test.eth'], - lastRequestTime: 123, - retryDelay: null, - }, - etherscan: { - proposedNames: ['TestContract'], - lastRequestTime: 123, - retryDelay: null, - }, - token: { - proposedNames: ['Test Token'], - lastRequestTime: 123, - retryDelay: null, - }, - lens: { - proposedNames: ['test.lens'], - lastRequestTime: 123, - retryDelay: null, - }, -}; - -const STATE_MOCK = { - ...mockState, +const storeMock = configureStore({ metamask: { - ...mockState.metamask, + ...mockNetworkState({chainId: chainIdMock}), useTokenDetection: true, - tokensChainsCache: {}, + tokenList: { + '0x0a3bb08b3a15a19b4de82f8acfc862606fb69a2d': { + address: '0x0a3bb08b3a15a19b4de82f8acfc862606fb69a2d', + symbol: 'IUSD', + name: 'iZUMi Bond USD', + iconUrl: + 'https://static.cx.metamask.io/api/v1/tokenIcons/1/0x0a3bb08b3a15a19b4de82f8acfc862606fb69a2d.png', + }, + '0x0a5e677a6a24b2f1a2bf4f3bffc443231d2fdec8': { + address: '0x0a5e677a6a24b2f1a2bf4f3bffc443231d2fdec8', + symbol: 'USX', + name: 'dForce USD', + iconUrl: + 'https://static.cx.metamask.io/api/v1/tokenIcons/1/0x0a5e677a6a24b2f1a2bf4f3bffc443231d2fdec8.png', + }, + }, names: { [NameType.ETHEREUM_ADDRESS]: { - [ADDRESS_MOCK]: { - [VARIATION_MOCK]: { - proposedNames: PROPOSED_NAMES_MOCK, + [addressNoSavedNameMock]: { + [chainIdMock]: { + proposedNames: { + ens: { + proposedNames: ['test.eth'], + lastRequestTime: 123, + retryDelay: null, + }, + etherscan: { + proposedNames: ['TestContract'], + lastRequestTime: 123, + retryDelay: null, + }, + token: { + proposedNames: ['Test Token'], + lastRequestTime: 123, + retryDelay: null, + }, + lens: { + proposedNames: ['test.lens'], + lastRequestTime: 123, + retryDelay: null, + }, + }, }, }, - [ADDRESS_NFT_MOCK]: { - [VARIATION_MOCK]: { - proposedNames: PROPOSED_NAMES_MOCK, + [addressSavedNameMock]: { + [chainIdMock]: { + proposedNames: { + ens: { + proposedNames: ['test.eth'], + lastRequestTime: 123, + retryDelay: null, + }, + etherscan: { + proposedNames: ['TestContract'], + lastRequestTime: 123, + retryDelay: null, + }, + token: { + proposedNames: ['Test Token'], + lastRequestTime: 123, + retryDelay: null, + }, + lens: { + proposedNames: ['test.lens'], + lastRequestTime: 123, + retryDelay: null, + }, + }, + name: 'Test Token', + sourceId: 'token', }, }, - [ADDRESS_FIRST_PARTY_MOCK]: { - [VARIATION_MOCK]: { - proposedNames: PROPOSED_NAMES_MOCK, + [addressSavedTokenMock]: { + [chainIdMock]: { + proposedNames: {}, + name: 'Saved Token Name', + sourceId: 'token', }, }, }, }, - nameSources: {}, + nameSources: { + ens: { label: 'Ethereum Name Service (ENS)' }, + etherscan: { label: 'Etherscan (Verified Contract Name)' }, + token: { label: 'Blockchain (Token Name)' }, + lens: { label: 'Lens Protocol' }, + }, }, -}; +}); /** * Displays the saved name for a raw value such as an Ethereum address.

@@ -92,10 +125,6 @@ export default { description: `The type of value.

Limited to the values in the \`NameType\` enum.`, }, - variation: { - control: 'text', - description: `The variation of the value.

For example, the chain ID if the type is Ethereum address.`, - }, disableEdit: { control: 'boolean', description: `Whether to prevent the modal from opening when the component is clicked.`, @@ -105,141 +134,68 @@ export default { }, }, args: { - value: ADDRESS_MOCK, + value: addressNoSavedNameMock, type: NameType.ETHEREUM_ADDRESS, - variation: VARIATION_MOCK, disableEdit: false, }, - render: ({ state, ...args }) => { - const finalState = cloneDeep(STATE_MOCK); - state?.(finalState); - - return ( - - - - ); - }, + decorators: [(story) => {story()}], }; +// eslint-disable-next-line jsdoc/require-param /** * No name has been saved for the value and type. */ -export const NoSavedName = { - name: 'No Saved Name', - args: { - value: ADDRESS_MOCK, - type: NameType.ETHEREUM_ADDRESS, - variation: VARIATION_MOCK, - }, +export const DefaultStory = (args) => { + return ; }; +DefaultStory.storyName = 'No Saved Name'; + /** * A name was previously saved for this value and type.

* The component will still display a modal when clicked to edit the name. */ -export const SavedNameStory = { - name: 'Saved Name', - args: { - value: ADDRESS_MOCK, - type: NameType.ETHEREUM_ADDRESS, - variation: VARIATION_MOCK, - state: (state) => { - state.metamask.names[NameType.ETHEREUM_ADDRESS][ADDRESS_MOCK][ - VARIATION_MOCK - ].name = NAME_MOCK; - }, - }, +export const SavedNameStory = () => { + return ; }; +SavedNameStory.storyName = 'Saved Name'; + /** * No name was previously saved for this recognized token.

* The component will still display a modal when clicked to edit the name. */ -export const DefaultTokenNameStory = { - name: 'Default ERC-20 Token Name', - args: { - value: ADDRESS_MOCK, - type: NameType.ETHEREUM_ADDRESS, - variation: VARIATION_MOCK, - state: (state) => { - state.metamask.tokensChainsCache = { - [VARIATION_MOCK]: { - data: { - [ADDRESS_MOCK]: { - address: ADDRESS_MOCK, - symbol: 'IUSD', - name: 'iZUMi Bond USD', - iconUrl: - 'https://static.cx.metamask.io/api/v1/tokenIcons/1/0x0a3bb08b3a15a19b4de82f8acfc862606fb69a2d.png', - }, - }, - }, - }; - }, - }, +export const UnsavedTokenNameStory = () => { + return ( + + ); }; -/** - * No name was previously saved for this watched NFT.

- * The component will still display a modal when clicked to edit the name. - */ -export const DefaultWatchedNFTNameStory = { - name: 'Default Watched NFT Name', - args: { - value: ADDRESS_MOCK, - type: NameType.ETHEREUM_ADDRESS, - variation: VARIATION_MOCK, - state: (state) => { - state.metamask.allNftContracts = { - '0x123': { - [VARIATION_MOCK]: [ - { - address: ADDRESS_MOCK, - name: 'Everything I Own', - }, - ], - }, - }; - }, - }, -}; +UnsavedTokenNameStory.storyName = 'Unsaved Token Name'; /** - * No name was previously saved for this recognized NFT.

+ * A name was previously saved for this recognized token.

* The component will still display a modal when clicked to edit the name. */ -export const DefaultNFTNameStory = { - name: 'Default NFT Name', - args: { - value: ADDRESS_NFT_MOCK, - type: NameType.ETHEREUM_ADDRESS, - variation: VARIATION_MOCK, - }, +export const SavedTokenNameStory = () => { + return ( + + ); }; -/** - * No name was previously saved for this first-party contract.

- * The component will still display a modal when clicked to edit the name. - */ -export const DefaultFirstPartyNameStory = { - name: 'Default First-Party Name', - args: { - value: ADDRESS_FIRST_PARTY_MOCK, - type: NameType.ETHEREUM_ADDRESS, - variation: VARIATION_MOCK, - }, -}; +SavedTokenNameStory.storyName = 'Saved Token Name'; /** * Clicking the component will not display a modal to edit the name. */ -export const EditDisabledStory = { - name: 'Edit Disabled', - args: { - value: ADDRESS_MOCK, - type: NameType.ETHEREUM_ADDRESS, - variation: VARIATION_MOCK, - disableEdit: true, - }, +export const EditDisabledStory = () => { + return ( + + ); }; + +EditDisabledStory.storyName = 'Edit Disabled'; diff --git a/ui/components/app/name/name.test.tsx b/ui/components/app/name/name.test.tsx index 33648e98e38c..061d39e670de 100644 --- a/ui/components/app/name/name.test.tsx +++ b/ui/components/app/name/name.test.tsx @@ -22,7 +22,6 @@ jest.mock('react-redux', () => ({ const ADDRESS_NO_SAVED_NAME_MOCK = '0xc0ffee254729296a45a3885639ac7e10f9d54977'; const ADDRESS_SAVED_NAME_MOCK = '0xc0ffee254729296a45a3885639ac7e10f9d54979'; const SAVED_NAME_MOCK = 'TestName'; -const VARIATION_MOCK = 'testVariation'; const STATE_MOCK = { metamask: { @@ -45,11 +44,7 @@ describe('Name', () => { }); const { container } = renderWithProvider( - , + , store, ); @@ -66,7 +61,6 @@ describe('Name', () => { , store, ); @@ -81,11 +75,7 @@ describe('Name', () => { }); const { container } = renderWithProvider( - , + , store, ); @@ -100,11 +90,7 @@ describe('Name', () => { }); const { container } = renderWithProvider( - , + , store, ); @@ -128,11 +114,7 @@ describe('Name', () => { renderWithProvider( - + , store, ); diff --git a/ui/components/app/name/name.tsx b/ui/components/app/name/name.tsx index 2097d21faf07..5af2851c8885 100644 --- a/ui/components/app/name/name.tsx +++ b/ui/components/app/name/name.tsx @@ -38,12 +38,6 @@ export type NameProps = { /** The raw value to display the name of. */ value: string; - - /** - * The variation of the value. - * Such as the chain ID if the `type` is an Ethereum address. - */ - variation: string; }; function formatValue(value: string, type: NameType): string { @@ -67,17 +61,15 @@ const Name = memo( disableEdit, internal, preferContractSymbol = false, - variation, }: NameProps) => { const [modalOpen, setModalOpen] = useState(false); const trackEvent = useContext(MetaMetricsContext); - const { name, hasPetname, image } = useDisplayName({ + const { name, hasPetname, image } = useDisplayName( value, type, preferContractSymbol, - variation, - }); + ); useEffect(() => { if (internal) { @@ -108,12 +100,7 @@ const Name = memo( return ( {!disableEdit && modalOpen && ( - + )}

{ const tooltipTitle = await waitFor(() => container.querySelector( - '[data-original-title="This account is not set up for use with Goerli"]', + '[data-original-title="This account is not set up for use with goerli"]', ), ); diff --git a/ui/components/app/snaps/snap-ui-address/__snapshots__/snap-ui-address.test.tsx.snap b/ui/components/app/snaps/snap-ui-address/__snapshots__/snap-ui-address.test.tsx.snap index 7dd3749a6e5f..d29236409dbc 100644 --- a/ui/components/app/snaps/snap-ui-address/__snapshots__/snap-ui-address.test.tsx.snap +++ b/ui/components/app/snaps/snap-ui-address/__snapshots__/snap-ui-address.test.tsx.snap @@ -3,7 +3,7 @@ exports[`SnapUIAddress renders Bitcoin address 1`] = `
= ({ [caipIdentifier], ); - const displayName = useDisplayName(parsed); - - const value = - displayName ?? - shortenAddress( - parsed.chain.namespace === 'eip155' - ? toChecksumHexAddress(parsed.address) - : parsed.address, - ); + // For EVM addresses, we make sure they are checksummed. + const transformedAddress = + parsed.chain.namespace === 'eip155' + ? toChecksumHexAddress(parsed.address) + : parsed.address; + const shortenedAddress = shortenAddress(transformedAddress); return ( - + - {value} + {shortenedAddress} ); }; diff --git a/ui/components/app/snaps/snap-ui-link/index.scss b/ui/components/app/snaps/snap-ui-link/index.scss deleted file mode 100644 index 7d3f75f0e372..000000000000 --- a/ui/components/app/snaps/snap-ui-link/index.scss +++ /dev/null @@ -1,11 +0,0 @@ -.snap-ui-renderer__link { - & .snap-ui-renderer__address { - // Fixes an issue where the link end icon would wrap - display: inline-flex; - } - - .snap-ui-renderer__address + .mm-icon { - // This fixes an issue where the icon would be misaligned with the Address component - top: 0; - } -} diff --git a/ui/components/app/snaps/snap-ui-link/snap-ui-link.js b/ui/components/app/snaps/snap-ui-link/snap-ui-link.js index 58a22008a52a..a1289543fd45 100644 --- a/ui/components/app/snaps/snap-ui-link/snap-ui-link.js +++ b/ui/components/app/snaps/snap-ui-link/snap-ui-link.js @@ -34,7 +34,7 @@ export const SnapUILink = ({ href, children }) => { {children} @@ -51,14 +51,7 @@ export const SnapUILink = ({ href, children }) => { externalLink size={ButtonLinkSize.Inherit} display={Display.Inline} - className="snap-ui-renderer__link" - style={{ - // Prevents the link from taking up the full width of the parent. - width: 'fit-content', - }} - textProps={{ - display: Display.Inline, - }} + className="snap-ui-link" > {children} diff --git a/ui/components/app/snaps/snap-ui-renderer/index.scss b/ui/components/app/snaps/snap-ui-renderer/index.scss index d32edf726479..7e18e72c917f 100644 --- a/ui/components/app/snaps/snap-ui-renderer/index.scss +++ b/ui/components/app/snaps/snap-ui-renderer/index.scss @@ -34,10 +34,6 @@ border-radius: 8px; border-color: var(--color-border-muted); - & .mm-icon { - top: 0; - } - .mm-text--overflow-wrap-anywhere { overflow-wrap: normal; } @@ -52,6 +48,10 @@ &__panel { gap: 8px; + + .mm-icon--size-inherit { + top: 0; + } } &__text { diff --git a/ui/components/app/toast-master/selectors.ts b/ui/components/app/toast-master/selectors.ts deleted file mode 100644 index b88762c3bc19..000000000000 --- a/ui/components/app/toast-master/selectors.ts +++ /dev/null @@ -1,108 +0,0 @@ -import { InternalAccount, isEvmAccountType } from '@metamask/keyring-api'; -import { getAlertEnabledness } from '../../../ducks/metamask/metamask'; -import { PRIVACY_POLICY_DATE } from '../../../helpers/constants/privacy-policy'; -import { - SURVEY_DATE, - SURVEY_END_TIME, - SURVEY_START_TIME, -} from '../../../helpers/constants/survey'; -import { getPermittedAccountsForCurrentTab } from '../../../selectors'; -import { MetaMaskReduxState } from '../../../store/store'; -import { getIsPrivacyToastRecent } from './utils'; - -// TODO: get this into one of the larger definitions of state type -type State = Omit & { - appState: { - showNftDetectionEnablementToast?: boolean; - }; - metamask: { - newPrivacyPolicyToastClickedOrClosed?: boolean; - newPrivacyPolicyToastShownDate?: number; - onboardingDate?: number; - showNftDetectionEnablementToast?: boolean; - surveyLinkLastClickedOrClosed?: number; - switchedNetworkNeverShowMessage?: boolean; - }; -}; - -/** - * Determines if the survey toast should be shown based on the current time, survey start and end times, and whether the survey link was last clicked or closed. - * - * @param state - The application state containing the necessary survey data. - * @returns True if the current time is between the survey start and end times and the survey link was not last clicked or closed. False otherwise. - */ -export function selectShowSurveyToast(state: State): boolean { - if (state.metamask?.surveyLinkLastClickedOrClosed) { - return false; - } - - const startTime = new Date(`${SURVEY_DATE} ${SURVEY_START_TIME}`).getTime(); - const endTime = new Date(`${SURVEY_DATE} ${SURVEY_END_TIME}`).getTime(); - const now = Date.now(); - - return now > startTime && now < endTime; -} - -/** - * Determines if the privacy policy toast should be shown based on the current date and whether the new privacy policy toast was clicked or closed. - * - * @param state - The application state containing the privacy policy data. - * @returns Boolean is True if the toast should be shown, and the number is the date the toast was last shown. - */ -export function selectShowPrivacyPolicyToast(state: State): { - showPrivacyPolicyToast: boolean; - newPrivacyPolicyToastShownDate?: number; -} { - const { - newPrivacyPolicyToastClickedOrClosed, - newPrivacyPolicyToastShownDate, - onboardingDate, - } = state.metamask || {}; - const newPrivacyPolicyDate = new Date(PRIVACY_POLICY_DATE); - const currentDate = new Date(Date.now()); - - const showPrivacyPolicyToast = - !newPrivacyPolicyToastClickedOrClosed && - currentDate >= newPrivacyPolicyDate && - getIsPrivacyToastRecent(newPrivacyPolicyToastShownDate) && - // users who onboarded before the privacy policy date should see the notice - // and - // old users who don't have onboardingDate set should see the notice - (!onboardingDate || onboardingDate < newPrivacyPolicyDate.valueOf()); - - return { showPrivacyPolicyToast, newPrivacyPolicyToastShownDate }; -} - -export function selectNftDetectionEnablementToast(state: State): boolean { - return Boolean(state.appState?.showNftDetectionEnablementToast); -} - -// If there is more than one connected account to activeTabOrigin, -// *BUT* the current account is not one of them, show the banner -export function selectShowConnectAccountToast( - state: State, - account: InternalAccount, -): boolean { - const allowShowAccountSetting = getAlertEnabledness(state).unconnectedAccount; - const connectedAccounts = getPermittedAccountsForCurrentTab(state); - const isEvmAccount = isEvmAccountType(account?.type); - - return ( - allowShowAccountSetting && - account && - state.activeTab?.origin && - isEvmAccount && - connectedAccounts.length > 0 && - !connectedAccounts.some((address) => address === account.address) - ); -} - -/** - * Retrieves user preference to never see the "Switched Network" toast - * - * @param state - Redux state object. - * @returns Boolean preference value - */ -export function selectSwitchedNetworkNeverShowMessage(state: State): boolean { - return Boolean(state.metamask.switchedNetworkNeverShowMessage); -} diff --git a/ui/components/app/toast-master/toast-master.js b/ui/components/app/toast-master/toast-master.js deleted file mode 100644 index 584f1cc25983..000000000000 --- a/ui/components/app/toast-master/toast-master.js +++ /dev/null @@ -1,299 +0,0 @@ -/* eslint-disable react/prop-types -- TODO: upgrade to TypeScript */ - -import React, { useState } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; -import { useHistory, useLocation } from 'react-router-dom'; -import { MILLISECOND, SECOND } from '../../../../shared/constants/time'; -import { - PRIVACY_POLICY_LINK, - SURVEY_LINK, -} from '../../../../shared/lib/ui-utils'; -import { - BorderColor, - BorderRadius, - IconColor, - TextVariant, -} from '../../../helpers/constants/design-system'; -import { - DEFAULT_ROUTE, - REVIEW_PERMISSIONS, -} from '../../../helpers/constants/routes'; -import { getURLHost } from '../../../helpers/utils/util'; -import { useI18nContext } from '../../../hooks/useI18nContext'; -import { usePrevious } from '../../../hooks/usePrevious'; -import { - getCurrentNetwork, - getOriginOfCurrentTab, - getSelectedAccount, - getSwitchedNetworkDetails, - getUseNftDetection, -} from '../../../selectors'; -import { - addPermittedAccount, - clearSwitchedNetworkDetails, - hidePermittedNetworkToast, -} from '../../../store/actions'; -import { - AvatarAccount, - AvatarAccountSize, - AvatarNetwork, - Icon, - IconName, -} from '../../component-library'; -import { Toast, ToastContainer } from '../../multichain'; -import { SurveyToast } from '../../ui/survey-toast'; -import { - selectNftDetectionEnablementToast, - selectShowConnectAccountToast, - selectShowPrivacyPolicyToast, - selectShowSurveyToast, - selectSwitchedNetworkNeverShowMessage, -} from './selectors'; -import { - setNewPrivacyPolicyToastClickedOrClosed, - setNewPrivacyPolicyToastShownDate, - setShowNftDetectionEnablementToast, - setSurveyLinkLastClickedOrClosed, - setSwitchedNetworkNeverShowMessage, -} from './utils'; - -export function ToastMaster() { - const location = useLocation(); - - const onHomeScreen = location.pathname === DEFAULT_ROUTE; - - return ( - onHomeScreen && ( - - - - - - - - - - ) - ); -} - -function ConnectAccountToast() { - const t = useI18nContext(); - const dispatch = useDispatch(); - - const [hideConnectAccountToast, setHideConnectAccountToast] = useState(false); - const account = useSelector(getSelectedAccount); - - // If the account has changed, allow the connect account toast again - const prevAccountAddress = usePrevious(account?.address); - if (account?.address !== prevAccountAddress && hideConnectAccountToast) { - setHideConnectAccountToast(false); - } - - const showConnectAccountToast = useSelector((state) => - selectShowConnectAccountToast(state, account), - ); - - const activeTabOrigin = useSelector(getOriginOfCurrentTab); - - return ( - Boolean(!hideConnectAccountToast && showConnectAccountToast) && ( - - } - text={t('accountIsntConnectedToastText', [ - account?.metadata?.name, - getURLHost(activeTabOrigin), - ])} - actionText={t('connectAccount')} - onActionClick={() => { - // Connect this account - dispatch(addPermittedAccount(activeTabOrigin, account.address)); - // Use setTimeout to prevent React re-render from - // hiding the tooltip - setTimeout(() => { - // Trigger a mouseenter on the header's connection icon - // to display the informative connection tooltip - document - .querySelector( - '[data-testid="connection-menu"] [data-tooltipped]', - ) - ?.dispatchEvent(new CustomEvent('mouseenter', {})); - }, 250 * MILLISECOND); - }} - onClose={() => setHideConnectAccountToast(true)} - /> - ) - ); -} - -function SurveyToastMayDelete() { - const t = useI18nContext(); - - const showSurveyToast = useSelector(selectShowSurveyToast); - - return ( - showSurveyToast && ( - - } - text={t('surveyTitle')} - actionText={t('surveyConversion')} - onActionClick={() => { - global.platform.openTab({ - url: SURVEY_LINK, - }); - setSurveyLinkLastClickedOrClosed(Date.now()); - }} - onClose={() => { - setSurveyLinkLastClickedOrClosed(Date.now()); - }} - /> - ) - ); -} - -function PrivacyPolicyToast() { - const t = useI18nContext(); - - const { showPrivacyPolicyToast, newPrivacyPolicyToastShownDate } = - useSelector(selectShowPrivacyPolicyToast); - - // If the privacy policy toast is shown, and there is no date set, set it - if (showPrivacyPolicyToast && !newPrivacyPolicyToastShownDate) { - setNewPrivacyPolicyToastShownDate(Date.now()); - } - - return ( - showPrivacyPolicyToast && ( - - } - text={t('newPrivacyPolicyTitle')} - actionText={t('newPrivacyPolicyActionButton')} - onActionClick={() => { - global.platform.openTab({ - url: PRIVACY_POLICY_LINK, - }); - setNewPrivacyPolicyToastClickedOrClosed(); - }} - onClose={setNewPrivacyPolicyToastClickedOrClosed} - /> - ) - ); -} - -function SwitchedNetworkToast() { - const t = useI18nContext(); - const dispatch = useDispatch(); - - const switchedNetworkDetails = useSelector(getSwitchedNetworkDetails); - const switchedNetworkNeverShowMessage = useSelector( - selectSwitchedNetworkNeverShowMessage, - ); - - const isShown = switchedNetworkDetails && !switchedNetworkNeverShowMessage; - - return ( - isShown && ( - - } - text={t('switchedNetworkToastMessage', [ - switchedNetworkDetails.nickname, - getURLHost(switchedNetworkDetails.origin), - ])} - actionText={t('switchedNetworkToastDecline')} - onActionClick={setSwitchedNetworkNeverShowMessage} - onClose={() => dispatch(clearSwitchedNetworkDetails())} - /> - ) - ); -} - -function NftEnablementToast() { - const t = useI18nContext(); - const dispatch = useDispatch(); - - const showNftEnablementToast = useSelector(selectNftDetectionEnablementToast); - const useNftDetection = useSelector(getUseNftDetection); - - const autoHideToastDelay = 5 * SECOND; - - return ( - showNftEnablementToast && - useNftDetection && ( - - } - text={t('nftAutoDetectionEnabled')} - borderRadius={BorderRadius.LG} - textVariant={TextVariant.bodyMd} - autoHideTime={autoHideToastDelay} - onAutoHideToast={() => - dispatch(setShowNftDetectionEnablementToast(false)) - } - /> - ) - ); -} - -function PermittedNetworkToast() { - const t = useI18nContext(); - const dispatch = useDispatch(); - - const isPermittedNetworkToastOpen = useSelector( - (state) => state.appState.showPermittedNetworkToastOpen, - ); - - const currentNetwork = useSelector(getCurrentNetwork); - const activeTabOrigin = useSelector(getOriginOfCurrentTab); - const safeEncodedHost = encodeURIComponent(activeTabOrigin); - const history = useHistory(); - - return ( - isPermittedNetworkToastOpen && ( - - } - text={t('permittedChainToastUpdate', [ - getURLHost(activeTabOrigin), - currentNetwork?.nickname, - ])} - actionText={t('editPermissions')} - onActionClick={() => { - dispatch(hidePermittedNetworkToast()); - history.push(`${REVIEW_PERMISSIONS}/${safeEncodedHost}`); - }} - onClose={() => dispatch(hidePermittedNetworkToast())} - /> - ) - ); -} diff --git a/ui/components/app/toast-master/toast-master.test.ts b/ui/components/app/toast-master/toast-master.test.ts deleted file mode 100644 index 8b29f20a240d..000000000000 --- a/ui/components/app/toast-master/toast-master.test.ts +++ /dev/null @@ -1,206 +0,0 @@ -import { PRIVACY_POLICY_DATE } from '../../../helpers/constants/privacy-policy'; -import { SURVEY_DATE, SURVEY_GMT } from '../../../helpers/constants/survey'; -import { - selectShowPrivacyPolicyToast, - selectShowSurveyToast, -} from './selectors'; - -describe('#getShowSurveyToast', () => { - const realDateNow = Date.now; - - afterEach(() => { - Date.now = realDateNow; - }); - - it('shows the survey link when not yet seen and within time bounds', () => { - Date.now = () => - new Date(`${SURVEY_DATE} 12:25:00 ${SURVEY_GMT}`).getTime(); - const result = selectShowSurveyToast({ - // @ts-expect-error: intentionally passing incomplete input - metamask: { - surveyLinkLastClickedOrClosed: undefined, - }, - }); - expect(result).toStrictEqual(true); - }); - - it('does not show the survey link when seen and within time bounds', () => { - Date.now = () => - new Date(`${SURVEY_DATE} 12:25:00 ${SURVEY_GMT}`).getTime(); - const result = selectShowSurveyToast({ - // @ts-expect-error: intentionally passing incomplete input - metamask: { - surveyLinkLastClickedOrClosed: 123456789, - }, - }); - expect(result).toStrictEqual(false); - }); - - it('does not show the survey link before time bounds', () => { - Date.now = () => - new Date(`${SURVEY_DATE} 11:25:00 ${SURVEY_GMT}`).getTime(); - const result = selectShowSurveyToast({ - // @ts-expect-error: intentionally passing incomplete input - metamask: { - surveyLinkLastClickedOrClosed: undefined, - }, - }); - expect(result).toStrictEqual(false); - }); - - it('does not show the survey link after time bounds', () => { - Date.now = () => - new Date(`${SURVEY_DATE} 14:25:00 ${SURVEY_GMT}`).getTime(); - const result = selectShowSurveyToast({ - // @ts-expect-error: intentionally passing incomplete input - metamask: { - surveyLinkLastClickedOrClosed: undefined, - }, - }); - expect(result).toStrictEqual(false); - }); -}); - -describe('#getShowPrivacyPolicyToast', () => { - let dateNowSpy: jest.SpyInstance; - - describe('mock one day after', () => { - beforeEach(() => { - const dayAfterPolicyDate = new Date(PRIVACY_POLICY_DATE); - dayAfterPolicyDate.setDate(dayAfterPolicyDate.getDate() + 1); - - dateNowSpy = jest - .spyOn(Date, 'now') - .mockReturnValue(dayAfterPolicyDate.getTime()); - }); - - afterEach(() => { - dateNowSpy.mockRestore(); - }); - - it('shows the privacy policy toast when not yet seen, on or after the policy date, and onboardingDate is before the policy date', () => { - const result = selectShowPrivacyPolicyToast({ - // @ts-expect-error: intentionally passing incomplete input - metamask: { - newPrivacyPolicyToastClickedOrClosed: false, - onboardingDate: new Date(PRIVACY_POLICY_DATE).setDate( - new Date(PRIVACY_POLICY_DATE).getDate() - 2, - ), - }, - }); - expect(result.showPrivacyPolicyToast).toBe(true); - }); - - it('does not show the privacy policy toast when seen, even if on or after the policy date and onboardingDate is before the policy date', () => { - const result = selectShowPrivacyPolicyToast({ - // @ts-expect-error: intentionally passing incomplete input - metamask: { - newPrivacyPolicyToastClickedOrClosed: true, - onboardingDate: new Date(PRIVACY_POLICY_DATE).setDate( - new Date(PRIVACY_POLICY_DATE).getDate() - 2, - ), - }, - }); - expect(result.showPrivacyPolicyToast).toBe(false); - }); - - it('shows the privacy policy toast when not yet seen, on or after the policy date, and onboardingDate is not set', () => { - const result = selectShowPrivacyPolicyToast({ - // @ts-expect-error: intentionally passing incomplete input - metamask: { - newPrivacyPolicyToastClickedOrClosed: false, - onboardingDate: undefined, - }, - }); - expect(result.showPrivacyPolicyToast).toBe(true); - }); - }); - - describe('mock same day', () => { - beforeEach(() => { - dateNowSpy = jest - .spyOn(Date, 'now') - .mockReturnValue(new Date(PRIVACY_POLICY_DATE).getTime()); - }); - - afterEach(() => { - dateNowSpy.mockRestore(); - }); - - it('shows the privacy policy toast when not yet seen, on or after the policy date, and onboardingDate is before the policy date', () => { - const result = selectShowPrivacyPolicyToast({ - // @ts-expect-error: intentionally passing incomplete input - metamask: { - newPrivacyPolicyToastClickedOrClosed: false, - onboardingDate: new Date(PRIVACY_POLICY_DATE).setDate( - new Date(PRIVACY_POLICY_DATE).getDate() - 2, - ), - }, - }); - expect(result.showPrivacyPolicyToast).toBe(true); - }); - - it('does not show the privacy policy toast when seen, even if on or after the policy date and onboardingDate is before the policy date', () => { - const result = selectShowPrivacyPolicyToast({ - // @ts-expect-error: intentionally passing incomplete input - metamask: { - newPrivacyPolicyToastClickedOrClosed: true, - onboardingDate: new Date(PRIVACY_POLICY_DATE).setDate( - new Date(PRIVACY_POLICY_DATE).getDate() - 2, - ), - }, - }); - expect(result.showPrivacyPolicyToast).toBe(false); - }); - - it('shows the privacy policy toast when not yet seen, on or after the policy date, and onboardingDate is not set', () => { - const result = selectShowPrivacyPolicyToast({ - // @ts-expect-error: intentionally passing incomplete input - metamask: { - newPrivacyPolicyToastClickedOrClosed: false, - onboardingDate: undefined, - }, - }); - expect(result.showPrivacyPolicyToast).toBe(true); - }); - }); - - describe('mock day before', () => { - beforeEach(() => { - const dayBeforePolicyDate = new Date(PRIVACY_POLICY_DATE); - dayBeforePolicyDate.setDate(dayBeforePolicyDate.getDate() - 1); - - dateNowSpy = jest - .spyOn(Date, 'now') - .mockReturnValue(dayBeforePolicyDate.getTime()); - }); - - afterEach(() => { - dateNowSpy.mockRestore(); - }); - - it('does not show the privacy policy toast before the policy date', () => { - const result = selectShowPrivacyPolicyToast({ - // @ts-expect-error: intentionally passing incomplete input - metamask: { - newPrivacyPolicyToastClickedOrClosed: false, - onboardingDate: new Date(PRIVACY_POLICY_DATE).setDate( - new Date(PRIVACY_POLICY_DATE).getDate() - 2, - ), - }, - }); - expect(result.showPrivacyPolicyToast).toBe(false); - }); - - it('does not show the privacy policy toast before the policy date even if onboardingDate is not set', () => { - const result = selectShowPrivacyPolicyToast({ - // @ts-expect-error: intentionally passing incomplete input - metamask: { - newPrivacyPolicyToastClickedOrClosed: false, - onboardingDate: undefined, - }, - }); - expect(result.showPrivacyPolicyToast).toBe(false); - }); - }); -}); diff --git a/ui/components/app/toast-master/utils.ts b/ui/components/app/toast-master/utils.ts deleted file mode 100644 index d6544707f45d..000000000000 --- a/ui/components/app/toast-master/utils.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { PayloadAction } from '@reduxjs/toolkit'; -import { ReactFragment } from 'react'; -import { SHOW_NFT_DETECTION_ENABLEMENT_TOAST } from '../../../store/actionConstants'; -import { submitRequestToBackground } from '../../../store/background-connection'; - -/** - * Returns true if the privacy policy toast was shown either never, or less than a day ago. - * - * @param newPrivacyPolicyToastShownDate - * @returns true if the privacy policy toast was shown either never, or less than a day ago - */ -export function getIsPrivacyToastRecent( - newPrivacyPolicyToastShownDate?: number, -): boolean { - if (!newPrivacyPolicyToastShownDate) { - return true; - } - - const currentDate = new Date(); - const oneDayInMilliseconds = 24 * 60 * 60 * 1000; - const newPrivacyPolicyToastShownDateObj = new Date( - newPrivacyPolicyToastShownDate, - ); - const toastWasShownLessThanADayAgo = - currentDate.valueOf() - newPrivacyPolicyToastShownDateObj.valueOf() < - oneDayInMilliseconds; - - return toastWasShownLessThanADayAgo; -} - -export function setNewPrivacyPolicyToastShownDate(time: number) { - submitRequestToBackgroundAndCatch('setNewPrivacyPolicyToastShownDate', [ - time, - ]); -} - -export function setNewPrivacyPolicyToastClickedOrClosed() { - submitRequestToBackgroundAndCatch('setNewPrivacyPolicyToastClickedOrClosed'); -} - -export function setShowNftDetectionEnablementToast( - value: boolean, -): PayloadAction { - return { - type: SHOW_NFT_DETECTION_ENABLEMENT_TOAST, - payload: value, - }; -} - -export function setSwitchedNetworkNeverShowMessage() { - submitRequestToBackgroundAndCatch('setSwitchedNetworkNeverShowMessage', [ - true, - ]); -} - -export function setSurveyLinkLastClickedOrClosed(time: number) { - submitRequestToBackgroundAndCatch('setSurveyLinkLastClickedOrClosed', [time]); -} - -// May move this to a different file after discussion with team -export function submitRequestToBackgroundAndCatch( - method: string, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - args?: any[], -) { - submitRequestToBackground(method, args)?.catch((error) => { - console.error('Error caught in submitRequestToBackground', error); - }); -} diff --git a/ui/components/app/transaction-list-item-details/transaction-list-item-details.component.js b/ui/components/app/transaction-list-item-details/transaction-list-item-details.component.js index 226a2a9113c0..751b0f53e73a 100644 --- a/ui/components/app/transaction-list-item-details/transaction-list-item-details.component.js +++ b/ui/components/app/transaction-list-item-details/transaction-list-item-details.component.js @@ -214,7 +214,7 @@ export default class TransactionListItemDetails extends PureComponent { primaryTransaction: transaction, initialTransaction: { type }, } = transactionGroup; - const { chainId, hash } = transaction; + const { hash } = transaction; return ( @@ -332,7 +332,6 @@ export default class TransactionListItemDetails extends PureComponent { recipientMetadataName={recipientMetadataName} senderName={senderNickname} senderAddress={senderAddress} - chainId={chainId} onRecipientClick={() => { this.context.trackEvent({ category: MetaMetricsEventCategory.Navigation, diff --git a/ui/components/app/user-preferenced-currency-display/__snapshots__/user-preferenced-currency-display.test.js.snap b/ui/components/app/user-preferenced-currency-display/__snapshots__/user-preferenced-currency-display.test.js.snap index 4a9fc4d3cf7a..b29efce542e3 100644 --- a/ui/components/app/user-preferenced-currency-display/__snapshots__/user-preferenced-currency-display.test.js.snap +++ b/ui/components/app/user-preferenced-currency-display/__snapshots__/user-preferenced-currency-display.test.js.snap @@ -8,7 +8,6 @@ exports[`UserPreferencedCurrencyDisplay Component rendering should match snapsho > 0 diff --git a/ui/components/app/user-preferenced-currency-display/user-preferenced-currency-display.component.d.ts b/ui/components/app/user-preferenced-currency-display/user-preferenced-currency-display.component.d.ts index 779309858a18..4db61d568f4a 100644 --- a/ui/components/app/user-preferenced-currency-display/user-preferenced-currency-display.component.d.ts +++ b/ui/components/app/user-preferenced-currency-display/user-preferenced-currency-display.component.d.ts @@ -16,7 +16,6 @@ export type UserPrefrencedCurrencyDisplayProps = OverridingUnion< showCurrencySuffix?: boolean; shouldCheckShowNativeToken?: boolean; isAggregatedFiatOverviewBalance?: boolean; - privacyMode?: boolean; } >; diff --git a/ui/components/app/user-preferenced-currency-display/user-preferenced-currency-display.component.js b/ui/components/app/user-preferenced-currency-display/user-preferenced-currency-display.component.js index a466f7813672..613b731d0a16 100644 --- a/ui/components/app/user-preferenced-currency-display/user-preferenced-currency-display.component.js +++ b/ui/components/app/user-preferenced-currency-display/user-preferenced-currency-display.component.js @@ -28,7 +28,6 @@ export default function UserPreferencedCurrencyDisplay({ showNative, showCurrencySuffix, shouldCheckShowNativeToken, - privacyMode = false, ...restProps }) { // NOTE: When displaying currencies, we need the actual account to detect whether we're in a @@ -84,7 +83,6 @@ export default function UserPreferencedCurrencyDisplay({ numberOfDecimals={numberOfDecimals} prefixComponent={prefixComponent} suffix={showCurrencySuffix && !showEthLogo && currency} - privacyMode={privacyMode} /> ); } @@ -128,7 +126,6 @@ const UserPreferencedCurrencyDisplayPropTypes = { textProps: PropTypes.object, suffixProps: PropTypes.object, shouldCheckShowNativeToken: PropTypes.bool, - privacyMode: PropTypes.bool, }; UserPreferencedCurrencyDisplay.propTypes = diff --git a/ui/components/app/wallet-overview/aggregated-percentage-overview.test.tsx b/ui/components/app/wallet-overview/aggregated-percentage-overview.test.tsx index 8da096151908..95e0d92fa2b8 100644 --- a/ui/components/app/wallet-overview/aggregated-percentage-overview.test.tsx +++ b/ui/components/app/wallet-overview/aggregated-percentage-overview.test.tsx @@ -7,7 +7,6 @@ import { getSelectedAccount, getShouldHideZeroBalanceTokens, getTokensMarketData, - getPreferences, } from '../../../selectors'; import { useAccountTotalFiatBalance } from '../../../hooks/useAccountTotalFiatBalance'; import { AggregatedPercentageOverview } from './aggregated-percentage-overview'; @@ -23,7 +22,6 @@ jest.mock('../../../ducks/locale/locale', () => ({ jest.mock('../../../selectors', () => ({ getCurrentCurrency: jest.fn(), getSelectedAccount: jest.fn(), - getPreferences: jest.fn(), getShouldHideZeroBalanceTokens: jest.fn(), getTokensMarketData: jest.fn(), })); @@ -34,7 +32,6 @@ jest.mock('../../../hooks/useAccountTotalFiatBalance', () => ({ const mockGetIntlLocale = getIntlLocale as unknown as jest.Mock; const mockGetCurrentCurrency = getCurrentCurrency as jest.Mock; -const mockGetPreferences = getPreferences as jest.Mock; const mockGetSelectedAccount = getSelectedAccount as unknown as jest.Mock; const mockGetShouldHideZeroBalanceTokens = getShouldHideZeroBalanceTokens as jest.Mock; @@ -162,7 +159,6 @@ describe('AggregatedPercentageOverview', () => { beforeEach(() => { mockGetIntlLocale.mockReturnValue('en-US'); mockGetCurrentCurrency.mockReturnValue('USD'); - mockGetPreferences.mockReturnValue({ privacyMode: false }); mockGetSelectedAccount.mockReturnValue(selectedAccountMock); mockGetShouldHideZeroBalanceTokens.mockReturnValue(false); mockGetTokensMarketData.mockReturnValue(marketDataMock); diff --git a/ui/components/app/wallet-overview/aggregated-percentage-overview.tsx b/ui/components/app/wallet-overview/aggregated-percentage-overview.tsx index 8c609610daa1..94555d3bc0cd 100644 --- a/ui/components/app/wallet-overview/aggregated-percentage-overview.tsx +++ b/ui/components/app/wallet-overview/aggregated-percentage-overview.tsx @@ -7,7 +7,6 @@ import { getSelectedAccount, getShouldHideZeroBalanceTokens, getTokensMarketData, - getPreferences, } from '../../../selectors'; import { useAccountTotalFiatBalance } from '../../../hooks/useAccountTotalFiatBalance'; @@ -20,7 +19,7 @@ import { TextColor, TextVariant, } from '../../../helpers/constants/design-system'; -import { Box, SensitiveText } from '../../component-library'; +import { Box, Text } from '../../component-library'; import { getCalculatedTokenAmount1dAgo } from '../../../helpers/utils/util'; // core already has this exported type but its not yet available in this version @@ -35,7 +34,6 @@ export const AggregatedPercentageOverview = () => { useSelector(getTokensMarketData); const locale = useSelector(getIntlLocale); const fiatCurrency = useSelector(getCurrentCurrency); - const { privacyMode } = useSelector(getPreferences); const selectedAccount = useSelector(getSelectedAccount); const shouldHideZeroBalanceTokens = useSelector( getShouldHideZeroBalanceTokens, @@ -112,7 +110,7 @@ export const AggregatedPercentageOverview = () => { let color = TextColor.textDefault; - if (!privacyMode && isValidAmount(amountChange)) { + if (isValidAmount(amountChange)) { if ((amountChange as number) === 0) { color = TextColor.textDefault; } else if ((amountChange as number) > 0) { @@ -120,33 +118,26 @@ export const AggregatedPercentageOverview = () => { } else { color = TextColor.errorDefault; } - } else { - color = TextColor.textAlternative; } - return ( - {formattedAmountChange} - - + {formattedPercentChange} - + ); }; diff --git a/ui/components/app/wallet-overview/coin-overview.tsx b/ui/components/app/wallet-overview/coin-overview.tsx index 93d9e1061428..2de787ef23c0 100644 --- a/ui/components/app/wallet-overview/coin-overview.tsx +++ b/ui/components/app/wallet-overview/coin-overview.tsx @@ -28,7 +28,6 @@ import { JustifyContent, TextAlign, TextVariant, - IconColor, } from '../../../helpers/constants/design-system'; ///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask) import { getPortfolioUrl } from '../../../helpers/utils/portfolio'; @@ -62,10 +61,7 @@ import Spinner from '../../ui/spinner'; import { PercentageAndAmountChange } from '../../multichain/token-list-item/price/percentage-and-amount-change/percentage-and-amount-change'; import { getMultichainIsEvm } from '../../../selectors/multichain'; import { useAccountTotalFiatBalance } from '../../../hooks/useAccountTotalFiatBalance'; -import { - setAggregatedBalancePopoverShown, - setPrivacyMode, -} from '../../../store/actions'; +import { setAggregatedBalancePopoverShown } from '../../../store/actions'; import { useTheme } from '../../../hooks/useTheme'; import { getSpecificSettingsRoute } from '../../../helpers/utils/settings-search'; import { useI18nContext } from '../../../hooks/useI18nContext'; @@ -132,8 +128,7 @@ export const CoinOverview = ({ const shouldShowPopover = useSelector(getShouldShowAggregatedBalancePopover); const isTestnet = useSelector(getIsTestnet); - const { showFiatInTestnets, privacyMode, showNativeTokenAsMainBalance } = - useSelector(getPreferences); + const { showFiatInTestnets } = useSelector(getPreferences); const selectedAccount = useSelector(getSelectedAccount); const shouldHideZeroBalanceTokens = useSelector( @@ -144,6 +139,8 @@ export const CoinOverview = ({ shouldHideZeroBalanceTokens, ); + const { showNativeTokenAsMainBalance } = useSelector(getPreferences); + const isEvm = useSelector(getMultichainIsEvm); const isNotAggregatedFiatBalance = showNativeTokenAsMainBalance || isTestnet || !isEvm; @@ -166,10 +163,6 @@ export const CoinOverview = ({ dispatch(setAggregatedBalancePopoverShown()); }; - const handleSensitiveToggle = () => { - dispatch(setPrivacyMode(!privacyMode)); - }; - const [referenceElement, setReferenceElement] = useState(null); const setBoxRef = (ref: HTMLSpanElement | null) => { @@ -260,39 +253,26 @@ export const CoinOverview = ({ ref={setBoxRef} > {balanceToDisplay ? ( - <> - - - + ) : ( )} diff --git a/ui/components/app/wallet-overview/index.scss b/ui/components/app/wallet-overview/index.scss index 47dc40200e69..318c26501097 100644 --- a/ui/components/app/wallet-overview/index.scss +++ b/ui/components/app/wallet-overview/index.scss @@ -78,8 +78,7 @@ display: flex; max-width: inherit; justify-content: center; - align-items: center; - flex-wrap: nowrap; + flex-wrap: wrap; } &__primary-balance { @@ -143,8 +142,7 @@ display: flex; max-width: inherit; justify-content: center; - align-items: center; - flex-wrap: nowrap; + flex-wrap: wrap; } &__primary-balance { diff --git a/ui/components/component-library/icon/icon.types.ts b/ui/components/component-library/icon/icon.types.ts index 3afd17ef983b..9c87851d0b65 100644 --- a/ui/components/component-library/icon/icon.types.ts +++ b/ui/components/component-library/icon/icon.types.ts @@ -44,7 +44,6 @@ export enum IconName { Book = 'book', Bookmark = 'bookmark', Bridge = 'bridge', - Collapse = 'collapse', Calculator = 'calculator', CardPos = 'card-pos', CardToken = 'card-token', diff --git a/ui/components/component-library/index.ts b/ui/components/component-library/index.ts index 634af093a41b..861fb80bcf2c 100644 --- a/ui/components/component-library/index.ts +++ b/ui/components/component-library/index.ts @@ -69,8 +69,6 @@ export { TagUrl } from './tag-url'; export type { TagUrlProps } from './tag-url'; export { Text, ValidTag, TextDirection, InvisibleCharacter } from './text'; export type { TextProps } from './text'; -export { SensitiveText, SensitiveTextLength } from './sensitive-text'; -export type { SensitiveTextProps } from './sensitive-text'; export { Input, InputType } from './input'; export type { InputProps } from './input'; export { TextField, TextFieldType, TextFieldSize } from './text-field'; diff --git a/ui/components/component-library/sensitive-text/README.mdx b/ui/components/component-library/sensitive-text/README.mdx deleted file mode 100644 index 9e950381e6f3..000000000000 --- a/ui/components/component-library/sensitive-text/README.mdx +++ /dev/null @@ -1,81 +0,0 @@ -import { Controls, Canvas } from '@storybook/blocks'; - -import * as SensitiveTextStories from './sensitive-text.stories'; - -# SensitiveText - -SensitiveText is a component that extends the Text component to handle sensitive information. It provides the ability to hide or show the text content, replacing it with dots when hidden. - - - -## Props - -The `SensitiveText` component extends the `Text` component. See the `Text` component for an extended list of props. - - - -### Children - -The text content to be displayed or hidden. - - - -```jsx -import { SensitiveText } from '../../component-library'; - - - Sensitive Information - -``` - - -### IsHidden - -Use the `isHidden` prop to determine whether the text should be hidden or visible. When `isHidden` is `true`, the component will display dots instead of the actual text. - - - -```jsx -import { SensitiveText } from '../../component-library'; - - - Sensitive Information - -``` - -### Length - -Use the `length` prop to determine the length of the hidden text (number of dots). Can be a predefined `SensitiveTextLength` or a custom string number. - -The following predefined length options are available: - -- `SensitiveTextLength.Short`: `6` -- `SensitiveTextLength.Medium`: `9` -- `SensitiveTextLength.Long`: `12` -- `SensitiveTextLength.ExtraLong`: `20` - -- The number of dots displayed is determined by the `length` prop. -- If an invalid `length` is provided, the component will fall back to `SensitiveTextLength.Short` and log a warning. -- Custom length values can be provided as strings, e.g. `15`. - - - -```jsx -import { SensitiveText, SensitiveTextLength } from '../../component-library'; - - - Length "short" (6 characters) - - - Length "medium" (9 characters) - - - Length "long" (12 characters) - - - Length "extra long" (20 characters) - - - Length "15" (15 characters) - -``` diff --git a/ui/components/component-library/sensitive-text/__snapshots__/sensitive-text.test.tsx.snap b/ui/components/component-library/sensitive-text/__snapshots__/sensitive-text.test.tsx.snap deleted file mode 100644 index 6844feb1783e..000000000000 --- a/ui/components/component-library/sensitive-text/__snapshots__/sensitive-text.test.tsx.snap +++ /dev/null @@ -1,11 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`SensitiveText should render correctly 1`] = ` -
-

- Sensitive Information -

-
-`; diff --git a/ui/components/component-library/sensitive-text/index.ts b/ui/components/component-library/sensitive-text/index.ts deleted file mode 100644 index ff89896fd03b..000000000000 --- a/ui/components/component-library/sensitive-text/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export { SensitiveText } from './sensitive-text'; -export { SensitiveTextLength } from './sensitive-text.types'; -export type { SensitiveTextProps } from './sensitive-text.types'; diff --git a/ui/components/component-library/sensitive-text/sensitive-text.stories.tsx b/ui/components/component-library/sensitive-text/sensitive-text.stories.tsx deleted file mode 100644 index 142def9118b5..000000000000 --- a/ui/components/component-library/sensitive-text/sensitive-text.stories.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react'; -import React from 'react'; -import { SensitiveText } from '.'; -import { SensitiveTextLength } from './sensitive-text.types'; -import README from './README.mdx'; -import { Box } from '../box'; -import { - Display, - FlexDirection, -} from '../../../helpers/constants/design-system'; - -const meta: Meta = { - title: 'Components/ComponentLibrary/SensitiveText', - component: SensitiveText, - parameters: { - docs: { - page: README, - }, - }, - args: { - children: 'Sensitive information', - isHidden: false, - length: SensitiveTextLength.Short, - }, -} as Meta; - -export default meta; -type Story = StoryObj; - -export const DefaultStory: Story = {}; -DefaultStory.storyName = 'Default'; - -export const Children: Story = { - args: { - children: 'Sensitive information', - }, - render: (args) => ( - - ), -}; - -export const IsHidden: Story = { - args: { - isHidden: true, - }, - render: (args) => ( - - ), -}; - -export const Length: Story = { - args: { - isHidden: true, - }, - render: (args) => ( - - - Length "short" (6 characters) - - - Length "medium" (9 characters) - - - Length "long" (12 characters) - - - Length "extra long" (20 characters) - - - Length "15" (15 characters) - - - ), -}; diff --git a/ui/components/component-library/sensitive-text/sensitive-text.test.tsx b/ui/components/component-library/sensitive-text/sensitive-text.test.tsx deleted file mode 100644 index a4be911ea78d..000000000000 --- a/ui/components/component-library/sensitive-text/sensitive-text.test.tsx +++ /dev/null @@ -1,81 +0,0 @@ -import React from 'react'; -import { render, screen } from '@testing-library/react'; -import { SensitiveText } from './sensitive-text'; -import { SensitiveTextLength } from './sensitive-text.types'; - -describe('SensitiveText', () => { - const testProps = { - isHidden: false, - length: SensitiveTextLength.Short, - children: 'Sensitive Information', - }; - - it('should render correctly', () => { - const { container } = render(); - expect(container).toMatchSnapshot(); - }); - - it('should display the text when isHidden is false', () => { - render(); - expect(screen.getByText('Sensitive Information')).toBeInTheDocument(); - }); - - it('should hide the text when isHidden is true', () => { - render(); - expect(screen.queryByText('Sensitive Information')).not.toBeInTheDocument(); - expect(screen.getByText('••••••')).toBeInTheDocument(); - }); - - it('should render the correct number of bullets for different lengths', () => { - const lengths = [ - SensitiveTextLength.Short, - SensitiveTextLength.Medium, - SensitiveTextLength.Long, - SensitiveTextLength.ExtraLong, - ]; - - lengths.forEach((length) => { - render(); - expect(screen.getByText('•'.repeat(Number(length)))).toBeInTheDocument(); - }); - }); - - it('should handle all predefined SensitiveTextLength values', () => { - Object.entries(SensitiveTextLength).forEach(([_, value]) => { - render(); - expect(screen.getByText('•'.repeat(Number(value)))).toBeInTheDocument(); - }); - }); - - it('should handle custom length as a string', () => { - render(); - expect(screen.getByText('•'.repeat(15))).toBeInTheDocument(); - }); - - it('should fall back to Short length for invalid custom length', () => { - render(); - expect( - screen.getByText('•'.repeat(Number(SensitiveTextLength.Short))), - ).toBeInTheDocument(); - }); - - it('should log a warning for invalid custom length', () => { - const consoleSpy = jest.spyOn(console, 'warn').mockImplementation(); - render(); - expect(consoleSpy).toHaveBeenCalledWith( - 'Invalid length provided: abc. Falling back to Short.', - ); - consoleSpy.mockRestore(); - }); - - it('should apply additional props to the Text component', () => { - render(); - expect(screen.getByTestId('sensitive-text')).toBeInTheDocument(); - }); - - it('should forward ref to the Text component', () => { - const ref = React.createRef(); - render(); - expect(ref.current).toBeInstanceOf(HTMLParagraphElement); - }); -}); diff --git a/ui/components/component-library/sensitive-text/sensitive-text.tsx b/ui/components/component-library/sensitive-text/sensitive-text.tsx deleted file mode 100644 index ddabda784fe1..000000000000 --- a/ui/components/component-library/sensitive-text/sensitive-text.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import React, { useMemo } from 'react'; -import { Text } from '../text'; -import { - SensitiveTextProps, - SensitiveTextLength, -} from './sensitive-text.types'; - -export const SensitiveText = React.forwardRef< - HTMLParagraphElement, - SensitiveTextProps ->((props, ref) => { - const { - isHidden = false, - length = SensitiveTextLength.Short, - children = '', - ...restProps - } = props; - - const getFallbackLength = useMemo( - () => (len: string) => { - const numLength = Number(len); - return Number.isNaN(numLength) ? 0 : numLength; - }, - [], - ); - - const isValidCustomLength = (value: string): boolean => { - const num = Number(value); - return !Number.isNaN(num) && num > 0; - }; - - let adjustedLength = length; - if (!(length in SensitiveTextLength) && !isValidCustomLength(length)) { - console.warn(`Invalid length provided: ${length}. Falling back to Short.`); - adjustedLength = SensitiveTextLength.Short; - } - - const fallback = useMemo( - () => '•'.repeat(getFallbackLength(adjustedLength)), - [length, getFallbackLength], - ); - - return ( - - {isHidden ? fallback : children} - - ); -}); diff --git a/ui/components/component-library/sensitive-text/sensitive-text.types.ts b/ui/components/component-library/sensitive-text/sensitive-text.types.ts deleted file mode 100644 index 1ea8270d377f..000000000000 --- a/ui/components/component-library/sensitive-text/sensitive-text.types.ts +++ /dev/null @@ -1,44 +0,0 @@ -import type { TextProps } from '../text/text.types'; - -/** - * SensitiveText length options. - */ -export const SensitiveTextLength = { - Short: '6', - Medium: '9', - Long: '12', - ExtraLong: '20', -} as const; - -/** - * Type for SensitiveTextLength values. - */ -export type SensitiveTextLengthType = - (typeof SensitiveTextLength)[keyof typeof SensitiveTextLength]; -/** - * Type for custom length values. - */ -export type CustomLength = string; - -export type SensitiveTextProps = Omit< - TextProps, - 'children' -> & { - /** - * Boolean to determine whether the text should be hidden or visible. - * - * @default false - */ - isHidden?: boolean; - /** - * Determines the length of the hidden text (number of asterisks). - * Can be a predefined SensitiveTextLength or a custom string number. - * - * @default SensitiveTextLength.Short - */ - length?: SensitiveTextLengthType | CustomLength; - /** - * The text content to be displayed or hidden. - */ - children?: React.ReactNode; -}; diff --git a/ui/components/institutional/interactive-replacement-token-modal/interactive-replacement-token-modal.tsx b/ui/components/institutional/interactive-replacement-token-modal/interactive-replacement-token-modal.tsx index e76dabc7add7..06fe1336ca7e 100644 --- a/ui/components/institutional/interactive-replacement-token-modal/interactive-replacement-token-modal.tsx +++ b/ui/components/institutional/interactive-replacement-token-modal/interactive-replacement-token-modal.tsx @@ -4,7 +4,7 @@ import { ICustodianType } from '@metamask-institutional/types'; import { useI18nContext } from '../../../hooks/useI18nContext'; import { MetaMetricsContext } from '../../../contexts/metametrics'; import { hideModal } from '../../../store/actions'; -import { getSelectedInternalAccount } from '../../../selectors/accounts'; +import { getSelectedInternalAccount } from '../../../selectors/selectors'; import { toChecksumHexAddress } from '../../../../shared/modules/hexstring-utils'; import { Box, diff --git a/ui/components/institutional/interactive-replacement-token-notification/interactive-replacement-token-notification.tsx b/ui/components/institutional/interactive-replacement-token-notification/interactive-replacement-token-notification.tsx index 7c1d0f60488f..10dc049b8678 100644 --- a/ui/components/institutional/interactive-replacement-token-notification/interactive-replacement-token-notification.tsx +++ b/ui/components/institutional/interactive-replacement-token-notification/interactive-replacement-token-notification.tsx @@ -56,7 +56,6 @@ const InteractiveReplacementTokenNotification: React.FC< interactiveReplacementToken && Boolean(Object.keys(interactiveReplacementToken).length); - // @ts-expect-error keyring type is wrong maybe? if (!/^Custody/u.test(keyring.type) || !hasInteractiveReplacementToken) { setShowNotification(false); return; @@ -67,7 +66,6 @@ const InteractiveReplacementTokenNotification: React.FC< )) as unknown as string; const custodyAccountDetails = await dispatch( mmiActions.getAllCustodianAccountsWithToken( - // @ts-expect-error keyring type is wrong maybe? keyring.type.split(' - ')[1], token, ), @@ -107,7 +105,6 @@ const InteractiveReplacementTokenNotification: React.FC< interactiveReplacementToken?.oldRefreshToken, isUnlocked, dispatch, - // @ts-expect-error keyring type is wrong maybe? keyring.type, interactiveReplacementToken, mmiActions, diff --git a/ui/components/multichain/account-list-item/__snapshots__/account-list-item.test.js.snap b/ui/components/multichain/account-list-item/__snapshots__/account-list-item.test.js.snap index e320bd1de0e3..51f6f2e905f9 100644 --- a/ui/components/multichain/account-list-item/__snapshots__/account-list-item.test.js.snap +++ b/ui/components/multichain/account-list-item/__snapshots__/account-list-item.test.js.snap @@ -242,7 +242,6 @@ exports[`AccountListItem renders AccountListItem component and shows account nam > $100,000.00 @@ -539,7 +538,6 @@ exports[`AccountListItem renders AccountListItem component and shows account nam > 0.006 @@ -583,7 +581,6 @@ exports[`AccountListItem renders AccountListItem component and shows account nam > 0.006 diff --git a/ui/components/multichain/account-list-item/account-list-item.js b/ui/components/multichain/account-list-item/account-list-item.js index 143c4d142a16..517639b1c86e 100644 --- a/ui/components/multichain/account-list-item/account-list-item.js +++ b/ui/components/multichain/account-list-item/account-list-item.js @@ -86,8 +86,6 @@ const AccountListItem = ({ isActive = false, startAccessory, onActionClick, - shouldScrollToWhenSelected = true, - privacyMode = false, }) => { const t = useI18nContext(); const [accountOptionsMenuOpen, setAccountOptionsMenuOpen] = useState(false); @@ -130,10 +128,10 @@ const AccountListItem = ({ // scroll the item into view const itemRef = useRef(null); useEffect(() => { - if (selected && shouldScrollToWhenSelected) { + if (selected) { itemRef.current?.scrollIntoView?.(); } - }, [itemRef, selected, shouldScrollToWhenSelected]); + }, [itemRef, selected]); const trackEvent = useContext(MetaMetricsContext); const primaryTokenImage = useMultichainSelector( @@ -314,7 +312,6 @@ const AccountListItem = ({ type={PRIMARY} showFiat={showFiat} data-testid="first-currency-display" - privacyMode={privacyMode} />
@@ -362,7 +359,6 @@ const AccountListItem = ({ type={SECONDARY} showNative data-testid="second-currency-display" - privacyMode={privacyMode} /> @@ -506,14 +502,6 @@ AccountListItem.propTypes = { * Represents start accessory */ startAccessory: PropTypes.node, - /** - * Determines if list item should be scrolled to when selected - */ - shouldScrollToWhenSelected: PropTypes.bool, - /** - * Determines if list balance should be obfuscated - */ - privacyMode: PropTypes.bool, }; AccountListItem.displayName = 'AccountListItem'; diff --git a/ui/components/multichain/account-list-menu/account-list-menu.tsx b/ui/components/multichain/account-list-menu/account-list-menu.tsx index cfb49d246ca6..19d313aedf54 100644 --- a/ui/components/multichain/account-list-menu/account-list-menu.tsx +++ b/ui/components/multichain/account-list-menu/account-list-menu.tsx @@ -188,7 +188,6 @@ export const mergeAccounts = ( type AccountListMenuProps = { onClose: () => void; - privacyMode?: boolean; showAccountCreation?: boolean; accountListItemProps?: object; allowedAccountTypes?: KeyringAccountType[]; @@ -196,7 +195,6 @@ type AccountListMenuProps = { export const AccountListMenu = ({ onClose, - privacyMode = false, showAccountCreation = true, accountListItemProps, allowedAccountTypes = [ @@ -457,7 +455,6 @@ export const AccountListMenu = ({ { trackEvent({ category: MetaMetricsEventCategory.Navigation, @@ -646,7 +643,6 @@ export const AccountListMenu = ({ isHidden={Boolean(account.hidden)} currentTabOrigin={currentTabOrigin} isActive={Boolean(account.active)} - privacyMode={privacyMode} {...accountListItemProps} /> diff --git a/ui/components/multichain/app-header/__snapshots__/app-header.test.js.snap b/ui/components/multichain/app-header/__snapshots__/app-header.test.js.snap index 247f7aeb5c78..d22597edd89f 100644 --- a/ui/components/multichain/app-header/__snapshots__/app-header.test.js.snap +++ b/ui/components/multichain/app-header/__snapshots__/app-header.test.js.snap @@ -616,7 +616,6 @@ exports[`App Header unlocked state matches snapshot: unlocked 1`] = ` >

1

diff --git a/ui/components/multichain/asset-picker-amount/asset-balance/__snapshots__/asset-balance-text.test.tsx.snap b/ui/components/multichain/asset-picker-amount/asset-balance/__snapshots__/asset-balance-text.test.tsx.snap index a0c808186082..9c0bd9c49482 100644 --- a/ui/components/multichain/asset-picker-amount/asset-balance/__snapshots__/asset-balance-text.test.tsx.snap +++ b/ui/components/multichain/asset-picker-amount/asset-balance/__snapshots__/asset-balance-text.test.tsx.snap @@ -8,7 +8,6 @@ exports[`AssetBalanceText matches snapshot 1`] = ` > prefix-fiat value diff --git a/ui/components/multichain/connect-accounts-modal/__snapshots__/connect-accounts-modal.test.tsx.snap b/ui/components/multichain/connect-accounts-modal/__snapshots__/connect-accounts-modal.test.tsx.snap index b4a4836db2d6..d53c8e7d8d8a 100644 --- a/ui/components/multichain/connect-accounts-modal/__snapshots__/connect-accounts-modal.test.tsx.snap +++ b/ui/components/multichain/connect-accounts-modal/__snapshots__/connect-accounts-modal.test.tsx.snap @@ -358,7 +358,6 @@ exports[`Connect More Accounts Modal should render correctly 1`] = ` > 0 @@ -402,7 +401,6 @@ exports[`Connect More Accounts Modal should render correctly 1`] = ` > 0 diff --git a/ui/components/multichain/funding-method-modal/funding-method-modal.test.tsx b/ui/components/multichain/funding-method-modal/funding-method-modal.test.tsx index 34ec98e671b9..509a4aa60a2a 100644 --- a/ui/components/multichain/funding-method-modal/funding-method-modal.test.tsx +++ b/ui/components/multichain/funding-method-modal/funding-method-modal.test.tsx @@ -57,7 +57,7 @@ describe('FundingMethodModal', () => { expect(queryByTestId('funding-method-modal')).toBeNull(); }); - it('should call openBuyCryptoInPdapp when the Token Marketplace item is clicked', () => { + it('should call openBuyCryptoInPdapp when the Buy Crypto item is clicked', () => { const { getByText } = renderWithProvider( { store, ); - fireEvent.click(getByText('Token marketplace')); + fireEvent.click(getByText('Buy crypto')); expect(openBuyCryptoInPdapp).toHaveBeenCalled(); }); diff --git a/ui/components/multichain/funding-method-modal/funding-method-modal.tsx b/ui/components/multichain/funding-method-modal/funding-method-modal.tsx index baa0e234a32a..47d6ed22c2e8 100644 --- a/ui/components/multichain/funding-method-modal/funding-method-modal.tsx +++ b/ui/components/multichain/funding-method-modal/funding-method-modal.tsx @@ -115,8 +115,8 @@ export const FundingMethodModal: React.FC = ({ { handleNotificationsClick()} - data-testid="notifications-menu-item" > void }) => { const generateNetworkListItem = (network: NetworkConfiguration) => { const isCurrentNetwork = network.chainId === currentChainId; const canDeleteNetwork = - isUnlocked && !isCurrentNetwork && network.chainId !== CHAIN_IDS.MAINNET; + isUnlocked && + !isCurrentNetwork && + network.chainId !== CHAIN_IDS.MAINNET && + network.chainId !== CHAIN_IDS.LINEA_MAINNET; return ( {notificationsUnreadCount > 10 ? '9+' : notificationsUnreadCount} diff --git a/ui/components/multichain/pages/connections/__snapshots__/connections.test.tsx.snap b/ui/components/multichain/pages/connections/__snapshots__/connections.test.tsx.snap index ad2dc490d7c0..afd02098086f 100644 --- a/ui/components/multichain/pages/connections/__snapshots__/connections.test.tsx.snap +++ b/ui/components/multichain/pages/connections/__snapshots__/connections.test.tsx.snap @@ -297,7 +297,6 @@ exports[`Connections Content should render correctly 1`] = ` > 966.988 @@ -341,7 +340,6 @@ exports[`Connections Content should render correctly 1`] = ` > 966.988 diff --git a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx index d5ca0b816d48..ae7a93283ead 100644 --- a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx +++ b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx @@ -82,15 +82,6 @@ export const SiteCell: React.FC = ({ ]) : t('requestingFor'); - const networkMessageConnectedState = - selectedChainIdsLength === 1 - ? t('connectedWithNetworkName', [selectedNetworks[0].name]) - : t('connectedWithNetwork', [selectedChainIdsLength]); - const networkMessageNotConnectedState = - selectedChainIdsLength === 1 - ? t('requestingForNetwork', [selectedNetworks[0].name]) - : t('requestingFor'); - return ( <> = ({ setShowEditAccountsModal(true); trackEvent({ category: MetaMetricsEventCategory.Navigation, - event: MetaMetricsEventName.ViewPermissionedAccounts, + event: MetaMetricsEventName.TokenImportButtonClicked, properties: { location: 'Connect view, Permissions toast, Permissions (dapp)', }, @@ -133,14 +124,16 @@ export const SiteCell: React.FC = ({ { setShowEditNetworksModal(true); trackEvent({ category: MetaMetricsEventCategory.Navigation, - event: MetaMetricsEventName.ViewPermissionedNetworks, + event: MetaMetricsEventName.TokenImportButtonClicked, properties: { location: 'Connect view, Permissions toast, Permissions (dapp)', }, diff --git a/ui/components/multichain/pages/send/__snapshots__/send.test.js.snap b/ui/components/multichain/pages/send/__snapshots__/send.test.js.snap index 7b0605b7ea60..814dc934fc9a 100644 --- a/ui/components/multichain/pages/send/__snapshots__/send.test.js.snap +++ b/ui/components/multichain/pages/send/__snapshots__/send.test.js.snap @@ -474,7 +474,6 @@ exports[`SendPage render and initialization should render correctly even when a > $0.00 @@ -518,7 +517,6 @@ exports[`SendPage render and initialization should render correctly even when a > 0 diff --git a/ui/components/multichain/pages/send/components/__snapshots__/your-accounts.test.tsx.snap b/ui/components/multichain/pages/send/components/__snapshots__/your-accounts.test.tsx.snap index 71431a330f94..9fbf7e29879b 100644 --- a/ui/components/multichain/pages/send/components/__snapshots__/your-accounts.test.tsx.snap +++ b/ui/components/multichain/pages/send/components/__snapshots__/your-accounts.test.tsx.snap @@ -248,7 +248,6 @@ exports[`SendPageYourAccounts render renders correctly 1`] = ` > 966.988 @@ -292,7 +291,6 @@ exports[`SendPageYourAccounts render renders correctly 1`] = ` > 966.988 @@ -547,7 +545,6 @@ exports[`SendPageYourAccounts render renders correctly 1`] = ` > 0 @@ -591,7 +588,6 @@ exports[`SendPageYourAccounts render renders correctly 1`] = ` > 0 @@ -846,7 +842,6 @@ exports[`SendPageYourAccounts render renders correctly 1`] = ` > 0 @@ -890,7 +885,6 @@ exports[`SendPageYourAccounts render renders correctly 1`] = ` > 0 @@ -1154,7 +1148,6 @@ exports[`SendPageYourAccounts render renders correctly 1`] = ` > 0 @@ -1198,7 +1191,6 @@ exports[`SendPageYourAccounts render renders correctly 1`] = ` > 0 @@ -1453,7 +1445,6 @@ exports[`SendPageYourAccounts render renders correctly 1`] = ` > 0 @@ -1497,7 +1488,6 @@ exports[`SendPageYourAccounts render renders correctly 1`] = ` > 0 @@ -1765,7 +1755,6 @@ exports[`SendPageYourAccounts render renders correctly 1`] = ` > 0 @@ -1809,7 +1798,6 @@ exports[`SendPageYourAccounts render renders correctly 1`] = ` > 0 diff --git a/ui/components/multichain/pages/send/components/your-accounts.tsx b/ui/components/multichain/pages/send/components/your-accounts.tsx index e59d0aa2d5a1..f53d6603cb78 100644 --- a/ui/components/multichain/pages/send/components/your-accounts.tsx +++ b/ui/components/multichain/pages/send/components/your-accounts.tsx @@ -57,7 +57,6 @@ export const SendPageYourAccounts = ({ selected={selectedAccount.address === account.address} key={account.address} isPinned={Boolean(account.pinned)} - shouldScrollToWhenSelected={false} onClick={() => { dispatch( addHistoryEntry( diff --git a/ui/components/multichain/ramps-card/ramps-card.js b/ui/components/multichain/ramps-card/ramps-card.js index 5fb91272ff83..ac72f4c5112f 100644 --- a/ui/components/multichain/ramps-card/ramps-card.js +++ b/ui/components/multichain/ramps-card/ramps-card.js @@ -30,6 +30,7 @@ const darkenGradient = export const RAMPS_CARD_VARIANT_TYPES = { TOKEN: 'token', + NFT: 'nft', ACTIVITY: 'activity', BTC: 'btc', }; @@ -40,8 +41,15 @@ export const RAMPS_CARD_VARIANTS = { gradient: // eslint-disable-next-line @metamask/design-tokens/color-no-hex 'linear-gradient(90deg, #0189EC 0%, #4B7AED 35%, #6774EE 58%, #706AF4 80.5%, #7C5BFC 100%)', - title: 'tipsForUsingAWallet', - body: 'tipsForUsingAWalletDescription', + title: 'fundYourWallet', + body: 'getStartedByFundingWallet', + }, + [RAMPS_CARD_VARIANT_TYPES.NFT]: { + illustrationSrc: './images/ramps-card-nft-illustration.png', + // eslint-disable-next-line @metamask/design-tokens/color-no-hex + gradient: 'linear-gradient(90deg, #F6822D 0%, #F894A7 52%, #ED94FB 92.5%)', + title: 'getStartedWithNFTs', + body: 'getStartedWithNFTsDescription', }, [RAMPS_CARD_VARIANT_TYPES.ACTIVITY]: { illustrationSrc: './images/ramps-card-activity-illustration.png', @@ -49,21 +57,22 @@ export const RAMPS_CARD_VARIANTS = { // eslint-disable-next-line @metamask/design-tokens/color-no-hex 'linear-gradient(90deg, #57C5DC 0%, #06BFDD 49.39%, #35A9C7 100%)', - title: 'tipsForUsingAWallet', - body: 'tipsForUsingAWalletDescription', + title: 'startYourJourney', + body: 'startYourJourneyDescription', }, [RAMPS_CARD_VARIANT_TYPES.BTC]: { illustrationSrc: './images/ramps-card-btc-illustration.png', gradient: // eslint-disable-next-line @metamask/design-tokens/color-no-hex 'linear-gradient(90deg, #017ED9 0%, #446FD9 35%, #5E6AD9 58%, #635ED9 80.5%, #6855D9 92.5%, #6A4FD9 100%)', - title: 'tipsForUsingAWallet', - body: 'tipsForUsingAWalletDescription', + title: 'fundYourWallet', + body: 'fundYourWalletDescription', }, }; const metamaskEntryMap = { [RAMPS_CARD_VARIANT_TYPES.TOKEN]: RampsMetaMaskEntry.TokensBanner, + [RAMPS_CARD_VARIANT_TYPES.NFT]: RampsMetaMaskEntry.NftBanner, [RAMPS_CARD_VARIANT_TYPES.ACTIVITY]: RampsMetaMaskEntry.ActivityBanner, [RAMPS_CARD_VARIANT_TYPES.BTC]: RampsMetaMaskEntry.BtcBanner, }; @@ -78,6 +87,8 @@ export const RampsCard = ({ variant, handleOnClick }) => { const { chainId, nickname } = useSelector(getMultichainCurrentNetwork); const { symbol } = useSelector(getMultichainDefaultToken); + const isTokenVariant = variant === RAMPS_CARD_VARIANT_TYPES.TOKEN; + useEffect(() => { trackEvent({ event: MetaMetricsEventName.EmptyBuyBannerDisplayed, @@ -99,7 +110,7 @@ export const RampsCard = ({ variant, handleOnClick }) => { category: MetaMetricsEventCategory.Navigation, properties: { location: `${variant} tab`, - text: `Token Marketplace`, + text: `Buy ${symbol}`, // FIXME: This might not be a number for non-EVM networks chain_id: chainId, token_symbol: symbol, @@ -121,14 +132,14 @@ export const RampsCard = ({ variant, handleOnClick }) => { }} > - {t(title)} + {t(title, [symbol])} - {t(body)} + {t(body, [symbol])} - {t('tokenMarketplace')} + {isTokenVariant ? t('getStarted') : t('buyToken', [symbol])} ); diff --git a/ui/components/multichain/ramps-card/ramps-card.stories.js b/ui/components/multichain/ramps-card/ramps-card.stories.js index 903ea3d27f9a..2a4dce444c7e 100644 --- a/ui/components/multichain/ramps-card/ramps-card.stories.js +++ b/ui/components/multichain/ramps-card/ramps-card.stories.js @@ -24,6 +24,12 @@ export const TokensStory = (args) => ( TokensStory.storyName = 'Tokens'; +export const NFTsStory = (args) => ( + +); + +NFTsStory.storyName = 'NFTs'; + export const ActivityStory = (args) => ( ); diff --git a/ui/components/multichain/token-list-item/token-list-item.tsx b/ui/components/multichain/token-list-item/token-list-item.tsx index bf3968963465..0c3c46114541 100644 --- a/ui/components/multichain/token-list-item/token-list-item.tsx +++ b/ui/components/multichain/token-list-item/token-list-item.tsx @@ -34,8 +34,6 @@ import { ModalFooter, ModalHeader, ModalOverlay, - SensitiveText, - SensitiveTextLength, Text, } from '../../component-library'; import { @@ -84,7 +82,6 @@ type TokenListItemProps = { address?: string | null; showPercentage?: boolean; isPrimaryTokenSymbolHidden?: boolean; - privacyMode?: boolean; }; export const TokenListItem = ({ @@ -102,7 +99,6 @@ export const TokenListItem = ({ isStakeable = false, address = null, showPercentage = false, - privacyMode = false, }: TokenListItemProps) => { const t = useI18nContext(); const isEvm = useSelector(getMultichainIsEvm); @@ -379,19 +375,17 @@ export const TokenListItem = ({ ariaLabel={''} /> - {primary}{' '} {isNativeCurrency || isPrimaryTokenSymbolHidden ? '' : tokenSymbol} - + ) : ( - {secondary} - - + {primary}{' '} {isNativeCurrency || isPrimaryTokenSymbolHidden ? '' : tokenSymbol} - + )} diff --git a/ui/components/ui/currency-display/__snapshots__/currency-display.component.test.js.snap b/ui/components/ui/currency-display/__snapshots__/currency-display.component.test.js.snap index eeb40144894b..44ba7be60b6f 100644 --- a/ui/components/ui/currency-display/__snapshots__/currency-display.component.test.js.snap +++ b/ui/components/ui/currency-display/__snapshots__/currency-display.component.test.js.snap @@ -8,7 +8,6 @@ exports[`CurrencyDisplay Component should match default snapshot 1`] = ` >
@@ -22,7 +21,6 @@ exports[`CurrencyDisplay Component should render text with a className 1`] = ` > $123.45 @@ -38,7 +36,6 @@ exports[`CurrencyDisplay Component should render text with a prefix 1`] = ` > - $123.45 diff --git a/ui/components/ui/currency-display/currency-display.component.js b/ui/components/ui/currency-display/currency-display.component.js index 7e2569ffaee3..ca9322661d79 100644 --- a/ui/components/ui/currency-display/currency-display.component.js +++ b/ui/components/ui/currency-display/currency-display.component.js @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import classnames from 'classnames'; import { useCurrencyDisplay } from '../../../hooks/useCurrencyDisplay'; import { EtherDenomination } from '../../../../shared/constants/common'; -import { SensitiveText, Box } from '../../component-library'; +import { Text, Box } from '../../component-library'; import { AlignItems, Display, @@ -33,7 +33,6 @@ export default function CurrencyDisplay({ textProps = {}, suffixProps = {}, isAggregatedFiatOverviewBalance = false, - privacyMode = false, ...props }) { const [title, parts] = useCurrencyDisplay(value, { @@ -69,33 +68,26 @@ export default function CurrencyDisplay({ {prefixComponent} ) : null} - {parts.prefix} {parts.value} - + {parts.suffix ? ( - {parts.suffix} - + ) : null} ); @@ -123,7 +115,6 @@ const CurrencyDisplayPropTypes = { textProps: PropTypes.object, suffixProps: PropTypes.object, isAggregatedFiatOverviewBalance: PropTypes.bool, - privacyMode: PropTypes.bool, }; CurrencyDisplay.propTypes = CurrencyDisplayPropTypes; diff --git a/ui/components/ui/definition-list/definition-list.js b/ui/components/ui/definition-list/definition-list.js index 84d3f48135ab..84a23325b37a 100644 --- a/ui/components/ui/definition-list/definition-list.js +++ b/ui/components/ui/definition-list/definition-list.js @@ -32,7 +32,7 @@ export default function DefinitionList({ {Object.entries(dictionary).map(([term, definition]) => ( ) : (
) : (
@@ -297,5 +292,4 @@ SenderToRecipient.propTypes = { onSenderClick: PropTypes.func, warnUserOnAccountMismatch: PropTypes.bool, recipientIsOwnedAccount: PropTypes.bool, - chainId: PropTypes.string, }; diff --git a/ui/components/ui/token-currency-display/token-currency-display.stories.tsx b/ui/components/ui/token-currency-display/token-currency-display.stories.tsx index 932d54210b84..7cf850c42c84 100644 --- a/ui/components/ui/token-currency-display/token-currency-display.stories.tsx +++ b/ui/components/ui/token-currency-display/token-currency-display.stories.tsx @@ -1,8 +1,9 @@ import React from 'react'; -import type { Meta, StoryObj } from '@storybook/react'; +import { Meta, Story } from '@storybook/react'; import TokenCurrencyDisplay from './token-currency-display.component'; +import { TokenCurrencyDisplayProps } from './token-currency-display.types'; -const meta: Meta = { +export default { title: 'Components/UI/TokenCurrencyDisplay', component: TokenCurrencyDisplay, argTypes: { @@ -11,15 +12,14 @@ const meta: Meta = { token: { control: 'object' }, prefix: { control: 'text' }, }, - args: { - className: '', - transactionData: '0x123', - token: { symbol: 'ETH' }, - prefix: '', - }, -}; +} as Meta; -export default meta; -type Story = StoryObj; +const Template: Story = (args) => ; -export const Default: Story = {}; +export const Default = Template.bind({}); +Default.args = { + className: '', + transactionData: '0x123', + token: { symbol: 'ETH' }, + prefix: '', +}; diff --git a/ui/components/ui/truncated-definition-list/truncated-definition-list.js b/ui/components/ui/truncated-definition-list/truncated-definition-list.js index 2db9784dad8e..ae1782979866 100644 --- a/ui/components/ui/truncated-definition-list/truncated-definition-list.js +++ b/ui/components/ui/truncated-definition-list/truncated-definition-list.js @@ -5,6 +5,7 @@ import { BorderColor, Size } from '../../../helpers/constants/design-system'; import Box from '../box'; import Button from '../button'; import DefinitionList from '../definition-list/definition-list'; +import Popover from '../popover'; import { useI18nContext } from '../../../hooks/useI18nContext'; export default function TruncatedDefinitionList({ @@ -12,6 +13,7 @@ export default function TruncatedDefinitionList({ tooltips, warnings, prefaceKeys, + title, }) { const [isPopoverOpen, setIsPopoverOpen] = useState(false); const t = useI18nContext(); @@ -31,27 +33,55 @@ export default function TruncatedDefinitionList({ type="link" onClick={() => setIsPopoverOpen(true)} > - {t('seeDetails')} + {t(process.env.CHAIN_PERMISSIONS ? 'seeDetails' : 'viewAllDetails')} ); + const renderPopover = () => + isPopoverOpen && ( + setIsPopoverOpen(false)} + footer={ + + } + > + + {renderDefinitionList(true)} + + + ); + const renderContent = () => { - return isPopoverOpen ? ( - renderDefinitionList(true) - ) : ( + if (process.env.CHAIN_PERMISSIONS) { + return isPopoverOpen ? ( + renderDefinitionList(true) + ) : ( + <> + {renderDefinitionList(false)} + {renderButton()} + + ); + } + return ( <> {renderDefinitionList(false)} {renderButton()} + {renderPopover()} ); }; return ( ( bridgeAction: BridgeUserAction | BridgeBackgroundAction, - args?: T, + args?: T[], ) => { return async (dispatch: MetaMaskReduxDispatch) => { - await submitRequestToBackground(bridgeAction, [args]); + await submitRequestToBackground(bridgeAction, args); await forceUpdateMetamaskState(dispatch); }; }; @@ -53,29 +53,20 @@ export const setBridgeFeatureFlags = () => { export const setFromChain = (chainId: Hex) => { return async (dispatch: MetaMaskReduxDispatch) => { dispatch( - callBridgeControllerMethod( - BridgeUserAction.SELECT_SRC_NETWORK, + callBridgeControllerMethod(BridgeUserAction.SELECT_SRC_NETWORK, [ chainId, - ), + ]), ); }; }; export const setToChain = (chainId: Hex) => { return async (dispatch: MetaMaskReduxDispatch) => { + dispatch(setToChainId_(chainId)); dispatch( - callBridgeControllerMethod( - BridgeUserAction.SELECT_DEST_NETWORK, + callBridgeControllerMethod(BridgeUserAction.SELECT_DEST_NETWORK, [ chainId, - ), - ); - }; -}; - -export const updateQuoteRequestParams = (params: Partial) => { - return async (dispatch: MetaMaskReduxDispatch) => { - await dispatch( - callBridgeControllerMethod(BridgeUserAction.UPDATE_QUOTE_PARAMS, params), + ]), ); }; }; diff --git a/ui/ducks/bridge/bridge.test.ts b/ui/ducks/bridge/bridge.test.ts index 6b85565c6143..f4a566c233b5 100644 --- a/ui/ducks/bridge/bridge.test.ts +++ b/ui/ducks/bridge/bridge.test.ts @@ -1,6 +1,5 @@ import configureMockStore from 'redux-mock-store'; import thunk from 'redux-thunk'; -import { zeroAddress } from 'ethereumjs-util'; import { createBridgeMockStore } from '../../../test/jest/mock-store'; import { CHAIN_IDS } from '../../../shared/constants/network'; import { setBackgroundConnection } from '../../store/background-connection'; @@ -19,8 +18,7 @@ import { setToToken, setFromChain, resetInputFields, - setToChainId, - updateQuoteRequestParams, + switchToAndFromTokens, } from './actions'; const middleware = [thunk]; @@ -33,23 +31,9 @@ describe('Ducks - Bridge', () => { store.clearActions(); }); - describe('setToChainId', () => { - it('calls the "bridge/setToChainId" action', () => { - const state = store.getState().bridge; - const actionPayload = CHAIN_IDS.OPTIMISM; - - store.dispatch(setToChainId(actionPayload as never) as never); - - // Check redux state - const actions = store.getActions(); - expect(actions[0].type).toStrictEqual('bridge/setToChainId'); - const newState = bridgeReducer(state, actions[0]); - expect(newState.toChainId).toStrictEqual(actionPayload); - }); - }); - describe('setToChain', () => { - it('calls the selectDestNetwork background action', () => { + it('calls the "bridge/setToChainId" action and the selectDestNetwork background action', () => { + const state = store.getState().bridge; const actionPayload = CHAIN_IDS.OPTIMISM; const mockSelectDestNetwork = jest.fn().mockReturnValue({}); @@ -59,6 +43,11 @@ describe('Ducks - Bridge', () => { store.dispatch(setToChain(actionPayload as never) as never); + // Check redux state + const actions = store.getActions(); + expect(actions[0].type).toStrictEqual('bridge/setToChainId'); + const newState = bridgeReducer(state, actions[0]); + expect(newState.toChainId).toStrictEqual(actionPayload); // Check background state expect(mockSelectDestNetwork).toHaveBeenCalledTimes(1); expect(mockSelectDestNetwork).toHaveBeenCalledWith( @@ -72,7 +61,7 @@ describe('Ducks - Bridge', () => { it('calls the "bridge/setFromToken" action', () => { const state = store.getState().bridge; const actionPayload = { symbol: 'SYMBOL', address: '0x13341432' }; - store.dispatch(setFromToken(actionPayload as never) as never); + store.dispatch(setFromToken(actionPayload)); const actions = store.getActions(); expect(actions[0].type).toStrictEqual('bridge/setFromToken'); const newState = bridgeReducer(state, actions[0]); @@ -84,8 +73,7 @@ describe('Ducks - Bridge', () => { it('calls the "bridge/setToToken" action', () => { const state = store.getState().bridge; const actionPayload = { symbol: 'SYMBOL', address: '0x13341431' }; - - store.dispatch(setToToken(actionPayload as never) as never); + store.dispatch(setToToken(actionPayload)); const actions = store.getActions(); expect(actions[0].type).toStrictEqual('bridge/setToToken'); const newState = bridgeReducer(state, actions[0]); @@ -97,8 +85,7 @@ describe('Ducks - Bridge', () => { it('calls the "bridge/setFromTokenInputValue" action', () => { const state = store.getState().bridge; const actionPayload = '10'; - - store.dispatch(setFromTokenInputValue(actionPayload as never) as never); + store.dispatch(setFromTokenInputValue(actionPayload)); const actions = store.getActions(); expect(actions[0].type).toStrictEqual('bridge/setFromTokenInputValue'); const newState = bridgeReducer(state, actions[0]); @@ -150,30 +137,31 @@ describe('Ducks - Bridge', () => { }); }); - describe('updateQuoteRequestParams', () => { - it('dispatches quote params to the bridge controller', () => { - const mockUpdateParams = jest.fn(); - setBackgroundConnection({ - [BridgeUserAction.UPDATE_QUOTE_PARAMS]: mockUpdateParams, - } as never); - - store.dispatch( - updateQuoteRequestParams({ - srcChainId: 1, - srcTokenAddress: zeroAddress(), - destTokenAddress: undefined, - }) as never, - ); - - expect(mockUpdateParams).toHaveBeenCalledTimes(1); - expect(mockUpdateParams).toHaveBeenCalledWith( - { - srcChainId: 1, - srcTokenAddress: zeroAddress(), - destTokenAddress: undefined, - }, - expect.anything(), + describe('switchToAndFromTokens', () => { + it('switches to and from input values', async () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const bridgeStore = configureMockStore(middleware)( + createBridgeMockStore( + {}, + { + toChainId: CHAIN_IDS.MAINNET, + fromToken: { symbol: 'WETH', address: '0x13341432' }, + toToken: { symbol: 'USDC', address: '0x13341431' }, + fromTokenInputValue: '10', + }, + ), ); + const state = bridgeStore.getState().bridge; + bridgeStore.dispatch(switchToAndFromTokens(CHAIN_IDS.POLYGON)); + const actions = bridgeStore.getActions(); + expect(actions[0].type).toStrictEqual('bridge/switchToAndFromTokens'); + const newState = bridgeReducer(state, actions[0]); + expect(newState).toStrictEqual({ + toChainId: CHAIN_IDS.POLYGON, + fromToken: { symbol: 'USDC', address: '0x13341431' }, + toToken: { symbol: 'WETH', address: '0x13341432' }, + fromTokenInputValue: null, + }); }); }); }); diff --git a/ui/ducks/bridge/bridge.ts b/ui/ducks/bridge/bridge.ts index c75030c7591d..9ec744d9e953 100644 --- a/ui/ducks/bridge/bridge.ts +++ b/ui/ducks/bridge/bridge.ts @@ -39,6 +39,12 @@ const bridgeSlice = createSlice({ resetInputFields: () => ({ ...initialState, }), + switchToAndFromTokens: (state, { payload }) => ({ + toChainId: payload, + fromToken: state.toToken, + toToken: state.fromToken, + fromTokenInputValue: null, + }), }, }); diff --git a/ui/ducks/bridge/selectors.test.ts b/ui/ducks/bridge/selectors.test.ts index 6be67515e6e4..cf27790aa943 100644 --- a/ui/ducks/bridge/selectors.test.ts +++ b/ui/ducks/bridge/selectors.test.ts @@ -30,7 +30,7 @@ describe('Bridge selectors', () => { { srcNetworkAllowlist: [CHAIN_IDS.ARBITRUM] }, { toChainId: '0xe708' }, {}, - { ...mockNetworkState(FEATURED_RPCS[1]) }, + { ...mockNetworkState(FEATURED_RPCS[0]) }, ); const result = getFromChain(state as never); @@ -89,7 +89,7 @@ describe('Bridge selectors', () => { ); const result = getAllBridgeableNetworks(state as never); - expect(result).toHaveLength(8); + expect(result).toHaveLength(7); expect(result[0]).toStrictEqual( expect.objectContaining({ chainId: FEATURED_RPCS[0].chainId }), ); @@ -190,19 +190,21 @@ describe('Bridge selectors', () => { }, {}, {}, - mockNetworkState(...FEATURED_RPCS), + mockNetworkState(...FEATURED_RPCS, { + chainId: CHAIN_IDS.LINEA_MAINNET, + }), ); const result = getToChains(state as never); expect(result).toHaveLength(3); expect(result[0]).toStrictEqual( - expect.objectContaining({ chainId: CHAIN_IDS.ARBITRUM }), + expect.objectContaining({ chainId: CHAIN_IDS.OPTIMISM }), ); expect(result[1]).toStrictEqual( - expect.objectContaining({ chainId: CHAIN_IDS.OPTIMISM }), + expect.objectContaining({ chainId: CHAIN_IDS.POLYGON }), ); expect(result[2]).toStrictEqual( - expect.objectContaining({ chainId: CHAIN_IDS.POLYGON }), + expect.objectContaining({ chainId: CHAIN_IDS.LINEA_MAINNET }), ); }); @@ -295,9 +297,7 @@ describe('Bridge selectors', () => { { ...mockNetworkState( ...Object.values(BUILT_IN_NETWORKS), - ...FEATURED_RPCS.filter( - (network) => network.chainId !== CHAIN_IDS.LINEA_MAINNET, // Linea mainnet is both a built in network, as well as featured RPC - ), + ...FEATURED_RPCS, ), useExternalServices: true, }, diff --git a/ui/ducks/bridge/selectors.ts b/ui/ducks/bridge/selectors.ts index 568d62e7a2d4..8cd56928fc66 100644 --- a/ui/ducks/bridge/selectors.ts +++ b/ui/ducks/bridge/selectors.ts @@ -8,7 +8,7 @@ import { getIsBridgeEnabled, getSwapsDefaultToken, SwapsEthToken, -} from '../../selectors/selectors'; +} from '../../selectors'; import { ALLOWED_BRIDGE_CHAIN_IDS } from '../../../shared/constants/bridge'; import { BridgeControllerState, @@ -110,7 +110,7 @@ export const getToTokens = (state: BridgeAppState) => { export const getFromToken = ( state: BridgeAppState, -): SwapsTokenObject | SwapsEthToken | null => { +): SwapsTokenObject | SwapsEthToken => { return state.bridge.fromToken?.address ? state.bridge.fromToken : getSwapsDefaultToken(state); diff --git a/ui/ducks/metamask/metamask.js b/ui/ducks/metamask/metamask.js index 9627608eb709..05cc6d46cb27 100644 --- a/ui/ducks/metamask/metamask.js +++ b/ui/ducks/metamask/metamask.js @@ -17,9 +17,9 @@ import { checkNetworkAndAccountSupports1559, getAddressBook, getSelectedNetworkClientId, + getSelectedInternalAccount, getNetworkConfigurationsByChainId, -} from '../../selectors/selectors'; -import { getSelectedInternalAccount } from '../../selectors/accounts'; +} from '../../selectors'; import * as actionConstants from '../../store/actionConstants'; import { updateTransactionGasFees } from '../../store/actions'; import { setCustomGasLimit, setCustomGasPrice } from '../gas/gas.duck'; @@ -50,7 +50,6 @@ const initialState = { smartTransactionsOptInStatus: false, petnamesEnabled: true, featureNotificationsEnabled: false, - privacyMode: false, showMultiRpcModal: false, }, firstTimeFlowType: null, diff --git a/ui/ducks/ramps/ramps.test.ts b/ui/ducks/ramps/ramps.test.ts index 8bd6865295d8..3cd543a65219 100644 --- a/ui/ducks/ramps/ramps.test.ts +++ b/ui/ducks/ramps/ramps.test.ts @@ -205,7 +205,7 @@ describe('rampsSlice', () => { }); it('should return true when Bitcoin is buyable and current chain is Bitcoin', () => { - getCurrentChainIdMock.mockReturnValue(CHAIN_IDS.MAINNET); + getCurrentChainIdMock.mockReturnValue(MultichainNetworks.BITCOIN); getMultichainIsBitcoinMock.mockReturnValue(true); const mockBuyableChains = [ { chainId: MultichainNetworks.BITCOIN, active: true }, @@ -219,7 +219,7 @@ describe('rampsSlice', () => { }); it('should return false when Bitcoin is not buyable and current chain is Bitcoin', () => { - getCurrentChainIdMock.mockReturnValue(CHAIN_IDS.MAINNET); + getCurrentChainIdMock.mockReturnValue(MultichainNetworks.BITCOIN); getMultichainIsBitcoinMock.mockReturnValue(true); const mockBuyableChains = [ { chainId: MultichainNetworks.BITCOIN, active: false }, diff --git a/ui/ducks/swaps/swaps.js b/ui/ducks/swaps/swaps.js index d23c0ce69381..8dd7336d7a62 100644 --- a/ui/ducks/swaps/swaps.js +++ b/ui/ducks/swaps/swaps.js @@ -858,7 +858,6 @@ export const fetchQuotesAndSetQuoteState = ( stx_enabled: smartTransactionsEnabled, current_stx_enabled: currentSmartTransactionsEnabled, stx_user_opt_in: getSmartTransactionsOptInStatusForMetrics(state), - gas_included: newSelectedQuote.isGasIncludedTrade, anonymizedData: true, }, }); diff --git a/ui/helpers/utils/notification.util.ts b/ui/helpers/utils/notification.util.ts index afbba2b88172..489f1ca2f272 100644 --- a/ui/helpers/utils/notification.util.ts +++ b/ui/helpers/utils/notification.util.ts @@ -420,11 +420,9 @@ export const getNetworkFees = async (notification: OnChainRawNotification) => { const rpcUrl = getRpcUrlByChainId(`0x${chainId}` as HexChainId); const connection = { url: rpcUrl, - headers: process.env.STORYBOOK - ? undefined - : { - 'Infura-Source': 'metamask/metamask', - }, + headers: { + 'Infura-Source': 'metamask/metamask', + }, }; const provider = new JsonRpcProvider(connection); diff --git a/ui/helpers/utils/notification.utils.test.ts b/ui/helpers/utils/notification.utils.test.ts index 2f4e66c26504..f82f8532cb58 100644 --- a/ui/helpers/utils/notification.utils.test.ts +++ b/ui/helpers/utils/notification.utils.test.ts @@ -9,7 +9,7 @@ import { describe('formatMenuItemDate', () => { beforeAll(() => { jest.useFakeTimers(); - jest.setSystemTime(new Date(Date.UTC(2024, 5, 7, 9, 40, 0))); // 2024-06-07T09:40:00Z + jest.setSystemTime(new Date('2024-06-07T09:40:00Z')); }); afterAll(() => { @@ -28,7 +28,7 @@ describe('formatMenuItemDate', () => { // assert 1 hour ago assertToday((testDate) => { - testDate.setUTCHours(testDate.getUTCHours() - 1); + testDate.setHours(testDate.getHours() - 1); return testDate; }); }); @@ -42,14 +42,14 @@ describe('formatMenuItemDate', () => { // assert exactly 1 day ago assertYesterday((testDate) => { - testDate.setUTCDate(testDate.getUTCDate() - 1); + testDate.setDate(testDate.getDate() - 1); }); // assert almost a day ago, but was still yesterday // E.g. if Today way 09:40AM, but date to test was 23 hours ago (yesterday at 10:40AM), we still want to to show yesterday assertYesterday((testDate) => { - testDate.setUTCDate(testDate.getUTCDate() - 1); - testDate.setUTCHours(testDate.getUTCHours() + 1); + testDate.setDate(testDate.getDate() - 1); + testDate.setHours(testDate.getHours() + 1); }); }); @@ -62,18 +62,18 @@ describe('formatMenuItemDate', () => { // assert exactly 1 month ago assertMonthsAgo((testDate) => { - testDate.setUTCMonth(testDate.getUTCMonth() - 1); + testDate.setMonth(testDate.getMonth() - 1); }); // assert 2 months ago assertMonthsAgo((testDate) => { - testDate.setUTCMonth(testDate.getUTCMonth() - 2); + testDate.setMonth(testDate.getMonth() - 2); }); // assert almost a month ago (where it is a new month, but not 30 days) assertMonthsAgo(() => { // jest mock date is set in july, so we will test with month may - return new Date(Date.UTC(2024, 4, 20, 9, 40, 0)); // 2024-05-20T09:40:00Z + return new Date('2024-05-20T09:40:00Z'); }); }); @@ -86,18 +86,18 @@ describe('formatMenuItemDate', () => { // assert exactly 1 year ago assertYearsAgo((testDate) => { - testDate.setUTCFullYear(testDate.getUTCFullYear() - 1); + testDate.setFullYear(testDate.getFullYear() - 1); }); // assert 2 years ago assertYearsAgo((testDate) => { - testDate.setUTCFullYear(testDate.getUTCFullYear() - 2); + testDate.setFullYear(testDate.getFullYear() - 2); }); // assert almost a year ago (where it is a new year, but not 365 days ago) assertYearsAgo(() => { // jest mock date is set in 2024, so we will test with year 2023 - return new Date(Date.UTC(2023, 10, 20, 9, 40, 0)); // 2023-11-20T09:40:00Z + return new Date('2023-11-20T09:40:00Z'); }); }); }); diff --git a/ui/hooks/bridge/useBridging.ts b/ui/hooks/bridge/useBridging.ts index fe7a21e2206f..a68aeb361bdd 100644 --- a/ui/hooks/bridge/useBridging.ts +++ b/ui/hooks/bridge/useBridging.ts @@ -43,7 +43,6 @@ const useBridging = () => { const isMarketingEnabled = useSelector(getDataCollectionForMarketing); const providerConfig = useSelector(getProviderConfig); const keyring = useSelector(getCurrentKeyring); - // @ts-expect-error keyring type is wrong maybe? const usingHardwareWallet = isHardwareKeyring(keyring.type); const isBridgeSupported = useSelector(getIsBridgeEnabled); diff --git a/ui/hooks/metamask-notifications/useNotifications.ts b/ui/hooks/metamask-notifications/useNotifications.ts index 9724253a8671..62367cdbe310 100644 --- a/ui/hooks/metamask-notifications/useNotifications.ts +++ b/ui/hooks/metamask-notifications/useNotifications.ts @@ -54,13 +54,8 @@ export function useListNotifications(): { setLoading(true); setError(null); - const urlParams = new URLSearchParams(window.location.search); - const previewToken = urlParams.get('previewToken'); - try { - const data = await dispatch( - fetchAndUpdateMetamaskNotifications(previewToken ?? undefined), - ); + const data = await dispatch(fetchAndUpdateMetamaskNotifications()); setNotificationsData(data as unknown as Notification[]); return data as unknown as Notification[]; } catch (e) { diff --git a/ui/hooks/metamask-notifications/useProfileSyncing.test.tsx b/ui/hooks/metamask-notifications/useProfileSyncing.test.tsx new file mode 100644 index 000000000000..951cec333ade --- /dev/null +++ b/ui/hooks/metamask-notifications/useProfileSyncing.test.tsx @@ -0,0 +1,156 @@ +import React from 'react'; +import { Provider } from 'react-redux'; +import { renderHook, act } from '@testing-library/react-hooks'; +import configureStore from 'redux-mock-store'; +import thunk from 'redux-thunk'; +import { waitFor } from '@testing-library/react'; +import * as actions from '../../store/actions'; +import { + useEnableProfileSyncing, + useDisableProfileSyncing, + useAccountSyncingEffect, + useDeleteAccountSyncingDataFromUserStorage, +} from './useProfileSyncing'; + +const middlewares = [thunk]; +const mockStore = configureStore(middlewares); + +jest.mock('../../store/actions', () => ({ + performSignIn: jest.fn(), + performSignOut: jest.fn(), + enableProfileSyncing: jest.fn(), + disableProfileSyncing: jest.fn(), + showLoadingIndication: jest.fn(), + hideLoadingIndication: jest.fn(), + syncInternalAccountsWithUserStorage: jest.fn(), + deleteAccountSyncingDataFromUserStorage: jest.fn(), +})); + +type ArrangeMocksMetamaskStateOverrides = { + isSignedIn?: boolean; + isProfileSyncingEnabled?: boolean; + isUnlocked?: boolean; + useExternalServices?: boolean; + completedOnboarding?: boolean; +}; + +const initialMetamaskState: ArrangeMocksMetamaskStateOverrides = { + isSignedIn: false, + isProfileSyncingEnabled: false, + isUnlocked: true, + useExternalServices: true, + completedOnboarding: true, +}; + +const arrangeMocks = ( + metamaskStateOverrides?: ArrangeMocksMetamaskStateOverrides, +) => { + const store = mockStore({ + metamask: { + ...initialMetamaskState, + ...metamaskStateOverrides, + participateInMetaMetrics: false, + internalAccounts: { + accounts: { + '0x123': { + address: '0x123', + id: 'account1', + metadata: {}, + options: {}, + methods: [], + type: 'eip155:eoa', + }, + }, + }, + }, + }); + + store.dispatch = jest.fn().mockImplementation((action) => { + if (typeof action === 'function') { + return action(store.dispatch, store.getState); + } + return Promise.resolve(); + }); + + jest.clearAllMocks(); + + return { store }; +}; + +describe('useProfileSyncing', () => { + it('should enable profile syncing', async () => { + const { store } = arrangeMocks(); + + const { result } = renderHook(() => useEnableProfileSyncing(), { + wrapper: ({ children }) => {children}, + }); + + act(() => { + result.current.enableProfileSyncing(); + }); + + expect(actions.enableProfileSyncing).toHaveBeenCalled(); + }); + + it('should disable profile syncing', async () => { + const { store } = arrangeMocks(); + + const { result } = renderHook(() => useDisableProfileSyncing(), { + wrapper: ({ children }) => {children}, + }); + + act(() => { + result.current.disableProfileSyncing(); + }); + + expect(actions.disableProfileSyncing).toHaveBeenCalled(); + }); + + it('should dispatch account syncing when conditions are met', async () => { + const { store } = arrangeMocks({ + isSignedIn: true, + isProfileSyncingEnabled: true, + }); + + renderHook(() => useAccountSyncingEffect(), { + wrapper: ({ children }) => {children}, + }); + + await waitFor(() => { + expect(actions.syncInternalAccountsWithUserStorage).toHaveBeenCalled(); + }); + }); + + it('should not dispatch account syncing when conditions are not met', async () => { + const { store } = arrangeMocks(); + + renderHook(() => useAccountSyncingEffect(), { + wrapper: ({ children }) => {children}, + }); + + await waitFor(() => { + expect( + actions.syncInternalAccountsWithUserStorage, + ).not.toHaveBeenCalled(); + }); + }); + + it('should dispatch account sync data deletion', async () => { + const { store } = arrangeMocks(); + + const { result } = renderHook( + () => useDeleteAccountSyncingDataFromUserStorage(), + { + wrapper: ({ children }) => ( + {children} + ), + }, + ); + + act(() => { + result.current.dispatchDeleteAccountSyncingDataFromUserStorage(); + }); + + expect(actions.deleteAccountSyncingDataFromUserStorage).toHaveBeenCalled(); + }); +}); diff --git a/ui/hooks/metamask-notifications/useProfileSyncing/profileSyncing.ts b/ui/hooks/metamask-notifications/useProfileSyncing.ts similarity index 53% rename from ui/hooks/metamask-notifications/useProfileSyncing/profileSyncing.ts rename to ui/hooks/metamask-notifications/useProfileSyncing.ts index 5c073fdf6d94..67899aa73927 100644 --- a/ui/hooks/metamask-notifications/useProfileSyncing/profileSyncing.ts +++ b/ui/hooks/metamask-notifications/useProfileSyncing.ts @@ -1,21 +1,35 @@ -import { useState, useCallback } from 'react'; +import { useState, useCallback, useEffect, useMemo } from 'react'; import { useDispatch, useSelector } from 'react-redux'; +import type { InternalAccount } from '@metamask/keyring-api'; import log from 'loglevel'; -import { useMetamaskNotificationsContext } from '../../../contexts/metamask-notifications/metamask-notifications'; import { disableProfileSyncing as disableProfileSyncingAction, enableProfileSyncing as enableProfileSyncingAction, setIsProfileSyncingEnabled as setIsProfileSyncingEnabledAction, hideLoadingIndication, -} from '../../../store/actions'; + syncInternalAccountsWithUserStorage, + deleteAccountSyncingDataFromUserStorage, +} from '../../store/actions'; -import { selectIsSignedIn } from '../../../selectors/metamask-notifications/authentication'; -import { selectIsProfileSyncingEnabled } from '../../../selectors/metamask-notifications/profile-syncing'; -import { getUseExternalServices } from '../../../selectors'; +import { selectIsSignedIn } from '../../selectors/metamask-notifications/authentication'; +import { selectIsProfileSyncingEnabled } from '../../selectors/metamask-notifications/profile-syncing'; +import { getUseExternalServices } from '../../selectors'; import { getIsUnlocked, getCompletedOnboarding, -} from '../../../ducks/metamask/metamask'; +} from '../../ducks/metamask/metamask'; + +// Define KeyringType interface +export type KeyringType = { + type: string; +}; + +// Define AccountType interface +export type AccountType = InternalAccount & { + balance: string; + keyring: KeyringType; + label: string; +}; /** * Custom hook to enable profile syncing. This hook handles the process of signing in @@ -60,7 +74,6 @@ export function useDisableProfileSyncing(): { error: string | null; } { const dispatch = useDispatch(); - const { listNotifications } = useMetamaskNotificationsContext(); const [error, setError] = useState(null); @@ -70,9 +83,6 @@ export function useDisableProfileSyncing(): { try { // disable profile syncing await dispatch(disableProfileSyncingAction()); - - // list notifications to update the counter - await listNotifications(); } catch (e) { const errorMessage = e instanceof Error ? e.message : JSON.stringify(e ?? ''); @@ -114,29 +124,92 @@ export function useSetIsProfileSyncingEnabled(): { } /** - * A utility used internally to decide if syncing features should be dispatched - * Considers factors like basic functionality; unlocked; finished onboarding, and is logged in + * Custom hook to dispatch account syncing. * - * @returns a boolean if internally we can perform syncing features or not. + * @returns An object containing the `dispatchAccountSyncing` function, boolean `shouldDispatchAccountSyncing`, + * and error state. */ -export const useShouldDispatchProfileSyncing = () => { +export const useAccountSyncing = () => { + const dispatch = useDispatch(); + + const [error, setError] = useState(null); + const isProfileSyncingEnabled = useSelector(selectIsProfileSyncingEnabled); - const basicFunctionality: boolean | undefined = useSelector( - getUseExternalServices, - ); - const isUnlocked: boolean | undefined = useSelector(getIsUnlocked); + const basicFunctionality = useSelector(getUseExternalServices); + const isUnlocked = useSelector(getIsUnlocked); const isSignedIn = useSelector(selectIsSignedIn); - const completedOnboarding: boolean | undefined = useSelector( - getCompletedOnboarding, - ); + const completedOnboarding = useSelector(getCompletedOnboarding); - const shouldDispatchProfileSyncing: boolean = Boolean( - basicFunctionality && + const shouldDispatchAccountSyncing = useMemo( + () => + basicFunctionality && isProfileSyncingEnabled && isUnlocked && isSignedIn && completedOnboarding, + [ + basicFunctionality, + isProfileSyncingEnabled, + isUnlocked, + isSignedIn, + completedOnboarding, + ], ); - return shouldDispatchProfileSyncing; + const dispatchAccountSyncing = useCallback(() => { + setError(null); + + try { + if (!shouldDispatchAccountSyncing) { + return; + } + dispatch(syncInternalAccountsWithUserStorage()); + } catch (e) { + log.error(e); + setError(e instanceof Error ? e.message : 'An unexpected error occurred'); + } + }, [dispatch, shouldDispatchAccountSyncing]); + + return { + dispatchAccountSyncing, + shouldDispatchAccountSyncing, + error, + }; +}; + +/** + * Custom hook to delete a user's account syncing data from user storage + */ + +export const useDeleteAccountSyncingDataFromUserStorage = () => { + const dispatch = useDispatch(); + + const [error, setError] = useState(null); + + const dispatchDeleteAccountSyncingDataFromUserStorage = useCallback(() => { + setError(null); + + try { + dispatch(deleteAccountSyncingDataFromUserStorage()); + } catch (e) { + log.error(e); + setError(e instanceof Error ? e.message : 'An unexpected error occurred'); + } + }, [dispatch]); + + return { + dispatchDeleteAccountSyncingDataFromUserStorage, + error, + }; +}; + +/** + * Custom hook to apply account syncing effect. + */ +export const useAccountSyncingEffect = () => { + const { dispatchAccountSyncing } = useAccountSyncing(); + + useEffect(() => { + dispatchAccountSyncing(); + }, [dispatchAccountSyncing]); }; diff --git a/ui/hooks/metamask-notifications/useProfileSyncing/accountSyncing.test.tsx b/ui/hooks/metamask-notifications/useProfileSyncing/accountSyncing.test.tsx deleted file mode 100644 index 604466b3a75c..000000000000 --- a/ui/hooks/metamask-notifications/useProfileSyncing/accountSyncing.test.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import { waitFor } from '@testing-library/react'; -import { act } from '@testing-library/react-hooks'; -import { renderHookWithProviderTyped } from '../../../../test/lib/render-helpers'; -import * as actions from '../../../store/actions'; -import { - useAccountSyncingEffect, - useDeleteAccountSyncingDataFromUserStorage, -} from './accountSyncing'; -import * as ProfileSyncModule from './profileSyncing'; - -describe('useDeleteAccountSyncingDataFromUserStorage()', () => { - it('should dispatch account sync data deletion', async () => { - const mockDeleteAccountSyncAction = jest.spyOn( - actions, - 'deleteAccountSyncingDataFromUserStorage', - ); - - const { result } = renderHookWithProviderTyped( - () => useDeleteAccountSyncingDataFromUserStorage(), - {}, - ); - - await act(async () => { - await result.current.dispatchDeleteAccountData(); - }); - - expect(mockDeleteAccountSyncAction).toHaveBeenCalled(); - }); -}); - -describe('useAccountSyncingEffect', () => { - const arrangeMocks = () => { - const mockUseShouldProfileSync = jest.spyOn( - ProfileSyncModule, - 'useShouldDispatchProfileSyncing', - ); - const mockSyncAccountsAction = jest.spyOn( - actions, - 'syncInternalAccountsWithUserStorage', - ); - return { - mockUseShouldProfileSync, - mockSyncAccountsAction, - }; - }; - - const arrangeAndAct = (props: { profileSyncConditionsMet: boolean }) => { - const mocks = arrangeMocks(); - mocks.mockUseShouldProfileSync.mockReturnValue( - props.profileSyncConditionsMet, - ); - - renderHookWithProviderTyped(() => useAccountSyncingEffect(), {}); - return mocks; - }; - - it('should run effect if profile sync conditions are met', async () => { - const mocks = arrangeAndAct({ profileSyncConditionsMet: true }); - await waitFor(() => { - expect(mocks.mockSyncAccountsAction).toHaveBeenCalled(); - }); - }); - - it('should not run effect if profile sync conditions are not met', async () => { - const mocks = arrangeAndAct({ profileSyncConditionsMet: false }); - await waitFor(() => { - expect(mocks.mockSyncAccountsAction).not.toHaveBeenCalled(); - }); - }); -}); diff --git a/ui/hooks/metamask-notifications/useProfileSyncing/accountSyncing.ts b/ui/hooks/metamask-notifications/useProfileSyncing/accountSyncing.ts deleted file mode 100644 index cef4dc80fa75..000000000000 --- a/ui/hooks/metamask-notifications/useProfileSyncing/accountSyncing.ts +++ /dev/null @@ -1,66 +0,0 @@ -import log from 'loglevel'; -import { useCallback, useEffect } from 'react'; -import { useDispatch } from 'react-redux'; -import { - deleteAccountSyncingDataFromUserStorage, - syncInternalAccountsWithUserStorage, -} from '../../../store/actions'; -import { useShouldDispatchProfileSyncing } from './profileSyncing'; - -/** - * Custom hook to dispatch account syncing. - * - * @returns An object containing the `dispatchAccountSyncing` function, boolean `shouldDispatchAccountSyncing`, - * and error state. - */ -const useAccountSyncing = () => { - const dispatch = useDispatch(); - - const shouldDispatchAccountSyncing = useShouldDispatchProfileSyncing(); - - const dispatchAccountSyncing = useCallback(() => { - try { - if (!shouldDispatchAccountSyncing) { - return; - } - dispatch(syncInternalAccountsWithUserStorage()); - } catch (e) { - log.error(e); - } - }, [dispatch, shouldDispatchAccountSyncing]); - - return { - dispatchAccountSyncing, - shouldDispatchAccountSyncing, - }; -}; - -/** - * Custom hook to apply account syncing effect. - */ -export const useAccountSyncingEffect = () => { - const shouldSync = useShouldDispatchProfileSyncing(); - const { dispatchAccountSyncing } = useAccountSyncing(); - - useEffect(() => { - if (shouldSync) { - dispatchAccountSyncing(); - } - }, [shouldSync, dispatchAccountSyncing]); -}; - -/** - * Custom hook to delete a user's account syncing data from user storage - */ -export const useDeleteAccountSyncingDataFromUserStorage = () => { - const dispatch = useDispatch(); - const dispatchDeleteAccountData = useCallback(async () => { - try { - await dispatch(deleteAccountSyncingDataFromUserStorage()); - } catch { - // Do Nothing - } - }, []); - - return { dispatchDeleteAccountData }; -}; diff --git a/ui/hooks/metamask-notifications/useProfileSyncing/index.ts b/ui/hooks/metamask-notifications/useProfileSyncing/index.ts deleted file mode 100644 index 9a6cda8468fb..000000000000 --- a/ui/hooks/metamask-notifications/useProfileSyncing/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -export { - useDisableProfileSyncing, - useEnableProfileSyncing, - useSetIsProfileSyncingEnabled, -} from './profileSyncing'; -export { - useAccountSyncingEffect, - useDeleteAccountSyncingDataFromUserStorage, -} from './accountSyncing'; diff --git a/ui/hooks/metamask-notifications/useProfileSyncing/profileSyncing.test.tsx b/ui/hooks/metamask-notifications/useProfileSyncing/profileSyncing.test.tsx deleted file mode 100644 index 99d3064085ea..000000000000 --- a/ui/hooks/metamask-notifications/useProfileSyncing/profileSyncing.test.tsx +++ /dev/null @@ -1,136 +0,0 @@ -import { act } from '@testing-library/react-hooks'; -import { renderHookWithProviderTyped } from '../../../../test/lib/render-helpers'; -import { MetamaskNotificationsProvider } from '../../../contexts/metamask-notifications'; -import * as actions from '../../../store/actions'; -import { - useDisableProfileSyncing, - useEnableProfileSyncing, - useShouldDispatchProfileSyncing, -} from './profileSyncing'; - -type ArrangeMocksMetamaskStateOverrides = { - isSignedIn?: boolean; - isProfileSyncingEnabled?: boolean; - isUnlocked?: boolean; - useExternalServices?: boolean; - completedOnboarding?: boolean; -}; - -const initialMetamaskState: ArrangeMocksMetamaskStateOverrides = { - isSignedIn: false, - isProfileSyncingEnabled: false, - isUnlocked: true, - useExternalServices: true, - completedOnboarding: true, -}; - -const arrangeMockState = ( - metamaskStateOverrides?: ArrangeMocksMetamaskStateOverrides, -) => { - const state = { - metamask: { - ...initialMetamaskState, - ...metamaskStateOverrides, - }, - }; - - return { state }; -}; - -describe('useEnableProfileSyncing()', () => { - it('should enable profile syncing', async () => { - const mockEnableProfileSyncingAction = jest.spyOn( - actions, - 'enableProfileSyncing', - ); - - const { state } = arrangeMockState(); - const { result } = renderHookWithProviderTyped( - () => useEnableProfileSyncing(), - state, - ); - await act(async () => { - await result.current.enableProfileSyncing(); - }); - - expect(mockEnableProfileSyncingAction).toHaveBeenCalled(); - }); -}); - -describe('useDisableProfileSyncing()', () => { - it('should disable profile syncing', async () => { - const mockDisableProfileSyncingAction = jest.spyOn( - actions, - 'disableProfileSyncing', - ); - - const { state } = arrangeMockState(); - - const { result } = renderHookWithProviderTyped( - () => useDisableProfileSyncing(), - state, - undefined, - MetamaskNotificationsProvider, - ); - - await act(async () => { - await result.current.disableProfileSyncing(); - }); - - expect(mockDisableProfileSyncingAction).toHaveBeenCalled(); - }); -}); - -describe('useShouldDispatchProfileSyncing()', () => { - const testCases = (() => { - const properties = [ - 'isSignedIn', - 'isProfileSyncingEnabled', - 'isUnlocked', - 'useExternalServices', - 'completedOnboarding', - ] as const; - const baseState = { - isSignedIn: true, - isProfileSyncingEnabled: true, - isUnlocked: true, - useExternalServices: true, - completedOnboarding: true, - }; - - const failureStateCases: { - state: ArrangeMocksMetamaskStateOverrides; - failingField: string; - }[] = []; - - // Generate test cases by toggling each property - properties.forEach((property) => { - const state = { ...baseState, [property]: false }; - failureStateCases.push({ state, failingField: property }); - }); - - const successTestCase = { state: baseState }; - - return { successTestCase, failureStateCases }; - })(); - - it('should return true if all conditions are met', () => { - const { state } = arrangeMockState(testCases.successTestCase.state); - const hook = renderHookWithProviderTyped( - () => useShouldDispatchProfileSyncing(), - state, - ); - expect(hook.result.current).toBe(true); - }); - - testCases.failureStateCases.forEach(({ state, failingField }) => { - it(`should return false if not all conditions are met [${failingField} = false]`, () => { - const { state: newState } = arrangeMockState(state); - const hook = renderHookWithProviderTyped( - () => useShouldDispatchProfileSyncing(), - newState, - ); - expect(hook.result.current).toBe(false); - }); - }); -}); diff --git a/ui/hooks/snaps/useDisplayName.ts b/ui/hooks/snaps/useDisplayName.ts deleted file mode 100644 index 6a6d3d7e6b51..000000000000 --- a/ui/hooks/snaps/useDisplayName.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { NamespaceId } from '@metamask/snaps-utils'; -import { CaipChainId, KnownCaipNamespace } from '@metamask/utils'; -import { useSelector } from 'react-redux'; -import { - getMemoizedAccountName, - getAddressBookEntryByNetwork, - AddressBookMetaMaskState, - AccountsMetaMaskState, -} from '../../selectors/snaps'; -import { toChecksumHexAddress } from '../../../shared/modules/hexstring-utils'; -import { decimalToHex } from '../../../shared/modules/conversion.utils'; - -export type UseDisplayNameParams = { - chain: { - namespace: NamespaceId; - reference: string; - }; - chainId: CaipChainId; - address: string; -}; - -/** - * Get the display name for an address. - * This will look for an account name in the state, and if not found, it will look for an address book entry. - * - * @param params - The parsed CAIP-10 ID. - * @returns The display name for the address. - */ -export const useDisplayName = ( - params: UseDisplayNameParams, -): string | undefined => { - const { - address, - chain: { namespace, reference }, - } = params; - - const isEip155 = namespace === KnownCaipNamespace.Eip155; - - const parsedAddress = isEip155 ? toChecksumHexAddress(address) : address; - - const accountName = useSelector((state: AccountsMetaMaskState) => - getMemoizedAccountName(state, parsedAddress), - ); - - const addressBookEntry = useSelector((state: AddressBookMetaMaskState) => - getAddressBookEntryByNetwork( - state, - parsedAddress, - `0x${decimalToHex(isEip155 ? reference : `0`)}`, - ), - ); - - return accountName || (isEip155 && addressBookEntry?.name) || undefined; -}; diff --git a/ui/hooks/useCurrencyRatePolling.ts b/ui/hooks/useCurrencyRatePolling.ts index e7ad21adedf5..fb14b1c94797 100644 --- a/ui/hooks/useCurrencyRatePolling.ts +++ b/ui/hooks/useCurrencyRatePolling.ts @@ -1,30 +1,24 @@ import { useSelector } from 'react-redux'; import { - getNetworkConfigurationsByChainId, + getSelectedNetworkClientId, getUseCurrencyRateCheck, } from '../selectors'; import { - currencyRateStartPolling, + currencyRateStartPollingByNetworkClientId, currencyRateStopPollingByPollingToken, } from '../store/actions'; import { getCompletedOnboarding } from '../ducks/metamask/metamask'; import usePolling from './usePolling'; -const useCurrencyRatePolling = () => { +const useCurrencyRatePolling = (networkClientId?: string) => { const useCurrencyRateCheck = useSelector(getUseCurrencyRateCheck); const completedOnboarding = useSelector(getCompletedOnboarding); - const networkConfigurations = useSelector(getNetworkConfigurationsByChainId); - - const nativeCurrencies = [ - ...new Set( - Object.values(networkConfigurations).map((n) => n.nativeCurrency), - ), - ]; + const selectedNetworkClientId = useSelector(getSelectedNetworkClientId); usePolling({ - startPolling: currencyRateStartPolling, + startPollingByNetworkClientId: currencyRateStartPollingByNetworkClientId, stopPollingByPollingToken: currencyRateStopPollingByPollingToken, - input: nativeCurrencies, + networkClientId: networkClientId ?? selectedNetworkClientId, enabled: useCurrencyRateCheck && completedOnboarding, }); }; diff --git a/ui/hooks/useDisplayName.test.ts b/ui/hooks/useDisplayName.test.ts index 5c36b0a97ed2..1d6fb22b5e69 100644 --- a/ui/hooks/useDisplayName.test.ts +++ b/ui/hooks/useDisplayName.test.ts @@ -1,533 +1,218 @@ -import { NameType } from '@metamask/name-controller'; -import { CHAIN_IDS } from '@metamask/transaction-controller'; -import { cloneDeep } from 'lodash'; -import { Hex } from '@metamask/utils'; -import { renderHookWithProvider } from '../../test/lib/render-helpers'; -import mockState from '../../test/data/mock-state.json'; -import { - EXPERIENCES_TYPE, - FIRST_PARTY_CONTRACT_NAMES, -} from '../../shared/constants/first-party-contracts'; +import { NameEntry, NameType } from '@metamask/name-controller'; +import { NftContract } from '@metamask/assets-controllers'; +import { renderHook } from '@testing-library/react-hooks'; +import { getRemoteTokens } from '../selectors'; +import { getNftContractsByAddressOnCurrentChain } from '../selectors/nft'; import { useDisplayName } from './useDisplayName'; -import { useNftCollectionsMetadata } from './useNftCollectionsMetadata'; import { useNames } from './useName'; +import { useFirstPartyContractNames } from './useFirstPartyContractName'; +import { useNftCollectionsMetadata } from './useNftCollectionsMetadata'; -jest.mock('./useName'); -jest.mock('./useNftCollectionsMetadata'); - -const VALUE_MOCK = 'testvalue'; -const VARIATION_MOCK = CHAIN_IDS.GOERLI; -const PETNAME_MOCK = 'testName1'; -const ERC20_TOKEN_NAME_MOCK = 'testName2'; -const WATCHED_NFT_NAME_MOCK = 'testName3'; -const NFT_NAME_MOCK = 'testName4'; -const FIRST_PARTY_CONTRACT_NAME_MOCK = 'testName5'; -const SYMBOL_MOCK = 'tes'; -const NFT_IMAGE_MOCK = 'testNftImage'; -const ERC20_IMAGE_MOCK = 'testImage'; -const OTHER_NAME_TYPE = 'test' as NameType; +jest.mock('react-redux', () => ({ + // TODO: Replace `any` with type + // eslint-disable-next-line @typescript-eslint/no-explicit-any + useSelector: (selector: any) => selector(), +})); + +jest.mock('./useName', () => ({ + useNames: jest.fn(), +})); + +jest.mock('./useFirstPartyContractName', () => ({ + useFirstPartyContractNames: jest.fn(), +})); + +jest.mock('./useNftCollectionsMetadata', () => ({ + useNftCollectionsMetadata: jest.fn(), +})); + +jest.mock('../selectors', () => ({ + getRemoteTokens: jest.fn(), + getCurrentChainId: jest.fn(), +})); + +jest.mock('../selectors/nft', () => ({ + getNftContractsByAddressOnCurrentChain: jest.fn(), +})); + +const VALUE_MOCK = '0xabc123'; +const TYPE_MOCK = NameType.ETHEREUM_ADDRESS; +const NAME_MOCK = 'TestName'; +const CONTRACT_NAME_MOCK = 'TestContractName'; +const FIRST_PARTY_CONTRACT_NAME_MOCK = 'MetaMask Bridge'; +const WATCHED_NFT_NAME_MOCK = 'TestWatchedNFTName'; + +const NO_PETNAME_FOUND_RETURN_VALUE = { + name: null, +} as NameEntry; +const NO_CONTRACT_NAME_FOUND_RETURN_VALUE = undefined; +const NO_FIRST_PARTY_CONTRACT_NAME_FOUND_RETURN_VALUE = null; +const NO_WATCHED_NFT_NAME_FOUND_RETURN_VALUE = {}; + +const PETNAME_FOUND_RETURN_VALUE = { + name: NAME_MOCK, +} as NameEntry; + +const WATCHED_NFT_FOUND_RETURN_VALUE = { + [VALUE_MOCK]: { + name: WATCHED_NFT_NAME_MOCK, + } as NftContract, +}; describe('useDisplayName', () => { const useNamesMock = jest.mocked(useNames); + const getRemoteTokensMock = jest.mocked(getRemoteTokens); + const useFirstPartyContractNamesMock = jest.mocked( + useFirstPartyContractNames, + ); + const getNftContractsByAddressOnCurrentChainMock = jest.mocked( + getNftContractsByAddressOnCurrentChain, + ); const useNftCollectionsMetadataMock = jest.mocked(useNftCollectionsMetadata); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - let state: any; + beforeEach(() => { + jest.resetAllMocks(); - function mockPetname(name: string) { - useNamesMock.mockReturnValue([ + useNamesMock.mockReturnValue([NO_PETNAME_FOUND_RETURN_VALUE]); + useFirstPartyContractNamesMock.mockReturnValue([ + NO_FIRST_PARTY_CONTRACT_NAME_FOUND_RETURN_VALUE, + ]); + getRemoteTokensMock.mockReturnValue([ { - name, - sourceId: null, - proposedNames: {}, - origin: null, + name: NO_CONTRACT_NAME_FOUND_RETURN_VALUE, }, ]); - } - - function mockERC20Token( - value: string, - variation: string, - name: string, - symbol: string, - image: string, - ) { - state.metamask.tokensChainsCache = { - [variation]: { - data: { - [value]: { - name, - symbol, - iconUrl: image, - }, - }, - }, - }; - } + getNftContractsByAddressOnCurrentChainMock.mockReturnValue( + NO_WATCHED_NFT_NAME_FOUND_RETURN_VALUE, + ); + useNftCollectionsMetadataMock.mockReturnValue({}); + }); - function mockWatchedNFTName(value: string, variation: string, name: string) { - state.metamask.allNftContracts = { - '0x123': { - [variation]: [{ address: value, name }], - }, - }; - } - - function mockNFT( - value: string, - variation: string, - name: string, - image: string, - isSpam: boolean, - ) { - useNftCollectionsMetadataMock.mockReturnValue({ - [variation]: { - [value]: { name, image, isSpam }, - }, + it('handles no name found', () => { + const { result } = renderHook(() => useDisplayName(VALUE_MOCK, TYPE_MOCK)); + expect(result.current).toEqual({ + name: null, + hasPetname: false, }); - } - - function mockFirstPartyContractName( - value: string, - variation: string, - name: string, - ) { - FIRST_PARTY_CONTRACT_NAMES[name as EXPERIENCES_TYPE] = { - [variation as Hex]: value as Hex, - }; - } - - beforeEach(() => { - jest.resetAllMocks(); - - useNftCollectionsMetadataMock.mockReturnValue({}); + }); - useNamesMock.mockReturnValue([ + it('prioritizes a petname over all else', () => { + useNamesMock.mockReturnValue([PETNAME_FOUND_RETURN_VALUE]); + useFirstPartyContractNamesMock.mockReturnValue([ + FIRST_PARTY_CONTRACT_NAME_MOCK, + ]); + getRemoteTokensMock.mockReturnValue([ { - name: null, - sourceId: null, - proposedNames: {}, - origin: null, + name: CONTRACT_NAME_MOCK, }, ]); - - state = cloneDeep(mockState); - - delete FIRST_PARTY_CONTRACT_NAMES[ - FIRST_PARTY_CONTRACT_NAME_MOCK as EXPERIENCES_TYPE - ]; - }); - - it('returns no name if no defaults found', () => { - const { result } = renderHookWithProvider( - () => - useDisplayName({ - value: VALUE_MOCK, - type: NameType.ETHEREUM_ADDRESS, - variation: VARIATION_MOCK, - }), - mockState, + getNftContractsByAddressOnCurrentChainMock.mockReturnValue( + WATCHED_NFT_FOUND_RETURN_VALUE, ); - expect(result.current).toStrictEqual({ - contractDisplayName: undefined, - hasPetname: false, - image: undefined, - name: null, - }); - }); + const { result } = renderHook(() => useDisplayName(VALUE_MOCK, TYPE_MOCK)); - describe('Petname', () => { - it('returns petname', () => { - mockPetname(PETNAME_MOCK); - - const { result } = renderHookWithProvider( - () => - useDisplayName({ - value: VALUE_MOCK, - type: NameType.ETHEREUM_ADDRESS, - variation: VARIATION_MOCK, - }), - state, - ); - - expect(result.current).toStrictEqual({ - contractDisplayName: undefined, - hasPetname: true, - image: undefined, - name: PETNAME_MOCK, - }); + expect(result.current).toEqual({ + name: NAME_MOCK, + hasPetname: true, + contractDisplayName: CONTRACT_NAME_MOCK, }); }); - describe('ERC-20 Token', () => { - it('returns ERC-20 token name and image', () => { - mockERC20Token( - VALUE_MOCK, - VARIATION_MOCK, - ERC20_TOKEN_NAME_MOCK, - SYMBOL_MOCK, - ERC20_IMAGE_MOCK, - ); - - const { result } = renderHookWithProvider( - () => - useDisplayName({ - value: VALUE_MOCK, - type: NameType.ETHEREUM_ADDRESS, - variation: VARIATION_MOCK, - }), - state, - ); - - expect(result.current).toStrictEqual({ - contractDisplayName: ERC20_TOKEN_NAME_MOCK, - hasPetname: false, - image: ERC20_IMAGE_MOCK, - name: ERC20_TOKEN_NAME_MOCK, - }); + it('prioritizes a first-party contract name over a contract name and watched NFT name', () => { + useFirstPartyContractNamesMock.mockReturnValue([ + FIRST_PARTY_CONTRACT_NAME_MOCK, + ]); + getRemoteTokensMock.mockReturnValue({ + name: CONTRACT_NAME_MOCK, }); + getNftContractsByAddressOnCurrentChainMock.mockReturnValue( + WATCHED_NFT_FOUND_RETURN_VALUE, + ); - it('returns ERC-20 token symbol', () => { - mockERC20Token( - VALUE_MOCK, - VARIATION_MOCK, - ERC20_TOKEN_NAME_MOCK, - SYMBOL_MOCK, - ERC20_IMAGE_MOCK, - ); - - const { result } = renderHookWithProvider( - () => - useDisplayName({ - value: VALUE_MOCK, - type: NameType.ETHEREUM_ADDRESS, - variation: CHAIN_IDS.GOERLI, - preferContractSymbol: true, - }), - state, - ); - - expect(result.current).toStrictEqual({ - contractDisplayName: SYMBOL_MOCK, - hasPetname: false, - image: ERC20_IMAGE_MOCK, - name: SYMBOL_MOCK, - }); - }); + const { result } = renderHook(() => useDisplayName(VALUE_MOCK, TYPE_MOCK)); - it('returns no name if type not address', () => { - mockERC20Token( - VALUE_MOCK, - VARIATION_MOCK, - ERC20_TOKEN_NAME_MOCK, - SYMBOL_MOCK, - ERC20_IMAGE_MOCK, - ); - - const { result } = renderHookWithProvider( - () => - useDisplayName({ - value: VALUE_MOCK, - type: OTHER_NAME_TYPE, - variation: CHAIN_IDS.GOERLI, - preferContractSymbol: true, - }), - state, - ); - - expect(result.current).toStrictEqual({ - contractDisplayName: undefined, - hasPetname: false, - image: undefined, - name: null, - }); + expect(result.current).toEqual({ + name: FIRST_PARTY_CONTRACT_NAME_MOCK, + hasPetname: false, }); }); - describe('First-party Contract', () => { - it('returns first-party contract name', () => { - mockFirstPartyContractName( - VALUE_MOCK, - VARIATION_MOCK, - FIRST_PARTY_CONTRACT_NAME_MOCK, - ); - - const { result } = renderHookWithProvider( - () => - useDisplayName({ - value: VALUE_MOCK, - type: NameType.ETHEREUM_ADDRESS, - variation: VARIATION_MOCK, - }), - mockState, - ); - - expect(result.current).toStrictEqual({ - contractDisplayName: undefined, - hasPetname: false, - image: undefined, - name: FIRST_PARTY_CONTRACT_NAME_MOCK, - }); - }); + it('prioritizes a contract name over a watched NFT name', () => { + getRemoteTokensMock.mockReturnValue([ + { + name: CONTRACT_NAME_MOCK, + }, + ]); + getNftContractsByAddressOnCurrentChainMock.mockReturnValue( + WATCHED_NFT_FOUND_RETURN_VALUE, + ); - it('returns no name if type is not address', () => { - const { result } = renderHookWithProvider( - () => - useDisplayName({ - value: - FIRST_PARTY_CONTRACT_NAMES[EXPERIENCES_TYPE.METAMASK_BRIDGE][ - CHAIN_IDS.OPTIMISM - ], - type: OTHER_NAME_TYPE, - variation: CHAIN_IDS.OPTIMISM, - }), - mockState, - ); - - expect(result.current).toStrictEqual({ - contractDisplayName: undefined, - hasPetname: false, - image: undefined, - name: null, - }); + const { result } = renderHook(() => useDisplayName(VALUE_MOCK, TYPE_MOCK)); + + expect(result.current).toEqual({ + name: CONTRACT_NAME_MOCK, + hasPetname: false, + contractDisplayName: CONTRACT_NAME_MOCK, }); }); - describe('Watched NFT', () => { - it('returns watched NFT name', () => { - mockWatchedNFTName(VALUE_MOCK, VARIATION_MOCK, WATCHED_NFT_NAME_MOCK); - - const { result } = renderHookWithProvider( - () => - useDisplayName({ - value: VALUE_MOCK, - type: NameType.ETHEREUM_ADDRESS, - variation: VARIATION_MOCK, - }), - state, - ); - - expect(result.current).toStrictEqual({ - contractDisplayName: undefined, - hasPetname: false, - image: undefined, - name: WATCHED_NFT_NAME_MOCK, - }); - }); + it('returns a watched NFT name if no other name is found', () => { + getNftContractsByAddressOnCurrentChainMock.mockReturnValue( + WATCHED_NFT_FOUND_RETURN_VALUE, + ); - it('returns no name if type is not address', () => { - mockWatchedNFTName(VALUE_MOCK, VARIATION_MOCK, WATCHED_NFT_NAME_MOCK); - - const { result } = renderHookWithProvider( - () => - useDisplayName({ - value: VALUE_MOCK, - type: OTHER_NAME_TYPE, - variation: VARIATION_MOCK, - }), - state, - ); - - expect(result.current).toStrictEqual({ - contractDisplayName: undefined, - hasPetname: false, - image: undefined, - name: null, - }); + const { result } = renderHook(() => useDisplayName(VALUE_MOCK, TYPE_MOCK)); + + expect(result.current).toEqual({ + name: WATCHED_NFT_NAME_MOCK, + hasPetname: false, }); }); - describe('NFT', () => { - it('returns NFT name and image', () => { - mockNFT(VALUE_MOCK, VARIATION_MOCK, NFT_NAME_MOCK, NFT_IMAGE_MOCK, false); - - const { result } = renderHookWithProvider( - () => - useDisplayName({ - value: VALUE_MOCK, - type: NameType.ETHEREUM_ADDRESS, - variation: VARIATION_MOCK, - }), - mockState, - ); - - expect(result.current).toStrictEqual({ - contractDisplayName: undefined, - hasPetname: false, - image: NFT_IMAGE_MOCK, - name: NFT_NAME_MOCK, - }); - }); + it('returns nft collection name from metadata if no other name is found', () => { + const IMAGE_MOCK = 'url'; - it('returns no name if NFT collection is spam', () => { - mockNFT(VALUE_MOCK, VARIATION_MOCK, NFT_NAME_MOCK, NFT_IMAGE_MOCK, true); - - const { result } = renderHookWithProvider( - () => - useDisplayName({ - value: VALUE_MOCK, - type: NameType.ETHEREUM_ADDRESS, - variation: VARIATION_MOCK, - }), - mockState, - ); - - expect(result.current).toStrictEqual({ - contractDisplayName: undefined, - hasPetname: false, - image: undefined, - name: null, - }); + useNftCollectionsMetadataMock.mockReturnValue({ + [VALUE_MOCK.toLowerCase()]: { + name: CONTRACT_NAME_MOCK, + image: IMAGE_MOCK, + isSpam: false, + }, }); - it('returns no name if type not address', () => { - mockNFT(VALUE_MOCK, VARIATION_MOCK, NFT_NAME_MOCK, NFT_IMAGE_MOCK, false); - - const { result } = renderHookWithProvider( - () => - useDisplayName({ - value: VALUE_MOCK, - type: OTHER_NAME_TYPE, - variation: VARIATION_MOCK, - }), - mockState, - ); - - expect(result.current).toStrictEqual({ - contractDisplayName: undefined, - hasPetname: false, - image: undefined, - name: null, - }); + const { result } = renderHook(() => + useDisplayName(VALUE_MOCK, TYPE_MOCK, false), + ); + + expect(result.current).toEqual({ + name: CONTRACT_NAME_MOCK, + hasPetname: false, + contractDisplayName: undefined, + image: IMAGE_MOCK, }); }); - describe('Priority', () => { - it('uses petname as first priority', () => { - mockPetname(PETNAME_MOCK); - mockFirstPartyContractName( - VALUE_MOCK, - VARIATION_MOCK, - FIRST_PARTY_CONTRACT_NAME_MOCK, - ); - mockNFT(VALUE_MOCK, VARIATION_MOCK, NFT_NAME_MOCK, NFT_IMAGE_MOCK, false); - mockERC20Token( - VALUE_MOCK, - VARIATION_MOCK, - ERC20_TOKEN_NAME_MOCK, - SYMBOL_MOCK, - ERC20_IMAGE_MOCK, - ); - mockWatchedNFTName(VALUE_MOCK, VARIATION_MOCK, WATCHED_NFT_NAME_MOCK); - - const { result } = renderHookWithProvider( - () => - useDisplayName({ - value: VALUE_MOCK, - type: NameType.ETHEREUM_ADDRESS, - variation: VARIATION_MOCK, - }), - state, - ); - - expect(result.current).toStrictEqual({ - contractDisplayName: ERC20_TOKEN_NAME_MOCK, - hasPetname: true, - image: NFT_IMAGE_MOCK, - name: PETNAME_MOCK, - }); - }); + it('does not return nft collection name if collection is marked as spam', () => { + const IMAGE_MOCK = 'url'; - it('uses first-party contract name as second priority', () => { - mockFirstPartyContractName( - VALUE_MOCK, - VARIATION_MOCK, - FIRST_PARTY_CONTRACT_NAME_MOCK, - ); - mockNFT(VALUE_MOCK, VARIATION_MOCK, NFT_NAME_MOCK, NFT_IMAGE_MOCK, false); - mockERC20Token( - VALUE_MOCK, - VARIATION_MOCK, - ERC20_TOKEN_NAME_MOCK, - SYMBOL_MOCK, - ERC20_IMAGE_MOCK, - ); - mockWatchedNFTName(VALUE_MOCK, VARIATION_MOCK, WATCHED_NFT_NAME_MOCK); - - const { result } = renderHookWithProvider( - () => - useDisplayName({ - value: VALUE_MOCK, - type: NameType.ETHEREUM_ADDRESS, - variation: VARIATION_MOCK, - }), - state, - ); - - expect(result.current).toStrictEqual({ - contractDisplayName: ERC20_TOKEN_NAME_MOCK, - hasPetname: false, - image: NFT_IMAGE_MOCK, - name: FIRST_PARTY_CONTRACT_NAME_MOCK, - }); + useNftCollectionsMetadataMock.mockReturnValue({ + [VALUE_MOCK.toLowerCase()]: { + name: CONTRACT_NAME_MOCK, + image: IMAGE_MOCK, + isSpam: true, + }, }); - it('uses NFT name as third priority', () => { - mockNFT(VALUE_MOCK, VARIATION_MOCK, NFT_NAME_MOCK, NFT_IMAGE_MOCK, false); - mockERC20Token( - VALUE_MOCK, - VARIATION_MOCK, - ERC20_TOKEN_NAME_MOCK, - SYMBOL_MOCK, - ERC20_IMAGE_MOCK, - ); - mockWatchedNFTName(VALUE_MOCK, VARIATION_MOCK, WATCHED_NFT_NAME_MOCK); - - const { result } = renderHookWithProvider( - () => - useDisplayName({ - value: VALUE_MOCK, - type: NameType.ETHEREUM_ADDRESS, - variation: VARIATION_MOCK, - }), - state, - ); - - expect(result.current).toStrictEqual({ - contractDisplayName: ERC20_TOKEN_NAME_MOCK, - hasPetname: false, - image: NFT_IMAGE_MOCK, - name: NFT_NAME_MOCK, - }); - }); + const { result } = renderHook(() => + useDisplayName(VALUE_MOCK, TYPE_MOCK, false), + ); - it('uses ERC-20 token name as fourth priority', () => { - mockERC20Token( - VALUE_MOCK, - VARIATION_MOCK, - ERC20_TOKEN_NAME_MOCK, - SYMBOL_MOCK, - ERC20_IMAGE_MOCK, - ); - mockWatchedNFTName(VALUE_MOCK, VARIATION_MOCK, WATCHED_NFT_NAME_MOCK); - - const { result } = renderHookWithProvider( - () => - useDisplayName({ - value: VALUE_MOCK, - type: NameType.ETHEREUM_ADDRESS, - variation: VARIATION_MOCK, - }), - state, - ); - - expect(result.current).toStrictEqual({ - contractDisplayName: ERC20_TOKEN_NAME_MOCK, - hasPetname: false, - image: ERC20_IMAGE_MOCK, - name: ERC20_TOKEN_NAME_MOCK, - }); - }); + expect(result.current).toEqual( + expect.objectContaining({ + name: null, + image: undefined, + }), + ); }); }); diff --git a/ui/hooks/useDisplayName.ts b/ui/hooks/useDisplayName.ts index 7b7429c7a0d4..64a878d2e357 100644 --- a/ui/hooks/useDisplayName.ts +++ b/ui/hooks/useDisplayName.ts @@ -1,20 +1,16 @@ +import { useMemo } from 'react'; import { NameType } from '@metamask/name-controller'; import { useSelector } from 'react-redux'; -import { Hex } from '@metamask/utils'; -import { selectERC20TokensByChain } from '../selectors'; -import { getNftContractsByAddressByChain } from '../selectors/nft'; -import { - EXPERIENCES_TYPE, - FIRST_PARTY_CONTRACT_NAMES, -} from '../../shared/constants/first-party-contracts'; +import { getRemoteTokens } from '../selectors'; +import { getNftContractsByAddressOnCurrentChain } from '../selectors/nft'; import { useNames } from './useName'; +import { useFirstPartyContractNames } from './useFirstPartyContractName'; import { useNftCollectionsMetadata } from './useNftCollectionsMetadata'; export type UseDisplayNameRequest = { + value: string; preferContractSymbol?: boolean; type: NameType; - value: string; - variation: string; }; export type UseDisplayNameResponse = { @@ -27,145 +23,79 @@ export type UseDisplayNameResponse = { export function useDisplayNames( requests: UseDisplayNameRequest[], ): UseDisplayNameResponse[] { - const nameEntries = useNames(requests); - const firstPartyContractNames = useFirstPartyContractNames(requests); - const erc20Tokens = useERC20Tokens(requests); - const watchedNFTNames = useWatchedNFTNames(requests); - const nfts = useNFTs(requests); + const nameRequests = useMemo( + () => requests.map(({ value, type }) => ({ value, type })), + [requests], + ); + + const nameEntries = useNames(nameRequests); + const firstPartyContractNames = useFirstPartyContractNames(nameRequests); + const nftCollections = useNftCollectionsMetadata(nameRequests); + const values = requests.map(({ value }) => value); + + const contractInfo = useSelector((state) => + // TODO: Replace `any` with type + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (getRemoteTokens as any)(state, values), + ); - return requests.map((_request, index) => { + const watchedNftNames = useSelector(getNftContractsByAddressOnCurrentChain); + + return requests.map(({ value, preferContractSymbol }, index) => { const nameEntry = nameEntries[index]; const firstPartyContractName = firstPartyContractNames[index]; - const erc20Token = erc20Tokens[index]; - const watchedNftName = watchedNFTNames[index]; - const nft = nfts[index]; + const singleContractInfo = contractInfo[index]; + const watchedNftName = watchedNftNames[value.toLowerCase()]?.name; + const nftCollectionProperties = nftCollections[value.toLowerCase()]; + + const isNotSpam = nftCollectionProperties?.isSpam === false; + + const nftCollectionName = isNotSpam + ? nftCollectionProperties?.name + : undefined; + const nftCollectionImage = isNotSpam + ? nftCollectionProperties?.image + : undefined; + + const contractDisplayName = + preferContractSymbol && singleContractInfo?.symbol + ? singleContractInfo.symbol + : singleContractInfo?.name; const name = nameEntry?.name || firstPartyContractName || - nft?.name || - erc20Token?.name || + nftCollectionName || + contractDisplayName || watchedNftName || null; - const image = nft?.image || erc20Token?.image; - const hasPetname = Boolean(nameEntry?.name); return { name, hasPetname, - contractDisplayName: erc20Token?.name, - image, + contractDisplayName, + image: nftCollectionImage, }; }); } +/** + * Attempts to resolve the name for the given parameters. + * + * @param value - The address or contract address to resolve. + * @param type - The type of value, e.g. NameType.ETHEREUM_ADDRESS. + * @param preferContractSymbol - Applies to recognized contracts when no petname is saved: + * If true the contract symbol (e.g. WBTC) will be used instead of the contract name. + * @returns An object with two properties: + * - `name` {string|null} - The display name, if it can be resolved, otherwise null. + * - `hasPetname` {boolean} - True if there is a petname for the given address. + */ export function useDisplayName( - request: UseDisplayNameRequest, + value: string, + type: NameType, + preferContractSymbol: boolean = false, ): UseDisplayNameResponse { - return useDisplayNames([request])[0]; -} - -function useERC20Tokens( - nameRequests: UseDisplayNameRequest[], -): ({ name?: string; image?: string } | undefined)[] { - const erc20TokensByChain = useSelector(selectERC20TokensByChain); - - return nameRequests.map( - ({ preferContractSymbol, type, value, variation }) => { - if (type !== NameType.ETHEREUM_ADDRESS) { - return undefined; - } - - const contractAddress = value.toLowerCase(); - - const { - iconUrl: image, - name: tokenName, - symbol, - } = erc20TokensByChain?.[variation]?.data?.[contractAddress] ?? {}; - - const name = preferContractSymbol && symbol ? symbol : tokenName; - - return { name, image }; - }, - ); -} - -function useWatchedNFTNames( - nameRequests: UseDisplayNameRequest[], -): (string | undefined)[] { - const watchedNftNamesByAddressByChain = useSelector( - getNftContractsByAddressByChain, - ); - - return nameRequests.map(({ type, value, variation }) => { - if (type !== NameType.ETHEREUM_ADDRESS) { - return undefined; - } - - const contractAddress = value.toLowerCase(); - const watchedNftNamesByAddress = watchedNftNamesByAddressByChain[variation]; - return watchedNftNamesByAddress?.[contractAddress]?.name; - }); -} - -function useNFTs( - nameRequests: UseDisplayNameRequest[], -): ({ name?: string; image?: string } | undefined)[] { - const requests = nameRequests - .filter(({ type }) => type === NameType.ETHEREUM_ADDRESS) - .map(({ value, variation }) => ({ - chainId: variation, - contractAddress: value, - })); - - const nftCollectionsByAddressByChain = useNftCollectionsMetadata(requests); - - return nameRequests.map( - ({ type, value: contractAddress, variation: chainId }) => { - if (type !== NameType.ETHEREUM_ADDRESS) { - return undefined; - } - - const nftCollectionProperties = - nftCollectionsByAddressByChain[chainId]?.[ - contractAddress.toLowerCase() - ]; - - const isSpam = nftCollectionProperties?.isSpam !== false; - - if (!nftCollectionProperties || isSpam) { - return undefined; - } - - const { name, image } = nftCollectionProperties; - - return { name, image }; - }, - ); -} - -function useFirstPartyContractNames(nameRequests: UseDisplayNameRequest[]) { - return nameRequests.map(({ type, value, variation }) => { - if (type !== NameType.ETHEREUM_ADDRESS) { - return undefined; - } - - const normalizedContractAddress = value.toLowerCase(); - - const contractNames = Object.keys( - FIRST_PARTY_CONTRACT_NAMES, - ) as EXPERIENCES_TYPE[]; - - return contractNames.find((contractName) => { - const currentContractAddress = - FIRST_PARTY_CONTRACT_NAMES[contractName]?.[variation as Hex]; - - return ( - currentContractAddress?.toLowerCase() === normalizedContractAddress - ); - }); - }); + return useDisplayNames([{ preferContractSymbol, type, value }])[0]; } diff --git a/ui/hooks/useFirstPartyContractName.test.ts b/ui/hooks/useFirstPartyContractName.test.ts new file mode 100644 index 000000000000..14d0cd429e6f --- /dev/null +++ b/ui/hooks/useFirstPartyContractName.test.ts @@ -0,0 +1,78 @@ +import { NameType } from '@metamask/name-controller'; +import { getCurrentChainId } from '../selectors'; +import { CHAIN_IDS } from '../../shared/constants/network'; +import { useFirstPartyContractName } from './useFirstPartyContractName'; + +jest.mock('react-redux', () => ({ + // TODO: Replace `any` with type + // eslint-disable-next-line @typescript-eslint/no-explicit-any + useSelector: (selector: any) => selector(), +})); + +jest.mock('../selectors', () => ({ + getCurrentChainId: jest.fn(), + getNames: jest.fn(), +})); + +const BRIDGE_NAME_MOCK = 'MetaMask Bridge'; +const BRIDGE_MAINNET_ADDRESS_MOCK = + '0x0439e60F02a8900a951603950d8D4527f400C3f1'; +const BRIDGE_OPTIMISM_ADDRESS_MOCK = + '0xB90357f2b86dbfD59c3502215d4060f71DF8ca0e'; +const UNKNOWN_ADDRESS_MOCK = '0xabc123'; + +describe('useFirstPartyContractName', () => { + const getCurrentChainIdMock = jest.mocked(getCurrentChainId); + beforeEach(() => { + jest.resetAllMocks(); + + getCurrentChainIdMock.mockReturnValue(CHAIN_IDS.MAINNET); + }); + + it('returns null if no name found', () => { + const name = useFirstPartyContractName( + UNKNOWN_ADDRESS_MOCK, + NameType.ETHEREUM_ADDRESS, + ); + + expect(name).toBe(null); + }); + + it('returns name if found', () => { + const name = useFirstPartyContractName( + BRIDGE_MAINNET_ADDRESS_MOCK, + NameType.ETHEREUM_ADDRESS, + ); + expect(name).toBe(BRIDGE_NAME_MOCK); + }); + + it('uses variation if specified', () => { + const name = useFirstPartyContractName( + BRIDGE_OPTIMISM_ADDRESS_MOCK, + NameType.ETHEREUM_ADDRESS, + CHAIN_IDS.OPTIMISM, + ); + + expect(name).toBe(BRIDGE_NAME_MOCK); + }); + + it('returns null if type is not address', () => { + const alternateType = 'alternateType' as NameType; + + const name = useFirstPartyContractName( + BRIDGE_MAINNET_ADDRESS_MOCK, + alternateType, + ); + + expect(name).toBe(null); + }); + + it('normalizes addresses to lowercase', () => { + const name = useFirstPartyContractName( + BRIDGE_MAINNET_ADDRESS_MOCK.toUpperCase(), + NameType.ETHEREUM_ADDRESS, + ); + + expect(name).toBe(BRIDGE_NAME_MOCK); + }); +}); diff --git a/ui/hooks/useFirstPartyContractName.ts b/ui/hooks/useFirstPartyContractName.ts new file mode 100644 index 000000000000..47468b472955 --- /dev/null +++ b/ui/hooks/useFirstPartyContractName.ts @@ -0,0 +1,45 @@ +import { NameType } from '@metamask/name-controller'; +import { useSelector } from 'react-redux'; +import { getCurrentChainId } from '../selectors'; +import { + EXPERIENCES_TYPE, + FIRST_PARTY_CONTRACT_NAMES, +} from '../../shared/constants/first-party-contracts'; + +export type UseFirstPartyContractNameRequest = { + value: string; + type: NameType; + variation?: string; +}; + +export function useFirstPartyContractNames( + requests: UseFirstPartyContractNameRequest[], +): (string | null)[] { + const currentChainId = useSelector(getCurrentChainId); + + return requests.map(({ type, value, variation }) => { + if (type !== NameType.ETHEREUM_ADDRESS) { + return null; + } + + const chainId = variation ?? currentChainId; + const normalizedValue = value.toLowerCase(); + + return ( + Object.keys(FIRST_PARTY_CONTRACT_NAMES).find( + (name) => + FIRST_PARTY_CONTRACT_NAMES[name as EXPERIENCES_TYPE]?.[ + chainId + ]?.toLowerCase() === normalizedValue, + ) ?? null + ); + }); +} + +export function useFirstPartyContractName( + value: string, + type: NameType, + variation?: string, +): string | null { + return useFirstPartyContractNames([{ value, type, variation }])[0]; +} diff --git a/ui/hooks/useGasFeeEstimates.js b/ui/hooks/useGasFeeEstimates.js index abbaf0db0bb9..5ad37925054b 100644 --- a/ui/hooks/useGasFeeEstimates.js +++ b/ui/hooks/useGasFeeEstimates.js @@ -74,10 +74,9 @@ export function useGasFeeEstimates(_networkClientId) { }, [networkClientId]); usePolling({ - startPolling: (input) => - gasFeeStartPollingByNetworkClientId(input.networkClientId), + startPollingByNetworkClientId: gasFeeStartPollingByNetworkClientId, stopPollingByPollingToken: gasFeeStopPollingByPollingToken, - input: { networkClientId }, + networkClientId, }); return { diff --git a/ui/hooks/useGasFeeEstimates.test.js b/ui/hooks/useGasFeeEstimates.test.js index dd63e10581d0..0187ac793bbe 100644 --- a/ui/hooks/useGasFeeEstimates.test.js +++ b/ui/hooks/useGasFeeEstimates.test.js @@ -8,6 +8,7 @@ import { getIsNetworkBusyByChainId, } from '../ducks/metamask/metamask'; import { + gasFeeStartPollingByNetworkClientId, gasFeeStopPollingByPollingToken, getNetworkConfigurationByNetworkClientId, } from '../store/actions'; @@ -114,9 +115,9 @@ describe('useGasFeeEstimates', () => { renderHook(() => useGasFeeEstimates()); }); expect(usePolling).toHaveBeenCalledWith({ - startPolling: expect.any(Function), + startPollingByNetworkClientId: gasFeeStartPollingByNetworkClientId, stopPollingByPollingToken: gasFeeStopPollingByPollingToken, - input: { networkClientId: 'selectedNetworkClientId' }, + networkClientId: 'selectedNetworkClientId', }); }); @@ -126,9 +127,9 @@ describe('useGasFeeEstimates', () => { renderHook(() => useGasFeeEstimates('networkClientId1')); }); expect(usePolling).toHaveBeenCalledWith({ - startPolling: expect.any(Function), + startPollingByNetworkClientId: gasFeeStartPollingByNetworkClientId, stopPollingByPollingToken: gasFeeStopPollingByPollingToken, - input: { networkClientId: 'networkClientId1' }, + networkClientId: 'networkClientId1', }); }); diff --git a/ui/hooks/useMultichainSelector.ts b/ui/hooks/useMultichainSelector.ts index 9bd979df7e7e..326ac79bf9cd 100644 --- a/ui/hooks/useMultichainSelector.ts +++ b/ui/hooks/useMultichainSelector.ts @@ -11,7 +11,6 @@ export function useMultichainSelector< ) { return useSelector((state: TState) => { // We either pass an account or fallback to the currently selected one - // @ts-expect-error state types don't match return selector(state, account || getSelectedInternalAccount(state)); }); } diff --git a/ui/hooks/useName.test.ts b/ui/hooks/useName.test.ts index f746c4bb6267..76bd5dc593ad 100644 --- a/ui/hooks/useName.test.ts +++ b/ui/hooks/useName.test.ts @@ -5,7 +5,7 @@ import { NameOrigin, NameType, } from '@metamask/name-controller'; -import { getNames } from '../selectors'; +import { getCurrentChainId, getNames } from '../selectors'; import { useName } from './useName'; jest.mock('react-redux', () => ({ @@ -19,14 +19,13 @@ jest.mock('../selectors', () => ({ getNames: jest.fn(), })); -const VARIATION_MOCK = '0x1'; -const VARIATION_2_MOCK = '0x2'; +const CHAIN_ID_MOCK = '0x1'; +const CHAIN_ID_2_MOCK = '0x2'; const VALUE_MOCK = '0xabc123'; const TYPE_MOCK = NameType.ETHEREUM_ADDRESS; const NAME_MOCK = 'TestName'; const SOURCE_ID_MOCK = 'TestSourceId'; const ORIGIN_MOCK = NameOrigin.API; - const PROPOSED_NAMES_MOCK = { [SOURCE_ID_MOCK]: { proposedNames: ['TestProposedName', 'TestProposedName2'], @@ -36,6 +35,7 @@ const PROPOSED_NAMES_MOCK = { }; describe('useName', () => { + const getCurrentChainIdMock = jest.mocked(getCurrentChainId); const getNamesMock = // TODO: Replace `any` with type // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -43,12 +43,14 @@ describe('useName', () => { beforeEach(() => { jest.resetAllMocks(); + + getCurrentChainIdMock.mockReturnValue(CHAIN_ID_MOCK); }); it('returns default values if no state', () => { getNamesMock.mockReturnValue({} as NameControllerState['names']); - const nameEntry = useName(VALUE_MOCK, TYPE_MOCK, VARIATION_MOCK); + const nameEntry = useName(VALUE_MOCK, TYPE_MOCK); expect(nameEntry).toStrictEqual({ name: null, @@ -62,7 +64,7 @@ describe('useName', () => { getNamesMock.mockReturnValue({ [TYPE_MOCK]: { [VALUE_MOCK]: { - [VARIATION_2_MOCK]: { + [CHAIN_ID_2_MOCK]: { name: NAME_MOCK, proposedNames: PROPOSED_NAMES_MOCK, sourceId: SOURCE_ID_MOCK, @@ -72,7 +74,7 @@ describe('useName', () => { }, }); - const nameEntry = useName(VALUE_MOCK, TYPE_MOCK, VARIATION_MOCK); + const nameEntry = useName(VALUE_MOCK, TYPE_MOCK); expect(nameEntry).toStrictEqual({ name: null, @@ -86,7 +88,7 @@ describe('useName', () => { getNamesMock.mockReturnValue({ [TYPE_MOCK]: { [VALUE_MOCK]: { - [VARIATION_MOCK]: { + [CHAIN_ID_MOCK]: { name: NAME_MOCK, proposedNames: PROPOSED_NAMES_MOCK, sourceId: SOURCE_ID_MOCK, @@ -96,7 +98,7 @@ describe('useName', () => { }, }); - const nameEntry = useName(VALUE_MOCK, TYPE_MOCK, VARIATION_MOCK); + const nameEntry = useName(VALUE_MOCK, TYPE_MOCK); expect(nameEntry).toStrictEqual({ name: NAME_MOCK, @@ -110,7 +112,7 @@ describe('useName', () => { getNamesMock.mockReturnValue({ [TYPE_MOCK]: { [VALUE_MOCK]: { - [VARIATION_2_MOCK]: { + [CHAIN_ID_2_MOCK]: { name: NAME_MOCK, proposedNames: PROPOSED_NAMES_MOCK, sourceId: SOURCE_ID_MOCK, @@ -120,7 +122,7 @@ describe('useName', () => { }, }); - const nameEntry = useName(VALUE_MOCK, TYPE_MOCK, VARIATION_2_MOCK); + const nameEntry = useName(VALUE_MOCK, TYPE_MOCK, CHAIN_ID_2_MOCK); expect(nameEntry).toStrictEqual({ name: NAME_MOCK, @@ -145,7 +147,7 @@ describe('useName', () => { }, }); - const nameEntry = useName(VALUE_MOCK, TYPE_MOCK, VARIATION_2_MOCK); + const nameEntry = useName(VALUE_MOCK, TYPE_MOCK, CHAIN_ID_2_MOCK); expect(nameEntry).toStrictEqual({ name: NAME_MOCK, @@ -159,7 +161,7 @@ describe('useName', () => { getNamesMock.mockReturnValue({ [TYPE_MOCK]: { [VALUE_MOCK]: { - [VARIATION_2_MOCK]: { + [CHAIN_ID_2_MOCK]: { name: null, proposedNames: PROPOSED_NAMES_MOCK, sourceId: null, @@ -175,7 +177,7 @@ describe('useName', () => { }, }); - const nameEntry = useName(VALUE_MOCK, TYPE_MOCK, VARIATION_2_MOCK); + const nameEntry = useName(VALUE_MOCK, TYPE_MOCK, CHAIN_ID_2_MOCK); expect(nameEntry).toStrictEqual({ name: NAME_MOCK, @@ -186,11 +188,37 @@ describe('useName', () => { }); }); + it('uses empty string as variation if not specified and type is not address', () => { + const alternateType = 'alternateType' as NameType; + + getNamesMock.mockReturnValue({ + [alternateType]: { + [VALUE_MOCK]: { + '': { + name: NAME_MOCK, + proposedNames: PROPOSED_NAMES_MOCK, + sourceId: SOURCE_ID_MOCK, + origin: ORIGIN_MOCK, + }, + }, + }, + }); + + const nameEntry = useName(VALUE_MOCK, alternateType); + + expect(nameEntry).toStrictEqual({ + name: NAME_MOCK, + sourceId: SOURCE_ID_MOCK, + proposedNames: PROPOSED_NAMES_MOCK, + origin: ORIGIN_MOCK, + }); + }); + it('normalizes addresses to lowercase', () => { getNamesMock.mockReturnValue({ [TYPE_MOCK]: { [VALUE_MOCK]: { - [VARIATION_MOCK]: { + [CHAIN_ID_MOCK]: { name: NAME_MOCK, proposedNames: PROPOSED_NAMES_MOCK, sourceId: SOURCE_ID_MOCK, @@ -200,7 +228,7 @@ describe('useName', () => { }, }); - const nameEntry = useName('0xAbC123', TYPE_MOCK, VARIATION_MOCK); + const nameEntry = useName('0xAbC123', TYPE_MOCK); expect(nameEntry).toStrictEqual({ name: NAME_MOCK, diff --git a/ui/hooks/useName.ts b/ui/hooks/useName.ts index dd587e81abf6..3af9b0457f79 100644 --- a/ui/hooks/useName.ts +++ b/ui/hooks/useName.ts @@ -5,29 +5,32 @@ import { } from '@metamask/name-controller'; import { useSelector } from 'react-redux'; import { isEqual } from 'lodash'; -import { getNames } from '../selectors'; +import { getCurrentChainId, getNames } from '../selectors'; export type UseNameRequest = { value: string; type: NameType; - variation: string; + variation?: string; }; export function useName( value: string, type: NameType, - variation: string, + variation?: string, ): NameEntry { return useNames([{ value, type, variation }])[0]; } export function useNames(requests: UseNameRequest[]): NameEntry[] { const names = useSelector(getNames, isEqual); + const chainId = useSelector(getCurrentChainId); return requests.map(({ value, type, variation }) => { const normalizedValue = normalizeValue(value, type); + const typeVariationKey = getVariationKey(type, chainId); + const variationKey = variation ?? typeVariationKey; const variationsToNameEntries = names[type]?.[normalizedValue] ?? {}; - const variationEntry = variationsToNameEntries[variation]; + const variationEntry = variationsToNameEntries[variationKey]; const fallbackEntry = variationsToNameEntries[FALLBACK_VARIATION]; const entry = @@ -60,3 +63,13 @@ function normalizeValue(value: string, type: string): string { return value; } } + +function getVariationKey(type: string, chainId: string): string { + switch (type) { + case NameType.ETHEREUM_ADDRESS: + return chainId; + + default: + return ''; + } +} diff --git a/ui/hooks/useNftCollectionsMetadata.test.ts b/ui/hooks/useNftCollectionsMetadata.test.ts index e1e2b6745ad1..4897e449e6ad 100644 --- a/ui/hooks/useNftCollectionsMetadata.test.ts +++ b/ui/hooks/useNftCollectionsMetadata.test.ts @@ -1,5 +1,6 @@ import { renderHook } from '@testing-library/react-hooks'; import { TokenStandard } from '../../shared/constants/transaction'; +import { getCurrentChainId } from '../selectors'; import { getNFTContractInfo, getTokenStandardAndDetails, @@ -41,6 +42,7 @@ const ERC_721_COLLECTION_2_MOCK = { }; describe('useNftCollectionsMetadata', () => { + const mockGetCurrentChainId = jest.mocked(getCurrentChainId); const mockGetNFTContractInfo = jest.mocked(getNFTContractInfo); const mockGetTokenStandardAndDetails = jest.mocked( getTokenStandardAndDetails, @@ -48,6 +50,7 @@ describe('useNftCollectionsMetadata', () => { beforeEach(() => { jest.resetAllMocks(); + mockGetCurrentChainId.mockReturnValue(CHAIN_ID_MOCK); mockGetNFTContractInfo.mockResolvedValue({ collections: [ERC_721_COLLECTION_1_MOCK, ERC_721_COLLECTION_2_MOCK], }); @@ -64,12 +67,10 @@ describe('useNftCollectionsMetadata', () => { const { result, waitForNextUpdate } = renderHook(() => useNftCollectionsMetadata([ { - chainId: CHAIN_ID_MOCK, - contractAddress: ERC_721_ADDRESS_1, + value: ERC_721_ADDRESS_1, }, { - chainId: CHAIN_ID_MOCK, - contractAddress: ERC_721_ADDRESS_2, + value: ERC_721_ADDRESS_2, }, ]), ); @@ -78,10 +79,8 @@ describe('useNftCollectionsMetadata', () => { expect(mockGetNFTContractInfo).toHaveBeenCalledTimes(1); expect(result.current).toStrictEqual({ - [CHAIN_ID_MOCK]: { - [ERC_721_ADDRESS_1.toLowerCase()]: ERC_721_COLLECTION_1_MOCK, - [ERC_721_ADDRESS_2.toLowerCase()]: ERC_721_COLLECTION_2_MOCK, - }, + [ERC_721_ADDRESS_1.toLowerCase()]: ERC_721_COLLECTION_1_MOCK, + [ERC_721_ADDRESS_2.toLowerCase()]: ERC_721_COLLECTION_2_MOCK, }); }); @@ -100,8 +99,7 @@ describe('useNftCollectionsMetadata', () => { renderHook(() => useNftCollectionsMetadata([ { - chainId: CHAIN_ID_MOCK, - contractAddress: '0xERC20Address', + value: '0xERC20Address', }, ]), ); @@ -116,8 +114,7 @@ describe('useNftCollectionsMetadata', () => { renderHook(() => useNftCollectionsMetadata([ { - chainId: CHAIN_ID_MOCK, - contractAddress: '0xERC20Address', + value: '0xERC20Address', }, ]), ); @@ -129,12 +126,10 @@ describe('useNftCollectionsMetadata', () => { const { waitForNextUpdate, rerender } = renderHook(() => useNftCollectionsMetadata([ { - chainId: CHAIN_ID_MOCK, - contractAddress: ERC_721_ADDRESS_1, + value: ERC_721_ADDRESS_1, }, { - chainId: CHAIN_ID_MOCK, - contractAddress: ERC_721_ADDRESS_2, + value: ERC_721_ADDRESS_2, }, ]), ); diff --git a/ui/hooks/useNftCollectionsMetadata.ts b/ui/hooks/useNftCollectionsMetadata.ts index e71216e254c9..641e0fb25dcd 100644 --- a/ui/hooks/useNftCollectionsMetadata.ts +++ b/ui/hooks/useNftCollectionsMetadata.ts @@ -1,5 +1,9 @@ +import { useMemo } from 'react'; +import { useSelector } from 'react-redux'; import { Collection } from '@metamask/assets-controllers'; +import type { Hex } from '@metamask/utils'; import { TokenStandard } from '../../shared/constants/transaction'; +import { getCurrentChainId } from '../selectors'; import { getNFTContractInfo, getTokenStandardAndDetails, @@ -7,62 +11,28 @@ import { import { useAsyncResult } from './useAsyncResult'; export type UseNftCollectionsMetadataRequest = { - chainId: string; - contractAddress: string; + value: string; + chainId?: string; +}; + +type CollectionsData = { + [key: string]: Collection; }; // For now, we only support ERC721 tokens const SUPPORTED_NFT_TOKEN_STANDARDS = [TokenStandard.ERC721]; -export function useNftCollectionsMetadata( - requests: UseNftCollectionsMetadataRequest[], -): Record> { - const { value: collectionsMetadata } = useAsyncResult( - () => fetchCollections(requests), - [JSON.stringify(requests)], - ); - - return collectionsMetadata ?? {}; -} - -async function fetchCollections(requests: UseNftCollectionsMetadataRequest[]) { - const valuesByChainId = requests.reduce>( - (acc, { chainId, contractAddress }) => { - acc[chainId] = [...(acc[chainId] ?? []), contractAddress.toLowerCase()]; - return acc; - }, - {}, - ); - - const chainIds = Object.keys(valuesByChainId); - - const responses = await Promise.all( - chainIds.map((chainId) => { - const contractAddresses = valuesByChainId[chainId]; - return fetchCollectionsForChain(contractAddresses, chainId); - }), - ); - - return chainIds.reduce>>( - (acc, chainId, index) => { - acc[chainId] = responses[index]; - return acc; - }, - {}, - ); -} - -async function fetchCollectionsForChain( - contractAddresses: string[], +async function fetchCollections( + memoisedContracts: string[], chainId: string, -) { +): Promise { const contractStandardsResponses = await Promise.all( - contractAddresses.map((contractAddress) => + memoisedContracts.map((contractAddress) => getTokenStandardAndDetails(contractAddress, chainId), ), ); - const supportedNFTContracts = contractAddresses.filter( + const supportedNFTContracts = memoisedContracts.filter( (_contractAddress, index) => SUPPORTED_NFT_TOKEN_STANDARDS.includes( contractStandardsResponses[index].standard as TokenStandard, @@ -78,16 +48,37 @@ async function fetchCollectionsForChain( chainId, ); - const collectionsData = collectionsResult.collections.reduce< - Record - >((acc, collection, index) => { - acc[supportedNFTContracts[index]] = { - name: collection?.name, - image: collection?.image, - isSpam: collection?.isSpam, - }; - return acc; - }, {}); + const collectionsData: CollectionsData = collectionsResult.collections.reduce( + (acc: CollectionsData, collection, index) => { + acc[supportedNFTContracts[index]] = { + name: collection?.name, + image: collection?.image, + isSpam: collection?.isSpam, + }; + return acc; + }, + {}, + ); return collectionsData; } + +export function useNftCollectionsMetadata( + requests: UseNftCollectionsMetadataRequest[], + providedChainId?: Hex, +) { + const chainId = useSelector(getCurrentChainId) || providedChainId; + + const memoisedContracts = useMemo(() => { + return requests + .filter(({ value }) => value) + .map(({ value }) => value.toLowerCase()); + }, [requests]); + + const { value: collectionsMetadata } = useAsyncResult( + () => fetchCollections(memoisedContracts, chainId), + [JSON.stringify(memoisedContracts), chainId], + ); + + return collectionsMetadata || {}; +} diff --git a/ui/hooks/usePolling.test.js b/ui/hooks/usePolling.test.js index a556bb86be54..9250257d3cbc 100644 --- a/ui/hooks/usePolling.test.js +++ b/ui/hooks/usePolling.test.js @@ -4,12 +4,13 @@ import usePolling from './usePolling'; describe('usePolling', () => { // eslint-disable-next-line jest/no-done-callback - it('calls startPolling and calls back with polling token when component instantiating the hook mounts', (done) => { + it('calls startPollingByNetworkClientId and callback option args with polling token when component instantiating the hook mounts', (done) => { const mockStart = jest.fn().mockImplementation(() => { return Promise.resolve('pollingToken'); }); const mockStop = jest.fn(); const networkClientId = 'mainnet'; + const options = {}; const mockState = { metamask: {}, }; @@ -17,16 +18,17 @@ describe('usePolling', () => { renderHookWithProvider(() => { usePolling({ callback: (pollingToken) => { - expect(mockStart).toHaveBeenCalledWith({ networkClientId }); + expect(mockStart).toHaveBeenCalledWith(networkClientId, options); expect(pollingToken).toBeDefined(); done(); return (_pollingToken) => { // noop }; }, - startPolling: mockStart, + startPollingByNetworkClientId: mockStart, stopPollingByPollingToken: mockStop, - input: { networkClientId }, + networkClientId, + options, }); }, mockState); }); @@ -37,6 +39,7 @@ describe('usePolling', () => { }); const mockStop = jest.fn(); const networkClientId = 'mainnet'; + const options = {}; const mockState = { metamask: {}, }; @@ -51,9 +54,10 @@ describe('usePolling', () => { done(); }; }, - startPolling: mockStart, + startPollingByNetworkClientId: mockStart, stopPollingByPollingToken: mockStop, - input: { networkClientId }, + networkClientId, + options, }), mockState, ); diff --git a/ui/hooks/usePolling.ts b/ui/hooks/usePolling.ts index 613e70cf17b5..1a9d6b1f576e 100644 --- a/ui/hooks/usePolling.ts +++ b/ui/hooks/usePolling.ts @@ -1,16 +1,22 @@ import { useEffect, useRef } from 'react'; -type UsePollingOptions = { +type UsePollingOptions = { callback?: (pollingToken: string) => (pollingToken: string) => void; - startPolling: (input: PollingInput) => Promise; + startPollingByNetworkClientId: ( + networkClientId: string, + // TODO: Replace `any` with type + // eslint-disable-next-line @typescript-eslint/no-explicit-any + options: any, + ) => Promise; stopPollingByPollingToken: (pollingToken: string) => void; - input: PollingInput; + networkClientId: string; + // TODO: Replace `any` with type + // eslint-disable-next-line @typescript-eslint/no-explicit-any + options?: any; enabled?: boolean; }; -const usePolling = ( - usePollingOptions: UsePollingOptions, -) => { +const usePolling = (usePollingOptions: UsePollingOptions) => { const pollTokenRef = useRef(null); const cleanupRef = useRef void)>(null); let isMounted = false; @@ -32,7 +38,10 @@ const usePolling = ( // Start polling when the component mounts usePollingOptions - .startPolling(usePollingOptions.input) + .startPollingByNetworkClientId( + usePollingOptions.networkClientId, + usePollingOptions.options, + ) .then((pollToken) => { pollTokenRef.current = pollToken; cleanupRef.current = usePollingOptions.callback?.(pollToken) || null; @@ -47,7 +56,12 @@ const usePolling = ( cleanup(); }; }, [ - usePollingOptions.input && JSON.stringify(usePollingOptions.input), + usePollingOptions.networkClientId, + usePollingOptions.options && + JSON.stringify( + usePollingOptions.options, + Object.keys(usePollingOptions.options).sort(), + ), usePollingOptions.enabled, ]); }; diff --git a/ui/pages/asset/components/__snapshots__/asset-page.test.tsx.snap b/ui/pages/asset/components/__snapshots__/asset-page.test.tsx.snap index 79400367de13..95828e3e250e 100644 --- a/ui/pages/asset/components/__snapshots__/asset-page.test.tsx.snap +++ b/ui/pages/asset/components/__snapshots__/asset-page.test.tsx.snap @@ -268,17 +268,17 @@ exports[`AssetPage should render a native asset 1`] = `

- Tips for using a wallet + Start your journey with ETH

- Adding tokens unlocks more ways to use web3. + Get started with web3 by adding some ETH to your wallet.

- Tips for using a wallet + Start your journey with ETH

- Adding tokens unlocks more ways to use web3. + Get started with web3 by adding some ETH to your wallet.

- Tips for using a wallet + Start your journey with ETH

- Adding tokens unlocks more ways to use web3. + Get started with web3 by adding some ETH to your wallet.

{ - beforeEach(() => { - jest.clearAllMocks(); - }); - describe('fetchBridgeFeatureFlags', () => { it('should fetch bridge feature flags successfully', async () => { const mockResponse = { - 'extension-config': { - refreshRate: 3, - maxRefreshCount: 1, - }, 'extension-support': true, 'src-network-allowlist': [1, 10, 59144, 120], 'dest-network-allowlist': [1, 137, 59144, 11111], @@ -43,10 +28,6 @@ describe('Bridge utils', () => { }); expect(result).toStrictEqual({ - extensionConfig: { - maxRefreshCount: 1, - refreshRate: 3, - }, extensionSupport: true, srcNetworkAllowlist: [ CHAIN_IDS.MAINNET, @@ -65,10 +46,8 @@ describe('Bridge utils', () => { it('should use fallback bridge feature flags if response is unexpected', async () => { const mockResponse = { - 'extension-support': 25, - 'src-network-allowlist': ['a', 'b', 1], - a: 'b', - 'dest-network-allowlist': [1, 137, 59144, 11111], + flag1: true, + flag2: false, }; (fetchWithCache as jest.Mock).mockResolvedValue(mockResponse); @@ -86,10 +65,6 @@ describe('Bridge utils', () => { }); expect(result).toStrictEqual({ - extensionConfig: { - maxRefreshCount: 5, - refreshRate: 30000, - }, extensionSupport: false, srcNetworkAllowlist: [], destNetworkAllowlist: [], @@ -166,113 +141,4 @@ describe('Bridge utils', () => { await expect(fetchBridgeTokens('0xa')).rejects.toThrowError(mockError); }); }); - - describe('fetchBridgeQuotes', () => { - it('should fetch bridge quotes successfully, no approvals', async () => { - (fetchWithCache as jest.Mock).mockResolvedValue( - mockBridgeQuotesNativeErc20, - ); - - const result = await fetchBridgeQuotes({ - walletAddress: '0x123', - srcChainId: 1, - destChainId: 10, - srcTokenAddress: zeroAddress(), - destTokenAddress: zeroAddress(), - srcTokenAmount: '20000', - slippage: 0.5, - }); - - expect(fetchWithCache).toHaveBeenCalledWith({ - url: 'https://bridge.api.cx.metamask.io/getQuote?walletAddress=0x123&srcChainId=1&destChainId=10&srcTokenAddress=0x0000000000000000000000000000000000000000&destTokenAddress=0x0000000000000000000000000000000000000000&srcTokenAmount=20000&slippage=0.5&insufficientBal=false&resetApproval=false', - fetchOptions: { - method: 'GET', - headers: { 'X-Client-Id': 'extension' }, - }, - cacheOptions: { cacheRefreshTime: 0 }, - functionName: 'fetchBridgeQuotes', - }); - - expect(result).toStrictEqual(mockBridgeQuotesNativeErc20); - }); - - it('should fetch bridge quotes successfully, with approvals', async () => { - (fetchWithCache as jest.Mock).mockResolvedValue([ - ...mockBridgeQuotesErc20Erc20, - { ...mockBridgeQuotesErc20Erc20[0], approval: null }, - { ...mockBridgeQuotesErc20Erc20[0], trade: null }, - ]); - - const result = await fetchBridgeQuotes({ - walletAddress: '0x123', - srcChainId: 1, - destChainId: 10, - srcTokenAddress: zeroAddress(), - destTokenAddress: zeroAddress(), - srcTokenAmount: '20000', - slippage: 0.5, - }); - - expect(fetchWithCache).toHaveBeenCalledWith({ - url: 'https://bridge.api.cx.metamask.io/getQuote?walletAddress=0x123&srcChainId=1&destChainId=10&srcTokenAddress=0x0000000000000000000000000000000000000000&destTokenAddress=0x0000000000000000000000000000000000000000&srcTokenAmount=20000&slippage=0.5&insufficientBal=false&resetApproval=false', - fetchOptions: { - method: 'GET', - headers: { 'X-Client-Id': 'extension' }, - }, - cacheOptions: { cacheRefreshTime: 0 }, - functionName: 'fetchBridgeQuotes', - }); - - expect(result).toStrictEqual(mockBridgeQuotesErc20Erc20); - }); - - it('should filter out malformed bridge quotes', async () => { - (fetchWithCache as jest.Mock).mockResolvedValue([ - ...mockBridgeQuotesErc20Erc20, - ...mockBridgeQuotesErc20Erc20.map( - ({ quote, ...restOfQuote }) => restOfQuote, - ), - { - ...mockBridgeQuotesErc20Erc20[0], - quote: { - srcAsset: { - ...mockBridgeQuotesErc20Erc20[0].quote.srcAsset, - decimals: undefined, - }, - }, - }, - { - ...mockBridgeQuotesErc20Erc20[1], - quote: { - srcAsset: { - ...mockBridgeQuotesErc20Erc20[1].quote.destAsset, - address: undefined, - }, - }, - }, - ]); - - const result = await fetchBridgeQuotes({ - walletAddress: '0x123', - srcChainId: 1, - destChainId: 10, - srcTokenAddress: zeroAddress(), - destTokenAddress: zeroAddress(), - srcTokenAmount: '20000', - slippage: 0.5, - }); - - expect(fetchWithCache).toHaveBeenCalledWith({ - url: 'https://bridge.api.cx.metamask.io/getQuote?walletAddress=0x123&srcChainId=1&destChainId=10&srcTokenAddress=0x0000000000000000000000000000000000000000&destTokenAddress=0x0000000000000000000000000000000000000000&srcTokenAmount=20000&slippage=0.5&insufficientBal=false&resetApproval=false', - fetchOptions: { - method: 'GET', - headers: { 'X-Client-Id': 'extension' }, - }, - cacheOptions: { cacheRefreshTime: 0 }, - functionName: 'fetchBridgeQuotes', - }); - - expect(result).toStrictEqual(mockBridgeQuotesErc20Erc20); - }); - }); }); diff --git a/ui/pages/bridge/bridge.util.ts b/ui/pages/bridge/bridge.util.ts index f154b7e62b19..915a933e7c02 100644 --- a/ui/pages/bridge/bridge.util.ts +++ b/ui/pages/bridge/bridge.util.ts @@ -11,6 +11,7 @@ import { } from '../../../shared/constants/bridge'; import { MINUTE } from '../../../shared/constants/time'; import fetchWithCache from '../../../shared/lib/fetch-with-cache'; +import { validateData } from '../../../shared/lib/swaps-utils'; import { decimalToHex, hexToDecimal, @@ -19,37 +20,43 @@ import { SWAPS_CHAINID_DEFAULT_TOKEN_MAP, SwapsTokenObject, } from '../../../shared/constants/swaps'; +import { TOKEN_VALIDATORS } from '../swaps/swaps.util'; import { isSwapsDefaultTokenAddress, isSwapsDefaultTokenSymbol, } from '../../../shared/modules/swaps.utils'; -// TODO: Remove restricted import -// eslint-disable-next-line import/no-restricted-paths -import { REFRESH_INTERVAL_MS } from '../../../app/scripts/controllers/bridge/constants'; -import { - BridgeAsset, - BridgeFlag, - FeatureFlagResponse, - FeeData, - FeeType, - Quote, - QuoteRequest, - QuoteResponse, - TxData, -} from './types'; -import { - FEATURE_FLAG_VALIDATORS, - QUOTE_VALIDATORS, - TX_DATA_VALIDATORS, - TOKEN_VALIDATORS, - validateResponse, - QUOTE_RESPONSE_VALIDATORS, - FEE_DATA_VALIDATORS, -} from './utils/validators'; const CLIENT_ID_HEADER = { 'X-Client-Id': BRIDGE_CLIENT_ID }; const CACHE_REFRESH_TEN_MINUTES = 10 * MINUTE; +// Types copied from Metabridge API +enum BridgeFlag { + EXTENSION_SUPPORT = 'extension-support', + NETWORK_SRC_ALLOWLIST = 'src-network-allowlist', + NETWORK_DEST_ALLOWLIST = 'dest-network-allowlist', +} + +export type FeatureFlagResponse = { + [BridgeFlag.EXTENSION_SUPPORT]: boolean; + [BridgeFlag.NETWORK_SRC_ALLOWLIST]: number[]; + [BridgeFlag.NETWORK_DEST_ALLOWLIST]: number[]; +}; +// End of copied types + +type Validator = { + property: keyof ExpectedResponse | string; + type: string; + validator: (value: DataToValidate) => boolean; +}; + +const validateResponse = ( + validators: Validator[], + data: unknown, + urlUsed: string, +): data is ExpectedResponse => { + return validateData(validators, data, urlUsed); +}; + export async function fetchBridgeFeatureFlags(): Promise { const url = `${BRIDGE_API_BASE_URL}/getAllFeatureFlags`; const rawFeatureFlags = await fetchWithCache({ @@ -60,15 +67,35 @@ export async function fetchBridgeFeatureFlags(): Promise { }); if ( - validateResponse( - FEATURE_FLAG_VALIDATORS, + validateResponse( + [ + { + property: BridgeFlag.EXTENSION_SUPPORT, + type: 'boolean', + validator: (v) => typeof v === 'boolean', + }, + { + property: BridgeFlag.NETWORK_SRC_ALLOWLIST, + type: 'object', + validator: (v): v is number[] => + Object.values(v as { [s: string]: unknown }).every( + (i) => typeof i === 'number', + ), + }, + { + property: BridgeFlag.NETWORK_DEST_ALLOWLIST, + type: 'object', + validator: (v): v is number[] => + Object.values(v as { [s: string]: unknown }).every( + (i) => typeof i === 'number', + ), + }, + ], rawFeatureFlags, url, ) ) { return { - [BridgeFeatureFlagsKey.EXTENSION_CONFIG]: - rawFeatureFlags[BridgeFlag.EXTENSION_CONFIG], [BridgeFeatureFlagsKey.EXTENSION_SUPPORT]: rawFeatureFlags[BridgeFlag.EXTENSION_SUPPORT], [BridgeFeatureFlagsKey.NETWORK_SRC_ALLOWLIST]: rawFeatureFlags[ @@ -81,10 +108,6 @@ export async function fetchBridgeFeatureFlags(): Promise { } return { - [BridgeFeatureFlagsKey.EXTENSION_CONFIG]: { - refreshRate: REFRESH_INTERVAL_MS, - maxRefreshCount: 5, - }, // TODO set default to true once bridging is live [BridgeFeatureFlagsKey.EXTENSION_SUPPORT]: false, // TODO set default to ALLOWED_BRIDGE_CHAIN_IDS once bridging is live @@ -119,9 +142,13 @@ export async function fetchBridgeTokens( transformedTokens[nativeToken.address] = nativeToken; } - tokens.forEach((token: unknown) => { + tokens.forEach((token: SwapsTokenObject) => { if ( - validateResponse(TOKEN_VALIDATORS, token, url) && + validateResponse( + TOKEN_VALIDATORS, + token, + url, + ) && !( isSwapsDefaultTokenSymbol(token.symbol, chainId) || isSwapsDefaultTokenAddress(token.address, chainId) @@ -132,51 +159,3 @@ export async function fetchBridgeTokens( }); return transformedTokens; } - -// Returns a list of bridge tx quotes -export async function fetchBridgeQuotes( - request: QuoteRequest, -): Promise { - const queryParams = new URLSearchParams({ - walletAddress: request.walletAddress, - srcChainId: request.srcChainId.toString(), - destChainId: request.destChainId.toString(), - srcTokenAddress: request.srcTokenAddress, - destTokenAddress: request.destTokenAddress, - srcTokenAmount: request.srcTokenAmount, - slippage: request.slippage.toString(), - insufficientBal: request.insufficientBal ? 'true' : 'false', - resetApproval: request.resetApproval ? 'true' : 'false', - }); - const url = `${BRIDGE_API_BASE_URL}/getQuote?${queryParams}`; - const quotes = await fetchWithCache({ - url, - fetchOptions: { method: 'GET', headers: CLIENT_ID_HEADER }, - cacheOptions: { cacheRefreshTime: 0 }, - functionName: 'fetchBridgeQuotes', - }); - - const filteredQuotes = quotes.filter((quoteResponse: QuoteResponse) => { - const { quote, approval, trade } = quoteResponse; - return ( - validateResponse( - QUOTE_RESPONSE_VALIDATORS, - quoteResponse, - url, - ) && - validateResponse(QUOTE_VALIDATORS, quote, url) && - validateResponse(TOKEN_VALIDATORS, quote.srcAsset, url) && - validateResponse(TOKEN_VALIDATORS, quote.destAsset, url) && - validateResponse(TX_DATA_VALIDATORS, trade, url) && - validateResponse( - FEE_DATA_VALIDATORS, - quote.feeData[FeeType.METABRIDGE], - url, - ) && - (approval - ? validateResponse(TX_DATA_VALIDATORS, approval, url) - : true) - ); - }); - return filteredQuotes; -} diff --git a/ui/pages/bridge/prepare/__snapshots__/prepare-bridge-page.test.tsx.snap b/ui/pages/bridge/prepare/__snapshots__/prepare-bridge-page.test.tsx.snap index 4284c1893d7c..b406cafe0941 100644 --- a/ui/pages/bridge/prepare/__snapshots__/prepare-bridge-page.test.tsx.snap +++ b/ui/pages/bridge/prepare/__snapshots__/prepare-bridge-page.test.tsx.snap @@ -107,7 +107,6 @@ exports[`PrepareBridgePage should render the component, with initial state 1`] = > $0.00 @@ -192,7 +191,6 @@ exports[`PrepareBridgePage should render the component, with initial state 1`] = > $0.00 @@ -318,7 +316,6 @@ exports[`PrepareBridgePage should render the component, with inputs set 1`] = ` > $0.00 @@ -447,7 +444,6 @@ exports[`PrepareBridgePage should render the component, with inputs set 1`] = ` > $0.00 diff --git a/ui/pages/bridge/prepare/prepare-bridge-page.tsx b/ui/pages/bridge/prepare/prepare-bridge-page.tsx index b0907f83dab7..2fdb11289c5b 100644 --- a/ui/pages/bridge/prepare/prepare-bridge-page.tsx +++ b/ui/pages/bridge/prepare/prepare-bridge-page.tsx @@ -1,15 +1,13 @@ -import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import React, { useState } from 'react'; import { useSelector, useDispatch } from 'react-redux'; import classnames from 'classnames'; -import { debounce } from 'lodash'; import { setFromChain, setFromToken, setFromTokenInputValue, setToChain, - setToChainId, setToToken, - updateQuoteRequestParams, + switchToAndFromTokens, } from '../../../ducks/bridge/actions'; import { getFromAmount, @@ -30,14 +28,11 @@ import { ButtonIcon, IconName, } from '../../../components/component-library'; -import { BlockSize } from '../../../helpers/constants/design-system'; import { useI18nContext } from '../../../hooks/useI18nContext'; import { TokenBucketPriority } from '../../../../shared/constants/swaps'; import { useTokensWithFiltering } from '../../../hooks/useTokensWithFiltering'; import { setActiveNetwork } from '../../../store/actions'; -import { hexToDecimal } from '../../../../shared/modules/conversion.utils'; -import { QuoteRequest } from '../types'; -import { calcTokenValue } from '../../../../shared/lib/swaps-utils'; +import { BlockSize } from '../../../helpers/constants/design-system'; import { BridgeInputGroup } from './bridge-input-group'; const PrepareBridgePage = () => { @@ -76,36 +71,6 @@ const PrepareBridgePage = () => { const [rotateSwitchTokens, setRotateSwitchTokens] = useState(false); - const quoteParams = useMemo( - () => ({ - srcTokenAddress: fromToken?.address, - destTokenAddress: toToken?.address || undefined, - srcTokenAmount: - fromAmount && fromAmount !== '' && fromToken?.decimals - ? calcTokenValue(fromAmount, fromToken.decimals).toString() - : undefined, - srcChainId: fromChain?.chainId - ? Number(hexToDecimal(fromChain.chainId)) - : undefined, - destChainId: toChain?.chainId - ? Number(hexToDecimal(toChain.chainId)) - : undefined, - }), - [fromToken, toToken, fromChain?.chainId, toChain?.chainId, fromAmount], - ); - - const debouncedUpdateQuoteRequestInController = useCallback( - debounce( - (p: Partial) => dispatch(updateQuoteRequestParams(p)), - 300, - ), - [], - ); - - useEffect(() => { - debouncedUpdateQuoteRequestInController(quoteParams); - }, Object.values(quoteParams)); - return (
@@ -116,10 +81,7 @@ const PrepareBridgePage = () => { onAmountChange={(e) => { dispatch(setFromTokenInputValue(e)); }} - onAssetChange={(token) => { - dispatch(setFromToken(token)); - dispatch(setFromTokenInputValue(null)); - }} + onAssetChange={(token) => dispatch(setFromToken(token))} networkProps={{ network: fromChain, networks: fromChains, @@ -132,8 +94,6 @@ const PrepareBridgePage = () => { ), ); dispatch(setFromChain(networkConfig.chainId)); - dispatch(setFromToken(null)); - dispatch(setFromTokenInputValue(null)); }, }} customTokenListGenerator={ @@ -161,18 +121,12 @@ const PrepareBridgePage = () => { onClick={() => { setRotateSwitchTokens(!rotateSwitchTokens); const toChainClientId = - toChain?.defaultRpcEndpointIndex !== undefined && - toChain?.rpcEndpoints - ? toChain.rpcEndpoints[toChain.defaultRpcEndpointIndex] + toChain?.defaultRpcEndpointIndex && toChain?.rpcEndpoints + ? toChain.rpcEndpoints?.[toChain.defaultRpcEndpointIndex] .networkClientId : undefined; toChainClientId && dispatch(setActiveNetwork(toChainClientId)); - toChain && dispatch(setFromChain(toChain.chainId)); - dispatch(setFromToken(toToken)); - dispatch(setFromTokenInputValue(null)); - fromChain?.chainId && dispatch(setToChain(fromChain.chainId)); - fromChain?.chainId && dispatch(setToChainId(fromChain.chainId)); - dispatch(setToToken(fromToken)); + dispatch(switchToAndFromTokens({ fromChain })); }} /> @@ -186,7 +140,6 @@ const PrepareBridgePage = () => { network: toChain, networks: toChains, onNetworkChange: (networkConfig) => { - dispatch(setToChainId(networkConfig.chainId)); dispatch(setToChain(networkConfig.chainId)); }, }} diff --git a/ui/pages/bridge/types.ts b/ui/pages/bridge/types.ts deleted file mode 100644 index 5d001e7ef7fc..000000000000 --- a/ui/pages/bridge/types.ts +++ /dev/null @@ -1,119 +0,0 @@ -// Types copied from Metabridge API -export enum BridgeFlag { - EXTENSION_CONFIG = 'extension-config', - EXTENSION_SUPPORT = 'extension-support', - NETWORK_SRC_ALLOWLIST = 'src-network-allowlist', - NETWORK_DEST_ALLOWLIST = 'dest-network-allowlist', -} - -export type FeatureFlagResponse = { - [BridgeFlag.EXTENSION_CONFIG]: { - refreshRate: number; - maxRefreshCount: number; - }; - [BridgeFlag.EXTENSION_SUPPORT]: boolean; - [BridgeFlag.NETWORK_SRC_ALLOWLIST]: number[]; - [BridgeFlag.NETWORK_DEST_ALLOWLIST]: number[]; -}; - -export type BridgeAsset = { - chainId: ChainId; - address: string; - symbol: string; - name: string; - decimals: number; - icon?: string; -}; - -export type QuoteRequest = { - walletAddress: string; - destWalletAddress?: string; - srcChainId: ChainId; - destChainId: ChainId; - srcTokenAddress: string; - destTokenAddress: string; - srcTokenAmount: string; - slippage: number; - aggIds?: string[]; - bridgeIds?: string[]; - insufficientBal?: boolean; - resetApproval?: boolean; - refuel?: boolean; -}; - -type Protocol = { - name: string; - displayName?: string; - icon?: string; -}; - -enum ActionTypes { - BRIDGE = 'bridge', - SWAP = 'swap', - REFUEL = 'refuel', -} - -type Step = { - action: ActionTypes; - srcChainId: ChainId; - destChainId?: ChainId; - srcAsset: BridgeAsset; - destAsset: BridgeAsset; - srcAmount: string; - destAmount: string; - protocol: Protocol; -}; - -type RefuelData = Step; - -export type Quote = { - requestId: string; - srcChainId: ChainId; - srcAsset: BridgeAsset; - srcTokenAmount: string; - destChainId: ChainId; - destAsset: BridgeAsset; - destTokenAmount: string; - feeData: Record & - Partial>; - bridgeId: string; - bridges: string[]; - steps: Step[]; - refuel?: RefuelData; -}; - -export type QuoteResponse = { - quote: Quote; - approval: TxData | null; - trade: TxData; - estimatedProcessingTimeInSeconds: number; -}; - -enum ChainId { - ETH = 1, - OPTIMISM = 10, - BSC = 56, - POLYGON = 137, - ZKSYNC = 324, - BASE = 8453, - ARBITRUM = 42161, - AVALANCHE = 43114, - LINEA = 59144, -} - -export enum FeeType { - METABRIDGE = 'metabridge', - REFUEL = 'refuel', -} -export type FeeData = { - amount: string; - asset: BridgeAsset; -}; -export type TxData = { - chainId: ChainId; - to: string; - from: string; - value: string; - data: string; - gasLimit: number | null; -}; diff --git a/ui/pages/bridge/utils/quote.ts b/ui/pages/bridge/utils/quote.ts deleted file mode 100644 index 0b83205580b4..000000000000 --- a/ui/pages/bridge/utils/quote.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { QuoteRequest } from '../types'; - -export const isValidQuoteRequest = ( - partialRequest: Partial, - requireAmount = true, -): partialRequest is QuoteRequest => { - const STRING_FIELDS = ['srcTokenAddress', 'destTokenAddress']; - if (requireAmount) { - STRING_FIELDS.push('srcTokenAmount'); - } - const NUMBER_FIELDS = ['srcChainId', 'destChainId', 'slippage']; - - return ( - STRING_FIELDS.every( - (field) => - field in partialRequest && - typeof partialRequest[field as keyof typeof partialRequest] === - 'string' && - partialRequest[field as keyof typeof partialRequest] !== undefined && - partialRequest[field as keyof typeof partialRequest] !== '' && - partialRequest[field as keyof typeof partialRequest] !== null, - ) && - NUMBER_FIELDS.every( - (field) => - field in partialRequest && - typeof partialRequest[field as keyof typeof partialRequest] === - 'number' && - partialRequest[field as keyof typeof partialRequest] !== undefined && - !isNaN(Number(partialRequest[field as keyof typeof partialRequest])) && - partialRequest[field as keyof typeof partialRequest] !== null, - ) - ); -}; diff --git a/ui/pages/bridge/utils/validators.ts b/ui/pages/bridge/utils/validators.ts deleted file mode 100644 index 01c716522968..000000000000 --- a/ui/pages/bridge/utils/validators.ts +++ /dev/null @@ -1,105 +0,0 @@ -import { isStrictHexString } from '@metamask/utils'; -import { isValidHexAddress as isValidHexAddress_ } from '@metamask/controller-utils'; -import { - truthyDigitString, - validateData, -} from '../../../../shared/lib/swaps-utils'; -import { BridgeFlag, FeatureFlagResponse } from '../types'; - -type Validator = { - property: keyof ExpectedResponse | string; - type: string; - validator?: (value: unknown) => boolean; -}; - -export const validateResponse = ( - validators: Validator[], - data: unknown, - urlUsed: string, -): data is ExpectedResponse => { - return validateData(validators, data, urlUsed); -}; - -export const isValidNumber = (v: unknown): v is number => typeof v === 'number'; -const isValidObject = (v: unknown): v is object => - typeof v === 'object' && v !== null; -const isValidString = (v: unknown): v is string => - typeof v === 'string' && v.length > 0; -const isValidHexAddress = (v: unknown) => - isValidString(v) && isValidHexAddress_(v, { allowNonPrefixed: false }); - -export const FEATURE_FLAG_VALIDATORS = [ - { - property: BridgeFlag.EXTENSION_CONFIG, - type: 'object', - validator: ( - v: unknown, - ): v is Pick => - isValidObject(v) && - 'refreshRate' in v && - isValidNumber(v.refreshRate) && - 'maxRefreshCount' in v && - isValidNumber(v.maxRefreshCount), - }, - { property: BridgeFlag.EXTENSION_SUPPORT, type: 'boolean' }, - { - property: BridgeFlag.NETWORK_SRC_ALLOWLIST, - type: 'object', - validator: (v: unknown): v is number[] => - isValidObject(v) && Object.values(v).every(isValidNumber), - }, - { - property: BridgeFlag.NETWORK_DEST_ALLOWLIST, - type: 'object', - validator: (v: unknown): v is number[] => - isValidObject(v) && Object.values(v).every(isValidNumber), - }, -]; - -export const TOKEN_VALIDATORS = [ - { property: 'decimals', type: 'number' }, - { property: 'address', type: 'string', validator: isValidHexAddress }, - { - property: 'symbol', - type: 'string', - validator: (v: unknown) => isValidString(v) && v.length <= 12, - }, -]; - -export const QUOTE_RESPONSE_VALIDATORS = [ - { property: 'quote', type: 'object', validator: isValidObject }, - { property: 'estimatedProcessingTimeInSeconds', type: 'number' }, - { - property: 'approval', - type: 'object|undefined', - validator: (v: unknown) => v === undefined || isValidObject(v), - }, - { property: 'trade', type: 'object', validator: isValidObject }, -]; - -export const QUOTE_VALIDATORS = [ - { property: 'requestId', type: 'string' }, - { property: 'srcTokenAmount', type: 'string' }, - { property: 'destTokenAmount', type: 'string' }, - { property: 'bridgeId', type: 'string' }, - { property: 'bridges', type: 'object', validator: isValidObject }, - { property: 'srcChainId', type: 'number' }, - { property: 'destChainId', type: 'number' }, - { property: 'srcAsset', type: 'object', validator: isValidObject }, - { property: 'destAsset', type: 'object', validator: isValidObject }, - { property: 'feeData', type: 'object', validator: isValidObject }, -]; - -export const FEE_DATA_VALIDATORS = [ - { property: 'amount', type: 'string', validator: truthyDigitString }, - { property: 'asset', type: 'object', validator: isValidObject }, -]; - -export const TX_DATA_VALIDATORS = [ - { property: 'chainId', type: 'number' }, - { property: 'value', type: 'string', validator: isStrictHexString }, - { property: 'gasLimit', type: 'number' }, - { property: 'to', type: 'string', validator: isValidHexAddress }, - { property: 'from', type: 'string', validator: isValidHexAddress }, - { property: 'data', type: 'string', validator: isStrictHexString }, -]; diff --git a/ui/pages/confirm-decrypt-message/__snapshots__/confirm-decrypt-message.component.test.js.snap b/ui/pages/confirm-decrypt-message/__snapshots__/confirm-decrypt-message.component.test.js.snap index d5e060e31d72..cb4715881e27 100644 --- a/ui/pages/confirm-decrypt-message/__snapshots__/confirm-decrypt-message.component.test.js.snap +++ b/ui/pages/confirm-decrypt-message/__snapshots__/confirm-decrypt-message.component.test.js.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`ConfirmDecryptMessage Component matches snapshot 1`] = ` +exports[`ConfirmDecryptMessage Component should match snapshot when preference is ETH currency 1`] = `
-
-
-
-
-
-
- Balance: -
-
- 966.987986 ETH -
-
-
-
-
- - T - -
- test would like to read this message to complete your action -
-
-
-
-
-
- {"domain":{"chainId":97,"name":"Ether Mail","verifyingContract":"0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC","version":"1"}} - -
-
-
-
- -
- Decrypt message -
-
-
-
-
-
-
- -
-
-`; - -exports[`ConfirmDecryptMessage Component matches snapshot if no unapproved decrypt messages 1`] = `
`; - -exports[`ConfirmDecryptMessage Component shows error on decrypt inline error 1`] = ` -
-
-
-
-
- Decrypt request -
-
-
-
-
-
-
+ {"domain":{"chainId":97,"name":"Ether Mail","verifyingContract":"0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC","version":"1"},"message":{"contents":"Hello, Bob!","from":{"name":"Cow","wallets":["0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826","0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF"]},"to":[{"name":"Bob","wallets":["0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB","0xB0BdaBea57B0BDABeA57b0bdABEA57b0BDabEa57","0xB0B0b0b0b0b0B000000000000000000000000000"]}]},"primaryType":"Mail","types":{"EIP712Domain":[{"name":"name","type":"string"},{"name":"version","type":"string"},{"name":"chainId","type":"uint256"},{"name":"verifyingContract","type":"address"}],"Mail":[{"name":"from","type":"Person"},{"name":"to","type":"Person[]"},{"name":"contents","type":"string"}],"Person":[{"name":"name","type":"string"},{"name":"wallets","type":"address[]"}]}} + +
+
+
- - This message cannot be decrypted due to error: Decrypt inline error -
-
-
+
- -
- Decrypt message -
+ Decrypt message
-
+
+
+
+
+
+
+ + + + +
@@ -494,7 +451,7 @@ exports[`ConfirmDecryptMessage Component shows the correct message data 1`] = `
- 966.987986 ETH + 1520956.064158 DEF
@@ -515,66 +472,35 @@ exports[`ConfirmDecryptMessage Component shows the correct message data 1`] = `
-
- raw message - -
-
-
-
- -
- Decrypt message -
-
-
+ {"domain":{"chainId":97,"name":"Ether Mail","verifyingContract":"0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC","version":"1"},"message":{"contents":"Hello, Bob!","from":{"name":"Cow","wallets":["0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826","0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF"]},"to":[{"name":"Bob","wallets":["0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB","0xB0BdaBea57B0BDABeA57b0bdABEA57b0BDabEa57","0xB0B0b0b0b0b0B000000000000000000000000000"]}]},"primaryType":"Mail","types":{"EIP712Domain":[{"name":"name","type":"string"},{"name":"version","type":"string"},{"name":"chainId","type":"uint256"},{"name":"verifyingContract","type":"address"}],"Mail":[{"name":"from","type":"Person"},{"name":"to","type":"Person[]"},{"name":"contents","type":"string"}],"Person":[{"name":"name","type":"string"},{"name":"wallets","type":"address[]"}]}} +
+
+
-
- Copy encrypted message -
- + Decrypt message
+