diff --git a/.circleci/config.yml b/.circleci/config.yml index 81cc863d1bbe..3097e060ff6f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -67,18 +67,6 @@ aliases: git checkout -B "$CIRCLE_BRANCH" "$CIRCLE_SHA1" fi - # Check if MMI Optional tests should run - - &check-mmi-optional - name: Check if MMI Optional tests should run - command: | - RUN_MMI_OPTIONAL=$(cat ./RUN_MMI_OPTIONAL) - if [[ "${CIRCLE_BRANCH}" == "develop" || "${RUN_MMI_OPTIONAL}" == "true" ]]; then - echo "Running MMI Optional tests" - else - echo "Skipping MMI Optional tests" - circleci step halt - fi - workflows: test_and_release: jobs: @@ -89,7 +77,6 @@ workflows: - trigger-beta-build: requires: - prep-deps - - check-pr-tag - prep-deps - test-deps-audit: requires: @@ -145,7 +132,6 @@ workflows: - prep-build-test-mmi-playwright: requires: - prep-deps - - check-pr-tag - prep-build-storybook: requires: - prep-deps @@ -387,68 +373,6 @@ 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 - - # GitHub Personal Access Token for API Authentication - GITHUB_TOKEN="${GITHUB_TOKEN}" - BRANCH="${CIRCLE_BRANCH}" - - # Fetch the PRs associated with the current branch and check the response - PR_RESPONSE=$(curl -s -H "Authorization: token ${GITHUB_TOKEN}" \ - "https://api.github.com/repos/$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME/pulls?state=open&head=${CIRCLE_PROJECT_USERNAME}:${BRANCH}") - echo "https://api.github.com/repos/$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME/pulls?state=open&head=${CIRCLE_PROJECT_USERNAME}:${BRANCH}" - - # Check if the response contains valid JSON - if ! echo "$PR_RESPONSE" | jq empty; then - echo "Failed to parse JSON response." - echo "$PR_RESPONSE" - exit 1 - fi - - # Check if we received an array of PRs - if ! echo "$PR_RESPONSE" | jq -e '. | type == "array"'; then - echo "$PR_RESPONSE" - echo "Expected an array of PRs, got something else." - exit 1 - fi - - # Check if the array of PRs is empty - PR_COUNT=$(echo "$PR_RESPONSE" | jq '. | length') - - # If no PRs are found, exit gracefully - if [ "$PR_COUNT" -eq 0 ]; then - echo "No open PRs found. Exiting." - echo "false" > ./RUN_MMI_OPTIONAL - exit 0 - fi - - # Extract label names from the PR_RESPONSE - LABEL_NAMES=$(echo "$PR_RESPONSE" | jq -r '.[0].labels[].name') - - echo "Labels found: $LABEL_NAMES" - - # Check if "team-mmi" label is present - if echo "$LABEL_NAMES" | grep -qw "team-mmi"; then - echo "team-mmi tag found." - # assign the RUN_MMI_OPTIONAL variable to true - echo "true" > ./RUN_MMI_OPTIONAL - else - echo "team-mmi tag not found." - # assign the RUN_MMI_OPTIONAL variable to false - echo "false" > ./RUN_MMI_OPTIONAL - fi - - persist_to_workspace: - root: . - paths: - - RUN_MMI_OPTIONAL - prep-deps: executor: node-browsers-medium steps: @@ -716,7 +640,6 @@ jobs: - run: *shallow-git-clone - attach_workspace: at: . - - run: *check-mmi-optional - run: name: Build MMI extension for Playwright e2e command: | @@ -731,7 +654,6 @@ jobs: - persist_to_workspace: root: . paths: - - RUN_MMI_OPTIONAL - dist-test-mmi-playwright - builds-test-mmi-playwright - store_artifacts: @@ -953,7 +875,7 @@ jobs: command: | if .circleci/scripts/test-run-e2e.sh then - timeout 20m yarn test:e2e:chrome --retries 2 + timeout 20m yarn test:e2e:chrome --retries 2 --debug fi no_output_timeout: 5m - store_artifacts: @@ -980,7 +902,7 @@ jobs: command: | if .circleci/scripts/test-run-e2e.sh then - timeout 20m yarn test:e2e:chrome --retries 2 + timeout 20m yarn test:e2e:chrome --retries 2 --debug fi no_output_timeout: 5m environment: @@ -1009,7 +931,7 @@ jobs: command: | if .circleci/scripts/test-run-e2e.sh then - timeout 20m yarn test:e2e:chrome --retries 2 || echo "Temporarily suppressing MV3 e2e test failures" + timeout 20m yarn test:e2e:chrome --retries 2 --debug || echo "Temporarily suppressing MV3 e2e test failures" fi no_output_timeout: 5m - store_artifacts: @@ -1087,7 +1009,7 @@ jobs: command: | if .circleci/scripts/test-run-e2e.sh then - timeout 20m yarn test:e2e:chrome:rpc --retries 2 --build-type=mmi + timeout 20m yarn test:e2e:chrome:rpc --retries 2 --debug --build-type=mmi fi no_output_timeout: 5m - store_artifacts: @@ -1107,7 +1029,7 @@ jobs: command: | if .circleci/scripts/test-run-e2e.sh then - yarn test:e2e:single test/e2e/vault-decryption-chrome.spec.js --browser chrome --retries 2 + yarn test:e2e:single test/e2e/vault-decryption-chrome.spec.js --browser chrome --retries 2 --debug fi no_output_timeout: 5m @@ -1129,7 +1051,7 @@ jobs: command: | if .circleci/scripts/test-run-e2e.sh then - timeout 20m yarn test:e2e:firefox:flask --retries 2 + timeout 20m yarn test:e2e:firefox:flask --retries 2 --debug fi no_output_timeout: 5m - store_artifacts: @@ -1156,7 +1078,7 @@ jobs: command: | if .circleci/scripts/test-run-e2e.sh then - timeout 20m yarn test:e2e:chrome:flask --retries 2 + timeout 20m yarn test:e2e:chrome:flask --retries 2 --debug fi no_output_timeout: 5m - store_artifacts: @@ -1183,7 +1105,7 @@ jobs: command: | if .circleci/scripts/test-run-e2e.sh then - timeout 20m yarn test:e2e:chrome:mmi --retries 2 --build-type=mmi + timeout 20m yarn test:e2e:chrome:mmi --retries 2 --debug --build-type=mmi fi no_output_timeout: 5m - store_artifacts: @@ -1199,7 +1121,6 @@ jobs: - run: *shallow-git-clone - attach_workspace: at: . - - run: *check-mmi-optional - run: name: Move test build to dist command: mv ./dist-test-mmi-playwright ./dist @@ -1251,7 +1172,7 @@ jobs: command: | if .circleci/scripts/test-run-e2e.sh then - timeout 20m yarn test:e2e:firefox --retries 2 + timeout 20m yarn test:e2e:firefox --retries 2 --debug fi no_output_timeout: 5m - store_artifacts: @@ -1650,4 +1571,4 @@ jobs: steps: - run: name: All Tests Passed - command: echo 'whew - everything passed!' + command: echo 'weew - everything passed!' diff --git a/.eslintrc.js b/.eslintrc.js index 53235d12c55a..d57cc7bf7aef 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -135,7 +135,6 @@ module.exports = { path.resolve(__dirname, '.eslintrc.typescript-compat.js'), ], rules: { - '@typescript-eslint/no-explicit-any': 'error', // this rule is new, but we didn't use it before, so it's off now '@typescript-eslint/no-duplicate-enum-values': 'off', '@typescript-eslint/no-shadow': [ diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index f94c729355c4..00c3678ffb6b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -51,6 +51,3 @@ privacy-snapshot.json @MetaMask/extension-privacy-reviewers # For now, restricting approvals inside the .devcontainer folder to devs # who were involved with the Codespaces project. .devcontainer/ @MetaMask/library-admins @HowardBraham @plasmacorral @brad-decker - -# Confirmations UX team to own code for confirmations on UI. -ui/pages/confirmations @MetaMask/confirmations-ux @MetaMask/confirmations-system-team diff --git a/.github/scripts/check-pr-has-required-labels.ts b/.github/scripts/check-pr-has-required-labels.ts index 354dc2c2aa7d..15ef77022ceb 100644 --- a/.github/scripts/check-pr-has-required-labels.ts +++ b/.github/scripts/check-pr-has-required-labels.ts @@ -73,7 +73,7 @@ async function main(): Promise { if (!hasTeamLabel) { errorMessage += 'No team labels found on the PR. '; } - errorMessage += `Please make sure the PR is appropriately labeled before merging it.\n\nSee labeling guidelines for more detail: https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md`; + errorMessage += `Please make sure the PR is appropriately labeled before merging it.\n\nSee labeling guidelines for more detail: https://github.com/MetaMask/metamask-extension/blob/develop/.github/LABELING_GUIDELINES.md`; core.setFailed(errorMessage); process.exit(1); } diff --git a/.github/workflows/security-code-scanner.yml b/.github/workflows/security-code-scanner.yml index 7da1773d666c..6b75b0d98bd0 100644 --- a/.github/workflows/security-code-scanner.yml +++ b/.github/workflows/security-code-scanner.yml @@ -32,5 +32,5 @@ jobs: node_modules rules_excluded: example - project_metrics_token: ${{secrets.SECURITY_SCAN_METRICS_TOKEN}} + mixpanel_project_token: ${{secrets.SECURITY_CODE_SCANNER_MIXPANEL_TOKEN}} slack_webhook: ${{ secrets.APPSEC_BOT_SLACK_WEBHOOK }} diff --git a/.github/workflows/sonar.yml b/.github/workflows/sonar.yml index a6f28ee8aa4e..afe3fe367504 100644 --- a/.github/workflows/sonar.yml +++ b/.github/workflows/sonar.yml @@ -4,16 +4,6 @@ on: secrets: SONAR_TOKEN: required: true - pull_request: - branches: - - develop - types: - - opened - - reopened - - synchronize - - labeled - - unlabeled - jobs: sonarcloud: name: SonarCloud @@ -23,8 +13,8 @@ jobs: with: fetch-depth: 0 # Shallow clones should be disabled for better relevancy of analysis - name: SonarCloud Scan - # This is SonarSource/sonarcloud-github-action@v2.0.0 - uses: SonarSource/sonarcloud-github-action@4b4d7634dab97dcee0b75763a54a6dc92a9e6bc1 + # v1.9.1 + uses: SonarSource/sonarcloud-github-action@5875562561d22a34be0c657405578705a169af6c with: args: > -Dsonar.javascript.lcov.reportPaths=tests/coverage/lcov.info diff --git a/.metamaskrc.dist b/.metamaskrc.dist index 0c8e6a5971be..d2663f882a52 100644 --- a/.metamaskrc.dist +++ b/.metamaskrc.dist @@ -21,4 +21,3 @@ BLOCKAID_PUBLIC_KEY= ; SELENIUM_HEADLESS= ; Set this to 1 to make chrome e2e tests disable DoH/DoT and use system DNS ; SELENIUM_USE_SYSTEM_DNS= -ENABLE_CONFIRMATION_REDESIGN= diff --git a/.storybook/main.js b/.storybook/main.js index 2a48b3b88654..67b47fd72c04 100644 --- a/.storybook/main.js +++ b/.storybook/main.js @@ -64,7 +64,6 @@ module.exports = { { loader: 'css-loader', options: { - esModule: false, import: false, url: false, }, @@ -84,15 +83,6 @@ module.exports = { config.plugins.push( new CopyWebpackPlugin({ patterns: [ - { - from: path.join( - 'ui', - 'css', - 'utilities', - 'fonts/', - ), - to: 'fonts', - }, { from: path.join( 'node_modules', diff --git a/.vscode/cspell.json b/.vscode/cspell.json deleted file mode 100644 index 24e3747bfa9b..000000000000 --- a/.vscode/cspell.json +++ /dev/null @@ -1,84 +0,0 @@ -{ - "ignorePaths": ["app/images", "package.json"], - "ignoreWords": [ - "acitores", - "autofetch", - "azuretools", - "Brainstem", - "C01LUJL3T98", - "C05QXJA7NP8", - "cids", - "eamodio", - "initialisation", - "koalaman", - "mockttp", - "multibase", - "multicodec", - "namelookup", - "pluggable", - "protobufjs", - "regadas", - "remotedev", - "rvest", - "sesify", - "siginsights", - "testrpc", - "txinsights", - "webextension", - "xvfb" - ], - "useGitignore": true, - "version": "0.2", - "words": [ - "bignumber", - "blockaid", - "browserlistrc", - "cimg", - "codecov", - "codespace", - "codespaces", - "corepack", - "datetime", - "datetimes", - "dedupe", - "depcheck", - "devcontainer", - "devcontainers", - "endregion", - "ensdomains", - "flamegraph", - "FONTCONFIG", - "hardfork", - "hexstring", - "jazzicon", - "keccak", - "lavadome", - "lavamoat", - "lavapack", - "lockdown", - "metamaskbot", - "metamaskrc", - "metametrics", - "mocharc", - "MULTICHAIN", - "MULTIPROVIDER", - "npmcli", - "onboarded", - "pageload", - "petnames", - "pipefail", - "quickstart", - "recompiles", - "shellcheck", - "sourcemaps", - "sprintf", - "testcase", - "TESTFILES", - "testid", - "tsbuildinfo", - "tsconfigs", - "typecheck", - "yargs", - "yarnpkg" - ] -} diff --git a/.vscode/launch.json b/.vscode/launch.json index e801fb65f8b7..49dda3b55d2d 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -38,7 +38,7 @@ "type": "pickString", "id": "browserToUse", "description": "Which browser do you want to test with?", - "options": ["chrome", "firefox", "all"], + "options": ["chrome", "firefox"], "default": "chrome" } ], diff --git a/.vscode/settings.json b/.vscode/settings.json index 02c20a82219a..5eca75a9f539 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,5 @@ { + "cSpell.words": ["blockaid", "lavamoat"], "editor.defaultFormatter": "rvest.vs-code-prettier-eslint", "editor.tabSize": 2, "files.associations": { diff --git a/.yarn/patches/@metamask-assets-controllers-patch-0f46262fea.patch b/.yarn/patches/@metamask-assets-controllers-patch-0f46262fea.patch deleted file mode 100644 index 2c0f9d1abbbc..000000000000 --- a/.yarn/patches/@metamask-assets-controllers-patch-0f46262fea.patch +++ /dev/null @@ -1,40 +0,0 @@ -diff --git a/dist/NftDetectionController.js b/dist/NftDetectionController.js -index 24373e328d3600d1168914a3dc0bbbd905b19ebe..3877bebee24d1ad5cd2183b50547e8cef1846558 100644 ---- a/dist/NftDetectionController.js -+++ b/dist/NftDetectionController.js -@@ -36,7 +36,7 @@ class NftDetectionController extends polling_controller_1.StaticIntervalPollingC - * @param config - Initial options used to configure this controller. - * @param state - Initial state to set on this controller. - */ -- constructor({ chainId: initialChainId, getNetworkClientById, onPreferencesStateChange, onNetworkStateChange, getOpenSeaApiKey, addNft, getNftApi, getNftState, }, config, state) { -+ constructor({ chainId: initialChainId, getNetworkClientById, onPreferencesStateChange, onNetworkStateChange, getOpenSeaApiKey, addNft, getNftApi, getNftState, disabled: initialDisabled, selectedAddress: initialSelectedAddress }, config, state) { - super(config, state); - /** - * Name of this controller used during composition -@@ -54,8 +54,8 @@ class NftDetectionController extends polling_controller_1.StaticIntervalPollingC - this.defaultConfig = { - interval: DEFAULT_INTERVAL, - chainId: initialChainId, -- selectedAddress: '', -- disabled: true, -+ selectedAddress: initialSelectedAddress, -+ disabled: initialDisabled, - }; - this.initialize(); - this.getNftState = getNftState; -diff --git a/dist/Standards/NftStandards/ERC721/ERC721Standard.js b/dist/Standards/NftStandards/ERC721/ERC721Standard.js -index d9286b0c0e607d2857f3ee7dad40d13a6c11d7d7..4e12e4b590b1f34a66602d63035f1905917f8c93 100644 ---- a/dist/Standards/NftStandards/ERC721/ERC721Standard.js -+++ b/dist/Standards/NftStandards/ERC721/ERC721Standard.js -@@ -66,7 +66,10 @@ class ERC721Standard { - const contract = new contracts_1.Contract(address, metamask_eth_abis_1.abiERC721, this.provider); - const supportsMetadata = yield this.contractSupportsMetadataInterface(address); - if (!supportsMetadata) { -- throw new Error('Contract does not support ERC721 metadata interface.'); -+ // Do not throw error here, supporting Metadata interface is optional even though majority of ERC721 nfts do support it. -+ // This change is made because of instances of NFTs that are ERC404( mixed ERC20 / ERC721 implementation). -+ // As of today, ERC404 is unofficial but some people use it, the contract does not support Metadata interface, but it has the tokenURI() fct. -+ console.error('Contract does not support ERC721 metadata interface.'); - } - return contract.tokenURI(tokenId); - }); diff --git a/.yarn/patches/@metamask-controller-utils-patch-a87ddc3d4b.patch b/.yarn/patches/@metamask-controller-utils-patch-a87ddc3d4b.patch deleted file mode 100644 index 4d2e10d38361..000000000000 --- a/.yarn/patches/@metamask-controller-utils-patch-a87ddc3d4b.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff --git a/dist/types.js b/dist/types.js -index c59368ae1b156162acec2aacb6d593c5122e9b09..012bb5197bbeaa5738b8144a540d3db8aa8cb85c 100644 ---- a/dist/types.js -+++ b/dist/types.js -@@ -9,6 +9,7 @@ exports.InfuraNetworkType = { - goerli: 'goerli', - sepolia: 'sepolia', - 'linea-goerli': 'linea-goerli', -+ "linea-sepolia": "linea-sepolia", - 'linea-mainnet': 'linea-mainnet', - }; - /** diff --git a/.yarn/patches/@metamask-keyring-controller-npm-13.0.0-d94816a680.patch b/.yarn/patches/@metamask-keyring-controller-npm-13.0.0-d94816a680.patch index df96971174a1..254cf55f3f5f 100644 --- a/.yarn/patches/@metamask-keyring-controller-npm-13.0.0-d94816a680.patch +++ b/.yarn/patches/@metamask-keyring-controller-npm-13.0.0-d94816a680.patch @@ -1,27 +1,8 @@ diff --git a/dist/KeyringController.js b/dist/KeyringController.js -index fc649ea6fc97b905d811b236de638172fb10b548..bb288fda7e2ef970ceefe2ac22bcf653a6a55b8a 100644 +index fc649ea6fc97b905d811b236de638172fb10b548..beab676ab85e5e372eda7846e98b7d34af6317f5 100644 --- a/dist/KeyringController.js +++ b/dist/KeyringController.js -@@ -576,6 +576,18 @@ class KeyringController extends base_controller_1.BaseController { - return { type, data }; - }))); - serializedKeyrings.push(...__classPrivateFieldGet(this, _KeyringController_unsupportedKeyrings, "f")); -+ /** -+ * ============================== PATCH INFORMATION ============================== -+ * The HD keyring is the default keyring for all wallets if this keyring is missing -+ * for some reason we should avoid saving the keyrings -+ * -+ * The upstream fix is here: https://github.com/MetaMask/core/pull/4168 -+ * -+ * This patch can be found on the core branch `extension-keyring-controller-v13-patch` -+ */ -+ if (!serializedKeyrings.some((keyring) => keyring.type === KeyringTypes.hd)) { -+ throw new Error(constants_1.KeyringControllerError.NoHdKeyring); -+ } - let vault; - let newEncryptionKey; - if (__classPrivateFieldGet(this, _KeyringController_cacheEncryptionKey, "f")) { -@@ -1092,9 +1104,16 @@ _KeyringController_keyringBuilders = new WeakMap(), _KeyringController_keyrings +@@ -1092,9 +1092,13 @@ _KeyringController_keyringBuilders = new WeakMap(), _KeyringController_keyrings }, _KeyringController_addQRKeyring = function _KeyringController_addQRKeyring() { return __awaiter(this, void 0, void 0, function* () { // QRKeyring is not yet compatible with Keyring type from @metamask/utils @@ -29,41 +10,12 @@ index fc649ea6fc97b905d811b236de638172fb10b548..bb288fda7e2ef970ceefe2ac22bcf653 - accounts: [], - })); + /** -+ * Patch for @metamask/keyring-controller v13.0.0 -+ * Below code change will fix the issue 23804, The intial code added a empty accounts as argument when creating a new QR keyring. -+ * cause the new Keystone MetamaskKeyring default properties all are undefined during deserialise() process. -+ * Please refer to PR 23903 for detail. -+ * -+ * This patch can be found on the core branch `extension-keyring-controller-v13-patch` ++ * Patch for @metamask/keyring-controller v13.0.0 ++ * Below code change will fix the issue 23804, The intial code added a empty accounts as argument when creating a new QR keyring. ++ * cause the new Keystone MetamaskKeyring default properties all are undefined during deserialise() process. ++ * Please refer to PR 23903 for detail. + */ -+ // @ts-expect-error See patch note + const qrKeyring = (yield __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_newKeyring).call(this, KeyringTypes.qr)); const accounts = yield qrKeyring.getAccounts(); yield __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_checkForDuplicate).call(this, KeyringTypes.qr, accounts); __classPrivateFieldGet(this, _KeyringController_keyrings, "f").push(qrKeyring); -diff --git a/dist/constants.d.ts b/dist/constants.d.ts -index 0c02177576b840c8412bd5c047010439927cf4af..805c0d5f78578efdda95a6da6d66dce13c9003c6 100644 ---- a/dist/constants.d.ts -+++ b/dist/constants.d.ts -@@ -25,6 +25,7 @@ export declare enum KeyringControllerError { - MissingVaultData = "KeyringController - Cannot persist vault without vault information", - ExpiredCredentials = "KeyringController - Encryption key and salt provided are expired", - NoKeyringBuilder = "KeyringController - No keyringBuilder found for keyring", -- DataType = "KeyringController - Incorrect data type provided" -+ DataType = "KeyringController - Incorrect data type provided", -+ NoHdKeyring = "KeyringController - No HD Keyring found" - } - //# sourceMappingURL=constants.d.ts.map -\ No newline at end of file -diff --git a/dist/constants.js b/dist/constants.js -index 58b3a15b796396de78b9dc252baf23d5bd40ae0a..10768a8a6ad111c1f6552ba43ce8eca3c570c8eb 100644 ---- a/dist/constants.js -+++ b/dist/constants.js -@@ -30,5 +30,6 @@ var KeyringControllerError; - KeyringControllerError["ExpiredCredentials"] = "KeyringController - Encryption key and salt provided are expired"; - KeyringControllerError["NoKeyringBuilder"] = "KeyringController - No keyringBuilder found for keyring"; - KeyringControllerError["DataType"] = "KeyringController - Incorrect data type provided"; -+ KeyringControllerError["NoHdKeyring"] = "KeyringController - No HD Keyring found"; - })(KeyringControllerError = exports.KeyringControllerError || (exports.KeyringControllerError = {})); - //# sourceMappingURL=constants.js.map -\ No newline at end of file diff --git a/.yarnrc.yml b/.yarnrc.yml index adc73de8f717..374745715f05 100644 --- a/.yarnrc.yml +++ b/.yarnrc.yml @@ -43,6 +43,7 @@ npmAuditIgnoreAdvisories: # not appear to be used. - 1092461 + # Temp fix for https://github.com/MetaMask/metamask-extension/pull/16920 for the sake of 11.7.1 hotfix # This will be removed in this ticket https://github.com/MetaMask/metamask-extension/issues/22299 - 'ts-custom-error (deprecation)' @@ -92,7 +93,7 @@ npmAuditIgnoreAdvisories: # MetaMask owned repositories brought in by other MetaMask dependencies that # can be resolved by updating the versions throughout the dependency tree - 'eth-sig-util (deprecation)' # via @metamask/eth-ledger-bridge-keyring - - '@metamask/controller-utils (deprecation)' # via @metamask/phishing-controller + - '@metamask/controller-utils (deprecation)' # via @metamask/phishin-controller - 'safe-event-emitter (deprecation)' # via eth-block-tracker and others # @metamask-institutional relies upon crypto which is deprecated @@ -125,18 +126,18 @@ npmAuditIgnoreAdvisories: - '@metamask/snaps-ui (deprecation)' npmRegistries: - 'https://npm.pkg.github.com': + "https://npm.pkg.github.com": npmAlwaysAuth: true - npmAuthToken: '${GITHUB_PACKAGE_READ_TOKEN-}' + npmAuthToken: "${GITHUB_PACKAGE_READ_TOKEN-}" npmScopes: metamask: - npmRegistryServer: '${METAMASK_NPM_REGISTRY:-https://registry.yarnpkg.com}' + npmRegistryServer: "${METAMASK_NPM_REGISTRY:-https://registry.yarnpkg.com}" plugins: - path: .yarn/plugins/@yarnpkg/plugin-allow-scripts.cjs - spec: 'https://raw.githubusercontent.com/LavaMoat/LavaMoat/main/packages/yarn-plugin-allow-scripts/bundles/@yarnpkg/plugin-allow-scripts.js' + spec: "https://raw.githubusercontent.com/LavaMoat/LavaMoat/main/packages/yarn-plugin-allow-scripts/bundles/@yarnpkg/plugin-allow-scripts.js" - path: .yarn/plugins/@yarnpkg/plugin-engines.cjs - spec: 'https://raw.githubusercontent.com/devoto13/yarn-plugin-engines/main/bundles/%40yarnpkg/plugin-engines.js' + spec: "https://raw.githubusercontent.com/devoto13/yarn-plugin-engines/main/bundles/%40yarnpkg/plugin-engines.js" yarnPath: .yarn/releases/yarn-4.0.2.cjs diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f781ff25b81..4dd8ff84720f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,45 +6,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -## [11.15.0] -### Added -- Expanded Smart Transactions (STX) functionality to include non-Swaps transactions on Ethereum Mainnet for users opted into STX ([#23155](https://github.com/MetaMask/metamask-extension/pull/23155)) -- Added Base Mainnet and removed Gnosis and Celo networks from popular networks list ([#23880](https://github.com/MetaMask/metamask-extension/pull/23880)) -- Enhanced balance fetching efficiency by supporting bulk balance checks on Linea, Aurora, Base, and ZkSync ([#23436](https://github.com/MetaMask/metamask-extension/pull/23436)) -- Introduced a 'Learn more' link to the simulations toggle in privacy and onboarding settings ([#23890](https://github.com/MetaMask/metamask-extension/pull/23890)) -- Introduced a confirmation step for approving higher token spending limits ([#23560](https://github.com/MetaMask/metamask-extension/pull/23560)) -- Added a dismissible alert for users migrated from OpenSea to Blockaid on unsupported networks ([#23743](https://github.com/MetaMask/metamask-extension/pull/23743)) -- Implemented support for signature requests with a redesigned interface ([#23539](https://github.com/MetaMask/metamask-extension/pull/23539)) -- Introduced support for the new Linea Sepolia network and deprecated the Linea Goerli network ([#23459](https://github.com/MetaMask/metamask-extension/pull/23459)) - -### Changed -- Removed outdated announcements for 'Snaps Open Beta' and 'Buy & Sell' features ([#23940](https://github.com/MetaMask/metamask-extension/pull/23940)) -- Enhanced Smart Transactions swaps with detailed simulation views ([#23963](https://github.com/MetaMask/metamask-extension/pull/23963)) -- Updated the Arbitrum logo ([#23969](https://github.com/MetaMask/metamask-extension/pull/23969)) -- Enhanced the appearance of links in Snap dialogs ([#23840](https://github.com/MetaMask/metamask-extension/pull/23840)) -- Improved the layout of the security alert option in the settings page ([#23718](https://github.com/MetaMask/metamask-extension/pull/23718)) -- Enhanced the appearance of security alerts by fine-tuning their spacing ([#23900](https://github.com/MetaMask/metamask-extension/pull/23900)) -- Unified the color scheme for secondary titles in settings ([#23764](https://github.com/MetaMask/metamask-extension/pull/23764)) -- Adjusted asset icon display and sizing in simulation details ([#23760](https://github.com/MetaMask/metamask-extension/pull/23760)) -- Enhanced display for near-zero amounts and updated native token visuals for all chains ([#23711](https://github.com/MetaMask/metamask-extension/pull/23711)) -- Enhanced default token name visibility by always using the remote token list for petnames ([#23919](https://github.com/MetaMask/metamask-extension/pull/23919)) -- Improved transaction confirmation clarity by hiding totals for successful simulations ([#23899](https://github.com/MetaMask/metamask-extension/pull/23899)) -- Updated transaction controller to display balance changes for wrapped ERC-20 tokens and legacy ERC-721 tokens ([#23915](https://github.com/MetaMask/metamask-extension/pull/23915)) - -### Fixed -- Enhanced the send flow by fine-tuning input fields to keep trailing zeros and decimals ([#23808](https://github.com/MetaMask/metamask-extension/pull/23808)) -- Resolved token detection and import issues ([#23798](https://github.com/MetaMask/metamask-extension/pull/23798)) -- Implemented a deprecation warning for users switching to the Mumbai network ([#23846](https://github.com/MetaMask/metamask-extension/pull/23846)) -- Corrected the display of crypto balances in the presence of scam network warnings ([#23645](https://github.com/MetaMask/metamask-extension/pull/23645)) -- Enhanced UI for simulation details by wrapping and adding tooltips to long asset names and amounts ([#23768](https://github.com/MetaMask/metamask-extension/pull/23768)) -- Expanded the deprecation warning for OpenSea security alerts to include typed signature confirmations ([#23743](https://github.com/MetaMask/metamask-extension/pull/23743)) -- Fixed an issue in Firefox where security alerts weren't displaying due to permission settings ([#23958](https://github.com/MetaMask/metamask-extension/pull/23958)) -- Resolved an issue where the loading indicator overlapped with the UI in security alerts ([#23927](https://github.com/MetaMask/metamask-extension/pull/23927)) -- Resolved an issue with an infinite loading spinner on blockaid alerts ([#23480](https://github.com/MetaMask/metamask-extension/pull/23480)) -- Improved accessibility in the app's network selection by ensuring screen readers announce network names more clearly ([#23842](https://github.com/MetaMask/metamask-extension/pull/23842)) -- Resolved an issue preventing QR code-based hardware wallet connections ([#23903](https://github.com/MetaMask/metamask-extension/pull/23903)) -- Resolved an issue where the connected accounts modal would crash if a dApp with an installed Snap was accessed ([#23928](https://github.com/MetaMask/metamask-extension/pull/23928)) - +## [11.14.4] +### Fixed +- Fix bug that could cause safe-transfer-from transactions to be converted to transfer-from transactions, by removing the edit button on the safe-transfer-from confirmation screens ([#24287](https://github.com/MetaMask/metamask-extension/pull/24287)) ## [11.14.3] ### Fixed @@ -4661,8 +4625,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/v11.15.0...HEAD -[11.15.0]: https://github.com/MetaMask/metamask-extension/compare/v11.14.3...v11.15.0 +[Unreleased]: https://github.com/MetaMask/metamask-extension/compare/v11.14.4...HEAD +[11.14.4]: https://github.com/MetaMask/metamask-extension/compare/v11.14.3...v11.14.4 [11.14.3]: https://github.com/MetaMask/metamask-extension/compare/v11.14.2...v11.14.3 [11.14.2]: https://github.com/MetaMask/metamask-extension/compare/v11.14.1...v11.14.2 [11.14.1]: https://github.com/MetaMask/metamask-extension/compare/v11.14.0...v11.14.1 diff --git a/README.md b/README.md index 41f53ba99d53..d9a31a86a8b0 100644 --- a/README.md +++ b/README.md @@ -117,21 +117,20 @@ Before running e2e tests, ensure you've run `yarn install` to download dependenc 1. Use `yarn download-builds:test` to quickly download and unzip test builds for Chrome and Firefox into the `./dist/` folder. This method is fast and convenient for standard testing. 2. Create a custom test build: for testing against different build types, use `yarn build:test`. This command allows you to generate test builds for various types, including: - - `yarn build:test` for main build - - `yarn build:test:flask` for flask build - - `yarn build:test:mmi` for mmi build - - `yarn build:test:mv3` for mv3 build -3. Start a test build with live changes: `yarn start:test` is particularly useful for development. It starts a test build that automatically recompiles application code upon changes. This option is ideal for iterative testing and development. This command also allows you to generate test builds for various types, including: - - `yarn start:test` for main build - - `yarn start:test:flask` for flask build - - `yarn start:test:mv3` for mv3 build + - `yarn build:test` for main build + - `yarn build:test:flask` for flask build + - `yarn build:test:mmi` for mmi build + - `yarn build:test:mv3` for mv3 build +3. Start a test build with live changes: `yarn start:test` is particularly useful for development. It starts a test build that automatically recompiles application code upon changes.This option is ideal for iterative testing and development. +This command also allows you to generate test builds for various types, including: + - `yarn start:test` for main build + - `yarn start:test:flask` for flask build + - `yarn start:test:mv3` for mv3 build Note: The `yarn start:test` command (which initiates the testDev build type) has LavaMoat disabled for both the build system and the application, offering a streamlined testing experience during development. On the other hand, `yarn build:test` enables LavaMoat for enhanced security in both the build system and application, mirroring production environments more closely. #### Running Tests - Once you have your test build ready, choose the browser for your e2e tests: - - For Firefox, run `yarn test:e2e:firefox`. - For Chrome, run `yarn test:e2e:chrome`. @@ -142,11 +141,10 @@ These scripts support additional options for debugging. Use `--help`to see all a Single e2e tests can be run with `yarn test:e2e:single test/e2e/tests/TEST_NAME.spec.js` along with the options below. ```console - --browser Set the browser to be used; specify 'chrome', 'firefox', 'all' - or leave unset to run on 'all' by default. - [string] [default: 'all'] + --browser Set the browser used; either 'chrome' or 'firefox'. + [string] [choices: "chrome", "firefox"] --debug Run tests in debug mode, logging each driver interaction - [boolean] [default: true] + [boolean] [default: false] --retries Set how many times the test should be retried upon failure. [number] [default: 0] --leave-running Leaves the browser running after a test fails, along with @@ -157,10 +155,10 @@ Single e2e tests can be run with `yarn test:e2e:single test/e2e/tests/TEST_NAME. ``` For example, to run the `account-details` tests using Chrome, with debug logging and with the browser set to remain open upon failure, you would use: -`yarn test:e2e:single test/e2e/tests/account-menu/account-details.spec.js --browser=chrome --leave-running` +`yarn test:e2e:single test/e2e/tests/account-menu/account-details.spec.js --browser=chrome --debug --leave-running` -#### Running e2e tests against specific feature flag +#### Running e2e tests against specific feature flag While developing new features, we often use feature flags. As we prepare to make these features generally available (GA), we remove the feature flags. Existing feature flags are listed in the `.metamaskrc.dist` file. To execute e2e tests with a particular feature flag enabled, it's necessary to first generate a test build with that feature flag activated. There are two ways to achieve this: - To enable a feature flag in your local configuration, you should first ensure you have a `.metamaskrc` file copied from `.metamaskrc.dist`. Then, within your local `.metamaskrc` file, you can set the desired feature flag to true. Following this, a test build with the feature flag enabled can be created by executing `yarn build:test`. @@ -168,7 +166,7 @@ While developing new features, we often use feature flags. As we prepare to make - Alternatively, for enabling a feature flag directly during the test build creation, you can pass the parameter as true via the command line. For instance, activating the MULTICHAIN feature flag can be done by running `MULTICHAIN=1 yarn build:test` or `MULTICHAIN=1 yarn start:test` . This method allows for quick adjustments to feature flags without altering the `.metamaskrc` file. Once you've created a test build with the desired feature flag enabled, proceed to run your tests as usual. Your tests will now run against the version of the extension with the specific feature flag activated. For example: -`yarn test:e2e:single test/e2e/tests/account-menu/account-details.spec.js --browser=chrome` +`yarn test:e2e:single test/e2e/tests/account-menu/account-details.spec.js --browser=chrome --debug --leave-running` This approach ensures that your e2e tests accurately reflect the user experience for the upcoming GA features. diff --git a/app/_locales/de/messages.json b/app/_locales/de/messages.json index 4a6835316343..d7283cacf04b 100644 --- a/app/_locales/de/messages.json +++ b/app/_locales/de/messages.json @@ -3480,6 +3480,12 @@ "smartContracts": { "message": "Smart Contracts" }, + "smartSwapsAreHere": { + "message": "Die Smart Swaps sind da!" + }, + "smartSwapsDescription": { + "message": "MetaMask Swaps ist jetzt wesentlich intelligenter! Die Aktivierung von Smart Swaps wird es MetaMask erlauben, Ihre Swaps programmatisch zu optimieren, um zu helfen:" + }, "smartSwapsErrorNotEnoughFunds": { "message": "Nicht genügend Gelder für einen Smart Swap." }, @@ -3759,6 +3765,18 @@ "strong": { "message": "Stark" }, + "stxBenefit1": { + "message": "Transaktionskosten minimieren" + }, + "stxBenefit2": { + "message": "Transaktionsausfälle reduzieren" + }, + "stxBenefit3": { + "message": "Steckengebliebene Transaktionen eliminieren" + }, + "stxBenefit4": { + "message": "Front-Running verhindern" + }, "stxCancelled": { "message": "Swap wäre gescheitert" }, @@ -4689,7 +4707,7 @@ }, "viewOnCustomBlockExplorer": { "message": "Zeige $1 bei $2", - "description": "$1 is the action type. e.g (Account, Transaction, Swap) and $2 is the Custom Block Explorer URL" + "description": "$1 is the action type. e.g (Account, Transaction, Swap) and $2 is the Custom Block Exporer URL" }, "viewOnEtherscan": { "message": "$1 auf Etherscan anzeigen", diff --git a/app/_locales/el/messages.json b/app/_locales/el/messages.json index 330fcbfbdebc..676378aa3946 100644 --- a/app/_locales/el/messages.json +++ b/app/_locales/el/messages.json @@ -3480,6 +3480,12 @@ "smartContracts": { "message": "Έξυπνα συμβόλαια" }, + "smartSwapsAreHere": { + "message": "Οι Έξυπνες Ανταλλαγές είναι εδώ!" + }, + "smartSwapsDescription": { + "message": "Οι Ανταλλαγές στο MetaMask μόλις έγιναν πολύ πιο έξυπνες! Η ενεργοποίηση των Έξυπνων Ανταλλαγών θα επιτρέψει στο MetaMask να βελτιστοποιήσει προγραμματιστικά τις Ανταλλαγές σας, ώστε να σας βοηθήσει:" + }, "smartSwapsErrorNotEnoughFunds": { "message": "Δεν υπάρχουν αρκετά κεφάλαια για έξυπνες ανταλλαγές." }, @@ -3759,6 +3765,18 @@ "strong": { "message": "Ισχυρό" }, + "stxBenefit1": { + "message": "Ελαχιστοποίηση του κόστους συναλλαγών" + }, + "stxBenefit2": { + "message": "Μείωση των αποτυχημένων συναλλαγών" + }, + "stxBenefit3": { + "message": "Εξάλειψη των εμπλοκών στις συναλλαγές" + }, + "stxBenefit4": { + "message": "Αποτροπή των προπορευόμενων συναλλαγών (front-running)" + }, "stxCancelled": { "message": "Η ανταλλαγή θα είχε αποτύχει" }, @@ -4689,7 +4707,7 @@ }, "viewOnCustomBlockExplorer": { "message": "Προβολή $1 στο $2", - "description": "$1 is the action type. e.g (Account, Transaction, Swap) and $2 is the Custom Block Explorer URL" + "description": "$1 is the action type. e.g (Account, Transaction, Swap) and $2 is the Custom Block Exporer URL" }, "viewOnEtherscan": { "message": "Προβολή $1 στο Etherscan", diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 66fb46b027c9..db38fa50d0fc 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -666,6 +666,9 @@ "busy": { "message": "Busy" }, + "buy": { + "message": "Buy" + }, "buyAndSell": { "message": "Buy & Sell" }, @@ -680,10 +683,6 @@ "buyNow": { "message": "Buy Now" }, - "buyToken": { - "message": "Buy $1", - "description": "$1 is the token symbol" - }, "bytes": { "message": "Bytes" }, @@ -760,9 +759,6 @@ "close": { "message": "Close" }, - "closeExtension": { - "message": "Close extension" - }, "coingecko": { "message": "CoinGecko" }, @@ -898,9 +894,6 @@ "message": "$1 can see the account balance, address, activity, and suggest transactions to approve for connected accounts.", "description": "$1 is the origin name" }, - "connectedAccountsToast": { - "message": "Connected accounts updated" - }, "connectedSites": { "message": "Connected sites" }, @@ -918,6 +911,9 @@ "connectedWith": { "message": "Connected with" }, + "connectedaccountsTabKey": { + "message": "Connected accounts" + }, "connecting": { "message": "Connecting..." }, @@ -1423,14 +1419,6 @@ "disconnectThisAccount": { "message": "Disconnect this account" }, - "disconnectedAllAccountsToast": { - "message": "All accounts disconnected from $1", - "description": "$1 is name of the dapp`" - }, - "disconnectedSingleAccountToast": { - "message": "$1 disconnected from $2", - "description": "$1 is name of the name and $2 represents the dapp name`" - }, "discoverSnaps": { "message": "Discover Snaps", "description": "Text that links to the Snaps website. Displayed in a banner on Snaps list page in settings." @@ -1578,17 +1566,14 @@ "editSpeedUpEditGasFeeModalTitle": { "message": "Edit speed up gas fee" }, - "enable": { - "message": "Enable" - }, "enableAutoDetect": { "message": " Enable autodetect" }, "enableFromSettings": { "message": " Enable it from Settings." }, - "enableSmartTransactions": { - "message": "Enable Smart Transactions" + "enableSmartSwaps": { + "message": "Enable Smart Swaps" }, "enableSnap": { "message": "Enable" @@ -1760,6 +1745,9 @@ "failureMessage": { "message": "Something went wrong, and we were unable to complete the action" }, + "faqAndRiskDisclosures": { + "message": "FAQ and Risk Disclosures" + }, "fast": { "message": "Fast" }, @@ -1838,13 +1826,6 @@ "functionType": { "message": "Function type" }, - "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" }, @@ -1928,14 +1909,6 @@ "genericExplorerView": { "message": "View account on $1" }, - "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" - }, "globalTitle": { "message": "Global menu" }, @@ -2254,9 +2227,6 @@ "interactingWith": { "message": "Interacting with" }, - "introducingSmartTransactions": { - "message": "Introducing Smart Transactions" - }, "invalidAddress": { "message": "Invalid address" }, @@ -2404,9 +2374,6 @@ "learnMoreUpperCase": { "message": "Learn more" }, - "learnMoreUpperCaseWithDot": { - "message": "Learn more." - }, "learnScamRisk": { "message": "scams and security risks." }, @@ -2549,6 +2516,9 @@ "message": "Make sure nobody is looking", "description": "Warning to users to be care while creating and saving their new Secret Recovery Phrase" }, + "manageInSettings": { + "message": "Manage in settings" + }, "max": { "message": "Max" }, @@ -2907,6 +2877,9 @@ "nftDisclaimer": { "message": "Disclaimer: MetaMask pulls the media file from the source url. This url sometimes gets changed by the marketplace on which the NFT was minted." }, + "nftLearnMore": { + "message": "Learn more about NFTs" + }, "nftOptions": { "message": "NFT Options" }, @@ -2972,9 +2945,6 @@ "noWebcamFoundTitle": { "message": "Webcam not found" }, - "nonCustodialAccounts": { - "message": "MetaMask Institutional allows you to use non-custodial accounts, if you plan to use these accounts backup the Secret Recovery Phrase." - }, "nonce": { "message": "Nonce" }, @@ -2999,9 +2969,6 @@ "notEnoughGas": { "message": "Not enough gas" }, - "notRightNow": { - "message": "Not right now" - }, "note": { "message": "Note" }, @@ -3095,14 +3062,6 @@ "notificationsEmptyText": { "message": "This is where you can find notifications from your installed snaps." }, - "notificationsFeatureToggle": { - "message": "Enable Wallet Notifications", - "description": "Experimental feature title" - }, - "notificationsFeatureToggleDescription": { - "message": "This enables wallet notifications like send/receive funds or nfts and feature announcements.", - "description": "Description of the experimental notifications feature" - }, "notificationsHeader": { "message": "Notifications" }, @@ -3796,9 +3755,6 @@ "receive": { "message": "Receive" }, - "receiveTokensCamelCase": { - "message": "Receive tokens" - }, "recipientAddressPlaceholder": { "message": "Enter public address (0x) or ENS name" }, @@ -3883,9 +3839,6 @@ "removeNFT": { "message": "Remove NFT" }, - "removeNftErrorMessage": { - "message": "We could not remove this NFT." - }, "removeNftMessage": { "message": "NFT was successfully removed!" }, @@ -4418,58 +4371,28 @@ "smartContracts": { "message": "Smart contracts" }, - "smartSwapsErrorNotEnoughFunds": { - "message": "Not enough funds for a smart swap." - }, - "smartSwapsErrorUnavailable": { - "message": "Smart Swaps are temporarily unavailable." - }, - "smartTransactionCancelled": { - "message": "Your transaction was canceled" - }, - "smartTransactionCancelledDescription": { - "message": "Your transaction couldn't be completed, so it was canceled to save you from paying unnecessary gas fees." - }, - "smartTransactionError": { - "message": "Your transaction failed" - }, - "smartTransactionErrorDescription": { - "message": "Sudden market changes can cause failures. If the problem continues, reach out to MetaMask customer support." + "smartSwaps": { + "message": "Smart Swaps" }, - "smartTransactionPending": { - "message": "Submitting your transaction" + "smartSwapsAreHere": { + "message": "Smart Swaps are here!" }, - "smartTransactionSuccess": { - "message": "Your transaction is complete" + "smartSwapsDescription": { + "message": "MetaMask Swaps just got a whole lot smarter! Enabling Smart Swaps will allow MetaMask to programmatically optimize your Swap to help:" }, - "smartTransactionTakingTooLong": { - "message": "Sorry for the wait" + "smartSwapsDescription2": { + "message": "*Smart Swaps will submit your transaction privately. You can opt-out in advanced settings at any time. To learn more about Smart Swaps, read our $1.", + "description": "$1 is an external link to FAQ and Risk Disclosures" }, - "smartTransactionTakingTooLongDescription": { - "message": "If your transaction is not finalized within $1, it will be canceled and you will not be charged for gas.", - "description": "$1 is remaining time in seconds" - }, - "smartTransactions": { - "message": "Smart Transactions" - }, - "smartTransactionsBenefit1": { - "message": "82% fewer failed transactions" - }, - "smartTransactionsBenefit2": { - "message": "Transaction protection" - }, - "smartTransactionsBenefit3": { - "message": "Real-time updates" - }, - "smartTransactionsDescription": { - "message": "Unlock the safest, most reliable, and easiest transaction experience - a smarter way to navigate web3." + "smartSwapsErrorNotEnoughFunds": { + "message": "Not enough funds for a smart swap." }, - "smartTransactionsDescription2": { - "message": "Millions of dollars are lost every month due to failed transactions & frontrunning. Smart Transactions fixes this." + "smartSwapsErrorUnavailable": { + "message": "Smart Swaps are temporarily unavailable." }, - "smartTransactionsDescription3": { - "message": "Right now, Smart Transactions are only available on ETH Mainnet. You can turn them off at any time in settings. $1", - "description": "$1 is an external link to learn more about Smart Transactions" + "smartSwapsTooltip": { + "message": "Simulate transactions before submitting to decrease transaction costs and reduce failures. To learn more, read our $1", + "description": "$1 is an external link to FAQ and Risk Disclosures" }, "snapAccountCreated": { "message": "Account created" @@ -4782,14 +4705,6 @@ "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." }, @@ -4845,6 +4760,18 @@ "strong": { "message": "Strong" }, + "stxBenefit1": { + "message": "Minimize transaction costs" + }, + "stxBenefit2": { + "message": "Reduce transaction failures" + }, + "stxBenefit3": { + "message": "Eliminate stuck transactions" + }, + "stxBenefit4": { + "message": "Prevent front-running" + }, "stxCancelled": { "message": "Swap would have failed" }, @@ -4854,10 +4781,6 @@ "stxCancelledSubDescription": { "message": "Try your swap again. We’ll be here to protect you against similar risks next time." }, - "stxEstimatedCompletion": { - "message": "Estimated completion in < $1", - "description": "$1 is remeaning time in minutes and seconds, e.g. 0:10" - }, "stxFailure": { "message": "Swap failed" }, @@ -4865,9 +4788,6 @@ "message": "Sudden market changes can cause failures. If the problem persists, please reach out to $1.", "description": "This message is shown to a user if their swap fails. The $1 will be replaced by support.metamask.io" }, - "stxOptInDescription": { - "message": "Turn on Smart Transactions for more reliable and secure transactions, and adjustable fees on ETH Mainnet. $1" - }, "stxPendingPrivatelySubmittingSwap": { "message": "Privately submitting your Swap..." }, @@ -5356,13 +5276,6 @@ "switchToThisAccount": { "message": "Switch to this account" }, - "switchedNetworkToastDecline": { - "message": "Don't show again" - }, - "switchedNetworkToastMessage": { - "message": "$1 is now active on $2", - "description": "$1 represents the account name, $2 represents the network name" - }, "switchedTo": { "message": "You're now using" }, @@ -5825,9 +5738,6 @@ "view": { "message": "View" }, - "viewActivity": { - "message": "View activity" - }, "viewAllDetails": { "message": "View all details" }, @@ -5851,7 +5761,7 @@ }, "viewOnCustomBlockExplorer": { "message": "View $1 at $2", - "description": "$1 is the action type. e.g (Account, Transaction, Swap) and $2 is the Custom Block Explorer URL" + "description": "$1 is the action type. e.g (Account, Transaction, Swap) and $2 is the Custom Block Exporer URL" }, "viewOnEtherscan": { "message": "View $1 on Etherscan", @@ -5863,9 +5773,6 @@ "viewOnOpensea": { "message": "View on Opensea" }, - "viewTransaction": { - "message": "View transaction" - }, "viewinCustodianApp": { "message": "View in custodian app" }, diff --git a/app/_locales/es/messages.json b/app/_locales/es/messages.json index d880225a4ffd..fbff1412a9e7 100644 --- a/app/_locales/es/messages.json +++ b/app/_locales/es/messages.json @@ -3480,6 +3480,12 @@ "smartContracts": { "message": "Contratos inteligentes" }, + "smartSwapsAreHere": { + "message": "¡Los intercambios inteligentes ya están aquí!" + }, + "smartSwapsDescription": { + "message": "¡La función Intercambios de MetaMask ahora es mucho más inteligente! Habilitar Intercambios inteligentes permitirá que MetaMask optimice mediante programación su intercambio para ayudar a:" + }, "smartSwapsErrorNotEnoughFunds": { "message": "No hay suficientes fondos para un intercambio inteligente." }, @@ -3759,6 +3765,18 @@ "strong": { "message": "Fuerte" }, + "stxBenefit1": { + "message": "Minimizar los costos de transacción" + }, + "stxBenefit2": { + "message": "Reducir las fallas en las transacciones" + }, + "stxBenefit3": { + "message": "Eliminar las transacciones atascadas" + }, + "stxBenefit4": { + "message": "Prevenir la inversión ventajista" + }, "stxCancelled": { "message": "El intercambio habría fallado" }, @@ -4689,7 +4707,7 @@ }, "viewOnCustomBlockExplorer": { "message": "Ver $1 en $2", - "description": "$1 is the action type. e.g (Account, Transaction, Swap) and $2 is the Custom Block Explorer URL" + "description": "$1 is the action type. e.g (Account, Transaction, Swap) and $2 is the Custom Block Exporer URL" }, "viewOnEtherscan": { "message": "Ver $1 en Etherscan", diff --git a/app/_locales/es_419/messages.json b/app/_locales/es_419/messages.json index 7ed07f835b8b..370c94563e9d 100644 --- a/app/_locales/es_419/messages.json +++ b/app/_locales/es_419/messages.json @@ -2543,7 +2543,7 @@ }, "viewOnCustomBlockExplorer": { "message": "Ver $1 en $2", - "description": "$1 is the action type. e.g (Account, Transaction, Swap) and $2 is the Custom Block Explorer URL" + "description": "$1 is the action type. e.g (Account, Transaction, Swap) and $2 is the Custom Block Exporer URL" }, "viewOnEtherscan": { "message": "Ver $1 en Etherscan", diff --git a/app/_locales/fr/messages.json b/app/_locales/fr/messages.json index a05b9ce014b5..6a29f4110dca 100644 --- a/app/_locales/fr/messages.json +++ b/app/_locales/fr/messages.json @@ -3483,6 +3483,12 @@ "smartContracts": { "message": "Contrats intelligents" }, + "smartSwapsAreHere": { + "message": "Les contrats de swap intelligents sont enfin arrivés !" + }, + "smartSwapsDescription": { + "message": "Les swaps sont devenus beaucoup plus intelligents sur MetaMask ! L’activation des contrats de swap intelligents permettra à MetaMask d’optimiser programmatiquement le processus contractuel pour vous aider à :" + }, "smartSwapsErrorNotEnoughFunds": { "message": "Fonds insuffisants pour souscrire un contrat de swap intelligent." }, @@ -3762,6 +3768,18 @@ "strong": { "message": "Robuste" }, + "stxBenefit1": { + "message": "Minimise les frais de transaction" + }, + "stxBenefit2": { + "message": "Réduit les échecs de transaction" + }, + "stxBenefit3": { + "message": "Élimine les blocages de transaction" + }, + "stxBenefit4": { + "message": "Empêcher le favoritisme" + }, "stxCancelled": { "message": "Le swap aurait échoué" }, @@ -4692,7 +4710,7 @@ }, "viewOnCustomBlockExplorer": { "message": "Afficher $1 à $2", - "description": "$1 is the action type. e.g (Account, Transaction, Swap) and $2 is the Custom Block Explorer URL" + "description": "$1 is the action type. e.g (Account, Transaction, Swap) and $2 is the Custom Block Exporer URL" }, "viewOnEtherscan": { "message": "Afficher $1 sur Etherscan", diff --git a/app/_locales/hi/messages.json b/app/_locales/hi/messages.json index e55d1f11604b..7d1c50d48515 100644 --- a/app/_locales/hi/messages.json +++ b/app/_locales/hi/messages.json @@ -3480,6 +3480,12 @@ "smartContracts": { "message": "स्मार्ट कॉन्ट्रैक्ट्स" }, + "smartSwapsAreHere": { + "message": "स्मार्ट स्वैप यहां हैं!" + }, + "smartSwapsDescription": { + "message": "MetaMask के स्वैप अब और अधिक स्मार्ट हो गए हैं! इन हेतु सहायता के लिए स्मार्ट स्वैप को इनेबल करने से MetaMask आपके स्वैप को प्रोग्रामेटिक रूप से ऑप्टिमाइज कर पाएगा:" + }, "smartSwapsErrorNotEnoughFunds": { "message": "स्मार्ट स्वैप के लिए पर्याप्त फंड नहीं है।" }, @@ -3759,6 +3765,18 @@ "strong": { "message": "मजबूत" }, + "stxBenefit1": { + "message": "ट्रांसेक्शन लागतें मिनिमाइज़ करें" + }, + "stxBenefit2": { + "message": "ट्रांसेक्शन विफलताएं कम करें" + }, + "stxBenefit3": { + "message": "अटके हुए ट्रांसेक्शन को हटा दें" + }, + "stxBenefit4": { + "message": "फ़्रंट-रनिंग को रोकें" + }, "stxCancelled": { "message": "स्वैप विफल हो सकता था" }, @@ -4689,7 +4707,7 @@ }, "viewOnCustomBlockExplorer": { "message": "$1 को $2 पर देखें", - "description": "$1 is the action type. e.g (Account, Transaction, Swap) and $2 is the Custom Block Explorer URL" + "description": "$1 is the action type. e.g (Account, Transaction, Swap) and $2 is the Custom Block Exporer URL" }, "viewOnEtherscan": { "message": "Etherscan पर $1 देखें", diff --git a/app/_locales/id/messages.json b/app/_locales/id/messages.json index e01944cd0a67..52017a426059 100644 --- a/app/_locales/id/messages.json +++ b/app/_locales/id/messages.json @@ -3480,6 +3480,12 @@ "smartContracts": { "message": "Kontrak cerdas" }, + "smartSwapsAreHere": { + "message": "Smart Swap telah hadir!" + }, + "smartSwapsDescription": { + "message": "MetaMask Swaps kini semakin pintar! Mengaktifkan Smart Swap akan mengizinkan MetaMask mengoptimalkan Swap secara terprogram untuk membantu:" + }, "smartSwapsErrorNotEnoughFunds": { "message": "Dana tidak cukup untuk pertukaran cerdas." }, @@ -3759,6 +3765,18 @@ "strong": { "message": "Kuat" }, + "stxBenefit1": { + "message": "Meminimalkan biaya transaksi" + }, + "stxBenefit2": { + "message": "Kurangi potensi kegagalan transaksi \t" + }, + "stxBenefit3": { + "message": "Hapus transaksi yang macet" + }, + "stxBenefit4": { + "message": "Cegah perilaku front running \t" + }, "stxCancelled": { "message": "Pertukaran akan gagal" }, @@ -4689,7 +4707,7 @@ }, "viewOnCustomBlockExplorer": { "message": "Lihat $1 di $2", - "description": "$1 is the action type. e.g (Account, Transaction, Swap) and $2 is the Custom Block Explorer URL" + "description": "$1 is the action type. e.g (Account, Transaction, Swap) and $2 is the Custom Block Exporer URL" }, "viewOnEtherscan": { "message": "Lihat $1 di Etherscan", diff --git a/app/_locales/ja/messages.json b/app/_locales/ja/messages.json index 671eae4c90d7..2c096d1b1b52 100644 --- a/app/_locales/ja/messages.json +++ b/app/_locales/ja/messages.json @@ -3480,6 +3480,12 @@ "smartContracts": { "message": "スマートコントラクト" }, + "smartSwapsAreHere": { + "message": "スマートスワップの登場です!" + }, + "smartSwapsDescription": { + "message": "MetaMask Swapsがはるかに賢くなりました!スマートスワップを有効にすると、MetaMaskがプログラムに従ってスワップを最適化できるようになるため、以下のようなメリットがあります。" + }, "smartSwapsErrorNotEnoughFunds": { "message": "スマートスワップに必要な資金が不足しています。" }, @@ -3759,6 +3765,18 @@ "strong": { "message": "強" }, + "stxBenefit1": { + "message": "トランザクションコストを最小化" + }, + "stxBenefit2": { + "message": "トランザクションの失敗数を低減" + }, + "stxBenefit3": { + "message": "トランザクションの停滞を解消" + }, + "stxBenefit4": { + "message": "フロントランニングを防止" + }, "stxCancelled": { "message": "スワップが失敗するところでした" }, @@ -4689,7 +4707,7 @@ }, "viewOnCustomBlockExplorer": { "message": "$1を$2で表示", - "description": "$1 is the action type. e.g (Account, Transaction, Swap) and $2 is the Custom Block Explorer URL" + "description": "$1 is the action type. e.g (Account, Transaction, Swap) and $2 is the Custom Block Exporer URL" }, "viewOnEtherscan": { "message": "$1をEtherscanで表示", diff --git a/app/_locales/ko/messages.json b/app/_locales/ko/messages.json index 02e18ddd1538..ad6702b20e57 100644 --- a/app/_locales/ko/messages.json +++ b/app/_locales/ko/messages.json @@ -3480,6 +3480,12 @@ "smartContracts": { "message": "스마트 계약" }, + "smartSwapsAreHere": { + "message": "스마트 스왑이 시작되었습니다!" + }, + "smartSwapsDescription": { + "message": "MetaMask 스왑이 더욱 스마트해졌습니다! 스마트 스왑을 활성화하면 MetaMask가 프로그램을 통해 스왑을 최적화하여 다음과 같은 활동에 도움을 드립니다." + }, "smartSwapsErrorNotEnoughFunds": { "message": "스마트 스왑 자금 부족" }, @@ -3759,6 +3765,18 @@ "strong": { "message": "강함" }, + "stxBenefit1": { + "message": "트랜잭션 비용 최소화하기" + }, + "stxBenefit2": { + "message": "트랜잭션 실패 줄이기" + }, + "stxBenefit3": { + "message": "중단된 트랜잭션 제거하기" + }, + "stxBenefit4": { + "message": "프런트 러닝 방지" + }, "stxCancelled": { "message": "스왑이 실패했을 것입니다" }, @@ -4689,7 +4707,7 @@ }, "viewOnCustomBlockExplorer": { "message": "$2에서 $1 보기", - "description": "$1 is the action type. e.g (Account, Transaction, Swap) and $2 is the Custom Block Explorer URL" + "description": "$1 is the action type. e.g (Account, Transaction, Swap) and $2 is the Custom Block Exporer URL" }, "viewOnEtherscan": { "message": "Etherscan에서 $1 보기", diff --git a/app/_locales/pt/messages.json b/app/_locales/pt/messages.json index cc46fcce77e9..adfdccf4280b 100644 --- a/app/_locales/pt/messages.json +++ b/app/_locales/pt/messages.json @@ -3484,6 +3484,12 @@ "smartContracts": { "message": "Contratos inteligentes" }, + "smartSwapsAreHere": { + "message": "As trocas inteligentes chegaram!" + }, + "smartSwapsDescription": { + "message": "As trocas na MetaMask ficaram muito mais inteligentes! Ativar as trocas inteligentes permitirá que a MetaMask otimize programaticamente sua troca para ajudar:" + }, "smartSwapsErrorNotEnoughFunds": { "message": "Fundos insuficientes para uma troca inteligente." }, @@ -3763,6 +3769,18 @@ "strong": { "message": "Forte" }, + "stxBenefit1": { + "message": "Minimize os custos das transações" + }, + "stxBenefit2": { + "message": "Reduza as falhas nas transações" + }, + "stxBenefit3": { + "message": "Elimine transações travadas" + }, + "stxBenefit4": { + "message": "Previna o front-running (uso de informações privilegiadas para negociações)" + }, "stxCancelled": { "message": "A troca teria falhado" }, @@ -4693,7 +4711,7 @@ }, "viewOnCustomBlockExplorer": { "message": "Ver $1 em $2", - "description": "$1 is the action type. e.g (Account, Transaction, Swap) and $2 is the Custom Block Explorer URL" + "description": "$1 is the action type. e.g (Account, Transaction, Swap) and $2 is the Custom Block Exporer URL" }, "viewOnEtherscan": { "message": "Ver $1 no Etherscan", diff --git a/app/_locales/pt_BR/messages.json b/app/_locales/pt_BR/messages.json index 1cb18170dda1..c2fe68724a82 100644 --- a/app/_locales/pt_BR/messages.json +++ b/app/_locales/pt_BR/messages.json @@ -2547,7 +2547,7 @@ }, "viewOnCustomBlockExplorer": { "message": "Ver $1 em $2", - "description": "$1 is the action type. e.g (Account, Transaction, Swap) and $2 is the Custom Block Explorer URL" + "description": "$1 is the action type. e.g (Account, Transaction, Swap) and $2 is the Custom Block Exporer URL" }, "viewOnEtherscan": { "message": "Ver $1 no Etherscan", diff --git a/app/_locales/ru/messages.json b/app/_locales/ru/messages.json index 892a68f3ec20..bd3b27f8beae 100644 --- a/app/_locales/ru/messages.json +++ b/app/_locales/ru/messages.json @@ -3480,6 +3480,12 @@ "smartContracts": { "message": "Смарт-контракты" }, + "smartSwapsAreHere": { + "message": "Появились смарт-свопы!" + }, + "smartSwapsDescription": { + "message": "Свопы MetaMask стали намного умнее! Включение смарт-свопов позволит MetaMask программно оптимизировать ваш своп, чтобы помочь:" + }, "smartSwapsErrorNotEnoughFunds": { "message": "Недостаточно средств для смарт-свопа." }, @@ -3759,6 +3765,18 @@ "strong": { "message": "Сильный" }, + "stxBenefit1": { + "message": "Минимизируйте транзакционные издержки" + }, + "stxBenefit2": { + "message": "Уменьшите количество сбоев транзакций" + }, + "stxBenefit3": { + "message": "Устраните зависание транзакций" + }, + "stxBenefit4": { + "message": "Предотвратите опережение" + }, "stxCancelled": { "message": "Своп бы не удался" }, @@ -4689,7 +4707,7 @@ }, "viewOnCustomBlockExplorer": { "message": "Смотреть $1 в $2", - "description": "$1 is the action type. e.g (Account, Transaction, Swap) and $2 is the Custom Block Explorer URL" + "description": "$1 is the action type. e.g (Account, Transaction, Swap) and $2 is the Custom Block Exporer URL" }, "viewOnEtherscan": { "message": "Смотреть 1$ на Etherscan", diff --git a/app/_locales/tl/messages.json b/app/_locales/tl/messages.json index b56219510f25..115104b327dd 100644 --- a/app/_locales/tl/messages.json +++ b/app/_locales/tl/messages.json @@ -3480,6 +3480,12 @@ "smartContracts": { "message": "Mga smart na kontrata" }, + "smartSwapsAreHere": { + "message": "Nandito na ang mga Smart Swap!" + }, + "smartSwapsDescription": { + "message": "Mas humusay pa ang mga MetaMask Swap! Ang pag-enable sa mga Smart Swap ay magbibigay-daan sa MetaMask na i-optimize ang iyong Swap gamit ang program para makatulong na:" + }, "smartSwapsErrorNotEnoughFunds": { "message": "Hindi sapat ang pondo para sa smart swap." }, @@ -3759,6 +3765,18 @@ "strong": { "message": "Mahirap" }, + "stxBenefit1": { + "message": "Bawasan ang mga gastos sa transaksyon" + }, + "stxBenefit2": { + "message": "Bawasan ang mga nabigong transaksyon" + }, + "stxBenefit3": { + "message": "Alisin ang mga hindi umuusad na transaksyon" + }, + "stxBenefit4": { + "message": "Pigilan ang front-running" + }, "stxCancelled": { "message": "Nabigo sana ang pag-swap kung" }, @@ -4689,7 +4707,7 @@ }, "viewOnCustomBlockExplorer": { "message": "Tingnan ang $1 sa $2", - "description": "$1 is the action type. e.g (Account, Transaction, Swap) and $2 is the Custom Block Explorer URL" + "description": "$1 is the action type. e.g (Account, Transaction, Swap) and $2 is the Custom Block Exporer URL" }, "viewOnEtherscan": { "message": "Tingnan ang $1 sa Etherscan", diff --git a/app/_locales/tr/messages.json b/app/_locales/tr/messages.json index 13163c4a498f..0b268dcd5a9e 100644 --- a/app/_locales/tr/messages.json +++ b/app/_locales/tr/messages.json @@ -3480,6 +3480,12 @@ "smartContracts": { "message": "Akıllı sözleşmeler" }, + "smartSwapsAreHere": { + "message": "Akıllı Swap'lar burada!" + }, + "smartSwapsDescription": { + "message": "MetaMask Swap işlemleri artık çok daha akıllı! Akıllı Swap'ları etkinleştirmek, MetaMask'in aşağıdakilere yardımcı olmak için Swap'ini programlı olarak optimize etmesine olanak tanır:" + }, "smartSwapsErrorNotEnoughFunds": { "message": "Akıllı swap için yeterli para yok." }, @@ -3759,6 +3765,18 @@ "strong": { "message": "Güçlü" }, + "stxBenefit1": { + "message": "İşlem maliyetlerini en aza indir" + }, + "stxBenefit2": { + "message": "İşlem hatalarını azalt" + }, + "stxBenefit3": { + "message": "Sıkışmış işlemleri ortadan kaldır" + }, + "stxBenefit4": { + "message": "Önden çalıştırmayı engelle" + }, "stxCancelled": { "message": "Swap işlemi başarısız olurdu" }, @@ -4689,7 +4707,7 @@ }, "viewOnCustomBlockExplorer": { "message": "$1 ögesini $2 üzerinde görüntüle", - "description": "$1 is the action type. e.g (Account, Transaction, Swap) and $2 is the Custom Block Explorer URL" + "description": "$1 is the action type. e.g (Account, Transaction, Swap) and $2 is the Custom Block Exporer URL" }, "viewOnEtherscan": { "message": "Etherscan'de $1 görüntüle", diff --git a/app/_locales/vi/messages.json b/app/_locales/vi/messages.json index fd6df9be7c59..62a63f8e1ec7 100644 --- a/app/_locales/vi/messages.json +++ b/app/_locales/vi/messages.json @@ -3480,6 +3480,12 @@ "smartContracts": { "message": "Hợp đồng thông minh" }, + "smartSwapsAreHere": { + "message": "Hoán đổi thông minh đã ra mắt!" + }, + "smartSwapsDescription": { + "message": "Tính năng Hoán đổi của MetaMask nay đã thông minh hơn rất nhiều! Kích hoạt Hoán đổi thông minh sẽ cho phép MetaMask tối ưu quy trình Hoán đổi để giúp bạn:" + }, "smartSwapsErrorNotEnoughFunds": { "message": "Không có đủ tiền để thực hiện hoán đổi thông minh." }, @@ -3759,6 +3765,18 @@ "strong": { "message": "Mạnh" }, + "stxBenefit1": { + "message": "Giảm thiểu chi phí giao dịch" + }, + "stxBenefit2": { + "message": "Giảm tỷ lệ thất bại khi giao dịch" + }, + "stxBenefit3": { + "message": "Loại bỏ các giao dịch bị mắc kẹt" + }, + "stxBenefit4": { + "message": "Ngăn chặn giao dịch chạy trước" + }, "stxCancelled": { "message": "Hoán đổi sẽ thất bại" }, @@ -4689,7 +4707,7 @@ }, "viewOnCustomBlockExplorer": { "message": "Xem $1 tại $2", - "description": "$1 is the action type. e.g (Account, Transaction, Swap) and $2 is the Custom Block Explorer URL" + "description": "$1 is the action type. e.g (Account, Transaction, Swap) and $2 is the Custom Block Exporer URL" }, "viewOnEtherscan": { "message": "Xem $1 trên Etherscan", diff --git a/app/_locales/zh_CN/messages.json b/app/_locales/zh_CN/messages.json index 303dad1975f3..8e2a95ae4ecb 100644 --- a/app/_locales/zh_CN/messages.json +++ b/app/_locales/zh_CN/messages.json @@ -3480,6 +3480,12 @@ "smartContracts": { "message": "智能合约" }, + "smartSwapsAreHere": { + "message": "智能兑换已推出!" + }, + "smartSwapsDescription": { + "message": "MetaMask Swaps 变得更加智能!启用智能兑换使得 MetaMask 在编程方面让您的兑换体验更加优化,有助于:" + }, "smartSwapsErrorNotEnoughFunds": { "message": "没有足够的资金进行智能兑换。" }, @@ -3759,6 +3765,18 @@ "strong": { "message": "强" }, + "stxBenefit1": { + "message": "将交易成本减至最低" + }, + "stxBenefit2": { + "message": "减少交易失败" + }, + "stxBenefit3": { + "message": "消除卡住的交易" + }, + "stxBenefit4": { + "message": "防止抢先交易" + }, "stxCancelled": { "message": "交换就会失败" }, @@ -4689,7 +4707,7 @@ }, "viewOnCustomBlockExplorer": { "message": "在 $2 上查看 $1", - "description": "$1 is the action type. e.g (Account, Transaction, Swap) and $2 is the Custom Block Explorer URL" + "description": "$1 is the action type. e.g (Account, Transaction, Swap) and $2 is the Custom Block Exporer URL" }, "viewOnEtherscan": { "message": "在 Etherscan 上查看 $1", diff --git a/app/_locales/zh_TW/messages.json b/app/_locales/zh_TW/messages.json index 57c40475e47b..ce0fc3d38a96 100644 --- a/app/_locales/zh_TW/messages.json +++ b/app/_locales/zh_TW/messages.json @@ -1483,7 +1483,7 @@ }, "viewOnCustomBlockExplorer": { "message": "在 $1 瀏覽", - "description": "$1 is the action type. e.g (Account, Transaction, Swap) and $2 is the Custom Block Explorer URL" + "description": "$1 is the action type. e.g (Account, Transaction, Swap) and $2 is the Custom Block Exporer URL" }, "viewOnEtherscan": { "message": "在 Etherscan 上瀏覽", diff --git a/app/background.html b/app/background.html index c0068295d730..148842e8f868 100644 --- a/app/background.html +++ b/app/background.html @@ -4,7 +4,7 @@ - - + + diff --git a/app/home.html b/app/home.html index b94800b13d78..3210c30475c9 100644 --- a/app/home.html +++ b/app/home.html @@ -8,7 +8,7 @@ <% } else { %> MetaMask <% } %> - +
@@ -16,6 +16,6 @@
- + diff --git a/app/images/arbitrum.svg b/app/images/arbitrum.svg index 9c19a48f2d72..8863afe882c8 100644 --- a/app/images/arbitrum.svg +++ b/app/images/arbitrum.svg @@ -1,40 +1,12 @@ - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + \ No newline at end of file diff --git a/app/images/logo/metamask-smart-transactions.png b/app/images/logo/metamask-smart-transactions.png new file mode 100644 index 000000000000..6eed753120bf Binary files /dev/null and b/app/images/logo/metamask-smart-transactions.png differ diff --git a/app/images/ramps-card-activity-illustration.png b/app/images/ramps-card-activity-illustration.png deleted file mode 100644 index 0a0d839e8fc8..000000000000 Binary files a/app/images/ramps-card-activity-illustration.png and /dev/null differ diff --git a/app/images/ramps-card-nft-illustration.png b/app/images/ramps-card-nft-illustration.png deleted file mode 100644 index 1cbc824592f8..000000000000 Binary files a/app/images/ramps-card-nft-illustration.png and /dev/null differ diff --git a/app/images/ramps-card-token-illustration.png b/app/images/ramps-card-token-illustration.png deleted file mode 100644 index 7a226ede84db..000000000000 Binary files a/app/images/ramps-card-token-illustration.png and /dev/null differ diff --git a/app/images/smart-transactions/smart-transactions-opt-in-background.svg b/app/images/smart-transactions/smart-transactions-opt-in-background.svg deleted file mode 100644 index 965a348c487a..000000000000 --- a/app/images/smart-transactions/smart-transactions-opt-in-background.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/app/loading.html b/app/loading.html index 600fa01960d1..dd5902a09a39 100644 --- a/app/loading.html +++ b/app/loading.html @@ -1,9 +1,9 @@ + + - - MetaMask Loading diff --git a/app/manifest/v2/_base.json b/app/manifest/v2/_base.json index 4197c535972e..c130b94ae1fb 100644 --- a/app/manifest/v2/_base.json +++ b/app/manifest/v2/_base.json @@ -31,12 +31,12 @@ { "matches": ["file://*/*", "http://*/*", "https://*/*"], "js": [ - "scripts/disable-console.js", - "scripts/lockdown-install.js", - "scripts/lockdown-run.js", - "scripts/lockdown-more.js", - "scripts/contentscript.js", - "scripts/inpage.js" + "disable-console.js", + "lockdown-install.js", + "lockdown-run.js", + "lockdown-more.js", + "contentscript.js", + "inpage.js" ], "run_at": "document_start", "all_frames": true diff --git a/app/manifest/v3/_base.json b/app/manifest/v3/_base.json index 39e96825add8..42df2d437671 100644 --- a/app/manifest/v3/_base.json +++ b/app/manifest/v3/_base.json @@ -14,7 +14,7 @@ }, "author": "https://metamask.io", "background": { - "service_worker": "scripts/app-init.js" + "service_worker": "app-init.js" }, "commands": { "_execute_browser_action": { @@ -30,11 +30,11 @@ { "matches": ["file://*/*", "http://*/*", "https://*/*"], "js": [ - "scripts/disable-console.js", - "scripts/lockdown-install.js", - "scripts/lockdown-run.js", - "scripts/lockdown-more.js", - "scripts/contentscript.js" + "disable-console.js", + "lockdown-install.js", + "lockdown-run.js", + "lockdown-more.js", + "contentscript.js" ], "run_at": "document_start", "all_frames": true diff --git a/app/notification.html b/app/notification.html index 70f02e9ab421..a0e0f6dcf5a7 100644 --- a/app/notification.html +++ b/app/notification.html @@ -32,7 +32,7 @@ margin-top: 1rem; } - +
@@ -50,6 +50,11 @@ />
- + diff --git a/app/popup.html b/app/popup.html index 296b0ceae711..c27442faa6af 100644 --- a/app/popup.html +++ b/app/popup.html @@ -4,7 +4,7 @@ MetaMask - +
@@ -12,6 +12,6 @@
- + diff --git a/app/scripts/app-init.js b/app/scripts/app-init.js index f5904e653cbe..579f2b96bdca 100644 --- a/app/scripts/app-init.js +++ b/app/scripts/app-init.js @@ -57,27 +57,27 @@ function importAllScripts() { throw new Error('Missing APPLY_LAVAMOAT environment variable'); } - loadFile('./scripts/sentry-install.js'); + loadFile('./sentry-install.js'); // eslint-disable-next-line no-undef const isWorker = !self.document; if (!isWorker) { - loadFile('./scripts/snow.js'); + loadFile('./snow.js'); } - loadFile('./scripts/use-snow.js'); + loadFile('./use-snow.js'); // Always apply LavaMoat in e2e test builds, so that we can capture initialization stats if (testMode || applyLavaMoat) { - loadFile('./scripts/runtime-lavamoat.js'); - loadFile('./scripts/lockdown-more.js'); - loadFile('./scripts/policy-load.js'); + loadFile('./runtime-lavamoat.js'); + loadFile('./lockdown-more.js'); + loadFile('./policy-load.js'); } else { - loadFile('./scripts/init-globals.js'); - loadFile('./scripts/lockdown-install.js'); - loadFile('./scripts/lockdown-run.js'); - loadFile('./scripts/lockdown-more.js'); - loadFile('./scripts/runtime-cjs.js'); + loadFile('./init-globals.js'); + loadFile('./lockdown-install.js'); + loadFile('./lockdown-run.js'); + loadFile('./lockdown-more.js'); + loadFile('./runtime-cjs.js'); } // This environment variable is set to a string of comma-separated relative file paths. @@ -145,7 +145,7 @@ const registerInPageContentScript = async () => { { id: 'inpage', matches: ['file://*/*', 'http://*/*', 'https://*/*'], - js: ['scripts/inpage.js'], + js: ['inpage.js'], runAt: 'document_start', world: 'MAIN', }, diff --git a/app/scripts/constants/contracts.ts b/app/scripts/constants/contracts.ts index bc27be31d95c..27fa0606dfba 100644 --- a/app/scripts/constants/contracts.ts +++ b/app/scripts/constants/contracts.ts @@ -12,9 +12,4 @@ export const SINGLE_CALL_BALANCES_ADDRESSES = { [CHAIN_IDS.FANTOM]: '0x07f697424ABe762bB808c109860c04eA488ff92B', [CHAIN_IDS.ARBITRUM]: '0x151E24A486D7258dd7C33Fb67E4bB01919B7B32c', [CHAIN_IDS.BLAST]: '0xfd5730e96f9dffae40d99b77015bd42816280998', - [CHAIN_IDS.LINEA_GOERLI]: '0x10dAd7Ca3921471f616db788D9300DC97Db01783', - [CHAIN_IDS.LINEA_MAINNET]: '0xF62e6a41561b3650a69Bb03199C735e3E3328c0D', - [CHAIN_IDS.AURORA]: '0x1286415D333855237f89Df27D388127181448538', - [CHAIN_IDS.BASE]: '0x6AA75276052D96696134252587894ef5FFA520af', - [CHAIN_IDS.ZKSYNC_ERA]: '0x458fEd3144680a5b8bcfaa0F9594aa19B4Ea2D34', }; diff --git a/app/scripts/controllers/app-state.js b/app/scripts/controllers/app-state.js index 92ebef3cebfa..44d1a367362e 100644 --- a/app/scripts/controllers/app-state.js +++ b/app/scripts/controllers/app-state.js @@ -67,10 +67,6 @@ export default class AppStateController extends EventEmitter { }, surveyLinkLastClickedOrClosed: null, signatureSecurityAlertResponses: {}, - // States used for displaying the changed network toast - switchedNetworkDetails: null, - switchedNetworkNeverShowMessage: false, - currentExtensionPopupId: 0, }); this.timer = null; @@ -405,45 +401,6 @@ export default class AppStateController extends EventEmitter { this.store.updateState({ showAccountBanner }); } - /** - * Sets a unique ID for the current extension popup - * - * @param currentExtensionPopupId - */ - setCurrentExtensionPopupId(currentExtensionPopupId) { - this.store.updateState({ currentExtensionPopupId }); - } - - /** - * Sets an object with networkName and appName - * or `null` if the message is meant to be cleared - * - * @param {{ origin: string, networkClientId: string } | null} switchedNetworkDetails - Details about the network that MetaMask just switched to. - */ - setSwitchedNetworkDetails(switchedNetworkDetails) { - this.store.updateState({ switchedNetworkDetails }); - } - - /** - * Clears the switched network details in state - */ - clearSwitchedNetworkDetails() { - this.store.updateState({ switchedNetworkDetails: null }); - } - - /** - * Remembers if the user prefers to never see the - * network switched message again - * - * @param {boolean} switchedNetworkNeverShowMessage - */ - setSwitchedNetworkNeverShowMessage(switchedNetworkNeverShowMessage) { - this.store.updateState({ - switchedNetworkDetails: null, - switchedNetworkNeverShowMessage, - }); - } - /** * Sets a property indicating the model of the user's Trezor hardware wallet * diff --git a/app/scripts/controllers/authentication/auth-snap-requests.ts b/app/scripts/controllers/authentication/auth-snap-requests.ts deleted file mode 100644 index 81c8beaafd40..000000000000 --- a/app/scripts/controllers/authentication/auth-snap-requests.ts +++ /dev/null @@ -1,32 +0,0 @@ -import type { SnapId } from '@metamask/snaps-sdk'; -import type { HandleSnapRequest } from '@metamask/snaps-controllers'; -import { HandlerType } from '@metamask/snaps-utils'; - -type SnapRPCRequest = Parameters[0]; - -const snapId = 'npm:@metamask/message-signing-snap' as SnapId; - -export function createSnapPublicKeyRequest(): SnapRPCRequest { - return { - snapId, - origin: '', - handler: HandlerType.OnRpcRequest, - request: { - method: 'getPublicKey', - }, - }; -} - -export function createSnapSignMessageRequest( - message: `metamask:${string}`, -): SnapRPCRequest { - return { - snapId, - origin: '', - handler: HandlerType.OnRpcRequest, - request: { - method: 'signMessage', - params: { message }, - }, - }; -} diff --git a/app/scripts/controllers/authentication/authentication-controller.test.ts b/app/scripts/controllers/authentication/authentication-controller.test.ts deleted file mode 100644 index 52cac9329a04..000000000000 --- a/app/scripts/controllers/authentication/authentication-controller.test.ts +++ /dev/null @@ -1,291 +0,0 @@ -import { ControllerMessenger } from '@metamask/base-controller'; -import type { HandleSnapRequest } from '@metamask/snaps-controllers'; -import AuthenticationController, { - AuthenticationControllerMessenger, - AuthenticationControllerState, -} from './authentication-controller'; -import { - MOCK_ACCESS_TOKEN, - MOCK_LOGIN_RESPONSE, - mockEndpointAccessToken, - mockEndpointGetNonce, - mockEndpointLogin, -} from './mocks/mockServices'; - -const mockSignedInState = (): AuthenticationControllerState => ({ - isSignedIn: true, - sessionData: { - accessToken: 'MOCK_ACCESS_TOKEN', - expiresIn: new Date().toString(), - profile: { - identifierId: MOCK_LOGIN_RESPONSE.profile.identifier_id, - profileId: MOCK_LOGIN_RESPONSE.profile.profile_id, - metametricsId: MOCK_LOGIN_RESPONSE.profile.metametrics_id, - }, - }, -}); - -describe('authentication/authentication-controller - constructor() tests', () => { - test('should initialize with default state', () => { - const controller = new AuthenticationController({ - messenger: createAuthenticationMessenger(), - }); - - expect(controller.state.isSignedIn).toBe(false); - expect(controller.state.sessionData).toBeUndefined(); - }); - - test('should initialize with override state', () => { - const controller = new AuthenticationController({ - messenger: createAuthenticationMessenger(), - state: mockSignedInState(), - }); - - expect(controller.state.isSignedIn).toBe(true); - expect(controller.state.sessionData).toBeDefined(); - }); -}); - -describe('authentication/authentication-controller - performSignIn() tests', () => { - test('Should create access token and update state', async () => { - const mockEndpoints = mockAuthenticationFlowEndpoints(); - const { messenger, mockSnapGetPublicKey, mockSnapSignMessage } = - createMockAuthenticationMessenger(); - - const controller = new AuthenticationController({ messenger }); - - const result = await controller.performSignIn(); - expect(mockSnapGetPublicKey).toBeCalled(); - expect(mockSnapSignMessage).toBeCalled(); - mockEndpoints.mockGetNonceEndpoint.done(); - mockEndpoints.mockLoginEndpoint.done(); - mockEndpoints.mockAccessTokenEndpoint.done(); - expect(result).toBe(MOCK_ACCESS_TOKEN); - - // Assert - state shows user is logged in - expect(controller.state.isSignedIn).toBe(true); - expect(controller.state.sessionData).toBeDefined(); - }); - - test('Should error when nonce endpoint fails', async () => { - await testAndAssertFailingEndpoints('nonce'); - }); - - test('Should error when login endpoint fails', async () => { - await testAndAssertFailingEndpoints('login'); - }); - - test('Should error when tokens endpoint fails', async () => { - await testAndAssertFailingEndpoints('token'); - }); - - async function testAndAssertFailingEndpoints( - endpointFail: 'nonce' | 'login' | 'token', - ) { - const mockEndpoints = mockAuthenticationFlowEndpoints({ - endpointFail, - }); - const { messenger } = createMockAuthenticationMessenger(); - const controller = new AuthenticationController({ messenger }); - - await expect(controller.performSignIn()).rejects.toThrow(); - expect(controller.state.isSignedIn).toBe(false); - - const endpointsCalled = [ - mockEndpoints.mockGetNonceEndpoint.isDone(), - mockEndpoints.mockLoginEndpoint.isDone(), - mockEndpoints.mockAccessTokenEndpoint.isDone(), - ]; - if (endpointFail === 'nonce') { - expect(endpointsCalled).toEqual([true, false, false]); - } - - if (endpointFail === 'login') { - expect(endpointsCalled).toEqual([true, true, false]); - } - - if (endpointFail === 'token') { - expect(endpointsCalled).toEqual([true, true, true]); - } - } -}); - -describe('authentication/authentication-controller - performSignOut() tests', () => { - test('Should remove signed in user and any access tokens', () => { - const { messenger } = createMockAuthenticationMessenger(); - const controller = new AuthenticationController({ - messenger, - state: mockSignedInState(), - }); - - controller.performSignOut(); - expect(controller.state.isSignedIn).toBe(false); - expect(controller.state.sessionData).toBeUndefined(); - }); - - test('Should throw error if attempting to sign out when user is not logged in', () => { - const { messenger } = createMockAuthenticationMessenger(); - const controller = new AuthenticationController({ - messenger, - state: { isSignedIn: false }, - }); - - expect(() => controller.performSignOut()).toThrow(); - }); -}); - -describe('authentication/authentication-controller - getBearerToken() tests', () => { - test('Should throw error if not logged in', async () => { - const { messenger } = createMockAuthenticationMessenger(); - const controller = new AuthenticationController({ - messenger, - state: { isSignedIn: false }, - }); - - await expect(controller.getBearerToken()).rejects.toThrow(); - }); - - test('Should return original access token in state', async () => { - const { messenger } = createMockAuthenticationMessenger(); - const originalState = mockSignedInState(); - const controller = new AuthenticationController({ - messenger, - state: originalState, - }); - - const result = await controller.getBearerToken(); - expect(result).toBeDefined(); - expect(result).toBe(originalState.sessionData?.accessToken); - }); - - test('Should return new access token if state is invalid', async () => { - const { messenger } = createMockAuthenticationMessenger(); - mockAuthenticationFlowEndpoints(); - const originalState = mockSignedInState(); - if (originalState.sessionData) { - originalState.sessionData.accessToken = 'ACCESS_TOKEN_1'; - - const d = new Date(); - d.setMinutes(d.getMinutes() - 31); // expires at 30 mins - originalState.sessionData.expiresIn = d.toString(); - } - - const controller = new AuthenticationController({ - messenger, - state: originalState, - }); - - const result = await controller.getBearerToken(); - expect(result).toBeDefined(); - expect(result).toBe(MOCK_ACCESS_TOKEN); - }); -}); - -describe('authentication/authentication-controller - getSessionProfile() tests', () => { - test('Should throw error if not logged in', async () => { - const { messenger } = createMockAuthenticationMessenger(); - const controller = new AuthenticationController({ - messenger, - state: { isSignedIn: false }, - }); - - await expect(controller.getSessionProfile()).rejects.toThrow(); - }); - - test('Should return original access token in state', async () => { - const { messenger } = createMockAuthenticationMessenger(); - const originalState = mockSignedInState(); - const controller = new AuthenticationController({ - messenger, - state: originalState, - }); - - const result = await controller.getSessionProfile(); - expect(result).toBeDefined(); - expect(result).toEqual(originalState.sessionData?.profile); - }); - - test('Should return new access token if state is invalid', async () => { - const { messenger } = createMockAuthenticationMessenger(); - mockAuthenticationFlowEndpoints(); - const originalState = mockSignedInState(); - if (originalState.sessionData) { - originalState.sessionData.profile.identifierId = 'ID_1'; - - const d = new Date(); - d.setMinutes(d.getMinutes() - 31); // expires at 30 mins - originalState.sessionData.expiresIn = d.toString(); - } - - const controller = new AuthenticationController({ - messenger, - state: originalState, - }); - - const result = await controller.getSessionProfile(); - expect(result).toBeDefined(); - expect(result.identifierId).toBe(MOCK_LOGIN_RESPONSE.profile.identifier_id); - expect(result.profileId).toBe(MOCK_LOGIN_RESPONSE.profile.profile_id); - expect(result.metametricsId).toBe( - MOCK_LOGIN_RESPONSE.profile.metametrics_id, - ); - }); -}); - -function createAuthenticationMessenger() { - const messenger = new ControllerMessenger(); - return messenger.getRestricted({ - name: 'AuthenticationController', - allowedActions: [`SnapController:handleRequest`], - }) as AuthenticationControllerMessenger; -} - -function createMockAuthenticationMessenger() { - const messenger = createAuthenticationMessenger(); - const mockCall = jest.spyOn(messenger, 'call'); - const mockSnapGetPublicKey = jest.fn().mockResolvedValue('MOCK_PUBLIC_KEY'); - const mockSnapSignMessage = jest - .fn() - .mockResolvedValue('MOCK_SIGNED_MESSAGE'); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - mockCall.mockImplementation(((actionType: any, params: any) => { - if ( - actionType === 'SnapController:handleRequest' && - params?.request.method === 'getPublicKey' - ) { - return mockSnapGetPublicKey(); - } - - if ( - actionType === 'SnapController:handleRequest' && - params?.request.method === 'signMessage' - ) { - return mockSnapSignMessage(); - } - - return ''; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - }) as any); - - return { messenger, mockSnapGetPublicKey, mockSnapSignMessage }; -} - -function mockAuthenticationFlowEndpoints(params?: { - endpointFail: 'nonce' | 'login' | 'token'; -}) { - const mockGetNonceEndpoint = mockEndpointGetNonce( - params?.endpointFail === 'nonce' ? { status: 500 } : undefined, - ); - const mockLoginEndpoint = mockEndpointLogin( - params?.endpointFail === 'login' ? { status: 500 } : undefined, - ); - const mockAccessTokenEndpoint = mockEndpointAccessToken( - params?.endpointFail === 'token' ? { status: 500 } : undefined, - ); - - return { - mockGetNonceEndpoint, - mockLoginEndpoint, - mockAccessTokenEndpoint, - }; -} diff --git a/app/scripts/controllers/authentication/authentication-controller.ts b/app/scripts/controllers/authentication/authentication-controller.ts deleted file mode 100644 index a8a3bb15c7bf..000000000000 --- a/app/scripts/controllers/authentication/authentication-controller.ts +++ /dev/null @@ -1,262 +0,0 @@ -import { - BaseController, - RestrictedControllerMessenger, - StateMetadata, -} from '@metamask/base-controller'; -import { HandleSnapRequest } from '@metamask/snaps-controllers'; -import { - createSnapPublicKeyRequest, - createSnapSignMessageRequest, -} from './auth-snap-requests'; -import { - createLoginRawMessage, - getAccessToken, - getNonce, - login, -} from './services'; - -const THIRTY_MIN_MS = 1000 * 60 * 30; - -const controllerName = 'AuthenticationController'; - -// State -type SessionProfile = { - identifierId: string; - profileId: string; - metametricsId: string; -}; - -type SessionData = { - /** profile - anonymous profile data for the given logged in user */ - profile: SessionProfile; - /** accessToken - used to make requests authorized endpoints */ - accessToken: string; - /** expiresIn - string date to determine if new access token is required */ - expiresIn: string; -}; - -export type AuthenticationControllerState = { - /** - * Global isSignedIn state. - * Can be used to determine if "Profile Syncing" is enabled. - */ - isSignedIn: boolean; - sessionData?: SessionData; -}; -const defaultState: AuthenticationControllerState = { isSignedIn: false }; -const metadata: StateMetadata = { - isSignedIn: { - persist: true, - anonymous: true, - }, - sessionData: { - persist: true, - anonymous: false, - }, -}; - -// Messenger Actions -type CreateActionsObj = { - [K in T]: { - type: `${typeof controllerName}:${K}`; - handler: AuthenticationController[K]; - }; -}; -type ActionsObj = CreateActionsObj< - 'performSignIn' | 'performSignOut' | 'getBearerToken' | 'getSessionProfile' ->; -export type Actions = ActionsObj[keyof ActionsObj]; -export type AuthenticationControllerPerformSignIn = ActionsObj['performSignIn']; -export type AuthenticationControllerPerformSignOut = - ActionsObj['performSignOut']; -export type AuthenticationControllerGetBearerToken = - ActionsObj['getBearerToken']; -export type AuthenticationControllerGetSessionProfile = - ActionsObj['getSessionProfile']; - -// Allowed Actions -type AllowedActions = HandleSnapRequest; - -// Messenger -export type AuthenticationControllerMessenger = RestrictedControllerMessenger< - typeof controllerName, - Actions | AllowedActions, - never, - AllowedActions['type'], - never ->; - -/** - * Controller that enables authentication for restricted endpoints. - * Used for Global Profile Syncing and Notifications - */ -export default class AuthenticationController extends BaseController< - typeof controllerName, - AuthenticationControllerState, - AuthenticationControllerMessenger -> { - constructor({ - messenger, - state, - }: { - messenger: AuthenticationControllerMessenger; - state?: AuthenticationControllerState; - }) { - super({ - messenger, - metadata, - name: controllerName, - state: { ...defaultState, ...state }, - }); - } - - public async performSignIn(): Promise { - const { accessToken } = await this.#performAuthenticationFlow(); - return accessToken; - } - - public performSignOut(): void { - this.#assertLoggedIn(); - - this.update((state) => { - state.isSignedIn = false; - state.sessionData = undefined; - }); - } - - public async getBearerToken(): Promise { - this.#assertLoggedIn(); - - if (this.#hasValidSession(this.state.sessionData)) { - return this.state.sessionData.accessToken; - } - - const { accessToken } = await this.#performAuthenticationFlow(); - return accessToken; - } - - /** - * Will return a session profile. - * Throws if a user is not logged in. - * - * @returns profile for the session. - */ - public async getSessionProfile(): Promise { - this.#assertLoggedIn(); - - if (this.#hasValidSession(this.state.sessionData)) { - return this.state.sessionData.profile; - } - - const { profile } = await this.#performAuthenticationFlow(); - return profile; - } - - #assertLoggedIn(): void { - if (!this.state.isSignedIn) { - throw new Error( - `${controllerName}: Unable to call method, user is not authenticated`, - ); - } - } - - async #performAuthenticationFlow(): Promise<{ - profile: SessionProfile; - accessToken: string; - }> { - try { - // 1. Nonce - const publicKey = await this.#snapGetPublicKey(); - const nonce = await getNonce(publicKey); - if (!nonce) { - throw new Error(`Unable to get nonce`); - } - - // 2. Login - const rawMessage = createLoginRawMessage(nonce, publicKey); - const signature = await this.#snapSignMessage(rawMessage); - const loginResponse = await login(rawMessage, signature); - if (!loginResponse?.token) { - throw new Error(`Unable to login`); - } - - const profile: SessionProfile = { - identifierId: loginResponse.profile.identifier_id, - profileId: loginResponse.profile.profile_id, - metametricsId: loginResponse.profile.metametrics_id, - }; - - // 3. Trade for Access Token - const accessToken = await getAccessToken(loginResponse.token); - if (!accessToken) { - throw new Error(`Unable to get Access Token`); - } - - // Update Internal State - this.update((state) => { - state.isSignedIn = true; - const expiresIn = new Date(); - expiresIn.setTime(expiresIn.getTime() + THIRTY_MIN_MS); - state.sessionData = { - profile, - accessToken, - expiresIn: expiresIn.toString(), - }; - }); - - return { - profile, - accessToken, - }; - } catch (e) { - const errorMessage = - e instanceof Error ? e.message : JSON.stringify(e ?? ''); - throw new Error( - `${controllerName}: Failed to authenticate - ${errorMessage}`, - ); - } - } - - #hasValidSession( - sessionData: SessionData | undefined, - ): sessionData is SessionData { - if (!sessionData) { - return false; - } - - const prevDate = Date.parse(sessionData.expiresIn); - if (isNaN(prevDate)) { - return false; - } - - const currentDate = new Date(); - const diffMs = Math.abs(currentDate.getTime() - prevDate); - - return THIRTY_MIN_MS > diffMs; - } - - /** - * Returns the auth snap public key. - * - * @returns The snap public key. - */ - #snapGetPublicKey(): Promise { - return this.messagingSystem.call( - 'SnapController:handleRequest', - createSnapPublicKeyRequest(), - ) as Promise; - } - - /** - * Signs a specific message using an underlying auth snap. - * - * @param message - A specific tagged message to sign. - * @returns A Signature created by the snap. - */ - #snapSignMessage(message: `metamask:${string}`): Promise { - return this.messagingSystem.call( - 'SnapController:handleRequest', - createSnapSignMessageRequest(message), - ) as Promise; - } -} diff --git a/app/scripts/controllers/authentication/mocks/mockServices.ts b/app/scripts/controllers/authentication/mocks/mockServices.ts deleted file mode 100644 index 5db1690517dd..000000000000 --- a/app/scripts/controllers/authentication/mocks/mockServices.ts +++ /dev/null @@ -1,62 +0,0 @@ -import nock from 'nock'; -import { - AUTH_LOGIN_ENDPOINT, - AUTH_NONCE_ENDPOINT, - LoginResponse, - NonceResponse, - OAuthTokenResponse, - OIDC_TOKENS_ENDPOINT, -} from '../services'; - -type MockReply = { - status: nock.StatusCode; - body?: nock.Body; -}; - -export const MOCK_NONCE = '4cbfqzoQpcNxVImGv'; -const MOCK_NONCE_RESPONSE: NonceResponse = { - nonce: MOCK_NONCE, -}; -export function mockEndpointGetNonce(mockReply?: MockReply) { - const reply = mockReply ?? { status: 200, body: MOCK_NONCE_RESPONSE }; - const mockNonceEndpoint = nock(AUTH_NONCE_ENDPOINT) - .get('') - .query(true) - .reply(reply.status, reply.body); - - return mockNonceEndpoint; -} - -export const MOCK_JWT = - 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c'; -export const MOCK_LOGIN_RESPONSE: LoginResponse = { - token: MOCK_JWT, - expires_in: new Date().toString(), - profile: { - identifier_id: 'MOCK_IDENTIFIER', - profile_id: 'MOCK_PROFILE_ID', - metametrics_id: 'MOCK_METAMETRICS_ID', - }, -}; -export function mockEndpointLogin(mockReply?: MockReply) { - const reply = mockReply ?? { status: 200, body: MOCK_LOGIN_RESPONSE }; - const mockLoginEndpoint = nock(AUTH_LOGIN_ENDPOINT) - .post('') - .reply(reply.status, reply.body); - - return mockLoginEndpoint; -} - -export const MOCK_ACCESS_TOKEN = `MOCK_ACCESS_TOKEN-${MOCK_JWT}`; -const MOCK_OATH_TOKEN_RESPONSE: OAuthTokenResponse = { - access_token: MOCK_ACCESS_TOKEN, - expires_in: new Date().getTime(), -}; -export function mockEndpointAccessToken(mockReply?: MockReply) { - const reply = mockReply ?? { status: 200, body: MOCK_OATH_TOKEN_RESPONSE }; - const mockOidcTokensEndpoint = nock(OIDC_TOKENS_ENDPOINT) - .post('') - .reply(reply.status, reply.body); - - return mockOidcTokensEndpoint; -} diff --git a/app/scripts/controllers/authentication/services.test.ts b/app/scripts/controllers/authentication/services.test.ts deleted file mode 100644 index 4806eb4159a1..000000000000 --- a/app/scripts/controllers/authentication/services.test.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { - MOCK_ACCESS_TOKEN, - MOCK_JWT, - MOCK_NONCE, - mockEndpointAccessToken, - mockEndpointGetNonce, - mockEndpointLogin, -} from './mocks/mockServices'; -import { - createLoginRawMessage, - getAccessToken, - getNonce, - login, -} from './services'; - -describe('authentication/services.ts - getNonce() tests', () => { - test('returns nonce on valid request', async () => { - const mockNonceEndpoint = mockEndpointGetNonce(); - const response = await getNonce('MOCK_PUBLIC_KEY'); - - mockNonceEndpoint.done(); - expect(response).toBe(MOCK_NONCE); - }); - - test('returns null if request is invalid', async () => { - async function testInvalidResponse( - status: number, - body: Record, - ) { - const mockNonceEndpoint = mockEndpointGetNonce({ status, body }); - const response = await getNonce('MOCK_PUBLIC_KEY'); - - mockNonceEndpoint.done(); - expect(response).toBe(null); - } - - await testInvalidResponse(500, { error: 'mock server error' }); - await testInvalidResponse(400, { error: 'mock bad request' }); - }); -}); - -describe('authentication/services.ts - login() tests', () => { - test('returns single-use jwt if successful login', async () => { - const mockLoginEndpoint = mockEndpointLogin(); - const response = await login('mock raw message', 'mock signature'); - - mockLoginEndpoint.done(); - expect(response?.token).toBe(MOCK_JWT); - expect(response?.profile).toBeDefined(); - }); - - test('returns null if request is invalid', async () => { - async function testInvalidResponse( - status: number, - body: Record, - ) { - const mockLoginEndpoint = mockEndpointLogin({ status, body }); - const response = await login('mock raw message', 'mock signature'); - - mockLoginEndpoint.done(); - expect(response).toBe(null); - } - - await testInvalidResponse(500, { error: 'mock server error' }); - await testInvalidResponse(400, { error: 'mock bad request' }); - }); -}); - -describe('authentication/services.ts - getAccessToken() tests', () => { - test('returns access token jwt if successful OIDC token request', async () => { - const mockLoginEndpoint = mockEndpointAccessToken(); - const response = await getAccessToken('mock single-use jwt'); - - mockLoginEndpoint.done(); - expect(response).toBe(MOCK_ACCESS_TOKEN); - }); - - test('returns null if request is invalid', async () => { - async function testInvalidResponse( - status: number, - body: Record, - ) { - const mockLoginEndpoint = mockEndpointAccessToken({ status, body }); - const response = await getAccessToken('mock single-use jwt'); - - mockLoginEndpoint.done(); - expect(response).toBe(null); - } - - await testInvalidResponse(500, { error: 'mock server error' }); - await testInvalidResponse(400, { error: 'mock bad request' }); - }); -}); - -describe('authentication/services.ts - createLoginRawMessage() tests', () => { - test('creates the raw message format for login request', () => { - const message = createLoginRawMessage('NONCE', 'PUBLIC_KEY'); - expect(message).toBe('metamask:NONCE:PUBLIC_KEY'); - }); -}); diff --git a/app/scripts/controllers/authentication/services.ts b/app/scripts/controllers/authentication/services.ts deleted file mode 100644 index 345302e905cd..000000000000 --- a/app/scripts/controllers/authentication/services.ts +++ /dev/null @@ -1,116 +0,0 @@ -const AUTH_ENDPOINT = process.env.AUTH_API || ''; -export const AUTH_NONCE_ENDPOINT = `${AUTH_ENDPOINT}/api/v2/nonce`; -export const AUTH_LOGIN_ENDPOINT = `${AUTH_ENDPOINT}/api/v2/srp/login`; - -const OIDC_ENDPOINT = process.env.OIDC_API || ''; -export const OIDC_TOKENS_ENDPOINT = `${OIDC_ENDPOINT}/oauth2/token`; -const OIDC_CLIENT_ID = process.env.OIDC_CLIENT_ID || ''; -const OIDC_GRANT_TYPE = process.env.OIDC_GRANT_TYPE || ''; - -export type NonceResponse = { - nonce: string; -}; -export async function getNonce(publicKey: string): Promise { - const nonceUrl = new URL(AUTH_NONCE_ENDPOINT); - nonceUrl.searchParams.set('identifier', publicKey); - - try { - const nonceResponse = await fetch(nonceUrl.toString()); - if (!nonceResponse.ok) { - return null; - } - - const nonceJson: NonceResponse = await nonceResponse.json(); - return nonceJson?.nonce ?? null; - } catch (e) { - console.error('authentication-controller/services: unable to get nonce', e); - return null; - } -} - -export type LoginResponse = { - token: string; - expires_in: string; - /** - * Contains anonymous information about the logged in profile. - * - * @property identifier_id - a deterministic unique identifier on the method used to sign in - * @property profile_id - a unique id for a given profile - * @property metametrics_id - an anonymous server id - */ - profile: { - identifier_id: string; - profile_id: string; - metametrics_id: string; - }; -}; -export async function login( - rawMessage: string, - signature: string, -): Promise { - try { - const response = await fetch(AUTH_LOGIN_ENDPOINT, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - signature, - raw_message: rawMessage, - }), - }); - - if (!response.ok) { - return null; - } - - const loginResponse: LoginResponse = await response.json(); - return loginResponse ?? null; - } catch (e) { - console.error('authentication-controller/services: unable to login', e); - return null; - } -} - -export type OAuthTokenResponse = { - access_token: string; - expires_in: number; -}; -export async function getAccessToken(jwtToken: string): Promise { - const headers = new Headers({ - 'Content-Type': 'application/x-www-form-urlencoded', - }); - - const urlEncodedBody = new URLSearchParams(); - urlEncodedBody.append('grant_type', OIDC_GRANT_TYPE); - urlEncodedBody.append('client_id', OIDC_CLIENT_ID); - urlEncodedBody.append('assertion', jwtToken); - - try { - const response = await fetch(OIDC_TOKENS_ENDPOINT, { - method: 'POST', - headers, - body: urlEncodedBody.toString(), - }); - - if (!response.ok) { - return null; - } - - const accessTokenResponse: OAuthTokenResponse = await response.json(); - return accessTokenResponse?.access_token ?? null; - } catch (e) { - console.error( - 'authentication-controller/services: unable to get access token', - e, - ); - return null; - } -} - -export function createLoginRawMessage( - nonce: string, - publicKey: string, -): `metamask:${string}:${string}` { - return `metamask:${nonce}:${publicKey}` as const; -} diff --git a/app/scripts/controllers/decrypt-message.test.ts b/app/scripts/controllers/decrypt-message.test.ts index 848fe3f9986d..a1cb0a58f037 100644 --- a/app/scripts/controllers/decrypt-message.test.ts +++ b/app/scripts/controllers/decrypt-message.test.ts @@ -38,8 +38,6 @@ const createMessengerMock = () => registerInitialEventPayload: jest.fn(), publish: jest.fn(), call: jest.fn(), - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any } as any as jest.Mocked); const createDecryptMessageManagerMock = () => @@ -59,8 +57,6 @@ const createDecryptMessageManagerMock = () => hub: { on: jest.fn(), }, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any } as any as jest.Mocked); describe('DecryptMessageController', () => { @@ -85,8 +81,6 @@ describe('DecryptMessageController', () => { const mockMessengerAction = ( action: string, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any callback: (actionName: string, ...args: any[]) => any, ) => { messengerMock.call.mockImplementation((actionName, ...rest) => { @@ -106,17 +100,9 @@ describe('DecryptMessageController', () => { ); decryptMessageController = new MockDecryptMessageController({ - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any getState: getStateMock as any, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any keyringController: keyringControllerMock as any, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any messenger: messengerMock as any, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any metricsEvent: metricsEventMock as any, } as DecryptMessageControllerOptions); }); @@ -130,8 +116,6 @@ describe('DecryptMessageController', () => { decryptMessageController.update(() => ({ unapprovedDecryptMsgs: { [messageIdMock]: messageMock, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any } as any, unapprovedDecryptMsgCount: 1, })); @@ -147,8 +131,6 @@ describe('DecryptMessageController', () => { it('should add unapproved messages', async () => { await decryptMessageController.newRequestDecryptMessage( messageMock, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any undefined as any, ); @@ -238,8 +220,6 @@ describe('DecryptMessageController', () => { const messageToDecrypt = { ...messageMock, data: messageDataMock, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any } as any; decryptMessageManagerMock.getMessage.mockReturnValue(messageToDecrypt); mockMessengerAction( @@ -291,8 +271,6 @@ describe('DecryptMessageController', () => { it('should be able to reject all unapproved messages', async () => { decryptMessageManagerMock.getUnapprovedMessages.mockReturnValue({ [messageIdMock]: messageMock, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any } as any); await decryptMessageController.rejectUnapproved('reason to cancel'); diff --git a/app/scripts/controllers/decrypt-message.ts b/app/scripts/controllers/decrypt-message.ts index 51bf9c4b1250..5dbb4d8b43c6 100644 --- a/app/scripts/controllers/decrypt-message.ts +++ b/app/scripts/controllers/decrypt-message.ts @@ -117,12 +117,8 @@ export type DecryptMessageControllerMessenger = RestrictedControllerMessenger< >; export type DecryptMessageControllerOptions = { - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any getState: () => any; messenger: DecryptMessageControllerMessenger; - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any metricsEvent: (payload: any, options?: any) => void; }; @@ -136,12 +132,8 @@ export default class DecryptMessageController extends BaseController< > { hub: EventEmitter; - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any private _getState: () => any; - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any private _metricsEvent: (payload: any, options?: any) => void; private _decryptMessageManager: DecryptMessageManager; @@ -371,8 +363,6 @@ export default class DecryptMessageController extends BaseController< ) { messageManager.subscribe((state: MessageManagerState) => { const newMessages = this._migrateMessages( - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any state.unapprovedMessages as any, ); this.update((draftState) => { diff --git a/app/scripts/controllers/encryption-public-key.test.ts b/app/scripts/controllers/encryption-public-key.test.ts index 274f0c67d7e9..c36418abffb1 100644 --- a/app/scripts/controllers/encryption-public-key.test.ts +++ b/app/scripts/controllers/encryption-public-key.test.ts @@ -34,8 +34,6 @@ const messageMock = { status: 'unapproved', type: 'testType', rawSig: undefined, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any } as any as AbstractMessage; const coreMessageMock = { @@ -58,8 +56,6 @@ const createMessengerMock = () => registerActionHandler: jest.fn(), publish: jest.fn(), call: jest.fn(), - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any } as any as jest.Mocked); const createEncryptionPublicKeyManagerMock = () => @@ -75,8 +71,6 @@ const createEncryptionPublicKeyManagerMock = () => hub: { on: jest.fn(), }, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any } as any as jest.Mocked); describe('EncryptionPublicKeyController', () => { @@ -102,20 +96,10 @@ describe('EncryptionPublicKeyController', () => { ); encryptionPublicKeyController = new EncryptionPublicKeyController({ - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any messenger: messengerMock as any, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any getEncryptionPublicKey: getEncryptionPublicKeyMock as any, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any getAccountKeyringType: getAccountKeyringTypeMock as any, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any getState: getStateMock as any, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any metricsEvent: metricsEventMock as any, } as EncryptionPublicKeyControllerOptions); }); @@ -136,8 +120,6 @@ describe('EncryptionPublicKeyController', () => { encryptionPublicKeyController.update(() => ({ unapprovedEncryptionPublicKeyMsgs: { [messageIdMock]: messageMock, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any } as any, unapprovedEncryptionPublicKeyMsgCount: 1, })); @@ -158,15 +140,11 @@ describe('EncryptionPublicKeyController', () => { [messageIdMock2]: messageMock, }; encryptionPublicKeyManagerMock.getUnapprovedMessages.mockReturnValueOnce( - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any messages as any, ); // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore encryptionPublicKeyController.update(() => ({ - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any unapprovedEncryptionPublicKeyMsgs: messages as any, })); }); @@ -375,8 +353,6 @@ describe('EncryptionPublicKeyController', () => { const mockListener = jest.fn(); encryptionPublicKeyController.hub.on('updateBadge', mockListener); - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any (encryptionPublicKeyManagerMock.hub.on as any).mock.calls[0][1](); expect(mockListener).toHaveBeenCalledTimes(1); @@ -385,8 +361,6 @@ describe('EncryptionPublicKeyController', () => { it('requires approval on unapproved message event from EncryptionPublicKeyManager', () => { messengerMock.call.mockResolvedValueOnce({}); - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any (encryptionPublicKeyManagerMock.hub.on as any).mock.calls[1][1]( messageParamsMock, ); @@ -405,16 +379,12 @@ describe('EncryptionPublicKeyController', () => { it('updates state on EncryptionPublicKeyManager state change', async () => { await encryptionPublicKeyManagerMock.subscribe.mock.calls[0][0]({ - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any unapprovedMessages: { [messageIdMock]: coreMessageMock as any }, unapprovedMessagesCount: 3, }); expect(encryptionPublicKeyController.state).toEqual({ unapprovedEncryptionPublicKeyMsgs: { - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any [messageIdMock]: stateMessageMock as any, }, unapprovedEncryptionPublicKeyMsgCount: 3, diff --git a/app/scripts/controllers/encryption-public-key.ts b/app/scripts/controllers/encryption-public-key.ts index 2864bab34600..4bb019a10b72 100644 --- a/app/scripts/controllers/encryption-public-key.ts +++ b/app/scripts/controllers/encryption-public-key.ts @@ -87,11 +87,7 @@ export type EncryptionPublicKeyControllerOptions = { messenger: EncryptionPublicKeyControllerMessenger; getEncryptionPublicKey: (address: string) => Promise; getAccountKeyringType: (account: string) => Promise; - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any getState: () => any; - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any metricsEvent: (payload: any, options?: any) => void; }; @@ -109,14 +105,10 @@ export default class EncryptionPublicKeyController extends BaseController< private _getAccountKeyringType: (account: string) => Promise; - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any private _getState: () => any; private _encryptionPublicKeyManager: EncryptionPublicKeyManager; - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any private _metricsEvent: (payload: any, options?: any) => void; /** @@ -360,8 +352,6 @@ export default class EncryptionPublicKeyController extends BaseController< ) { messageManager.subscribe((state: MessageManagerState) => { const newMessages = this._migrateMessages( - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any state.unapprovedMessages as any, ); this.update((draftState) => { diff --git a/app/scripts/controllers/mmi-controller.ts b/app/scripts/controllers/mmi-controller.ts index 4f8b77de2f52..b2d66f36afdf 100644 --- a/app/scripts/controllers/mmi-controller.ts +++ b/app/scripts/controllers/mmi-controller.ts @@ -48,8 +48,6 @@ type UpdateCustodianTransactionsParameters = { txList: string[]; custodyController: CustodyController; transactionUpdateController: TransactionUpdateController; - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any txStateManager: any; getPendingNonce: (address: string) => Promise; setTxHash: (txId: string, txHash: string) => void; @@ -60,8 +58,6 @@ export default class MMIController extends EventEmitter { public mmiConfigurationController: MmiConfigurationController; - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any public keyringController: any; public preferencesController: PreferencesController; @@ -72,12 +68,8 @@ export default class MMIController extends EventEmitter { private custodyController: CustodyController; - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any private getState: () => any; - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any private getPendingNonce: (address: string) => Promise; private accountTracker: AccountTracker; @@ -86,42 +78,28 @@ export default class MMIController extends EventEmitter { private networkController: NetworkController; - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any private permissionController: any; private signatureController: SignatureController; - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any private messenger: any; - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any private platform: any; - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any private extension: any; private updateTransactionHash: (txId: string, txHash: string) => void; public trackTransactionEvents: ( args: { transactionMeta: TransactionMeta }, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any event: any, ) => void; private txStateManager: { - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any getTransactions: (query?: any, opts?: any, fullTx?: boolean) => any[]; setTxStatusSigned: (txId: string) => void; setTxStatusSubmitted: (txId: string) => void; setTxStatusFailed: (txId: string) => void; - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any updateTransaction: (txMeta: any) => void; }; @@ -193,8 +171,6 @@ export default class MMIController extends EventEmitter { async trackTransactionEventFromCustodianEvent( txMeta: TransactionMeta, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any event: any, ) { // transactionMetricsRequest parameter is already bound in the constructor @@ -346,8 +322,6 @@ export default class MMIController extends EventEmitter { string, { name: string; - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any custodianDetails: any; labels: Label[]; token: string; diff --git a/app/scripts/controllers/onboarding.test.ts b/app/scripts/controllers/onboarding.test.ts deleted file mode 100644 index 61b9cf8de589..000000000000 --- a/app/scripts/controllers/onboarding.test.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { FirstTimeFlowType } from '../../../shared/constants/onboarding'; -import OnboardingController, { OnboardingControllerState } from './onboarding'; - -describe('OnboardingController', () => { - let onboardingController: OnboardingController; - - beforeEach(() => { - onboardingController = new OnboardingController({ - initState: { - seedPhraseBackedUp: null, - firstTimeFlowType: null, - completedOnboarding: false, - onboardingTabs: {}, - }, - }); - }); - - it('should set the seedPhraseBackedUp property', () => { - const newSeedPhraseBackUpState = true; - onboardingController.setSeedPhraseBackedUp(newSeedPhraseBackUpState); - const state: OnboardingControllerState = - onboardingController.store.getState(); - expect(state.seedPhraseBackedUp).toBe(newSeedPhraseBackUpState); - }); - - it('should set the firstTimeFlowType property', () => { - const type: FirstTimeFlowType = FirstTimeFlowType.create; - onboardingController.setFirstTimeFlowType(type); - const state: OnboardingControllerState = - onboardingController.store.getState(); - expect(state.firstTimeFlowType).toBe(type); - }); - - it('should register a site for onboarding', async () => { - const location = 'example.com'; - const tabId = '123'; - await onboardingController.registerOnboarding(location, tabId); - const state: OnboardingControllerState = - onboardingController.store.getState(); - expect(state.onboardingTabs?.[location]).toBe(tabId); - }); - - it('should skip update state if the location is already onboard', async () => { - const location = 'example.com'; - const tabId = '123'; - await onboardingController.registerOnboarding(location, tabId); - const state: OnboardingControllerState = - onboardingController.store.getState(); - const updateStateSpy = jest.spyOn( - onboardingController.store, - 'updateState', - ); - - expect(state.onboardingTabs?.[location]).toBe(tabId); - expect(updateStateSpy).not.toHaveBeenCalled(); - }); -}); diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js index 01404fe9f743..7a50baaffb0d 100644 --- a/app/scripts/controllers/preferences.js +++ b/app/scripts/controllers/preferences.js @@ -91,11 +91,9 @@ export default class PreferencesController { showExtensionInFullSizeView: false, showFiatInTestnets: false, showTestNetworks: false, - smartTransactionsOptInStatus: null, // null means we will show the Smart Transactions opt-in modal to a user if they are eligible useNativeCurrencyAsPrimaryCurrency: true, hideZeroBalanceTokens: false, petnamesEnabled: true, - featureNotificationsEnabled: false, }, // ENS decentralized website resolution ipfsGateway: IPFS_DEFAULT_GATEWAY_URL, diff --git a/app/scripts/lib/AbstractPetnamesBridge.test.ts b/app/scripts/lib/AbstractPetnamesBridge.test.ts index 3347c90f13f5..608454b38afe 100644 --- a/app/scripts/lib/AbstractPetnamesBridge.test.ts +++ b/app/scripts/lib/AbstractPetnamesBridge.test.ts @@ -94,8 +94,6 @@ function createNameControllerMock(state: NameControllerState) { function createMessengerMock(): jest.Mocked { return { subscribe: jest.fn(), - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any } as any; } diff --git a/app/scripts/lib/AccountIdentitiesPetnamesBridge.test.ts b/app/scripts/lib/AccountIdentitiesPetnamesBridge.test.ts index b7ac979f3a1c..6431381d7099 100644 --- a/app/scripts/lib/AccountIdentitiesPetnamesBridge.test.ts +++ b/app/scripts/lib/AccountIdentitiesPetnamesBridge.test.ts @@ -105,22 +105,16 @@ function createNameControllerMock( return { state, setName: jest.fn(), - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any } as any; } function simulateSubscribe( messenger: jest.Mocked, stateChange: AccountsControllerState, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any patch: any[], ) { const listener = messenger.subscribe.mock.calls[0][1] as ( stateChange: AccountsControllerState, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any patch: any[], ) => void; listener(stateChange, patch); diff --git a/app/scripts/lib/AddressBookPetnamesBridge.test.ts b/app/scripts/lib/AddressBookPetnamesBridge.test.ts index f95726fba7b2..c98e6a5e309e 100644 --- a/app/scripts/lib/AddressBookPetnamesBridge.test.ts +++ b/app/scripts/lib/AddressBookPetnamesBridge.test.ts @@ -37,16 +37,12 @@ function createNameControllerMock( return { state, setName: jest.fn(), - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any } as any; } function createMessengerMock(): jest.Mocked { return { subscribe: jest.fn(), - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any } as any; } diff --git a/app/scripts/lib/AddressBookPetnamesBridge.ts b/app/scripts/lib/AddressBookPetnamesBridge.ts index 141814a9ef62..e6f7e9813fa1 100644 --- a/app/scripts/lib/AddressBookPetnamesBridge.ts +++ b/app/scripts/lib/AddressBookPetnamesBridge.ts @@ -35,13 +35,9 @@ export class AddressBookPetnamesBridge extends AbstractPetnamesBridge { const entries: PetnameEntry[] = []; const { state } = this.#addressBookController; for (const chainId of Object.keys(state.addressBook)) { - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any const chainEntries = state.addressBook[chainId as any]; for (const address of Object.keys(chainEntries)) { - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any const entry = state.addressBook[chainId as any][address]; const normalizedChainId = chainId.toLowerCase(); const { name, isEns } = entry; @@ -68,17 +64,11 @@ export class AddressBookPetnamesBridge extends AbstractPetnamesBridge { */ protected updateSourceEntry(type: ChangeType, entry: PetnameEntry): void { if (type === ChangeType.DELETED) { - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any this.#addressBookController.delete(entry.variation as any, entry.value); } else { this.#addressBookController.set( entry.value, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any entry.name as any, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any entry.variation as any, ); } diff --git a/app/scripts/lib/SnapsNameProvider.test.ts b/app/scripts/lib/SnapsNameProvider.test.ts index 25a5f22a3f80..6d6f0dd6a120 100644 --- a/app/scripts/lib/SnapsNameProvider.test.ts +++ b/app/scripts/lib/SnapsNameProvider.test.ts @@ -94,8 +94,6 @@ function createMockMessenger({ return { call: callMock, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any } as any; } diff --git a/app/scripts/lib/backup.test.js b/app/scripts/lib/backup.test.js index 850e6a719467..ceab30f28188 100644 --- a/app/scripts/lib/backup.test.js +++ b/app/scripts/lib/backup.test.js @@ -161,7 +161,6 @@ const jsonData = JSON.stringify({ showExtensionInFullSizeView: false, showFiatInTestnets: false, showTestNetworks: true, - smartTransactionsOptInStatus: false, useNativeCurrencyAsPrimaryCurrency: true, }, ipfsGateway: 'dweb.link', diff --git a/app/scripts/lib/encryptor-factory.test.ts b/app/scripts/lib/encryptor-factory.test.ts deleted file mode 100644 index 729162ff2ecb..000000000000 --- a/app/scripts/lib/encryptor-factory.test.ts +++ /dev/null @@ -1,174 +0,0 @@ -import * as browserPassworder from '@metamask/browser-passworder'; -import { encryptorFactory } from './encryptor-factory'; - -jest.mock('@metamask/browser-passworder'); - -const mockIterations = 100; -const mockPassword = 'password'; -const mockData = 'data'; - -describe('encryptorFactory', () => { - afterEach(async () => { - jest.resetAllMocks(); - }); - - const mockBrowserPassworder = browserPassworder as jest.Mocked< - typeof browserPassworder - >; - - it('should return an object with browser passworder methods', () => { - const encryptor = encryptorFactory(mockIterations); - - [ - 'encrypt', - 'encryptWithDetail', - 'encryptWithKey', - 'decrypt', - 'decryptWithDetail', - 'decryptWithKey', - 'keyFromPassword', - 'importKey', - 'isVaultUpdated', - ].forEach((method) => { - expect(encryptor).toHaveProperty(method); - }); - }); - - describe('encrypt', () => { - it('should call browser-passworder.encrypt with the given password, data, and iterations', async () => { - const encryptor = encryptorFactory(mockIterations); - - await encryptor.encrypt(mockPassword, mockData); - - expect(mockBrowserPassworder.encrypt).toHaveBeenCalledWith( - mockPassword, - mockData, - undefined, - undefined, - { - algorithm: 'PBKDF2', - params: { - iterations: mockIterations, - }, - }, - ); - }); - - it('should return the result of browser-passworder.encrypt', async () => { - const encryptor = encryptorFactory(mockIterations); - const mockResult = 'result'; - mockBrowserPassworder.encrypt.mockResolvedValue(mockResult); - - expect(await encryptor.encrypt(mockPassword, mockData)).toBe(mockResult); - }); - }); - - describe('encryptWithDetail', () => { - it('should call browser-passworder.encryptWithDetail with the given password, object, and iterations', async () => { - const encryptor = encryptorFactory(mockIterations); - - await encryptor.encryptWithDetail(mockPassword, { foo: 'bar' }, 'salt'); - - expect(mockBrowserPassworder.encryptWithDetail).toHaveBeenCalledWith( - mockPassword, - { foo: 'bar' }, - 'salt', - { - algorithm: 'PBKDF2', - params: { - iterations: mockIterations, - }, - }, - ); - }); - - it('should return the result of browser-passworder.encryptWithDetail', async () => { - const encryptor = encryptorFactory(mockIterations); - const mockResult = { - vault: 'vault', - exportedKeyString: 'salt', - }; - mockBrowserPassworder.encryptWithDetail.mockResolvedValue(mockResult); - - expect( - await encryptor.encryptWithDetail(mockPassword, { foo: 'bar' }, 'salt'), - ).toBe(mockResult); - }); - }); - - describe('decrypt', () => { - it('should call browser-passworder.decrypt with the given password, data, and iterations', async () => { - const encryptor = encryptorFactory(mockIterations); - - await encryptor.decrypt(mockPassword, mockData); - - expect(mockBrowserPassworder.decrypt).toHaveBeenCalledWith( - mockPassword, - mockData, - ); - }); - - it('should return the result of browser-passworder.decrypt', async () => { - const encryptor = encryptorFactory(mockIterations); - const mockResult = 'result'; - mockBrowserPassworder.decrypt.mockResolvedValue(mockResult); - - expect(await encryptor.decrypt(mockPassword, mockData)).toBe(mockResult); - }); - }); - - describe('decryptWithDetail', () => { - it('should call browser-passworder.decryptWithDetail with the given password and object', async () => { - const encryptor = encryptorFactory(mockIterations); - - await encryptor.decryptWithDetail(mockPassword, mockData); - - expect(mockBrowserPassworder.decryptWithDetail).toHaveBeenCalledWith( - mockPassword, - mockData, - ); - }); - - it('should return the result of browser-passworder.decryptWithDetail', async () => { - const encryptor = encryptorFactory(mockIterations); - const mockResult = { - exportedKeyString: 'key', - vault: 'data', - salt: 'salt', - }; - mockBrowserPassworder.decryptWithDetail.mockResolvedValue(mockResult); - - expect(await encryptor.decryptWithDetail(mockPassword, mockData)).toBe( - mockResult, - ); - }); - }); - - describe('isVaultUpdated', () => { - it('should call browser-passworder.isVaultUpdated with the given vault and iterations', () => { - const encryptor = encryptorFactory(mockIterations); - const mockVault = 'vault'; - - encryptor.isVaultUpdated(mockVault); - - expect(mockBrowserPassworder.isVaultUpdated).toHaveBeenCalledWith( - mockVault, - { - algorithm: 'PBKDF2', - params: { - iterations: mockIterations, - }, - }, - ); - }); - - it('should return the result of browser-passworder.isVaultUpdated', () => { - const encryptor = encryptorFactory(mockIterations); - const mockResult = false; - const mockVault = 'vault'; - mockBrowserPassworder.isVaultUpdated.mockReturnValue(mockResult); - - expect(encryptor.isVaultUpdated(mockVault)).toBe(mockResult); - }); - }); -}); diff --git a/app/scripts/lib/encryptor-factory.ts b/app/scripts/lib/encryptor-factory.ts index b4eac5e0e6eb..68f8ee42307e 100644 --- a/app/scripts/lib/encryptor-factory.ts +++ b/app/scripts/lib/encryptor-factory.ts @@ -57,7 +57,7 @@ const encryptWithDetailFactory = * @param iterations - The number of iterations to use for the PBKDF2 algorithm. * @returns A function that checks if the vault was encrypted with the given number of iterations. */ -const isVaultUpdatedFactory = (iterations: number) => (vault: string) => +const isVaultUpdatedFactory = (iterations: number) => async (vault: string) => isVaultUpdated(vault, { algorithm: 'PBKDF2', params: { diff --git a/app/scripts/lib/keyring-snaps-permissions.test.ts b/app/scripts/lib/keyring-snaps-permissions.test.ts index 3e457df2e258..afe4b40fefa6 100644 --- a/app/scripts/lib/keyring-snaps-permissions.test.ts +++ b/app/scripts/lib/keyring-snaps-permissions.test.ts @@ -15,8 +15,6 @@ describe('keyringSnapPermissionsBuilder', () => { registerActionHandler: jest.fn(), registerInitialEventPayload: jest.fn(), publish: jest.fn(), - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any } as any, state: {}, }); @@ -87,8 +85,6 @@ describe('keyringSnapPermissionsBuilder', () => { ])('"%s" cannot call any methods', (origin) => { const permissions = keyringSnapPermissionsBuilder( mockController, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any origin as any, ); expect(permissions()).toStrictEqual([]); @@ -110,8 +106,6 @@ describe('isProtocolAllowed', () => { [1, false], [0, false], [-1, false], - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any ])('"%s" cannot call any methods', (origin: any, expected: boolean) => { expect(isProtocolAllowed(origin)).toBe(expected); }); diff --git a/app/scripts/lib/offscreen-bridge/lattice-offscreen-keyring.ts b/app/scripts/lib/offscreen-bridge/lattice-offscreen-keyring.ts index 27993e04d03c..c690c1004382 100644 --- a/app/scripts/lib/offscreen-bridge/lattice-offscreen-keyring.ts +++ b/app/scripts/lib/offscreen-bridge/lattice-offscreen-keyring.ts @@ -61,8 +61,6 @@ class LatticeKeyringOffscreen extends LatticeKeyring { }); return creds; - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (err: any) { throw new Error(err); } diff --git a/app/scripts/lib/ppom/indexed-db-backend.ts b/app/scripts/lib/ppom/indexed-db-backend.ts index 8fbf63c1aced..41c5af2629d1 100644 --- a/app/scripts/lib/ppom/indexed-db-backend.ts +++ b/app/scripts/lib/ppom/indexed-db-backend.ts @@ -38,8 +38,6 @@ export class IndexedDBPPOMStorage implements StorageBackend { reject( new Error( `Failed to open database ${this.storeName}: ${ - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any (event.target as any)?.error }`, ), @@ -67,12 +65,8 @@ export class IndexedDBPPOMStorage implements StorageBackend { private async objectStoreAction( method: 'get' | 'delete' | 'put' | 'getAllKeys', - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any args?: any, mode: IDBTransactionMode = 'readonly', - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any ): Promise { return new Promise((resolve, reject) => { this.#getObjectStore(mode) @@ -87,8 +81,6 @@ export class IndexedDBPPOMStorage implements StorageBackend { reject( new Error( `Error in indexDB operation ${method}: ${ - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any (event.target as any)?.error }`, ), @@ -103,8 +95,6 @@ export class IndexedDBPPOMStorage implements StorageBackend { async read(key: StorageKey, checksum: string): Promise { const event = await this.objectStoreAction('get', [key.name, key.chainId]); - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any const data = (event.target as any)?.result?.data; await validateChecksum(key, data, checksum); return data; @@ -129,8 +119,6 @@ export class IndexedDBPPOMStorage implements StorageBackend { async dir(): Promise { const event = await this.objectStoreAction('getAllKeys'); - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any return (event.target as any)?.result.map(([name, chainId]: string[]) => ({ name, chainId, diff --git a/app/scripts/lib/ppom/ppom-middleware.test.ts b/app/scripts/lib/ppom/ppom-middleware.test.ts index 370acc51e944..de3cff83f93a 100644 --- a/app/scripts/lib/ppom/ppom-middleware.test.ts +++ b/app/scripts/lib/ppom/ppom-middleware.test.ts @@ -1,11 +1,4 @@ -import { - type Hex, - JsonRpcRequestStruct, - JsonRpcResponseStruct, -} from '@metamask/utils'; -import { waitFor } from '@testing-library/react'; import { CHAIN_IDS } from '../../../../shared/constants/network'; - import { BlockaidReason, BlockaidResultType, @@ -26,24 +19,10 @@ Object.defineProperty(globalThis, 'performance', { }); const createMiddleWare = ( - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any usePPOM?: any, - options: { - securityAlertsEnabled?: boolean; - chainId?: Hex; - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any - mockUpdateSecurityAlertResponseByTxId?: any; - } = { - mockUpdateSecurityAlertResponseByTxId: () => undefined, - }, + securityAlertsEnabled?: boolean, + chainId?: string, ) => { - const { - securityAlertsEnabled, - chainId, - mockUpdateSecurityAlertResponseByTxId, - } = options; const usePPOMMock = jest.fn(); const ppomController = { usePPOM: usePPOM || usePPOMMock, @@ -64,19 +43,11 @@ const createMiddleWare = ( }; return createPPOMMiddleware( - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any ppomController as any, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any preferenceController as any, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any networkController as any, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any appStateController as any, - mockUpdateSecurityAlertResponseByTxId, + () => undefined, ); }; @@ -93,87 +64,28 @@ describe('PPOMMiddleware', () => { const usePPOMMock = jest.fn(); const middlewareFunction = createMiddleWare(usePPOMMock); await middlewareFunction( - { ...JsonRpcRequestStruct, method: 'eth_sendTransaction' }, - { ...JsonRpcResponseStruct }, + { method: 'eth_sendTransaction' }, + undefined, () => undefined, ); expect(usePPOMMock).toHaveBeenCalledTimes(1); }); - it('adds loading response to confirmation requests while validation is in progress', async () => { + it('should add validation response on confirmation requests', async () => { const usePPOM = async () => Promise.resolve('VALIDATION_RESULT'); const middlewareFunction = createMiddleWare(usePPOM); const req = { - ...JsonRpcRequestStruct, - method: 'eth_sendTransaction', - securityAlertResponse: undefined, - }; - await middlewareFunction( - req, - { ...JsonRpcResponseStruct }, - () => undefined, - ); - - expect(req.securityAlertResponse.reason).toBe(BlockaidResultType.Loading); - expect(req.securityAlertResponse.result_type).toBe( - BlockaidReason.inProgress, - ); - }); - - it('adds validation response to confirmation requests on supported networks', async () => { - const validateMock = jest.fn().mockImplementation(() => - Promise.resolve({ - result_type: BlockaidResultType.Malicious, - reason: BlockaidReason.permitFarming, - }), - ); - - const ppom = { - validateJsonRpc: validateMock, - }; - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const usePPOM = async (callback: any) => { - callback(ppom); - }; - const mockUpdateSecurityAlertResponseByTxId = jest.fn(); - const middlewareFunction = createMiddleWare(usePPOM, { - chainId: '0xa', - mockUpdateSecurityAlertResponseByTxId, - }); - const req = { - ...JsonRpcRequestStruct, method: 'eth_sendTransaction', securityAlertResponse: undefined, }; - await middlewareFunction( - req, - { ...JsonRpcResponseStruct }, - () => undefined, - ); - - await waitFor(() => { - const mockCallSecurityAlertResponse = - mockUpdateSecurityAlertResponseByTxId.mock.calls[0][1]; - - expect(mockCallSecurityAlertResponse.result_type).toBe( - BlockaidResultType.Malicious, - ); - expect(mockCallSecurityAlertResponse.reason).toBe( - BlockaidReason.permitFarming, - ); - expect(mockCallSecurityAlertResponse.securityAlertId).toBeDefined(); - expect(req.securityAlertResponse).toBeDefined(); - }); + await middlewareFunction(req, undefined, () => undefined); + expect(req.securityAlertResponse).toBeDefined(); }); - it('does not do validation if the user has not enabled the preference', async () => { + it('should not do validation if user has not enabled preference', async () => { const usePPOM = async () => Promise.resolve('VALIDATION_RESULT'); - const middlewareFunction = createMiddleWare(usePPOM, { - securityAlertsEnabled: false, - }); + const middlewareFunction = createMiddleWare(usePPOM, false); const req = { - ...JsonRpcRequestStruct, method: 'eth_sendTransaction', securityAlertResponse: undefined, }; @@ -181,124 +93,94 @@ describe('PPOMMiddleware', () => { expect(req.securityAlertResponse).toBeUndefined(); }); - it('does not do validation if user is not on a supported network', async () => { + it('should not do validation if user is not on mainnet', async () => { const usePPOM = async () => Promise.resolve('VALIDATION_RESULT'); - const middlewareFunction = createMiddleWare(usePPOM, { - chainId: '0x2', - }); + const middlewareFunction = createMiddleWare(usePPOM, false, '0x2'); const req = { - ...JsonRpcRequestStruct, method: 'eth_sendTransaction', securityAlertResponse: undefined, }; - await middlewareFunction( - req, - { ...JsonRpcResponseStruct }, - () => undefined, - ); + await middlewareFunction(req, undefined, () => undefined); expect(req.securityAlertResponse).toBeUndefined(); }); - it('sets error types in the response if usePPOM throws an error', async () => { + it('should set error type in response if usePPOM throw error', async () => { const usePPOM = async () => { throw new Error('some error'); }; - const mockUpdateSecurityAlertResponseByTxId = jest.fn(); - const middlewareFunction = createMiddleWare(usePPOM, { - mockUpdateSecurityAlertResponseByTxId, - }); + const middlewareFunction = createMiddleWare({ usePPOM }); const req = { - ...JsonRpcRequestStruct, method: 'eth_sendTransaction', + securityAlertResponse: undefined, }; - await middlewareFunction( - req, - { ...JsonRpcResponseStruct }, - () => undefined, + await middlewareFunction(req, undefined, () => undefined); + expect((req.securityAlertResponse as any)?.result_type).toBe( + BlockaidResultType.Errored, + ); + expect((req.securityAlertResponse as any)?.reason).toBe( + BlockaidReason.errored, ); - - await waitFor(() => { - const mockCallSecurityAlertResponse = - mockUpdateSecurityAlertResponseByTxId.mock.calls[0][1]; - expect(mockCallSecurityAlertResponse.description).toBe( - 'Error: some error', - ); - expect(mockCallSecurityAlertResponse.result_type).toBe( - BlockaidResultType.Errored, - ); - expect(mockCallSecurityAlertResponse.result_type).toBe( - BlockaidReason.errored, - ); - expect(mockCallSecurityAlertResponse.securityAlertId).toBeDefined(); - }); }); - it('calls next method when ppomController.usePPOM completes', async () => { + it('should call next method when ppomController.usePPOM completes', async () => { const ppom = { validateJsonRpc: () => undefined, }; - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any const usePPOM = async (callback: any) => { callback(ppom); }; const middlewareFunction = createMiddleWare(usePPOM); const nextMock = jest.fn(); await middlewareFunction( - { ...JsonRpcRequestStruct, method: 'eth_sendTransaction' }, - { ...JsonRpcResponseStruct }, + { method: 'eth_sendTransaction' }, + undefined, nextMock, ); expect(nextMock).toHaveBeenCalledTimes(1); }); - it('calls next method when ppomController.usePPOM throws error', async () => { - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any + it('should call next method when ppomController.usePPOM throws error', async () => { const usePPOM = async (_callback: any) => { throw Error('Some error'); }; const middlewareFunction = createMiddleWare(usePPOM); const nextMock = jest.fn(); await middlewareFunction( - { ...JsonRpcRequestStruct, method: 'eth_sendTransaction' }, - { ...JsonRpcResponseStruct }, + { method: 'eth_sendTransaction' }, + undefined, nextMock, ); expect(nextMock).toHaveBeenCalledTimes(1); }); - it('calls ppom.validateJsonRpc when invoked', async () => { + + it('should call ppom.validateJsonRpc when invoked', async () => { const validateMock = jest.fn(); const ppom = { validateJsonRpc: validateMock, }; - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any const usePPOM = async (callback: any) => { callback(ppom); }; const middlewareFunction = createMiddleWare(usePPOM); await middlewareFunction( - { ...JsonRpcRequestStruct, method: 'eth_sendTransaction' }, - { ...JsonRpcResponseStruct }, + { method: 'eth_sendTransaction' }, + undefined, () => undefined, ); expect(validateMock).toHaveBeenCalledTimes(1); }); - it('does not call ppom.validateJsonRpc when request is not for confirmation method', async () => { + it('should not call ppom.validateJsonRpc when request is not for confirmation method', async () => { const validateMock = jest.fn(); const ppom = { validateJsonRpc: validateMock, }; - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any const usePPOM = async (callback: any) => { callback(ppom); }; const middlewareFunction = createMiddleWare(usePPOM); await middlewareFunction( - { ...JsonRpcRequestStruct, method: 'eth_someRequest' }, + { method: 'eth_someRequest' }, undefined, () => undefined, ); @@ -307,7 +189,6 @@ describe('PPOMMiddleware', () => { it('normalizes transaction requests before validation', async () => { const requestMock1 = { - ...JsonRpcRequestStruct, method: 'eth_sendTransaction', params: [{ data: '0x1' }], }; @@ -325,19 +206,13 @@ describe('PPOMMiddleware', () => { validateJsonRpc: validateMock, }; - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any const usePPOM = async (callback: any) => { callback(ppom); }; const middlewareFunction = createMiddleWare(usePPOM); - await middlewareFunction( - requestMock1, - { ...JsonRpcResponseStruct }, - () => undefined, - ); + await middlewareFunction(requestMock1, undefined, () => undefined); expect(normalizePPOMRequestMock).toHaveBeenCalledTimes(1); expect(normalizePPOMRequestMock).toHaveBeenCalledWith(requestMock1); diff --git a/app/scripts/lib/ppom/ppom-middleware.ts b/app/scripts/lib/ppom/ppom-middleware.ts index 8992b3a37c07..c30c38fa8622 100644 --- a/app/scripts/lib/ppom/ppom-middleware.ts +++ b/app/scripts/lib/ppom/ppom-middleware.ts @@ -1,13 +1,6 @@ import { PPOM } from '@blockaid/ppom_release'; import { PPOMController } from '@metamask/ppom-validator'; import { NetworkController } from '@metamask/network-controller'; -import { - Hex, - Json, - JsonRpcParams, - JsonRpcRequest, - JsonRpcResponse, -} from '@metamask/utils'; import { v4 as uuid } from 'uuid'; import { @@ -20,7 +13,7 @@ import { PreferencesController } from '../../controllers/preferences'; import { SecurityAlertResponse } from '../transaction/util'; import { normalizePPOMRequest } from './ppom-util'; -const { sentry } = global; +const { sentry } = global as any; const CONFIRMATION_METHODS = Object.freeze([ 'eth_sendRawTransaction', @@ -28,7 +21,7 @@ const CONFIRMATION_METHODS = Object.freeze([ ...SIGNING_METHODS, ]); -export const SUPPORTED_CHAIN_IDS: Hex[] = [ +export const SUPPORTED_CHAIN_IDS: string[] = [ CHAIN_IDS.ARBITRUM, CHAIN_IDS.AVALANCHE, CHAIN_IDS.BASE, @@ -56,30 +49,17 @@ export const SUPPORTED_CHAIN_IDS: Hex[] = [ * @param updateSecurityAlertResponseByTxId * @returns PPOMMiddleware function. */ -export function createPPOMMiddleware< - Params extends JsonRpcParams, - Result extends Json, ->( +export function createPPOMMiddleware( ppomController: PPOMController, preferencesController: PreferencesController, networkController: NetworkController, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any appStateController: any, updateSecurityAlertResponseByTxId: ( - req: JsonRpcRequest & { - securityAlertResponse: SecurityAlertResponse; - }, + req: any, securityAlertResponse: SecurityAlertResponse, ) => void, ) { - return async ( - req: JsonRpcRequest & { - securityAlertResponse: SecurityAlertResponse; - }, - _res: JsonRpcResponse, - next: () => void, - ) => { + return async (req: any, _res: any, next: () => void) => { try { const securityAlertsEnabled = preferencesController.store.getState()?.securityAlertsEnabled; @@ -91,59 +71,32 @@ export function createPPOMMiddleware< ) { // eslint-disable-next-line require-atomic-updates const securityAlertId = uuid(); - let securityAlertResponse: SecurityAlertResponse = { - reason: BlockaidResultType.Loading, - result_type: BlockaidReason.inProgress, - securityAlertId, - }; ppomController .usePPOM(async (ppom: PPOM) => { try { const normalizedRequest = normalizePPOMRequest(req); - securityAlertResponse = await ppom.validateJsonRpc( + const securityAlertResponse = await ppom.validateJsonRpc( normalizedRequest, ); + securityAlertResponse.securityAlertId = securityAlertId; - } catch (error: unknown) { + return securityAlertResponse; + } catch (error: any) { sentry?.captureException(error); - console.error( - 'Error validating JSON RPC using PPOM: ', - typeof error === 'object' || typeof error === 'string' - ? error - : JSON.stringify(error), - ); - - securityAlertResponse = { + const errorObject = error as unknown as Error; + console.error('Error validating JSON RPC using PPOM: ', error); + const securityAlertResponse = { result_type: BlockaidResultType.Errored, reason: BlockaidReason.errored, - description: - error instanceof Error - ? `${error.name}: ${error.message}` - : JSON.stringify(error), + description: `${errorObject.name}: ${errorObject.message}`, }; - } - }) - .catch((error: unknown) => { - sentry?.captureException(error); - console.error( - 'Error createPPOMMiddleware#usePPOM: ', - typeof error === 'object' || typeof error === 'string' - ? error - : JSON.stringify(error), - ); - securityAlertResponse = { - result_type: BlockaidResultType.Errored, - reason: BlockaidReason.errored, - description: - error instanceof Error - ? `${error.name}: ${error.message}` - : JSON.stringify(error), - }; + return securityAlertResponse; + } }) - .finally(() => { + .then((securityAlertResponse) => { updateSecurityAlertResponseByTxId(req, { ...securityAlertResponse, securityAlertId, @@ -151,31 +104,32 @@ export function createPPOMMiddleware< }); if (SIGNING_METHODS.includes(req.method)) { + req.securityAlertResponse = { + reason: BlockaidResultType.Loading, + result_type: BlockaidReason.inProgress, + securityAlertId, + }; appStateController.addSignatureSecurityAlertResponse({ reason: BlockaidResultType.Loading, result_type: BlockaidReason.inProgress, securityAlertId, }); + } else { + req.securityAlertResponse = { + reason: BlockaidResultType.Loading, + result_type: BlockaidReason.inProgress, + securityAlertId, + }; } - - req.securityAlertResponse = { ...securityAlertResponse }; } - } catch (error: unknown) { + } catch (error: any) { + const errorObject = error as unknown as Error; sentry?.captureException(error); - console.error( - 'Error createPPOMMiddleware: ', - typeof error === 'object' || typeof error === 'string' - ? error - : JSON.stringify(error), - ); - + console.error('Error validating JSON RPC using PPOM: ', error); req.securityAlertResponse = { result_type: BlockaidResultType.Errored, reason: BlockaidReason.errored, - description: - error instanceof Error - ? `${error.name}: ${error.message}` - : JSON.stringify(error), + description: `${errorObject.name}: ${errorObject.message}`, }; } finally { next(); diff --git a/app/scripts/lib/ppom/ppom-util.ts b/app/scripts/lib/ppom/ppom-util.ts index 95806d41bb4a..d0f9141996c0 100644 --- a/app/scripts/lib/ppom/ppom-util.ts +++ b/app/scripts/lib/ppom/ppom-util.ts @@ -2,8 +2,6 @@ import { normalizeTransactionParams } from '@metamask/transaction-controller'; const METHOD_SEND_TRANSACTION = 'eth_sendTransaction'; -// TODO: Replace `any` with type -// eslint-disable-next-line @typescript-eslint/no-explicit-any export function normalizePPOMRequest(request: any) { if (request.method !== METHOD_SEND_TRANSACTION) { return request; diff --git a/app/scripts/lib/setupSentry.js b/app/scripts/lib/setupSentry.js index df283b15c0bb..b03a15a45dd3 100644 --- a/app/scripts/lib/setupSentry.js +++ b/app/scripts/lib/setupSentry.js @@ -86,7 +86,6 @@ export const SENTRY_BACKGROUND_STATE = { browserEnvironment: true, connectedStatusPopoverHasBeenShown: true, currentPopupId: false, - currentExtensionPopupId: false, defaultHomeActiveTabName: true, fullScreenGasPollTokens: true, hadAdvancedGasFeesSetPriorToMigration92_3: true, @@ -103,8 +102,6 @@ export const SENTRY_BACKGROUND_STATE = { showProductTour: true, showNetworkBanner: true, showAccountBanner: true, - switchedNetworkDetails: false, - switchedNetworkNeverShowMessage: false, showTestnetMessageInDropdown: true, surveyLinkLastClickedOrClosed: true, snapsInstallPrivacyWarningShown: true, @@ -222,7 +219,6 @@ export const SENTRY_BACKGROUND_STATE = { showExtensionInFullSizeView: true, showFiatInTestnets: true, showTestNetworks: true, - smartTransactionsOptInStatus: true, useNativeCurrencyAsPrimaryCurrency: true, petnamesEnabled: true, }, @@ -385,8 +381,6 @@ export const SENTRY_UI_STATE = { addSnapAccountEnabled: false, snapsAddSnapAccountModalDismissed: false, ///: END:ONLY_INCLUDE_IF - switchedNetworkDetails: false, - switchedNetworkNeverShowMessage: false, }, unconnectedAccount: true, }; @@ -481,18 +475,10 @@ export default function setupSentry({ release, getState }) { `Missing SENTRY_DSN environment variable in production environment`, ); } - - ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) - sentryTarget = process.env.SENTRY_MMI_DSN; - ///: END:ONLY_INCLUDE_IF - - ///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask) - sentryTarget = process.env.SENTRY_DSN; - ///: END:ONLY_INCLUDE_IF - console.log( `Setting up Sentry Remote Error Reporting for '${environment}': SENTRY_DSN`, ); + sentryTarget = process.env.SENTRY_DSN; } else { console.log( `Setting up Sentry Remote Error Reporting for '${environment}': SENTRY_DSN_DEV`, @@ -507,10 +493,6 @@ export default function setupSentry({ release, getState }) { * @returns `true` if MetaMetrics is enabled, `false` otherwise. */ async function getMetaMetricsEnabled() { - if (METAMASK_BUILD_TYPE === 'mmi') { - return true; - } - const appState = getState(); if (appState.state || appState.persistedState) { return getMetaMetricsEnabledFromAppState(appState); @@ -526,22 +508,6 @@ export default function setupSentry({ release, getState }) { } } - /** - * Returns whether Sentry should be enabled or not. If the build type is mmi - * it will always be enabled, if it's main it will first check for MetaMetrics - * value before returning true or false - * - * @returns `true` if Sentry should be enabled, depends on the build type and - * whether MetaMetrics is on or off for all build types except mmi - */ - async function getSentryEnabled() { - // For MMI we want Sentry always logging, doesn't depend on MetaMetrics being on or off - if (METAMASK_BUILD_TYPE === 'mmi') { - return true; - } - return getMetaMetricsEnabled(); - } - Sentry.init({ dsn: sentryTarget, debug: METAMASK_DEBUG, @@ -601,7 +567,7 @@ export default function setupSentry({ release, getState }) { const startSession = async () => { const hub = Sentry.getCurrentHub?.(); const options = hub.getClient?.().getOptions?.() ?? {}; - if (hub && (await getSentryEnabled()) === true) { + if (hub && (await getMetaMetricsEnabled()) === true) { options.autoSessionTracking = true; hub.startSession(); } @@ -615,7 +581,7 @@ export default function setupSentry({ release, getState }) { const endSession = async () => { const hub = Sentry.getCurrentHub?.(); const options = hub.getClient?.().getOptions?.() ?? {}; - if (hub && (await getSentryEnabled()) === false) { + if (hub && (await getMetaMetricsEnabled()) === false) { options.autoSessionTracking = false; hub.endSession(); } @@ -631,11 +597,14 @@ export default function setupSentry({ release, getState }) { const options = hub.getClient?.().getOptions?.() ?? { autoSessionTracking: false, }; - const isSentryEnabled = await getSentryEnabled(); - if (isSentryEnabled === true && options.autoSessionTracking === false) { + const isMetaMetricsEnabled = await getMetaMetricsEnabled(); + if ( + isMetaMetricsEnabled === true && + options.autoSessionTracking === false + ) { await startSession(); } else if ( - isSentryEnabled === false && + isMetaMetricsEnabled === false && options.autoSessionTracking === true ) { await endSession(); diff --git a/app/scripts/lib/snap-keyring/metrics.test.ts b/app/scripts/lib/snap-keyring/metrics.test.ts index fe49b2be73e4..e0ef57fc2b59 100644 --- a/app/scripts/lib/snap-keyring/metrics.test.ts +++ b/app/scripts/lib/snap-keyring/metrics.test.ts @@ -3,8 +3,6 @@ import { getSnapAndHardwareInfoForMetrics } from './metrics'; describe('getSnapAndHardwareInfoForMetrics', () => { let getAccountType: jest.Mock; let getDeviceModel: jest.Mock; - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any let messenger: any; beforeEach(() => { diff --git a/app/scripts/lib/snap-keyring/snap-keyring.ts b/app/scripts/lib/snap-keyring/snap-keyring.ts index 62b61c8ed335..17683dee0a52 100644 --- a/app/scripts/lib/snap-keyring/snap-keyring.ts +++ b/app/scripts/lib/snap-keyring/snap-keyring.ts @@ -51,22 +51,14 @@ export const snapKeyringBuilder = ( getSnapController: () => SnapController, persistKeyringHelper: () => Promise, setSelectedAccountHelper: (address: string) => void, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any removeAccountHelper: (address: string) => Promise, trackEvent: ( - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any payload: Record, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any options?: Record, ) => void, getSnapName: (snapId: string) => string, ) => { const builder = (() => { - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any return new SnapKeyring(getSnapController() as any, { addressExists: async (address) => { const addresses = await controllerMessenger.call( @@ -356,8 +348,6 @@ export const snapKeyringBuilder = ( } }, }); - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any }) as any; builder.type = SnapKeyring.type; return builder; diff --git a/app/scripts/lib/snap-keyring/utils/isBlockedUrl.test.ts b/app/scripts/lib/snap-keyring/utils/isBlockedUrl.test.ts index 03d949a814d1..839176dca5a1 100644 --- a/app/scripts/lib/snap-keyring/utils/isBlockedUrl.test.ts +++ b/app/scripts/lib/snap-keyring/utils/isBlockedUrl.test.ts @@ -36,8 +36,6 @@ describe('isBlockedUrl', () => { [1, true], [0, true], [-1, true], - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any ])('"%s" is blocked: %s', async (url: any, expected: boolean) => { const result = await isBlockedUrl( url, diff --git a/app/scripts/lib/snap-keyring/utils/showResult.ts b/app/scripts/lib/snap-keyring/utils/showResult.ts index 086f9fe07174..c0b4c530ed09 100644 --- a/app/scripts/lib/snap-keyring/utils/showResult.ts +++ b/app/scripts/lib/snap-keyring/utils/showResult.ts @@ -42,8 +42,6 @@ export const showError = ( controllerMessenger: SnapKeyringBuilderMessenger, snapId: string, opts: ResultComponentOptions, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any properties: Record, ): Promise => { return controllerMessenger.call('ApprovalController:showError', { @@ -72,8 +70,6 @@ export const showSuccess = ( controllerMessenger: SnapKeyringBuilderMessenger, snapId: string, opts: ResultComponentOptions, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any properties: Record, ): Promise => { return controllerMessenger.call('ApprovalController:showSuccess', { diff --git a/app/scripts/lib/transaction/metrics.test.ts b/app/scripts/lib/transaction/metrics.test.ts index d1d3f5e88091..a4eb13918d7d 100644 --- a/app/scripts/lib/transaction/metrics.test.ts +++ b/app/scripts/lib/transaction/metrics.test.ts @@ -68,12 +68,8 @@ const mockTransactionMetricsRequest = { getTokenStandardAndDetails: jest.fn(), getTransaction: jest.fn(), provider: provider as Provider, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any snapAndHardwareMessenger: jest.fn() as any, trackEvent: jest.fn(), - getIsSmartTransaction: jest.fn(), - getSmartTransactionByMinedTxHash: jest.fn(), } as TransactionMetricsRequest; describe('Transaction metrics', () => { @@ -81,17 +77,9 @@ describe('Transaction metrics', () => { mockChainId, mockNetworkId, mockTransactionMeta: TransactionMeta, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any mockTransactionMetaWithBlockaid: any, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any expectedProperties: any, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any expectedSensitiveProperties: any, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any mockActionId: any; beforeEach(() => { @@ -172,8 +160,6 @@ describe('Transaction metrics', () => { describe('handleTransactionAdded', () => { it('should return if transaction meta is not defined', async () => { - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any await handleTransactionAdded(mockTransactionMetricsRequest, {} as any); expect( mockTransactionMetricsRequest.createEventFragment, @@ -182,8 +168,6 @@ describe('Transaction metrics', () => { it('should create event fragment', async () => { await handleTransactionAdded(mockTransactionMetricsRequest, { - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any transactionMeta: mockTransactionMeta as any, actionId: mockActionId, }); @@ -211,8 +195,6 @@ describe('Transaction metrics', () => { }; await handleTransactionAdded(mockTransactionMetricsRequest, { - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any transactionMeta: mockTransactionMeta as any, actionId: mockActionId, }); @@ -239,8 +221,6 @@ describe('Transaction metrics', () => { it('should create event fragment with blockaid', async () => { await handleTransactionAdded(mockTransactionMetricsRequest, { - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any transactionMeta: mockTransactionMetaWithBlockaid as any, actionId: mockActionId, }); @@ -271,8 +251,6 @@ describe('Transaction metrics', () => { describe('handleTransactionApproved', () => { it('should return if transaction meta is not defined', async () => { - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any await handleTransactionApproved(mockTransactionMetricsRequest, {} as any); expect( mockTransactionMetricsRequest.createEventFragment, @@ -287,8 +265,6 @@ describe('Transaction metrics', () => { it('should create, update, finalize event fragment', async () => { await handleTransactionApproved(mockTransactionMetricsRequest, { - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any transactionMeta: mockTransactionMeta as any, actionId: mockActionId, }); @@ -330,8 +306,6 @@ describe('Transaction metrics', () => { it('should create, update, finalize event fragment with blockaid', async () => { await handleTransactionApproved(mockTransactionMetricsRequest, { - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any transactionMeta: mockTransactionMetaWithBlockaid as any, actionId: mockActionId, }); @@ -388,8 +362,6 @@ describe('Transaction metrics', () => { describe('handleTransactionFailed', () => { it('should return if transaction meta is not defined', async () => { - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any await handleTransactionFailed(mockTransactionMetricsRequest, {} as any); expect( mockTransactionMetricsRequest.createEventFragment, @@ -414,8 +386,6 @@ describe('Transaction metrics', () => { transactionMeta: mockTransactionMeta, actionId: mockActionId, error: mockErrorMessage, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any } as any); const expectedUniqueId = 'transaction-submitted-1'; @@ -470,8 +440,6 @@ describe('Transaction metrics', () => { transactionMeta: mockTransactionMetaWithBlockaid, actionId: mockActionId, error: mockErrorMessage, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any } as any); const expectedUniqueId = 'transaction-submitted-1'; @@ -535,8 +503,6 @@ describe('Transaction metrics', () => { transactionMeta: mockTransactionMeta, actionId: mockActionId, error: mockErrorMessage, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any } as any); const expectedUniqueId = 'transaction-submitted-1'; @@ -584,8 +550,6 @@ describe('Transaction metrics', () => { it('should return if transaction meta is not defined', async () => { await handleTransactionConfirmed( mockTransactionMetricsRequest, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any {} as any, ); expect( @@ -609,8 +573,6 @@ describe('Transaction metrics', () => { await handleTransactionConfirmed(mockTransactionMetricsRequest, { transactionMeta: mockTransactionMeta, actionId: mockActionId, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any } as any); const expectedUniqueId = 'transaction-submitted-1'; @@ -667,8 +629,6 @@ describe('Transaction metrics', () => { await handleTransactionConfirmed(mockTransactionMetricsRequest, { transactionMeta: mockTransactionMetaWithBlockaid, actionId: mockActionId, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any } as any); const expectedUniqueId = 'transaction-submitted-1'; @@ -732,8 +692,6 @@ describe('Transaction metrics', () => { describe('handleTransactionDropped', () => { it('should return if transaction meta is not defined', async () => { - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any await handleTransactionDropped(mockTransactionMetricsRequest, {} as any); expect( mockTransactionMetricsRequest.createEventFragment, @@ -750,8 +708,6 @@ describe('Transaction metrics', () => { await handleTransactionDropped(mockTransactionMetricsRequest, { transactionMeta: mockTransactionMeta, actionId: mockActionId, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any } as any); const expectedUniqueId = 'transaction-submitted-1'; @@ -800,8 +756,6 @@ describe('Transaction metrics', () => { await handleTransactionDropped(mockTransactionMetricsRequest, { transactionMeta: mockTransactionMetaWithBlockaid, actionId: mockActionId, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any } as any); const expectedUniqueId = 'transaction-submitted-1'; @@ -863,8 +817,6 @@ describe('Transaction metrics', () => { describe('handleTransactionRejected', () => { it('should return if transaction meta is not defined', async () => { - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any await handleTransactionRejected(mockTransactionMetricsRequest, {} as any); expect( mockTransactionMetricsRequest.createEventFragment, @@ -881,8 +833,6 @@ describe('Transaction metrics', () => { await handleTransactionRejected(mockTransactionMetricsRequest, { transactionMeta: mockTransactionMeta, actionId: mockActionId, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any } as any); const expectedUniqueId = 'transaction-added-1'; @@ -926,8 +876,6 @@ describe('Transaction metrics', () => { await handleTransactionRejected(mockTransactionMetricsRequest, { transactionMeta: mockTransactionMetaWithBlockaid, actionId: mockActionId, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any } as any); const expectedUniqueId = 'transaction-added-1'; @@ -986,8 +934,6 @@ describe('Transaction metrics', () => { it('should return if transaction meta is not defined', async () => { await handleTransactionSubmitted( mockTransactionMetricsRequest, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any {} as any, ); expect( @@ -997,8 +943,6 @@ describe('Transaction metrics', () => { it('should only create event fragment', async () => { await handleTransactionSubmitted(mockTransactionMetricsRequest, { - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any transactionMeta: mockTransactionMeta as any, actionId: mockActionId, }); diff --git a/app/scripts/lib/transaction/metrics.ts b/app/scripts/lib/transaction/metrics.ts index fa0a1469578e..1335878393a9 100644 --- a/app/scripts/lib/transaction/metrics.ts +++ b/app/scripts/lib/transaction/metrics.ts @@ -7,7 +7,6 @@ import { TransactionMeta, TransactionType, } from '@metamask/transaction-controller'; -import { SmartTransaction } from '@metamask/smart-transactions-controller/dist/types'; import { ORIGIN_METAMASK } from '../../../../shared/constants/app'; import { determineTransactionAssetType, @@ -39,7 +38,6 @@ import { ///: BEGIN:ONLY_INCLUDE_IF(blockaid) import { getBlockaidMetricsProps } from '../../../../ui/helpers/utils/metrics'; ///: END:ONLY_INCLUDE_IF -import { getSmartTransactionMetricsProperties } from '../../../../shared/modules/metametrics'; import { getSnapAndHardwareInfoForMetrics, type SnapAndHardwareMessenger, @@ -71,8 +69,6 @@ export type TransactionMetricsRequest = { // According to the type GasFeeState returned from getEIP1559GasFeeEstimates // doesn't include some properties used in buildEventFragmentProperties, // hence returning any here to avoid type errors. - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any getEIP1559GasFeeEstimates(options?: FetchGasFeeEstimateOptions): Promise; getParticipateInMetrics: () => boolean; getSelectedAddress: () => string; @@ -85,13 +81,7 @@ export type TransactionMetricsRequest = { getTransaction: (transactionId: string) => TransactionMeta; provider: Provider; snapAndHardwareMessenger: SnapAndHardwareMessenger; - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any trackEvent: (payload: any) => void; - getIsSmartTransaction: () => boolean; - getSmartTransactionByMinedTxHash: ( - txhash: string | undefined, - ) => SmartTransaction; }; export const METRICS_STATUS_FAILED = 'failed on-chain'; @@ -171,8 +161,6 @@ export const handleTransactionFailed = async ( return; } - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any const extraParams = {} as Record; if (transactionEventPayload.error) { // This is a failed transaction @@ -203,8 +191,6 @@ export const handleTransactionConfirmed = async ( return; } - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any const extraParams = {} as Record; const { transactionMeta } = transactionEventPayload; const { txReceipt } = transactionMeta; @@ -498,8 +484,6 @@ function createTransactionEventFragment({ eventName: TransactionMetaMetricsEvent; transactionEventPayload: TransactionEventPayload; transactionMetricsRequest: TransactionMetricsRequest; - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any payload: any; }) { if ( @@ -613,8 +597,6 @@ function updateTransactionEventFragment({ eventName: TransactionMetaMetricsEvent; transactionEventPayload: TransactionEventPayload; transactionMetricsRequest: TransactionMetricsRequest; - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any payload: any; }) { const uniqueId = getUniqueId(eventName, transactionMeta.id); @@ -684,8 +666,6 @@ async function createUpdateFinalizeTransactionEventFragment({ eventName: TransactionMetaMetricsEvent; transactionEventPayload: TransactionEventPayload; transactionMetricsRequest: TransactionMetricsRequest; - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any extraParams?: Record; }) { const { properties, sensitiveProperties } = @@ -723,8 +703,6 @@ async function createUpdateFinalizeTransactionEventFragment({ } function hasFragment( - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any getEventFragmentById: (arg0: string) => any, eventName: TransactionMetaMetricsEvent, transactionMeta: TransactionMeta, @@ -753,8 +731,6 @@ async function buildEventFragmentProperties({ transactionMetricsRequest, extraParams = {}, }: { - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any extraParams?: Record; transactionEventPayload: TransactionEventPayload; transactionMetricsRequest: TransactionMetricsRequest; @@ -794,8 +770,6 @@ async function buildEventFragmentProperties({ transactionMetricsRequest.getTokenStandardAndDetails, ); - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any const gasParams = {} as Record; if (isEIP1559Transaction(transactionMeta)) { @@ -875,6 +849,7 @@ async function buildEventFragmentProperties({ TransactionType.tokenMethodSetApprovalForAll, TransactionType.tokenMethodTransfer, TransactionType.tokenMethodTransferFrom, + TransactionType.smart, TransactionType.swap, TransactionType.swapApproval, ].includes(type); @@ -960,8 +935,6 @@ async function buildEventFragmentProperties({ } ///: BEGIN:ONLY_INCLUDE_IF(blockaid) - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any const blockaidProperties: any = getBlockaidMetricsProps(transactionMeta); if (blockaidProperties?.ui_customizations?.length > 0) { @@ -973,12 +946,6 @@ async function buildEventFragmentProperties({ uiCustomizations.push(MetaMetricsEventUiCustomization.GasEstimationFailed); } - const smartTransactionMetricsProperties = - getSmartTransactionMetricsProperties( - transactionMetricsRequest, - transactionMeta, - ); - /** The transaction status property is not considered sensitive and is now included in the non-anonymous event */ let properties = { chain_id: chainId, @@ -1005,9 +972,6 @@ async function buildEventFragmentProperties({ ///: END:ONLY_INCLUDE_IF // ui_customizations must come after ...blockaidProperties ui_customizations: uiCustomizations.length > 0 ? uiCustomizations : null, - ...smartTransactionMetricsProperties, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any } as Record; const snapAndHardwareInfo = await getSnapAndHardwareInfoForMetrics( @@ -1034,8 +998,6 @@ async function buildEventFragmentProperties({ transaction_replaced: transactionReplaced, ...extraParams, ...gasParamsInGwei, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any } as Record; if (transactionContractMethod === contractMethodNames.APPROVE) { @@ -1051,11 +1013,7 @@ async function buildEventFragmentProperties({ return { properties, sensitiveProperties }; } -// TODO: Replace `any` with type -// eslint-disable-next-line @typescript-eslint/no-explicit-any function getGasValuesInGWEI(gasParams: Record) { - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any const gasValuesInGwei = {} as Record; for (const param in gasParams) { if (isHexString(gasParams[param])) { diff --git a/app/scripts/lib/transaction/mmi-hooks.test.ts b/app/scripts/lib/transaction/mmi-hooks.test.ts index fee5dff83de5..210c3093ada8 100644 --- a/app/scripts/lib/transaction/mmi-hooks.test.ts +++ b/app/scripts/lib/transaction/mmi-hooks.test.ts @@ -13,8 +13,6 @@ describe('MMI hooks', () => { const custodyIdMocked = '123'; describe('afterTransactionSign', () => { it('returns false if txMeta has no custodyStatus', () => { - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any const txMeta = { to: toMocked } as any; const signedEthTx = {}; const result = afterTransactionSign(txMeta, signedEthTx, jest.fn()); @@ -26,8 +24,6 @@ describe('MMI hooks', () => { custodyStatus: TransactionStatus.approved, custodyId: custodyIdMocked, txParams: { from: fromMocked }, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any } as any; const signedEthTx = { custodian_transactionId: custodyIdMocked, @@ -51,16 +47,12 @@ describe('MMI hooks', () => { describe('beforeTransactionPublish', () => { it('returns true if txMeta has custodyStatus', () => { - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any const txMeta = { custodyStatus: TransactionStatus.approved } as any; const result = beforeTransactionPublish(txMeta); expect(result).toBe(false); }); it('returns false if txMeta has no custodyStatus', () => { - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any const txMeta = { to: toMocked } as any; const result = beforeTransactionPublish(txMeta); expect(result).toBe(true); @@ -69,16 +61,12 @@ describe('MMI hooks', () => { describe('getAdditionalSignArguments', () => { it('returns an array with txMeta when custodyStatus is truthy', () => { - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any const txMeta = { custodyStatus: TransactionStatus.approved } as any; const result = getAdditionalSignArguments(txMeta); expect(result).toEqual([txMeta]); }); it('returns an empty array when custodyStatus is falsy', () => { - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any const txMeta = { to: toMocked } as any; const result = getAdditionalSignArguments(txMeta); expect(result).toEqual([]); @@ -87,16 +75,12 @@ describe('MMI hooks', () => { describe('beforeTransactionApproveOnInit', () => { it('returns true if txMeta has custodyStatus', () => { - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any const txMeta = { custodyStatus: TransactionStatus.approved } as any; const result = beforeTransactionApproveOnInit(txMeta); expect(result).toBe(false); }); it('returns false if txMeta has no custodyStatus', () => { - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any const txMeta = { to: toMocked } as any; const result = beforeTransactionApproveOnInit(txMeta); expect(result).toBe(true); @@ -108,16 +92,12 @@ describe('MMI hooks', () => { const txMeta = { custodyStatus: TransactionStatus.approved, custodyId: 1, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any } as any; const result = beforeCheckPendingTransaction(txMeta); expect(result).toBe(false); }); it('returns false if txMeta has no custodyStatus', () => { - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any const txMeta = { to: toMocked } as any; const result = beforeCheckPendingTransaction(txMeta); expect(result).toBe(true); diff --git a/app/scripts/lib/transaction/mmi-hooks.ts b/app/scripts/lib/transaction/mmi-hooks.ts index d4dc3cc4dcda..92ea0cf792b9 100644 --- a/app/scripts/lib/transaction/mmi-hooks.ts +++ b/app/scripts/lib/transaction/mmi-hooks.ts @@ -9,8 +9,6 @@ import { TransactionMeta } from '@metamask/transaction-controller'; */ export function afterTransactionSign( txMeta: TransactionMeta, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any signedEthTx: any, addTransactionToWatchList: ( custodianTransactionId: string | undefined, diff --git a/app/scripts/lib/transaction/smart-transactions.test.ts b/app/scripts/lib/transaction/smart-transactions.test.ts deleted file mode 100644 index 35f533f7c882..000000000000 --- a/app/scripts/lib/transaction/smart-transactions.test.ts +++ /dev/null @@ -1,348 +0,0 @@ -import EventEmitter from 'events'; -import { - TransactionType, - TransactionStatus, - TransactionController, -} from '@metamask/transaction-controller'; -import SmartTransactionsController from '@metamask/smart-transactions-controller'; -import { CHAIN_IDS } from '../../../../shared/constants/network'; -import { submitSmartTransactionHook } from './smart-transactions'; -import type { - SubmitSmartTransactionRequest, - SmartTransactionsControllerMessenger, -} from './smart-transactions'; - -const addressFrom = '0xabce7847fd3661a9b7c86aaf1daea08d9da5750e'; -const txHash = - '0x0302b75dfb9fd9eb34056af031efcaee2a8cbd799ea054a85966165cd82a7356'; -const uuid = 'uuid'; -const txId = '1'; - -let addRequestCallback: () => void; - -type SubmitSmartTransactionRequestMocked = SubmitSmartTransactionRequest & { - smartTransactionsController: jest.Mocked; - transactionController: jest.Mocked; -}; - -const createSignedTransaction = () => { - return '0xf86c098504a817c800825208943535353535353535353535353535353535353535880de0b6b3a76400008025a02b79f322a625d623a2bb2911e0c6b3e7eaf741a7c7c5d2e8c67ef3ff4acf146ca01ae168fea63dc3391b75b586c8a7c0cb55cdf3b8e2e4d8e097957a3a56c6f2c5'; -}; - -const createTransactionControllerMock = () => { - return { - approveTransactionsWithSameNonce: jest.fn((transactions = []) => { - return transactions.length === 0 ? [] : [createSignedTransaction()]; - }), - state: { transactions: [] }, - } as unknown as jest.Mocked; -}; - -const createSmartTransactionsControllerMessengerMock = () => { - return { - call: jest.fn((type) => { - if (type === 'ApprovalController:addRequest') { - return { - then: (callback: () => void) => { - addRequestCallback = callback; - }, - }; - } - return Promise.resolve({ id: 'approvalId' }); - }), - } as unknown as jest.Mocked; -}; - -const createSmartTransactionsControllerMock = () => { - return { - getFees: jest.fn(async () => { - return { - tradeTxFees: { - cancelFees: [], - feeEstimate: 42000000000000, - fees: [ - { maxFeePerGas: 12843636951, maxPriorityFeePerGas: 2853145236 }, - ], - gasLimit: 21000, - gasUsed: 21000, - }, - }; - }), - submitSignedTransactions: jest.fn(async () => { - return { - uuid, - txHash, - }; - }), - eventEmitter: new EventEmitter(), - } as unknown as jest.Mocked; -}; - -describe('submitSmartTransactionHook', () => { - const createRequest = () => { - return { - transactionMeta: { - hash: txHash, - status: TransactionStatus.signed, - id: '1', - txParams: { - from: addressFrom, - to: '0x1678a085c290ebd122dc42cba69373b5953b831d', - gasPrice: '0x77359400', - gas: '0x7b0d', - nonce: '0x4b', - }, - type: TransactionType.simpleSend, - chainId: CHAIN_IDS.MAINNET, - time: 1624408066355, - defaultGasEstimates: { - gas: '0x7b0d', - gasPrice: '0x77359400', - }, - error: { - name: 'Error', - message: 'Details of the error', - }, - securityProviderResponse: { - flagAsDangerous: 0, - }, - }, - smartTransactionsController: createSmartTransactionsControllerMock(), - transactionController: createTransactionControllerMock(), - isSmartTransaction: true, - controllerMessenger: createSmartTransactionsControllerMessengerMock(), - featureFlags: { - extensionActive: true, - mobileActive: false, - smartTransactions: { - expectedDeadline: 45, - maxDeadline: 150, - returnTxHashAsap: false, - }, - }, - }; - }; - - beforeEach(() => { - addRequestCallback = () => undefined; - }); - - it('does not submit a transaction that is not a smart transaction', async () => { - const request: SubmitSmartTransactionRequestMocked = createRequest(); - request.isSmartTransaction = false; - const result = await submitSmartTransactionHook(request); - expect(result).toEqual({ transactionHash: undefined }); - }); - - it('returns a txHash asap if the feature flag requires it', async () => { - const request: SubmitSmartTransactionRequestMocked = createRequest(); - request.featureFlags.smartTransactions.returnTxHashAsap = true; - const result = await submitSmartTransactionHook(request); - expect(result).toEqual({ transactionHash: txHash }); - }); - - it('throws an error if there is no uuid', async () => { - const request: SubmitSmartTransactionRequestMocked = createRequest(); - request.smartTransactionsController.submitSignedTransactions = jest.fn( - // eslint-disable-next-line @typescript-eslint/no-unused-vars - async ({ signedTransactions, signedCanceledTransactions }) => { - return { uuid: undefined }; - }, - ); - await expect(submitSmartTransactionHook(request)).rejects.toThrow( - 'No smart transaction UUID', - ); - }); - - it('throws an error if there is no transaction hash', async () => { - const request: SubmitSmartTransactionRequestMocked = createRequest(); - setImmediate(() => { - request.smartTransactionsController.eventEmitter.emit( - `uuid:smartTransaction`, - { - status: 'cancelled', - statusMetadata: { - minedHash: '', - }, - }, - ); - }); - await expect(submitSmartTransactionHook(request)).rejects.toThrow( - 'Transaction does not have a transaction hash, there was a problem', - ); - }); - - it('submits a smart transaction', async () => { - const request: SubmitSmartTransactionRequestMocked = createRequest(); - setImmediate(() => { - request.smartTransactionsController.eventEmitter.emit( - `uuid:smartTransaction`, - { - status: 'pending', - statusMetadata: { - minedHash: '', - }, - }, - ); - request.smartTransactionsController.eventEmitter.emit( - `uuid:smartTransaction`, - { - status: 'success', - statusMetadata: { - minedHash: txHash, - }, - }, - ); - }); - const result = await submitSmartTransactionHook(request); - expect(result).toEqual({ transactionHash: txHash }); - const { txParams, chainId } = request.transactionMeta; - expect( - request.transactionController.approveTransactionsWithSameNonce, - ).toHaveBeenCalledWith( - [ - { - ...txParams, - maxFeePerGas: '0x2fd8a58d7', - maxPriorityFeePerGas: '0xaa0f8a94', - chainId, - }, - ], - { hasNonce: true }, - ); - expect( - request.smartTransactionsController.submitSignedTransactions, - ).toHaveBeenCalledWith({ - signedTransactions: [createSignedTransaction()], - signedCanceledTransactions: [], - txParams, - transactionMeta: request.transactionMeta, - }); - addRequestCallback(); - expect(request.controllerMessenger.call).toHaveBeenCalledTimes(4); - expect(request.controllerMessenger.call).toHaveBeenCalledWith( - 'ApprovalController:startFlow', - ); - expect(request.controllerMessenger.call).toHaveBeenCalledWith( - 'ApprovalController:addRequest', - { - id: 'approvalId', - origin: 'http://localhost', - type: 'smartTransaction:showSmartTransactionStatusPage', - requestState: { - smartTransaction: { - status: 'pending', - uuid, - creationTime: expect.any(Number), - }, - isDapp: true, - txId, - }, - }, - true, - ); - expect(request.controllerMessenger.call).toHaveBeenCalledWith( - 'ApprovalController:updateRequestState', - { - id: 'approvalId', - requestState: { - smartTransaction: { - status: 'success', - statusMetadata: { - minedHash: - '0x0302b75dfb9fd9eb34056af031efcaee2a8cbd799ea054a85966165cd82a7356', - }, - }, - isDapp: true, - txId, - }, - }, - ); - - expect(request.controllerMessenger.call).toHaveBeenCalledWith( - 'ApprovalController:endFlow', - { - id: 'approvalId', - }, - ); - }); - - it('submits a smart transaction and does not update approval request if approval was already approved or rejected', async () => { - const request: SubmitSmartTransactionRequestMocked = createRequest(); - setImmediate(() => { - request.smartTransactionsController.eventEmitter.emit( - `uuid:smartTransaction`, - { - status: 'pending', - uuid, - statusMetadata: { - minedHash: '', - }, - }, - ); - addRequestCallback(); - request.smartTransactionsController.eventEmitter.emit( - `uuid:smartTransaction`, - { - status: 'success', - uuid, - statusMetadata: { - minedHash: txHash, - }, - }, - ); - }); - const result = await submitSmartTransactionHook(request); - expect(result).toEqual({ transactionHash: txHash }); - const { txParams, chainId } = request.transactionMeta; - expect( - request.transactionController.approveTransactionsWithSameNonce, - ).toHaveBeenCalledWith( - [ - { - ...txParams, - maxFeePerGas: '0x2fd8a58d7', - maxPriorityFeePerGas: '0xaa0f8a94', - chainId, - }, - ], - { hasNonce: true }, - ); - expect( - request.smartTransactionsController.submitSignedTransactions, - ).toHaveBeenCalledWith({ - signedTransactions: [createSignedTransaction()], - signedCanceledTransactions: [], - txParams, - transactionMeta: request.transactionMeta, - }); - expect(request.controllerMessenger.call).toHaveBeenCalledTimes(3); - expect(request.controllerMessenger.call).toHaveBeenCalledWith( - 'ApprovalController:startFlow', - ); - expect(request.controllerMessenger.call).toHaveBeenCalledWith( - 'ApprovalController:addRequest', - { - id: 'approvalId', - origin: 'http://localhost', - type: 'smartTransaction:showSmartTransactionStatusPage', - requestState: { - smartTransaction: { - status: 'pending', - uuid, - creationTime: expect.any(Number), - }, - isDapp: true, - txId, - }, - }, - true, - ); - expect(request.controllerMessenger.call).toHaveBeenCalledWith( - 'ApprovalController:endFlow', - { - id: 'approvalId', - }, - ); - }); -}); diff --git a/app/scripts/lib/transaction/smart-transactions.ts b/app/scripts/lib/transaction/smart-transactions.ts deleted file mode 100644 index df9fc82e33f5..000000000000 --- a/app/scripts/lib/transaction/smart-transactions.ts +++ /dev/null @@ -1,328 +0,0 @@ -import SmartTransactionsController from '@metamask/smart-transactions-controller'; -import { - Fee, - Fees, - SmartTransactionStatuses, - SmartTransaction, -} from '@metamask/smart-transactions-controller/dist/types'; -import type { Hex } from '@metamask/utils'; -import { - TransactionController, - TransactionMeta, - TransactionParams, -} from '@metamask/transaction-controller'; -import log from 'loglevel'; -import { - RestrictedControllerMessenger, - EventConstraint, -} from '@metamask/base-controller'; -import { - AddApprovalRequest, - UpdateRequestState, - StartFlow, - EndFlow, -} from '@metamask/approval-controller'; - -import { decimalToHex } from '../../../../shared/modules/conversion.utils'; -import { CANCEL_GAS_LIMIT_DEC } from '../../../../shared/constants/smartTransactions'; -import { - SMART_TRANSACTION_CONFIRMATION_TYPES, - ORIGIN_METAMASK, -} from '../../../../shared/constants/app'; - -const namespace = 'SmartTransactions'; - -type AllowedActions = - | AddApprovalRequest - | UpdateRequestState - | StartFlow - | EndFlow; - -export type SmartTransactionsControllerMessenger = - RestrictedControllerMessenger< - typeof namespace, - AllowedActions, - EventConstraint, - AllowedActions['type'], - never - >; - -export type FeatureFlags = { - extensionActive: boolean; - mobileActive: boolean; - smartTransactions: { - expectedDeadline?: number; - maxDeadline?: number; - returnTxHashAsap?: boolean; - }; -}; - -export type SubmitSmartTransactionRequest = { - transactionMeta: TransactionMeta; - smartTransactionsController: SmartTransactionsController; - transactionController: TransactionController; - isSmartTransaction: boolean; - controllerMessenger: SmartTransactionsControllerMessenger; - featureFlags: FeatureFlags; -}; - -class SmartTransactionHook { - #approvalFlowEnded: boolean; - - #approvalFlowId: string; - - #chainId: Hex; - - #controllerMessenger: SmartTransactionsControllerMessenger; - - #featureFlags: { - extensionActive: boolean; - mobileActive: boolean; - smartTransactions: { - expectedDeadline?: number; - maxDeadline?: number; - returnTxHashAsap?: boolean; - }; - }; - - #isDapp: boolean; - - #isSmartTransaction: boolean; - - #smartTransactionsController: SmartTransactionsController; - - #transactionController: TransactionController; - - #transactionMeta: TransactionMeta; - - #txParams: TransactionParams; - - constructor(request: SubmitSmartTransactionRequest) { - const { - transactionMeta, - smartTransactionsController, - transactionController, - isSmartTransaction, - controllerMessenger, - featureFlags, - } = request; - this.#approvalFlowId = ''; - this.#approvalFlowEnded = false; - this.#transactionMeta = transactionMeta; - this.#smartTransactionsController = smartTransactionsController; - this.#transactionController = transactionController; - this.#isSmartTransaction = isSmartTransaction; - this.#controllerMessenger = controllerMessenger; - this.#featureFlags = featureFlags; - this.#isDapp = transactionMeta.origin !== ORIGIN_METAMASK; - this.#chainId = transactionMeta.chainId; - this.#txParams = transactionMeta.txParams; - } - - async submit() { - // Will cause TransactionController to publish to the RPC provider as normal. - const useRegularTransactionSubmit = { transactionHash: undefined }; - if (!this.#isSmartTransaction) { - return useRegularTransactionSubmit; - } - const { id: approvalFlowId } = await this.#controllerMessenger.call( - 'ApprovalController:startFlow', - ); - this.#approvalFlowId = approvalFlowId; - try { - const getFeesResponse = await this.#smartTransactionsController.getFees( - { ...this.#txParams, chainId: this.#chainId }, - undefined, - ); - const submitTransactionResponse = await this.#signAndSubmitTransactions({ - getFeesResponse, - }); - const uuid = submitTransactionResponse?.uuid; - if (!uuid) { - throw new Error('No smart transaction UUID'); - } - const returnTxHashAsap = - this.#featureFlags?.smartTransactions?.returnTxHashAsap; - this.#addApprovalRequest({ - uuid, - }); - this.#addListenerToUpdateStatusPage({ - uuid, - }); - let transactionHash: string | undefined | null; - if (returnTxHashAsap && submitTransactionResponse?.txHash) { - transactionHash = submitTransactionResponse.txHash; - } else { - transactionHash = await this.#waitForTransactionHash({ - uuid, - }); - } - if (transactionHash === null) { - throw new Error( - 'Transaction does not have a transaction hash, there was a problem', - ); - } - return { transactionHash }; - } catch (error) { - log.error('Error in smart transaction publish hook', error); - this.#onApproveOrReject(); - throw error; - } - } - - #onApproveOrReject() { - if (this.#approvalFlowEnded) { - return; - } - this.#approvalFlowEnded = true; - this.#controllerMessenger.call('ApprovalController:endFlow', { - id: this.#approvalFlowId, - }); - } - - #addApprovalRequest({ uuid }: { uuid: string }) { - const onApproveOrRejectWrapper = () => { - this.#onApproveOrReject(); - }; - this.#controllerMessenger - .call( - 'ApprovalController:addRequest', - { - id: this.#approvalFlowId, - origin, - type: SMART_TRANSACTION_CONFIRMATION_TYPES.showSmartTransactionStatusPage, - requestState: { - smartTransaction: { - status: SmartTransactionStatuses.PENDING, - creationTime: Date.now(), - uuid, - }, - isDapp: this.#isDapp, - txId: this.#transactionMeta.id, - }, - }, - true, - ) - .then(onApproveOrRejectWrapper, onApproveOrRejectWrapper); - } - - async #updateApprovalRequest({ - smartTransaction, - }: { - smartTransaction: SmartTransaction; - }) { - return await this.#controllerMessenger.call( - 'ApprovalController:updateRequestState', - { - id: this.#approvalFlowId, - requestState: { - smartTransaction, - isDapp: this.#isDapp, - txId: this.#transactionMeta.id, - }, - }, - ); - } - - async #addListenerToUpdateStatusPage({ uuid }: { uuid: string }) { - this.#smartTransactionsController.eventEmitter.on( - `${uuid}:smartTransaction`, - async (smartTransaction: SmartTransaction) => { - const { status } = smartTransaction; - if (!status || status === SmartTransactionStatuses.PENDING) { - return; - } - if (!this.#approvalFlowEnded) { - await this.#updateApprovalRequest({ - smartTransaction, - }); - } - }, - ); - } - - #waitForTransactionHash({ uuid }: { uuid: string }): Promise { - return new Promise((resolve) => { - this.#smartTransactionsController.eventEmitter.on( - `${uuid}:smartTransaction`, - async (smartTransaction: SmartTransaction) => { - const { status, statusMetadata } = smartTransaction; - if (!status || status === SmartTransactionStatuses.PENDING) { - return; - } - log.debug('Smart Transaction: ', smartTransaction); - if (statusMetadata?.minedHash) { - log.debug( - 'Smart Transaction - Received tx hash: ', - statusMetadata?.minedHash, - ); - resolve(statusMetadata.minedHash); - } else { - resolve(null); - } - }, - ); - }); - } - - async #signAndSubmitTransactions({ - getFeesResponse, - }: { - getFeesResponse: Fees; - }) { - const signedTransactions = await this.#createSignedTransactions( - getFeesResponse.tradeTxFees?.fees ?? [], - false, - ); - const signedCanceledTransactions = await this.#createSignedTransactions( - getFeesResponse.tradeTxFees?.cancelFees || [], - true, - ); - return await this.#smartTransactionsController.submitSignedTransactions({ - signedTransactions, - signedCanceledTransactions, - txParams: this.#txParams, - transactionMeta: this.#transactionMeta, - }); - } - - #applyFeeToTransaction(fee: Fee, isCancel: boolean): TransactionParams { - const unsignedTransaction = { - ...this.#txParams, - maxFeePerGas: `0x${decimalToHex(fee.maxFeePerGas)}`, - maxPriorityFeePerGas: `0x${decimalToHex(fee.maxPriorityFeePerGas)}`, - gas: isCancel - ? `0x${decimalToHex(CANCEL_GAS_LIMIT_DEC)}` // It has to be 21000 for cancel transactions, otherwise the API would reject it. - : this.#txParams.gas, - }; - if (isCancel) { - unsignedTransaction.to = unsignedTransaction.from; - unsignedTransaction.data = '0x'; - } - return unsignedTransaction; - } - - async #createSignedTransactions( - fees: Fee[], - isCancel: boolean, - ): Promise { - const unsignedTransactions = fees.map((fee) => { - return this.#applyFeeToTransaction(fee, isCancel); - }); - const transactionsWithChainId = unsignedTransactions.map((tx) => ({ - ...tx, - chainId: tx.chainId || this.#chainId, - })); - return (await this.#transactionController.approveTransactionsWithSameNonce( - transactionsWithChainId, - { hasNonce: true }, - )) as string[]; - } -} - -export const submitSmartTransactionHook = ( - request: SubmitSmartTransactionRequest, -) => { - const smartTransactionHook = new SmartTransactionHook(request); - return smartTransactionHook.submit(); -}; diff --git a/app/scripts/lib/transaction/util.test.ts b/app/scripts/lib/transaction/util.test.ts index 9b31caff37e0..b69fec9ae959 100644 --- a/app/scripts/lib/transaction/util.test.ts +++ b/app/scripts/lib/transaction/util.test.ts @@ -515,8 +515,6 @@ describe('Transaction Utils', () => { request.securityAlertsEnabled = true; request.chainId = '0x1'; - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any request.ppomController.usePPOM = (callback: any) => callback(ppomMock); await addTransaction(request); diff --git a/app/scripts/lib/transaction/util.ts b/app/scripts/lib/transaction/util.ts index 1781dd19a416..f625dcbfb709 100644 --- a/app/scripts/lib/transaction/util.ts +++ b/app/scripts/lib/transaction/util.ts @@ -12,7 +12,6 @@ import { AddUserOperationOptions, UserOperationController, } from '@metamask/user-operation-controller'; -import type { Hex } from '@metamask/utils'; ///: BEGIN:ONLY_INCLUDE_IF(blockaid) import { PPOMController } from '@metamask/ppom-validator'; import { captureException } from '@sentry/browser'; @@ -35,7 +34,6 @@ export type SecurityAlertResponse = { result_type: string; providerRequestsCount?: Record; securityAlertId?: string; - description?: string; }; export type AddTransactionOptions = NonNullable< @@ -61,7 +59,7 @@ export type AddTransactionOptions = NonNullable< >; type BaseAddTransactionRequest = { - chainId: Hex; + chainId: string; networkClientId: string; ppomController: PPOMController; securityAlertsEnabled: boolean; @@ -80,8 +78,6 @@ export type AddTransactionRequest = FinalAddTransactionRequest & { }; export type AddDappTransactionRequest = BaseAddTransactionRequest & { - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any dappRequest: Record; }; @@ -274,8 +270,6 @@ async function addUserOperationWithController( } = request; const { maxFeePerGas, maxPriorityFeePerGas } = transactionParams; - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any const { origin, requireApproval, type } = transactionOptions as any; const normalisedTransaction: TransactionParams = { diff --git a/app/scripts/lib/util.ts b/app/scripts/lib/util.ts index c523f6c03bbd..77c2e8543b64 100644 --- a/app/scripts/lib/util.ts +++ b/app/scripts/lib/util.ts @@ -181,8 +181,6 @@ export const isValidDate = (d: Date | number) => { */ type DeferredPromise = { - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any promise: Promise; resolve?: () => void; reject?: () => void; diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 25ba90d3eae2..2153c5c61558 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -171,7 +171,6 @@ import { TEST_NETWORK_TICKER_MAP, NetworkStatus, } from '../../shared/constants/network'; -import { ALLOWED_SMART_TRANSACTIONS_CHAIN_IDS } from '../../shared/constants/smartTransactions'; import { HardwareDeviceNames, LedgerTransportTypes, @@ -212,12 +211,6 @@ import { STATIC_MAINNET_TOKEN_LIST } from '../../shared/constants/tokens'; import { getTokenValueParam } from '../../shared/lib/metamask-controller-utils'; import { isManifestV3 } from '../../shared/modules/mv3.utils'; import { convertNetworkId } from '../../shared/modules/network.utils'; -import { - getIsSmartTransaction, - getFeatureFlagsByChainId, - getSmartTransactionsOptInStatus, - getCurrentChainSupportsSmartTransactions, -} from '../../shared/modules/selectors'; import { ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) handleMMITransactionUpdate, @@ -241,7 +234,6 @@ import { getAdditionalSignArguments as getAdditionalSignArgumentsMMI, } from './lib/transaction/mmi-hooks'; ///: END:ONLY_INCLUDE_IF -import { submitSmartTransactionHook } from './lib/transaction/smart-transactions'; ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps) import { keyringSnapPermissionsBuilder } from './lib/keyring-snaps-permissions'; ///: END:ONLY_INCLUDE_IF @@ -312,10 +304,6 @@ import { snapKeyringBuilder, getAccountsBySnapId } from './lib/snap-keyring'; import { encryptorFactory } from './lib/encryptor-factory'; import { addDappTransaction, addTransaction } from './lib/transaction/util'; import { LatticeKeyringOffscreen } from './lib/offscreen-bridge/lattice-offscreen-keyring'; -///: BEGIN:ONLY_INCLUDE_IF(snaps) -import PREINSTALLED_SNAPS from './snaps/preinstalled-snaps'; -///: END:ONLY_INCLUDE_IF -import AuthenticationController from './controllers/authentication/authentication-controller'; export const METAMASK_CONTROLLER_EVENTS = { // Fired after state changes that impact the extension badge (unapproved msg count) @@ -498,13 +486,28 @@ export default class MetamaskController extends EventEmitter { this.networkController.getProviderAndBlockTracker().provider; this.blockTracker = this.networkController.getProviderAndBlockTracker().blockTracker; - this.deprecatedNetworkVersions = {}; + + // TODO: Delete when ready to remove `networkVersion` from provider object + this.deprecatedNetworkId = null; + networkControllerMessenger.subscribe( + 'NetworkController:networkDidChange', + () => this.updateDeprecatedNetworkId(), + ); const tokenListMessenger = this.controllerMessenger.getRestricted({ name: 'TokenListController', allowedEvents: ['NetworkController:stateChange'], }); + this.tokenListController = new TokenListController({ + chainId: this.networkController.state.providerConfig.chainId, + preventPollingOnNetworkRestart: initState.TokenListController + ? initState.TokenListController.preventPollingOnNetworkRestart + : true, + messenger: tokenListMessenger, + state: initState.TokenListController, + }); + const preferencesMessenger = this.controllerMessenger.getRestricted({ name: 'PreferencesController', allowedActions: [], @@ -524,15 +527,6 @@ export default class MetamaskController extends EventEmitter { ), }); - this.tokenListController = new TokenListController({ - chainId: this.networkController.state.providerConfig.chainId, - preventPollingOnNetworkRestart: !this.#isTokenListPollingRequired( - this.preferencesController.store.getState(), - ), - messenger: tokenListMessenger, - state: initState.TokenListController, - }); - this.assetsContractController = new AssetsContractController( { chainId: this.networkController.state.providerConfig.chainId, @@ -690,14 +684,6 @@ export default class MetamaskController extends EventEmitter { addNft: this.nftController.addNft.bind(this.nftController), getNftApi: this.nftController.getNftApi.bind(this.nftController), getNftState: () => this.nftController.state, - // added this to track previous value of useNftDetection, should be true on very first initializing of controller[] - disabled: - this.preferencesController.store.getState().useNftDetection === - undefined - ? true - : !this.preferencesController.store.getState().useNftDetection, - selectedAddress: - this.preferencesController.store.getState().selectedAddress, }); this.metaMetricsController = new MetaMetricsController({ @@ -993,7 +979,6 @@ export default class MetamaskController extends EventEmitter { additionalKeyrings.push( mmiKeyringBuilderFactory(CUSTODIAN_TYPES[custodianType].keyringClass, { mmiConfigurationController: this.mmiConfigurationController, - captureException, }), ); } @@ -1112,7 +1097,6 @@ export default class MetamaskController extends EventEmitter { this.controllerMessenger.subscribe('KeyringController:lock', () => this._onLock(), ); - this.controllerMessenger.subscribe( 'KeyringController:stateChange', (state) => { @@ -1300,7 +1284,6 @@ export default class MetamaskController extends EventEmitter { allowLocalSnaps, requireAllowlist, }, - preinstalledSnaps: PREINSTALLED_SNAPS, }); this.notificationController = new NotificationController({ @@ -1407,15 +1390,6 @@ export default class MetamaskController extends EventEmitter { ///: END:ONLY_INCLUDE_IF - // Notification Controllers - this.authenticationController = new AuthenticationController({ - state: initState.AuthenticationController, - messenger: this.controllerMessenger.getRestricted({ - name: 'AuthenticationController', - allowedActions: [`${this.snapController.name}:handleRequest`], - }), - }); - // account tracker watches balances, nonces, and any code at their address this.accountTracker = new AccountTracker({ provider: this.provider, @@ -1620,15 +1594,6 @@ export default class MetamaskController extends EventEmitter { () => listener(), ); }, - pendingTransactions: { - isResubmitEnabled: () => { - const state = this._getMetaMaskState(); - return !( - getSmartTransactionsOptInStatus(state) && - getCurrentChainSupportsSmartTransactions(state) - ); - }, - }, provider: this.provider, hooks: { ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) @@ -1646,7 +1611,6 @@ export default class MetamaskController extends EventEmitter { beforePublish: beforeTransactionPublishMMI.bind(this), getAdditionalSignArguments: getAdditionalSignArgumentsMMI.bind(this), ///: END:ONLY_INCLUDE_IF - publish: this._publishSmartTransactionHook.bind(this), }, sign: (...args) => this.keyringController.signTransaction(...args), state: initState.TransactionController, @@ -1835,19 +1799,18 @@ export default class MetamaskController extends EventEmitter { networkControllerMessenger, 'NetworkController:stateChange', ), - getNonceLock: this.txController.getNonceLock.bind(this.txController), + getNonceLock: this.txController.nonceTracker.getNonceLock.bind( + this.txController.nonceTracker, + ), confirmExternalTransaction: this.txController.confirmExternalTransaction.bind(this.txController), - getTransactions: this.txController.getTransactions.bind( - this.txController, - ), provider: this.provider, trackMetaMetricsEvent: this.metaMetricsController.trackEvent.bind( this.metaMetricsController, ), }, { - supportedChainIds: ALLOWED_SMART_TRANSACTIONS_CHAIN_IDS, + supportedChainIds: [CHAIN_IDS.MAINNET, CHAIN_IDS.GOERLI], }, initState.SmartTransactionsController, ); @@ -2238,6 +2201,7 @@ export default class MetamaskController extends EventEmitter { } postOnboardingInitialization() { + this.updateDeprecatedNetworkId(); this.networkController.lookupNetwork(); } @@ -2249,12 +2213,7 @@ export default class MetamaskController extends EventEmitter { const preferencesControllerState = this.preferencesController.store.getState(); - const { useCurrencyRateCheck, useNftDetection } = - preferencesControllerState; - - if (useNftDetection) { - this.nftDetectionController.start(); - } + const { useCurrencyRateCheck } = preferencesControllerState; if (useCurrencyRateCheck) { this.currencyRateController.startPollingByNetworkClientId( @@ -2271,7 +2230,6 @@ export default class MetamaskController extends EventEmitter { this.accountTracker.stop(); this.txController.stopIncomingTransactionPolling(); this.tokenDetectionController.disable(); - this.nftDetectionController.stop(); const preferencesControllerState = this.preferencesController.store.getState(); @@ -2720,21 +2678,20 @@ export default class MetamaskController extends EventEmitter { // subset of state for metamask inpage provider const publicConfigStore = new ObservableStore(); - const selectPublicState = async ({ isUnlocked }) => { - const { chainId, networkVersion } = await this.getProviderNetworkState(); - + const selectPublicState = (chainId, { isUnlocked }) => { return { isUnlocked, chainId, - networkVersion: networkVersion ?? 'loading', + networkVersion: this.deprecatedNetworkId ?? 'loading', }; }; - const updatePublicConfigStore = async (memState) => { + const updatePublicConfigStore = (memState) => { const networkStatus = memState.networksMetadata[memState.selectedNetworkClientId]?.status; + const { chainId } = this.networkController.state.providerConfig; if (networkStatus === NetworkStatus.Available) { - publicConfigStore.putState(await selectPublicState(memState)); + publicConfigStore.putState(selectPublicState(chainId, memState)); } }; @@ -2752,14 +2709,12 @@ export default class MetamaskController extends EventEmitter { * @returns {Promise<{ isUnlocked: boolean, networkVersion: string, chainId: string, accounts: string[] }>} An object with relevant state properties. */ async getProviderState(origin) { - const providerNetworkState = await this.getProviderNetworkState( - this.preferencesController.getUseRequestQueue() ? origin : undefined, - ); - return { isUnlocked: this.isUnlocked(), accounts: await this.getPermittedAccounts(origin), - ...providerNetworkState, + ...this.getProviderNetworkState( + this.preferencesController.getUseRequestQueue() ? origin : undefined, + ), }; } @@ -2769,43 +2724,73 @@ export default class MetamaskController extends EventEmitter { * @param {string} origin - The origin identifier for which network state is requested (default: 'metamask'). * @returns {object} An object containing important network state properties, including chainId and networkVersion. */ - async getProviderNetworkState(origin = METAMASK_DOMAIN) { - const networkClientId = this.controllerMessenger.call( - 'SelectedNetworkController:getNetworkClientIdForDomain', - origin, - ); - - const networkClient = this.controllerMessenger.call( - 'NetworkController:getNetworkClientById', - networkClientId, - ); - - const { chainId } = networkClient.configuration; - - const { completedOnboarding } = this.onboardingController.store.getState(); + getProviderNetworkState(origin = METAMASK_DOMAIN) { + let chainId; + if ( + this.preferencesController.getUseRequestQueue() && + origin !== METAMASK_DOMAIN + ) { + const networkClientId = this.controllerMessenger.call( + 'SelectedNetworkController:getNetworkClientIdForDomain', + origin, + ); - let networkVersion = this.deprecatedNetworkVersions[networkClientId]; - if (!networkVersion && completedOnboarding) { - const ethQuery = new EthQuery(networkClient.provider); - networkVersion = await new Promise((resolve) => { - ethQuery.sendAsync({ method: 'net_version' }, (error, result) => { - if (error) { - console.error(error); - resolve(null); - } else { - resolve(convertNetworkId(result)); - } - }); - }); - this.deprecatedNetworkVersions[networkClientId] = networkVersion; + const networkClient = this.controllerMessenger.call( + 'NetworkController:getNetworkClientById', + networkClientId, + ); + chainId = networkClient.configuration.chainId; + } else { + chainId = this.networkController.state.providerConfig.chainId; } return { chainId, - networkVersion: networkVersion ?? 'loading', + networkVersion: this.deprecatedNetworkId ?? 'loading', }; } + /** + * TODO: Delete when ready to remove `networkVersion` from provider object + * Updates the `deprecatedNetworkId` value + */ + async updateDeprecatedNetworkId() { + try { + this.deprecatedNetworkId = await this.deprecatedGetNetworkId(); + } catch (error) { + console.error(error); + this.deprecatedNetworkId = null; + } + this._notifyChainChange(); + } + + /** + * TODO: Delete when ready to remove `networkVersion` from provider object + * Gets current networkId as returned by `net_version` + * + * @returns {string} The networkId for the current network or null on failure + * @throws Will throw if there is a problem getting the network version + */ + async deprecatedGetNetworkId() { + const ethQuery = this.controllerMessenger.call( + 'NetworkController:getEthQuery', + ); + + if (!ethQuery) { + throw new Error('Provider has not been initialized'); + } + + return new Promise((resolve, reject) => { + ethQuery.sendAsync({ method: 'net_version' }, (error, result) => { + if (error) { + reject(error); + } else { + resolve(convertNetworkId(result)); + } + }); + }); + } + //============================================================================= // EXPOSED TO THE UI SUBSYSTEM //============================================================================= @@ -3020,12 +3005,6 @@ export default class MetamaskController extends EventEmitter { setActiveNetwork: (networkConfigurationId) => { return this.networkController.setActiveNetwork(networkConfigurationId); }, - setNetworkClientIdForDomain: (origin, networkClientId) => { - return this.selectedNetworkController.setNetworkClientIdForDomain( - origin, - networkClientId, - ); - }, rollbackToPreviousProvider: networkController.rollbackToPreviousProvider.bind(networkController), removeNetworkConfiguration: @@ -3141,8 +3120,6 @@ export default class MetamaskController extends EventEmitter { // AppStateController setLastActiveTime: appStateController.setLastActiveTime.bind(appStateController), - setCurrentExtensionPopupId: - appStateController.setCurrentExtensionPopupId.bind(appStateController), setDefaultHomeActiveTabName: appStateController.setDefaultHomeActiveTabName.bind(appStateController), setConnectedStatusPopoverHasBeenShown: @@ -3191,14 +3168,6 @@ export default class MetamaskController extends EventEmitter { appStateController.updateNftDropDownState.bind(appStateController), setFirstTimeUsedNetwork: appStateController.setFirstTimeUsedNetwork.bind(appStateController), - setSwitchedNetworkDetails: - appStateController.setSwitchedNetworkDetails.bind(appStateController), - clearSwitchedNetworkDetails: - appStateController.clearSwitchedNetworkDetails.bind(appStateController), - setSwitchedNetworkNeverShowMessage: - appStateController.setSwitchedNetworkNeverShowMessage.bind( - appStateController, - ), // EnsController tryReverseResolveAddress: @@ -3468,6 +3437,10 @@ export default class MetamaskController extends EventEmitter { swapsController.setSwapsQuotesPollingLimitEnabled.bind(swapsController), // Smart Transactions + setSmartTransactionsOptInStatus: + smartTransactionsController.setOptInState.bind( + smartTransactionsController, + ), fetchSmartTransactionFees: smartTransactionsController.getFees.bind( smartTransactionsController, ), @@ -5326,12 +5299,8 @@ export default class MetamaskController extends EventEmitter { Object.keys(this.connections).forEach((origin) => { Object.values(this.connections[origin]).forEach(async (conn) => { - try { - if (conn.engine) { - conn.engine.emit('notification', await getPayload(origin)); - } - } catch (err) { - console.error(err); + if (conn.engine) { + conn.engine.emit('notification', await getPayload(origin)); } }); }); @@ -5595,14 +5564,6 @@ export default class MetamaskController extends EventEmitter { getTokenStandardAndDetails: this.getTokenStandardAndDetails.bind(this), getTransaction: (id) => this.txController.state.transactions.find((tx) => tx.id === id), - getIsSmartTransaction: () => { - return getIsSmartTransaction(this._getMetaMaskState()); - }, - getSmartTransactionByMinedTxHash: (txHash) => { - return this.smartTransactionsController.getSmartTransactionByMinedTxHash( - txHash, - ); - }, }; return { ...controllerActions, @@ -5876,16 +5837,16 @@ export default class MetamaskController extends EventEmitter { this.permissionLogController.updateAccountsHistory(origin, newAccounts); } - async _notifyChainChange() { + _notifyChainChange() { if (this.preferencesController.getUseRequestQueue()) { - this.notifyAllConnections(async (origin) => ({ + this.notifyAllConnections((origin) => ({ method: NOTIFICATION_NAMES.chainChanged, - params: await this.getProviderNetworkState(origin), + params: this.getProviderNetworkState(origin), })); } else { this.notifyAllConnections({ method: NOTIFICATION_NAMES.chainChanged, - params: await this.getProviderNetworkState(), + params: this.getProviderNetworkState(), }); } } @@ -6049,30 +6010,6 @@ export default class MetamaskController extends EventEmitter { ); } - _publishSmartTransactionHook(transactionMeta) { - const state = this._getMetaMaskState(); - const isSmartTransaction = getIsSmartTransaction(state); - if (!isSmartTransaction) { - // Will cause TransactionController to publish to the RPC provider as normal. - return { transactionHash: undefined }; - } - const featureFlags = getFeatureFlagsByChainId(state); - return submitSmartTransactionHook({ - transactionMeta, - transactionController: this.txController, - smartTransactionsController: this.smartTransactionsController, - controllerMessenger: this.controllerMessenger, - isSmartTransaction, - featureFlags, - }); - } - - _getMetaMaskState() { - return { - metamask: this.getState(), - }; - } - async #onPreferencesControllerStateChange(currentState, previousState) { const { currentLocale } = currentState; const { chainId } = this.networkController.state.providerConfig; diff --git a/app/scripts/migrations/081.ts b/app/scripts/migrations/081.ts index aa7e48a8c9fa..0162a43b6e49 100644 --- a/app/scripts/migrations/081.ts +++ b/app/scripts/migrations/081.ts @@ -91,7 +91,7 @@ function transformState(state: Record) { // Adding the snap name to the wallet_snap permission's caveat value const snapId = permissionName.slice(snapPrefix.length); const caveat = ( - (updatedPermissions.wallet_snap as Record) + (updatedPermissions.wallet_snap as Record) .caveats as unknown[] )[0]; diff --git a/app/scripts/migrations/095.ts b/app/scripts/migrations/095.ts index 6361bba388fc..39128284e03f 100644 --- a/app/scripts/migrations/095.ts +++ b/app/scripts/migrations/095.ts @@ -35,11 +35,7 @@ function migrateData(state: Record): void { removeIncomingTransactionsControllerState(state); } -// TODO: Replace `any` with type -// eslint-disable-next-line @typescript-eslint/no-explicit-any function moveIncomingTransactions(state: Record) { - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any const incomingTransactions: Record = state.IncomingTransactionsController?.incomingTransactions || {}; @@ -50,8 +46,6 @@ function moveIncomingTransactions(state: Record) { const transactions = state.TransactionController?.transactions || {}; const updatedTransactions = Object.values(incomingTransactions).reduce( - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any (result: Record, tx: any) => { result[tx.id] = tx; return result; @@ -65,11 +59,7 @@ function moveIncomingTransactions(state: Record) { }; } -// TODO: Replace `any` with type -// eslint-disable-next-line @typescript-eslint/no-explicit-any function generateLastFetchedBlockNumbers(state: Record) { - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any const incomingTransactions: Record = state.IncomingTransactionsController?.incomingTransactions || {}; diff --git a/app/scripts/migrations/096.ts b/app/scripts/migrations/096.ts index b04b7613e008..749956a726b2 100644 --- a/app/scripts/migrations/096.ts +++ b/app/scripts/migrations/096.ts @@ -4,8 +4,6 @@ import { CHAIN_IDS } from '../../../shared/constants/network'; type VersionedData = { meta: { version: number }; - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any data: Record; }; @@ -36,8 +34,6 @@ export async function migrate( } type NetworkConfiguration = { - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any chainId: Record; }; @@ -52,18 +48,10 @@ function transformState(state: Record) { return state; } const { PreferencesController, NetworkController } = state; - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any const { featureFlags }: Record = PreferencesController; - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any const { showIncomingTransactions }: any = featureFlags; - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any const { networkConfigurations }: Record = NetworkController; - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any const addedNetwork: Record[] = Object.values(networkConfigurations).map( (network) => network.chainId, @@ -75,8 +63,6 @@ function transformState(state: Record) { CHAIN_IDS.SEPOLIA, CHAIN_IDS.LINEA_GOERLI, ]; - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any const allSavedNetworks: Record = [ ...mainNetworks, ...addedNetwork, diff --git a/app/scripts/migrations/097.ts b/app/scripts/migrations/097.ts index 56a11679449d..9e99cf61b40a 100644 --- a/app/scripts/migrations/097.ts +++ b/app/scripts/migrations/097.ts @@ -21,8 +21,6 @@ export async function migrate( return versionedData; } -// TODO: Replace `any` with type -// eslint-disable-next-line @typescript-eslint/no-explicit-any function transformState(state: Record) { const transactionControllerState = state?.TransactionController || {}; const transactions = transactionControllerState?.transactions || {}; diff --git a/app/scripts/migrations/098.ts b/app/scripts/migrations/098.ts index 2fe77148e052..3085827b4c6a 100644 --- a/app/scripts/migrations/098.ts +++ b/app/scripts/migrations/098.ts @@ -25,8 +25,6 @@ export async function migrate( return versionedData; } -// TODO: Replace `any` with type -// eslint-disable-next-line @typescript-eslint/no-explicit-any function transformState(state: Record) { const transactionControllerState = state?.TransactionController || {}; const transactions = transactionControllerState?.transactions || {}; diff --git a/app/scripts/migrations/099.test.ts b/app/scripts/migrations/099.test.ts index 3b98f4f72a26..1feba98e6fa3 100644 --- a/app/scripts/migrations/099.test.ts +++ b/app/scripts/migrations/099.test.ts @@ -72,8 +72,6 @@ describe('migration #99', () => { const newStorage = await migrate(oldStorage); - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any const migratedTransactions = (newStorage.data.TransactionController as any) .transactions; diff --git a/app/scripts/migrations/099.ts b/app/scripts/migrations/099.ts index 38018c97919d..9464d19a64df 100644 --- a/app/scripts/migrations/099.ts +++ b/app/scripts/migrations/099.ts @@ -24,8 +24,6 @@ export async function migrate( return versionedData; } -// TODO: Replace `any` with type -// eslint-disable-next-line @typescript-eslint/no-explicit-any function transformState(state: Record) { const transactionControllerState = state?.TransactionController || {}; const transactions = transactionControllerState?.transactions || {}; @@ -35,8 +33,6 @@ function transformState(state: Record) { } const newTxs = Object.keys(transactions).reduce( - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any (txs: { [key: string]: any }, oldTransactionId) => { // Clone the transaction const transaction = cloneDeep(transactions[oldTransactionId]); diff --git a/app/scripts/migrations/100.ts b/app/scripts/migrations/100.ts index 89dbe0d5670d..c9b4a99afceb 100644 --- a/app/scripts/migrations/100.ts +++ b/app/scripts/migrations/100.ts @@ -25,8 +25,6 @@ export async function migrate( return versionedData; } -// TODO: Replace `any` with type -// eslint-disable-next-line @typescript-eslint/no-explicit-any function transformState(state: Record) { const addressBook = state?.AddressBookController?.addressBook ?? {}; const names = state?.NameController?.names?.ethereumAddress ?? {}; diff --git a/app/scripts/migrations/102.ts b/app/scripts/migrations/102.ts index a1fde4f27f7f..820e67605251 100644 --- a/app/scripts/migrations/102.ts +++ b/app/scripts/migrations/102.ts @@ -23,8 +23,6 @@ export async function migrate( return versionedData; } -// TODO: Replace `any` with type -// eslint-disable-next-line @typescript-eslint/no-explicit-any function transformState(state: Record) { const transactionControllerState = state?.TransactionController || {}; const transactions = transactionControllerState?.transactions || {}; @@ -34,8 +32,6 @@ function transformState(state: Record) { } const newTxs = Object.keys(transactions).reduce( - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any (txs: { [key: string]: any }, txId) => { // Clone the transaction const transaction = cloneDeep(transactions[txId]); diff --git a/app/scripts/migrations/104.ts b/app/scripts/migrations/104.ts index 38ab3c0f57c8..340d167ccd5f 100644 --- a/app/scripts/migrations/104.ts +++ b/app/scripts/migrations/104.ts @@ -22,8 +22,6 @@ export async function migrate( return versionedData; } -// TODO: Replace `any` with type -// eslint-disable-next-line @typescript-eslint/no-explicit-any function transformState(state: Record) { const transactionControllerState = state?.TransactionController; @@ -34,8 +32,6 @@ function transformState(state: Record) { const transactionsObject = transactionControllerState?.transactions || {}; const transactionsArray = Object.values(transactionsObject).sort( - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any (a: any, b: any) => (a.time > b.time ? -1 : 1), // Descending ); diff --git a/app/scripts/migrations/105.ts b/app/scripts/migrations/105.ts index 482bc5a73d15..10be55e69e0a 100644 --- a/app/scripts/migrations/105.ts +++ b/app/scripts/migrations/105.ts @@ -48,8 +48,6 @@ function migrateData(state: Record): void { createSelectedAccountForAccountsController(state); } -// TODO: Replace `any` with type -// eslint-disable-next-line @typescript-eslint/no-explicit-any function createDefaultAccountsController(state: Record) { state.AccountsController = { internalAccounts: { @@ -60,8 +58,6 @@ function createDefaultAccountsController(state: Record) { } function createInternalAccountsForAccountsController( - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any state: Record, ) { const identities: { @@ -102,8 +98,6 @@ function createInternalAccountsForAccountsController( } function createSelectedAccountForAccountsController( - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any state: Record, ) { const selectedAddress = state.PreferencesController?.selectedAddress; diff --git a/app/scripts/migrations/108.ts b/app/scripts/migrations/108.ts index 4b12de5f9004..1ea75957b73a 100644 --- a/app/scripts/migrations/108.ts +++ b/app/scripts/migrations/108.ts @@ -27,8 +27,6 @@ export async function migrate( return versionedData; } -// TODO: Replace `any` with type -// eslint-disable-next-line @typescript-eslint/no-explicit-any function transformState(state: Record) { const addressBook = state?.AddressBookController?.addressBook ?? {}; const names = state?.NameController?.names?.ethereumAddress ?? {}; diff --git a/app/scripts/migrations/109.ts b/app/scripts/migrations/109.ts index 13e268b8e061..5c40b07d4b09 100644 --- a/app/scripts/migrations/109.ts +++ b/app/scripts/migrations/109.ts @@ -27,8 +27,6 @@ export async function migrate( return versionedData; } -// TODO: Replace `any` with type -// eslint-disable-next-line @typescript-eslint/no-explicit-any function transformState(state: Record) { const identities: PreferencesControllerState['identities'] = state?.PreferencesController?.identities ?? {}; diff --git a/app/scripts/migrations/110.ts b/app/scripts/migrations/110.ts index 7a677ed8b9cd..bc941ac87695 100644 --- a/app/scripts/migrations/110.ts +++ b/app/scripts/migrations/110.ts @@ -35,8 +35,6 @@ export async function migrate( return versionedData; } -// TODO: Replace `any` with type -// eslint-disable-next-line @typescript-eslint/no-explicit-any function transformState(state: Record) { const NetworkController = state?.NetworkController || {}; const provider = NetworkController?.providerConfig || {}; diff --git a/app/scripts/migrations/111.ts b/app/scripts/migrations/111.ts index 1a06e655cabb..a45c24fa168f 100644 --- a/app/scripts/migrations/111.ts +++ b/app/scripts/migrations/111.ts @@ -28,8 +28,6 @@ export async function migrate( return versionedData; } -// TODO: Replace `any` with type -// eslint-disable-next-line @typescript-eslint/no-explicit-any function transformState(state: Record) { if (!hasProperty(state, 'SelectedNetworkController')) { return state; diff --git a/app/scripts/migrations/112.ts b/app/scripts/migrations/112.ts index 519be68c9ca3..6313462be199 100644 --- a/app/scripts/migrations/112.ts +++ b/app/scripts/migrations/112.ts @@ -26,8 +26,6 @@ export async function migrate( return versionedData; } -// TODO: Replace `any` with type -// eslint-disable-next-line @typescript-eslint/no-explicit-any function transformState(state: Record) { if (!hasProperty(state, 'SelectedNetworkController')) { return state; diff --git a/app/scripts/migrations/114.ts b/app/scripts/migrations/114.ts index bed9c70a8850..2288dc84d612 100644 --- a/app/scripts/migrations/114.ts +++ b/app/scripts/migrations/114.ts @@ -26,8 +26,6 @@ export async function migrate( return versionedData; } -// TODO: Replace `any` with type -// eslint-disable-next-line @typescript-eslint/no-explicit-any function transformState(state: Record) { if (!hasProperty(state, 'PreferencesController')) { return state; diff --git a/app/scripts/snaps/preinstalled-snaps.ts b/app/scripts/snaps/preinstalled-snaps.ts deleted file mode 100644 index 0a014c350c21..000000000000 --- a/app/scripts/snaps/preinstalled-snaps.ts +++ /dev/null @@ -1,8 +0,0 @@ -import type { PreinstalledSnap } from '@metamask/snaps-controllers'; -import MessageSigningSnap from '@metamask/message-signing-snap/dist/preinstalled-snap.json'; - -const PREINSTALLED_SNAPS: readonly PreinstalledSnap[] = Object.freeze([ - MessageSigningSnap as PreinstalledSnap, -]); - -export default PREINSTALLED_SNAPS; diff --git a/app/trezor-usb-permissions.html b/app/trezor-usb-permissions.html index fa116f591fc2..b3c6ce72b11c 100644 --- a/app/trezor-usb-permissions.html +++ b/app/trezor-usb-permissions.html @@ -1,9 +1,9 @@ + + - - TrezorConnect | Trezor @@ -31,5 +31,5 @@ - + diff --git a/builds.yml b/builds.yml index 446ce72139ec..bf949e39bc78 100644 --- a/builds.yml +++ b/builds.yml @@ -27,7 +27,7 @@ buildTypes: - SEGMENT_WRITE_KEY_REF: SEGMENT_PROD_WRITE_KEY - ALLOW_LOCAL_SNAPS: false - REQUIRE_SNAPS_ALLOWLIST: true - - IFRAME_EXECUTION_ENVIRONMENT_URL: https://execution.metamask.io/iframe/5.0.4/index.html + - IFRAME_EXECUTION_ENVIRONMENT_URL: https://execution.metamask.io/iframe/5.0.3/index.html - ACCOUNT_SNAPS_DIRECTORY_URL: https://snaps.metamask.io/account-management # Main build uses the default browser manifest manifestOverrides: false @@ -64,7 +64,7 @@ buildTypes: - SEGMENT_FLASK_WRITE_KEY - ALLOW_LOCAL_SNAPS: true - REQUIRE_SNAPS_ALLOWLIST: false - - IFRAME_EXECUTION_ENVIRONMENT_URL: https://execution.metamask.io/iframe/5.0.4/index.html + - IFRAME_EXECUTION_ENVIRONMENT_URL: https://execution.metamask.io/iframe/5.0.3/index.html - SUPPORT_LINK: https://metamask-flask.zendesk.com/hc - SUPPORT_REQUEST_LINK: https://metamask-flask.zendesk.com/hc/en-us/requests/new - INFURA_ENV_KEY_REF: INFURA_FLASK_PROJECT_ID @@ -85,7 +85,7 @@ buildTypes: - SEGMENT_FLASK_WRITE_KEY - ALLOW_LOCAL_SNAPS: true - REQUIRE_SNAPS_ALLOWLIST: false - - IFRAME_EXECUTION_ENVIRONMENT_URL: https://execution.metamask.io/iframe/5.0.4/index.html + - IFRAME_EXECUTION_ENVIRONMENT_URL: https://execution.metamask.io/iframe/5.0.3/index.html - SUPPORT_LINK: https://metamask-flask.zendesk.com/hc - SUPPORT_REQUEST_LINK: https://metamask-flask.zendesk.com/hc/en-us/requests/new - INFURA_ENV_KEY_REF: INFURA_FLASK_PROJECT_ID @@ -108,7 +108,7 @@ buildTypes: - SEGMENT_WRITE_KEY_REF: SEGMENT_MMI_WRITE_KEY - ALLOW_LOCAL_SNAPS: false - REQUIRE_SNAPS_ALLOWLIST: true - - IFRAME_EXECUTION_ENVIRONMENT_URL: https://execution.metamask.io/iframe/5.0.4/index.html + - IFRAME_EXECUTION_ENVIRONMENT_URL: https://execution.metamask.io/iframe/5.0.3/index.html - MMI_CONFIGURATION_SERVICE_URL: https://configuration.metamask-institutional.io/v2/configuration/default - SUPPORT_LINK: https://mmi-support.metamask.io/hc/en-us - SUPPORT_REQUEST_LINK: https://mmi-support.metamask.io/hc/en-us/requests/new @@ -144,6 +144,7 @@ features: env: - BLOCKAID_FILE_CDN: static.cx.metamask.io/api/v1/confirmations/ppom - BLOCKAID_PUBLIC_KEY: 066ad3e8af5583385e312c156d238055215d5f25247c1e91055afa756cb98a88 + conf-redesign: ### # Build Type code extensions. Things like different support links, warning pages, banners ### @@ -197,14 +198,6 @@ env: # This variable is read by Trezor's source and breaks build if not included - ASSET_PREFIX: null - ### - # Notifications Feature - ### - - AUTH_API: https://authentication.api.cx.metamask.io - - OIDC_API: https://oidc.api.cx.metamask.io - - OIDC_CLIENT_ID: 1132f10a-b4e5-4390-a5f2-d9c6022db564 - - OIDC_GRANT_TYPE: urn:ietf:params:oauth:grant-type:jwt-bearer - ### # API keys to 3rd party services ### @@ -271,12 +264,6 @@ env: # Determines if feature flagged Multichain Transactions should be used - TRANSACTION_MULTICHAIN: '' - # Used to enable confirmation redesigned pages - - ENABLE_CONFIRMATION_REDESIGN: '' - - # Enables the notifications feature within the build: - - NOTIFICATIONS: '' - ### # Meta variables ### diff --git a/development/build/scripts.js b/development/build/scripts.js index 1baa6e9d7f27..9e0ebd53715c 100644 --- a/development/build/scripts.js +++ b/development/build/scripts.js @@ -4,6 +4,7 @@ const { callbackify } = require('util'); const path = require('path'); const { writeFileSync, readFileSync, unlinkSync } = require('fs'); const EventEmitter = require('events'); +const assert = require('assert'); const gulp = require('gulp'); const watch = require('gulp-watch'); const Vinyl = require('vinyl'); @@ -29,9 +30,9 @@ const terser = require('terser'); const bifyModuleGroups = require('bify-module-groups'); +const phishingWarningManifest = require('@metamask/phishing-warning/package.json'); const { streamFlatMap } = require('../stream-flat-map'); -const { setEnvironmentVariables } = require('./set-environment-variables'); -const { BUILD_TARGETS } = require('./constants'); +const { BUILD_TARGETS, ENVIRONMENT } = require('./constants'); const { getConfig } = require('./config'); const { isDevBuild, @@ -40,6 +41,8 @@ const { logError, wrapAgainstScuttling, getBuildName, + getBuildAppId, + getBuildIcon, makeSelfInjecting, } = require('./utils'); @@ -55,7 +58,7 @@ const { // map dist files to bag of needed native APIs against LM scuttling const scuttlingConfigBase = { - 'scripts/sentry-install.js': { + 'sentry-install.js': { // globals sentry need to function window: '', navigator: '', @@ -93,12 +96,128 @@ const mv3ScuttlingConfig = { ...scuttlingConfigBase }; const standardScuttlingConfig = { ...scuttlingConfigBase, - 'scripts/sentry-install.js': { - ...scuttlingConfigBase['scripts/sentry-install.js'], + 'sentry-install.js': { + ...scuttlingConfigBase['sentry-install.js'], document: '', }, }; +/** + * Get the appropriate Infura project ID. + * + * @param {object} options - The Infura project ID options. + * @param {string} options.buildType - The current build type. + * @param {ENVIRONMENT[keyof ENVIRONMENT]} options.environment - The build environment. + * @param {boolean} options.testing - Whether this is a test build or not. + * @param options.variables + * @returns {string} The Infura project ID. + */ +function getInfuraProjectId({ buildType, variables, environment, testing }) { + const EMPTY_PROJECT_ID = '00000000000000000000000000000000'; + if (testing) { + return EMPTY_PROJECT_ID; + } else if (environment !== ENVIRONMENT.PRODUCTION) { + // Skip validation because this is unset on PRs from forks. + // For forks, return empty project ID if we don't have one. + if ( + !variables.isDefined('INFURA_PROJECT_ID') && + environment === ENVIRONMENT.PULL_REQUEST + ) { + return EMPTY_PROJECT_ID; + } + return variables.get('INFURA_PROJECT_ID'); + } + /** @type {string|undefined} */ + const infuraKeyReference = variables.get('INFURA_ENV_KEY_REF'); + assert( + typeof infuraKeyReference === 'string' && infuraKeyReference.length > 0, + `Build type "${buildType}" has improperly set INFURA_ENV_KEY_REF in builds.yml. Current value: "${infuraKeyReference}"`, + ); + /** @type {string|undefined} */ + const infuraProjectId = variables.get(infuraKeyReference); + assert( + typeof infuraProjectId === 'string' && infuraProjectId.length > 0, + `Infura Project ID environmental variable "${infuraKeyReference}" is set improperly.`, + ); + return infuraProjectId; +} + +/** + * Get the appropriate Segment write key. + * + * @param {object} options - The Segment write key options. + * @param {string} options.buildType - The current build type. + * @param {keyof ENVIRONMENT} options.environment - The current build environment. + * @param {import('../lib/variables').Variables} options.variables - Object containing all variables that modify the build pipeline + * @returns {string} The Segment write key. + */ +function getSegmentWriteKey({ buildType, variables, environment }) { + if (environment !== ENVIRONMENT.PRODUCTION) { + // Skip validation because this is unset on PRs from forks, and isn't necessary for development builds. + return variables.get('SEGMENT_WRITE_KEY'); + } + + const segmentKeyReference = variables.get('SEGMENT_WRITE_KEY_REF'); + assert( + typeof segmentKeyReference === 'string' && segmentKeyReference.length > 0, + `Build type "${buildType}" has improperly set SEGMENT_WRITE_KEY_REF in builds.yml. Current value: "${segmentKeyReference}"`, + ); + + const segmentWriteKey = variables.get(segmentKeyReference); + assert( + typeof segmentWriteKey === 'string' && segmentWriteKey.length > 0, + `Segment Write Key environmental variable "${segmentKeyReference}" is set improperly.`, + ); + return segmentWriteKey; +} + +/** + * Get the URL for the phishing warning page, if it has been set. + * + * @param {object} options - The phishing warning page options. + * @param {boolean} options.testing - Whether this is a test build or not. + * @param {import('../lib/variables').Variables} options.variables - Object containing all variables that modify the build pipeline + * @returns {string} The URL for the phishing warning page, or `undefined` if no URL is set. + */ +function getPhishingWarningPageUrl({ variables, testing }) { + let phishingWarningPageUrl = variables.get('PHISHING_WARNING_PAGE_URL'); + + assert( + phishingWarningPageUrl === null || + typeof phishingWarningPageUrl === 'string', + ); + if (phishingWarningPageUrl === null) { + phishingWarningPageUrl = testing + ? 'http://localhost:9999/' + : `https://metamask.github.io/phishing-warning/v${phishingWarningManifest.version}/`; + } + + // We add a hash/fragment to the URL dynamically, so we need to ensure it + // has a valid pathname to append a hash to. + const normalizedUrl = phishingWarningPageUrl.endsWith('/') + ? phishingWarningPageUrl + : `${phishingWarningPageUrl}/`; + + let phishingWarningPageUrlObject; + try { + // eslint-disable-next-line no-new + phishingWarningPageUrlObject = new URL(normalizedUrl); + } catch (error) { + throw new Error( + `Invalid phishing warning page URL: '${normalizedUrl}'`, + error, + ); + } + if (phishingWarningPageUrlObject.hash) { + // The URL fragment must be set dynamically + throw new Error( + `URL fragment not allowed in phishing warning page URL: '${normalizedUrl}'`, + ); + } + + return normalizedUrl; +} + const noopWriteStream = through.obj((_file, _fileEncoding, callback) => callback(), ); @@ -281,7 +400,7 @@ function createScriptTasks({ browserPlatforms, buildTarget, buildType, - destFilepath: `scripts/${label}.js`, + destFilepath: `${label}.js`, entryFilepath: `./app/scripts/${label}.js`, ignoredFiles, label, @@ -305,7 +424,7 @@ function createScriptTasks({ browserPlatforms, buildTarget, buildType, - destFilepath: `scripts/${label}.js`, + destFilepath: `${label}.js`, entryFilepath: `./app/scripts/${label}.js`, ignoredFiles, label, @@ -333,7 +452,7 @@ function createScriptTasks({ buildTarget, buildType, browserPlatforms, - destFilepath: `scripts/${inpage}.js`, + destFilepath: `${inpage}.js`, entryFilepath: `./app/scripts/${inpage}.js`, label: inpage, ignoredFiles, @@ -348,29 +467,25 @@ function createScriptTasks({ if (process.env.ENABLE_MV3) { return; } - // stringify scripts/inpage.js into itself, and then make it inject itself into the page + // stringify inpage.js into itself, and then make it inject itself into the page browserPlatforms.forEach((browser) => { makeSelfInjecting( - path.join(__dirname, `../../dist/${browser}/scripts/${inpage}.js`), + path.join(__dirname, `../../dist/${browser}/${inpage}.js`), ); }); - // delete the scripts/inpage.js source map, as it no longer represents - // scripts/inpage.js and so `yarn source-map-explorer` can't handle it. - // It's also not useful anyway, as scripts/inpage.js is injected as a - // `script.textContent`, and not tracked in Sentry or browsers devtools - // anyway. + // delete the inpage.js source map, as it no longer represents inpage.js + // and so `yarn source-map-explorer` can't handle it. It's also not + // useful anyway, as inpage.js is injected as a `script.textContent`, + // and not tracked in Sentry or browsers devtools anyway. unlinkSync( - path.join( - __dirname, - `../../dist/sourcemaps/scripts/${inpage}.js.map`, - ), + path.join(__dirname, `../../dist/sourcemaps/${inpage}.js.map`), ); }, createNormalBundle({ buildTarget, buildType, browserPlatforms, - destFilepath: `scripts/${contentscript}.js`, + destFilepath: `${contentscript}.js`, entryFilepath: `./app/scripts/${contentscript}.js`, label: contentscript, ignoredFiles, @@ -444,7 +559,7 @@ async function createManifestV3AppInitializationBundle({ browserPlatforms: mv3BrowserPlatforms, buildTarget, buildType, - destFilepath: 'scripts/app-init.js', + destFilepath: 'app-init.js', entryFilepath: './app/scripts/app-init.js', extraEnvironmentVariables, ignoredFiles, @@ -520,16 +635,12 @@ function createFactoredBuild({ const environment = getEnvironment({ buildTarget }); const config = await getConfig(buildType, environment); const { variables, activeBuild } = config; - setEnvironmentVariables({ - isDevBuild: reloadOnChange, - isTestBuild: isTestBuild(buildTarget), - buildName: getBuildName({ - environment, - buildType, - }), + await setEnvironmentVariables({ + buildTarget, buildType, environment, variables, + activeBuild, version, }); const features = { @@ -607,7 +718,7 @@ function createFactoredBuild({ // add lavamoat policy loader file to packer output moduleGroupPackerStream.push( new Vinyl({ - path: 'scripts/policy-load.js', + path: 'policy-load.js', contents: lavapack.makePolicyLoaderStream(lavamoatOpts), }), ); @@ -789,16 +900,12 @@ function createNormalBundle({ const environment = getEnvironment({ buildTarget }); const config = await getConfig(buildType, environment); const { activeBuild, variables } = config; - setEnvironmentVariables({ - buildName: getBuildName({ - environment, - buildType, - }), - isDevBuild: devMode, - isTestBuild: isTestBuild(buildTarget), + await setEnvironmentVariables({ + buildTarget, buildType, variables, environment, + activeBuild, version, }); Object.entries(extraEnvironmentVariables ?? {}).forEach(([key, value]) => @@ -1091,6 +1198,70 @@ async function createBundle(buildConfiguration, { reloadOnChange }) { } } +/** + * Sets environment variables to inject in the current build. + * + * @param {object} options - Build options. + * @param {BUILD_TARGETS} options.buildTarget - The current build target. + * @param {string} options.buildType - The current build type (e.g. "main", + * "flask", etc.). + * @param {string} options.version - The current version of the extension. + * @param options.activeBuild + * @param options.variables + * @param options.environment + */ +async function setEnvironmentVariables({ + buildTarget, + buildType, + activeBuild, + environment, + variables, + version, +}) { + const devMode = isDevBuild(buildTarget); + const testing = isTestBuild(buildTarget); + + variables.set({ + DEBUG: devMode || testing ? variables.getMaybe('DEBUG') : undefined, + EIP_4337_ENTRYPOINT: + variables.getMaybe('EIP_4337_ENTRYPOINT') || + (testing ? '0x18b06605539dc02ecD3f7AB314e38eB7c1dA5c9b' : undefined), + IN_TEST: testing, + INFURA_PROJECT_ID: getInfuraProjectId({ + buildType, + activeBuild, + variables, + environment, + testing, + }), + METAMASK_DEBUG: devMode || variables.getMaybe('METAMASK_DEBUG') === true, + METAMASK_BUILD_NAME: getBuildName({ + environment, + buildType, + }), + METAMASK_BUILD_APP_ID: getBuildAppId({ + buildType, + }), + METAMASK_BUILD_ICON: getBuildIcon({ + buildType, + }), + METAMASK_ENVIRONMENT: environment, + METAMASK_VERSION: version, + METAMASK_BUILD_TYPE: buildType, + NODE_ENV: devMode ? ENVIRONMENT.DEVELOPMENT : ENVIRONMENT.PRODUCTION, + PHISHING_WARNING_PAGE_URL: getPhishingWarningPageUrl({ + variables, + testing, + }), + SEGMENT_WRITE_KEY: getSegmentWriteKey({ + buildType, + activeBuild, + variables, + environment, + }), + }); +} + function renderJavaScriptLoader({ groupSet, commonSet, @@ -1109,22 +1280,18 @@ function renderJavaScriptLoader({ ); const securityScripts = applyLavaMoat - ? [ - './scripts/runtime-lavamoat.js', - './scripts/lockdown-more.js', - './scripts/policy-load.js', - ] + ? ['./runtime-lavamoat.js', './lockdown-more.js', './policy-load.js'] : [ - './scripts/lockdown-install.js', - './scripts/lockdown-run.js', - './scripts/lockdown-more.js', - './scripts/runtime-cjs.js', + './lockdown-install.js', + './lockdown-run.js', + './lockdown-more.js', + './runtime-cjs.js', ]; const requiredScripts = [ - './scripts/snow.js', - './scripts/use-snow.js', - './scripts/sentry-install.js', + './snow.js', + './use-snow.js', + './sentry-install.js', ...securityScripts, ...jsBundles, ]; diff --git a/development/build/set-environment-variables.js b/development/build/set-environment-variables.js deleted file mode 100644 index 83f0016f5f3e..000000000000 --- a/development/build/set-environment-variables.js +++ /dev/null @@ -1,215 +0,0 @@ -const { readFileSync } = require('node:fs'); -const assert = require('node:assert'); -const { ENVIRONMENT } = require('./constants'); - -/** - * Sets environment variables to inject in the current build. - * - * @param {object} options - Build options. - * @param {string} options.buildName - The name of the build. - * @param {boolean} options.isDevBuild - Whether the build is a development build. - * @param {boolean} options.isTestBuild - Whether the build is a test build. - * @param {string} options.buildType - The current build type (e.g. "main", - * "flask", etc.). - * @param {string} options.version - The current version of the extension. - * @param {import('../lib/variables').Variables} options.variables - * @param {ENVIRONMENT[keyof ENVIRONMENT]} options.environment - The build environment. - */ -module.exports.setEnvironmentVariables = function setEnvironmentVariables({ - buildName, - isDevBuild, - isTestBuild, - buildType, - environment, - variables, - version, -}) { - variables.set({ - DEBUG: isDevBuild || isTestBuild ? variables.getMaybe('DEBUG') : undefined, - EIP_4337_ENTRYPOINT: - variables.getMaybe('EIP_4337_ENTRYPOINT') || - (isTestBuild ? '0x18b06605539dc02ecD3f7AB314e38eB7c1dA5c9b' : undefined), - IN_TEST: isTestBuild, - INFURA_PROJECT_ID: getInfuraProjectId({ - buildType, - variables, - environment, - testing: isTestBuild, - }), - METAMASK_DEBUG: isDevBuild || variables.getMaybe('METAMASK_DEBUG') === true, - METAMASK_BUILD_NAME: buildName, - METAMASK_BUILD_APP_ID: getBuildAppId({ - buildType, - }), - METAMASK_BUILD_ICON: getBuildIcon({ - buildType, - }), - METAMASK_ENVIRONMENT: environment, - METAMASK_VERSION: version, - METAMASK_BUILD_TYPE: buildType, - NODE_ENV: isDevBuild ? ENVIRONMENT.DEVELOPMENT : ENVIRONMENT.PRODUCTION, - PHISHING_WARNING_PAGE_URL: getPhishingWarningPageUrl({ - variables, - testing: isTestBuild, - }), - SEGMENT_WRITE_KEY: getSegmentWriteKey({ - buildType, - variables, - environment, - }), - }); -}; - -const BUILD_TYPES_TO_SVG_LOGO_PATH = { - main: './app/images/logo/metamask-fox.svg', - beta: './app/build-types/beta/images/logo/metamask-fox.svg', - flask: './app/build-types/flask/images/logo/metamask-fox.svg', - mmi: './app/build-types/mmi/images/logo/mmi-logo.svg', - desktop: './app/build-types/desktop/images/logo/metamask-fox.svg', -}; - -/** - * Get the image data uri for the svg icon for the current build. - * - * @param {object} options - The build options. - * @param {string} options.buildType - The build type of the current build. - * @returns {string} The image data uri for the icon. - */ -function getBuildIcon({ buildType }) { - const svgLogoPath = - BUILD_TYPES_TO_SVG_LOGO_PATH[buildType] || - BUILD_TYPES_TO_SVG_LOGO_PATH.main; - // encode as base64 as its more space-efficient for most SVGs than a data uri - return `data:image/svg+xml;base64,${readFileSync(svgLogoPath, 'base64')}`; -} - -/** - * Get the app ID for the current build. Should be valid reverse FQDN. - * - * @param {object} options - The build options. - * @param {string} options.buildType - The build type of the current build. - * @returns {string} The build app ID. - */ -function getBuildAppId({ buildType }) { - const baseDomain = 'io.metamask'; - return buildType === 'main' ? baseDomain : `${baseDomain}.${buildType}`; -} - -/** - * Get the appropriate Infura project ID. - * - * @param {object} options - The Infura project ID options. - * @param {string} options.buildType - The current build type. - * @param {ENVIRONMENT[keyof ENVIRONMENT]} options.environment - The build environment. - * @param {boolean} options.testing - Whether this is a test build or not. - * @param options.variables - * @returns {string} The Infura project ID. - */ -function getInfuraProjectId({ buildType, variables, environment, testing }) { - const EMPTY_PROJECT_ID = '00000000000000000000000000000000'; - if (testing) { - return EMPTY_PROJECT_ID; - } else if (environment !== ENVIRONMENT.PRODUCTION) { - // Skip validation because this is unset on PRs from forks. - // For forks, return empty project ID if we don't have one. - if ( - !variables.isDefined('INFURA_PROJECT_ID') && - environment === ENVIRONMENT.PULL_REQUEST - ) { - return EMPTY_PROJECT_ID; - } - return variables.get('INFURA_PROJECT_ID'); - } - /** @type {string|undefined} */ - const infuraKeyReference = variables.get('INFURA_ENV_KEY_REF'); - assert( - typeof infuraKeyReference === 'string' && infuraKeyReference.length > 0, - `Build type "${buildType}" has improperly set INFURA_ENV_KEY_REF in builds.yml. Current value: "${infuraKeyReference}"`, - ); - /** @type {string|undefined} */ - const infuraProjectId = variables.get(infuraKeyReference); - assert( - typeof infuraProjectId === 'string' && infuraProjectId.length > 0, - `Infura Project ID environmental variable "${infuraKeyReference}" is set improperly.`, - ); - return infuraProjectId; -} - -/** - * Get the appropriate Segment write key. - * - * @param {object} options - The Segment write key options. - * @param {string} options.buildType - The current build type. - * @param {keyof ENVIRONMENT} options.environment - The current build environment. - * @param {import('../lib/variables').Variables} options.variables - Object containing all variables that modify the build pipeline - * @returns {string} The Segment write key. - */ -function getSegmentWriteKey({ buildType, variables, environment }) { - if (environment !== ENVIRONMENT.PRODUCTION) { - // Skip validation because this is unset on PRs from forks, and isn't necessary for development builds. - return variables.get('SEGMENT_WRITE_KEY'); - } - - const segmentKeyReference = variables.get('SEGMENT_WRITE_KEY_REF'); - assert( - typeof segmentKeyReference === 'string' && segmentKeyReference.length > 0, - `Build type "${buildType}" has improperly set SEGMENT_WRITE_KEY_REF in builds.yml. Current value: "${segmentKeyReference}"`, - ); - - const segmentWriteKey = variables.get(segmentKeyReference); - assert( - typeof segmentWriteKey === 'string' && segmentWriteKey.length > 0, - `Segment Write Key environmental variable "${segmentKeyReference}" is set improperly.`, - ); - return segmentWriteKey; -} - -/** - * Get the URL for the phishing warning page, if it has been set. - * - * @param {object} options - The phishing warning page options. - * @param {boolean} options.testing - Whether this is a test build or not. - * @param {import('../lib/variables').Variables} options.variables - Object containing all variables that modify the build pipeline - * @returns {string} The URL for the phishing warning page, or `undefined` if no URL is set. - */ -function getPhishingWarningPageUrl({ variables, testing }) { - let phishingWarningPageUrl = variables.get('PHISHING_WARNING_PAGE_URL'); - - assert( - phishingWarningPageUrl === null || - typeof phishingWarningPageUrl === 'string', - ); - if (phishingWarningPageUrl === null) { - phishingWarningPageUrl = testing - ? 'http://localhost:9999/' - : `https://metamask.github.io/phishing-warning/v${ - // eslint-disable-next-line node/global-require - require('@metamask/phishing-warning/package.json').version - }/`; - } - - // We add a hash/fragment to the URL dynamically, so we need to ensure it - // has a valid pathname to append a hash to. - const normalizedUrl = phishingWarningPageUrl.endsWith('/') - ? phishingWarningPageUrl - : `${phishingWarningPageUrl}/`; - - let phishingWarningPageUrlObject; - try { - // eslint-disable-next-line no-new - phishingWarningPageUrlObject = new URL(normalizedUrl); - } catch (error) { - throw new Error( - `Invalid phishing warning page URL: '${normalizedUrl}'`, - error, - ); - } - if (phishingWarningPageUrlObject.hash) { - // The URL fragment must be set dynamically - throw new Error( - `URL fragment not allowed in phishing warning page URL: '${normalizedUrl}'`, - ); - } - - return normalizedUrl; -} diff --git a/development/build/static.js b/development/build/static.js index 54bef8f980ad..c8eb7d6d5ef2 100644 --- a/development/build/static.js +++ b/development/build/static.js @@ -155,46 +155,46 @@ function getCopyTargets( src: shouldIncludeSnow ? `./node_modules/@lavamoat/snow/snow.prod.js` : EMPTY_JS_FILE, - dest: `scripts/snow.js`, + dest: `snow.js`, }, { src: shouldIncludeSnow ? `./app/scripts/use-snow.js` : EMPTY_JS_FILE, - dest: `scripts/use-snow.js`, + dest: `use-snow.js`, }, { src: shouldIncludeLockdown ? getPathInsideNodeModules('ses', 'dist/lockdown.umd.min.js') : EMPTY_JS_FILE, - dest: `scripts/lockdown-install.js`, + dest: `lockdown-install.js`, }, { src: './app/scripts/init-globals.js', - dest: 'scripts/init-globals.js', + dest: 'init-globals.js', }, { src: './app/scripts/load-app.js', - dest: 'scripts/load-app.js', + dest: 'load-app.js', }, { src: shouldIncludeLockdown ? `./app/scripts/lockdown-run.js` : EMPTY_JS_FILE, - dest: `scripts/lockdown-run.js`, + dest: `lockdown-run.js`, }, { src: shouldIncludeLockdown ? `./app/scripts/lockdown-more.js` : EMPTY_JS_FILE, - dest: `scripts/lockdown-more.js`, + dest: `lockdown-more.js`, }, { src: getPathInsideNodeModules('@lavamoat/lavapack', 'src/runtime-cjs.js'), - dest: `scripts/runtime-cjs.js`, + dest: `runtime-cjs.js`, pattern: '', }, { src: getPathInsideNodeModules('@lavamoat/lavapack', 'src/runtime.js'), - dest: `scripts/runtime-lavamoat.js`, + dest: `runtime-lavamoat.js`, pattern: '', }, { diff --git a/development/build/utils.js b/development/build/utils.js index 746290fb8e2b..07349c88db28 100644 --- a/development/build/utils.js +++ b/development/build/utils.js @@ -5,6 +5,14 @@ const { capitalize } = require('lodash'); const { loadBuildTypesConfig } = require('../lib/build-type'); const { BUILD_TARGETS, ENVIRONMENT } = require('./constants'); +const BUILD_TYPES_TO_SVG_LOGO_PATH = { + main: './app/images/logo/metamask-fox.svg', + beta: './app/build-types/beta/images/logo/metamask-fox.svg', + flask: './app/build-types/flask/images/logo/metamask-fox.svg', + mmi: './app/build-types/mmi/images/logo/mmi-logo.svg', + desktop: './app/build-types/desktop/images/logo/metamask-fox.svg', +}; + /** * Returns whether the current build is a development build or not. * @@ -253,6 +261,32 @@ function getBuildName({ return name; } +/** + * Get the app ID for the current build. Should be valid reverse FQDN. + * + * @param {object} options - The build options. + * @param {string} options.buildType - The build type of the current build. + * @returns {string} The build app ID. + */ +function getBuildAppId({ buildType }) { + const baseDomain = 'io.metamask'; + return buildType === 'main' ? baseDomain : `${baseDomain}.${buildType}`; +} + +/** + * Get the image data uri for the svg icon for the current build. + * + * @param {object} options - The build options. + * @param {string} options.buildType - The build type of the current build. + * @returns {string} The image data uri for the icon. + */ +function getBuildIcon({ buildType }) { + const svgLogoPath = + BUILD_TYPES_TO_SVG_LOGO_PATH[buildType] || + BUILD_TYPES_TO_SVG_LOGO_PATH.main; + const svg = readFileSync(svgLogoPath, 'utf8'); + return `data:image/svg+xml,${encodeURIComponent(svg)}`; +} /** * Takes the given JavaScript file at `filePath` and replaces its contents with * a script that injects the original file contents into the document in which @@ -272,6 +306,8 @@ function makeSelfInjecting(filePath) { module.exports = { getBrowserVersionMap, getBuildName, + getBuildAppId, + getBuildIcon, getEnvironment, isDevBuild, isTestBuild, diff --git a/development/charts/flamegraph/chart/index.html b/development/charts/flamegraph/chart/index.html index ce53076ad9e4..7afe9f9d0dcb 100644 --- a/development/charts/flamegraph/chart/index.html +++ b/development/charts/flamegraph/chart/index.html @@ -9,7 +9,7 @@ rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" /> - +