From 9611708651f29e4651e63910727ef76909629ffc Mon Sep 17 00:00:00 2001 From: Harika <153644847+hjetpoluru@users.noreply.github.com> Date: Thu, 19 Sep 2024 20:35:43 -0400 Subject: [PATCH 1/7] chore: Changed the order of the report (#27260) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** I noticed that the test scenario successfully passed but immediately showed as a failure due to the snapshot check that happens after the console statement. This could be causing confusion, so with the suggestion by @HowardBraham changed the order to report success at the end. [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/27260?quickstart=1) ## **Related issues** Fixes: ## **Manual testing steps** Check the order of the report. ## **Screenshots/Recordings** ### **Before** Screenshot 2024-09-18 at 1 46 41 PM ### **After** Screenshot 2024-09-18 at 2 04 10 PM ## **Pre-merge author checklist** - [ ] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md). - [ ] I've completed the PR template to the best of my ability - [ ] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --- test/e2e/helpers.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/e2e/helpers.js b/test/e2e/helpers.js index 15fc5eff1ade..cf337b84e8f5 100644 --- a/test/e2e/helpers.js +++ b/test/e2e/helpers.js @@ -229,10 +229,6 @@ async function withFixtures(options, testSuite) { throw new Error(errorsAndExceptions); } - // At this point the suite has executed successfully, so we can log out a success message - // (Note: a Chrome browser error will unfortunately pop up after this success message) - console.log(`\nSuccess on testcase: '${title}'\n`); - // Evaluate whether any new hosts received network requests during E2E test // suite execution. If so, fail the test unless the // --update-privacy-snapshot was specified. In that case, update the @@ -271,6 +267,10 @@ async function withFixtures(options, testSuite) { ); } } + + // At this point the suite has executed successfully, so we can log out a success message + // (Note: a Chrome browser error will unfortunately pop up after this success message) + console.log(`\nSuccess on testcase: '${title}'\n`); } catch (error) { failed = true; if (webDriver) { From a1962dcc1aec3091911041ed357a3740a56c61c1 Mon Sep 17 00:00:00 2001 From: "devin-ai-integration[bot]" <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 20 Sep 2024 09:53:07 +0200 Subject: [PATCH 2/7] test: [POM] create ResetPasswordPage for e2e tests and migrate 2 test files to POM (#27244) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** This pull request creates the ResetPasswordPage class for the E2E tests page object model and migrates 2 test files in `test/e2e/tests/account` to POM **and** TypeScript, with enhanced log messages. I have placed the test `test/e2e/tests/account/metamask-responsive-ui.spec.js` outside the account folder because these tests don't test account functionality. They are for testing the extension UI in small viewports. [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/27155?quickstart=1) ## **Related issues** Fixes: #27245 ## **Manual testing steps** Check code readability, make sure tests pass. ## **Screenshots/Recordings** ### **Before** ### **After** ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I’ve included tests if applicable - [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [x] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [x] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --------- Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Chloe Gao --- test/e2e/accounts/forgot-password.spec.ts | 65 ------------------- .../page-objects/pages/account-list-page.ts | 13 ++++ test/e2e/page-objects/pages/login-page.ts | 11 ++++ .../page-objects/pages/reset-password-page.ts | 53 +++++++++++++++ .../tests/account/account-hide-unhide.spec.ts | 1 + .../tests/account/account-pin-unpin.spec.ts | 2 + .../e2e/tests/account/forgot-password.spec.ts | 42 ++++++++++++ .../tests/account/migrate-old-vault.spec.js | 39 ----------- .../tests/account/migrate-old-vault.spec.ts | 24 +++++++ .../metamask-responsive-ui.spec.js | 0 10 files changed, 146 insertions(+), 104 deletions(-) delete mode 100644 test/e2e/accounts/forgot-password.spec.ts create mode 100644 test/e2e/page-objects/pages/reset-password-page.ts create mode 100644 test/e2e/tests/account/forgot-password.spec.ts delete mode 100644 test/e2e/tests/account/migrate-old-vault.spec.js create mode 100644 test/e2e/tests/account/migrate-old-vault.spec.ts rename test/e2e/tests/{account => responsive-ui}/metamask-responsive-ui.spec.js (100%) diff --git a/test/e2e/accounts/forgot-password.spec.ts b/test/e2e/accounts/forgot-password.spec.ts deleted file mode 100644 index 3bf347fa59cc..000000000000 --- a/test/e2e/accounts/forgot-password.spec.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { Suite } from 'mocha'; -import { unlockWallet, withFixtures, TEST_SEED_PHRASE_TWO } from '../helpers'; -import FixtureBuilder from '../fixture-builder'; -import { Driver } from '../webdriver/driver'; - -const newPassword = 'this is the best password ever'; - -describe('Forgot password', function (this: Suite) { - it('resets password and then unlock wallet with new password', async function () { - await withFixtures( - { - fixtures: new FixtureBuilder().build(), - title: this.test?.fullTitle(), - }, - async ({ driver }: { driver: Driver }) => { - await unlockWallet(driver); - - // Lock Wallet - await driver.waitForSelector( - '[data-testid="account-options-menu-button"]', - ); - await driver.clickElement( - '[data-testid="account-options-menu-button"]', - ); - await driver.clickElement({ - text: 'Lock MetaMask', - tag: 'div', - }); - - // Go to reset password page - await driver.waitForSelector('.unlock-page__link'); - await driver.clickElement({ - text: 'Forgot password?', - tag: 'a', - }); - - // Reset password with a new password - await driver.pasteIntoField( - '[data-testid="import-srp__srp-word-0"]', - TEST_SEED_PHRASE_TWO, - ); - - await driver.fill('#password', newPassword); - await driver.fill('#confirm-password', newPassword); - await driver.press('#confirm-password', driver.Key.ENTER); - - // Lock wallet again - await driver.waitForSelector( - '[data-testid="account-options-menu-button"]', - ); - await driver.clickElement( - '[data-testid="account-options-menu-button"]', - ); - await driver.clickElement({ - text: 'Lock MetaMask', - tag: 'div', - }); - - // log in with new password - await driver.fill('#password', newPassword); - await driver.press('#password', driver.Key.ENTER); - }, - ); - }); -}); diff --git a/test/e2e/page-objects/pages/account-list-page.ts b/test/e2e/page-objects/pages/account-list-page.ts index 8550c092b23c..5ebf7fbc861e 100644 --- a/test/e2e/page-objects/pages/account-list-page.ts +++ b/test/e2e/page-objects/pages/account-list-page.ts @@ -33,6 +33,19 @@ class AccountListPage { '.multichain-account-menu-popover__list--menu-item-hidden-account [data-testid="account-list-item-menu-button"]'; } + async check_pageIsLoaded(): Promise { + try { + await this.driver.waitForMultipleSelectors([ + this.accountListItem, + this.accountOptionsMenuButton, + ]); + } catch (e) { + console.log('Timeout while waiting for account list to be loaded', e); + throw e; + } + console.log('Account list is loaded'); + } + async hideAccount(): Promise { console.log(`Hide account in account list`); await this.driver.clickElement(this.hideUnhideAccountButton); diff --git a/test/e2e/page-objects/pages/login-page.ts b/test/e2e/page-objects/pages/login-page.ts index b96600675040..079e8e091f04 100644 --- a/test/e2e/page-objects/pages/login-page.ts +++ b/test/e2e/page-objects/pages/login-page.ts @@ -9,6 +9,8 @@ class LoginPage { private welcomeBackMessage: object; + private forgotPasswordButton: object; + constructor(driver: Driver) { this.driver = driver; this.passwordInput = '[data-testid="unlock-password"]'; @@ -17,6 +19,10 @@ class LoginPage { css: '[data-testid="unlock-page-title"]', text: 'Welcome back!', }; + this.forgotPasswordButton = { + text: 'Forgot password?', + tag: 'a', + }; } async check_pageIsLoaded(): Promise { @@ -40,6 +46,11 @@ class LoginPage { async clickUnlockButton(): Promise { await this.driver.clickElement(this.unlockButton); } + + async gotoResetPasswordPage(): Promise { + console.log('Navigating to reset password page'); + await this.driver.clickElement(this.forgotPasswordButton); + } } export default LoginPage; diff --git a/test/e2e/page-objects/pages/reset-password-page.ts b/test/e2e/page-objects/pages/reset-password-page.ts new file mode 100644 index 000000000000..fa811f0a85dc --- /dev/null +++ b/test/e2e/page-objects/pages/reset-password-page.ts @@ -0,0 +1,53 @@ +import { Driver } from '../../webdriver/driver'; + +class ResetPasswordPage { + private driver: Driver; + + private seedPhraseInput: string; + + private passwordInput: string; + + private confirmPasswordInput: string; + + private restoreButton: string; + + constructor(driver: Driver) { + this.driver = driver; + this.seedPhraseInput = '[data-testid="import-srp__srp-word-0"]'; + this.passwordInput = '[data-testid="create-vault-password"]'; + this.confirmPasswordInput = '[data-testid="create-vault-confirm-password"]'; + this.restoreButton = '[data-testid="create-new-vault-submit-button"]'; + } + + async check_pageIsLoaded(): Promise { + try { + await this.driver.waitForMultipleSelectors([ + this.passwordInput, + this.confirmPasswordInput, + ]); + } catch (e) { + console.log( + 'Timeout while waiting for reset password page to be loaded', + e, + ); + throw e; + } + console.log('Reset password page is loaded'); + } + + /** + * Resets the password using the provided seed phrase and new password. + * + * @param seedPhrase - The seed phrase to verify account ownership + * @param newPassword - The new password to set for the account + */ + async resetPassword(seedPhrase: string, newPassword: string): Promise { + console.log(`Resetting password with new password: ${newPassword}`); + await this.driver.pasteIntoField(this.seedPhraseInput, seedPhrase); + await this.driver.fill(this.passwordInput, newPassword); + await this.driver.fill(this.confirmPasswordInput, newPassword); + await this.driver.clickElement(this.restoreButton); + } +} + +export default ResetPasswordPage; diff --git a/test/e2e/tests/account/account-hide-unhide.spec.ts b/test/e2e/tests/account/account-hide-unhide.spec.ts index 19523b0ab0ce..136cf32fb5cd 100644 --- a/test/e2e/tests/account/account-hide-unhide.spec.ts +++ b/test/e2e/tests/account/account-hide-unhide.spec.ts @@ -20,6 +20,7 @@ describe('Account list - hide/unhide functionality', function (this: Suite) { // hide account const accountListPage = new AccountListPage(driver); + await accountListPage.check_pageIsLoaded(); await accountListPage.openAccountOptionsMenu(); await accountListPage.hideAccount(); await accountListPage.check_hiddenAccountsListExists(); diff --git a/test/e2e/tests/account/account-pin-unpin.spec.ts b/test/e2e/tests/account/account-pin-unpin.spec.ts index a37859dfa6a9..8c8bcea59796 100644 --- a/test/e2e/tests/account/account-pin-unpin.spec.ts +++ b/test/e2e/tests/account/account-pin-unpin.spec.ts @@ -20,6 +20,7 @@ describe('Account list - pin/unpin functionality', function (this: Suite) { // pin account const accountListPage = new AccountListPage(driver); + await accountListPage.check_pageIsLoaded(); await accountListPage.openAccountOptionsMenu(); await accountListPage.pinAccount(); await accountListPage.check_accountIsPinned(); @@ -45,6 +46,7 @@ describe('Account list - pin/unpin functionality', function (this: Suite) { // pin account const accountListPage = new AccountListPage(driver); + await accountListPage.check_pageIsLoaded(); await accountListPage.openAccountOptionsMenu(); await accountListPage.pinAccount(); await accountListPage.check_accountIsPinned(); diff --git a/test/e2e/tests/account/forgot-password.spec.ts b/test/e2e/tests/account/forgot-password.spec.ts new file mode 100644 index 000000000000..d5af9998520d --- /dev/null +++ b/test/e2e/tests/account/forgot-password.spec.ts @@ -0,0 +1,42 @@ +import { withFixtures, defaultGanacheOptions } from '../../helpers'; +import FixtureBuilder from '../../fixture-builder'; +import { E2E_SRP } from '../../default-fixture'; +import { Driver } from '../../webdriver/driver'; +import HomePage from '../../page-objects/pages/homepage'; +import LoginPage from '../../page-objects/pages/login-page'; +import ResetPasswordPage from '../../page-objects/pages/reset-password-page'; +import { loginWithBalanceValidation } from '../../page-objects/flows/login.flow'; + +const newPassword = 'this is the best password ever'; + +describe('Forgot password', function () { + it('resets password and then unlock wallet with new password', async function () { + await withFixtures( + { + fixtures: new FixtureBuilder().build(), + ganacheOptions: defaultGanacheOptions, + title: this.test?.fullTitle(), + }, + async ({ driver }: { driver: Driver }) => { + await loginWithBalanceValidation(driver); + + // Lock Wallet + const homePage = new HomePage(driver); + await homePage.headerNavbar.lockMetaMask(); + + // Click forgot password button and reset password + await new LoginPage(driver).gotoResetPasswordPage(); + + const resetPasswordPage = new ResetPasswordPage(driver); + await resetPasswordPage.check_pageIsLoaded(); + await resetPasswordPage.resetPassword(E2E_SRP, newPassword); + + // Lock wallet again + await homePage.headerNavbar.lockMetaMask(); + + // Check user can log in with new password + await loginWithBalanceValidation(driver, newPassword); + }, + ); + }); +}); diff --git a/test/e2e/tests/account/migrate-old-vault.spec.js b/test/e2e/tests/account/migrate-old-vault.spec.js deleted file mode 100644 index 610a2afebec3..000000000000 --- a/test/e2e/tests/account/migrate-old-vault.spec.js +++ /dev/null @@ -1,39 +0,0 @@ -const { strict: assert } = require('assert'); -const { defaultGanacheOptions, withFixtures } = require('../../helpers'); -const FixtureBuilder = require('../../fixture-builder'); - -const lock = async (driver) => { - await driver.clickElement('[data-testid="account-options-menu-button"]'); - const lockButton = await driver.findClickableElement( - '[data-testid="global-menu-lock"]', - ); - await lockButton.click(); -}; - -const unlock = async (driver) => { - await driver.fill('#password', 'correct horse battery staple'); - await driver.press('#password', driver.Key.ENTER); -}; - -describe('Migrate vault with old encryption', function () { - it('successfully unlocks an old vault, locks it, and unlock again', async function () { - await withFixtures( - { - fixtures: new FixtureBuilder().withKeyringControllerOldVault().build(), - ganacheOptions: defaultGanacheOptions, - title: this.test.fullTitle(), - }, - async ({ driver }) => { - await driver.navigate(); - - await unlock(driver); - await lock(driver); - await unlock(driver); - const walletBalance = await driver.findElement( - '.eth-overview__primary-balance', - ); - assert.equal(/^25\s*ETH$/u.test(await walletBalance.getText()), true); - }, - ); - }); -}); diff --git a/test/e2e/tests/account/migrate-old-vault.spec.ts b/test/e2e/tests/account/migrate-old-vault.spec.ts new file mode 100644 index 000000000000..eeacfbc24a09 --- /dev/null +++ b/test/e2e/tests/account/migrate-old-vault.spec.ts @@ -0,0 +1,24 @@ +import { Suite } from 'mocha'; +import { defaultGanacheOptions, withFixtures } from '../../helpers'; +import FixtureBuilder from '../../fixture-builder'; +import { Driver } from '../../webdriver/driver'; +import HomePage from '../../page-objects/pages/homepage'; +import { loginWithBalanceValidation } from '../../page-objects/flows/login.flow'; + +describe('Migrate vault with old encryption', function (this: Suite) { + it('successfully unlocks an old vault, locks it, and unlocks again', async function () { + await withFixtures( + { + fixtures: new FixtureBuilder().withKeyringControllerOldVault().build(), + ganacheOptions: defaultGanacheOptions, + title: this.test?.fullTitle(), + }, + async ({ driver }: { driver: Driver }) => { + await loginWithBalanceValidation(driver); + const homePage = new HomePage(driver); + await homePage.headerNavbar.lockMetaMask(); + await loginWithBalanceValidation(driver); + }, + ); + }); +}); diff --git a/test/e2e/tests/account/metamask-responsive-ui.spec.js b/test/e2e/tests/responsive-ui/metamask-responsive-ui.spec.js similarity index 100% rename from test/e2e/tests/account/metamask-responsive-ui.spec.js rename to test/e2e/tests/responsive-ui/metamask-responsive-ui.spec.js From 555d42b9ead0f4919356ff16e11c663c5e38639e Mon Sep 17 00:00:00 2001 From: Brian Bergeron Date: Fri, 20 Sep 2024 01:35:03 -0700 Subject: [PATCH 3/7] feat: network controller upgrade + network editing UI (#26433) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** This PR upgrades the network controller, and enables a new UI for adding and editing networks. The network controller changes are described in full in https://github.com/MetaMask/core/pull/4286 In short, there's now a single network per chain id. Multiple RPC endpoints for a chain are now represented as an array under the single network configuration, instead of being separate networks. Each network has some RPC endpoint chosen as the default. Also the built in Infura networks are now represented in state, where they were not before. A migration is added to transform the network controller state to this new format. For the UI, networks are now added, edited, and deleted directly in the network list. Networks are no longer edited via the settings page. Users with multiple RPC endpoints per chain are shown a modal upon upgrade, allowing them to select a different endpoint as the default. The UI for `wallet_addEthereumChain` is changed, to message that users may be adding an additional endpoint to an existing network, rather than adding a new network. This PR contains both the controller upgrade and UI, because it's not possible to perfectly recrate the old UI with the new state or vice versa. To minimize changes, some selectors are shimmed to return equivalent data. This includes `getProviderConfig` and `getCurrentNetwork`. Other selectors have been removed in favor of selecting the new state, as there was no behavior that satisfied every caller. This includes `getNetworkConfigurations` and its dependents like `getNonTestNetworks` `getTestNetworks`. Places listing networks now go through the new `getNetworkConfigurationsByChainId`. [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/26433?quickstart=1) ## **Related issues** Fixes: ## **Manual testing steps** 1. Upgrade from an older build with custom networks 2. Verify they all still appear in the network dropdown - Verify multiple endpoints for the same chain have been merged into 1 network with multiple RPC endponits 3. Click the network dropdown 4. Try: - Adding popular networks - Adding a custom network - Editing an existing network - Deleting an existing network 5. Try adding a new network via a dapp 6. Try adding a new RPC endpoint to an existing network via a dapp ## **Screenshots/Recordings** ### **Before** ### **After** https://github.com/user-attachments/assets/dd215f30-9c83-4490-83c3-8aaf39412763 ## **Pre-merge author checklist** - [ ] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md). - [ ] I've completed the PR template to the best of my ability - [ ] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --------- Co-authored-by: salimtb Co-authored-by: MetaMask Bot Co-authored-by: David Walsh Co-authored-by: Howard Braham Co-authored-by: Maarten Zuidhoorn --- .storybook/test-data.js | 28 +- ...rk-controller-npm-21.0.0-559aa8e395.patch} | 24 +- app/_locales/am/messages.json | 6 - app/_locales/ar/messages.json | 6 - app/_locales/bg/messages.json | 6 - app/_locales/bn/messages.json | 6 - app/_locales/ca/messages.json | 6 - app/_locales/cs/messages.json | 3 - app/_locales/da/messages.json | 6 - app/_locales/de/messages.json | 80 - app/_locales/el/messages.json | 80 - app/_locales/en/messages.json | 118 +- app/_locales/es/messages.json | 80 - app/_locales/es_419/messages.json | 18 - app/_locales/et/messages.json | 6 - app/_locales/fa/messages.json | 6 - app/_locales/fi/messages.json | 6 - app/_locales/fil/messages.json | 6 - app/_locales/fr/messages.json | 80 - app/_locales/he/messages.json | 6 - app/_locales/hi/messages.json | 80 - app/_locales/hn/messages.json | 3 - app/_locales/hr/messages.json | 6 - app/_locales/ht/messages.json | 3 - app/_locales/hu/messages.json | 6 - app/_locales/id/messages.json | 80 - app/_locales/it/messages.json | 31 - app/_locales/ja/messages.json | 80 - app/_locales/kn/messages.json | 6 - app/_locales/ko/messages.json | 80 - app/_locales/lt/messages.json | 6 - app/_locales/lv/messages.json | 6 - app/_locales/ms/messages.json | 6 - app/_locales/nl/messages.json | 3 - app/_locales/no/messages.json | 6 - app/_locales/ph/messages.json | 15 - app/_locales/pl/messages.json | 6 - app/_locales/pt/messages.json | 80 - app/_locales/pt_BR/messages.json | 18 - app/_locales/ro/messages.json | 6 - app/_locales/ru/messages.json | 80 - app/_locales/sk/messages.json | 6 - app/_locales/sl/messages.json | 6 - app/_locales/sr/messages.json | 6 - app/_locales/sv/messages.json | 6 - app/_locales/sw/messages.json | 6 - app/_locales/ta/messages.json | 3 - app/_locales/th/messages.json | 6 - app/_locales/tl/messages.json | 80 - app/_locales/tr/messages.json | 80 - app/_locales/uk/messages.json | 6 - app/_locales/vi/messages.json | 80 - app/_locales/zh_CN/messages.json | 80 - app/_locales/zh_TW/messages.json | 15 - app/images/networks1.png | Bin 0 -> 5923 bytes app/scripts/controllers/metametrics.js | 6 +- app/scripts/controllers/mmi-controller.ts | 50 +- app/scripts/controllers/network-order.ts | 83 +- .../preferences-controller.test.ts | 20 +- .../controllers/preferences-controller.ts | 6 +- app/scripts/lib/backup.js | 4 +- app/scripts/lib/backup.test.js | 35 +- .../handlers/add-ethereum-chain.js | 195 ++- .../handlers/add-ethereum-chain.test.js | 352 ++-- .../handlers/ethereum-chain-utils.js | 25 - .../handlers/switch-ethereum-chain.js | 20 +- .../handlers/switch-ethereum-chain.test.js | 78 +- app/scripts/metamask-controller.js | 188 +- app/scripts/metamask-controller.test.js | 91 - app/scripts/migrations/127.test.ts | 1263 ++++++++++++++ app/scripts/migrations/127.ts | 453 +++++ app/scripts/migrations/index.js | 1 + builds.yml | 2 - lavamoat/browserify/beta/policy.json | 66 +- lavamoat/browserify/flask/policy.json | 66 +- lavamoat/browserify/main/policy.json | 66 +- lavamoat/browserify/mmi/policy.json | 66 +- lavamoat/build-system/policy.json | 18 +- package.json | 10 +- shared/constants/network.test.ts | 34 +- shared/constants/network.ts | 133 +- shared/constants/swaps.ts | 4 +- .../modules/selectors/smart-transactions.ts | 8 - test/data/confirmations/helper.ts | 3 +- test/data/mock-send-state.json | 19 +- test/data/mock-state.json | 34 +- test/e2e/default-fixture.js | 9 +- test/e2e/fixture-builder.js | 50 +- .../dapp-interactions/block-explorer.spec.js | 16 +- ...rs-after-init-opt-in-background-state.json | 7 +- .../errors-after-init-opt-in-ui-state.json | 6 +- ...s-before-init-opt-in-background-state.json | 3 +- .../errors-before-init-opt-in-ui-state.json | 3 +- .../tests/network/add-custom-network.spec.js | 491 ++++-- .../tests/network/custom-rpc-history.spec.js | 220 ++- test/e2e/tests/network/update-network.spec.ts | 82 +- test/e2e/tests/onboarding/onboarding.spec.js | 61 +- test/e2e/tests/request-queuing/ui.spec.js | 50 +- .../e2e/tests/settings/backup-restore.spec.js | 2 +- .../tests/settings/settings-search.spec.js | 26 - .../data/integration-init-state.json | 69 +- .../data/onboarding-completion-route.json | 19 +- test/jest/mock-store.js | 7 + test/stub/networks.ts | 84 +- ui/components/app/add-network/add-network.js | 324 ---- .../app/add-network/add-network.stories.js | 115 -- .../app/add-network/add-network.test.js | 66 - ui/components/app/add-network/index.js | 1 - ui/components/app/add-network/index.scss | 94 - ui/components/app/app-components.scss | 1 - .../__snapshots__/nft-details.test.js.snap | 4 +- .../__snapshots__/nft-full-image.test.js.snap | 2 +- .../nfts/nfts-items/nfts-items.stories.tsx | 28 +- .../incoming-transaction-toggle.test.js.snap | 6 +- .../incoming-transaction-toggle.stories.tsx | 6 +- .../incoming-transaction-toggle.test.js | 22 +- .../incoming-transaction-toggle.tsx | 44 +- .../incoming-trasaction-toggle/mock-data.ts | 135 +- .../loading-network-screen.container.js | 6 +- .../confirm-delete-network.component.js | 6 +- .../confirm-delete-network.container.js | 16 +- .../confirm-delete-network.test.js | 8 +- .../confirm-delete-rpc-url-modal.tsx | 72 - ui/components/app/modals/modal.js | 49 - .../multi-rpc-edit-modal.test.tsx.snap | 53 +- .../multi-rpc-edit-modal.test.tsx | 3 +- .../multi-rpc-edit-modal.tsx | 12 +- .../network-list-item.test.tsx | 2 + .../network-list-item/network-list-item.tsx | 14 +- .../app/network-display/network-display.js | 1 + .../selected-account-component.test.js.snap | 49 + .../selected-account-component.test.js | 31 +- .../keyring-snap-removal-warning.tsx | 4 +- .../snap-permission-adapter.test.js | 3 + .../snap-permissions-list.test.js | 2 + .../wrong-network-notification.tsx | 6 +- .../address-copy-button.test.js.snap | 35 + .../address-copy-button.test.js | 14 +- .../multichain/app-header/app-header.test.js | 2 +- .../edit-networks-modal.js | 41 +- .../network-list-item-menu.js | 8 +- .../network-list-item.test.js.snap | 30 +- .../multichain/network-list-item/index.scss | 11 - .../network-list-item.test.js | 42 +- ...ork-list-item.js => network-list-item.tsx} | 166 +- .../add-block-explorer-modal.tsx | 1 + .../add-rpc-url-modal.test.tsx.snap | 4 +- .../add-rpc-url-modal/add-rpc-url-modal.tsx | 1 + .../multichain/network-list-menu/index.scss | 11 +- .../network-list-menu/network-list-menu.js | 647 ------- .../network-list-menu.test.js | 108 +- .../network-list-menu/network-list-menu.tsx | 601 +++++++ .../popular-network-list.test.tsx | 67 +- .../popular-network-list.tsx | 159 +- .../network-list-menu/rpc-list-item.tsx | 7 +- .../select-rpc-url-modal.test.tsx | 35 +- .../select-rpc-url-modal.tsx | 77 +- ...ification-detail-block-explorer-button.tsx | 26 +- .../permissions-page/permissions-page.test.js | 10 +- .../network-picker.test.tsx.snap | 4 +- .../permission-details-modal.test.tsx | 7 +- .../token-list-item/token-list-item.tsx | 18 +- .../deprecated-networks.js | 40 +- .../new-network-info/new-network-info.test.js | 7 +- ui/ducks/app/app.ts | 7 +- ui/ducks/bridge/selectors.test.ts | 43 +- ui/ducks/bridge/selectors.ts | 32 +- ui/ducks/metamask/metamask.js | 50 +- ui/ducks/send/helpers.test.js | 1 + ui/ducks/send/send.js | 2 +- ui/helpers/constants/zendesk-url.js | 2 +- ui/helpers/utils/feature-flags.js | 3 - ui/helpers/utils/notification.util.ts | 2 +- ui/hooks/ramps/useRamps/useRamps.test.tsx | 2 + ui/hooks/useTokenTracker.js | 11 +- ui/index.js | 6 - ui/pages/asset/components/native-asset.tsx | 4 +- .../confirm-add-suggested-nft.test.js | 4 +- .../advanced-gas-controls.test.js | 6 +- .../advanced-gas-fee-defaults.test.js | 5 +- .../advanced-gas-inputs.test.js | 2 +- .../confirm-subtitle/confirm-subtitle.test.js | 5 +- .../header/__snapshots__/header.test.tsx.snap | 12 +- .../components/confirm/header/header.test.tsx | 2 +- .../network-change-toast-legacy.tsx | 13 +- .../blockaid-banner-alert.test.js | 38 +- .../signature-request-header.test.js.snap | 4 +- .../signature-request-original.test.js.snap | 4 +- .../signature-request-siwe.test.js.snap | 4 +- ...ture-request-header.component.test.js.snap | 8 +- .../signature-request.test.js | 3 + .../confirm-send-ether.test.js.snap | 6 +- .../__snapshots__/confirm.test.tsx.snap | 38 +- .../confirmation-network-switch.js | 16 +- .../confirmation-network-switch.stories.js | 4 +- .../confirmation/confirmation.js | 9 + .../confirmation/stories/util.js | 13 + .../add-ethereum-chain.test.js.snap | 4 +- .../remove-snap-account.test.js.snap | 10 +- .../templates/add-ethereum-chain.js | 67 +- .../confirmation/templates/index.js | 5 +- .../templates/remove-snap-account.test.js | 3 + .../templates/switch-ethereum-chain.test.js | 12 +- .../useConfirmationOriginAlerts.test.ts | 3 +- .../hooks/useConfirmationNetworkInfo.ts | 52 +- .../hooks/useTransactionInfo.test.js | 5 +- ui/pages/home/home.component.js | 72 +- ui/pages/home/home.container.js | 4 +- .../institutional-entity-done-page.test.tsx | 1 + .../add-network-modal.test.js.snap | 175 -- .../add-network-modal.stories.tsx | 40 - .../add-network-modal.test.js | 76 - .../add-network-modal/index.js | 89 - .../onboarding-flow/onboarding-flow.test.js | 6 +- .../privacy-settings/privacy-settings.js | 180 +- .../privacy-settings/privacy-settings.test.js | 8 +- ui/pages/routes/routes.component.js | 4 +- ui/pages/routes/routes.component.test.js | 5 - ui/pages/routes/routes.container.js | 2 - .../developer-options-tab.test.tsx.snap | 70 - .../developer-options-tab.tsx | 24 - .../custom-content-search.js | 110 -- .../custom-content-search.stories.js | 23 - .../custom-content-search.test.js | 110 -- .../custom-content-search/index.js | 1 - ui/pages/settings/networks-tab/index.js | 1 - ui/pages/settings/networks-tab/index.scss | 117 +- .../rpc-url-editor.test.tsx.snap | 29 - .../networks-form/networks-form-state.ts | 75 + .../networks-form/networks-form.js | 1512 ----------------- .../networks-form/networks-form.test.js | 520 +++--- .../networks-form/networks-form.tsx | 651 +++++++ .../networks-form/rpc-url-editor.test.tsx | 82 - .../networks-form/rpc-url-editor.tsx | 168 -- .../networks-tab/networks-list-item/index.js | 1 - .../networks-list-item/networks-list-item.js | 147 -- .../networks-list-item.stories.js | 41 - .../networks-list-item.test.js | 46 - .../networks-tab/networks-list/index.js | 1 - .../networks-list/network-list.stories.js | 28 - .../networks-list/networks-list.js | 96 -- .../networks-list/networks-list.test.js | 41 - .../networks-tab-content/index.js | 1 - .../networks-tab-content.js | 52 - .../networks-tab-content.test.js | 100 -- .../networks-tab-subheader/index.js | 1 - .../networks-tab-subheader.js | 44 - .../networks-tab-subheader.test.js | 37 - .../networks-tab/networks-tab.constants.js | 64 - .../settings/networks-tab/networks-tab.js | 155 -- .../networks-tab/networks-tab.test.js | 53 - .../__snapshots__/security-tab.test.js.snap | 261 +-- .../security-tab/security-tab.component.js | 6 +- .../security-tab/security-tab.container.js | 6 +- ui/pages/settings/settings.component.js | 25 +- ui/pages/settings/settings.container.js | 12 +- .../swaps/build-quote/build-quote.test.js | 46 +- .../prepare-swap-page.test.js | 26 +- ui/selectors/institutional/selectors.test.ts | 13 + ui/selectors/multichain.test.ts | 24 +- ui/selectors/multichain.ts | 58 +- ui/selectors/selectors.js | 247 +-- ui/selectors/selectors.test.js | 368 ++-- ui/store/actions.test.js | 96 +- ui/store/actions.ts | 121 +- yarn.lock | 109 +- 266 files changed, 6910 insertions(+), 9625 deletions(-) rename .yarn/patches/{@metamask-network-controller-npm-20.2.0-98b1a5ae59.patch => @metamask-network-controller-npm-21.0.0-559aa8e395.patch} (69%) create mode 100644 app/images/networks1.png create mode 100644 app/scripts/migrations/127.test.ts create mode 100644 app/scripts/migrations/127.ts delete mode 100644 ui/components/app/add-network/add-network.js delete mode 100644 ui/components/app/add-network/add-network.stories.js delete mode 100644 ui/components/app/add-network/add-network.test.js delete mode 100644 ui/components/app/add-network/index.js delete mode 100644 ui/components/app/add-network/index.scss delete mode 100644 ui/components/app/modals/confirm-delete-rpc-url-modal/confirm-delete-rpc-url-modal.tsx create mode 100644 ui/components/app/selected-account/__snapshots__/selected-account-component.test.js.snap create mode 100644 ui/components/multichain/address-copy-button/__snapshots__/address-copy-button.test.js.snap rename ui/components/multichain/network-list-item/{network-list-item.js => network-list-item.tsx} (50%) delete mode 100644 ui/components/multichain/network-list-menu/network-list-menu.js create mode 100644 ui/components/multichain/network-list-menu/network-list-menu.tsx delete mode 100644 ui/helpers/utils/feature-flags.js delete mode 100644 ui/pages/onboarding-flow/add-network-modal/__snapshots__/add-network-modal.test.js.snap delete mode 100644 ui/pages/onboarding-flow/add-network-modal/add-network-modal.stories.tsx delete mode 100644 ui/pages/onboarding-flow/add-network-modal/add-network-modal.test.js delete mode 100644 ui/pages/onboarding-flow/add-network-modal/index.js delete mode 100644 ui/pages/settings/networks-tab/custom-content-search/custom-content-search.js delete mode 100644 ui/pages/settings/networks-tab/custom-content-search/custom-content-search.stories.js delete mode 100644 ui/pages/settings/networks-tab/custom-content-search/custom-content-search.test.js delete mode 100644 ui/pages/settings/networks-tab/custom-content-search/index.js delete mode 100644 ui/pages/settings/networks-tab/index.js delete mode 100644 ui/pages/settings/networks-tab/networks-form/__snapshots__/rpc-url-editor.test.tsx.snap create mode 100644 ui/pages/settings/networks-tab/networks-form/networks-form-state.ts delete mode 100644 ui/pages/settings/networks-tab/networks-form/networks-form.js create mode 100644 ui/pages/settings/networks-tab/networks-form/networks-form.tsx delete mode 100644 ui/pages/settings/networks-tab/networks-form/rpc-url-editor.test.tsx delete mode 100644 ui/pages/settings/networks-tab/networks-form/rpc-url-editor.tsx delete mode 100644 ui/pages/settings/networks-tab/networks-list-item/index.js delete mode 100644 ui/pages/settings/networks-tab/networks-list-item/networks-list-item.js delete mode 100644 ui/pages/settings/networks-tab/networks-list-item/networks-list-item.stories.js delete mode 100644 ui/pages/settings/networks-tab/networks-list-item/networks-list-item.test.js delete mode 100644 ui/pages/settings/networks-tab/networks-list/index.js delete mode 100644 ui/pages/settings/networks-tab/networks-list/network-list.stories.js delete mode 100644 ui/pages/settings/networks-tab/networks-list/networks-list.js delete mode 100644 ui/pages/settings/networks-tab/networks-list/networks-list.test.js delete mode 100644 ui/pages/settings/networks-tab/networks-tab-content/index.js delete mode 100644 ui/pages/settings/networks-tab/networks-tab-content/networks-tab-content.js delete mode 100644 ui/pages/settings/networks-tab/networks-tab-content/networks-tab-content.test.js delete mode 100644 ui/pages/settings/networks-tab/networks-tab-subheader/index.js delete mode 100644 ui/pages/settings/networks-tab/networks-tab-subheader/networks-tab-subheader.js delete mode 100644 ui/pages/settings/networks-tab/networks-tab-subheader/networks-tab-subheader.test.js delete mode 100644 ui/pages/settings/networks-tab/networks-tab.constants.js delete mode 100644 ui/pages/settings/networks-tab/networks-tab.js delete mode 100644 ui/pages/settings/networks-tab/networks-tab.test.js diff --git a/.storybook/test-data.js b/.storybook/test-data.js index 274fdfdb3651..72a9bc3b78aa 100644 --- a/.storybook/test-data.js +++ b/.storybook/test-data.js @@ -2,7 +2,10 @@ import { draftTransactionInitialState } from '../ui/ducks/send'; import { KeyringType } from '../shared/constants/keyring'; import { NetworkStatus } from '@metamask/network-controller'; import { EthAccountType } from '@metamask/keyring-api'; -import { CHAIN_IDS } from '../shared/constants/network'; +import { + CHAIN_IDS, + LINEA_MAINNET_DISPLAY_NAME, +} from '../shared/constants/network'; import { copyable, divider, heading, panel, text } from '@metamask/snaps-sdk'; import { getJsxElementFromComponent } from '@metamask/snaps-utils'; import { FirstTimeFlowType } from '../shared/constants/onboarding'; @@ -1218,23 +1221,37 @@ const state = { accounts: ['0x9d0ba4ddac06032527b140912ec808ab9451b788'], }, ], - ...mockNetworkState({ + ...mockNetworkState( + { id: 'test-networkConfigurationId-1', rpcUrl: 'https://testrpc.com', chainId: '0x1', nickname: 'mainnet', + name: 'mainnet', blockExplorerUrl: 'https://etherscan.io', metadata: { EIPS: { 1559: true }, status: NetworkStatus.Available, - } - }, { + }, + }, + { id: 'test-networkConfigurationId-2', + rpcUrl: 'https://testrpc2.com', + chainId: '0xe708', + nickname: LINEA_MAINNET_DISPLAY_NAME, + name: LINEA_MAINNET_DISPLAY_NAME, + blockExplorerUrl: 'https://lineascan.build', + metadata: { EIPS: { 1559: true }, status: NetworkStatus.Available }, + }, + { + id: 'test-networkConfigurationId-3', rpcUrl: 'http://localhost:8545', chainId: '0x539', + name: 'test network', ticker: 'ETH', nickname: 'Localhost 8545', - }), + }, + ), accountTokens: { '0x64a845a5b02460acf8a3d84503b0d68d028b4bb4': { '0x1': [ @@ -1278,6 +1295,7 @@ const state = { ipfsGateway: 'dweb.link', migratedPrivacyMode: false, selectedAddress: '0x9d0ba4ddac06032527b140912ec808ab9451b788', + selectedNetworkClientId: 'test-networkConfigurationId-1', metaMetricsId: '0xc2377d11fec1c3b7dd88c4854240ee5e3ed0d9f63b00456d98d80320337b827f', currentCurrency: 'usd', diff --git a/.yarn/patches/@metamask-network-controller-npm-20.2.0-98b1a5ae59.patch b/.yarn/patches/@metamask-network-controller-npm-21.0.0-559aa8e395.patch similarity index 69% rename from .yarn/patches/@metamask-network-controller-npm-20.2.0-98b1a5ae59.patch rename to .yarn/patches/@metamask-network-controller-npm-21.0.0-559aa8e395.patch index 63fff98c47e6..5719ae0284f7 100644 --- a/.yarn/patches/@metamask-network-controller-npm-20.2.0-98b1a5ae59.patch +++ b/.yarn/patches/@metamask-network-controller-npm-21.0.0-559aa8e395.patch @@ -7,26 +7,26 @@ index 0000000000000000000000000000000000000000..78b9156dc2b0bf7c33dadf325cb3ec0b +We remove `lookupNetwork` from `initializeProvider` in the network controller to prevent network requests before user onboarding is completed. +The network lookup is done after onboarding is completed, and when the extension reloads if onboarding has been completed. +This patch is part of a temporary fix that will be reverted soon to make way for a more permanent solution. https://github.com/MetaMask/metamask-extension/pull/23005 -diff --git a/dist/chunk-BEL2VMHN.js b/dist/chunk-BEL2VMHN.js -index fcf6c5ad51d0db75cf0e3219a569e17437a55486..751447609c924e626c0f442931eb77687b160e42 100644 ---- a/dist/chunk-BEL2VMHN.js -+++ b/dist/chunk-BEL2VMHN.js -@@ -315,7 +315,6 @@ var NetworkController = class extends _basecontroller.BaseController { +diff --git a/dist/chunk-BV3ZGWII.mjs b/dist/chunk-BV3ZGWII.mjs +index 0d1bf3b6348ad4ec7a799083fcadf36f9fc74851..48a09c6e474da9c18115bec88130a88888337044 100644 +--- a/dist/chunk-BV3ZGWII.mjs ++++ b/dist/chunk-BV3ZGWII.mjs +@@ -468,7 +468,6 @@ var NetworkController = class extends BaseController { */ async initializeProvider() { - _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _applyNetworkSelection, applyNetworkSelection_fn).call(this, this.state.selectedNetworkClientId); + __privateMethod(this, _applyNetworkSelection, applyNetworkSelection_fn).call(this, this.state.selectedNetworkClientId); - await this.lookupNetwork(); } /** * Refreshes the network meta with EIP-1559 support and the network status -diff --git a/dist/chunk-RTMQACMX.mjs b/dist/chunk-RTMQACMX.mjs -index fc6ae58a396aaa062e8d9a8de2cddd5ef073a5a4..2a6f811c10a0ed3fc943f4672b21a5d1c195c7cd 100644 ---- a/dist/chunk-RTMQACMX.mjs -+++ b/dist/chunk-RTMQACMX.mjs -@@ -315,7 +315,6 @@ var NetworkController = class extends BaseController { +diff --git a/dist/chunk-YOHMQPGM.js b/dist/chunk-YOHMQPGM.js +index ff15cd78ef90b35f86aae9dc64d17d1d2efe352d..14a8bba39c204585164dfb252d0a183844a58d63 100644 +--- a/dist/chunk-YOHMQPGM.js ++++ b/dist/chunk-YOHMQPGM.js +@@ -468,7 +468,6 @@ var NetworkController = class extends _basecontroller.BaseController { */ async initializeProvider() { - __privateMethod(this, _applyNetworkSelection, applyNetworkSelection_fn).call(this, this.state.selectedNetworkClientId); + _chunkZ4BLTVTBjs.__privateMethod.call(void 0, this, _applyNetworkSelection, applyNetworkSelection_fn).call(this, this.state.selectedNetworkClientId); - await this.lookupNetwork(); } /** diff --git a/app/_locales/am/messages.json b/app/_locales/am/messages.json index f2efb2511d06..4ce44a388ac1 100644 --- a/app/_locales/am/messages.json +++ b/app/_locales/am/messages.json @@ -196,9 +196,6 @@ "delete": { "message": "ሰርዝ" }, - "deleteNetwork": { - "message": "አውታረ መረብ ይሰረዝ?" - }, "details": { "message": "ዝርዝሮች" }, @@ -342,9 +339,6 @@ "invalidAddressRecipient": { "message": "የተቀባይ አድራሻ ትክክል አይደለም" }, - "invalidBlockExplorerURL": { - "message": "ልክ ያልሆነ Block Explorer ዩአርኤል" - }, "invalidRPC": { "message": "ልክ ያልሆነ RPC ዩአርኤል" }, diff --git a/app/_locales/ar/messages.json b/app/_locales/ar/messages.json index e9a099be976c..6cb79c56b136 100644 --- a/app/_locales/ar/messages.json +++ b/app/_locales/ar/messages.json @@ -209,9 +209,6 @@ "delete": { "message": "حذف" }, - "deleteNetwork": { - "message": "هل تريد حذف الشبكة؟" - }, "details": { "message": "التفاصيل" }, @@ -351,9 +348,6 @@ "invalidAddressRecipient": { "message": "عنوان المستلم غير صحيح" }, - "invalidBlockExplorerURL": { - "message": "غير صحيح Block Explorer رابط" - }, "invalidRPC": { "message": "رابط آر بي سي غير صحيح" }, diff --git a/app/_locales/bg/messages.json b/app/_locales/bg/messages.json index 8a21fc86c7f4..1fa7a14393d4 100644 --- a/app/_locales/bg/messages.json +++ b/app/_locales/bg/messages.json @@ -205,9 +205,6 @@ "delete": { "message": "Изтриване" }, - "deleteNetwork": { - "message": "Да се изтрие ли мрежата?" - }, "details": { "message": "Подробности" }, @@ -347,9 +344,6 @@ "invalidAddressRecipient": { "message": "Адресът на получателя е невалиден" }, - "invalidBlockExplorerURL": { - "message": "Невалиден Block Explorer URL адрес" - }, "invalidRPC": { "message": "Невалиден RPC URL адрес" }, diff --git a/app/_locales/bn/messages.json b/app/_locales/bn/messages.json index 593ab8dd158b..a9cc5aa0d845 100644 --- a/app/_locales/bn/messages.json +++ b/app/_locales/bn/messages.json @@ -202,9 +202,6 @@ "delete": { "message": "মুছুন" }, - "deleteNetwork": { - "message": "নেটওয়ার্ক মুছবেন?" - }, "details": { "message": "বিশদ বিবরণ" }, @@ -348,9 +345,6 @@ "invalidAddressRecipient": { "message": "প্রাপকের ঠিকানা অবৈধ" }, - "invalidBlockExplorerURL": { - "message": "অবৈধ Block Explorer URL" - }, "invalidRPC": { "message": "অবৈধ RPC URL" }, diff --git a/app/_locales/ca/messages.json b/app/_locales/ca/messages.json index 61f2c5e32481..92f2b2771ff9 100644 --- a/app/_locales/ca/messages.json +++ b/app/_locales/ca/messages.json @@ -202,9 +202,6 @@ "delete": { "message": "Suprimeix" }, - "deleteNetwork": { - "message": "Esborrar Xarxa?" - }, "details": { "message": "Detalls" }, @@ -338,9 +335,6 @@ "invalidAddressRecipient": { "message": "L'adreça del recipient no és vàlida" }, - "invalidBlockExplorerURL": { - "message": "URL de Block Explorer" - }, "invalidRPC": { "message": "URL de RPC" }, diff --git a/app/_locales/cs/messages.json b/app/_locales/cs/messages.json index 96b9a3291ea3..6e3bfa315303 100644 --- a/app/_locales/cs/messages.json +++ b/app/_locales/cs/messages.json @@ -157,9 +157,6 @@ "invalidAddressRecipient": { "message": "Adresa příjemce je neplatná" }, - "invalidBlockExplorerURL": { - "message": "Neplatné Block Explorer URI" - }, "invalidRPC": { "message": "Neplatné RPC URI" }, diff --git a/app/_locales/da/messages.json b/app/_locales/da/messages.json index 26aa63a60257..12eba292e0a4 100644 --- a/app/_locales/da/messages.json +++ b/app/_locales/da/messages.json @@ -205,9 +205,6 @@ "delete": { "message": "Slet" }, - "deleteNetwork": { - "message": "Slet Netværk?" - }, "details": { "message": "Detaljer" }, @@ -344,9 +341,6 @@ "invalidAddressRecipient": { "message": "Modtageradressen er ugyldig" }, - "invalidBlockExplorerURL": { - "message": "Ugyldig Block Explorer-webadresse" - }, "invalidRPC": { "message": "Ugyldig RPC-webadresse" }, diff --git a/app/_locales/de/messages.json b/app/_locales/de/messages.json index ff0773178691..85e776155793 100644 --- a/app/_locales/de/messages.json +++ b/app/_locales/de/messages.json @@ -183,9 +183,6 @@ "addANetwork": { "message": "Ein neues Netzwerk hinzufügen" }, - "addANetworkManually": { - "message": "Ein Netzwerk manuell hinzufügen" - }, "addANickname": { "message": "Spitznamen hinzufügen" }, @@ -252,9 +249,6 @@ "addFriendsAndAddresses": { "message": "Freunde und Adressen hinzufügen, welchen Sie vertrauen" }, - "addFromAListOfPopularNetworks": { - "message": "Fügen Sie ein Netzwerk aus einer Liste beliebter Netzwerke hinzu oder fügen Sie es manuell hinzu. Interagieren Sie nur mit Organisationen, denen Sie vertrauen." - }, "addHardwareWallet": { "message": "Hardware-Wallet hinzufügen" }, @@ -267,16 +261,9 @@ "addMemo": { "message": "Notiz hinzufügen" }, - "addMoreNetworks": { - "message": "weitere Netzwerke manuell hinzufügen" - }, "addNetwork": { "message": "Netzwerk hinzufügen" }, - "addNetworkTooltipWarning": { - "message": "Diese Netzwerkverbindung ist von Dritten abhängig. Diese Verbindung könnte unzuverlässiger sein oder Dritten erlauben, Ihre Aktivitäten zu verfolgen. $1", - "description": "$1 is Learn more link" - }, "addNewAccount": { "message": "Ein neues Konto hinzufügen" }, @@ -1000,9 +987,6 @@ "confirmConnectionTitle": { "message": "Verbindung mit $1 bestätigen" }, - "confirmDeletion": { - "message": "Löschung bestätigen" - }, "confirmFieldPaymaster": { "message": "Gebühr bezahlt von" }, @@ -1015,9 +999,6 @@ "confirmRecoveryPhrase": { "message": "Geheime Wiederherstellungsphrase bestätigen" }, - "confirmRpcUrlDeletionMessage": { - "message": "Sind Sie sicher, dass Sie die RPC-URL löschen möchten? Ihre Informationen werden für dieses Netzwerk nicht gespeichert." - }, "confirmTitleDescPermitSignature": { "message": "Diese Website möchte die Genehmigung, Ihre Tokens auszugeben." }, @@ -1368,9 +1349,6 @@ "custom": { "message": "Erweitert" }, - "customContentSearch": { - "message": "Suche nach einem zuvor hinzugefügten Netzwerk" - }, "customGasSettingToolTipMessage": { "message": "$1 verwenden, um den Gas-Preis anzupassen. Das kann verwirrend sein, wenn Sie damit nicht vertraut sind. Interaktion auf eigene Gefahr.", "description": "$1 is key 'advanced' (text: 'Advanced') separated here so that it can be passed in with bold font-weight" @@ -1490,9 +1468,6 @@ "deleteContact": { "message": "Kontakt löschen" }, - "deleteNetwork": { - "message": "Netzwerk löschen?" - }, "deleteNetworkIntro": { "message": "Wenn Sie dieses Netzwerk löschen, müssen Sie es erneut hinzufügen, um Ihre Assets in diesem Netzwerk anzuzeigen." }, @@ -1500,9 +1475,6 @@ "message": "$1-Netzwerk löschen?", "description": "$1 represents the name of the network" }, - "deleteRpcUrl": { - "message": "RPC-URL löschen" - }, "deposit": { "message": "Einzahlung" }, @@ -1867,9 +1839,6 @@ "existingRequestsBannerAlertDesc": { "message": "Um sich Ihre jüngste Anfrage anzeigen zu lassen und diese zu bestätigen, müssen Sie zunächst bestehende Anfragen genehmigen oder ablehnen." }, - "existingRpcUrl": { - "message": "Diese URL ist mit einer anderen Chain-ID verknüpft." - }, "expandView": { "message": "Ansicht erweitern" }, @@ -1901,9 +1870,6 @@ "failedToFetchChainId": { "message": "Chain-ID konnte nicht abgerufen werden. Ist Ihre RPC-URL korrekt?" }, - "failedToFetchTickerSymbolData": { - "message": "Daten zur Überprüfung des Tickersymbols sind derzeit nicht verfügbar. Vergewissern Sie sich, dass das von Ihnen eingegebene Symbol korrekt ist. Es wirkt sich auf die Umrechnungskurse aus, die Sie für dieses Netzwerk sehen." - }, "failureMessage": { "message": "Etwas ist schiefgelaufen und wir konnten die Aktion nicht abschließen." }, @@ -1924,9 +1890,6 @@ "message": "Dateiimport fehlgeschlagen? Bitte hier klicken!", "description": "Helps user import their account from a JSON file" }, - "findTheRightChainId": { - "message": "Finden Sie das Richtige auf:" - }, "flaskWelcomeUninstall": { "message": "Sie sollten diese Erweiterung deinstallieren.", "description": "This request is shown on the Flask Welcome screen. It is intended for non-developers, and will be bolded." @@ -2416,9 +2379,6 @@ "invalidAssetType": { "message": "Dieses Asset ist ein NFT und muss auf der Seite „NFTs importieren“ unter dem Tab NFTs erneut hinzugefügt werden." }, - "invalidBlockExplorerURL": { - "message": "Ungültige Block Explorer URI" - }, "invalidChainIdTooBig": { "message": "Ungültige Chain-ID. Die Chain-ID ist zu groß." }, @@ -2938,9 +2898,6 @@ "network": { "message": "Netzwerk:" }, - "networkAddedSuccessfully": { - "message": "Netzwerk erfolgreich hinzugefügt!" - }, "networkDetails": { "message": "Netzwerkdetails" }, @@ -3001,9 +2958,6 @@ "networkProvider": { "message": "Netzwerkanbieter" }, - "networkSettingsChainIdDescription": { - "message": "Die Chain-ID wird zum Unterzeichnen von Transaktionen verwendet. Sie muss mit der vom Netzwerk zurückgegebenen Chain-ID übereinstimmen. Sie können eine Dezimalzahl oder eine Hexadezimalzahl mit vorangestelltem '0x' eingeben, aber wir zeigen die Zahl in Dezimalzahlen an." - }, "networkStatus": { "message": "Netzwerkstatus" }, @@ -3452,9 +3406,6 @@ "onboardingAdvancedPrivacyIPFSValid": { "message": "URL für IPFS-Gateway ist gültig" }, - "onboardingAdvancedPrivacyNetworkButton": { - "message": "Benutzerdefiniertes Netzwerk hinzufügen" - }, "onboardingAdvancedPrivacyNetworkDescription": { "message": "Wir verwenden Infura als Anbieter für ferngesteuerte Prozeduranrufe (RPC), um den verlässlichsten und vertraulichsten Zugriff auf Ethereum-Daten zu ermöglichen, den wir können. Sie können Ihren eigenen RPC auswählen, bedenken Sie aber, dass RPC Ihre IP-Adresse und Ihre Ethereum-Wallet erhalten wird, um Transaktionen durchzuführen. Lesen Sie unsere $1, um mehr darüber zu erfahren, wie Infura mit Ihren Daten umgeht." }, @@ -3483,9 +3434,6 @@ "onboardingMetametricsInfuraTermsPolicy": { "message": "Datenschutzrichtlinie" }, - "onboardingMetametricsModalTitle": { - "message": "Benutzerdefiniertes Netzwerk hinzufügen" - }, "onboardingMetametricsNeverCollect": { "message": "$1 Klicks und Aufrufe der App werden gespeichert, andere Details (wie Ihre öffentliche Adresse) jedoch nicht.", "description": "$1 represents `onboardingMetametricsNeverCollectEmphasis`" @@ -3578,9 +3526,6 @@ "onekey": { "message": "OneKey" }, - "onlyAddTrustedNetworks": { - "message": "Ein betrügerischer Netzwerkanbieter kann bezüglich des Status der Blockchain täuschen und Ihre Netzwerkaktivitäten aufzeichnen. Fügen Sie nur vertrauenswürdige Netzwerke hinzu." - }, "onlyConnectTrust": { "message": "Verbinden Sie sich nur mit Websites, denen Sie vertrauen. $1", "description": "Text displayed above the buttons for connection confirmation. $1 is the link to the learn more web page." @@ -3605,9 +3550,6 @@ "optional": { "message": "Optional" }, - "optionalWithParanthesis": { - "message": "(Optional)" - }, "options": { "message": "Optionen" }, @@ -3959,9 +3901,6 @@ "message": "+ $1 mehr", "description": "$1 is a number of additional but unshown items in a list- this message will be shown in place of those items" }, - "popularCustomNetworks": { - "message": "Beliebte benutzerdefinierte Netzwerke" - }, "popularNetworkAddToolTip": { "message": "Einige dieser Netzwerke werden von Dritten betrieben. Die Verbindungen können weniger zuverlässig sein oder Dritten ermöglichen, Aktivitäten zu verfolgen. $1", "description": "$1 is Learn more link" @@ -5298,9 +5237,6 @@ "suggestedTokenName": { "message": "Vorgeschlagener Name:" }, - "suggestedTokenSymbol": { - "message": "Vorgeschlagenes Tickersymbol:" - }, "support": { "message": "Support" }, @@ -5789,9 +5725,6 @@ "termsOfUseTitle": { "message": "Unsere Nutzungsbedingungen wurden aktualisiert." }, - "testNetworks": { - "message": "Test-Netzwerke" - }, "theme": { "message": "Motiv" }, @@ -6103,9 +6036,6 @@ "message": "U2F", "description": "A name on an API for the browser to interact with devices that support the U2F protocol. On some browsers we use it to connect MetaMask to Ledger devices." }, - "unMatchedChain": { - "message": "Laut unseren Aufzeichnungen stimmt diese URL nicht mit einem bekannten Anbieter für diese Chain-ID überein." - }, "unapproved": { "message": "Nicht genehmigt" }, @@ -6175,9 +6105,6 @@ "urlErrorMsg": { "message": "URIs benötigen die korrekten HTTP/HTTPS Präfixe." }, - "urlExistsErrorMsg": { - "message": "Diese URL wird derzeit vom $1-Netzwerk verwendet." - }, "use4ByteResolution": { "message": "Smart Contracts dekodieren" }, @@ -6387,9 +6314,6 @@ "whatsThis": { "message": "Was ist das?" }, - "wrongChainId": { - "message": "Diese Chain-ID stimmt nicht mit dem Netzwerknamen überein." - }, "wrongNetworkName": { "message": "Laut unseren Aufzeichnungen stimmt dieser Netzwerkname nicht mit dieser Chain-ID überein." }, @@ -6403,10 +6327,6 @@ "you": { "message": "Sie" }, - "youHaveAddedAll": { - "message": "Sie haben alle beliebten Netzwerke hinzugefügt. Sie können weitere Netzwerke entdecken $1 oder Sie können $2", - "description": "$1 is a link with the text 'here' and $2 is a button with the text 'add more networks manually'" - }, "youNeedToAllowCameraAccess": { "message": "Sie müssen Zugriff auf die Kamera erlauben, um diese Funktion nutzen zu können." }, diff --git a/app/_locales/el/messages.json b/app/_locales/el/messages.json index 3a191b6e9579..a8cee7a6aab1 100644 --- a/app/_locales/el/messages.json +++ b/app/_locales/el/messages.json @@ -183,9 +183,6 @@ "addANetwork": { "message": "Προσθήκη ενός δικτύου" }, - "addANetworkManually": { - "message": "Προσθήκη δικτύου με μη αυτόματο τρόπο" - }, "addANickname": { "message": "Προσθήκη ενός ψευδωνύμου" }, @@ -252,9 +249,6 @@ "addFriendsAndAddresses": { "message": "Προσθέστε φίλους και διευθύνσεις που εμπιστεύεστε" }, - "addFromAListOfPopularNetworks": { - "message": "Προσθέστε από μια λίστα δημοφιλών δικτύων ή προσθέστε ένα δίκτυο με μη αυτόματο τρόπο. Να αλληλεπιδράτε μόνο με τις οντότητες που εμπιστεύεστε." - }, "addHardwareWallet": { "message": "Προσθήκη πορτοφολιού υλικού" }, @@ -267,16 +261,9 @@ "addMemo": { "message": "Προσθήκη σημειώματος" }, - "addMoreNetworks": { - "message": "προσθέστε περισσότερα δίκτυα χειροκίνητα" - }, "addNetwork": { "message": "Προσθήκη δικτύου" }, - "addNetworkTooltipWarning": { - "message": "Αυτή η σύνδεση δικτύου βασίζεται σε τρίτους. H σύνδεση ενδέχεται να είναι λιγότερο αξιόπιστη ή να επιτρέπει σε τρίτους να παρακολουθούν τη δραστηριότητα. $1", - "description": "$1 is Learn more link" - }, "addNewAccount": { "message": "Προσθήκη νέου λογαριασμού Ethereum" }, @@ -1000,9 +987,6 @@ "confirmConnectionTitle": { "message": "Επιβεβαίωση σύνδεσης με το $1" }, - "confirmDeletion": { - "message": "Επιβεβαίωση διαγραφής" - }, "confirmFieldPaymaster": { "message": "Τα τέλη καταβλήθηκαν από" }, @@ -1015,9 +999,6 @@ "confirmRecoveryPhrase": { "message": "Επιβεβαιώστε τη Μυστική Φράση Ανάκτησης" }, - "confirmRpcUrlDeletionMessage": { - "message": "Είστε σίγουροι ότι θέλετε να διαγράψετε τη διεύθυνση URL RPC; Οι πληροφορίες σας δεν θα αποθηκευτούν για αυτό το δίκτυο." - }, "confirmTitleDescPermitSignature": { "message": "Αυτός ο ιστότοπος ζητάει άδεια για να δαπανήσει τα tokens σας." }, @@ -1368,9 +1349,6 @@ "custom": { "message": "Σύνθετες" }, - "customContentSearch": { - "message": "Αναζήτηση δικτύου που προστέθηκε προηγουμένως" - }, "customGasSettingToolTipMessage": { "message": "Χρησιμοποιήστε το $1 για να προσαρμόσετε την τιμή του τέλους συναλλαγής. Αυτό μπορεί να προκαλέσει σύγχυση, αν δεν είστε εξοικειωμένοι. Αλληλεπίδραση με δική σας ευθύνη.", "description": "$1 is key 'advanced' (text: 'Advanced') separated here so that it can be passed in with bold font-weight" @@ -1490,9 +1468,6 @@ "deleteContact": { "message": "Διαγραφή επαφής" }, - "deleteNetwork": { - "message": "Διαγραφή δικτύου;" - }, "deleteNetworkIntro": { "message": "Εάν διαγράψετε αυτό το δίκτυο, θα πρέπει να το προσθέσετε ξανά για να δείτε τα περιουσιακά σας στοιχεία σε αυτό το δίκτυο" }, @@ -1500,9 +1475,6 @@ "message": "Διαγραφή του δικτύου $1;", "description": "$1 represents the name of the network" }, - "deleteRpcUrl": { - "message": "Διαγραφή της διεύθυνσης URL RPC" - }, "deposit": { "message": "Κατάθεση" }, @@ -1867,9 +1839,6 @@ "existingRequestsBannerAlertDesc": { "message": "Για να προβάλετε και να επιβεβαιώσετε το πιο πρόσφατο αίτημά σας, θα πρέπει πρώτα να εγκρίνετε ή να απορρίψετε τα υπάρχοντα αιτήματα." }, - "existingRpcUrl": { - "message": "Αυτή η διεύθυνση URL συνδέεται με ένα άλλο αναγνωριστικό αλυσίδας." - }, "expandView": { "message": "Ανάπτυξη προβολής" }, @@ -1901,9 +1870,6 @@ "failedToFetchChainId": { "message": "Δεν είναι δυνατή η ανάκτηση του αναγνωριστικού αλυσίδας. Είναι σωστή η διεύθυνση URL του RPC;" }, - "failedToFetchTickerSymbolData": { - "message": "Τα δεδομένα επαλήθευσης αυτού του συμβόλου είναι προς το παρόν μη διαθέσιμα. Βεβαιωθείτε ότι το σύμβολο που έχετε εισάγει είναι σωστό. Αυτό θα επηρεάσει τα ποσοστά μετατροπών που βλέπετε για αυτό το δίκτυο" - }, "failureMessage": { "message": "Κάτι πήγε στραβά και δεν μπορέσαμε να ολοκληρώσουμε την ενέργεια" }, @@ -1924,9 +1890,6 @@ "message": "Η εισαγωγή αρχείων δεν λειτουργεί; Κάντε κλικ εδώ!", "description": "Helps user import their account from a JSON file" }, - "findTheRightChainId": { - "message": "Βρείτε το σωστό στο:" - }, "flaskWelcomeUninstall": { "message": "θα πρέπει να απεγκαταστήσετε αυτή την επέκταση", "description": "This request is shown on the Flask Welcome screen. It is intended for non-developers, and will be bolded." @@ -2416,9 +2379,6 @@ "invalidAssetType": { "message": "Αυτό το περιουσιακό στοιχείο είναι NFT και πρέπει να προστεθεί ξανά στη σελίδα «Εισαγωγή NFT» που βρίσκεται στην καρτέλα των NFT" }, - "invalidBlockExplorerURL": { - "message": "Μη έγκυρη διεύθυνση του Block Explorer" - }, "invalidChainIdTooBig": { "message": "Μη έγκυρο αναγνωριστικό αλυσίδας. Το αναγνωριστικό αλυσίδας είναι πολύ μεγάλο." }, @@ -2938,9 +2898,6 @@ "network": { "message": "Δίκτυο:" }, - "networkAddedSuccessfully": { - "message": "Το δίκτυο προστέθηκε με επιτυχία!" - }, "networkDetails": { "message": "Λεπτομέρειες Δικτύου" }, @@ -3001,9 +2958,6 @@ "networkProvider": { "message": "Πάροχος δικτύου" }, - "networkSettingsChainIdDescription": { - "message": "Το αναγνωριστικό αλυσίδας χρησιμοποιείται για την υπογραφή συναλλαγών. Πρέπει να ταιριάζει με το αναγνωριστικό αλυσίδας που επιστρέφεται από το δίκτυο. Μπορείτε να εισαγάγετε έναν δεκαδικό ή δεκαεξαδικό αριθμό με πρόθεμα \"0x\", αλλά εμείς θα εμφανίσουμε τον αριθμό στο δεκαδικό σύστημα." - }, "networkStatus": { "message": "Κατάσταση δικτύου" }, @@ -3452,9 +3406,6 @@ "onboardingAdvancedPrivacyIPFSValid": { "message": "Η διεύθυνση URL της πύλης IPFS είναι έγκυρη" }, - "onboardingAdvancedPrivacyNetworkButton": { - "message": "Προσθήκη προσαρμοσμένου δικτύου" - }, "onboardingAdvancedPrivacyNetworkDescription": { "message": "Χρησιμοποιούμε την Infura ως την υπηρεσία κλήσης απομακρυσμένης διαδικασίας (RPC) για να προσφέρουμε την πιο αξιόπιστη και ιδιωτική πρόσβαση στα δεδομένα του Ethereum που μπορούμε. Μπορείτε να επιλέξετε τη δική σας RPC, αλλά να θυμάστε ότι οποιαδήποτε RPC θα λαμβάνει τη διεύθυνση IP και το πορτοφόλι σας στο Ethereum για να πραγματοποιεί συναλλαγές. Διαβάστε το $1 για να μάθετε περισσότερα σχετικά με τον τρόπο με τον οποίο η Infura χειρίζεται τα δεδομένα." }, @@ -3483,9 +3434,6 @@ "onboardingMetametricsInfuraTermsPolicy": { "message": "Πολιτική Απορρήτου" }, - "onboardingMetametricsModalTitle": { - "message": "Προσθήκη προσαρμοσμένου δικτύου" - }, "onboardingMetametricsNeverCollect": { "message": "$1 τα κλικ και οι προβολές στην εφαρμογή αποθηκεύονται, ωστόσο άλλα στοιχεία (όπως η δημόσια διεύθυνσή σας) όχι.", "description": "$1 represents `onboardingMetametricsNeverCollectEmphasis`" @@ -3578,9 +3526,6 @@ "onekey": { "message": "OneKey" }, - "onlyAddTrustedNetworks": { - "message": "Ένας κακόβουλος πάροχος δικτύου μπορεί να πει ψέματα σχετικά με την κατάσταση του blockchain και να καταγράψει τη δραστηριότητα του δικτύου σας. Προσθέστε μόνο προσαρμοσμένα δίκτυα που εμπιστεύεστε." - }, "onlyConnectTrust": { "message": "Συνδεθείτε μόνο με ιστότοπους που εμπιστεύεστε. $1", "description": "Text displayed above the buttons for connection confirmation. $1 is the link to the learn more web page." @@ -3605,9 +3550,6 @@ "optional": { "message": "Προαιρετικά" }, - "optionalWithParanthesis": { - "message": "(Προαιρετικά)" - }, "options": { "message": "Επιλογές" }, @@ -3959,9 +3901,6 @@ "message": "+ $1 ακόμη", "description": "$1 is a number of additional but unshown items in a list- this message will be shown in place of those items" }, - "popularCustomNetworks": { - "message": "Δημοφιλή προσαρμοσμένα δίκτυα" - }, "popularNetworkAddToolTip": { "message": "Ορισμένα από αυτά τα δίκτυα βασίζονται σε τρίτους. Οι συνδέσεις μπορεί να είναι λιγότερο αξιόπιστες ή να επιτρέπουν σε τρίτους να παρακολουθούν τη δραστηριότητα. $1", "description": "$1 is Learn more link" @@ -5298,9 +5237,6 @@ "suggestedTokenName": { "message": "Προτεινόμενο όνομα:" }, - "suggestedTokenSymbol": { - "message": "Προτεινόμενο σύμβολο μετοχής:" - }, "support": { "message": "Υποστήριξη" }, @@ -5789,9 +5725,6 @@ "termsOfUseTitle": { "message": "Οι Όροι Χρήσης μας έχουν ενημερωθεί" }, - "testNetworks": { - "message": "Δοκιμαστικά δίκτυα" - }, "theme": { "message": "Θέμα" }, @@ -6103,9 +6036,6 @@ "message": "U2F", "description": "A name on an API for the browser to interact with devices that support the U2F protocol. On some browsers we use it to connect MetaMask to Ledger devices." }, - "unMatchedChain": { - "message": "Σύμφωνα με τα αρχεία μας, αυτή η διεύθυνση URL δεν ταιριάζει με γνωστό πάροχο για αυτό το αναγνωριστικό αλυσίδας." - }, "unapproved": { "message": "Μη εγκεκριμένο" }, @@ -6175,9 +6105,6 @@ "urlErrorMsg": { "message": "Οι διευθύνσεις URL απαιτούν το κατάλληλο πρόθεμα HTTP/HTTPS." }, - "urlExistsErrorMsg": { - "message": "Αυτή η διεύθυνση URL χρησιμοποιείται επί του παρόντος από το δίκτυο $1." - }, "use4ByteResolution": { "message": "Αποκωδικοποίηση έξυπνων συμβολαίων" }, @@ -6387,9 +6314,6 @@ "whatsThis": { "message": "Τι είναι αυτό;" }, - "wrongChainId": { - "message": "Αυτό το αναγνωριστικό αλυσίδας δεν ταιριάζει με το όνομα του δικτύου." - }, "wrongNetworkName": { "message": "Σύμφωνα με τα αρχεία μας, το όνομα του δικτύου ενδέχεται να μην αντιστοιχεί με αυτό το αναγνωριστικό αλυσίδας." }, @@ -6403,10 +6327,6 @@ "you": { "message": "Εσείς" }, - "youHaveAddedAll": { - "message": "Προσθέσατε όλα τα δημοφιλή δίκτυα. Μπορείτε να ανακαλύψετε περισσότερα δίκτυα $1 Ή μπορείτε να $2", - "description": "$1 is a link with the text 'here' and $2 is a button with the text 'add more networks manually'" - }, "youNeedToAllowCameraAccess": { "message": "Πρέπει να επιτρέψετε την πρόσβαση στην κάμερα για να χρησιμοποιήσετε αυτή τη λειτουργία." }, diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 5aee8cd393aa..614318e22c89 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -186,15 +186,18 @@ "add": { "message": "Add" }, + "addACustomNetwork": { + "message": "Add a custom network" + }, "addANetwork": { "message": "Add a network" }, - "addANetworkManually": { - "message": "Add a network manually" - }, "addANickname": { "message": "Add a nickname" }, + "addAUrl": { + "message": "Add a URL" + }, "addAccount": { "message": "Add account" }, @@ -210,6 +213,9 @@ "addBlockExplorer": { "message": "Add a block explorer" }, + "addBlockExplorerUrl": { + "message": "Add a block explorer URL" + }, "addContact": { "message": "Add contact" }, @@ -261,9 +267,6 @@ "addFriendsAndAddresses": { "message": "Add friends and addresses you trust" }, - "addFromAListOfPopularNetworks": { - "message": "Add from a list of popular networks or add a network manually. Only interact with the entities you trust." - }, "addHardwareWallet": { "message": "Add hardware wallet" }, @@ -276,9 +279,6 @@ "addMemo": { "message": "Add memo" }, - "addMoreNetworks": { - "message": "add more networks manually" - }, "addNetwork": { "message": "Add network" }, @@ -286,10 +286,6 @@ "message": "Add $1", "description": "$1 represents network name" }, - "addNetworkTooltipWarning": { - "message": "This network connection relies on third parties. This connection may be less reliable or enable third-parties to track activity. $1", - "description": "$1 is Learn more link" - }, "addNewAccount": { "message": "Add a new Ethereum account" }, @@ -1033,9 +1029,6 @@ "confirmConnectionTitle": { "message": "Confirm connection to $1" }, - "confirmDeletion": { - "message": "Confirm deletion" - }, "confirmFieldPaymaster": { "message": "Fee paid by" }, @@ -1048,9 +1041,6 @@ "confirmRecoveryPhrase": { "message": "Confirm Secret Recovery Phrase" }, - "confirmRpcUrlDeletionMessage": { - "message": "Are you sure you want to delete the RPC URL? Your information will not be saved for this network." - }, "confirmTitleApproveTransaction": { "message": "Allowance request" }, @@ -1425,9 +1415,6 @@ "custom": { "message": "Advanced" }, - "customContentSearch": { - "message": "Search for a previously added network" - }, "customGasSettingToolTipMessage": { "message": "Use $1 to customize the gas price. This can be confusing if you aren’t familiar. Interact at your own risk.", "description": "$1 is key 'advanced' (text: 'Advanced') separated here so that it can be passed in with bold font-weight" @@ -1547,9 +1534,6 @@ "deleteContact": { "message": "Delete contact" }, - "deleteNetwork": { - "message": "Delete network?" - }, "deleteNetworkIntro": { "message": "If you delete this network, you will need to add it again to view your assets in this network" }, @@ -1557,9 +1541,6 @@ "message": "Delete $1 network?", "description": "$1 represents the name of the network" }, - "deleteRpcUrl": { - "message": "Delete RPC URL" - }, "deposit": { "message": "Deposit" }, @@ -1824,7 +1805,7 @@ "message": "Request encryption public key" }, "endpointReturnedDifferentChainId": { - "message": "The RPC URL you have entered returned a different chain ID ($1). Please update the Chain ID to match the RPC URL of the network you are trying to add.", + "message": "The RPC URL you have entered returned a different chain ID ($1).", "description": "$1 is the return value of eth_chainId from an RPC endpoint" }, "enhancedTokenDetectionAlertMessage": { @@ -1854,12 +1835,18 @@ "enterANumber": { "message": "Enter a number" }, + "enterChainId": { + "message": "Enter Chain ID" + }, "enterCustodianToken": { "message": "Enter your $1 token or add a new token" }, "enterMaxSpendLimit": { "message": "Enter max spend limit" }, + "enterNetworkName": { + "message": "Enter network name" + }, "enterOptionalPassword": { "message": "Enter optional password" }, @@ -1869,6 +1856,9 @@ "enterRpcUrl": { "message": "Enter RPC URL" }, + "enterSymbol": { + "message": "Enter symbol" + }, "enterTokenNameOrAddress": { "message": "Enter token name or paste address" }, @@ -1948,9 +1938,6 @@ "existingRequestsBannerAlertDesc": { "message": "To view and confirm your most recent request, you'll need to approve or reject existing requests first." }, - "existingRpcUrl": { - "message": "This URL is associated with another chain ID." - }, "expandView": { "message": "Expand view" }, @@ -1991,9 +1978,6 @@ "failedToFetchChainId": { "message": "Could not fetch chain ID. Is your RPC URL correct?" }, - "failedToFetchTickerSymbolData": { - "message": "Ticker symbol verification data is currently unavailable, make sure that the symbol you have entered is correct. It will impact the conversion rates that you see for this network" - }, "failureMessage": { "message": "Something went wrong, and we were unable to complete the action" }, @@ -2014,9 +1998,6 @@ "message": "File import not working? Click here!", "description": "Helps user import their account from a JSON file" }, - "findTheRightChainId": { - "message": "Find the right one on:" - }, "flaskWelcomeUninstall": { "message": "you should uninstall this extension", "description": "This request is shown on the Flask Welcome screen. It is intended for non-developers, and will be bolded." @@ -2512,9 +2493,6 @@ "invalidAssetType": { "message": "This asset is an NFT and needs to be re-added on the Import NFTs page found under the NFTs tab" }, - "invalidBlockExplorerURL": { - "message": "Invalid block explorer URL" - }, "invalidChainIdTooBig": { "message": "Invalid chain ID. The chain ID is too big." }, @@ -3040,9 +3018,6 @@ "network": { "message": "Network:" }, - "networkAddedSuccessfully": { - "message": "Network added successfully!" - }, "networkDetails": { "message": "Network details" }, @@ -3106,9 +3081,6 @@ "networkProvider": { "message": "Network provider" }, - "networkSettingsChainIdDescription": { - "message": "The chain ID is used for signing transactions. It must match the chain ID returned by the network. You can enter a decimal or '0x'-prefixed hexadecimal number, but we will display the number in decimal." - }, "networkStatus": { "message": "Network status" }, @@ -3194,6 +3166,9 @@ "newPrivacyPolicyTitle": { "message": "We’ve updated our privacy policy" }, + "newRpcUrl": { + "message": "New RPC URL" + }, "newTokensImportedMessage": { "message": "You’ve successfully imported $1.", "description": "$1 is the string of symbols of all the tokens imported" @@ -3565,9 +3540,6 @@ "onboardingAdvancedPrivacyIPFSValid": { "message": "IPFS gateway URL is valid" }, - "onboardingAdvancedPrivacyNetworkButton": { - "message": "Add custom network" - }, "onboardingAdvancedPrivacyNetworkDescription": { "message": "We use Infura as our remote procedure call (RPC) provider to offer the most reliable and private access to Ethereum data we can. You can choose your own RPC, but remember that any RPC will receive your IP address and Ethereum wallet to make transactions. Read our $1 to learn more about how Infura handles data." }, @@ -3596,9 +3568,6 @@ "onboardingMetametricsInfuraTermsPolicy": { "message": "Privacy Policy" }, - "onboardingMetametricsModalTitle": { - "message": "Add custom network" - }, "onboardingMetametricsNeverCollect": { "message": "$1 clicks and views on the app are stored, but other details (like your public address) are not.", "description": "$1 represents `onboardingMetametricsNeverCollectEmphasis`" @@ -3691,9 +3660,6 @@ "onekey": { "message": "OneKey" }, - "onlyAddTrustedNetworks": { - "message": "A malicious network provider can lie about the state of the blockchain and record your network activity. Only add custom networks you trust." - }, "onlyConnectTrust": { "message": "Only connect with sites you trust. $1", "description": "Text displayed above the buttons for connection confirmation. $1 is the link to the learn more web page." @@ -3718,9 +3684,6 @@ "optional": { "message": "Optional" }, - "optionalWithParanthesis": { - "message": "(Optional)" - }, "options": { "message": "Options" }, @@ -4080,9 +4043,6 @@ "message": "+ $1 more", "description": "$1 is a number of additional but unshown items in a list- this message will be shown in place of those items" }, - "popularCustomNetworks": { - "message": "Popular custom networks" - }, "popularNetworkAddToolTip": { "message": "Some of these networks rely on third parties. The connections may be less reliable or enable third-parties to track activity. $1", "description": "$1 is Learn more link" @@ -4547,7 +4507,7 @@ "message": "RPC Name (Optional)" }, "rpcUrl": { - "message": "New RPC URL" + "message": "RPC URL" }, "safeTransferFrom": { "message": "Safe transfer from" @@ -4702,6 +4662,9 @@ "selectPathHelp": { "message": "If you don't see the accounts you expect, try switching the HD path or current selected network." }, + "selectRpcUrl": { + "message": "Select RPC URL" + }, "selectType": { "message": "Select Type" }, @@ -5444,12 +5407,12 @@ "message": "Suggested by $1", "description": "$1 is the snap name" }, + "suggestedCurrencySymbol": { + "message": "Suggested currency symbol:" + }, "suggestedTokenName": { "message": "Suggested name:" }, - "suggestedTokenSymbol": { - "message": "Suggested ticker symbol:" - }, "support": { "message": "Support" }, @@ -5941,9 +5904,6 @@ "termsOfUseTitle": { "message": "Our Terms of Use have updated" }, - "testNetworks": { - "message": "Test networks" - }, "testnets": { "message": "Testnets" }, @@ -6261,9 +6221,6 @@ "message": "U2F", "description": "A name on an API for the browser to interact with devices that support the U2F protocol. On some browsers we use it to connect MetaMask to Ledger devices." }, - "unMatchedChain": { - "message": "According to our records, this URL does not match a known provider for this chain ID." - }, "unapproved": { "message": "Unapproved" }, @@ -6318,6 +6275,13 @@ "update": { "message": "Update" }, + "updateEthereumChainConfirmationDescription": { + "message": "This site is requesting to update your default network URL. You can edit defaults and network information any time." + }, + "updateNetworkConfirmationTitle": { + "message": "Update $1", + "description": "$1 represents network name" + }, "updateOrEditNetworkInformations": { "message": "Update your information or" }, @@ -6339,9 +6303,6 @@ "urlErrorMsg": { "message": "URLs require the appropriate HTTP/HTTPS prefix." }, - "urlExistsErrorMsg": { - "message": "This URL is currently used by the $1 network." - }, "use4ByteResolution": { "message": "Decode smart contracts" }, @@ -6558,9 +6519,6 @@ "whatsThis": { "message": "What's this?" }, - "wrongChainId": { - "message": "This chain ID doesn’t match the network name." - }, "wrongNetworkName": { "message": "According to our records, the network name may not correctly match this chain ID." }, @@ -6574,10 +6532,6 @@ "you": { "message": "You" }, - "youHaveAddedAll": { - "message": "You've added all the popular networks. You can discover more networks $1 Or you can $2", - "description": "$1 is a link with the text 'here' and $2 is a button with the text 'add more networks manually'" - }, "youNeedToAllowCameraAccess": { "message": "You need to allow camera access to use this feature." }, diff --git a/app/_locales/es/messages.json b/app/_locales/es/messages.json index 88524f86f3cc..2346fda97d16 100644 --- a/app/_locales/es/messages.json +++ b/app/_locales/es/messages.json @@ -183,9 +183,6 @@ "addANetwork": { "message": "Agregar una red" }, - "addANetworkManually": { - "message": "Agregar una red manualmente" - }, "addANickname": { "message": "Añadir un apodo" }, @@ -252,9 +249,6 @@ "addFriendsAndAddresses": { "message": "Agregue amigos y direcciones de confianza" }, - "addFromAListOfPopularNetworks": { - "message": "Agregue desde una lista de redes populares o agregue una red manualmente. Interactúe solo con las entidades en las que confía." - }, "addHardwareWallet": { "message": "Agregar monedero físico" }, @@ -267,16 +261,9 @@ "addMemo": { "message": "Añadir memo" }, - "addMoreNetworks": { - "message": "agregar más redes manualmente" - }, "addNetwork": { "message": "Agregar red" }, - "addNetworkTooltipWarning": { - "message": "Esta conexión de red depende de terceros. Y puede ser menos confiable o permite que terceros rastreen la actividad. $1", - "description": "$1 is Learn more link" - }, "addNewAccount": { "message": "Añadir una cuenta nueva de Ethereum" }, @@ -997,9 +984,6 @@ "confirmConnectionTitle": { "message": "Confirmar conexión a $1" }, - "confirmDeletion": { - "message": "Confirmar la eliminación" - }, "confirmFieldPaymaster": { "message": "Tarifa pagada por" }, @@ -1012,9 +996,6 @@ "confirmRecoveryPhrase": { "message": "Confirmar frase secreta de recuperación" }, - "confirmRpcUrlDeletionMessage": { - "message": "¿Está seguro de que desea eliminar la URL RPC? Su información no se guardará para esta red." - }, "confirmTitleDescPermitSignature": { "message": "Este sitio solicita permiso para gastar sus tokens." }, @@ -1365,9 +1346,6 @@ "custom": { "message": "Avanzado" }, - "customContentSearch": { - "message": "Buscar una red agregada anteriormente" - }, "customGasSettingToolTipMessage": { "message": "Use $1 para personalizar el precio de gas. Esto puede ser confuso si no está familiarizado. Interactúe bajo su propio riesgo.", "description": "$1 is key 'advanced' (text: 'Advanced') separated here so that it can be passed in with bold font-weight" @@ -1487,9 +1465,6 @@ "deleteContact": { "message": "Eliminar contacto" }, - "deleteNetwork": { - "message": "¿Eliminar red?" - }, "deleteNetworkIntro": { "message": "Si elimina esta red, deberá volver a agregarla para ver sus activos en esta red" }, @@ -1497,9 +1472,6 @@ "message": "¿Eliminar la red de $1?", "description": "$1 represents the name of the network" }, - "deleteRpcUrl": { - "message": "Eliminar URL RPC" - }, "deposit": { "message": "Depositar" }, @@ -1864,9 +1836,6 @@ "existingRequestsBannerAlertDesc": { "message": "Para ver y confirmar su solicitud más reciente, primero deberá aprobar o rechazar las solicitudes existentes." }, - "existingRpcUrl": { - "message": "Esta URL está asociada a otro ID de cadena." - }, "expandView": { "message": "Expandir vista" }, @@ -1898,9 +1867,6 @@ "failedToFetchChainId": { "message": "No se pudo capturar el id. de cadena. ¿La dirección URL de RPC es correcta?" }, - "failedToFetchTickerSymbolData": { - "message": "Los datos de verificación del símbolo de teletipo no están disponibles actualmente, asegúrese de que el símbolo que ingresó sea correcto. Tendrá un impacto en las tasas de conversión que vea para esta red" - }, "failureMessage": { "message": "Se produjo un error y no pudimos completar la acción" }, @@ -1921,9 +1887,6 @@ "message": "¿No funciona la importación del archivo? Haga clic aquí.", "description": "Helps user import their account from a JSON file" }, - "findTheRightChainId": { - "message": "Encuentre el correcto en:" - }, "flaskWelcomeUninstall": { "message": "le recomendamos que desinstale esta extensión", "description": "This request is shown on the Flask Welcome screen. It is intended for non-developers, and will be bolded." @@ -2413,9 +2376,6 @@ "invalidAssetType": { "message": "Este activo es un NFT y debe volver a añadirse en la página de Importar NFTs que se encuentra en la pestaña de NFTs" }, - "invalidBlockExplorerURL": { - "message": "Dirección URL del explorador de bloques no válida" - }, "invalidChainIdTooBig": { "message": "Identificador de cadena no válido. El identificador de cadena es demasiado grande." }, @@ -2935,9 +2895,6 @@ "network": { "message": "Red:" }, - "networkAddedSuccessfully": { - "message": "¡Red añadida exitosamente!" - }, "networkDetails": { "message": "Detalles de la red" }, @@ -2998,9 +2955,6 @@ "networkProvider": { "message": "Proveedor de red" }, - "networkSettingsChainIdDescription": { - "message": "El id. de la cadena se usa para firmar transacciones. Debe coincidir con el id. de la cadena que devuelve la red. Puede escribir un número decimal o un número hexadecimal con el prefijo “0x”, pero el número se mostrará en decimal." - }, "networkStatus": { "message": "Estado de la red" }, @@ -3449,9 +3403,6 @@ "onboardingAdvancedPrivacyIPFSValid": { "message": "La URL de la puerta de enlace de IPFS es válida" }, - "onboardingAdvancedPrivacyNetworkButton": { - "message": "Agregar red personalizada" - }, "onboardingAdvancedPrivacyNetworkDescription": { "message": "Usamos Infura como nuestro proveedor de llamada a procedimiento remoto (RPC) para ofrecer el acceso más confiable y privado posible a los datos de Ethereum. Puede elegir su propio RPC, pero recuerde que cualquier RPC recibirá su dirección IP y su monedero de Ethereum para realizar transacciones. Lea nuestra $1 para obtener más información sobre cómo Infura maneja los datos." }, @@ -3480,9 +3431,6 @@ "onboardingMetametricsInfuraTermsPolicy": { "message": "Política de privacidad" }, - "onboardingMetametricsModalTitle": { - "message": "Agregar red personalizada" - }, "onboardingMetametricsNeverCollect": { "message": "Se almacenan los clics y las visualizaciones de $1 en la aplicación, pero no otros detalles (como su dirección pública).", "description": "$1 represents `onboardingMetametricsNeverCollectEmphasis`" @@ -3575,9 +3523,6 @@ "onekey": { "message": "OneKey" }, - "onlyAddTrustedNetworks": { - "message": "Un proveedor de red malintencionado puede mentir sobre el estado de la cadena de bloques y registrar su actividad de red. Agregue solo redes personalizadas de confianza." - }, "onlyConnectTrust": { "message": "Conéctese solo con sitios de confianza. $1", "description": "Text displayed above the buttons for connection confirmation. $1 is the link to the learn more web page." @@ -3602,9 +3547,6 @@ "optional": { "message": "Opcional" }, - "optionalWithParanthesis": { - "message": "(Opcional)" - }, "options": { "message": "Opciones" }, @@ -3956,9 +3898,6 @@ "message": "+ $1 más", "description": "$1 is a number of additional but unshown items in a list- this message will be shown in place of those items" }, - "popularCustomNetworks": { - "message": "Redes populares personalizadas" - }, "popularNetworkAddToolTip": { "message": "Algunas de estas redes dependen de terceros. Las conexiones pueden ser menos confiables o permitir que terceros realicen un seguimiento de la actividad. $1", "description": "$1 is Learn more link" @@ -5295,9 +5234,6 @@ "suggestedTokenName": { "message": "Nombre sugerido:" }, - "suggestedTokenSymbol": { - "message": "Símbolo de cotización sugerido:" - }, "support": { "message": "Soporte técnico" }, @@ -5786,9 +5722,6 @@ "termsOfUseTitle": { "message": "Nuestros Términos de uso han sido actualizados" }, - "testNetworks": { - "message": "Redes de prueba" - }, "theme": { "message": "Tema" }, @@ -6100,9 +6033,6 @@ "message": "U2F", "description": "A name on an API for the browser to interact with devices that support the U2F protocol. On some browsers we use it to connect MetaMask to Ledger devices." }, - "unMatchedChain": { - "message": "Según nuestros registros, esta URL no coincide con un proveedor conocido para este ID de cadena." - }, "unapproved": { "message": "No aprobado" }, @@ -6172,9 +6102,6 @@ "urlErrorMsg": { "message": "Las direcciones URL requieren el prefijo HTTP/HTTPS adecuado." }, - "urlExistsErrorMsg": { - "message": "En este momento, la red $1 está utilizando esta dirección URL." - }, "use4ByteResolution": { "message": "Decodificar contratos inteligentes" }, @@ -6384,9 +6311,6 @@ "whatsThis": { "message": "¿Qué es esto?" }, - "wrongChainId": { - "message": "Este ID de cadena no coincide con el nombre de la red." - }, "wrongNetworkName": { "message": "Según nuestros registros, es posible que el nombre de la red no coincida correctamente con este ID de cadena." }, @@ -6400,10 +6324,6 @@ "you": { "message": "Usted" }, - "youHaveAddedAll": { - "message": "Ha agregado todas las redes populares. Puede descubrir más redes $1 o puede $2", - "description": "$1 is a link with the text 'here' and $2 is a button with the text 'add more networks manually'" - }, "youNeedToAllowCameraAccess": { "message": "Necesita permitir el acceso a la cámara para usar esta función." }, diff --git a/app/_locales/es_419/messages.json b/app/_locales/es_419/messages.json index 1dd375450a81..1b8bc945343b 100644 --- a/app/_locales/es_419/messages.json +++ b/app/_locales/es_419/messages.json @@ -534,9 +534,6 @@ "delete": { "message": "Eliminar" }, - "deleteNetwork": { - "message": "¿Eliminar red?" - }, "description": { "message": "Descripción" }, @@ -1014,9 +1011,6 @@ "invalidAddressRecipient": { "message": "La dirección del destinatario no es válida" }, - "invalidBlockExplorerURL": { - "message": "Dirección URL del explorador de bloques no válida" - }, "invalidChainIdTooBig": { "message": "ID de cadena no válido. El identificador de cadena es demasiado grande." }, @@ -1263,9 +1257,6 @@ "networkNameTestnet": { "message": "Red de prueba" }, - "networkSettingsChainIdDescription": { - "message": "El ID de la cadena se usa para firmar transacciones. Debe coincidir con el ID de la cadena que devuelve la red. Puede escribir un número decimal o un número hexadecimal con el prefijo “0x”, pero el número se mostrará en decimal." - }, "networkStatus": { "message": "Estado de la red" }, @@ -1420,9 +1411,6 @@ "onboardingPinExtensionTitle": { "message": "¡Su instalación de MetaMask ha finalizado!" }, - "onlyAddTrustedNetworks": { - "message": "Un proveedor de red malintencionado puede mentir sobre el estado de la cadena de bloques y registrar su actividad de red. Agregue solo redes personalizadas de confianza." - }, "onlyConnectTrust": { "message": "Conéctese solo con sitios de confianza.", "description": "Text displayed above the buttons for connection confirmation. $1 is the link to the learn more web page." @@ -1434,9 +1422,6 @@ "optional": { "message": "Opcional" }, - "optionalWithParanthesis": { - "message": "(Opcional)" - }, "or": { "message": "o" }, @@ -2426,9 +2411,6 @@ "urlErrorMsg": { "message": "Las direcciones URL requieren el prefijo HTTP/HTTPS adecuado." }, - "urlExistsErrorMsg": { - "message": "En este momento, la red $1 está utilizando esta dirección URL." - }, "useNftDetection": { "message": "Autodetectar NFT" }, diff --git a/app/_locales/et/messages.json b/app/_locales/et/messages.json index 1e9bdffeb20e..acebcc9091da 100644 --- a/app/_locales/et/messages.json +++ b/app/_locales/et/messages.json @@ -205,9 +205,6 @@ "delete": { "message": "Kustuta" }, - "deleteNetwork": { - "message": "Võrk kustutada?" - }, "details": { "message": "Üksikasjad" }, @@ -347,9 +344,6 @@ "invalidAddressRecipient": { "message": "Saaja aadress on vale" }, - "invalidBlockExplorerURL": { - "message": "Vale Block Explorer URL" - }, "invalidRPC": { "message": "Vale RPC URL" }, diff --git a/app/_locales/fa/messages.json b/app/_locales/fa/messages.json index 8fe574e7a81a..c9c1bafdc7bf 100644 --- a/app/_locales/fa/messages.json +++ b/app/_locales/fa/messages.json @@ -205,9 +205,6 @@ "delete": { "message": "حذف" }, - "deleteNetwork": { - "message": "شبکه حذف شود؟" - }, "details": { "message": "جزئیات" }, @@ -351,9 +348,6 @@ "invalidAddressRecipient": { "message": "آدرس دریافت کننده نامعتبر است" }, - "invalidBlockExplorerURL": { - "message": "Block Explorer URL نا معتبر" - }, "invalidRPC": { "message": "RPC URL نا معتبر" }, diff --git a/app/_locales/fi/messages.json b/app/_locales/fi/messages.json index b69440707992..1c9cdb7c7a43 100644 --- a/app/_locales/fi/messages.json +++ b/app/_locales/fi/messages.json @@ -205,9 +205,6 @@ "delete": { "message": "Poista" }, - "deleteNetwork": { - "message": "Poistetaanko verkko?" - }, "details": { "message": "Tiedot" }, @@ -351,9 +348,6 @@ "invalidAddressRecipient": { "message": "Vastaanottajan osoite on virheellinen" }, - "invalidBlockExplorerURL": { - "message": "Virheellinen Block Explorer URL-osoite" - }, "invalidRPC": { "message": "Virheellinen RPC:n URL-osoite" }, diff --git a/app/_locales/fil/messages.json b/app/_locales/fil/messages.json index a437fb1e7100..e08c88bd7ffa 100644 --- a/app/_locales/fil/messages.json +++ b/app/_locales/fil/messages.json @@ -181,9 +181,6 @@ "delete": { "message": "I-delete" }, - "deleteNetwork": { - "message": "I-delete ang Network?" - }, "details": { "message": "Mga Detalye" }, @@ -308,9 +305,6 @@ "invalidAddressRecipient": { "message": "Hindi valid ang address ng recipient" }, - "invalidBlockExplorerURL": { - "message": "Hindi valid ang Block Explorer URL" - }, "invalidRPC": { "message": "Hindi valid ang RPC URL" }, diff --git a/app/_locales/fr/messages.json b/app/_locales/fr/messages.json index f376101c5b2d..eff9261237fa 100644 --- a/app/_locales/fr/messages.json +++ b/app/_locales/fr/messages.json @@ -183,9 +183,6 @@ "addANetwork": { "message": "Ajouter un réseau" }, - "addANetworkManually": { - "message": "Ajouter manuellement un réseau" - }, "addANickname": { "message": "Ajouter un pseudo" }, @@ -252,9 +249,6 @@ "addFriendsAndAddresses": { "message": "Ajoutez uniquement des amis et des adresses de confiance" }, - "addFromAListOfPopularNetworks": { - "message": "Ajoutez à partir d’une liste de réseaux populaires ou ajoutez un réseau manuellement. Interagissez uniquement avec les entités en lesquelles vous avez confiance." - }, "addHardwareWallet": { "message": "Ajouter un portefeuille matériel" }, @@ -267,16 +261,9 @@ "addMemo": { "message": "Ajouter un mémo" }, - "addMoreNetworks": { - "message": "ajouter manuellement d’autres réseaux" - }, "addNetwork": { "message": "Ajouter un réseau" }, - "addNetworkTooltipWarning": { - "message": "Cette connexion réseau repose sur des tiers. Elle peut être moins fiable ou permettre à des tiers de suivre l’activité des utilisateurs. $1", - "description": "$1 is Learn more link" - }, "addNewAccount": { "message": "Ajouter un nouveau compte Ethereum" }, @@ -1000,9 +987,6 @@ "confirmConnectionTitle": { "message": "Confirmer la connexion à $1" }, - "confirmDeletion": { - "message": "Confirmer la suppression" - }, "confirmFieldPaymaster": { "message": "Frais payés par" }, @@ -1015,9 +999,6 @@ "confirmRecoveryPhrase": { "message": "Confirmer la phrase secrète de récupération" }, - "confirmRpcUrlDeletionMessage": { - "message": "Voulez-vous vraiment supprimer l’URL du RPC ? Vos informations ne seront pas sauvegardées pour ce réseau." - }, "confirmTitleDescPermitSignature": { "message": "Ce site demande que vous lui accordiez l'autorisation de dépenser vos jetons." }, @@ -1368,9 +1349,6 @@ "custom": { "message": "Paramètres avancés" }, - "customContentSearch": { - "message": "Chercher un réseau ajouté précédemment" - }, "customGasSettingToolTipMessage": { "message": "Utilisez $1 pour personnaliser le prix du carburant. Cela peut porter à confusion si vous n’en avez pas l’habitude. Agissez avec prudence !", "description": "$1 is key 'advanced' (text: 'Advanced') separated here so that it can be passed in with bold font-weight" @@ -1490,9 +1468,6 @@ "deleteContact": { "message": "Supprimer le contact" }, - "deleteNetwork": { - "message": "Supprimer le réseau ?" - }, "deleteNetworkIntro": { "message": "Si vous supprimez ce réseau, vous devrez l’ajouter à nouveau pour voir vos actifs sur ce réseau" }, @@ -1500,9 +1475,6 @@ "message": "Supprimer le réseau $1 ?", "description": "$1 represents the name of the network" }, - "deleteRpcUrl": { - "message": "Supprimer l’URL du RPC" - }, "deposit": { "message": "Effectuez un dépôt" }, @@ -1867,9 +1839,6 @@ "existingRequestsBannerAlertDesc": { "message": "Pour afficher et confirmer votre demande la plus récente, vous devez d’abord approuver ou rejeter les demandes existantes." }, - "existingRpcUrl": { - "message": "Cette URL est associée à un autre ID de chaîne." - }, "expandView": { "message": "Agrandir la vue" }, @@ -1901,9 +1870,6 @@ "failedToFetchChainId": { "message": "Impossible de récupérer l’ID de la chaîne. Votre URL de RPC est-elle correcte ?" }, - "failedToFetchTickerSymbolData": { - "message": "Les données de vérification de code mnémonique sont actuellement indisponibles. Assurez-vous que le code que vous avez saisi est correct. Il aura une incidence sur les taux de conversion que vous voyez pour ce réseau" - }, "failureMessage": { "message": "Un problème est survenu et nous n’avons pas pu mener à bien l’action" }, @@ -1924,9 +1890,6 @@ "message": "L’importation de fichier ne fonctionne pas ? Cliquez ici !", "description": "Helps user import their account from a JSON file" }, - "findTheRightChainId": { - "message": "Trouvez le bon ID de chaîne sur :" - }, "flaskWelcomeUninstall": { "message": "vous devriez désinstaller cette extension", "description": "This request is shown on the Flask Welcome screen. It is intended for non-developers, and will be bolded." @@ -2416,9 +2379,6 @@ "invalidAssetType": { "message": "Cet actif est un NFT et doit être ajouté de nouveau à la page Importer des NFT qui se trouve sous l’onglet NFT" }, - "invalidBlockExplorerURL": { - "message": "L’URL de l’explorateur de blocs est invalide" - }, "invalidChainIdTooBig": { "message": "ID de chaîne invalide. L’ID de la chaîne est trop grand." }, @@ -2938,9 +2898,6 @@ "network": { "message": "Réseau :" }, - "networkAddedSuccessfully": { - "message": "Réseau ajouté avec succès !" - }, "networkDetails": { "message": "Détails du réseau" }, @@ -3001,9 +2958,6 @@ "networkProvider": { "message": "Fournisseur de réseau" }, - "networkSettingsChainIdDescription": { - "message": "L’ID de la chaîne est utilisé pour la signature des transactions. Il doit correspondre à l’ID de la chaîne renvoyé par le réseau. Vous pouvez saisir un numéro décimal ou hexadécimal avec le préfixe « 0x », mais nous afficherons le numéro en décimal." - }, "networkStatus": { "message": "Statut du réseau" }, @@ -3452,9 +3406,6 @@ "onboardingAdvancedPrivacyIPFSValid": { "message": "L’URL de la passerelle IPFS est valide" }, - "onboardingAdvancedPrivacyNetworkButton": { - "message": "Ajouter un réseau personnalisé" - }, "onboardingAdvancedPrivacyNetworkDescription": { "message": "Nous utilisons Infura comme fournisseur de RPC (Remote Procedure Call) pour vous fournir l’accès le plus fiable et le plus privé possible aux données Ethereum. Vous pouvez choisir votre propre RPC, mais n’oubliez pas que tout RPC a besoin d’accéder à votre adresse IP et à l’adresse de votre portefeuille Ethereum pour valider les transactions. Lisez notre $1 pour en savoir plus sur la façon dont Infura traite les données personnelles." }, @@ -3483,9 +3434,6 @@ "onboardingMetametricsInfuraTermsPolicy": { "message": "Politique de confidentialité" }, - "onboardingMetametricsModalTitle": { - "message": "Ajouter un réseau personnalisé" - }, "onboardingMetametricsNeverCollect": { "message": "$1 les clics et les contenus visualisés sur l’application sont enregistrés, mais d’autres renseignements (comme votre adresse publique) ne le sont pas.", "description": "$1 represents `onboardingMetametricsNeverCollectEmphasis`" @@ -3578,9 +3526,6 @@ "onekey": { "message": "OneKey" }, - "onlyAddTrustedNetworks": { - "message": "Un fournisseur de réseau malveillant peut mentir quant à l’état de la blockchain et enregistrer votre activité réseau. N’ajoutez que des réseaux personnalisés auxquels vous faites confiance." - }, "onlyConnectTrust": { "message": "Ne vous connectez qu’aux sites auxquels vous faites confiance. $1", "description": "Text displayed above the buttons for connection confirmation. $1 is the link to the learn more web page." @@ -3605,9 +3550,6 @@ "optional": { "message": "Facultatif" }, - "optionalWithParanthesis": { - "message": "(Facultatif)" - }, "options": { "message": "Options" }, @@ -3959,9 +3901,6 @@ "message": "+ $1 de plus", "description": "$1 is a number of additional but unshown items in a list- this message will be shown in place of those items" }, - "popularCustomNetworks": { - "message": "Réseaux personnalisés populaires" - }, "popularNetworkAddToolTip": { "message": "Certains de ces réseaux dépendent de services tiers. Ils peuvent être moins fiables ou permettre à des tiers de suivre l’activité des utilisateurs. $1", "description": "$1 is Learn more link" @@ -5298,9 +5237,6 @@ "suggestedTokenName": { "message": "Nom suggéré :" }, - "suggestedTokenSymbol": { - "message": "Symbole boursier suggéré :" - }, "support": { "message": "Assistance" }, @@ -5789,9 +5725,6 @@ "termsOfUseTitle": { "message": "Nos conditions d’utilisation ont été mises à jour" }, - "testNetworks": { - "message": "Réseaux de test" - }, "theme": { "message": "Thème" }, @@ -6103,9 +6036,6 @@ "message": "U2F", "description": "A name on an API for the browser to interact with devices that support the U2F protocol. On some browsers we use it to connect MetaMask to Ledger devices." }, - "unMatchedChain": { - "message": "Selon nos informations, cette URL ne correspond pas à celle d’un fournisseur connu pour cet ID de chaîne." - }, "unapproved": { "message": "Non autorisé" }, @@ -6175,9 +6105,6 @@ "urlErrorMsg": { "message": "Les URLs requièrent un préfixe HTTP/HTTPS approprié." }, - "urlExistsErrorMsg": { - "message": "Cette URL est actuellement utilisée par le réseau $1." - }, "use4ByteResolution": { "message": "Décoder les contrats intelligents" }, @@ -6387,9 +6314,6 @@ "whatsThis": { "message": "Qu’est-ce que c’est ?" }, - "wrongChainId": { - "message": "Cet ID de chaîne ne correspond pas au nom du réseau." - }, "wrongNetworkName": { "message": "Selon nos informations, il se peut que le nom du réseau ne corresponde pas exactement à l’ID de chaîne." }, @@ -6403,10 +6327,6 @@ "you": { "message": "Vous" }, - "youHaveAddedAll": { - "message": "Vous avez ajouté tous les réseaux populaires. Vous pouvez découvrir d’autres réseaux $1 ou $2", - "description": "$1 is a link with the text 'here' and $2 is a button with the text 'add more networks manually'" - }, "youNeedToAllowCameraAccess": { "message": "Vous devez autoriser l’accès à votre appareil pour utiliser cette fonctionnalité." }, diff --git a/app/_locales/he/messages.json b/app/_locales/he/messages.json index b255bd0310c4..9d118e31c098 100644 --- a/app/_locales/he/messages.json +++ b/app/_locales/he/messages.json @@ -205,9 +205,6 @@ "delete": { "message": "מחיקה" }, - "deleteNetwork": { - "message": "למחוק את הרשת?" - }, "details": { "message": "פרטים" }, @@ -351,9 +348,6 @@ "invalidAddressRecipient": { "message": "כתובת הנמען אינה חוקית" }, - "invalidBlockExplorerURL": { - "message": "כתובת URL לא חוקית של Block Explorer" - }, "invalidRPC": { "message": "כתובת URL לא חוקית של RPC" }, diff --git a/app/_locales/hi/messages.json b/app/_locales/hi/messages.json index cff9295abc33..a41bda8439cc 100644 --- a/app/_locales/hi/messages.json +++ b/app/_locales/hi/messages.json @@ -183,9 +183,6 @@ "addANetwork": { "message": "एक नेटवर्क जोड़ें" }, - "addANetworkManually": { - "message": "मैन्युअल रूप से नेटवर्क जोड़ें" - }, "addANickname": { "message": "एक उपनाम जोड़ें" }, @@ -252,9 +249,6 @@ "addFriendsAndAddresses": { "message": "उन मित्रों और पतों को जोड़ें, जिन पर आप भरोसा करते हैं" }, - "addFromAListOfPopularNetworks": { - "message": "पॉपुलर नेटवर्क की लिस्ट से जोड़ें या मैन्युअल रूप से नेटवर्क जोड़ें। केवल उन संस्थाओं के साथ संवाद करें जिन पर आप भरोसा करते हैं।" - }, "addHardwareWallet": { "message": "hardware wallet जोड़ें" }, @@ -267,16 +261,9 @@ "addMemo": { "message": "मेमो जोड़ें" }, - "addMoreNetworks": { - "message": "मैन्युअल रूप से अधिक नेटवर्क जोड़ें" - }, "addNetwork": { "message": "नेटवर्क जोड़ें" }, - "addNetworkTooltipWarning": { - "message": "यह नेटवर्क कनेक्शन थर्ड पार्टियों पर निर्भर करता है। यह कनेक्शन संभवतः कम विश्वसनीय है या थर्ड-पार्टियों को एक्टिविटी को ट्रैक करने के लिए इनेबल कर सकता है। $1", - "description": "$1 is Learn more link" - }, "addNewAccount": { "message": "एक नया Ethereum अकाउंट जोड़ें" }, @@ -1000,9 +987,6 @@ "confirmConnectionTitle": { "message": "$1 से कनेक्शन को कन्फर्म करें" }, - "confirmDeletion": { - "message": "हटाना कन्फर्म करें" - }, "confirmFieldPaymaster": { "message": "के द्वारा शुल्क का भुगतान किया गया" }, @@ -1015,9 +999,6 @@ "confirmRecoveryPhrase": { "message": "सीक्रेट रिकवरी फ्रेज कन्फर्म करें" }, - "confirmRpcUrlDeletionMessage": { - "message": "क्या आप वाकई RPC URL को हटाना चाहते हैं? आपकी जानकारी इस नेटवर्क के लिए सेव नहीं की जाएगी।" - }, "confirmTitleDescPermitSignature": { "message": "यह साइट आपके टोकन खर्च करने की अनुमति चाहती है।" }, @@ -1368,9 +1349,6 @@ "custom": { "message": "एडवांस्ड" }, - "customContentSearch": { - "message": "पहले से जुड़े हुए नेटवर्क के लिए खोजें" - }, "customGasSettingToolTipMessage": { "message": "गैस प्राइस को कस्टमाइज़ करने के लिए $1 का इस्तेमाल करें। यदि आपको इसकी जानकारी नहीं हैं तो ये कंफ्यूज़ करने वाला हो सकता है। अपनी ज़िम्मेदारी पर इंटरैक्ट करें।", "description": "$1 is key 'advanced' (text: 'Advanced') separated here so that it can be passed in with bold font-weight" @@ -1490,9 +1468,6 @@ "deleteContact": { "message": "कॉन्टेक्ट मिटाएं" }, - "deleteNetwork": { - "message": "नेटवर्क हटाएं?" - }, "deleteNetworkIntro": { "message": "अगर आप इस नेटवर्क को हटाते हैं, तो आपको इस नेटवर्क में अपने एसेट देखने के लिए इसे फिर से जोड़ना होगा" }, @@ -1500,9 +1475,6 @@ "message": "$1 नेटवर्क को हटाएं?", "description": "$1 represents the name of the network" }, - "deleteRpcUrl": { - "message": "RPC URL को हटाएं" - }, "deposit": { "message": "डिपॉज़िट करें" }, @@ -1867,9 +1839,6 @@ "existingRequestsBannerAlertDesc": { "message": "अपने सबसे हालिया अनुरोध को देखने और कन्फर्म करने के लिए, आपको पहले मौजूदा अनुरोधों को एप्रूव या रिजेक्ट करना होगा।" }, - "existingRpcUrl": { - "message": "यह URL किसी अन्य चेन ID से जुड़ा है।" - }, "expandView": { "message": "व्यू को बड़ा करें" }, @@ -1901,9 +1870,6 @@ "failedToFetchChainId": { "message": "चेन ID प्राप्त नहीं की जा सकी। क्या आपका RPC URL सही है?" }, - "failedToFetchTickerSymbolData": { - "message": "टिकर सिंबल वेरिफिकेशन डेटा वर्तमान में अनुपलब्ध है, पक्का करें कि आपने जो चिन्ह डाला है वह सही है। ये उन कन्वर्शन दरों को प्रभावित करेगा जो आपको इस नेटवर्क के लिए दिख रहे हैं" - }, "failureMessage": { "message": "कुछ गलत हुआ और हम एक्शन को पूरा नहीं कर सकें" }, @@ -1924,9 +1890,6 @@ "message": "फाइल इम्पोर्ट काम नहीं कर रहा है? यहां क्लिक करें!", "description": "Helps user import their account from a JSON file" }, - "findTheRightChainId": { - "message": "यहां पर सही खोजें:" - }, "flaskWelcomeUninstall": { "message": "आपको इस एक्सटेन्शन को अनइंस्टाल करना चाहिए", "description": "This request is shown on the Flask Welcome screen. It is intended for non-developers, and will be bolded." @@ -2416,9 +2379,6 @@ "invalidAssetType": { "message": "ये एसेट एक NFT है और इसे इंपोर्ट NFTज़ पेज पर फिर से जोड़ना होगा जो NFTज़ टैब के नीचे मिलेगा" }, - "invalidBlockExplorerURL": { - "message": "ग़लत ब्लॉक एक्सप्लोरर URL" - }, "invalidChainIdTooBig": { "message": "ग़लत चेन ID। चेन ID बहुत बड़ी है।" }, @@ -2938,9 +2898,6 @@ "network": { "message": "नेटवर्क:" }, - "networkAddedSuccessfully": { - "message": "नेटवर्क सफलतापूर्वक जोड़ा गया!" - }, "networkDetails": { "message": "नेटवर्क विवरण" }, @@ -3001,9 +2958,6 @@ "networkProvider": { "message": "नेटवर्क प्रोवाइडर" }, - "networkSettingsChainIdDescription": { - "message": "चेन ID का इस्तेमाल ट्रांसेक्शन पर हस्ताक्षर करने के लिए किया जाता है। इसे नेटवर्क द्वारा दी गई चेन ID से मेल खाना चाहिए। आप डेसीमल या '0x'-प्रीफिक्स्ड वाली हेक्साडेसिमल संख्या डाल सकते हैं, लेकिन हम संख्या को डेसीमल में दिखाएंगे।" - }, "networkStatus": { "message": "नेटवर्क का स्टेटस" }, @@ -3452,9 +3406,6 @@ "onboardingAdvancedPrivacyIPFSValid": { "message": "IPFS गेटवे URL मान्य है" }, - "onboardingAdvancedPrivacyNetworkButton": { - "message": "कस्टम नेटवर्क जोड़ें" - }, "onboardingAdvancedPrivacyNetworkDescription": { "message": "हम अपने रिमोट प्रोसीजर कॉल (RPC) प्रोवाइडर के रूप में Infura का इस्तेमाल करते हैं ताकि हम Ethereum डेटा तक सबसे विश्वसनीय और निजी ऐक्सेस प्रदान कर सकें। आप अपना स्वयं का RPC चुन सकते हैं, लेकिन याद रखें कि कोई भी RPC ट्रांसेक्शन करने के लिए आपका IP एड्रेस और Ethereum वॉलेट प्राप्त करेगा। Infura डेटा को कैसे प्रबंधित करता है, इस बारे में अधिक जानने के लिए हमारा $1 पढ़ें।" }, @@ -3483,9 +3434,6 @@ "onboardingMetametricsInfuraTermsPolicy": { "message": "गोपनीयता नीति" }, - "onboardingMetametricsModalTitle": { - "message": "कस्टम नेटवर्क जोड़ें" - }, "onboardingMetametricsNeverCollect": { "message": "ऐप पर $1 क्लिक और व्यूज़ स्टोर किए जाते हैं, लेकिन अन्य विवरण (जैसे आपका पब्लिक एड्रेस) स्टोर नहीं होते।", "description": "$1 represents `onboardingMetametricsNeverCollectEmphasis`" @@ -3578,9 +3526,6 @@ "onekey": { "message": "OneKey" }, - "onlyAddTrustedNetworks": { - "message": "एक बुरी नीयत वाला नेटवर्क प्रोवाइडर ब्लॉकचेन की स्थिति के बारे में झूठ बोल सकता है और आपकी नेटवर्क एक्टिविटी को रिकॉर्ड कर सकता है। केवल उन कस्टम नेटवर्क को जोड़ें, जिन पर आप भरोसा करते हैं।" - }, "onlyConnectTrust": { "message": "केवल उन साइटों से कनेक्ट करें, जिन पर आप भरोसा करते हैं। $1", "description": "Text displayed above the buttons for connection confirmation. $1 is the link to the learn more web page." @@ -3605,9 +3550,6 @@ "optional": { "message": "वैकल्पिक" }, - "optionalWithParanthesis": { - "message": "(वैकल्पिक)" - }, "options": { "message": "विकल्प" }, @@ -3959,9 +3901,6 @@ "message": "+ $1 अधिक", "description": "$1 is a number of additional but unshown items in a list- this message will be shown in place of those items" }, - "popularCustomNetworks": { - "message": "पॉपुलर कस्टम नेटवर्क" - }, "popularNetworkAddToolTip": { "message": "इनमें से कुछ नेटवर्क थर्ड पार्टीज़ पर निर्भर हैं। कनेक्शन कम विश्वसनीय हो सकते हैं या गतिविधि को ट्रैक करने के लिए थर्ड पार्टीज़ को चालू कर सकते हैं। $1", "description": "$1 is Learn more link" @@ -5298,9 +5237,6 @@ "suggestedTokenName": { "message": "सुझाया गया नाम:" }, - "suggestedTokenSymbol": { - "message": "सुझाया गया टिकर सिंबल:" - }, "support": { "message": "सहायता" }, @@ -5789,9 +5725,6 @@ "termsOfUseTitle": { "message": "हमारी इस्तेमाल की शर्तों को अपडेट कर दिया गया है" }, - "testNetworks": { - "message": "टेस्ट नेटवर्क्स" - }, "theme": { "message": "थीम" }, @@ -6103,9 +6036,6 @@ "message": "U2F", "description": "A name on an API for the browser to interact with devices that support the U2F protocol. On some browsers we use it to connect MetaMask to Ledger devices." }, - "unMatchedChain": { - "message": "हमारे रिकॉर्ड के अनुसार, यह URL इस चेन ID के लिए ज्ञात प्रदाता से मेल नहीं खाता है।" - }, "unapproved": { "message": "रिजेक्ट" }, @@ -6175,9 +6105,6 @@ "urlErrorMsg": { "message": "URL को उपयुक्त HTTP/HTTPS प्रीफिक्स्ड की आवश्यकता होती है।" }, - "urlExistsErrorMsg": { - "message": "यह URL वर्तमान में $1 नेटवर्क द्वारा इस्तेमाल किया जाता है।" - }, "use4ByteResolution": { "message": "स्मार्ट कॉन्ट्रैक्ट्स को डीकोड करें" }, @@ -6387,9 +6314,6 @@ "whatsThis": { "message": "यह क्या है?" }, - "wrongChainId": { - "message": "यह चेन ID नेटवर्क के नाम से मेल नहीं खाती।" - }, "wrongNetworkName": { "message": "हमारे रिकॉर्ड के अनुसार, नेटवर्क का नाम इस चेन ID से ठीक से मेल नहीं खा सकता है।" }, @@ -6403,10 +6327,6 @@ "you": { "message": "आप" }, - "youHaveAddedAll": { - "message": "आपने सभी पॉपुलर नेटवर्क जोड़ लिए हैं। आप अधिक नेटवर्क खोज सकते हैं $1 या आप $2 कर सकते हैं", - "description": "$1 is a link with the text 'here' and $2 is a button with the text 'add more networks manually'" - }, "youNeedToAllowCameraAccess": { "message": "आपको इस सुविधा का इस्तेमाल करने के लिए कैमरे तक पहुंच की अनुमति देने की आवश्यकता है।" }, diff --git a/app/_locales/hn/messages.json b/app/_locales/hn/messages.json index 246c9c88d5bc..8e9091f911db 100644 --- a/app/_locales/hn/messages.json +++ b/app/_locales/hn/messages.json @@ -143,9 +143,6 @@ "invalidAddressRecipient": { "message": "प्राप्तकर्ता का पता अमान्य है" }, - "invalidBlockExplorerURL": { - "message": "अमान्य Block Explorer कै URI" - }, "invalidRPC": { "message": "अमान्य RPC कै URI" }, diff --git a/app/_locales/hr/messages.json b/app/_locales/hr/messages.json index b8e1c9bf15c1..4463408d9435 100644 --- a/app/_locales/hr/messages.json +++ b/app/_locales/hr/messages.json @@ -205,9 +205,6 @@ "delete": { "message": "Izbriši" }, - "deleteNetwork": { - "message": "Izbrisati mrežu?" - }, "details": { "message": "Detalji" }, @@ -347,9 +344,6 @@ "invalidAddressRecipient": { "message": "Adresa primatelja nije valjana" }, - "invalidBlockExplorerURL": { - "message": "Nevaljani URL Block Explorer-a" - }, "invalidRPC": { "message": "Nevaljani URL RPC-a" }, diff --git a/app/_locales/ht/messages.json b/app/_locales/ht/messages.json index 879eeab1722d..700f6debed18 100644 --- a/app/_locales/ht/messages.json +++ b/app/_locales/ht/messages.json @@ -236,9 +236,6 @@ "invalidAddressRecipient": { "message": "Moun ki resevwa adrès la pa valab" }, - "invalidBlockExplorerURL": { - "message": "Block Explorer URI pa valab" - }, "invalidRPC": { "message": "RPC URI pa valab" }, diff --git a/app/_locales/hu/messages.json b/app/_locales/hu/messages.json index eef301ef9273..4786cfb9703d 100644 --- a/app/_locales/hu/messages.json +++ b/app/_locales/hu/messages.json @@ -205,9 +205,6 @@ "delete": { "message": "Törlés" }, - "deleteNetwork": { - "message": "Törli a hálózatot?" - }, "details": { "message": "Részletek" }, @@ -347,9 +344,6 @@ "invalidAddressRecipient": { "message": "A címzett címe érvénytelen " }, - "invalidBlockExplorerURL": { - "message": "Helytelen Block Explorer URL" - }, "invalidRPC": { "message": "Helytelen RPC URL" }, diff --git a/app/_locales/id/messages.json b/app/_locales/id/messages.json index 8e2437d8c2bf..b13602011b99 100644 --- a/app/_locales/id/messages.json +++ b/app/_locales/id/messages.json @@ -183,9 +183,6 @@ "addANetwork": { "message": "Tambahkan jaringan" }, - "addANetworkManually": { - "message": "Tambahkan jaringan secara manual" - }, "addANickname": { "message": "Tambahkan nama panggilan" }, @@ -252,9 +249,6 @@ "addFriendsAndAddresses": { "message": "Tambahkan teman dan alamat yang Anda percayai" }, - "addFromAListOfPopularNetworks": { - "message": "Tambahkan dari daftar jaringan populer atau tambahkan jaringan secara manual. Lakukan interaksi hanya dengan entitas yang Anda percayai." - }, "addHardwareWallet": { "message": "Tambahkan dompet perangkat keras" }, @@ -267,16 +261,9 @@ "addMemo": { "message": "Tambahkan memo" }, - "addMoreNetworks": { - "message": "tambahkan jaringan lainnya secara manual" - }, "addNetwork": { "message": "Tambahkan jaringan" }, - "addNetworkTooltipWarning": { - "message": "Koneksi jaringan ini mengandalkan pihak ketiga. Koneksi ini mungkin kurang bisa diandalkan atau memungkinkan pihak ketiga melacak aktivitas. $1", - "description": "$1 is Learn more link" - }, "addNewAccount": { "message": "Tambahkan akun Ethereum baru" }, @@ -1000,9 +987,6 @@ "confirmConnectionTitle": { "message": "Konfirmasikan koneksi ke $1" }, - "confirmDeletion": { - "message": "Konfirmasikan penghapusan" - }, "confirmFieldPaymaster": { "message": "Biaya dibayar oleh" }, @@ -1015,9 +999,6 @@ "confirmRecoveryPhrase": { "message": "Konfirmasikan Frasa Pemulihan Rahasia" }, - "confirmRpcUrlDeletionMessage": { - "message": "Yakin ingin menghapus URL RPC? Informasi Anda tidak akan disimpan untuk jaringan ini." - }, "confirmTitleDescPermitSignature": { "message": "Situs ini meminta izin untuk menggunakan token Anda." }, @@ -1368,9 +1349,6 @@ "custom": { "message": "Lanjutan" }, - "customContentSearch": { - "message": "Cari jaringan yang ditambahkan sebelumnya" - }, "customGasSettingToolTipMessage": { "message": "Gunakan $1 untuk menyesuaikan harga gas. Anda akan bingung jika tidak terbiasa. Berinteraksi dengan risiko Anda sendiri.", "description": "$1 is key 'advanced' (text: 'Advanced') separated here so that it can be passed in with bold font-weight" @@ -1490,9 +1468,6 @@ "deleteContact": { "message": "Hapus kontak" }, - "deleteNetwork": { - "message": "Hapus jaringan?" - }, "deleteNetworkIntro": { "message": "Jika menghapus jaringan ini, Anda harus menambahkannya lagi untuk melihat aset Anda di jaringan ini" }, @@ -1500,9 +1475,6 @@ "message": "Hapus jaringan $1?", "description": "$1 represents the name of the network" }, - "deleteRpcUrl": { - "message": "Hapus URL RPC" - }, "deposit": { "message": "Deposit" }, @@ -1867,9 +1839,6 @@ "existingRequestsBannerAlertDesc": { "message": "Untuk melihat dan mengonfirmasi permintaan terkini, Anda harus menyetujui atau menolak permintaan yang ada terlebih dahulu." }, - "existingRpcUrl": { - "message": "URL ini terhubung dengan ID chain lain." - }, "expandView": { "message": "Perluas tampilan" }, @@ -1901,9 +1870,6 @@ "failedToFetchChainId": { "message": "Tidak dapat mengambil ID chain. Apakah URL RPC Anda benar?" }, - "failedToFetchTickerSymbolData": { - "message": "Data verifikasi simbol ticker saat ini tidak tersedia, pastikan simbol yang Anda masukkan sudah benar. Ini akan memengaruhi tingkat konversi yang Anda lihat untuk jaringan ini" - }, "failureMessage": { "message": "Terjadi kesalahan dan kami tidak dapat menyelesaikan tindakan" }, @@ -1924,9 +1890,6 @@ "message": "Impor file tidak bekerja? Klik di sini!", "description": "Helps user import their account from a JSON file" }, - "findTheRightChainId": { - "message": "Temukan yang tepat di:" - }, "flaskWelcomeUninstall": { "message": "Anda harus menghapus ekstensi ini", "description": "This request is shown on the Flask Welcome screen. It is intended for non-developers, and will be bolded." @@ -2416,9 +2379,6 @@ "invalidAssetType": { "message": "Aset ini merupakan NFT dan harus ditambahkan kembali di halaman Impor NFT yang ada di bawah tab NFT" }, - "invalidBlockExplorerURL": { - "message": "URL block explorer tidak valid" - }, "invalidChainIdTooBig": { "message": "ID chain tidak valid. ID chain terlalu besar." }, @@ -2938,9 +2898,6 @@ "network": { "message": "Jaringan:" }, - "networkAddedSuccessfully": { - "message": "Jaringan berhasil ditambahkan!" - }, "networkDetails": { "message": "Detail jaringan" }, @@ -3001,9 +2958,6 @@ "networkProvider": { "message": "Penyedia jaringan" }, - "networkSettingsChainIdDescription": { - "message": "ID chain digunakan untuk menandatangani transaksi. Harus cocok dengan ID chain yang dikembalikan oleh jaringan. Anda dapat memasukkan bilangan heksadesimal berawalan '0x' atau desimal, namun kami akan menampilkan bilangan tersebut dalam desimal." - }, "networkStatus": { "message": "Status jaringan" }, @@ -3452,9 +3406,6 @@ "onboardingAdvancedPrivacyIPFSValid": { "message": "URL gateway IPFS valid" }, - "onboardingAdvancedPrivacyNetworkButton": { - "message": "Tambahkan jaringan khusus" - }, "onboardingAdvancedPrivacyNetworkDescription": { "message": "Kami menggunakan Infura sebagai penyedia pemanggilan prosedur jarak jauh (RPC) kami untuk menawarkan akses paling andal dan pribadi ke data Ethereum. Anda dapat memilih RPC Anda sendiri, tetapi ingatlah bahwa setiap RPC akan menerima alamat IP dan dompet Ethereum Anda untuk melakukan transaksi. Baca $1 kami untuk mempelajari lebih lanjut seputar cara Infura menangani data." }, @@ -3483,9 +3434,6 @@ "onboardingMetametricsInfuraTermsPolicy": { "message": "Kebijakan Privasi" }, - "onboardingMetametricsModalTitle": { - "message": "Tambahkan jaringan khusus" - }, "onboardingMetametricsNeverCollect": { "message": "$1 klik dan tampilan pada aplikasi disimpan, tetapi tidak untuk detail lainnya (seperti alamat publik).", "description": "$1 represents `onboardingMetametricsNeverCollectEmphasis`" @@ -3578,9 +3526,6 @@ "onekey": { "message": "OneKey" }, - "onlyAddTrustedNetworks": { - "message": "Penyedia jaringan jahat dapat berbohong tentang status blockchain dan merekam aktivitas jaringan Anda. Hanya tambahkan jaringan kustom yang Anda percayai." - }, "onlyConnectTrust": { "message": "Hanya hubungkan ke situs yang Anda percayai. $1", "description": "Text displayed above the buttons for connection confirmation. $1 is the link to the learn more web page." @@ -3605,9 +3550,6 @@ "optional": { "message": "Opsional" }, - "optionalWithParanthesis": { - "message": "(Opsional)" - }, "options": { "message": "Opsi" }, @@ -3959,9 +3901,6 @@ "message": "+ $1 lagi", "description": "$1 is a number of additional but unshown items in a list- this message will be shown in place of those items" }, - "popularCustomNetworks": { - "message": "Jaringan khusus populer" - }, "popularNetworkAddToolTip": { "message": "Beberapa jaringan ini mengandalkan pihak ketiga. Koneksi ini kurang dapat diandalkan atau memungkinkan pihak ketiga melacak aktivitas. $1", "description": "$1 is Learn more link" @@ -5298,9 +5237,6 @@ "suggestedTokenName": { "message": "Nama yang disarankan:" }, - "suggestedTokenSymbol": { - "message": "Simbol ticker yang disarankan:" - }, "support": { "message": "Dukungan" }, @@ -5789,9 +5725,6 @@ "termsOfUseTitle": { "message": "Syarat Penggunaan kami telah diperbarui" }, - "testNetworks": { - "message": "Jaringan pengujian" - }, "theme": { "message": " Saya menyetujui Ketentuan Penggunaan, yang berlaku untuk penggunaan atas MetaMask dan semua fiturnya" }, @@ -6103,9 +6036,6 @@ "message": "U2F", "description": "A name on an API for the browser to interact with devices that support the U2F protocol. On some browsers we use it to connect MetaMask to Ledger devices." }, - "unMatchedChain": { - "message": "Menurut catatan kami, URL ini tidak sesuai dengan penyedia yang dikenal untuk ID chain ini." - }, "unapproved": { "message": "Tidak disetujui" }, @@ -6175,9 +6105,6 @@ "urlErrorMsg": { "message": "URL memerlukan awalan HTTP/HTTPS yang sesuai." }, - "urlExistsErrorMsg": { - "message": "URL ini saat ini digunakan oleh jaringan $1." - }, "use4ByteResolution": { "message": "Uraikan kode kontrak cerdas" }, @@ -6387,9 +6314,6 @@ "whatsThis": { "message": "Apa ini?" }, - "wrongChainId": { - "message": "ID chain ini tidak sesuai dengan nama jaringan." - }, "wrongNetworkName": { "message": "Menurut catatan kami, nama jaringan mungkin tidak cocok dengan ID chain ini." }, @@ -6403,10 +6327,6 @@ "you": { "message": "Anda" }, - "youHaveAddedAll": { - "message": "Anda telah menambahkan semua jaringan populer. Anda dapat menemukan lebih banyak jaringan $1 atau dapat $2", - "description": "$1 is a link with the text 'here' and $2 is a button with the text 'add more networks manually'" - }, "youNeedToAllowCameraAccess": { "message": "Anda harus mengizinkan akses kamera untuk menggunakan fitur ini." }, diff --git a/app/_locales/it/messages.json b/app/_locales/it/messages.json index 2cd696e5b5ba..7c413941da92 100644 --- a/app/_locales/it/messages.json +++ b/app/_locales/it/messages.json @@ -137,9 +137,6 @@ "addANetwork": { "message": "Aggiungi una rete" }, - "addANetworkManually": { - "message": "Aggungi manualmente una rete" - }, "addANickname": { "message": "Aggiungo un nickname" }, @@ -175,22 +172,12 @@ "addFriendsAndAddresses": { "message": "Aggiungi amici e indirizzi di cui ti fidi" }, - "addFromAListOfPopularNetworks": { - "message": "Aggiungi da un elenco di reti popolari o aggiungi una rete manualmente. Interagisci solo con le entità di cui ti fidi." - }, "addMemo": { "message": "Aggiungi memo" }, - "addMoreNetworks": { - "message": "aggiungi più reti manualmente" - }, "addNetwork": { "message": "Aggiungi Rete" }, - "addNetworkTooltipWarning": { - "message": "Questarete si basa su terze parti. La connessione potrebbe essere meno affidabile o consentire a terzi di tracciare le tue attività. $1", - "description": "$1 is Learn more link" - }, "addSuggestedTokens": { "message": "Aggiungi Token Suggeriti" }, @@ -588,9 +575,6 @@ "custom": { "message": "Avanzate" }, - "customContentSearch": { - "message": "Cerca una rete aggiunta in precedenza" - }, "customGasSettingToolTipMessage": { "message": "Usa $1 per personalizzare il prezzo del gas. Questo può creare confusione se non hai familiarità. Interagisci a tuo rischio.", "description": "$1 is key 'advanced' (text: 'Advanced') separated here so that it can be passed in with bold font-weight" @@ -662,9 +646,6 @@ "delete": { "message": "Elimina" }, - "deleteNetwork": { - "message": "Cancella la rete?" - }, "description": { "message": "Descrizione" }, @@ -952,9 +933,6 @@ "invalidAddressRecipient": { "message": "Indirizzo destinatario invalido" }, - "invalidBlockExplorerURL": { - "message": "URI Block Explorer invalido" - }, "invalidChainIdTooBig": { "message": "Chain ID non valido. Il chain ID è troppo grande." }, @@ -1076,9 +1054,6 @@ "networkName": { "message": "Nome Rete" }, - "networkSettingsChainIdDescription": { - "message": "Il chain ID è usato per firmare le transazioni. Deve essere uguale al chain ID restituito dalla rete. Puoi inserire un numero intero o un numero esadecimale con prefisso '0x', ma mostreremo sempre un numero intero." - }, "networks": { "message": "Reti" }, @@ -1144,9 +1119,6 @@ "offlineForMaintenance": { "message": "Offline per manutenzione" }, - "onlyAddTrustedNetworks": { - "message": "Una rete malevola può mentire sullo stato della blockchain e registrare le tue azioni. Aggiungi solo reti fidate." - }, "onlyConnectTrust": { "message": "Connettiti solo con siti di cui ti fidi.", "description": "Text displayed above the buttons for connection confirmation. $1 is the link to the learn more web page." @@ -1732,9 +1704,6 @@ "urlErrorMsg": { "message": "Gli URI richiedono un prefisso HTTP/HTTPS." }, - "urlExistsErrorMsg": { - "message": "L'URL è già presente nella lista di reti" - }, "usePhishingDetection": { "message": "Rilevamento del Phishing" }, diff --git a/app/_locales/ja/messages.json b/app/_locales/ja/messages.json index e1ca82a04a42..8d006fef2257 100644 --- a/app/_locales/ja/messages.json +++ b/app/_locales/ja/messages.json @@ -183,9 +183,6 @@ "addANetwork": { "message": "ネットワークを追加" }, - "addANetworkManually": { - "message": "ネットワークを手動で追加" - }, "addANickname": { "message": "ニックネームを追加" }, @@ -252,9 +249,6 @@ "addFriendsAndAddresses": { "message": "信頼できる友達とアドレスを追加する" }, - "addFromAListOfPopularNetworks": { - "message": "人気のネットワークのリストから追加するか、ネットワークを手動で追加します。信頼できる相手と以外はやり取りしないようにしてください。" - }, "addHardwareWallet": { "message": "ハードウェアウォレットを追加" }, @@ -267,16 +261,9 @@ "addMemo": { "message": "メモを追加" }, - "addMoreNetworks": { - "message": "他のネットワークを手動で追加" - }, "addNetwork": { "message": "ネットワークを追加" }, - "addNetworkTooltipWarning": { - "message": "このネットワーク接続はサードパーティに依存しているため、信頼性が低かったり、サードパーティによるアクティビティの追跡が可能になる可能性があります。$1", - "description": "$1 is Learn more link" - }, "addNewAccount": { "message": "新しいイーサリアムアカウントを追加" }, @@ -1000,9 +987,6 @@ "confirmConnectionTitle": { "message": "$1への接続の確定" }, - "confirmDeletion": { - "message": "削除の確定" - }, "confirmFieldPaymaster": { "message": "手数料の支払元" }, @@ -1015,9 +999,6 @@ "confirmRecoveryPhrase": { "message": "シークレットリカバリーフレーズの確認" }, - "confirmRpcUrlDeletionMessage": { - "message": "RPC URLを削除してよろしいですか?このネットワークの情報は保存されません。" - }, "confirmTitleDescPermitSignature": { "message": "このサイトがトークンの使用許可を求めています。" }, @@ -1368,9 +1349,6 @@ "custom": { "message": "高度な設定" }, - "customContentSearch": { - "message": "以前追加されたネットワークを検索" - }, "customGasSettingToolTipMessage": { "message": "ガス価格をカスタマイズするには$1を使用します。慣れていない場合はわかりにくい可能性があります。自己責任で操作してください。", "description": "$1 is key 'advanced' (text: 'Advanced') separated here so that it can be passed in with bold font-weight" @@ -1490,9 +1468,6 @@ "deleteContact": { "message": "連絡先を削除" }, - "deleteNetwork": { - "message": "ネットワークを削除しますか?" - }, "deleteNetworkIntro": { "message": "このネットワークを削除した場合、このネットワーク内の資産を見るには、再度ネットワークの追加が必要になります。" }, @@ -1500,9 +1475,6 @@ "message": "$1ネットワークを削除しますか?", "description": "$1 represents the name of the network" }, - "deleteRpcUrl": { - "message": "RPC URLを削除" - }, "deposit": { "message": "入金" }, @@ -1867,9 +1839,6 @@ "existingRequestsBannerAlertDesc": { "message": "一番最近の要求を表示して確定するには、はじめに既存の要求を承認または拒否する必要があります。" }, - "existingRpcUrl": { - "message": "このURLは別のチェーンIDに関連付けられています。" - }, "expandView": { "message": "ビューを展開" }, @@ -1901,9 +1870,6 @@ "failedToFetchChainId": { "message": "チェーンIDを取り込むことができませんでした。お使いのRPC URLは正しいですか?" }, - "failedToFetchTickerSymbolData": { - "message": "ティッカーシンボルの検証データが現在利用できません。入力されたシンボルが正しいことを確認してください。これはこのネットワークの換算レートに影響を与えます" - }, "failureMessage": { "message": "問題が発生しました。アクションを完了させることができません" }, @@ -1924,9 +1890,6 @@ "message": "ファイルのインポートが機能していない場合、ここをクリックしてください!", "description": "Helps user import their account from a JSON file" }, - "findTheRightChainId": { - "message": "正しいIDを検索:" - }, "flaskWelcomeUninstall": { "message": "この拡張機能はアンインストールしてください", "description": "This request is shown on the Flask Welcome screen. It is intended for non-developers, and will be bolded." @@ -2416,9 +2379,6 @@ "invalidAssetType": { "message": "このアセットはNFTであるため、「NFT」タブの「NFTのインポート」ページで追加しなおす必要があります" }, - "invalidBlockExplorerURL": { - "message": "ブロックエクスプローラーのURLが無効です" - }, "invalidChainIdTooBig": { "message": "無効なチェーンID。チェーンIDが大きすぎます。" }, @@ -2938,9 +2898,6 @@ "network": { "message": "ネットワーク:" }, - "networkAddedSuccessfully": { - "message": "ネットワークが追加されました!" - }, "networkDetails": { "message": "ネットワークの詳細" }, @@ -3001,9 +2958,6 @@ "networkProvider": { "message": "ネットワークプロバイダー" }, - "networkSettingsChainIdDescription": { - "message": "チェーンIDはトランザクションの署名に使用されます。チェーンIDはネットワークが返すチェーンIDと一致する必要があります。10進数または「0x」が先行する16進数を入力できますが、10進数で表示されます。" - }, "networkStatus": { "message": "ネットワークステータス" }, @@ -3452,9 +3406,6 @@ "onboardingAdvancedPrivacyIPFSValid": { "message": "IPFSゲートウェイのURLが有効です" }, - "onboardingAdvancedPrivacyNetworkButton": { - "message": "カスタムネットワークを追加" - }, "onboardingAdvancedPrivacyNetworkDescription": { "message": "当社では、遠隔手続き呼び出し (RPC) プロバイダーにInfuraを使用して、イーサリアムデータにできるだけ信頼性の高いプライベートな形でアクセスできるようにしています。独自のRPCをお選びいただくこともできますが、どのRPCもトランザクションを実行するために、ユーザーのIPアドレスとイーサリアムウォレットを取得する点にご注意ください。Infuraによるデータの取扱いに関する詳細は、$1をご覧ください。" }, @@ -3483,9 +3434,6 @@ "onboardingMetametricsInfuraTermsPolicy": { "message": "プライバシー ポリシー" }, - "onboardingMetametricsModalTitle": { - "message": "カスタムネットワークを追加" - }, "onboardingMetametricsNeverCollect": { "message": "$1 アプリのクリックやビューは保存されますが、その他の詳細 (ユーザーのパブリックアドレスなど) は保存されません。", "description": "$1 represents `onboardingMetametricsNeverCollectEmphasis`" @@ -3578,9 +3526,6 @@ "onekey": { "message": "OneKey" }, - "onlyAddTrustedNetworks": { - "message": "悪意のあるネットワーク プロバイダーは、ブロックチェーンのステートを偽り、ユーザーのネットワークアクティビティを記録することがあります。信頼するカスタムネットワークのみを追加してください。" - }, "onlyConnectTrust": { "message": "信頼するサイトにのみ接続してください。$1", "description": "Text displayed above the buttons for connection confirmation. $1 is the link to the learn more web page." @@ -3605,9 +3550,6 @@ "optional": { "message": "オプション" }, - "optionalWithParanthesis": { - "message": "(オプション)" - }, "options": { "message": "オプション" }, @@ -3959,9 +3901,6 @@ "message": "その他$1件", "description": "$1 is a number of additional but unshown items in a list- this message will be shown in place of those items" }, - "popularCustomNetworks": { - "message": "人気のカスタムネットワーク" - }, "popularNetworkAddToolTip": { "message": "これらのネットワークの一部はサードパーティに依存しているため、接続の信頼性が低かったり、サードパーティによるアクティビティの追跡が可能になったりする可能性があります。$1", "description": "$1 is Learn more link" @@ -5298,9 +5237,6 @@ "suggestedTokenName": { "message": "提案された名前:" }, - "suggestedTokenSymbol": { - "message": "推奨ティッカーシンボル:" - }, "support": { "message": "サポート" }, @@ -5789,9 +5725,6 @@ "termsOfUseTitle": { "message": "利用規約が更新されました" }, - "testNetworks": { - "message": "テストネットワーク" - }, "theme": { "message": "テーマ" }, @@ -6103,9 +6036,6 @@ "message": "U2F", "description": "A name on an API for the browser to interact with devices that support the U2F protocol. On some browsers we use it to connect MetaMask to Ledger devices." }, - "unMatchedChain": { - "message": "当社の記録によると、このURLは、このチェーンIDの既知のプロバイダーと一致しません。" - }, "unapproved": { "message": "未承認" }, @@ -6175,9 +6105,6 @@ "urlErrorMsg": { "message": "URLには適切なHTTP/HTTPSプレフィックスが必要です。" }, - "urlExistsErrorMsg": { - "message": "このURLは現在$1ネットワークで使用されています。" - }, "use4ByteResolution": { "message": "スマートコントラクトのデコード" }, @@ -6387,9 +6314,6 @@ "whatsThis": { "message": "これは何ですか?" }, - "wrongChainId": { - "message": "このチェーンIDはネットワーク名と一致しません。" - }, "wrongNetworkName": { "message": "弊社の記録によると、ネットワーク名がこのチェーンIDと正しく一致していない可能性があります。" }, @@ -6403,10 +6327,6 @@ "you": { "message": "ユーザー" }, - "youHaveAddedAll": { - "message": "すべての人気ネットワークを追加しました。$1で他のネットワークを発見するか、$2できます。", - "description": "$1 is a link with the text 'here' and $2 is a button with the text 'add more networks manually'" - }, "youNeedToAllowCameraAccess": { "message": "この機能を使用するには、カメラへのアクセスを許可する必要があります。" }, diff --git a/app/_locales/kn/messages.json b/app/_locales/kn/messages.json index d5fe970b32e0..6471b738b5b9 100644 --- a/app/_locales/kn/messages.json +++ b/app/_locales/kn/messages.json @@ -205,9 +205,6 @@ "delete": { "message": "ಅಳಿಸಿ" }, - "deleteNetwork": { - "message": "ನೆಟ್‌ವರ್ಕ್ ಅಳಿಸುವುದೇ?" - }, "details": { "message": "ವಿವರಗಳು" }, @@ -351,9 +348,6 @@ "invalidAddressRecipient": { "message": "ಸ್ವೀಕೃತಿದಾರರ ವಿಳಾಸವು ಅಮಾನ್ಯವಾಗಿದೆ" }, - "invalidBlockExplorerURL": { - "message": "ಅಮಾನ್ಯವಾದ Block Explorer URL" - }, "invalidRPC": { "message": "ಅಮಾನ್ಯವಾದ RPC URL" }, diff --git a/app/_locales/ko/messages.json b/app/_locales/ko/messages.json index 2c36d6ad2f58..666d99d90b00 100644 --- a/app/_locales/ko/messages.json +++ b/app/_locales/ko/messages.json @@ -183,9 +183,6 @@ "addANetwork": { "message": "네트워크 추가" }, - "addANetworkManually": { - "message": "네트워크 직접 추가" - }, "addANickname": { "message": "닉네임 추가" }, @@ -252,9 +249,6 @@ "addFriendsAndAddresses": { "message": "신뢰하는 친구 및 주소 추가" }, - "addFromAListOfPopularNetworks": { - "message": "주요 네트워크 목록에서 추가하거나 네트워크를 직접 추가합니다. 신뢰할 수 있는 엔터티만 이용하세요." - }, "addHardwareWallet": { "message": "하드웨어 지갑 추가" }, @@ -267,16 +261,9 @@ "addMemo": { "message": "메모 추가" }, - "addMoreNetworks": { - "message": "네트워크 직접 추가" - }, "addNetwork": { "message": "네트워크 추가" }, - "addNetworkTooltipWarning": { - "message": "이 네트워크 연결은 타사 서비스를 이용합니다. 연결의 보안이 취약하거나 타사에 연결 내용이 노출될 수 있습니다. $1", - "description": "$1 is Learn more link" - }, "addNewAccount": { "message": "새 이더리움 계정 추가" }, @@ -1000,9 +987,6 @@ "confirmConnectionTitle": { "message": "$1에 연결 확인" }, - "confirmDeletion": { - "message": "컨펌 삭제" - }, "confirmFieldPaymaster": { "message": "수수료 지불:" }, @@ -1015,9 +999,6 @@ "confirmRecoveryPhrase": { "message": "비밀복구구문 컨펌" }, - "confirmRpcUrlDeletionMessage": { - "message": "정말로 RPC URL을 삭제하시겠습니까? 고객님의 정보는 이 네트워크에 저장되지 않습니다." - }, "confirmTitleDescPermitSignature": { "message": "해당 사이트에서 토큰 사용 승인을 요청합니다." }, @@ -1368,9 +1349,6 @@ "custom": { "message": "고급" }, - "customContentSearch": { - "message": "이전에 추가된 네트워크 검색" - }, "customGasSettingToolTipMessage": { "message": "$1 사용으로 가스 가격을 맞춤 설정하세요. 익숙하지 않은 경우 혼동될 수 있습니다. 자신의 책임하에 인터렉션하세요.", "description": "$1 is key 'advanced' (text: 'Advanced') separated here so that it can be passed in with bold font-weight" @@ -1490,9 +1468,6 @@ "deleteContact": { "message": "연락처 삭제" }, - "deleteNetwork": { - "message": "네트워크를 삭제하시겠습니까?" - }, "deleteNetworkIntro": { "message": "이 네트워크를 삭제하면 나중에 이 네트워크에 있는 자산을 보고 싶을 때 네트워크를 다시 추가해야 합니다" }, @@ -1500,9 +1475,6 @@ "message": "$1 네트워크를 삭제하시겠습니까?", "description": "$1 represents the name of the network" }, - "deleteRpcUrl": { - "message": "RPC URL 삭제" - }, "deposit": { "message": "예치" }, @@ -1867,9 +1839,6 @@ "existingRequestsBannerAlertDesc": { "message": "가장 최근 요청을 보고 확인하려면, 먼저 기존 요청을 승인하거나 거부해야 합니다." }, - "existingRpcUrl": { - "message": "이 URL은 다른 체인 ID랑 연결되어 있습니다." - }, "expandView": { "message": "보기 확장" }, @@ -1901,9 +1870,6 @@ "failedToFetchChainId": { "message": "체인 ID를 가져올 수 없습니다. RPC URL이 올바른가요?" }, - "failedToFetchTickerSymbolData": { - "message": "현재 티커 기호 확인 데이터를 사용할 수 없습니다. 입력한 기호가 올바른지 확인하세요. 이는 네트워크에 표시되는 전환율에 영향을 미칩니다." - }, "failureMessage": { "message": "문제가 발생했습니다. 작업을 완료할 수 없습니다." }, @@ -1924,9 +1890,6 @@ "message": "파일 가져오기가 작동하지 않나요? 여기를 클릭하세요.", "description": "Helps user import their account from a JSON file" }, - "findTheRightChainId": { - "message": "다음에서 적합한 것을 찾아보세요" - }, "flaskWelcomeUninstall": { "message": "이 확장 프로그램을 삭제해야 합니다", "description": "This request is shown on the Flask Welcome screen. It is intended for non-developers, and will be bolded." @@ -2416,9 +2379,6 @@ "invalidAssetType": { "message": "이 자산은 NFT이므로 NFT 탭에 있는 NFT 가져오기 페이지에서 다시 추가해야 합니다" }, - "invalidBlockExplorerURL": { - "message": "잘못된 블록 탐색기 URL" - }, "invalidChainIdTooBig": { "message": "잘못된 체인 ID. 체인 ID가 너무 큽니다." }, @@ -2938,9 +2898,6 @@ "network": { "message": "네트워크:" }, - "networkAddedSuccessfully": { - "message": "성공적으로 네트워크를 추가했습니다!" - }, "networkDetails": { "message": "네트워크 세부 정보" }, @@ -3001,9 +2958,6 @@ "networkProvider": { "message": "네트워크 공급업체" }, - "networkSettingsChainIdDescription": { - "message": "체인 ID는 트랜잭션 서명에 사용됩니다. 이는 네트워크에서 반환하는 체인 ID와 일치해야 합니다. 십진수나 '0x'로 시작하는 16진수를 입력할 수 있지만, 표시되는 형식은 십진수입니다." - }, "networkStatus": { "message": "네트워크 상태" }, @@ -3452,9 +3406,6 @@ "onboardingAdvancedPrivacyIPFSValid": { "message": "PFS 게이트웨이 URL이 유효합니다" }, - "onboardingAdvancedPrivacyNetworkButton": { - "message": "사용자 정의 네트워크 추가" - }, "onboardingAdvancedPrivacyNetworkDescription": { "message": "가능한 가장 안정적인 비공개 이더리움 액세스를 위해 Infura를 원격 프로시저 호출(RPC) 공급업체로 선정하였습니다. 사용자는 개인적으로 원하는 RPC를 선택할 수 있습니다. 하지만, 모든 RPC는 거래를 위해 사용자의 IP 주소와 이더리움 지갑을 수령한다는 점을 잊지 말아야 합니다. Infura의 데이터 처리 방법은 $1에서 상세히 확인할 수 있습니다." }, @@ -3483,9 +3434,6 @@ "onboardingMetametricsInfuraTermsPolicy": { "message": "개인정보 처리방침" }, - "onboardingMetametricsModalTitle": { - "message": "맞춤 네트워크 추가" - }, "onboardingMetametricsNeverCollect": { "message": "$1 앱의 클릭 수와 조회수는 저장되지만, 공개 주소와 같은 기타 세부 정보는 저장되지 않습니다.", "description": "$1 represents `onboardingMetametricsNeverCollectEmphasis`" @@ -3578,9 +3526,6 @@ "onekey": { "message": "OneKey" }, - "onlyAddTrustedNetworks": { - "message": "악성 네트워크 공급업체는 블록체인 상태를 거짓으로 보고하고 네트워크 활동을 기록할 수 있습니다. 신뢰하는 맞춤 네트워크만 추가하세요." - }, "onlyConnectTrust": { "message": "신뢰하는 사이트만 연결하세요. $1", "description": "Text displayed above the buttons for connection confirmation. $1 is the link to the learn more web page." @@ -3605,9 +3550,6 @@ "optional": { "message": "옵션" }, - "optionalWithParanthesis": { - "message": "(옵션)" - }, "options": { "message": "옵션" }, @@ -3959,9 +3901,6 @@ "message": "+ 그 외 $1개", "description": "$1 is a number of additional but unshown items in a list- this message will be shown in place of those items" }, - "popularCustomNetworks": { - "message": "인기 사용자 정의 네트워크" - }, "popularNetworkAddToolTip": { "message": "이러한 네트워크 중 일부는 제삼자에 의존합니다. 이러한 연결은 안정성이 떨어지거나 제삼자가 활동을 추적할 수 있습니다. $1", "description": "$1 is Learn more link" @@ -5298,9 +5237,6 @@ "suggestedTokenName": { "message": "추천 이름:" }, - "suggestedTokenSymbol": { - "message": "추천 티커 심볼:" - }, "support": { "message": "지원" }, @@ -5789,9 +5725,6 @@ "termsOfUseTitle": { "message": "이용약관이 개정되었습니다" }, - "testNetworks": { - "message": "테스트 네트워크" - }, "theme": { "message": "테마" }, @@ -6103,9 +6036,6 @@ "message": "U2F", "description": "A name on an API for the browser to interact with devices that support the U2F protocol. On some browsers we use it to connect MetaMask to Ledger devices." }, - "unMatchedChain": { - "message": "기록에 따르면 이 URL은 이 체인 ID의 알려진 제공업체와 일치하지 않습니다." - }, "unapproved": { "message": "승인되지 않음" }, @@ -6175,9 +6105,6 @@ "urlErrorMsg": { "message": "URI에는 적절한 HTTP/HTTPS 접두사가 필요합니다." }, - "urlExistsErrorMsg": { - "message": "이 URL은 현재 $1 네트워크에서 사용됩니다." - }, "use4ByteResolution": { "message": "스마트 계약 디코딩" }, @@ -6387,9 +6314,6 @@ "whatsThis": { "message": "이것은 무엇인가요?" }, - "wrongChainId": { - "message": "이 체인 ID는 네트워크 이름과 일치하지 않습니다." - }, "wrongNetworkName": { "message": "기록에 따르면 네트워크 이름이 이 체인 ID와 일치하지 않습니다." }, @@ -6403,10 +6327,6 @@ "you": { "message": "회원님" }, - "youHaveAddedAll": { - "message": "모든 인기 네트워크를 추가했습니다. $1에서 더 많은 네트워크를 확인하거나 $2을(를) 할 수 있습니다.", - "description": "$1 is a link with the text 'here' and $2 is a button with the text 'add more networks manually'" - }, "youNeedToAllowCameraAccess": { "message": "이 기능을 사용하려면 카메라 액세스를 허용해야 합니다." }, diff --git a/app/_locales/lt/messages.json b/app/_locales/lt/messages.json index 1c0c341f7726..0668166eb8fe 100644 --- a/app/_locales/lt/messages.json +++ b/app/_locales/lt/messages.json @@ -205,9 +205,6 @@ "delete": { "message": "Ištrinti" }, - "deleteNetwork": { - "message": "Panaikinti tinklą?" - }, "details": { "message": "Išsami informacija" }, @@ -351,9 +348,6 @@ "invalidAddressRecipient": { "message": "Gavėjo adresas netinkamas" }, - "invalidBlockExplorerURL": { - "message": "Netinkamas Block Explorer URL" - }, "invalidRPC": { "message": "Netinkamas RPC URL" }, diff --git a/app/_locales/lv/messages.json b/app/_locales/lv/messages.json index 08b28f2c30ca..3939c9145c12 100644 --- a/app/_locales/lv/messages.json +++ b/app/_locales/lv/messages.json @@ -205,9 +205,6 @@ "delete": { "message": "Dzēst" }, - "deleteNetwork": { - "message": "Dzēst tīklu?" - }, "details": { "message": "Informācija" }, @@ -347,9 +344,6 @@ "invalidAddressRecipient": { "message": "Nederīga saņēmēja adrese" }, - "invalidBlockExplorerURL": { - "message": "Nederīgs Block Explorer URL" - }, "invalidRPC": { "message": "Nederīgs RPC URL" }, diff --git a/app/_locales/ms/messages.json b/app/_locales/ms/messages.json index 8bd9278ef2d3..cfad6a22d73d 100644 --- a/app/_locales/ms/messages.json +++ b/app/_locales/ms/messages.json @@ -205,9 +205,6 @@ "delete": { "message": "Padam" }, - "deleteNetwork": { - "message": "Padamkan Rangkaian?" - }, "details": { "message": "Butiran" }, @@ -340,9 +337,6 @@ "invalidAddressRecipient": { "message": "Alamat penerima tidak sah" }, - "invalidBlockExplorerURL": { - "message": "URL Block Explorer tidak sah" - }, "invalidRPC": { "message": "URL RPC tidak sah" }, diff --git a/app/_locales/nl/messages.json b/app/_locales/nl/messages.json index de0a2fd9f5e2..946976a7a93e 100644 --- a/app/_locales/nl/messages.json +++ b/app/_locales/nl/messages.json @@ -140,9 +140,6 @@ "invalidAddressRecipient": { "message": "Het adres van de ontvanger is ongeldig" }, - "invalidBlockExplorerURL": { - "message": "Ongeldige Block Explorer URI" - }, "invalidRPC": { "message": "Ongeldige RPC-URI" }, diff --git a/app/_locales/no/messages.json b/app/_locales/no/messages.json index 7e50d555e852..0d1fa5173a9f 100644 --- a/app/_locales/no/messages.json +++ b/app/_locales/no/messages.json @@ -202,9 +202,6 @@ "delete": { "message": "Slett" }, - "deleteNetwork": { - "message": "Slette nettverk? " - }, "details": { "message": "Detaljer" }, @@ -338,9 +335,6 @@ "invalidAddressRecipient": { "message": "Mottaksadresse er ugyldig " }, - "invalidBlockExplorerURL": { - "message": "Ugyldig Block Explorer URL" - }, "invalidRPC": { "message": "Ugyldig RPC URL" }, diff --git a/app/_locales/ph/messages.json b/app/_locales/ph/messages.json index ecbc7a99ab37..df15e05a0bb6 100644 --- a/app/_locales/ph/messages.json +++ b/app/_locales/ph/messages.json @@ -371,9 +371,6 @@ "delete": { "message": "I-delete" }, - "deleteNetwork": { - "message": "I-delete ang Network?" - }, "details": { "message": "Mga Detalye" }, @@ -667,9 +664,6 @@ "invalidAddressRecipient": { "message": "Hindi valid ang address ng tatanggap" }, - "invalidBlockExplorerURL": { - "message": "Invalid na URL ng Block Explorer" - }, "invalidChainIdTooBig": { "message": "Invalid na chain ID. Masyadong malaki ang chain ID." }, @@ -832,9 +826,6 @@ "networkNameTestnet": { "message": "Testnet" }, - "networkSettingsChainIdDescription": { - "message": "Ginagamit ang chain ID sa paglagda ng mga transaksyon. Dapat itong tumugma sa chain ID na ibinalik ng network. Puwede kang maglagay ng decimal o '0x'-prefixed hexadecimal number, pero ipapakita namin ang numero sa decimal." - }, "networkURL": { "message": "URL ng Network" }, @@ -915,9 +906,6 @@ "on": { "message": "Naka-on" }, - "onlyAddTrustedNetworks": { - "message": "Magagawa ng nakakapinsalang network provider na magsinungaling tungkol sa status ng blockchain at itala ang aktibidad ng iyong network. Magdagdag lang ng mga custom na network na pinagkakatiwalaan mo." - }, "onlyConnectTrust": { "message": "Kumonekta lang sa mga site na pinagkakatiwalaan mo.", "description": "Text displayed above the buttons for connection confirmation. $1 is the link to the learn more web page." @@ -1673,9 +1661,6 @@ "urlErrorMsg": { "message": "Kinakailangan ng mga URL ang naaangkop na HTTP/HTTPS prefix." }, - "urlExistsErrorMsg": { - "message": "Kasalukuyang ginagamit ng $1 network ang URL na ito." - }, "usePhishingDetection": { "message": "Gumamit ng Pag-detect ng Phishing" }, diff --git a/app/_locales/pl/messages.json b/app/_locales/pl/messages.json index 46b178cdf985..cb82388a8634 100644 --- a/app/_locales/pl/messages.json +++ b/app/_locales/pl/messages.json @@ -205,9 +205,6 @@ "delete": { "message": "Usuń" }, - "deleteNetwork": { - "message": "Usunąć sieć?" - }, "details": { "message": "Szczegóły" }, @@ -351,9 +348,6 @@ "invalidAddressRecipient": { "message": "Nieprawidłowy adres odbiorcy" }, - "invalidBlockExplorerURL": { - "message": "Nieprawidłowe Block Explorer URI" - }, "invalidRPC": { "message": "Nieprawidłowe RPC URI" }, diff --git a/app/_locales/pt/messages.json b/app/_locales/pt/messages.json index 27587daf31d0..a5de21cb0889 100644 --- a/app/_locales/pt/messages.json +++ b/app/_locales/pt/messages.json @@ -183,9 +183,6 @@ "addANetwork": { "message": "Adicionar uma rede" }, - "addANetworkManually": { - "message": "Adicionar uma rede manualmente" - }, "addANickname": { "message": "Adicionar um apelido" }, @@ -252,9 +249,6 @@ "addFriendsAndAddresses": { "message": "Adicionar amigos e endereços confiáveis" }, - "addFromAListOfPopularNetworks": { - "message": "Adicione a partir de uma lista de redes populares ou adicione uma rede manualmente. Interaja apenas com entidades de sua confiança." - }, "addHardwareWallet": { "message": "Adicionar carteira de hardware" }, @@ -267,16 +261,9 @@ "addMemo": { "message": "Adicionar observação" }, - "addMoreNetworks": { - "message": "adicionar mais redes manualmente" - }, "addNetwork": { "message": "Adicionar rede" }, - "addNetworkTooltipWarning": { - "message": "Esta conexão de rede depende de terceiros. Esta conexão pode ser menos confiável ou permitir que terceiros rastreiem atividades. $1", - "description": "$1 is Learn more link" - }, "addNewAccount": { "message": "Adicionar uma nova conta Ethereum" }, @@ -1000,9 +987,6 @@ "confirmConnectionTitle": { "message": "Confirmar conexão ao $1" }, - "confirmDeletion": { - "message": "Confirmar exclusão" - }, "confirmFieldPaymaster": { "message": "Taxa paga por" }, @@ -1015,9 +999,6 @@ "confirmRecoveryPhrase": { "message": "Confirmar Frase de Recuperação Secreta" }, - "confirmRpcUrlDeletionMessage": { - "message": "Tem certeza de que deseja excluir o URL da RPC? Suas informações não serão salvas para essa rede." - }, "confirmTitleDescPermitSignature": { "message": "Este site quer permissão para gastar seus tokens." }, @@ -1368,9 +1349,6 @@ "custom": { "message": "Avançado" }, - "customContentSearch": { - "message": "Pesquise uma rede previamente adicionada" - }, "customGasSettingToolTipMessage": { "message": "Use $1 para personalizar o preço do gás. Isso pode parecer confuso se você não estiver familiarizado. Interaja por sua conta e risco.", "description": "$1 is key 'advanced' (text: 'Advanced') separated here so that it can be passed in with bold font-weight" @@ -1490,9 +1468,6 @@ "deleteContact": { "message": "Excluir contato" }, - "deleteNetwork": { - "message": "Excluir rede?" - }, "deleteNetworkIntro": { "message": "Se excluir essa rede, você precisará adicioná-la novamente para ver seus ativos nela" }, @@ -1500,9 +1475,6 @@ "message": "Excluir rede $1?", "description": "$1 represents the name of the network" }, - "deleteRpcUrl": { - "message": "Excluir URL da RPC" - }, "deposit": { "message": "Depositar" }, @@ -1867,9 +1839,6 @@ "existingRequestsBannerAlertDesc": { "message": "Para visualizar e confirmar sua solicitação mais recente, primeiro é necessário aprovar ou recusar as solicitações existentes." }, - "existingRpcUrl": { - "message": "Este URL está associado a outro ID de cadeia." - }, "expandView": { "message": "Expandir exibição" }, @@ -1901,9 +1870,6 @@ "failedToFetchChainId": { "message": "Não foi possível buscar o ID da cadeia. Seu URL da RPC está correto?" }, - "failedToFetchTickerSymbolData": { - "message": "Dados de verificação do símbolo do ticker indisponíveis no momento. Certifique-se de que o símbolo inserido está correto. Isso afetará as taxas de conversão indicadas para essa rede" - }, "failureMessage": { "message": "Ocorreu algum erro e não conseguimos concluir a ação" }, @@ -1924,9 +1890,6 @@ "message": "A importação de arquivo não está funcionando? Clique aqui!", "description": "Helps user import their account from a JSON file" }, - "findTheRightChainId": { - "message": "Encontre a opção correta em:" - }, "flaskWelcomeUninstall": { "message": "você deve desinstalar essa extensão", "description": "This request is shown on the Flask Welcome screen. It is intended for non-developers, and will be bolded." @@ -2416,9 +2379,6 @@ "invalidAssetType": { "message": "Esse ativo é um NFT e precisa ser adicionado novamente à página Importar NFT, encontrada na aba NFTs." }, - "invalidBlockExplorerURL": { - "message": "URL inválido do explorador de blocos" - }, "invalidChainIdTooBig": { "message": "ID de cadeia inválido. O ID de cadeia é muito grande." }, @@ -2938,9 +2898,6 @@ "network": { "message": "Ethereum:" }, - "networkAddedSuccessfully": { - "message": "Rede adicionada com sucesso!" - }, "networkDetails": { "message": "Detalhes da rede" }, @@ -3001,9 +2958,6 @@ "networkProvider": { "message": "Provedor de rede" }, - "networkSettingsChainIdDescription": { - "message": "O ID da cadeia é usado para assinar transações. É preciso ser igual ao ID da cadeia retornado pela rede. Você pode informar um número decimal ou um número hexadecimal com prefixo “0x”, mas exibiremos o número em casas decimais." - }, "networkStatus": { "message": "Status da rede" }, @@ -3452,9 +3406,6 @@ "onboardingAdvancedPrivacyIPFSValid": { "message": "O URL do gateway IPFS é válido" }, - "onboardingAdvancedPrivacyNetworkButton": { - "message": "Adicionar rede personalizada" - }, "onboardingAdvancedPrivacyNetworkDescription": { "message": "Usamos a Infura como nosso provedor de chamadas de procedimento remoto (RPC) para oferecermos o acesso mais confiável e privado possível aos dados do Ethereum. Você pode escolher sua própria RPC, mas lembre-se de que qualquer RPC receberá seu endereço IP e da carteira de Ethereum para realizar transações. Leia nosso $1 para saber mais sobre como a Infura trata os dados." }, @@ -3483,9 +3434,6 @@ "onboardingMetametricsInfuraTermsPolicy": { "message": "Política de Privacidade" }, - "onboardingMetametricsModalTitle": { - "message": "Adicionar rede personalizada" - }, "onboardingMetametricsNeverCollect": { "message": "$1 cliques e visualizações no app são armazenados, mas outros detalhes (como seu endereço público) não são.", "description": "$1 represents `onboardingMetametricsNeverCollectEmphasis`" @@ -3578,9 +3526,6 @@ "onekey": { "message": "OneKey" }, - "onlyAddTrustedNetworks": { - "message": "Um provedor de rede mal-intencionado pode mentir sobre o estado da blockchain e registrar as atividades da sua rede. Adicione somente as redes personalizadas em que você confia." - }, "onlyConnectTrust": { "message": "Conecte-se somente com sites em que você confia. $1", "description": "Text displayed above the buttons for connection confirmation. $1 is the link to the learn more web page." @@ -3605,9 +3550,6 @@ "optional": { "message": "Opcional" }, - "optionalWithParanthesis": { - "message": "(Opcional)" - }, "options": { "message": "Opções" }, @@ -3959,9 +3901,6 @@ "message": "E mais $1", "description": "$1 is a number of additional but unshown items in a list- this message will be shown in place of those items" }, - "popularCustomNetworks": { - "message": "Redes personalizadas populares" - }, "popularNetworkAddToolTip": { "message": "Algumas dessas redes dependem de terceiros. As conexões podem ser menos confiáveis ​​ou permitir que terceiros rastreiem atividades. $1", "description": "$1 is Learn more link" @@ -5298,9 +5237,6 @@ "suggestedTokenName": { "message": "Nome sugerido:" }, - "suggestedTokenSymbol": { - "message": "Símbolo do ticker sugerido:" - }, "support": { "message": "Suporte" }, @@ -5789,9 +5725,6 @@ "termsOfUseTitle": { "message": "Nossos Termos de Uso foram atualizados" }, - "testNetworks": { - "message": "Redes de teste" - }, "theme": { "message": "Tema" }, @@ -6103,9 +6036,6 @@ "message": "U2F", "description": "A name on an API for the browser to interact with devices that support the U2F protocol. On some browsers we use it to connect MetaMask to Ledger devices." }, - "unMatchedChain": { - "message": "De acordo com nossos registros, este URL não corresponde a um provedor conhecido para este ID de cadeia." - }, "unapproved": { "message": "Não aprovado" }, @@ -6175,9 +6105,6 @@ "urlErrorMsg": { "message": "URLs precisam do prefixo HTTP/HTTPS adequado." }, - "urlExistsErrorMsg": { - "message": "O ID da cadeia está sendo usado pela rede $1." - }, "use4ByteResolution": { "message": "Decodificar contratos inteligentes" }, @@ -6387,9 +6314,6 @@ "whatsThis": { "message": "O que é isso?" }, - "wrongChainId": { - "message": "Este ID de cadeia não corresponde ao nome da rede." - }, "wrongNetworkName": { "message": "De acordo com os nossos registros, o nome da rede pode não corresponder a esta ID de cadeia." }, @@ -6403,10 +6327,6 @@ "you": { "message": "Você" }, - "youHaveAddedAll": { - "message": "Você adicionou todas as redes populares. Você pode descobrir mais redes $1 Ou você pode $2", - "description": "$1 is a link with the text 'here' and $2 is a button with the text 'add more networks manually'" - }, "youNeedToAllowCameraAccess": { "message": "Você precisa permitir o acesso à câmera para usar esse recurso." }, diff --git a/app/_locales/pt_BR/messages.json b/app/_locales/pt_BR/messages.json index eae48ba1dc7f..4c4b17d74f6e 100644 --- a/app/_locales/pt_BR/messages.json +++ b/app/_locales/pt_BR/messages.json @@ -534,9 +534,6 @@ "delete": { "message": "Excluir" }, - "deleteNetwork": { - "message": "Excluir rede?" - }, "description": { "message": "Descrição" }, @@ -1014,9 +1011,6 @@ "invalidAddressRecipient": { "message": "O endereço do destinatário é inválido" }, - "invalidBlockExplorerURL": { - "message": "URL inválido do Block Explorer" - }, "invalidChainIdTooBig": { "message": "ID de cadeia inválido. O ID de cadeia é muito grande." }, @@ -1263,9 +1257,6 @@ "networkNameTestnet": { "message": "Testnet" }, - "networkSettingsChainIdDescription": { - "message": "O ID da cadeia é usado para assinar transações. É preciso ser igual ao ID da cadeia retornado pela rede. Você pode informar um número decimal ou um número hexadecimal com prefixo “0x”, mas exibiremos o número em casas decimais." - }, "networkStatus": { "message": "Status da rede" }, @@ -1424,9 +1415,6 @@ "message": "Os alertas de detecção de phishing dependem de comunicação com $1. O jsDeliver terá acesso ao seu endereço IP. Veja $2.", "description": "The $1 is the word 'jsDeliver', from key 'jsDeliver' and $2 is the words Privacy Policy from key 'privacyMsg', both separated here so that it can be wrapped as a link" }, - "onlyAddTrustedNetworks": { - "message": "Um provedor de rede mal-intencionado pode mentir sobre o estado do blockchain e registrar as atividades da sua rede. Adicione somente as redes personalizadas em que você confia." - }, "onlyConnectTrust": { "message": "Conecte-se somente com sites em que você confia.", "description": "Text displayed above the buttons for connection confirmation. $1 is the link to the learn more web page." @@ -1438,9 +1426,6 @@ "optional": { "message": "Opcional" }, - "optionalWithParanthesis": { - "message": "(Opcional)" - }, "or": { "message": "ou" }, @@ -2430,9 +2415,6 @@ "urlErrorMsg": { "message": "Os URLs precisam do prefixo HTTP/HTTPS adequado." }, - "urlExistsErrorMsg": { - "message": "O ID da cadeia está sendo usado pela rede $1." - }, "useNftDetection": { "message": "Detectar NFTs automaticamente" }, diff --git a/app/_locales/ro/messages.json b/app/_locales/ro/messages.json index dde9be461087..aad720151da6 100644 --- a/app/_locales/ro/messages.json +++ b/app/_locales/ro/messages.json @@ -205,9 +205,6 @@ "delete": { "message": "Șterge" }, - "deleteNetwork": { - "message": "Ștergeți rețeaua?" - }, "details": { "message": "Detalii" }, @@ -341,9 +338,6 @@ "invalidAddressRecipient": { "message": "Adresa destinatarului nu este validă" }, - "invalidBlockExplorerURL": { - "message": "URL Block Explorer nevalid" - }, "invalidRPC": { "message": "URL RPC nevalid" }, diff --git a/app/_locales/ru/messages.json b/app/_locales/ru/messages.json index 6b13eaf1a46f..aba23b0b3284 100644 --- a/app/_locales/ru/messages.json +++ b/app/_locales/ru/messages.json @@ -183,9 +183,6 @@ "addANetwork": { "message": "Добавить сеть" }, - "addANetworkManually": { - "message": "Добавить сеть вручную" - }, "addANickname": { "message": "Добавить ник" }, @@ -252,9 +249,6 @@ "addFriendsAndAddresses": { "message": "Добавьте друзей и адреса, которым доверяете" }, - "addFromAListOfPopularNetworks": { - "message": "Добавьте из списка популярных сетей или добавьте сеть вручную. Взаимодействуйте только с теми организациями, которым доверяете." - }, "addHardwareWallet": { "message": "Добавить аппаратный кошелек" }, @@ -267,16 +261,9 @@ "addMemo": { "message": "Добавить примечание" }, - "addMoreNetworks": { - "message": "добавить другие сети вручную" - }, "addNetwork": { "message": "Добавить сеть" }, - "addNetworkTooltipWarning": { - "message": "Это сетевое подключение зависит от третьих сторон. Оно может быть менее надежным или позволять третьим лицам отслеживать активность. $1", - "description": "$1 is Learn more link" - }, "addNewAccount": { "message": "Добавить новый счет Ethereum" }, @@ -1000,9 +987,6 @@ "confirmConnectionTitle": { "message": "Подтвердите подключение к $1" }, - "confirmDeletion": { - "message": "Подтвердить удаление" - }, "confirmFieldPaymaster": { "message": "Комиссия оплачена" }, @@ -1015,9 +999,6 @@ "confirmRecoveryPhrase": { "message": "Подтвердите секретную фразу для восстановления" }, - "confirmRpcUrlDeletionMessage": { - "message": "Уверены, что хотите удалить URL-адрес RPC? Ваша информация не будет сохранена для этой сети." - }, "confirmTitleDescPermitSignature": { "message": "Этот сайт хочет получить разрешение на расходование ваших токенов." }, @@ -1368,9 +1349,6 @@ "custom": { "message": "Дополнительно" }, - "customContentSearch": { - "message": "Поиск ранее добавленной сети" - }, "customGasSettingToolTipMessage": { "message": "Использовать $1, чтобы настроить цену на газ. Это может сбивать с толку, если вы не знакомы с этим. Взаимодействуйте на свой страх и риск.", "description": "$1 is key 'advanced' (text: 'Advanced') separated here so that it can be passed in with bold font-weight" @@ -1490,9 +1468,6 @@ "deleteContact": { "message": "Удалить контакт" }, - "deleteNetwork": { - "message": "Удалить сеть?" - }, "deleteNetworkIntro": { "message": "Если вы удалите эту сеть, вам нужно будет добавить ее снова, чтобы просмотреть свои активы в этой сети" }, @@ -1500,9 +1475,6 @@ "message": "Удалить сеть $1?", "description": "$1 represents the name of the network" }, - "deleteRpcUrl": { - "message": "Удалить URL-адрес RPC" - }, "deposit": { "message": "Депозит" }, @@ -1867,9 +1839,6 @@ "existingRequestsBannerAlertDesc": { "message": "Чтобы просмотреть и подтвердить свой последний запрос, вам необходимо сначала утвердить или отклонить существующие запросы." }, - "existingRpcUrl": { - "message": "Этот URL-адрес связан с другим ID блокчейна." - }, "expandView": { "message": "Развернуть представление" }, @@ -1901,9 +1870,6 @@ "failedToFetchChainId": { "message": "Не удалось получить ID блокчейна. Ваш URL RPC правильный?" }, - "failedToFetchTickerSymbolData": { - "message": "Данные проверки тикера в настоящее время недоступны. Убедитесь, что вы ввели правильный символ. Это повлияет на коэффициенты обмена, которые вы видите для этой сети." - }, "failureMessage": { "message": "Что-то пошло не так, и мы не смогли завершить действие" }, @@ -1924,9 +1890,6 @@ "message": "Импорт файлов не работает? Нажмите здесь!", "description": "Helps user import their account from a JSON file" }, - "findTheRightChainId": { - "message": "Найдите подходящий в:" - }, "flaskWelcomeUninstall": { "message": "вам нужно должны удалить это расширение", "description": "This request is shown on the Flask Welcome screen. It is intended for non-developers, and will be bolded." @@ -2416,9 +2379,6 @@ "invalidAssetType": { "message": "Этот актив является NFT, и его необходимо повторно добавить на странице «Импорт NFT», которая находится на вкладке NFT." }, - "invalidBlockExplorerURL": { - "message": "Недействительный URL обозревателя блоков" - }, "invalidChainIdTooBig": { "message": "Недействительный ID блокчейна. Он слишком длинный." }, @@ -2938,9 +2898,6 @@ "network": { "message": "Сеть:" }, - "networkAddedSuccessfully": { - "message": "Сеть успешно добавлена!" - }, "networkDetails": { "message": "Сведения о сети" }, @@ -3001,9 +2958,6 @@ "networkProvider": { "message": "Поставщик услуг сети" }, - "networkSettingsChainIdDescription": { - "message": "ID блокчейна используется для подписания транзакций. Он должен соответствовать ID блокчейна, возвращаемому сетью. Вы можете ввести десятичное или шестнадцатеричное число с префиксом «0x», но мы будем отображать число в десятичном виде." - }, "networkStatus": { "message": "Статус сети" }, @@ -3452,9 +3406,6 @@ "onboardingAdvancedPrivacyIPFSValid": { "message": "URL шлюза IPFS действителен" }, - "onboardingAdvancedPrivacyNetworkButton": { - "message": "Добавить пользовательскую сеть" - }, "onboardingAdvancedPrivacyNetworkDescription": { "message": "Мы используем Infura в качестве нашего поставщика удаленного вызова процедур (RPC), чтобы предложить наиболее надежный и конфиденциальный доступ к данным Ethereum из возможных. Вы можете выбрать свой собственного RPC, но помните, что любой RPC получит ваш IP-адрес и номер кошелька Ethereum для совершения транзакций. См. наше $1, чтобы узнать больше о том, как Infura обрабатывает данные." }, @@ -3483,9 +3434,6 @@ "onboardingMetametricsInfuraTermsPolicy": { "message": "Политикой конфиденциальности" }, - "onboardingMetametricsModalTitle": { - "message": "Добавить пользовательскую сеть" - }, "onboardingMetametricsNeverCollect": { "message": "$1 клики и просмотры в приложении сохраняются, но другие данные (например, ваш публичный адрес) — нет.", "description": "$1 represents `onboardingMetametricsNeverCollectEmphasis`" @@ -3578,9 +3526,6 @@ "onekey": { "message": "OneKey" }, - "onlyAddTrustedNetworks": { - "message": "Вредоносный сетевой провайдер может дезинформировать о состоянии блокчейна и записывать ваши действия в сети. Добавляйте только те пользовательские сети, которым доверяете." - }, "onlyConnectTrust": { "message": "Подключайтесь только к сайтам, которым доверяете. $1", "description": "Text displayed above the buttons for connection confirmation. $1 is the link to the learn more web page." @@ -3605,9 +3550,6 @@ "optional": { "message": "Необязательно" }, - "optionalWithParanthesis": { - "message": "(Необязательно)" - }, "options": { "message": "Опционы" }, @@ -3959,9 +3901,6 @@ "message": "+ еще $1", "description": "$1 is a number of additional but unshown items in a list- this message will be shown in place of those items" }, - "popularCustomNetworks": { - "message": "Популярные пользовательские сети" - }, "popularNetworkAddToolTip": { "message": "Некоторые из этих сетей являются зависимыми от третьих сторон. Соединения могут быть менее надежными или позволять третьим сторонам отслеживать активность. $1", "description": "$1 is Learn more link" @@ -5298,9 +5237,6 @@ "suggestedTokenName": { "message": "Предлагаемое имя:" }, - "suggestedTokenSymbol": { - "message": "Рекомендуемый символ-тикер:" - }, "support": { "message": "Поддержка" }, @@ -5789,9 +5725,6 @@ "termsOfUseTitle": { "message": "Наши Условия использования обновлены" }, - "testNetworks": { - "message": "Протестировать сети" - }, "theme": { "message": "Тема" }, @@ -6103,9 +6036,6 @@ "message": "U2F", "description": "A name on an API for the browser to interact with devices that support the U2F protocol. On some browsers we use it to connect MetaMask to Ledger devices." }, - "unMatchedChain": { - "message": "Согласно нашим данным, этот URL-адрес не соответствует известному поставщику для этого ID блокчейна." - }, "unapproved": { "message": "Не одобрен" }, @@ -6175,9 +6105,6 @@ "urlErrorMsg": { "message": "URL должен иметь соответствующий префикс HTTP/HTTPS." }, - "urlExistsErrorMsg": { - "message": "Это URL в настоящее время используется сетью $1." - }, "use4ByteResolution": { "message": "Расшифровать смарт-контракты" }, @@ -6387,9 +6314,6 @@ "whatsThis": { "message": "Что это?" }, - "wrongChainId": { - "message": "Этот ID блокчейна не соответствует имени сети." - }, "wrongNetworkName": { "message": "Согласно нашим записям, имя сети может не соответствовать этому ID блокчейна." }, @@ -6403,10 +6327,6 @@ "you": { "message": "Вы" }, - "youHaveAddedAll": { - "message": "Вы добавили все популярные сети. Вы можете открыть для себя больше сетей $1 или можете $2", - "description": "$1 is a link with the text 'here' and $2 is a button with the text 'add more networks manually'" - }, "youNeedToAllowCameraAccess": { "message": "Для использования этой функции вам необходимо предоставить доступ к камере." }, diff --git a/app/_locales/sk/messages.json b/app/_locales/sk/messages.json index 6e28cc44f574..564e7cb12d94 100644 --- a/app/_locales/sk/messages.json +++ b/app/_locales/sk/messages.json @@ -199,9 +199,6 @@ "delete": { "message": "Odstrániť" }, - "deleteNetwork": { - "message": "Odstrániť sieť?" - }, "details": { "message": "Podrobnosti" }, @@ -338,9 +335,6 @@ "invalidAddressRecipient": { "message": "Adresa příjemce je neplatná" }, - "invalidBlockExplorerURL": { - "message": "Neplatné Block Explorer URI" - }, "invalidRPC": { "message": "Neplatné RPC URI" }, diff --git a/app/_locales/sl/messages.json b/app/_locales/sl/messages.json index 60f18a004305..8d43d184c427 100644 --- a/app/_locales/sl/messages.json +++ b/app/_locales/sl/messages.json @@ -205,9 +205,6 @@ "delete": { "message": "Izbriši" }, - "deleteNetwork": { - "message": "Izbrišem to omrežje?" - }, "details": { "message": "Podrobnosti" }, @@ -345,9 +342,6 @@ "invalidAddressRecipient": { "message": "Neveljaven nasklov prejemnika" }, - "invalidBlockExplorerURL": { - "message": "Neveljaven Block Explorer URL" - }, "invalidRPC": { "message": "Neveljaven RPC URL" }, diff --git a/app/_locales/sr/messages.json b/app/_locales/sr/messages.json index 58bc09614d32..d3f5e27c6235 100644 --- a/app/_locales/sr/messages.json +++ b/app/_locales/sr/messages.json @@ -202,9 +202,6 @@ "delete": { "message": "Избриши" }, - "deleteNetwork": { - "message": "Da li želite da obrišete mrežu?" - }, "details": { "message": "Детаљи" }, @@ -348,9 +345,6 @@ "invalidAddressRecipient": { "message": "Adresa primaoca nije važeća" }, - "invalidBlockExplorerURL": { - "message": "Nevažeći Block Explorer URL" - }, "invalidRPC": { "message": "Nevažeći RPC URL" }, diff --git a/app/_locales/sv/messages.json b/app/_locales/sv/messages.json index 204c84f63caa..a98db5bea015 100644 --- a/app/_locales/sv/messages.json +++ b/app/_locales/sv/messages.json @@ -199,9 +199,6 @@ "delete": { "message": "Radera" }, - "deleteNetwork": { - "message": "Radera nätverk?" - }, "details": { "message": "Info" }, @@ -341,9 +338,6 @@ "invalidAddressRecipient": { "message": "Mottagarens adress är ogiltig" }, - "invalidBlockExplorerURL": { - "message": "Ogiltig Block Explorer URL" - }, "invalidRPC": { "message": "Ogiltig RPC-URL" }, diff --git a/app/_locales/sw/messages.json b/app/_locales/sw/messages.json index 1d9be8ad3f8d..d8ad6258e8ba 100644 --- a/app/_locales/sw/messages.json +++ b/app/_locales/sw/messages.json @@ -199,9 +199,6 @@ "delete": { "message": "Futa" }, - "deleteNetwork": { - "message": "Futa Mtandao?" - }, "details": { "message": "Maelezo" }, @@ -338,9 +335,6 @@ "invalidAddressRecipient": { "message": "Anwani ya mpokeaji si halali" }, - "invalidBlockExplorerURL": { - "message": "Block Explorer URL batili" - }, "invalidRPC": { "message": "RPC URL batili" }, diff --git a/app/_locales/ta/messages.json b/app/_locales/ta/messages.json index 8a6f3351db0d..8e38061bebb2 100644 --- a/app/_locales/ta/messages.json +++ b/app/_locales/ta/messages.json @@ -191,9 +191,6 @@ "invalidAddressRecipient": { "message": "பெறுநர் முகவரி தவறானது" }, - "invalidBlockExplorerURL": { - "message": "தவறான Block Explorer URI" - }, "invalidRPC": { "message": "தவறான RPC URI" }, diff --git a/app/_locales/th/messages.json b/app/_locales/th/messages.json index 19b005ef7cee..c3b4a8a6e3fa 100644 --- a/app/_locales/th/messages.json +++ b/app/_locales/th/messages.json @@ -93,9 +93,6 @@ "delete": { "message": "ลบ" }, - "deleteNetwork": { - "message": "ลบเครือข่าย?" - }, "details": { "message": "รายละเอียด" }, @@ -182,9 +179,6 @@ "invalidAddressRecipient": { "message": "แอดแดรสผู้รับไม่ถูกต้อง" }, - "invalidBlockExplorerURL": { - "message": "Block Explorer URI ไม่ถูกต้อง" - }, "invalidRPC": { "message": "RPC URI ไม่ถูกต้อง" }, diff --git a/app/_locales/tl/messages.json b/app/_locales/tl/messages.json index 624e9059634a..9089ff123db1 100644 --- a/app/_locales/tl/messages.json +++ b/app/_locales/tl/messages.json @@ -183,9 +183,6 @@ "addANetwork": { "message": "Magdagdag ng network" }, - "addANetworkManually": { - "message": "Mano-manong idagdag ang network" - }, "addANickname": { "message": "Magdagdag ng palayaw" }, @@ -252,9 +249,6 @@ "addFriendsAndAddresses": { "message": "Magdagdag ng mga kaibigan at address na pinagkakatiwalaan mo" }, - "addFromAListOfPopularNetworks": { - "message": "Idagdag mula sa listahan ng mga sikat na network o mano-manong idagdag ang network. Makipag-ugnayan lamang sa mga entidad na iyong pinagkakatiwalaan." - }, "addHardwareWallet": { "message": "Magdagdag ng wallet na hardware" }, @@ -267,16 +261,9 @@ "addMemo": { "message": "Magdagdag ng memo" }, - "addMoreNetworks": { - "message": "magdagdag pa ng mga network nang mano-mano" - }, "addNetwork": { "message": "Magdagdag ng Network" }, - "addNetworkTooltipWarning": { - "message": "Ang koneksyon sa network na ito ay umaasa sa mga third party. Ang koneksyon na ito ay maaaring hindi gaanong maaasahan o binibigyang-daan ang mga third-party na mag-track ng aktibidad. $1", - "description": "$1 is Learn more link" - }, "addNewAccount": { "message": "Magdagdag ng bagong account sa Ethereum" }, @@ -1000,9 +987,6 @@ "confirmConnectionTitle": { "message": "Kumpirmahin ang koneksyon sa $1" }, - "confirmDeletion": { - "message": "Kumpirmahin ang pagtanggal" - }, "confirmFieldPaymaster": { "message": "Ang bayarin ay binayaran ni" }, @@ -1015,9 +999,6 @@ "confirmRecoveryPhrase": { "message": "Kumpirmahin ang Lihim na Parirala sa Pagbawi" }, - "confirmRpcUrlDeletionMessage": { - "message": "Sigurado ka ba na nais mong tanggalin ang RPC URL? Ang iyong impormasyon ay hindi mase-save sa network na ito." - }, "confirmTitleDescPermitSignature": { "message": "Ang site na ito ay nais ng permiso para gamitin ang iyong mga token." }, @@ -1368,9 +1349,6 @@ "custom": { "message": "Makabago" }, - "customContentSearch": { - "message": "Maghanap ng naunang idinagdag na network" - }, "customGasSettingToolTipMessage": { "message": "Gamitin ang $1 para i-customize ang presyo ng gas. Ito ay maaaring nakakalito kung hindi ka pamilyar. Harapin ang sarili mong panganib.", "description": "$1 is key 'advanced' (text: 'Advanced') separated here so that it can be passed in with bold font-weight" @@ -1490,9 +1468,6 @@ "deleteContact": { "message": "Tanggalin ang kontak" }, - "deleteNetwork": { - "message": "Tanggalin ang network?" - }, "deleteNetworkIntro": { "message": "Kapag tinanggal mo ang network na ito, kakailanganin mo itong idagdag muli para makita ang mga asset mo sa network na ito" }, @@ -1500,9 +1475,6 @@ "message": "Tanggalin ang $1 network?", "description": "$1 represents the name of the network" }, - "deleteRpcUrl": { - "message": "Tanggalin ang RPC URL" - }, "deposit": { "message": "Deposito" }, @@ -1867,9 +1839,6 @@ "existingRequestsBannerAlertDesc": { "message": "Para tingnan at kumpirmahin ang iyong pinakabagong hiling, kailangan mo munang aprubahan o tanggihan ang mga umiiral na hiling." }, - "existingRpcUrl": { - "message": "Ang URL na ito ay nauugnay sa ibang ID ng chain." - }, "expandView": { "message": "I-expand ang view" }, @@ -1901,9 +1870,6 @@ "failedToFetchChainId": { "message": "Hindi makuha ang ID ng chain. Tama ba ang iyong RPC URL?" }, - "failedToFetchTickerSymbolData": { - "message": "Ang data ng pag-verify sa simbolo ng ticker ay hindi available sa kasalukuyan, tiyaking tama ang simbolo na inilagay mo. Makakaapekto ito sa mga rate ng palitan na nakikita mo para sa network na ito" - }, "failureMessage": { "message": "Nagkaproblema, at hindi namin makumpleto ang aksyon" }, @@ -1924,9 +1890,6 @@ "message": "Hindi gumagana ang pag-import ng file? Mag-click dito!", "description": "Helps user import their account from a JSON file" }, - "findTheRightChainId": { - "message": "Hanapin ang tama sa:" - }, "flaskWelcomeUninstall": { "message": "dapat mong i-uninstall ang extension na ito", "description": "This request is shown on the Flask Welcome screen. It is intended for non-developers, and will be bolded." @@ -2416,9 +2379,6 @@ "invalidAssetType": { "message": "Ang asset na ito ay isang NFT at kailangang idagdag muli sa pahina ng Mag-import ng mga NFT na matatagpuan sa ilalim ng tab ng mga NFT" }, - "invalidBlockExplorerURL": { - "message": "Di-wastong URL ng block explorer" - }, "invalidChainIdTooBig": { "message": "Di-wastong ID ng chain. Napakalaki ng ID ng chain." }, @@ -2938,9 +2898,6 @@ "network": { "message": "Network:" }, - "networkAddedSuccessfully": { - "message": "Matagumpay na naidagdag ang network!" - }, "networkDetails": { "message": "Mga Detalye ng Network" }, @@ -3001,9 +2958,6 @@ "networkProvider": { "message": "Network provider" }, - "networkSettingsChainIdDescription": { - "message": "Ginagamit ang ID ng chain sa pagpirma ng mga transaksyon. Dapat itong tumugma sa ID ng chain na ibinalik ng network. Puwede kang maglagay ng decimal o '0x'-prefixed hexadecimal number, pero ipapakita namin ang numero sa decimal." - }, "networkStatus": { "message": "Katayuan ng network" }, @@ -3452,9 +3406,6 @@ "onboardingAdvancedPrivacyIPFSValid": { "message": "Ang URL ng IPFS gateway ay wasto" }, - "onboardingAdvancedPrivacyNetworkButton": { - "message": "Magdagdag ng custom na network" - }, "onboardingAdvancedPrivacyNetworkDescription": { "message": "Gumagamit kami ng Infura bilang aming provider ng remote procedure call (RPC) para mag-alok ng pinakamaaasahan at pribadong access sa Ethereum data na kaya namin. Maaari kang pumili ng iyong sariling RPC, ngunit tandaan na ang anumang RPC ay makakatanggap ng iyong IP address at Ethereum wallet upang gumawa ng mga transaksyon. Basahin ang aming $1 para matuto pa tungkol sa kung paano pinangangasiwaan ng Infura ang data." }, @@ -3483,9 +3434,6 @@ "onboardingMetametricsInfuraTermsPolicy": { "message": "Patakaran sa Pagkapribado" }, - "onboardingMetametricsModalTitle": { - "message": "Magdagdag ng custom na network" - }, "onboardingMetametricsNeverCollect": { "message": "$1 mga click at view sa app ay nakaimbak, ngunit ang ibang detalye (tulad ng iyong address) ay hindi.", "description": "$1 represents `onboardingMetametricsNeverCollectEmphasis`" @@ -3578,9 +3526,6 @@ "onekey": { "message": "OneKey" }, - "onlyAddTrustedNetworks": { - "message": "Ang isang mapaminsalang network provider ay maaaring magsinungaling tungkol sa estado ng blockchain at itala ang iyong aktibidad sa network. Magdagdag lamang ng mga custom na network na pinagkakatiwalaan mo." - }, "onlyConnectTrust": { "message": "Kumonekta lang sa mga site na pinagkakatiwalaan mo. $1", "description": "Text displayed above the buttons for connection confirmation. $1 is the link to the learn more web page." @@ -3605,9 +3550,6 @@ "optional": { "message": "Opsyonal" }, - "optionalWithParanthesis": { - "message": "(Opsyonal)" - }, "options": { "message": "Mga pagpipilian" }, @@ -3959,9 +3901,6 @@ "message": "+ $1 pa", "description": "$1 is a number of additional but unshown items in a list- this message will be shown in place of those items" }, - "popularCustomNetworks": { - "message": "Mga sikat na custom na network" - }, "popularNetworkAddToolTip": { "message": "Ang ilan sa mga network na ito ay umaasa sa mga third party. Ang koneksyon na ito ay maaaring hindi gaanong maaasahan o binibigyang-daan ang mga third-party na mag-track ng aktibidad. $1", "description": "$1 is Learn more link" @@ -5298,9 +5237,6 @@ "suggestedTokenName": { "message": "Iminumungkahing pangalan:" }, - "suggestedTokenSymbol": { - "message": "Iminumungkahing ticket symbol:" - }, "support": { "message": "Humingi ng Tulong" }, @@ -5789,9 +5725,6 @@ "termsOfUseTitle": { "message": "Na-update ang aming Mga Tuntunin sa Paggamit" }, - "testNetworks": { - "message": "Suriin ang mga network" - }, "theme": { "message": "Tema" }, @@ -6103,9 +6036,6 @@ "message": "U2F", "description": "A name on an API for the browser to interact with devices that support the U2F protocol. On some browsers we use it to connect MetaMask to Ledger devices." }, - "unMatchedChain": { - "message": "Ayon sa aming mga talaan, ang URL na ito ay hindi tumutugma sa isang kilalang provider para sa ID ng chain na ito." - }, "unapproved": { "message": "Hindi inaprubahan" }, @@ -6175,9 +6105,6 @@ "urlErrorMsg": { "message": "Kinakailangan ng mga URL ang naaangkop na HTTP/HTTPS prefix." }, - "urlExistsErrorMsg": { - "message": "Nasa kasalukuyang listahan ng mga network na ang URL." - }, "use4ByteResolution": { "message": "I-decode ang mga smart na kontrata" }, @@ -6387,9 +6314,6 @@ "whatsThis": { "message": "Ano ito?" }, - "wrongChainId": { - "message": "Ang ID ng chain na ito ay hindi tugma sa pangalan ng network." - }, "wrongNetworkName": { "message": "Ayon sa aming mga talaan, ang pangalan ng network ay maaaring hindi tumugma nang tama sa ID ng chain na ito." }, @@ -6403,10 +6327,6 @@ "you": { "message": "Ikaw" }, - "youHaveAddedAll": { - "message": "Idinagdag mo ang lahat ng sikat na network. Maaari kang makatuklas ng higit pang mga network $1 O maaari kang", - "description": "$1 is a link with the text 'here' and $2 is a button with the text 'add more networks manually'" - }, "youNeedToAllowCameraAccess": { "message": "Kailangan mong payagan ang pag-access sa camera para magamit ang feature na ito." }, diff --git a/app/_locales/tr/messages.json b/app/_locales/tr/messages.json index 09ee23ea8d92..7537fd19b1fe 100644 --- a/app/_locales/tr/messages.json +++ b/app/_locales/tr/messages.json @@ -183,9 +183,6 @@ "addANetwork": { "message": "Ağ ekle" }, - "addANetworkManually": { - "message": "Manuel olarak bir ağ ekle" - }, "addANickname": { "message": "Takma ad ekle" }, @@ -252,9 +249,6 @@ "addFriendsAndAddresses": { "message": "Güvendiğiniz arkadaşlarınızı ve adresleri ekleyin" }, - "addFromAListOfPopularNetworks": { - "message": "Popüler ağlar listesinden ekle veya manuel olarak bir ağ ekle. Yalnızca güvendiğin varlıklarla etkileşim kur." - }, "addHardwareWallet": { "message": "Donanım cüzdanı ekleyin" }, @@ -267,16 +261,9 @@ "addMemo": { "message": "Not ekleyin" }, - "addMoreNetworks": { - "message": "manuel olarak daha fazla ağ ekleyin" - }, "addNetwork": { "message": "Ağ ekle" }, - "addNetworkTooltipWarning": { - "message": "Bu ağ bağlantısı üçüncü taraflara dayalıdır. Bu bağlantı daha az güvenli olabilir veya üçüncü tarafların aktiviteleri takip etmesine olanak sağlayabilir. $1", - "description": "$1 is Learn more link" - }, "addNewAccount": { "message": "Yeni bir Ethereum hesabı ekle" }, @@ -1000,9 +987,6 @@ "confirmConnectionTitle": { "message": "$1 ile bağlantıyı onayla" }, - "confirmDeletion": { - "message": "Silme işlemini onayla" - }, "confirmFieldPaymaster": { "message": "Ücreti ödeyen taraf" }, @@ -1015,9 +999,6 @@ "confirmRecoveryPhrase": { "message": "Gizli Kurtarma İfadesini Onayla" }, - "confirmRpcUrlDeletionMessage": { - "message": "RPC URL adresini silmek istediğinizden emin misiniz? Bilgileriniz bu ağ için kaydedilmeyecektir." - }, "confirmTitleDescPermitSignature": { "message": "Bu site token'lerinizi harcamak için izin istiyor." }, @@ -1368,9 +1349,6 @@ "custom": { "message": "Gelişmiş" }, - "customContentSearch": { - "message": "Önceden eklenmiş bir ağı ara" - }, "customGasSettingToolTipMessage": { "message": "Gaz fiyatını özelleştirmek için $1 kullanın. Bu, bilgi sahibi değilseniz kafa karıştırıcı olabilir. Riski size ait olmak üzere kullanın.", "description": "$1 is key 'advanced' (text: 'Advanced') separated here so that it can be passed in with bold font-weight" @@ -1490,9 +1468,6 @@ "deleteContact": { "message": "Kişiyi sil" }, - "deleteNetwork": { - "message": "Ağı Sil?" - }, "deleteNetworkIntro": { "message": "Bu ağı silerseniz bu ağdaki varlıklarınızı görüntülemek için bu ağı tekrar eklemeniz gerekecek" }, @@ -1500,9 +1475,6 @@ "message": "$1 ağını sil?", "description": "$1 represents the name of the network" }, - "deleteRpcUrl": { - "message": "RPC URL'sini sil" - }, "deposit": { "message": "Para Yatır" }, @@ -1867,9 +1839,6 @@ "existingRequestsBannerAlertDesc": { "message": "En son talebinizi görüntülemek ve onaylamak için önce mevcut taleplerinizi onaylamanız veya reddetmeniz gereklidir." }, - "existingRpcUrl": { - "message": "Bu URL adresi başka bir zincir kimliği ile ilişkilidir." - }, "expandView": { "message": "Görünümü genişlet" }, @@ -1901,9 +1870,6 @@ "failedToFetchChainId": { "message": "Zincir kimliği alınamadı. RPC URL adresiniz doğru mu?" }, - "failedToFetchTickerSymbolData": { - "message": "Ticker sembolü doğrulama verileri şu anda mevcut değil, girdiğin sembolün doğru olduğundan emin ol. Bu ağ için gördüğün dönüşüm oranlarını etkileyecektir" - }, "failureMessage": { "message": "Bir şeyler ters gitti ve işlemi tamamlayamadık" }, @@ -1924,9 +1890,6 @@ "message": "Dosya içe aktarma çalışmıyor mu? Buraya tıklayın!", "description": "Helps user import their account from a JSON file" }, - "findTheRightChainId": { - "message": "Doğrusunu şurada bulabilirsiniz:" - }, "flaskWelcomeUninstall": { "message": "bu uzantıyı kaldırmalısın", "description": "This request is shown on the Flask Welcome screen. It is intended for non-developers, and will be bolded." @@ -2416,9 +2379,6 @@ "invalidAssetType": { "message": "Bu varlık bir NFT'dir ve NFT'ler sekmesi altında bulunan NFT'leri İçe Aktar sayfasına yeniden eklenmesi gerekir" }, - "invalidBlockExplorerURL": { - "message": "Block Explorer URL adresi geçersiz" - }, "invalidChainIdTooBig": { "message": "Zincir kimliği geçersiz. Zincir kimliği çok büyük." }, @@ -2938,9 +2898,6 @@ "network": { "message": "Ağ:" }, - "networkAddedSuccessfully": { - "message": "Ağ başarılı bir şekilde eklendi!" - }, "networkDetails": { "message": "Ağ bilgileri" }, @@ -3001,9 +2958,6 @@ "networkProvider": { "message": "Ağ sağlayıcısı" }, - "networkSettingsChainIdDescription": { - "message": "İşlemlerin imzalanması için kullanılan zincir kimliği. Ağ tarafından yanıt olarak sunulan zincir kimliği ile uyumlu olmalıdır. Bir ondalık ya da \"0x\" ön ekli on altılı sayı girebilirsiniz ancak biz sayıyı ondalık olarak gösteririz." - }, "networkStatus": { "message": "Ağ durumu" }, @@ -3452,9 +3406,6 @@ "onboardingAdvancedPrivacyIPFSValid": { "message": "IPFS ağ geçidi URL adresi geçerli" }, - "onboardingAdvancedPrivacyNetworkButton": { - "message": "Özel ağ ekle" - }, "onboardingAdvancedPrivacyNetworkDescription": { "message": "Ethereum verilerine mümkün olan en güvenilir ve gizli erişimi sunmak amacıyla uzak yordam çağrısı (RPC) sağlayıcısı olarak Infura kullanırız. Kendi RPC'nizi seçebilirsiniz ancak tüm RPC'lerin işlemleri gerçekleştirmek için IP adresinizi ve Ethereum cüzdanınızı alacağını unutmayın. Infura'nın verileri nasıl kullandığı hakkında daha fazla bilgi edinmek için $1 bölümümüzü okuyun." }, @@ -3483,9 +3434,6 @@ "onboardingMetametricsInfuraTermsPolicy": { "message": "Gizlilik Politikası" }, - "onboardingMetametricsModalTitle": { - "message": "Özel ağ ekle" - }, "onboardingMetametricsNeverCollect": { "message": "$1 uygulama üzerindeki tıklamalar ve görüntülemeler depolanır ancak diğer bilgiler (genel adresiniz gibi) depolanmaz.", "description": "$1 represents `onboardingMetametricsNeverCollectEmphasis`" @@ -3578,9 +3526,6 @@ "onekey": { "message": "OneKey" }, - "onlyAddTrustedNetworks": { - "message": "Kötü amaçlı bir ağ sağlayıcı blokzinciri durumu hakkında yalan söyleyebilir ve ağ aktivitenizi kaydedebilir. Sadece güvendiğiniz özel ağları ekleyin." - }, "onlyConnectTrust": { "message": "Sadece güvendiğiniz sitelere bağlayın. $1", "description": "Text displayed above the buttons for connection confirmation. $1 is the link to the learn more web page." @@ -3605,9 +3550,6 @@ "optional": { "message": "İsteğe bağlı" }, - "optionalWithParanthesis": { - "message": "(İsteğe bağlı)" - }, "options": { "message": "Seçenekler" }, @@ -3959,9 +3901,6 @@ "message": "+ $1 tane daha", "description": "$1 is a number of additional but unshown items in a list- this message will be shown in place of those items" }, - "popularCustomNetworks": { - "message": "Popüler özel ağlar" - }, "popularNetworkAddToolTip": { "message": "Bu ağların bazıları üçüncü taraflara dayalıdır. Bağlantılar daha az güvenilir olabilir veya üçüncü tarafların aktiviteleri takip etmesine olanak sağlayabilir. $1", "description": "$1 is Learn more link" @@ -5298,9 +5237,6 @@ "suggestedTokenName": { "message": "Önerilen isim:" }, - "suggestedTokenSymbol": { - "message": "Önerilen ticker sembolü:" - }, "support": { "message": "Destek" }, @@ -5789,9 +5725,6 @@ "termsOfUseTitle": { "message": "Kullanım Şartları bölümümüz güncellendi" }, - "testNetworks": { - "message": "Test ağları" - }, "theme": { "message": "Tema" }, @@ -6103,9 +6036,6 @@ "message": "U2F", "description": "A name on an API for the browser to interact with devices that support the U2F protocol. On some browsers we use it to connect MetaMask to Ledger devices." }, - "unMatchedChain": { - "message": "Kayıtlarımıza göre bu URL adresi bu zincir kimliğinin bilinen bir sağlayıcısı ile uyumlu değil." - }, "unapproved": { "message": "Onaylanmadı" }, @@ -6175,9 +6105,6 @@ "urlErrorMsg": { "message": "URL adresleri için uygun HTTP/HTTPS ön eki gerekir." }, - "urlExistsErrorMsg": { - "message": "Bu URL şu anda $1 ağı tarafından kullanılıyor." - }, "use4ByteResolution": { "message": "Akıllı sözleşmelerin şifresini çöz" }, @@ -6387,9 +6314,6 @@ "whatsThis": { "message": "Bu nedir?" }, - "wrongChainId": { - "message": "Bu zincir kimliği ağ adı ile uyumlu değil." - }, "wrongNetworkName": { "message": "Kayıtlarımıza göre ağ adı bu zincir kimliği ile doğru şekilde eşleşmiyor olabilir." }, @@ -6403,10 +6327,6 @@ "you": { "message": "Siz" }, - "youHaveAddedAll": { - "message": "Tüm popüler ağları eklediniz. $1 daha fazla ağ gekşefedebilir veya $2 seçeneğini seçebilirsiniz", - "description": "$1 is a link with the text 'here' and $2 is a button with the text 'add more networks manually'" - }, "youNeedToAllowCameraAccess": { "message": "Bu özelliği kullanmak için kamera erişimine izin vermeniz gerekir." }, diff --git a/app/_locales/uk/messages.json b/app/_locales/uk/messages.json index 48d559bbdb40..37bab506a87d 100644 --- a/app/_locales/uk/messages.json +++ b/app/_locales/uk/messages.json @@ -205,9 +205,6 @@ "delete": { "message": "Видалити" }, - "deleteNetwork": { - "message": "Видалити мережу?" - }, "details": { "message": "Деталі" }, @@ -351,9 +348,6 @@ "invalidAddressRecipient": { "message": "Недійсна адреса отримувача" }, - "invalidBlockExplorerURL": { - "message": "Недійсний Block Explorer URL" - }, "invalidRPC": { "message": "Недійсний RPC URL" }, diff --git a/app/_locales/vi/messages.json b/app/_locales/vi/messages.json index b1fa5514980e..81e8c008a46b 100644 --- a/app/_locales/vi/messages.json +++ b/app/_locales/vi/messages.json @@ -183,9 +183,6 @@ "addANetwork": { "message": "Thêm mạng" }, - "addANetworkManually": { - "message": "Thêm mạng theo cách thủ công" - }, "addANickname": { "message": "Thêm biệt danh" }, @@ -252,9 +249,6 @@ "addFriendsAndAddresses": { "message": "Thêm bạn bè và địa chỉ mà bạn tin tưởng" }, - "addFromAListOfPopularNetworks": { - "message": "Thêm từ danh sách các mạng phổ biến hoặc thêm mạng theo cách thủ công. Chỉ tương tác với các đối tượng mà bạn tin tưởng." - }, "addHardwareWallet": { "message": "Thêm ví cứng" }, @@ -267,16 +261,9 @@ "addMemo": { "message": "Thêm bản ghi nhớ" }, - "addMoreNetworks": { - "message": "thêm thủ công các mạng khác" - }, "addNetwork": { "message": "Thêm mạng" }, - "addNetworkTooltipWarning": { - "message": "Kết nối mạng này dựa vào các bên thứ ba. Kết nối này có thể kém tin cậy hơn hoặc cho phép các bên thứ ba theo dõi hoạt động. $1", - "description": "$1 is Learn more link" - }, "addNewAccount": { "message": "Thêm tài khoản Ethereum mới" }, @@ -1000,9 +987,6 @@ "confirmConnectionTitle": { "message": "Xác nhận kết nối với $1" }, - "confirmDeletion": { - "message": "Xác nhận xóa" - }, "confirmFieldPaymaster": { "message": "Phí được thanh toán bởi" }, @@ -1015,9 +999,6 @@ "confirmRecoveryPhrase": { "message": "Xác nhận Cụm từ khôi phục bí mật" }, - "confirmRpcUrlDeletionMessage": { - "message": "Bạn có chắc chắn muốn xóa URL RPC? Thông tin của bạn sẽ không được lưu cho mạng này." - }, "confirmTitleDescPermitSignature": { "message": "Trang web này muốn được cấp quyền để chi tiêu số token của bạn." }, @@ -1368,9 +1349,6 @@ "custom": { "message": "Nâng cao" }, - "customContentSearch": { - "message": "Tìm kiếm mạng đã thêm trước đây" - }, "customGasSettingToolTipMessage": { "message": "Sử dụng $1 để tùy chỉnh giá gas. Việc này có thể gây nhầm lẫn nếu bạn không quen thuộc. Bạn phải tự chịu trách nhiệm nếu thực hiện.", "description": "$1 is key 'advanced' (text: 'Advanced') separated here so that it can be passed in with bold font-weight" @@ -1490,9 +1468,6 @@ "deleteContact": { "message": "Xóa địa chỉ liên hệ" }, - "deleteNetwork": { - "message": "Xóa mạng?" - }, "deleteNetworkIntro": { "message": "Nếu xóa mạng này, bạn sẽ cần thêm lại mạng này để xem các tài sản của mình trong mạng này" }, @@ -1500,9 +1475,6 @@ "message": "Xóa mạng $1?", "description": "$1 represents the name of the network" }, - "deleteRpcUrl": { - "message": "Xóa URL RPC" - }, "deposit": { "message": "Nạp" }, @@ -1867,9 +1839,6 @@ "existingRequestsBannerAlertDesc": { "message": "Để xem và xác nhận yêu cầu gần đây nhất của bạn, trước tiên bạn cần phải chấp thuận hoặc từ chối các yêu cầu hiện có." }, - "existingRpcUrl": { - "message": "URL này được liên kết với một ID chuỗi khác." - }, "expandView": { "message": "Chế độ xem mở rộng" }, @@ -1901,9 +1870,6 @@ "failedToFetchChainId": { "message": "Không thể tìm nạp ID chuỗi. URL RPC của bạn có chính xác không?" }, - "failedToFetchTickerSymbolData": { - "message": "Dữ liệu xác minh ký hiệu mã hiện không khả dụng, hãy chắc chắn bạn đã nhập đúng ký hiệu. Điều này sẽ ảnh hưởng đến tỷ giá chuyển đổi mà bạn nhìn thấy trong mạng này" - }, "failureMessage": { "message": "Đã xảy ra sự cố và chúng tôi không thể hoàn tất hành động" }, @@ -1924,9 +1890,6 @@ "message": "Tính năng nhập tập tin không hoạt động? Nhấp vào đây!", "description": "Helps user import their account from a JSON file" }, - "findTheRightChainId": { - "message": "Tìm ID chuỗi đúng trên:" - }, "flaskWelcomeUninstall": { "message": "bạn nên gỡ cài đặt tiện ích mở rộng này", "description": "This request is shown on the Flask Welcome screen. It is intended for non-developers, and will be bolded." @@ -2416,9 +2379,6 @@ "invalidAssetType": { "message": "Tài sản này là một NFT và cần được thêm lại trên trang Nhập NFT bên dưới thẻ NFT" }, - "invalidBlockExplorerURL": { - "message": "URL trình khám phá khối không hợp lệ" - }, "invalidChainIdTooBig": { "message": "ID chuỗi không hợp lệ. ID chuỗi quá lớn." }, @@ -2938,9 +2898,6 @@ "network": { "message": "Mạng:" }, - "networkAddedSuccessfully": { - "message": "Đã thêm mạng thành công!" - }, "networkDetails": { "message": "Thông tin về mạng" }, @@ -3001,9 +2958,6 @@ "networkProvider": { "message": "Nhà cung cấp mạng" }, - "networkSettingsChainIdDescription": { - "message": "ID chuỗi được dùng để ký các giao dịch. Giá trị này phải khớp với ID chuỗi do mạng trả về. Bạn có thể nhập một số thập phân hoặc số thập lục phân bắt đầu bằng “0x” nhưng chúng tôi sẽ hiển thị số ở dạng thập phân." - }, "networkStatus": { "message": "Trạng thái mạng" }, @@ -3452,9 +3406,6 @@ "onboardingAdvancedPrivacyIPFSValid": { "message": "URL cổng IPFS hợp lệ" }, - "onboardingAdvancedPrivacyNetworkButton": { - "message": "Thêm mạng tùy chỉnh" - }, "onboardingAdvancedPrivacyNetworkDescription": { "message": "Chúng tôi sử dụng Infura làm nhà cung cấp dịch vụ gọi hàm từ xa (RPC) để cung cấp quyền truy cập riêng tư và đáng tin cậy nhất vào dữ liệu Ethereum mà chúng tôi có thể. Bạn có thể chọn RPC của riêng mình, nhưng hãy nhớ rằng bất kỳ RPC nào cũng sẽ thu thập địa chỉ IP và ví Ethereum của bạn để thực hiện giao dịch. Đọc $1 của chúng tôi để tìm hiểu thêm về cách Infura xử lý dữ liệu." }, @@ -3483,9 +3434,6 @@ "onboardingMetametricsInfuraTermsPolicy": { "message": "Chính sách quyền riêng tư" }, - "onboardingMetametricsModalTitle": { - "message": "Thêm mạng tùy chỉnh" - }, "onboardingMetametricsNeverCollect": { "message": "$1 chỉ lưu trữ các lượt nhấn và lượt xem trên ứng dụng, các thông tin khác (chẳng hạn như địa chỉ công khai của bạn) sẽ không được lưu trữ.", "description": "$1 represents `onboardingMetametricsNeverCollectEmphasis`" @@ -3578,9 +3526,6 @@ "onekey": { "message": "OneKey" }, - "onlyAddTrustedNetworks": { - "message": "Một nhà cung cấp mạng độc hại có thể nói dối về trạng thái của chuỗi khối và ghi lại hoạt động của bạn trên mạng. Chỉ thêm các mạng tùy chỉnh mà bạn tin tưởng." - }, "onlyConnectTrust": { "message": "Chỉ kết nối với các trang web mà bạn tin tưởng. $1", "description": "Text displayed above the buttons for connection confirmation. $1 is the link to the learn more web page." @@ -3605,9 +3550,6 @@ "optional": { "message": "Không bắt buộc" }, - "optionalWithParanthesis": { - "message": "(Không bắt buộc)" - }, "options": { "message": "Tùy chọn" }, @@ -3959,9 +3901,6 @@ "message": "+ $1 khác", "description": "$1 is a number of additional but unshown items in a list- this message will be shown in place of those items" }, - "popularCustomNetworks": { - "message": "Mạng tùy chỉnh phổ biến" - }, "popularNetworkAddToolTip": { "message": "Một vài mạng trong số này phụ thuộc vào bên thứ ba. Kết nối có thể kém tin cậy hơn hoặc cho phép bên thứ ba theo dõi hoạt động. $1", "description": "$1 is Learn more link" @@ -5298,9 +5237,6 @@ "suggestedTokenName": { "message": "Tên đề xuất:" }, - "suggestedTokenSymbol": { - "message": "Mã chứng khoán được đề xuất:" - }, "support": { "message": "Hỗ trợ" }, @@ -5789,9 +5725,6 @@ "termsOfUseTitle": { "message": "Điều khoản sử dụng của chúng tôi đã được cập nhật" }, - "testNetworks": { - "message": "Mạng thử nghiệm" - }, "theme": { "message": "Chủ đề" }, @@ -6103,9 +6036,6 @@ "message": "U2F", "description": "A name on an API for the browser to interact with devices that support the U2F protocol. On some browsers we use it to connect MetaMask to Ledger devices." }, - "unMatchedChain": { - "message": "Theo hồ sơ của chúng tôi, URL này không khớp với nhà cung cấp đã biết cho ID chuỗi này." - }, "unapproved": { "message": "Chưa chấp thuận" }, @@ -6175,9 +6105,6 @@ "urlErrorMsg": { "message": "URL phải có tiền tố HTTP/HTTPS phù hợp." }, - "urlExistsErrorMsg": { - "message": "Mạng $1 hiện đang sử dụng URL này." - }, "use4ByteResolution": { "message": "Giải mã hợp đồng thông minh" }, @@ -6387,9 +6314,6 @@ "whatsThis": { "message": "Đây là gì?" }, - "wrongChainId": { - "message": "ID chuỗi này không khớp với tên mạng." - }, "wrongNetworkName": { "message": "Theo hồ sơ của chúng tôi, tên mạng có thể không khớp chính xác với ID chuỗi này." }, @@ -6403,10 +6327,6 @@ "you": { "message": "Bạn" }, - "youHaveAddedAll": { - "message": "Bạn đã thêm tất cả các mạng phổ biến. Bạn có thể khám phá thêm nhiều mạng khác $1 Hoặc bạn có thể $2", - "description": "$1 is a link with the text 'here' and $2 is a button with the text 'add more networks manually'" - }, "youNeedToAllowCameraAccess": { "message": "Bạn cần cho phép truy cập vào máy ảnh để sử dụng tính năng này." }, diff --git a/app/_locales/zh_CN/messages.json b/app/_locales/zh_CN/messages.json index 8733daf9431c..5b556663ac31 100644 --- a/app/_locales/zh_CN/messages.json +++ b/app/_locales/zh_CN/messages.json @@ -183,9 +183,6 @@ "addANetwork": { "message": "添加网络" }, - "addANetworkManually": { - "message": "手动添加网络" - }, "addANickname": { "message": "添加昵称" }, @@ -252,9 +249,6 @@ "addFriendsAndAddresses": { "message": "添加您信任的朋友和地址" }, - "addFromAListOfPopularNetworks": { - "message": "从热门网络列表中选择网络来添加,或手动添加网络。仅可与您信任的实体互动。" - }, "addHardwareWallet": { "message": "添加硬件钱包" }, @@ -267,16 +261,9 @@ "addMemo": { "message": "添加备忘录" }, - "addMoreNetworks": { - "message": "手动添加更多网络" - }, "addNetwork": { "message": "添加网络" }, - "addNetworkTooltipWarning": { - "message": "此网络连接依赖于第三方。此连接可能不太可靠,或使第三方可进行活动跟踪。$1", - "description": "$1 is Learn more link" - }, "addNewAccount": { "message": "添加新账户" }, @@ -1000,9 +987,6 @@ "confirmConnectionTitle": { "message": "确认连接到$1" }, - "confirmDeletion": { - "message": "确认删除" - }, "confirmFieldPaymaster": { "message": "费用支付方" }, @@ -1015,9 +999,6 @@ "confirmRecoveryPhrase": { "message": "确认私钥助记词" }, - "confirmRpcUrlDeletionMessage": { - "message": "您确定要删除 RPC(远程过程调用)URL 吗?您的信息将不会保存到此网络。" - }, "confirmTitleDescPermitSignature": { "message": "此网站需要获得许可来使用您的代币。" }, @@ -1368,9 +1349,6 @@ "custom": { "message": "高级" }, - "customContentSearch": { - "message": "搜索以前添加的网络" - }, "customGasSettingToolTipMessage": { "message": "使用$1来定制燃料价格。如果您不熟悉这可能会引起混淆。操作风险自付。", "description": "$1 is key 'advanced' (text: 'Advanced') separated here so that it can be passed in with bold font-weight" @@ -1490,9 +1468,6 @@ "deleteContact": { "message": "删除联系人" }, - "deleteNetwork": { - "message": "删除网络?" - }, "deleteNetworkIntro": { "message": "如果您删除此网络,则需要再次添加此网络才能查看您在其中的资产" }, @@ -1500,9 +1475,6 @@ "message": "要删除$1网络吗?", "description": "$1 represents the name of the network" }, - "deleteRpcUrl": { - "message": "删除 RPC(远程过程调用)URL" - }, "deposit": { "message": "保证金" }, @@ -1867,9 +1839,6 @@ "existingRequestsBannerAlertDesc": { "message": "如需查看和确认您的最新请求,您需要首先批准或拒绝现有请求。" }, - "existingRpcUrl": { - "message": "此 URL 与另一个链 ID 相关联。" - }, "expandView": { "message": "展开视图" }, @@ -1901,9 +1870,6 @@ "failedToFetchChainId": { "message": "无法获取链 ID。您的 RPC URL 正确吗?" }, - "failedToFetchTickerSymbolData": { - "message": "股票代码验证数据当前未能提供,请确保您输入的代码正确无误。这会影响您在此网络看到的兑换率" - }, "failureMessage": { "message": "出了点问题,我们无法完成此操作" }, @@ -1924,9 +1890,6 @@ "message": "文件导入失败?点击这里!", "description": "Helps user import their account from a JSON file" }, - "findTheRightChainId": { - "message": "在下方找到合适的:" - }, "flaskWelcomeUninstall": { "message": "您应该卸载此扩展程序", "description": "This request is shown on the Flask Welcome screen. It is intended for non-developers, and will be bolded." @@ -2416,9 +2379,6 @@ "invalidAssetType": { "message": "该资产是NFT,需要在NFT选项卡下的“导入NFT”页面上重新添加" }, - "invalidBlockExplorerURL": { - "message": "无效的区块浏览器 URL" - }, "invalidChainIdTooBig": { "message": "无效的链 ID,链 ID 过大。" }, @@ -2938,9 +2898,6 @@ "network": { "message": "网络: " }, - "networkAddedSuccessfully": { - "message": "网络添加成功!" - }, "networkDetails": { "message": "网络详情" }, @@ -3001,9 +2958,6 @@ "networkProvider": { "message": "网络提供商" }, - "networkSettingsChainIdDescription": { - "message": "链 ID 用于签署交易。它必须与网络返回的链 ID 相匹配。您可以输入十进制或以'0x'开头的十六进制数字,但我们将以十进制显示该数字。" - }, "networkStatus": { "message": "网络状态" }, @@ -3452,9 +3406,6 @@ "onboardingAdvancedPrivacyIPFSValid": { "message": "IPFS 网关 URL 有效" }, - "onboardingAdvancedPrivacyNetworkButton": { - "message": "添加自定义网络" - }, "onboardingAdvancedPrivacyNetworkDescription": { "message": "我们使用 Infura 作为我们的远程过程调用(RPC)提供商,以提供最可靠和最私密的以太坊数据访问。您可以选择自己的 RPC,但请谨记,任何 RPC 都可以接收您的 IP 地址和以太坊钱包以进行交易。请阅读我们的 $1,进一步了解 Infura 如何处理数据。" }, @@ -3483,9 +3434,6 @@ "onboardingMetametricsInfuraTermsPolicy": { "message": "隐私政策" }, - "onboardingMetametricsModalTitle": { - "message": "添加自定义网络" - }, "onboardingMetametricsNeverCollect": { "message": "$1 会存储点击量和应用程序的浏览量,但不会存储其他详情(如您的公钥)。", "description": "$1 represents `onboardingMetametricsNeverCollectEmphasis`" @@ -3578,9 +3526,6 @@ "onekey": { "message": "OneKey" }, - "onlyAddTrustedNetworks": { - "message": "恶意网络提供商可能会谎报区块链的状态并记录您的网络活动。只添加您信任的自定义网络。" - }, "onlyConnectTrust": { "message": "仅连接您信任的网站。$1", "description": "Text displayed above the buttons for connection confirmation. $1 is the link to the learn more web page." @@ -3605,9 +3550,6 @@ "optional": { "message": "可选" }, - "optionalWithParanthesis": { - "message": "(可选)" - }, "options": { "message": "期权" }, @@ -3959,9 +3901,6 @@ "message": "另外 $1 项", "description": "$1 is a number of additional but unshown items in a list- this message will be shown in place of those items" }, - "popularCustomNetworks": { - "message": "流行自定义网络" - }, "popularNetworkAddToolTip": { "message": "这些网络中的其中一些依赖于第三方。此连接可能不太可靠,或使第三方可进行活动跟踪。$1", "description": "$1 is Learn more link" @@ -5298,9 +5237,6 @@ "suggestedTokenName": { "message": "建议名称:" }, - "suggestedTokenSymbol": { - "message": "建议的股票代码:" - }, "support": { "message": "获取帮助" }, @@ -5789,9 +5725,6 @@ "termsOfUseTitle": { "message": "我们的使用条款已更新" }, - "testNetworks": { - "message": "测试网络" - }, "theme": { "message": "主题" }, @@ -6103,9 +6036,6 @@ "message": "U2F", "description": "A name on an API for the browser to interact with devices that support the U2F protocol. On some browsers we use it to connect MetaMask to Ledger devices." }, - "unMatchedChain": { - "message": "根据我们的记录,此 URL 与此链 ID 的已知提供者不匹配。" - }, "unapproved": { "message": "未批准" }, @@ -6175,9 +6105,6 @@ "urlErrorMsg": { "message": "URL 需要相应的 HTTP/HTTPS 前缀。" }, - "urlExistsErrorMsg": { - "message": "此 URL 目前已被 $1 网络使用。" - }, "use4ByteResolution": { "message": "对智能合约进行解码" }, @@ -6387,9 +6314,6 @@ "whatsThis": { "message": "这是什么?" }, - "wrongChainId": { - "message": "此链 ID 与网络名称不匹配。" - }, "wrongNetworkName": { "message": "根据我们的记录,该网络名称可能与此链 ID 不匹配。" }, @@ -6403,10 +6327,6 @@ "you": { "message": "您" }, - "youHaveAddedAll": { - "message": "您已经添加了所有热门网络。您可以探索更多网络$1,或者您可以$2", - "description": "$1 is a link with the text 'here' and $2 is a button with the text 'add more networks manually'" - }, "youNeedToAllowCameraAccess": { "message": "需要开启相机访问权限,才能使用该功能。" }, diff --git a/app/_locales/zh_TW/messages.json b/app/_locales/zh_TW/messages.json index 04d3b772da22..0924d284b529 100644 --- a/app/_locales/zh_TW/messages.json +++ b/app/_locales/zh_TW/messages.json @@ -367,9 +367,6 @@ "delete": { "message": "刪除" }, - "deleteNetwork": { - "message": "刪除網路?" - }, "details": { "message": "詳情" }, @@ -669,9 +666,6 @@ "invalidAddressRecipient": { "message": "接收位址錯誤" }, - "invalidBlockExplorerURL": { - "message": "無效的區塊鏈瀏覽器 URL" - }, "invalidChainIdTooBig": { "message": "無效的鏈 ID。鏈 ID 數值過大。" }, @@ -822,9 +816,6 @@ "networkNameTestnet": { "message": "測試網路" }, - "networkSettingsChainIdDescription": { - "message": "鏈 ID 用來簽署交易。它必須和該網路回傳的鏈 ID 一致。您可以輸入一個十進位數值或 0x 開頭的十六進位數值,但我們會顯示成十進位。" - }, "networkURL": { "message": "網路 URL" }, @@ -908,9 +899,6 @@ "onboardingPinExtensionBillboardAccess": { "message": "Full Access" }, - "onlyAddTrustedNetworks": { - "message": "惡意的網路提供者可以欺騙您區塊鏈上的狀態或記錄您的網路活動。請只新增您信任的自訂網路。" - }, "onlyConnectTrust": { "message": "記住,只連線到您信任的網站。", "description": "Text displayed above the buttons for connection confirmation. $1 is the link to the learn more web page." @@ -1398,9 +1386,6 @@ "urlErrorMsg": { "message": "URL 需要以適當的 HTTP/HTTPS 作為開頭" }, - "urlExistsErrorMsg": { - "message": "URL 已經在既有的網路列表裡了" - }, "usePhishingDetection": { "message": "使用網路釣魚偵測" }, diff --git a/app/images/networks1.png b/app/images/networks1.png new file mode 100644 index 0000000000000000000000000000000000000000..780470e87810c91b5e531e635e89229bf8406aae GIT binary patch literal 5923 zcma)=Ra+YjkcH9W6e#Xi+#QM(D-tvWcXzkq5VW{!D8+)i2X~j^E+0?`PSFB|?LN=` zfW0_#X69nf-CVriG}IJuFv&3y5D;*b6y<>b-1eXB(BJ*5XCxOK{|v)TQQs2*0qev6 z3K1a}Li%rs=m}JiL8zUfg#Q~*Y^7DD5fB=ZuwKkj5fF&mmE@#BUlGrZu+ojS{a)|= zwnsMuj%HMBWSAHkFzAxyP(Ijy){rAtM&-=lFk?M702&x_pb}bhZdB=*(;3A5kTGwl zE+T3{av-$gL^;q-OD6ptEOnIPx3l7R-u-+Tp5KuqYJ1=Iw({_1 z8;XKKz^Ir^D-HhtH2WCp5+7@(CxtqJ%2C1?Ow-(zB!5fyC^uE|Y`^e332zPJEPtTS zhC{2V$K%P~>FJ=0DqfmQq8MUq=m|<1?bL6XuC%%B4dA)*sE~U+`U(YaJq)l-mLA@9r{E6q!D-%&lhShq>es&a4|raQZD`2SKdvFfHFXB@cRQ~X zXxqdqQPTBvHD1O}t5^RS4l3wz!yd&;i-P=KF*V7f#!|D4MKtg-07bCU@_kepMdjzb z66m%`+gc@#D9pCNGXh5=#ye0&=^`fuCVMN9UGF)R!n}_@zb)K3J$V8yx?uhyE7!X| zIc&1k1~yR zFsDXpWJ*br@}*Oj_wVO4BTeHp>ad8HzkbSU%CVdoG`f4ETa#RuSn@k%~S640a9~(pn%fx?Fb7uzF3vGZ zCu}$PgVefQVU*0bk~!o35u1UJHa9xm5f**fR~d>EL`g44cwyTfskuioL%vVM1Rn_9 ziU%b{7Ie-5tuLDc*R=eNfjyfWy$U#ZW=TiLUXQYD%YnC@a?OZyV$VZ6xbXLn2aEKq zMd4wQ4^$>hSsq#;%wy{y@yTZ2(2Yd>D*3kk|2;lfGCLjne$n1MFqV<7ktnRGU@Ox1 zC6H5%x{Y-#fba@gkz39WGPVZ+OEG)IFvc~CZbYbl1ItIZQa)`rb4;`zZVz@u1o%v@ z88yabu!HnzeK||SHZ*RAQfC8CcDM(5`$}lbV8_>Mf$%i7zu-~LoEwg>!Ke-%$YKG) z7KQrKg`d!DO_?8)W@R7GHZkw8S>U>pw8cO$K|B_2)NS?`|K zY0=^oGs=F<3BHF6q~R-Tz_4pmKLI)fJ)#d1>C^GAq$8~C5vwK!yl#3;PG3q}kU7Hy zxWWZ)T_lpTO3Kuwa}>);+%xw#vKF)+ERc9v$J}Y5q|MI#G7}>#_p9m7E&O73DZg}% z?LBN?dAd?`@vu`nPq>R<>~Rg!$d+n~Gj4W6H$13^B&yVdojL=$Zv{hTqYwKg;GzoflFsSJEph7lF|(J$29X3aO=znNIQ z`6b3w64I$K*B8c%@u{vpg1s%Tbo0v1nHqCAJl1lOLDl4-cJg;Air>J4)|ZaorLHLX z2GBh0y8y}oK63DIK!Dsw7A&<8$G30JkpUCouiU)-*_1BRnfWmsXXTK@yCu##a0h_~PH9n|}H>o@T6C{E!&2`kH?Zypv z`cN!^$shVTB_F1S%j3jtyw1NU9N|7vg)16&Az6$`udX1uhO)gaEsUYI3`qGAt*dLV zk&_Vs8|0}x6gkO&OeFeubD4Sc_JQzuoF(m~>1nQzmKzf6w}AbnP3D+i9h=EwWV^~P zHBV#fCB%56mK4EHluV>)GLN?km#e^YZ`tHdDuThU;`-`u=xWpGQ`y`1{aEV0A@LX^yls>p{l6S?w~@pJ zXgX~~`8Ahso(}E4a`@a^^E4J%3Y?5!Z9kh`<3loDG1h?$=!(f0fvNb4P@$H_&s;bS zOFToUka4}8d{4tR(>84rd&3tJZLOEvd6MVZ;4^jD)7zC8WDBm)MfP*yU}Z)ZYFS=P z(63@>_(e!8X_s_2JFUrO>;1VFY`oy71$yl5yAFnhi{WN_A!xp0Zf>*pvX@W*=`$PS zv{iZ(Yk3lb(sjdsrqZJn(3+?U9V9JS`IDFq+C~0wrfJW3=Y+u4TQ#Y(l>SRsm)UsI zz438^p>Q-rB{3(Ul3QCyANhXd*m?O6ojz2)1!Ub0=G@QeeEV}rHp<(_%WS*dRVwYh zp({l(wwwg3a7c1|$~3U!QO_Z&X_}ZvS?BMz;yqz)#G7CEI)oRIZkR`wUrH8Fh)CoK zG7ZB}O?EbT#)TY3djfl5RyA*hW9T$Fe4zy^mCF`NQkvP~J{J%HK?gQc=i61wzG9Fo zvCC9RF_CwOo8xHwe{L0v`=j>eH$3*a&XZYh8C%vf7y3IryG&_h zEBWJDmtz#NTT{5sBSl}56oy>lqDjwdEVgp#Cp@?qS?8>X%o0m)1(rn4nZGa34TL;_ z<0QL&&)pdP`RX-`(K|f45JdQhvt+LwM|;PYMjuiB@w0aWK%xk}r7UXhkcju-QkPoTl_A~LmHi1Jmn0KB^d3~RIhZMI)(`67j&go@=nn8=vx~;5+gsgnW_VeuO z0P7K#V)gFpam{wv`#1;6%XFgsD2iFh&R~XmwCh47zKzFK)pKL!l35;{XUOmR>?}1R z1**2IcCWPwD#3uG`N_=JpQ{>dN^#NKRD3);02Hn4lyw}3mBL^6b5~IZ+aA_mh>JH% zB0qnzs>dgSg2f1Q1|3sfxF+@5kF8$&WbMeAh(reqA2-%XDLiD~(CU6SiXYC`B@@P) z3MLLlv9Sa8`$;4=I+lw{1~GH^2=eDEn5s@_;EVbaW!R}xb>SRizKnr)Vt0}A>2_Dd zKt$Gg(8J{rYg3cmrJ}H)X12gVS3vREsxXt31`UyvUWki}l!?NokL9_g5v<_9iLeUB zaDLTzCfRAmADSy;tGP(?=UvL5-KV08(~$(P(>&7c5|jnNpjHxoTS&P9A%1R*lC0Ve zsidSvmZ;=$2v!|KkNbw5dzZx@nD(`knWedAyHU z=8%^hHvL_EM(g)We>dFL!Pwq<6)8tBB-MZ4xy*S@Blw}deVqHowrH}6{aTDZgS$Ch zfq|Lfzn-GS8Z8Ma8RM1qk2TGrh2e?RPjnd&j`*0E#efWKxgK?8EZJw@AH_9M#K1mh zhwu&fqIrTL6(W8*x-=sF46zi6G(#7HWPjCuN+BAdB@^M1>QMkoGA6R?qs z(Q?J7p`V9eHm`D$8mq}cKf`jt6Q2-3y48u=1amIQh?SNoehov6mv2GJjlp0(+TzAl zoh@xF(-hkrq=eWw4U%PePN!j^h9lwbM2~&>tV`rKu`}A8hfR4XnHURUtwBeNVNjEU z7_LUj153{;Us(&7NP4kY#ei6)KF8w zyi5CGXMLLBWfQm*2#S7Iu0IA8+SR&%403S~Rpf3|C(O(m%?)0|v z6*EftOj1;4Q$qf?B!9zalEzY zR4~cIj&8{vwLBYDQH?bUmGR&)3vSU-L@?nd)mJ9~ZqAf|F`9cgf%_*J*BVU0*KB9H zBgb$13m&oWC&8nYD;|@*tg)LFsXjCe02Y6cy#jCrm&s}~gl=aT&Qk%^%5ky#wiLzc zQsuIKH`bAnRV=^ca6#g>oxLXN&gfwd75GwHygp- z^V#030bRJ3=@5Pq$l`Ya^)8QtXfe1X}7M6P2I@-9RdU8{xs{NbbS=_>pt^ zBDx_!C6_*X=|atc365Z0W+b2%go4zP8=Vq_ctV65jX8g^D)MEBXFIht(_5&NS&5Z9 z)KP!5!0=N|$kt>hmaWTo6H5-}N1=t?4dkqIm`Hb{Ek?kvzYC9*Gf%autDOsTEfM6E zKb#O!#{UcR7Vv)r+j}sgS`%xNNNBT_&TwalQ_^u5E^7yQ+C?AyIId9A(r*UhMG#Bn z6Xx8WVGm1q^vmTfsb|ba(Ruv}@=xq3E9s;6aJEX4^3X{q1~$=nz9Zj>e%NtO6!4dt zUEO}@D_M5ZPLAUeKCBD|Vl&zrB-ZPQClMQ`S$>liz@MI4&VHGxxx|sb^L%Gw zGNlntP6=ufQZDi}5}Y&&yS+^i@V8%dF07_29!d}_#LzvwIOGJCAeMGAD--WDNVPy$@r{*U8sixaywk#Zi@87^{%_ zI@pP_^Nhm32)(m3maFS36!d4d9{hNEeR>^>PF6MOzjHE9*5cE;UUsGEKnAqTR=ZL- zr4nTMO=9?xW;G(hy<*W5hjcH>K-sh)r1Td)f+QSc9zB-f@0Psx5F>gkcz%D2k*=Nd z^qq51gH`H6uaR+Qv2#23OQNg6Cr-g<4%~P)x>oFN?fo!!g`IfElMN9W`=Sy4#m+X( zk8rb9HhLA`qVE?Y?Hf2vp6{xi9?$@#02kJbnWS<*o4~eCNls6&r;V7QVPJpdaS$h2 zK(JU$a%C*B-yKqUIrk<#Lj5aD7sz@&ti5W8 zla~F=7eQ5;cI2Xw?{_-FLmno0*P)T&VDY%|Wbx=d+9cKH52ky5@vHLlFzA4yx^%W< zbb)iJ7`?*ZAgxk^TMYKrim~qvyarzZqthl2680S;gJ&Ha7OeOs(dW5Fw$ZQWa7FR0 z`CQ|zC!Qz3M}X^7r^#*zF@I=yF>F1e#GOa}ac7b8xMzICEkLERKPLv9LQ!V``;o|0 zY=6LFc}>k%z$<@95Psl0GTuE#=|2j!=r#&{t+@PrxhUkc(MaK~-hmMZu4YryYN#y@ zj)Cdx;+U6%bQ3l!M`8(0e?*@9BKNRq4L^*}dPEC-(GhYa;3J+V|sbK)x(0`PEKb7kUoS$CxmV6xXX|ZQ+YVdE_bn= z(0>)T-Eb_5ee!O?EOvm6(d}d2PPd<|lnIu|H<7P_Hr0R5#3;tvwb)p{ELHt|`ScK& zN#(AecA5luJxRwD=^) zG%mshSlEjdetL1=Y}6CDiMr=K;$NJQ0t2Ms;VN5u(8L0ouNSq}d^^IVQ$KLw6$i4j zco6O%298`EKkXM|CRucDC5oQJRKA)#W>ECFZTqO;=J z)Laz)w1xgXYIiXL2)Hn7X@&~tnnd;WPoO=yD(F>m+1JGErNMJnx-8FIuD->siAZ$K ziv*W{?|j%kyoY)`bEHi7ELq$m;W~ymFkq1Z@crYhoCP>^6XRU4wI~nrCQ7!^gj!HbhdWk@t&Q2%Y*0+dauAg z)d>qEM!RGGyru9-K;Q<8B~)&(C_Nq&E_VBsdj6Erx|vwwl9edKwU$3gPI!nJ+3!CB zVTtn14(B1LGLKHL_G;+#%X$YC5D=GschHQh-OMOeNCNq4PD_nO$FR(})iRqzI4~@% z@G-)_V`}o348EV~I(@R{+TOMEZ>sq(=`~7zxG_dA(SQNeSE0rnC(nrmYqHa_=o{@I zmRdTejM4}xl>LiHUr6Cb#{aqSV?rW?4%qlaul=~wbq^qU{u*8kKY;X3Kx&;^swxw||Iz{K_LWHDWW039)D_F?t`YUMzz{b@92cu`WADR7=SWFd_Qx}O zxVb{T1pdh>!=xeNtij<@w_us6Pym9fg$lZ8jq$y!y!KcifOPtdFQBY!m1%{zu0FI* zXxeP)wZ8H68wm|5XNIyWm8--y51otZo)mQz7aMG3_qM?~;D|Gy+@9p&> ZqRf+lv7bhf{xNL?C3!WuS{XC&{{TXuTB-m5 literal 0 HcmV?d00001 diff --git a/app/scripts/controllers/metametrics.js b/app/scripts/controllers/metametrics.js index e7618decb3ba..5ce36871262d 100644 --- a/app/scripts/controllers/metametrics.js +++ b/app/scripts/controllers/metametrics.js @@ -815,12 +815,12 @@ export default class MetaMetricsController { [MetaMetricsUserTrait.LedgerConnectionType]: metamaskState.ledgerTransportType, [MetaMetricsUserTrait.NetworksAdded]: Object.values( - metamaskState.networkConfigurations, + metamaskState.networkConfigurationsByChainId, ).map((networkConfiguration) => networkConfiguration.chainId), [MetaMetricsUserTrait.NetworksWithoutTicker]: Object.values( - metamaskState.networkConfigurations, + metamaskState.networkConfigurationsByChainId, ) - .filter(({ ticker }) => !ticker) + .filter(({ nativeCurrency }) => !nativeCurrency) .map(({ chainId }) => chainId), [MetaMetricsUserTrait.NftAutodetectionEnabled]: metamaskState.useNftDetection, diff --git a/app/scripts/controllers/mmi-controller.ts b/app/scripts/controllers/mmi-controller.ts index 7cc024d0cd47..6351de5b7239 100644 --- a/app/scripts/controllers/mmi-controller.ts +++ b/app/scripts/controllers/mmi-controller.ts @@ -26,8 +26,8 @@ import { } from '@metamask/message-manager'; import { NetworkController } from '@metamask/network-controller'; import { InternalAccount } from '@metamask/keyring-api'; +import { toHex } from '@metamask/controller-utils'; import { toChecksumHexAddress } from '../../../shared/modules/hexstring-utils'; -import { CHAIN_IDS } from '../../../shared/constants/network'; import { CONNECT_HARDWARE_ROUTE } from '../../../ui/helpers/constants/routes'; import { MMIControllerOptions, @@ -759,15 +759,9 @@ export default class MMIController extends EventEmitter { this.custodyController.getAccountDetails(address); const extensionId = this.extension.runtime.id; - const { networkConfigurations: networkConfigurationsById } = - this.networkController.state; - const networkConfigurations = Object.values(networkConfigurationsById); - - const networks = [ - ...networkConfigurations, - { chainId: CHAIN_IDS.MAINNET }, - { chainId: CHAIN_IDS.SEPOLIA }, - ]; + const networks = Object.values( + this.networkController.state.networkConfigurationsByChainId, + ); return handleMmiPortfolio({ keyringAccounts, @@ -853,26 +847,24 @@ export default class MMIController extends EventEmitter { internalAccount.id, ); } - const selectedChainId = parseInt( - getCurrentChainId({ metamask: this.networkController.state }), - 16, - ); - if (selectedChainId !== chainId && chainId === 1) { - await this.networkController.setActiveNetwork('mainnet'); - } else if (selectedChainId !== chainId) { - const { networkConfigurations } = this.networkController.state; - - const foundNetworkConfiguration = Object.values( - networkConfigurations, - ).find( - (networkConfiguration) => - parseInt(networkConfiguration.chainId, 16) === chainId, - ); - if (foundNetworkConfiguration !== undefined) { - await this.networkController.setActiveNetwork( - foundNetworkConfiguration.id, - ); + const selectedChainId = getCurrentChainId({ + metamask: this.networkController.state, + }); + + if (selectedChainId !== toHex(chainId)) { + const networkConfiguration = + this.networkController.state.networkConfigurationsByChainId[ + toHex(chainId) + ]; + + const { networkClientId } = + networkConfiguration?.rpcEndpoints?.[ + networkConfiguration.defaultRpcEndpointIndex + ] ?? {}; + + if (networkClientId) { + await this.networkController.setActiveNetwork(networkClientId); } } diff --git a/app/scripts/controllers/network-order.ts b/app/scripts/controllers/network-order.ts index 8bd8372d05c0..324678682120 100644 --- a/app/scripts/controllers/network-order.ts +++ b/app/scripts/controllers/network-order.ts @@ -6,19 +6,18 @@ import { NetworkControllerStateChangeEvent, NetworkState, } from '@metamask/network-controller'; +import { Hex } from '@metamask/utils'; import type { Patch } from 'immer'; -import { MAINNET_CHAINS } from '../../../shared/constants/network'; +import { TEST_CHAINS } from '../../../shared/constants/network'; // Unique name for the controller const controllerName = 'NetworkOrderController'; /** - * The network ID of a network. + * Information about an ordered network. */ - export type NetworksInfo = { - networkId: string; - networkRpcUrl: string; + networkId: Hex; // The network's chain id }; // State shape for NetworkOrderController @@ -109,51 +108,32 @@ export class NetworkOrderController extends BaseController< * Handles the state change of the network controller and updates the networks list. * * @param networkControllerState - The state of the network controller. + * @param networkControllerState.networkConfigurationsByChainId */ - onNetworkControllerStateChange(networkControllerState: NetworkState) { - // Extract network configurations from the state - const networkConfigurations = Object.values( - networkControllerState.networkConfigurations, - ); - - // Since networkConfigurations doesn't have default or mainnet network configurations we need to combine mainnet chains with network configurations - const combinedNetworks = [...MAINNET_CHAINS, ...networkConfigurations]; - - // Extract unique chainIds from the combined networks - const uniqueChainIds = combinedNetworks.map((item) => ({ - networkId: item.chainId, - networkRpcUrl: item.rpcUrl, - })); - - // Arrays to store reordered and new unique chainIds - let reorderedNetworks: NetworksInfo[] = []; - const newUniqueNetworks: NetworksInfo[] = []; - - // Iterate through uniqueChainIds to reorder existing elements - uniqueChainIds.forEach((newItem) => { - const existingIndex = this.state.orderedNetworkList.findIndex( - (item) => - item.networkId === newItem.networkId && - item.networkRpcUrl === newItem.networkRpcUrl, - ); - // eslint-disable-next-line no-negated-condition - if (existingIndex !== -1) { - // Reorder existing element - reorderedNetworks[existingIndex] = newItem; - } else { - // Add new unique element - newUniqueNetworks.push(newItem); - } - }); - - // Filter out null values and concatenate reordered and new unique networks - reorderedNetworks = reorderedNetworks - .filter((item) => Boolean(item)) - .concat(newUniqueNetworks); - - // Update the state with the new networks list + onNetworkControllerStateChange({ + networkConfigurationsByChainId, + }: NetworkState) { this.update((state) => { - state.orderedNetworkList = reorderedNetworks; + // Filter out testnets, which are in the state but not orderable + const chainIds = Object.keys(networkConfigurationsByChainId).filter( + (chainId) => + !TEST_CHAINS.includes(chainId as (typeof TEST_CHAINS)[number]), + ) as Hex[]; + + const newNetworks = chainIds + .filter( + (chainId) => + !state.orderedNetworkList.some( + ({ networkId }) => networkId === chainId, + ), + ) + .map((chainId) => ({ networkId: chainId })); + + state.orderedNetworkList = state.orderedNetworkList + // Filter out deleted networks + .filter(({ networkId }) => chainIds.includes(networkId)) + // Append new networks to the end + .concat(newNetworks); }); } @@ -163,10 +143,11 @@ export class NetworkOrderController extends BaseController< * @param networkList - The list of networks to update in the state. */ - updateNetworksList(networkList: []) { + updateNetworksList(chainIds: Hex[]) { this.update((state) => { - state.orderedNetworkList = networkList; - return state; + state.orderedNetworkList = chainIds.map((chainId) => ({ + networkId: chainId, + })); }); } } diff --git a/app/scripts/controllers/preferences-controller.test.ts b/app/scripts/controllers/preferences-controller.test.ts index d35a35b24f51..f825c1eb5aee 100644 --- a/app/scripts/controllers/preferences-controller.test.ts +++ b/app/scripts/controllers/preferences-controller.test.ts @@ -11,6 +11,7 @@ import { KeyringControllerAccountRemovedEvent, } from '@metamask/keyring-controller'; import { SnapControllerStateChangeEvent } from '@metamask/snaps-controllers'; +import { Hex } from '@metamask/utils'; import { CHAIN_IDS } from '../../../shared/constants/network'; import { mockNetworkState } from '../../../test/stub/networks'; import { ThemeType } from '../../../shared/constants/preferences'; @@ -27,6 +28,7 @@ const NETWORK_CONFIGURATION_DATA = mockNetworkState( id: 'test-networkConfigurationId-1', rpcUrl: 'https://testrpc.com', chainId: CHAIN_IDS.GOERLI, + blockExplorerUrl: 'https://etherscan.io', nickname: '0X5', }, { @@ -36,7 +38,7 @@ const NETWORK_CONFIGURATION_DATA = mockNetworkState( ticker: 'ETH', nickname: 'Localhost 8545', }, -).networkConfigurations; +).networkConfigurationsByChainId; describe('preferences controller', () => { let controllerMessenger: ControllerMessenger< @@ -94,7 +96,7 @@ describe('preferences controller', () => { preferencesController = new PreferencesController({ initLangCode: 'en_US', - networkConfigurations: NETWORK_CONFIGURATION_DATA, + networkConfigurationsByChainId: NETWORK_CONFIGURATION_DATA, messenger: preferencesMessenger, }); }); @@ -403,7 +405,7 @@ describe('preferences controller', () => { initState: { useTokenDetection: false, }, - networkConfigurations: NETWORK_CONFIGURATION_DATA, + networkConfigurationsByChainId: NETWORK_CONFIGURATION_DATA, }); const state = preferencesControllerExistingUser.store.getState(); expect(state.useTokenDetection).toStrictEqual(false); @@ -520,8 +522,10 @@ describe('preferences controller', () => { expect(state.incomingTransactionsPreferences).toStrictEqual({ [CHAIN_IDS.MAINNET]: true, [CHAIN_IDS.LINEA_MAINNET]: true, - [NETWORK_CONFIGURATION_DATA[addedNonTestNetworks[0]].chainId]: true, - [NETWORK_CONFIGURATION_DATA[addedNonTestNetworks[1]].chainId]: true, + [NETWORK_CONFIGURATION_DATA[addedNonTestNetworks[0] as Hex].chainId]: + true, + [NETWORK_CONFIGURATION_DATA[addedNonTestNetworks[1] as Hex].chainId]: + true, [CHAIN_IDS.GOERLI]: true, [CHAIN_IDS.SEPOLIA]: true, [CHAIN_IDS.LINEA_SEPOLIA]: true, @@ -539,8 +543,10 @@ describe('preferences controller', () => { expect(state.incomingTransactionsPreferences).toStrictEqual({ [CHAIN_IDS.MAINNET]: true, [CHAIN_IDS.LINEA_MAINNET]: false, - [NETWORK_CONFIGURATION_DATA[addedNonTestNetworks[0]].chainId]: true, - [NETWORK_CONFIGURATION_DATA[addedNonTestNetworks[1]].chainId]: true, + [NETWORK_CONFIGURATION_DATA[addedNonTestNetworks[0] as Hex].chainId]: + true, + [NETWORK_CONFIGURATION_DATA[addedNonTestNetworks[1] as Hex].chainId]: + true, [CHAIN_IDS.GOERLI]: true, [CHAIN_IDS.SEPOLIA]: true, [CHAIN_IDS.LINEA_SEPOLIA]: true, diff --git a/app/scripts/controllers/preferences-controller.ts b/app/scripts/controllers/preferences-controller.ts index b06f83739e8f..ab6c3e959215 100644 --- a/app/scripts/controllers/preferences-controller.ts +++ b/app/scripts/controllers/preferences-controller.ts @@ -84,7 +84,7 @@ export type PreferencesControllerMessenger = RestrictedControllerMessenger< >; type PreferencesControllerOptions = { - networkConfigurations?: Record; + networkConfigurationsByChainId?: Record; initState?: Partial; initLangCode?: string; messenger: PreferencesControllerMessenger; @@ -102,6 +102,7 @@ export type Preferences = { redesignedConfirmationsEnabled: boolean; redesignedTransactionsEnabled: boolean; featureNotificationsEnabled: boolean; + showMultiRpcModal: boolean; isRedesignedConfirmationsDeveloperEnabled: boolean; showConfirmationAdvancedDetails: boolean; }; @@ -170,7 +171,7 @@ export default class PreferencesController { */ constructor(opts: PreferencesControllerOptions) { const addedNonMainNetwork: Record = Object.values( - opts.networkConfigurations ?? {}, + opts.networkConfigurationsByChainId ?? {}, ).reduce((acc: Record, element) => { acc[element.chainId] = true; return acc; @@ -228,6 +229,7 @@ export default class PreferencesController { redesignedConfirmationsEnabled: true, redesignedTransactionsEnabled: true, featureNotificationsEnabled: false, + showMultiRpcModal: false, isRedesignedConfirmationsDeveloperEnabled: false, showConfirmationAdvancedDetails: false, }, diff --git a/app/scripts/lib/backup.js b/app/scripts/lib/backup.js index 7fda93995766..7c550c1581ab 100644 --- a/app/scripts/lib/backup.js +++ b/app/scripts/lib/backup.js @@ -57,8 +57,8 @@ export default class Backup { }, addressBook: { ...this.addressBookController.state }, network: { - networkConfigurations: - this.networkController.state.networkConfigurations, + networkConfigurationsByChainId: + this.networkController.state.networkConfigurationsByChainId, }, }; diff --git a/app/scripts/lib/backup.test.js b/app/scripts/lib/backup.test.js index 947761449a36..0d9712ba5be5 100644 --- a/app/scripts/lib/backup.test.js +++ b/app/scripts/lib/backup.test.js @@ -62,11 +62,11 @@ function getMockAddressBookController() { function getMockNetworkController() { const state = { - networkConfigurations: {}, + networkConfigurationsByChainId: {}, }; - const loadBackup = ({ networkConfigurations }) => { - Object.assign(state, { networkConfigurations }); + const loadBackup = ({ networkConfigurationsByChainId }) => { + Object.assign(state, { networkConfigurationsByChainId }); }; return { state, loadBackup }; @@ -169,6 +169,7 @@ const jsonData = JSON.stringify({ showTestNetworks: true, smartTransactionsOptInStatus: false, useNativeCurrencyAsPrimaryCurrency: true, + showMultiRpcModal: false, }, ipfsGateway: 'dweb.link', ledgerTransportType: 'webhid', @@ -221,25 +222,21 @@ describe('Backup', function () { await backup.restoreUserData(jsonData); // check networks backup expect( - backup.networkController.state.networkConfigurations[ - 'network-configuration-id-1' - ].chainId, - ).toStrictEqual('0x539'); + backup.networkController.state.networkConfigurationsByChainId['0x539'] + .rpcEndpoints[0].networkClientId, + ).toStrictEqual('network-configuration-id-1'); expect( - backup.networkController.state.networkConfigurations[ - 'network-configuration-id-2' - ].chainId, - ).toStrictEqual('0x38'); + backup.networkController.state.networkConfigurationsByChainId['0x38'] + .rpcEndpoints[0].networkClientId, + ).toStrictEqual('network-configuration-id-2'); expect( - backup.networkController.state.networkConfigurations[ - 'network-configuration-id-3' - ].chainId, - ).toStrictEqual('0x61'); + backup.networkController.state.networkConfigurationsByChainId['0x61'] + .rpcEndpoints[0].networkClientId, + ).toStrictEqual('network-configuration-id-3'); expect( - backup.networkController.state.networkConfigurations[ - 'network-configuration-id-4' - ].chainId, - ).toStrictEqual('0x89'); + backup.networkController.state.networkConfigurationsByChainId['0x89'] + .rpcEndpoints[0].networkClientId, + ).toStrictEqual('network-configuration-id-4'); // make sure identities are not lost after restore expect( backup.preferencesController.store.identities[ diff --git a/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.js b/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.js index 571688688611..adf596824bdd 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.js @@ -1,9 +1,10 @@ -import { ethErrors } from 'eth-rpc-errors'; import { ApprovalType } from '@metamask/controller-utils'; - +import * as URI from 'uri-js'; +import { RpcEndpointType } from '@metamask/network-controller'; +import { ethErrors } from 'eth-rpc-errors'; +import { cloneDeep } from 'lodash'; import { MESSAGE_TYPE } from '../../../../../shared/constants/app'; import { - findExistingNetwork, validateAddEthereumChainParams, switchChain, } from './ethereum-chain-utils'; @@ -12,9 +13,9 @@ const addEthereumChain = { methodNames: [MESSAGE_TYPE.ADD_ETHEREUM_CHAIN], implementation: addEthereumChainHandler, hookNames: { - upsertNetworkConfiguration: true, - getCurrentRpcUrl: true, - findNetworkConfigurationBy: true, + addNetwork: true, + updateNetwork: true, + getNetworkConfigurationByChainId: true, setActiveNetwork: true, requestUserApproval: true, startApprovalFlow: true, @@ -34,9 +35,9 @@ async function addEthereumChainHandler( _next, end, { - upsertNetworkConfiguration, - getCurrentRpcUrl, - findNetworkConfigurationBy, + addNetwork, + updateNetwork, + getNetworkConfigurationByChainId, setActiveNetwork, requestUserApproval, startApprovalFlow, @@ -64,20 +65,15 @@ async function addEthereumChainHandler( const { origin } = req; const currentChainIdForDomain = getCurrentChainIdForDomain(origin); - const currentNetworkConfiguration = findExistingNetwork( + const currentNetworkConfiguration = getNetworkConfigurationByChainId( currentChainIdForDomain, - findNetworkConfigurationBy, - ); - - const existingNetwork = findExistingNetwork( - chainId, - findNetworkConfigurationBy, ); + const existingNetwork = getNetworkConfigurationByChainId(chainId); if ( existingNetwork && existingNetwork.chainId === chainId && - existingNetwork.ticker !== ticker + existingNetwork.nativeCurrency !== ticker ) { return end( ethErrors.rpc.invalidParams({ @@ -86,11 +82,26 @@ async function addEthereumChainHandler( ); } - let networkClientId; - let requestData; let approvalFlowId; + let updatedNetwork = existingNetwork; + + let rpcIndex = existingNetwork?.rpcEndpoints.findIndex(({ url }) => + URI.equal(url, firstValidRPCUrl), + ); - if (!existingNetwork || existingNetwork.rpcUrl !== firstValidRPCUrl) { + let blockExplorerIndex = firstValidBlockExplorerUrl + ? existingNetwork?.blockExplorerUrls.findIndex((url) => + URI.equal(url, firstValidBlockExplorerUrl), + ) + : undefined; + + // If there's something to add or update + if ( + !existingNetwork || + rpcIndex !== existingNetwork.defaultRpcEndpointIndex || + (firstValidBlockExplorerUrl && + blockExplorerIndex !== existingNetwork.defaultBlockExplorerUrlIndex) + ) { ({ id: approvalFlowId } = await startApprovalFlow()); try { @@ -106,63 +117,111 @@ async function addEthereumChainHandler( }, }); - networkClientId = await upsertNetworkConfiguration( - { + if (existingNetwork) { + // A network for this chain id already exists. + // Update it with any new information. + + const clonedNetwork = cloneDeep(existingNetwork); + + // If the RPC endpoint doesn't exist, add a new one + if (rpcIndex === -1) { + clonedNetwork.rpcEndpoints = [ + ...clonedNetwork.rpcEndpoints, + { + url: firstValidRPCUrl, + type: RpcEndpointType.Custom, + name: chainName, + }, + ]; + rpcIndex = clonedNetwork.rpcEndpoints.length - 1; + } + + // The provided rpc endpoint becomes the default + clonedNetwork.defaultRpcEndpointIndex = rpcIndex; + + if (firstValidBlockExplorerUrl) { + // If a block explorer was provided and it doesn't exist, add a new one + if (blockExplorerIndex === -1) { + clonedNetwork.blockExplorerUrls = [ + ...clonedNetwork.blockExplorerUrls, + firstValidBlockExplorerUrl, + ]; + blockExplorerIndex = clonedNetwork.blockExplorerUrls.length - 1; + } + + // The provided block explorer becomes the default + clonedNetwork.defaultBlockExplorerUrlIndex = blockExplorerIndex; + } + + updatedNetwork = await updateNetwork( + clonedNetwork.chainId, + clonedNetwork, + currentChainIdForDomain === chainId + ? { + replacementSelectedRpcEndpointIndex: + clonedNetwork.defaultRpcEndpointIndex, + } + : undefined, + ); + } else { + // A network for this chain id does not exist, so add a new network + updatedNetwork = await addNetwork({ + blockExplorerUrls: firstValidBlockExplorerUrl + ? [firstValidBlockExplorerUrl] + : [], + defaultBlockExplorerUrlIndex: firstValidBlockExplorerUrl + ? 0 + : undefined, chainId, - rpcPrefs: { blockExplorerUrl: firstValidBlockExplorerUrl }, - nickname: chainName, - rpcUrl: firstValidRPCUrl, - ticker, - }, - { source: 'dapp', referrer: origin }, - ); + defaultRpcEndpointIndex: 0, + name: chainName, + nativeCurrency: ticker, + rpcEndpoints: [ + { + url: firstValidRPCUrl, + name: chainName, + type: RpcEndpointType.Custom, + }, + ], + }); + } } catch (error) { endApprovalFlow({ id: approvalFlowId }); return end(error); } + } - requestData = { - toNetworkConfiguration: { - rpcUrl: firstValidRPCUrl, - chainId, - nickname: chainName, - ticker, - networkClientId, - }, - fromNetworkConfiguration: currentNetworkConfiguration, - }; - } else { - networkClientId = existingNetwork.id ?? existingNetwork.type; - const currentRpcUrl = getCurrentRpcUrl(); - if ( - currentChainIdForDomain === chainId && - currentRpcUrl === firstValidRPCUrl - ) { - res.result = null; - return end(); - } + // If the added or updated network is not the current chain, prompt the user to switch + if (chainId !== currentChainIdForDomain) { + const { networkClientId } = + updatedNetwork.rpcEndpoints[updatedNetwork.defaultRpcEndpointIndex]; - requestData = { - toNetworkConfiguration: existingNetwork, + const requestData = { + toNetworkConfiguration: updatedNetwork, fromNetworkConfiguration: currentNetworkConfiguration, }; + + return switchChain( + res, + end, + origin, + chainId, + requestData, + networkClientId, + approvalFlowId, + { + getChainPermissionsFeatureFlag, + setActiveNetwork, + requestUserApproval, + getCaveat, + requestPermittedChainsPermission, + endApprovalFlow, + }, + ); + } else if (approvalFlowId) { + endApprovalFlow({ id: approvalFlowId }); } - return switchChain( - res, - end, - origin, - chainId, - requestData, - networkClientId, - approvalFlowId, - { - getChainPermissionsFeatureFlag, - setActiveNetwork, - requestUserApproval, - getCaveat, - requestPermittedChainsPermission, - endApprovalFlow, - }, - ); + res.result = null; + return end(); } diff --git a/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.test.js b/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.test.js index bd7434e61481..d04037949e87 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.test.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.test.js @@ -1,41 +1,55 @@ import { ethErrors } from 'eth-rpc-errors'; -import { - CHAIN_IDS, - NETWORK_TYPES, -} from '../../../../../shared/constants/network'; +import { CHAIN_IDS } from '../../../../../shared/constants/network'; import addEthereumChain from './add-ethereum-chain'; const NON_INFURA_CHAIN_ID = '0x123456789'; const createMockMainnetConfiguration = () => ({ chainId: CHAIN_IDS.MAINNET, - nickname: 'Ethereum Mainnet', - rpcUrl: 'https://mainnet.infura.io/v3/', - type: NETWORK_TYPES.MAINNET, - ticker: 'ETH', - rpcPrefs: { - blockExplorerUrl: 'https://etherscan.io', - }, + name: 'Ethereum Mainnet', + defaultRpcEndpointIndex: 0, + rpcEndpoints: [ + { + networkClientId: 'mainnet', + url: 'https://mainnet.infura.io/v3/', + type: 'infura', + }, + ], + nativeCurrency: 'ETH', + blockExplorerUrls: ['https://etherscan.io'], + defaultBlockExplorerUrlIndex: 0, }); const createMockOptimismConfiguration = () => ({ chainId: CHAIN_IDS.OPTIMISM, - nickname: 'Optimism', - rpcUrl: 'https://optimism.llamarpc.com', - rpcPrefs: { - blockExplorerUrl: 'https://optimistic.etherscan.io', - }, - ticker: 'ETH', + name: 'Optimism', + defaultRpcEndpointIndex: 0, + rpcEndpoints: [ + { + networkClientId: 'optimism-network-client-id', + url: 'https://optimism.llamarpc.com', + type: 'custom', + }, + ], + nativeCurrency: 'ETH', + blockExplorerUrls: ['https://optimistic.etherscan.io'], + defaultBlockExplorerUrlIndex: 0, }); const createMockNonInfuraConfiguration = () => ({ chainId: NON_INFURA_CHAIN_ID, - rpcUrl: 'https://custom.network', - ticker: 'CUST', - nickname: 'Custom Network', - rpcPrefs: { - blockExplorerUrl: 'https://custom.blockexplorer', - }, + name: 'Custom Network', + defaultRpcEndpointIndex: 0, + rpcEndpoints: [ + { + name: 'Custom Network', + url: 'https://custom.network', + type: 'custom', + }, + ], + nativeCurrency: 'CUST', + blockExplorerUrls: ['https://custom.blockexplorer'], + defaultBlockExplorerUrlIndex: 0, }); describe('addEthereumChainHandler', () => { @@ -52,19 +66,21 @@ describe('addEthereumChainHandler', () => { .fn() .mockReturnValue(NON_INFURA_CHAIN_ID), setNetworkClientIdForDomain: jest.fn(), - findNetworkConfigurationBy: jest - .fn() - .mockReturnValue(createMockMainnetConfiguration()), + getNetworkConfigurationByChainId: jest.fn(), setActiveNetwork: jest.fn(), - getCurrentRpcUrl: jest - .fn() - .mockReturnValue(createMockMainnetConfiguration().rpcUrl), requestUserApproval: jest.fn().mockResolvedValue(123), requestPermittedChainsPermission: jest.fn(), getCaveat: jest.fn().mockReturnValue({ value: permissionedChainIds }), - upsertNetworkConfiguration: jest.fn().mockResolvedValue(123), startApprovalFlow: () => ({ id: 'approvalFlowId' }), endApprovalFlow: jest.fn(), + addNetwork: jest.fn().mockResolvedValue({ + defaultRpcEndpointIndex: 0, + rpcEndpoints: [{ networkClientId: 123 }], + }), + updateNetwork: jest.fn().mockResolvedValue({ + defaultRpcEndpointIndex: 0, + rpcEndpoints: [{ networkClientId: 123 }], + }), ...overrides, }; }; @@ -103,19 +119,22 @@ describe('addEthereumChainHandler', () => { // called twice, once for the add and once for the switch expect(mocks.requestUserApproval).toHaveBeenCalledTimes(2); - expect(mocks.upsertNetworkConfiguration).toHaveBeenCalledTimes(1); - expect(mocks.upsertNetworkConfiguration).toHaveBeenCalledWith( - { - chainId: CHAIN_IDS.OPTIMISM, - nickname: 'Optimism Mainnet', - rpcUrl: 'https://optimism.llamarpc.com', - ticker: 'ETH', - rpcPrefs: { - blockExplorerUrl: 'https://optimistic.etherscan.io', + expect(mocks.addNetwork).toHaveBeenCalledTimes(1); + expect(mocks.addNetwork).toHaveBeenCalledWith({ + blockExplorerUrls: ['https://optimistic.etherscan.io'], + defaultBlockExplorerUrlIndex: 0, + chainId: '0xa', + defaultRpcEndpointIndex: 0, + name: 'Optimism Mainnet', + nativeCurrency: 'ETH', + rpcEndpoints: [ + { + name: 'Optimism Mainnet', + url: 'https://optimism.llamarpc.com', + type: 'custom', }, - }, - { referrer: 'example.com', source: 'dapp' }, - ); + ], + }); expect(mocks.setActiveNetwork).toHaveBeenCalledTimes(1); expect(mocks.setActiveNetwork).toHaveBeenCalledWith(123); }); @@ -145,18 +164,23 @@ describe('addEthereumChainHandler', () => { jest.fn(), mocks, ); - expect(mocks.upsertNetworkConfiguration).toHaveBeenCalledTimes(1); + expect(mocks.addNetwork).toHaveBeenCalledTimes(1); expect(mocks.setActiveNetwork).toHaveBeenCalledTimes(1); }); describe('if a networkConfiguration for the given chainId already exists', () => { - it('creates a new network configuration for the given chainid and switches to it if proposed networkConfiguration has a different rpcUrl from all existing networkConfigurations', async () => { + it('updates the existing networkConfiguration with the new rpc url if it doesnt already exist', async () => { const mocks = makeMocks({ permissionsFeatureFlagIsActive: false, overrides: { - upsertNetworkConfiguration: jest.fn().mockResolvedValue(123456), + getNetworkConfigurationByChainId: jest + .fn() + // Start with just infura endpoint + .mockReturnValue(createMockMainnetConfiguration()), }, }); + + // Add a custom endpoint await addEthereumChainHandler( { origin: 'example.com', @@ -179,51 +203,133 @@ describe('addEthereumChainHandler', () => { mocks, ); - expect(mocks.upsertNetworkConfiguration).toHaveBeenCalledTimes(1); - expect(mocks.setActiveNetwork).toHaveBeenCalledWith(123456); + expect(mocks.updateNetwork).toHaveBeenCalledTimes(1); + expect(mocks.updateNetwork).toHaveBeenCalledWith( + '0x1', + { + chainId: '0x1', + name: 'Ethereum Mainnet', + // Expect both endpoints + rpcEndpoints: [ + { + networkClientId: 'mainnet', + url: 'https://mainnet.infura.io/v3/', + type: 'infura', + }, + { + name: 'Ethereum Mainnet', + url: 'https://eth.llamarpc.com', + type: 'custom', + }, + ], + // and the new one is the default + defaultRpcEndpointIndex: 1, + nativeCurrency: 'ETH', + blockExplorerUrls: ['https://etherscan.io'], + defaultBlockExplorerUrlIndex: 0, + }, + undefined, + ); }); - it('switches to the existing networkConfiguration if the proposed networkConfiguration has the same rpcUrl as an existing networkConfiguration and the currently selected network doesnt match the requested one', async () => { + it('makes the rpc url the default if it already exists', async () => { + const existingNetwork = { + chainId: '0x1', + name: 'Ethereum Mainnet', + // Start with infura + custom endpoint + rpcEndpoints: [ + { + networkClientId: 'mainnet', + url: 'https://mainnet.infura.io/v3/', + type: 'infura', + }, + { + name: 'Ethereum Mainnet', + url: 'https://eth.llamarpc.com', + type: 'custom', + }, + ], + // Infura is the default + defaultRpcEndpointIndex: 0, + nativeCurrency: 'ETH', + blockExplorerUrls: ['https://etherscan.io'], + defaultBlockExplorerUrlIndex: 0, + }; + const mocks = makeMocks({ permissionsFeatureFlagIsActive: false, overrides: { - getCurrentRpcUrl: jest + getNetworkConfigurationByChainId: jest .fn() - .mockReturnValue(createMockNonInfuraConfiguration().rpcUrl), + .mockReturnValue(existingNetwork), + }, + }); + + // Add the same custom endpoint + await addEthereumChainHandler( + { + origin: 'example.com', + params: [ + { + chainId: CHAIN_IDS.MAINNET, + chainName: 'Ethereum Mainnet', + rpcUrls: ['https://eth.llamarpc.com'], + nativeCurrency: { + symbol: 'ETH', + decimals: 18, + }, + blockExplorerUrls: ['https://etherscan.io'], + }, + ], + }, + {}, + jest.fn(), + jest.fn(), + mocks, + ); + + expect(mocks.updateNetwork).toHaveBeenCalledTimes(1); + expect(mocks.updateNetwork).toHaveBeenCalledWith( + '0x1', + { + ...existingNetwork, + // Verify the custom endpoint becomes the default + defaultRpcEndpointIndex: 1, + }, + undefined, + ); + }); + + it('switches to the network if its not already the currently selected chain id', async () => { + const existingNetwork = createMockMainnetConfiguration(); + + const mocks = makeMocks({ + permissionsFeatureFlagIsActive: false, + overrides: { + // Start on sepolia getCurrentChainIdForDomain: jest .fn() - .mockReturnValue(createMockNonInfuraConfiguration().chainId), - findNetworkConfigurationBy: jest + .mockReturnValue(CHAIN_IDS.SEPOLIA), + getNetworkConfigurationByChainId: jest .fn() - .mockImplementation(({ chainId }) => { - switch (chainId) { - case createMockNonInfuraConfiguration().chainId: - return createMockNonInfuraConfiguration(); - case createMockOptimismConfiguration().chainId: - return createMockOptimismConfiguration(); - default: - return undefined; - } - }), - upsertNetworkConfiguration: jest.fn().mockResolvedValue(123), + .mockReturnValue(existingNetwork), }, }); + // Add with rpc + block explorers that already exist await addEthereumChainHandler( { origin: 'example.com', params: [ { - chainId: createMockOptimismConfiguration().chainId, - chainName: createMockOptimismConfiguration().nickname, - rpcUrls: [createMockOptimismConfiguration().rpcUrl], + chainId: CHAIN_IDS.MAINNET, + chainName: 'Ethereum Mainnet', + rpcUrls: [existingNetwork.rpcEndpoints[0].url], nativeCurrency: { - symbol: createMockOptimismConfiguration().ticker, + symbol: 'ETH', decimals: 18, }, - blockExplorerUrls: [ - createMockOptimismConfiguration().rpcPrefs.blockExplorerUrl, - ], + blockExplorerUrls: ['https://etherscan.io'], }, ], }, @@ -233,27 +339,12 @@ describe('addEthereumChainHandler', () => { mocks, ); - expect(mocks.requestUserApproval).toHaveBeenCalledTimes(1); - expect(mocks.requestUserApproval).toHaveBeenCalledWith({ - origin: 'example.com', - requestData: { - fromNetworkConfiguration: createMockNonInfuraConfiguration(), - toNetworkConfiguration: { - chainId: '0xa', - nickname: 'Optimism', - rpcPrefs: { - blockExplorerUrl: 'https://optimistic.etherscan.io', - }, - rpcUrl: 'https://optimism.llamarpc.com', - ticker: 'ETH', - }, - }, - type: 'wallet_switchEthereumChain', - }); + // No updates, network already had all the info + expect(mocks.updateNetwork).toHaveBeenCalledTimes(0); + + // User should be prompted to switch chains expect(mocks.setActiveNetwork).toHaveBeenCalledTimes(1); - expect(mocks.setActiveNetwork).toHaveBeenCalledWith( - createMockOptimismConfiguration().id, - ); + expect(mocks.setActiveNetwork).toHaveBeenCalledWith('mainnet'); }); it('should return error for invalid chainId', async () => { @@ -284,25 +375,32 @@ describe('addEthereumChainHandler', () => { describe('with `endowment:permitted-chains` permissioning active', () => { it('creates a new network configuration for the given chainid, requests `endowment:permitted-chains` permission and switches to it if no networkConfigurations with the same chainId exist', async () => { + const nonInfuraConfiguration = createMockNonInfuraConfiguration(); + const mocks = makeMocks({ permissionedChainIds: [], permissionsFeatureFlagIsActive: true, + overrides: { + getCurrentChainIdForDomain: jest + .fn() + .mockReturnValue(CHAIN_IDS.MAINNET), + }, }); await addEthereumChainHandler( { origin: 'example.com', params: [ { - chainId: createMockNonInfuraConfiguration().chainId, - chainName: createMockNonInfuraConfiguration().nickname, - rpcUrls: [createMockNonInfuraConfiguration().rpcUrl], + chainId: nonInfuraConfiguration.chainId, + chainName: nonInfuraConfiguration.name, + rpcUrls: nonInfuraConfiguration.rpcEndpoints.map( + (rpc) => rpc.url, + ), nativeCurrency: { - symbol: createMockNonInfuraConfiguration().ticker, + symbol: nonInfuraConfiguration.nativeCurrency, decimals: 18, }, - blockExplorerUrls: [ - createMockNonInfuraConfiguration().rpcPrefs.blockExplorerUrl, - ], + blockExplorerUrls: nonInfuraConfiguration.blockExplorerUrls, }, ], }, @@ -312,10 +410,7 @@ describe('addEthereumChainHandler', () => { mocks, ); - expect(mocks.upsertNetworkConfiguration).toHaveBeenCalledWith( - createMockNonInfuraConfiguration(), - { referrer: 'example.com', source: 'dapp' }, - ); + expect(mocks.addNetwork).toHaveBeenCalledWith(nonInfuraConfiguration); expect(mocks.requestPermittedChainsPermission).toHaveBeenCalledTimes(1); expect(mocks.requestPermittedChainsPermission).toHaveBeenCalledWith([ createMockNonInfuraConfiguration().chainId, @@ -330,6 +425,11 @@ describe('addEthereumChainHandler', () => { const mocks = makeMocks({ permissionedChainIds: [CHAIN_IDS.MAINNET], permissionsFeatureFlagIsActive: true, + overrides: { + getCurrentChainIdForDomain: jest + .fn() + .mockReturnValue(CHAIN_IDS.SEPOLIA), + }, }); await addEthereumChainHandler( @@ -365,7 +465,7 @@ describe('addEthereumChainHandler', () => { permissionsFeatureFlagIsActive: true, permissionedChainIds: [], overrides: { - findNetworkConfigurationBy: jest + getNetworkConfigurationByChainId: jest .fn() .mockReturnValue(createMockNonInfuraConfiguration()), getCurrentChainIdForDomain: jest @@ -396,7 +496,7 @@ describe('addEthereumChainHandler', () => { mocks, ); - expect(mocks.upsertNetworkConfiguration).toHaveBeenCalledTimes(1); + expect(mocks.updateNetwork).toHaveBeenCalledTimes(1); expect(mocks.requestPermittedChainsPermission).toHaveBeenCalledTimes( 1, ); @@ -407,15 +507,18 @@ describe('addEthereumChainHandler', () => { }); }); - it('should switch to the existing networkConfiguration if the proposed networkConfiguration has the same rpcUrl as the one already in state (and is not currently selected)', async () => { + it('should switch to the existing networkConfiguration if one already exsits for the given chain id', async () => { const mocks = makeMocks({ - permissionedChainIds: [createMockOptimismConfiguration().chainId], + permissionedChainIds: [ + createMockOptimismConfiguration().chainId, + CHAIN_IDS.MAINNET, + ], permissionsFeatureFlagIsActive: true, overrides: { - getCurrentRpcUrl: jest + getCurrentChainIdForDomain: jest .fn() - .mockReturnValue('https://eth.llamarpc.com'), - findNetworkConfigurationBy: jest + .mockReturnValue(CHAIN_IDS.MAINNET), + getNetworkConfigurationByChainId: jest .fn() .mockReturnValue(createMockOptimismConfiguration()), }, @@ -427,15 +530,16 @@ describe('addEthereumChainHandler', () => { params: [ { chainId: createMockOptimismConfiguration().chainId, - chainName: createMockOptimismConfiguration().nickname, - rpcUrls: [createMockOptimismConfiguration().rpcUrl], + chainName: createMockOptimismConfiguration().name, + rpcUrls: createMockOptimismConfiguration().rpcEndpoints.map( + (rpc) => rpc.url, + ), nativeCurrency: { - symbol: createMockOptimismConfiguration().ticker, + symbol: createMockOptimismConfiguration().nativeCurrency, decimals: 18, }, - blockExplorerUrls: [ - createMockOptimismConfiguration().rpcPrefs.blockExplorerUrl, - ], + blockExplorerUrls: + createMockOptimismConfiguration().blockExplorerUrls, }, ], }, @@ -445,11 +549,10 @@ describe('addEthereumChainHandler', () => { mocks, ); - expect(mocks.requestUserApproval).not.toHaveBeenCalled(); expect(mocks.requestPermittedChainsPermission).not.toHaveBeenCalled(); expect(mocks.setActiveNetwork).toHaveBeenCalledTimes(1); expect(mocks.setActiveNetwork).toHaveBeenCalledWith( - createMockOptimismConfiguration().id, + createMockOptimismConfiguration().rpcEndpoints[0].networkClientId, ); }); }); @@ -476,7 +579,7 @@ describe('addEthereumChainHandler', () => { decimals: 18, }, blockExplorerUrls: [ - createMockNonInfuraConfiguration().rpcPrefs.blockExplorerUrl, + createMockNonInfuraConfiguration().blockExplorerUrls[0], ], [unexpectedParam]: 'parameter', }, @@ -501,6 +604,9 @@ describe('addEthereumChainHandler', () => { permissionsFeatureFlagIsActive: true, permissionedChainIds: [], overrides: { + getCurrentChainIdForDomain: jest + .fn() + .mockReturnValue(CHAIN_IDS.SEPOLIA), requestPermittedChainsPermission: jest .fn() .mockRejectedValue(mockError), @@ -539,6 +645,11 @@ describe('addEthereumChainHandler', () => { const mocks = makeMocks({ permissionedChainIds: [CHAIN_IDS.MAINNET], permissionsFeatureFlagIsActive: true, + overrides: { + getNetworkConfigurationByChainId: jest + .fn() + .mockReturnValue(createMockMainnetConfiguration()), + }, }); const mockEnd = jest.fn(); @@ -580,10 +691,9 @@ describe('addEthereumChainHandler', () => { getCurrentChainIdForDomain: jest .fn() .mockReturnValue(CURRENT_RPC_CONFIG.chainId), - findNetworkConfigurationBy: jest + getNetworkConfigurationByChainId: jest .fn() .mockReturnValue(CURRENT_RPC_CONFIG), - getCurrentRpcUrl: jest.fn().mockReturnValue(CURRENT_RPC_CONFIG.rpcUrl), }, }); const res = {}; @@ -595,9 +705,9 @@ describe('addEthereumChainHandler', () => { { chainId: CURRENT_RPC_CONFIG.chainId, chainName: 'Custom Network', - rpcUrls: [CURRENT_RPC_CONFIG.rpcUrl], + rpcUrls: [CURRENT_RPC_CONFIG.rpcEndpoints[0].url], nativeCurrency: { - symbol: CURRENT_RPC_CONFIG.ticker, + symbol: CURRENT_RPC_CONFIG.nativeCurrency, decimals: 18, }, blockExplorerUrls: ['https://custom.blockexplorer'], diff --git a/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js b/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js index 3d5e0de38c5b..89415d471468 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js @@ -1,13 +1,5 @@ import { errorCodes, ethErrors } from 'eth-rpc-errors'; import { ApprovalType } from '@metamask/controller-utils'; - -import { - BUILT_IN_INFURA_NETWORKS, - CHAIN_ID_TO_RPC_URL_MAP, - CHAIN_ID_TO_TYPE_MAP, - CURRENCY_SYMBOLS, - NETWORK_TO_NAME_MAP, -} from '../../../../../shared/constants/network'; import { isPrefixedFormattedHexString, isSafeChainId, @@ -17,23 +9,6 @@ import { UNKNOWN_TICKER_SYMBOL } from '../../../../../shared/constants/app'; import { PermissionNames } from '../../../controllers/permissions'; import { getValidUrl } from '../../util'; -export function findExistingNetwork(chainId, findNetworkConfigurationBy) { - if ( - Object.values(BUILT_IN_INFURA_NETWORKS) - .map(({ chainId: id }) => id) - .includes(chainId) - ) { - return { - chainId, - ticker: CURRENCY_SYMBOLS.ETH, - nickname: NETWORK_TO_NAME_MAP[chainId], - rpcUrl: CHAIN_ID_TO_RPC_URL_MAP[chainId], - type: CHAIN_ID_TO_TYPE_MAP[chainId], - }; - } - return findNetworkConfigurationBy({ chainId }); -} - export function validateChainId(chainId) { const _chainId = typeof chainId === 'string' && chainId.toLowerCase(); if (!isPrefixedFormattedHexString(_chainId)) { diff --git a/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.js b/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.js index f701ba06ea6f..847cdf8abe24 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.js @@ -1,7 +1,6 @@ import { ethErrors } from 'eth-rpc-errors'; import { MESSAGE_TYPE } from '../../../../../shared/constants/app'; import { - findExistingNetwork, validateSwitchEthereumChainParams, switchChain, } from './ethereum-chain-utils'; @@ -10,7 +9,7 @@ const switchEthereumChain = { methodNames: [MESSAGE_TYPE.SWITCH_ETHEREUM_CHAIN], implementation: switchEthereumChainHandler, hookNames: { - findNetworkConfigurationBy: true, + getNetworkConfigurationByChainId: true, setActiveNetwork: true, getCaveat: true, requestPermittedChainsPermission: true, @@ -28,7 +27,7 @@ async function switchEthereumChainHandler( _next, end, { - findNetworkConfigurationBy, + getNetworkConfigurationByChainId, setActiveNetwork, requestPermittedChainsPermission, getCaveat, @@ -51,14 +50,12 @@ async function switchEthereumChainHandler( return end(); } - const networkConfigurationForRequestedChainId = findExistingNetwork( - chainId, - findNetworkConfigurationBy, - ); - + const networkConfigurationForRequestedChainId = + getNetworkConfigurationByChainId(chainId); const networkClientIdToSwitchTo = - networkConfigurationForRequestedChainId?.id ?? - networkConfigurationForRequestedChainId?.type; + networkConfigurationForRequestedChainId?.rpcEndpoints[ + networkConfigurationForRequestedChainId.defaultRpcEndpointIndex + ].networkClientId; if (!networkClientIdToSwitchTo) { return end( @@ -71,9 +68,8 @@ async function switchEthereumChainHandler( const requestData = { toNetworkConfiguration: networkConfigurationForRequestedChainId, - fromNetworkConfiguration: findExistingNetwork( + fromNetworkConfiguration: getNetworkConfigurationByChainId( currentChainIdForOrigin, - findNetworkConfigurationBy, ), }; diff --git a/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.test.js b/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.test.js index e49964314b5c..30a9f9aa8f8e 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.test.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.test.js @@ -11,15 +11,23 @@ const mockRequestUserApproval = ({ requestData }) => { }; const createMockMainnetConfiguration = () => ({ - id: 123, chainId: CHAIN_IDS.MAINNET, - type: NETWORK_TYPES.MAINNET, + defaultRpcEndpointIndex: 0, + rpcEndpoints: [ + { + networkClientId: NETWORK_TYPES.MAINNET, + }, + ], }); const createMockLineaMainnetConfiguration = () => ({ - id: 1234, chainId: CHAIN_IDS.LINEA_MAINNET, - type: NETWORK_TYPES.LINEA_MAINNET, + defaultRpcEndpointIndex: 0, + rpcEndpoints: [ + { + networkClientId: NETWORK_TYPES.LINEA_MAINNET, + }, + ], }); describe('switchEthereumChainHandler', () => { @@ -27,7 +35,7 @@ describe('switchEthereumChainHandler', () => { permissionedChainIds = [], permissionsFeatureFlagIsActive = false, overrides = {}, - mockedFindNetworkConfigurationByReturnValue = createMockMainnetConfiguration(), + mockedGetNetworkConfigurationByChainIdReturnValue = createMockMainnetConfiguration(), mockedGetCurrentChainIdForDomainReturnValue = NON_INFURA_CHAIN_ID, } = {}) => { const mockGetCaveat = jest.fn(); @@ -39,15 +47,15 @@ describe('switchEthereumChainHandler', () => { .fn() .mockReturnValue(mockedGetCurrentChainIdForDomainReturnValue), setNetworkClientIdForDomain: jest.fn(), - findNetworkConfigurationBy: jest - .fn() - .mockReturnValue(mockedFindNetworkConfigurationByReturnValue), setActiveNetwork: jest.fn(), requestUserApproval: jest .fn() .mockImplementation(mockRequestUserApproval), requestPermittedChainsPermission: jest.fn(), getCaveat: mockGetCaveat, + getNetworkConfigurationByChainId: jest + .fn() + .mockReturnValue(mockedGetNetworkConfigurationByChainIdReturnValue), ...overrides, }; }; @@ -63,7 +71,7 @@ describe('switchEthereumChainHandler', () => { const mocks = makeMocks({ permissionsFeatureFlagIsActive, overrides: { - findNetworkConfigurationBy: jest + getNetworkConfigurationByChainId: jest .fn() .mockReturnValue(createMockMainnetConfiguration()), }, @@ -81,7 +89,7 @@ describe('switchEthereumChainHandler', () => { ); expect(mocks.setActiveNetwork).toHaveBeenCalledTimes(1); expect(mocks.setActiveNetwork).toHaveBeenCalledWith( - createMockMainnetConfiguration().type, + createMockMainnetConfiguration().rpcEndpoints[0].networkClientId, ); }); @@ -89,7 +97,7 @@ describe('switchEthereumChainHandler', () => { const mocks = makeMocks({ permissionsFeatureFlagIsActive, overrides: { - findNetworkConfigurationBy: jest + getNetworkConfigurationByChainId: jest .fn() .mockReturnValue(createMockLineaMainnetConfiguration()), }, @@ -107,7 +115,7 @@ describe('switchEthereumChainHandler', () => { ); expect(mocks.setActiveNetwork).toHaveBeenCalledTimes(1); expect(mocks.setActiveNetwork).toHaveBeenCalledWith( - createMockLineaMainnetConfiguration().type, + createMockLineaMainnetConfiguration().rpcEndpoints[0].networkClientId, ); }); @@ -115,7 +123,7 @@ describe('switchEthereumChainHandler', () => { const mocks = makeMocks({ permissionsFeatureFlagIsActive, overrides: { - findNetworkConfigurationBy: jest + getNetworkConfigurationByChainId: jest .fn() .mockReturnValue(createMockLineaMainnetConfiguration()), }, @@ -133,7 +141,7 @@ describe('switchEthereumChainHandler', () => { ); expect(mocks.setActiveNetwork).toHaveBeenCalledTimes(1); expect(mocks.setActiveNetwork).toHaveBeenCalledWith( - createMockLineaMainnetConfiguration().type, + createMockLineaMainnetConfiguration().rpcEndpoints[0].networkClientId, ); }); @@ -159,9 +167,45 @@ describe('switchEthereumChainHandler', () => { ); expect(mocks.setActiveNetwork).toHaveBeenCalledTimes(1); expect(mocks.setActiveNetwork).toHaveBeenCalledWith( - createMockMainnetConfiguration().id, + createMockMainnetConfiguration().rpcEndpoints[0].networkClientId, ); }); + + it('should handle missing networkConfiguration', async () => { + // Mock a network configuration that has an undefined or missing rpcEndpoints + const mockNetworkConfiguration = undefined; + + const mocks = makeMocks({ + overrides: { + getNetworkConfigurationByChainId: jest + .fn() + .mockReturnValue(mockNetworkConfiguration), + }, + }); + + const switchEthereumChainHandler = switchEthereumChain.implementation; + + const mockEnd = jest.fn(); + await switchEthereumChainHandler( + { + origin: 'example.com', + params: [{ chainId: CHAIN_IDS.MAINNET }], + }, + {}, + jest.fn(), + mockEnd, + mocks, + ); + + // Check that the function handled the missing rpcEndpoints and did not attempt to call setActiveNetwork + expect(mockEnd).toHaveBeenCalledWith( + expect.objectContaining({ + code: 4902, + message: expect.stringContaining('Unrecognized chain ID'), + }), + ); + expect(mocks.setActiveNetwork).not.toHaveBeenCalled(); + }); }); describe('with permittedChains permissioning active', () => { @@ -196,7 +240,7 @@ describe('switchEthereumChainHandler', () => { ]); expect(mocks.setActiveNetwork).toHaveBeenCalledTimes(1); expect(mocks.setActiveNetwork).toHaveBeenCalledWith( - createMockMainnetConfiguration().type, + createMockMainnetConfiguration().rpcEndpoints[0].networkClientId, ); }); @@ -220,7 +264,7 @@ describe('switchEthereumChainHandler', () => { expect(mocks.requestPermittedChainsPermission).not.toHaveBeenCalled(); expect(mocks.setActiveNetwork).toHaveBeenCalledTimes(1); expect(mocks.setActiveNetwork).toHaveBeenCalledWith( - createMockMainnetConfiguration().type, + createMockMainnetConfiguration().rpcEndpoints[0].networkClientId, ); }); diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index c5fd4b834aed..5513b043ce4e 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -56,7 +56,10 @@ import { ControllerMessenger } from '@metamask/base-controller'; import { EnsController } from '@metamask/ens-controller'; import { PhishingController } from '@metamask/phishing-controller'; import { AnnouncementController } from '@metamask/announcement-controller'; -import { NetworkController } from '@metamask/network-controller'; +import { + NetworkController, + getDefaultNetworkControllerState, +} from '@metamask/network-controller'; import { GasFeeController } from '@metamask/gas-fee-controller'; import { PermissionController, @@ -90,6 +93,13 @@ import { buildSnapEndowmentSpecifications, buildSnapRestrictedMethodSpecifications, } from '@metamask/snaps-rpc-methods'; +import { + ApprovalType, + ERC1155, + ERC20, + ERC721, + BlockExplorerUrl, +} from '@metamask/controller-utils'; import { AccountsController } from '@metamask/accounts-controller'; @@ -104,12 +114,6 @@ import { TransactionUpdateController } from '@metamask-institutional/transaction ///: END:ONLY_INCLUDE_IF import { SignatureController } from '@metamask/signature-controller'; import { PPOMController } from '@metamask/ppom-validator'; -import { - ApprovalType, - ERC1155, - ERC20, - ERC721, -} from '@metamask/controller-utils'; import { wordlist } from '@metamask/scure-bip39/dist/wordlists/english'; import { @@ -170,6 +174,7 @@ import { CHAIN_IDS, NETWORK_TYPES, NetworkStatus, + MAINNET_DISPLAY_NAME, } from '../../shared/constants/network'; import { getAllowedSmartTransactionsChainIds } from '../../shared/constants/smartTransactions'; @@ -501,38 +506,59 @@ export default class MetamaskController extends EventEmitter { name: 'NetworkController', }); - let initialNetworkControllerState = {}; - if (initState.NetworkController) { - initialNetworkControllerState = initState.NetworkController; - } else if (process.env.IN_TEST) { - const networkConfig = { - chainId: CHAIN_IDS.LOCALHOST, - nickname: 'Localhost 8545', - rpcPrefs: {}, - rpcUrl: 'http://localhost:8545', - ticker: 'ETH', - id: 'networkConfigurationId', - }; - initialNetworkControllerState = { - selectedNetworkClientId: networkConfig.id, - networkConfigurations: { - [networkConfig.id]: networkConfig, - }, - }; - } else if ( - process.env.METAMASK_DEBUG || - process.env.METAMASK_ENVIRONMENT === 'test' - ) { - initialNetworkControllerState = { - selectedNetworkClientId: NETWORK_TYPES.SEPOLIA, - }; + let initialNetworkControllerState = initState.NetworkController; + if (!initialNetworkControllerState) { + initialNetworkControllerState = getDefaultNetworkControllerState(); + + const networks = + initialNetworkControllerState.networkConfigurationsByChainId; + + // Note: Consider changing `getDefaultNetworkControllerState` + // on the controller side to include some of these tweaks. + networks[CHAIN_IDS.MAINNET].name = MAINNET_DISPLAY_NAME; + delete networks[CHAIN_IDS.GOERLI]; + delete networks[CHAIN_IDS.LINEA_GOERLI]; + + Object.values(networks).forEach((network) => { + const id = network.rpcEndpoints[0].networkClientId; + network.blockExplorerUrls = [BlockExplorerUrl[id]]; + network.defaultBlockExplorerUrlIndex = 0; + }); + + let network; + if (process.env.IN_TEST) { + network = { + chainId: CHAIN_IDS.LOCALHOST, + name: 'Localhost 8545', + nativeCurrency: 'ETH', + blockExplorerUrls: [], + defaultRpcEndpointIndex: 0, + rpcEndpoints: [ + { + networkClientId: 'networkConfigurationId', + url: 'http://localhost:8545', + type: 'custom', + }, + ], + }; + networks[CHAIN_IDS.LOCALHOST] = network; + } else if ( + process.env.METAMASK_DEBUG || + process.env.METAMASK_ENVIRONMENT === 'test' + ) { + network = networks[CHAIN_IDS.SEPOLIA]; + } else { + network = networks[CHAIN_IDS.MAINNET]; + } + + initialNetworkControllerState.selectedNetworkClientId = + network.rpcEndpoints[network.defaultRpcEndpointIndex].networkClientId; } + this.networkController = new NetworkController({ messenger: networkControllerMessenger, state: initialNetworkControllerState, infuraProjectId: opts.infuraProjectId, - trackMetaMetricsEvent: (...args) => - this.metaMetricsController.trackEvent(...args), }); this.networkController.initializeProvider(); this.provider = @@ -575,7 +601,8 @@ export default class MetamaskController extends EventEmitter { initLangCode: opts.initLangCode, messenger: preferencesMessenger, provider: this.provider, - networkConfigurations: this.networkController.state.networkConfigurations, + networkConfigurationsByChainId: + this.networkController.state.networkConfigurationsByChainId, }); const tokenListMessenger = this.controllerMessenger.getRestricted({ @@ -3310,11 +3337,15 @@ export default class MetamaskController extends EventEmitter { }, rollbackToPreviousProvider: networkController.rollbackToPreviousProvider.bind(networkController), - removeNetworkConfiguration: this.removeNetworkConfiguration.bind(this), - upsertNetworkConfiguration: - this.networkController.upsertNetworkConfiguration.bind( - this.networkController, - ), + addNetwork: this.networkController.addNetwork.bind( + this.networkController, + ), + updateNetwork: this.networkController.updateNetwork.bind( + this.networkController, + ), + removeNetwork: this.networkController.removeNetwork.bind( + this.networkController, + ), getCurrentNetworkEIP1559Compatibility: this.networkController.getEIP1559Compatibility.bind( this.networkController, @@ -4764,28 +4795,6 @@ export default class MetamaskController extends EventEmitter { ); } - removeNetworkConfiguration(networkConfigurationId) { - const { networkConfigurations } = this.networkController.state; - const { chainId } = networkConfigurations[networkConfigurationId] ?? {}; - if (!chainId) { - throw new Error('Network configuration not found'); - } - const hasOtherConfigsForChainId = Object.values(networkConfigurations).some( - (config) => - config.chainId === chainId && - config.id !== networkConfigurationId && - config.type !== networkConfigurationId, - ); - - // if this network configuration is only one for a given chainId - // remove all permissions for that chainId - if (!hasOtherConfigsForChainId) { - this.removeAllChainIdPermissions(chainId); - } - - this.networkController.removeNetworkConfiguration(networkConfigurationId); - } - /** * Stops exposing the account with the specified address to all third parties. * Exposed accounts are stored in caveats of the eth_accounts permission. This @@ -5706,15 +5715,7 @@ export default class MetamaskController extends EventEmitter { }, getChainPermissionsFeatureFlag: () => Boolean(process.env.CHAIN_PERMISSIONS), - getCurrentRpcUrl: () => - getProviderConfig({ - metamask: this.networkController.state, - }).rpcUrl, // network configuration-related - upsertNetworkConfiguration: - this.networkController.upsertNetworkConfiguration.bind( - this.networkController, - ), setActiveNetwork: async (networkClientId) => { await this.networkController.setActiveNetwork(networkClientId); // if the origin has the eth_accounts permission @@ -5731,7 +5732,16 @@ export default class MetamaskController extends EventEmitter { ); } }, - findNetworkConfigurationBy: this.findNetworkConfigurationBy.bind(this), + addNetwork: this.networkController.addNetwork.bind( + this.networkController, + ), + updateNetwork: this.networkController.updateNetwork.bind( + this.networkController, + ), + getNetworkConfigurationByChainId: + this.networkController.getNetworkConfigurationByChainId.bind( + this.networkController, + ), getCurrentChainIdForDomain: (domain) => { const networkClientId = this.selectedNetworkController.getNetworkClientIdForDomain(domain); @@ -6383,26 +6393,6 @@ export default class MetamaskController extends EventEmitter { // CONFIG //============================================================================= - /** - * Returns the first network configuration object that matches at least one field of the - * provided search criteria. Returns null if no match is found - * - * @param {object} rpcInfo - The RPC endpoint properties and values to check. - * @returns {object} rpcInfo found in the network configurations list - */ - findNetworkConfigurationBy(rpcInfo) { - const { networkConfigurations } = this.networkController.state; - const networkConfiguration = Object.values(networkConfigurations).find( - (configuration) => { - return Object.keys(rpcInfo).some((key) => { - return configuration[key] === rpcInfo[key]; - }); - }, - ); - - return networkConfiguration || null; - } - /** * Sets the Ledger Live preference to use for Ledger hardware wallet support * @@ -6535,9 +6525,9 @@ export default class MetamaskController extends EventEmitter { } }; - updateNetworksList = (sortedNetworkList) => { + updateNetworksList = (chainIds) => { try { - this.networkOrderController.updateNetworksList(sortedNetworkList); + this.networkOrderController.updateNetworksList(chainIds); } catch (err) { log.error(err.message); throw err; @@ -6686,13 +6676,15 @@ export default class MetamaskController extends EventEmitter { let rpcPrefs = {}; if (chainId) { - const { networkConfigurations } = this.networkController.state; + const networkConfiguration = + this.networkController.state.networkConfigurationsByChainId?.[chainId]; - const matchingNetworkConfig = Object.values(networkConfigurations).find( - (networkConfiguration) => networkConfiguration.chainId === chainId, - ); + const blockExplorerUrl = + networkConfiguration?.blockExplorerUrls?.[ + networkConfiguration?.defaultBlockExplorerUrlIndex + ]; - rpcPrefs = matchingNetworkConfig?.rpcPrefs ?? {}; + rpcPrefs = { blockExplorerUrl }; } try { diff --git a/app/scripts/metamask-controller.test.js b/app/scripts/metamask-controller.test.js index 401f95a21990..4121160a45af 100644 --- a/app/scripts/metamask-controller.test.js +++ b/app/scripts/metamask-controller.test.js @@ -223,11 +223,9 @@ const NOTIFICATION_ID = 'NHL8f2eSSTn9TKBamRLiU'; const ALT_MAINNET_RPC_URL = 'http://localhost:8545'; const POLYGON_RPC_URL = 'https://polygon.llamarpc.com'; -const POLYGON_RPC_URL_2 = 'https://polygon-rpc.com'; const NETWORK_CONFIGURATION_ID_1 = 'networkConfigurationId1'; const NETWORK_CONFIGURATION_ID_2 = 'networkConfigurationId2'; -const NETWORK_CONFIGURATION_ID_3 = 'networkConfigurationId3'; const ETH = 'ETH'; const MATIC = 'MATIC'; @@ -261,14 +259,6 @@ const firstTimeState = { id: NETWORK_CONFIGURATION_ID_2, blockExplorerUrl: undefined, }, - { - rpcUrl: POLYGON_RPC_URL_2, - chainId: POLYGON_CHAIN_ID, - ticker: MATIC, - nickname: 'Alt Polygon', - id: NETWORK_CONFIGURATION_ID_3, - blockExplorerUrl: undefined, - }, ), }, NotificationController: { @@ -2047,87 +2037,6 @@ describe('MetaMaskController', () => { expect(tokenDetails.symbol).toStrictEqual(tokenData.symbol); expect(tokenDetails.balance).toStrictEqual(tokenData.balance); }); - - describe('findNetworkConfigurationBy', () => { - it('returns null if passed an object containing a valid networkConfiguration key but no matching value is found', () => { - expect( - metamaskController.findNetworkConfigurationBy({ - chainId: '0xnone', - }), - ).toStrictEqual(null); - }); - it('returns null if passed an object containing an invalid networkConfiguration key', () => { - expect( - metamaskController.findNetworkConfigurationBy({ - invalidKey: '0xnone', - }), - ).toStrictEqual(null); - }); - - it('returns matching networkConfiguration when passed a chainId that matches an existing configuration', () => { - expect( - metamaskController.findNetworkConfigurationBy({ - chainId: MAINNET_CHAIN_ID, - }), - ).toStrictEqual({ - chainId: MAINNET_CHAIN_ID, - nickname: 'Alt Mainnet', - id: NETWORK_CONFIGURATION_ID_1, - rpcUrl: ALT_MAINNET_RPC_URL, - ticker: ETH, - }); - }); - - it('returns matching networkConfiguration when passed a ticker that matches an existing configuration', () => { - expect( - metamaskController.findNetworkConfigurationBy({ - ticker: MATIC, - }), - ).toStrictEqual({ - rpcUrl: POLYGON_RPC_URL, - chainId: POLYGON_CHAIN_ID, - ticker: MATIC, - nickname: 'Polygon', - id: NETWORK_CONFIGURATION_ID_2, - }); - }); - - it('returns matching networkConfiguration when passed a nickname that matches an existing configuration', () => { - expect( - metamaskController.findNetworkConfigurationBy({ - nickname: 'Alt Mainnet', - }), - ).toStrictEqual({ - chainId: MAINNET_CHAIN_ID, - nickname: 'Alt Mainnet', - id: NETWORK_CONFIGURATION_ID_1, - rpcUrl: ALT_MAINNET_RPC_URL, - ticker: ETH, - }); - }); - - it('returns null if passed an object containing mismatched networkConfiguration key/value combination', () => { - expect( - metamaskController.findNetworkConfigurationBy({ - nickname: MAINNET_CHAIN_ID, - }), - ).toStrictEqual(null); - }); - - it('returns the first networkConfiguration added if passed an key/value combination for which there are multiple matching configurations', () => { - expect( - metamaskController.findNetworkConfigurationBy({ - chainId: POLYGON_CHAIN_ID, - }), - ).toStrictEqual({ - rpcUrl: POLYGON_RPC_URL, - chainId: POLYGON_CHAIN_ID, - ticker: MATIC, - nickname: 'Polygon', - id: NETWORK_CONFIGURATION_ID_2, - }); - }); - }); }); describe('getTokenSymbol', () => { diff --git a/app/scripts/migrations/127.test.ts b/app/scripts/migrations/127.test.ts new file mode 100644 index 000000000000..b7735de831af --- /dev/null +++ b/app/scripts/migrations/127.test.ts @@ -0,0 +1,1263 @@ +import { migrate, version } from './127'; + +const oldVersion = 126; + +const sentryCaptureExceptionMock = jest.fn(); +global.sentry = { + captureException: sentryCaptureExceptionMock, +}; + +describe(`migration #${version}`, () => { + afterEach(() => jest.resetAllMocks()); + + it('updates the version metadata', async () => { + const oldState = { + meta: { version: oldVersion }, + data: {}, + }; + + const newState = await migrate(oldState); + expect(newState.meta).toStrictEqual({ version }); + }); + + it('captures an exception if the network controller state is not defined', async () => { + const oldState = { + meta: { version: oldVersion }, + data: {}, + }; + + await migrate(oldState); + expect(sentryCaptureExceptionMock).toHaveBeenCalledTimes(1); + expect(sentryCaptureExceptionMock).toHaveBeenCalledWith( + new Error(`state.NetworkController is not defined`), + ); + }); + + it('captures an exception if the network controller state is not an object', async () => { + for (const NetworkController of [undefined, null, 1, 'foo']) { + const oldState = { + meta: { version: oldVersion }, + data: { NetworkController }, + }; + + await migrate(oldState); + expect(sentryCaptureExceptionMock).toHaveBeenCalledTimes(1); + expect(sentryCaptureExceptionMock).toHaveBeenCalledWith( + new Error( + `typeof state.NetworkController is ${typeof NetworkController}`, + ), + ); + sentryCaptureExceptionMock.mockClear(); + } + }); + + it('captures an exception if the transaction controller state is not defined', async () => { + const oldState = { + meta: { version: oldVersion }, + data: { NetworkController: {} }, + }; + + await migrate(oldState); + expect(sentryCaptureExceptionMock).toHaveBeenCalledTimes(1); + expect(sentryCaptureExceptionMock).toHaveBeenCalledWith( + new Error(`state.TransactionController is not defined`), + ); + }); + + it('captures an exception if the transaction controller state is not an object', async () => { + for (const TransactionController of [undefined, null, 1, 'foo']) { + const oldState = { + meta: { version: oldVersion }, + data: { NetworkController: {}, TransactionController }, + }; + + await migrate(oldState); + expect(sentryCaptureExceptionMock).toHaveBeenCalledTimes(1); + expect(sentryCaptureExceptionMock).toHaveBeenCalledWith( + new Error( + `typeof state.TransactionController is ${typeof TransactionController}`, + ), + ); + sentryCaptureExceptionMock.mockClear(); + } + }); + + it('migrates a custom network on a built-in chain', async () => { + const customNetwork = { + id: 'network-configuration-id', + chainId: '0x1', + nickname: 'My Local Node', + ticker: 'ETH', + rpcUrl: 'https://localhost/rpc', + rpcPrefs: { + blockExplorerUrl: 'https://localhost/explorer', + }, + }; + + const oldState = { + meta: { version: oldVersion }, + data: { + TransactionController: {}, + NetworkController: { + selectedNetworkClientId: 'mainnet', + networkConfigurations: { + [customNetwork.id]: customNetwork, + }, + }, + }, + }; + + const defaultStateToExpect = defaultPostMigrationState(); + const expectedNetwork = { + ...defaultStateToExpect.networkConfigurationsByChainId[ + customNetwork.chainId + ], + }; + + // The custom network's rpc url should be added to the existing network + expectedNetwork.rpcEndpoints.push({ + networkClientId: customNetwork.id, + name: customNetwork.nickname, + url: customNetwork.rpcUrl, + type: 'custom', + }); + + // The custom network's block explorer should be added to the existing network + expectedNetwork.blockExplorerUrls.push( + customNetwork.rpcPrefs.blockExplorerUrl, + ); + + const expectedState = { + ...defaultStateToExpect, + networkConfigurationsByChainId: { + ...defaultStateToExpect.networkConfigurationsByChainId, + [customNetwork.chainId]: expectedNetwork, + }, + }; + + const newState = await migrate(oldState); + expect(newState.data.NetworkController).toStrictEqual(expectedState); + }); + + it('migrates a custom network on a not built-in chain', async () => { + const customNetwork = { + id: 'network-configuration-id', + chainId: '0x123456789', + nickname: 'My Random Network', + ticker: 'FOO', + rpcUrl: 'https://localhost/rpc', + rpcPrefs: { + blockExplorerUrl: 'https://localhost/explorer', + }, + }; + + const oldState = { + meta: { version: oldVersion }, + data: { + TransactionController: {}, + NetworkController: { + selectedNetworkClientId: 'mainnet', + networkConfigurations: { + [customNetwork.id]: customNetwork, + }, + }, + }, + }; + + const expectedState = defaultPostMigrationState(); + + // A new network should be added + expectedState.networkConfigurationsByChainId[customNetwork.chainId] = { + chainId: customNetwork.chainId, + name: customNetwork.nickname, + nativeCurrency: customNetwork.ticker, + rpcEndpoints: [ + { + name: customNetwork.nickname, + networkClientId: customNetwork.id, + url: customNetwork.rpcUrl, + type: 'custom', + }, + ], + defaultRpcEndpointIndex: 0, + blockExplorerUrls: [customNetwork.rpcPrefs.blockExplorerUrl], + defaultBlockExplorerUrlIndex: 0, + }; + + const newState = await migrate(oldState); + expect(newState.data.NetworkController).toStrictEqual(expectedState); + }); + + it('tie breaks with the globally selected network', async () => { + const customNetwork = { + id: 'network-configuration-id', + chainId: '0x1', + nickname: 'My Local Node', + ticker: 'FOO', + rpcUrl: 'https://localhost/rpc', + rpcPrefs: { + blockExplorerUrl: 'https://localhost/explorer', + }, + }; + + const oldState = { + meta: { version: oldVersion }, + data: { + TransactionController: {}, + NetworkController: { + selectedNetworkClientId: customNetwork.id, // make it the selected network + networkConfigurations: { + [customNetwork.id]: customNetwork, + }, + }, + }, + }; + + const defaultStateToExpect = defaultPostMigrationState(); + const expectedNetwork = { + ...defaultStateToExpect.networkConfigurationsByChainId[ + customNetwork.chainId + ], + }; + + // The custom network should become the default RPC url + expectedNetwork.defaultRpcEndpointIndex = + expectedNetwork.rpcEndpoints.push({ + networkClientId: customNetwork.id, + name: customNetwork.nickname, + url: customNetwork.rpcUrl, + type: 'custom', + }) - 1; + + // The custom network's block explorer should be added to + // the existing network, and it should become the default. + expectedNetwork.defaultBlockExplorerUrlIndex = + expectedNetwork.blockExplorerUrls.push( + customNetwork.rpcPrefs.blockExplorerUrl, + ) - 1; + + const expectedState = { + ...defaultStateToExpect, + // The custom network should remain selected + selectedNetworkClientId: customNetwork.id, + networkConfigurationsByChainId: { + ...defaultStateToExpect.networkConfigurationsByChainId, + [customNetwork.chainId]: expectedNetwork, + }, + }; + + const newState = await migrate(oldState); + expect(newState.data.NetworkController).toStrictEqual(expectedState); + }); + + it('tie breaks with the most recently transacted network', async () => { + const customNetwork = { + id: 'network-configuration-id', + chainId: '0x1', + nickname: 'My Local Node', + ticker: 'FOO', + rpcUrl: 'https://localhost/rpc', + rpcPrefs: { + blockExplorerUrl: 'https://localhost/explorer', + }, + }; + + const oldState = { + meta: { version: oldVersion }, + data: { + TransactionController: { + transactions: [ + { + chainId: customNetwork.chainId, + time: 1, + networkClientId: 'mainnet', + }, + { + chainId: customNetwork.chainId, + time: 2, + networkClientId: customNetwork.id, + }, + ], + }, + NetworkController: { + selectedNetworkClientId: 'sepolia', + networkConfigurations: { + [customNetwork.id]: customNetwork, + }, + }, + }, + }; + + const defaultStateToExpect = defaultPostMigrationState(); + const expectedNetwork = { + ...defaultStateToExpect.networkConfigurationsByChainId[ + customNetwork.chainId + ], + }; + + // The custom network's rpc url should be added to the + // existing network, and become the default RPC url + expectedNetwork.defaultRpcEndpointIndex = + expectedNetwork.rpcEndpoints.push({ + networkClientId: customNetwork.id, + name: customNetwork.nickname, + url: customNetwork.rpcUrl, + type: 'custom', + }) - 1; + + // The custom network's block explorer should be added to + // the existing network, and it should become the default. + expectedNetwork.defaultBlockExplorerUrlIndex = + expectedNetwork.blockExplorerUrls.push( + customNetwork.rpcPrefs.blockExplorerUrl, + ) - 1; + + const expectedState = { + ...defaultStateToExpect, + // Selected network shouldn't change + selectedNetworkClientId: + oldState.data.NetworkController.selectedNetworkClientId, + networkConfigurationsByChainId: { + ...defaultStateToExpect.networkConfigurationsByChainId, + [customNetwork.chainId]: expectedNetwork, + }, + }; + + const newState = await migrate(oldState); + expect(newState.data.NetworkController).toStrictEqual(expectedState); + }); + + it('tie breaks with the custom network that is not built in infura', async () => { + const customNetwork = { + id: 'network-configuration-id', + chainId: '0x1', + nickname: 'My Local Node', + ticker: 'FOO', + rpcUrl: 'https://localhost/rpc', + rpcPrefs: { + blockExplorerUrl: 'https://localhost/explorer', + }, + }; + + const oldState = { + meta: { version: oldVersion }, + data: { + TransactionController: {}, + NetworkController: { + selectedNetworkClientId: 'sepolia', + networkConfigurations: { + [customNetwork.id]: customNetwork, + }, + }, + }, + }; + + const defaultStateToExpect = defaultPostMigrationState(); + const expectedNetwork = { + ...defaultStateToExpect.networkConfigurationsByChainId[ + customNetwork.chainId + ], + }; + + // The custom network should become the default RPC url + expectedNetwork.defaultRpcEndpointIndex = + expectedNetwork.rpcEndpoints.push({ + networkClientId: customNetwork.id, + name: customNetwork.nickname, + url: customNetwork.rpcUrl, + type: 'custom', + }) - 1; + + // The custom network's block explorer should be added to + // the existing network, and it should become the default. + expectedNetwork.defaultBlockExplorerUrlIndex = + expectedNetwork.blockExplorerUrls.push( + customNetwork.rpcPrefs.blockExplorerUrl, + ) - 1; + + const expectedState = { + ...defaultStateToExpect, + selectedNetworkClientId: + oldState.data.NetworkController.selectedNetworkClientId, + networkConfigurationsByChainId: { + ...defaultStateToExpect.networkConfigurationsByChainId, + [customNetwork.chainId]: expectedNetwork, + }, + }; + + const newState = await migrate(oldState); + expect(newState.data.NetworkController).toStrictEqual(expectedState); + }); + + it('dedupes if there are multiple block explorers within a chain id', async () => { + const randomChainId = '0x123456'; + + const oldState = { + meta: { version: oldVersion }, + data: { + TransactionController: {}, + NetworkController: { + selectedNetworkClientId: 'sepolia', + networkConfigurations: { + 'network-id-1': { + id: 'network-id-1', + chainId: randomChainId, + nickname: 'Random Network', + ticker: 'FOO', + rpcUrl: 'https://localhost/rpc/1', + rpcPrefs: { + blockExplorerUrl: 'https://localhost/explorer', + }, + }, + 'network-id-2': { + id: 'network-id-2', + chainId: randomChainId, + nickname: 'Random Network', + ticker: 'FOO', + rpcUrl: 'https://localhost/rpc/2', + rpcPrefs: { + blockExplorerUrl: 'https://localhost/explorer', + }, + }, + }, + }, + }, + }; + + const newState = await migrate(oldState); + + const { networkConfigurationsByChainId } = newState.data + .NetworkController as { + networkConfigurationsByChainId: Record< + string, + { + defaultBlockExplorerUrlIndex: number; + blockExplorerUrls: string[]; + } + >; + }; + + const networkConfiguration = networkConfigurationsByChainId[randomChainId]; + expect(networkConfiguration.defaultBlockExplorerUrlIndex).toStrictEqual(0); + expect(networkConfiguration.blockExplorerUrls).toStrictEqual([ + 'https://localhost/explorer', + ]); + }); + + it('dedupes if there are duplicate rpc urls within a chain id, and none are the selected network', async () => { + for (const [url1, url2] of [ + ['http://test.endpoint/bar', 'http://test.endpoint/bar'], + // Check case insensitivity (network controller requires this) + ['http://test.endpoint/bar', 'HTTP://TEST.ENDPOINT/bar'], + ]) { + const randomChainId = '0x123456'; + + const oldState = { + meta: { version: oldVersion }, + data: { + TransactionController: {}, + NetworkController: { + selectedNetworkClientId: 'sepolia', + networkConfigurations: { + 'network-id-1': { + id: 'network-id-1', + chainId: randomChainId, + nickname: 'Random Network', + ticker: 'FOO', + rpcUrl: url1, + }, + 'network-id-2': { + id: 'network-id-2', + chainId: randomChainId, + nickname: 'Random Network', + ticker: 'FOO', + rpcUrl: url2, + }, + }, + }, + }, + }; + + const newState = await migrate(oldState); + + const { networkConfigurationsByChainId } = newState.data + .NetworkController as { + networkConfigurationsByChainId: Record< + string, + { + defaultRpcEndpointIndex: number; + rpcEndpoints: { + url: string; + type: string; + name: string; + networkClientId: string; + }[]; + } + >; + }; + + const networkConfiguration = + networkConfigurationsByChainId[randomChainId]; + expect(networkConfiguration.defaultRpcEndpointIndex).toStrictEqual(0); + + // The first duplicate encountered should be used to tie break + // duplicate endpoints, since none were the selected network + expect(networkConfiguration.rpcEndpoints).toStrictEqual([ + { + url: url1, + type: 'custom', + name: 'Random Network', + networkClientId: 'network-id-1', + }, + ]); + } + }); + + it('dedupes if there are duplicate rpc urls within a chain id, and one is the selected network', async () => { + for (const [url1, url2] of [ + ['http://test.endpoint/bar', 'http://test.endpoint/bar'], + // Check case insensitivity (network controller requires this) + ['http://test.endpoint/bar', 'HTTP://TEST.ENDPOINT/bar'], + ]) { + const randomChainId = '0x123456'; + + const oldState = { + meta: { version: oldVersion }, + data: { + TransactionController: {}, + NetworkController: { + selectedNetworkClientId: 'network-id-2', + networkConfigurations: { + 'network-id-1': { + id: 'network-id-1', + chainId: randomChainId, + nickname: 'Random Network', + ticker: 'FOO', + rpcUrl: url1, + }, + // I'm the selected network and should be + // used to tie break duplicate endpoints + 'network-id-2': { + id: 'network-id-2', + chainId: randomChainId, + nickname: 'Random Network', + ticker: 'FOO', + rpcUrl: url2, + }, + }, + }, + }, + }; + + const newState = await migrate(oldState); + + const { networkConfigurationsByChainId } = newState.data + .NetworkController as { + networkConfigurationsByChainId: Record< + string, + { + defaultRpcEndpointIndex: number; + rpcEndpoints: { + url: string; + type: string; + name: string; + networkClientId: string; + }[]; + } + >; + }; + + const networkConfiguration = + networkConfigurationsByChainId[randomChainId]; + expect(networkConfiguration.defaultRpcEndpointIndex).toStrictEqual(0); + expect(networkConfiguration.rpcEndpoints).toStrictEqual([ + { + url: url2, + type: 'custom', + name: 'Random Network', + networkClientId: 'network-id-2', + }, + ]); + } + }); + + it('dedupes if there are duplicate rpc urls across chain ids and none are the selected network', async () => { + const randomChainId1 = '0x123456'; + const randomChainId2 = '0x123456789'; + + const oldState = { + meta: { version: oldVersion }, + data: { + TransactionController: {}, + NetworkController: { + selectedNetworkClientId: 'mainnet', + networkConfigurations: { + // I'm the first encountered duplicate endpoint and should + // be used to tie break, since none are the selected network + 'network-id-1': { + id: 'network-id-1', + chainId: randomChainId1, + nickname: 'Random Network', + ticker: 'FOO', + rpcUrl: 'http://localhost/rpc', + rpcPrefs: { + blockExplorerUrl: 'https://localhost/explorer', + }, + }, + // I'm the duplicate endpoint that lost the tie break and should be omitted + 'network-id-2': { + id: 'network-id-2', + chainId: randomChainId2, + nickname: 'Random Network', + ticker: 'FOO', + rpcUrl: 'http://localhost/rpc', + rpcPrefs: { + blockExplorerUrl: 'https://localhost/explorer', + }, + }, + 'network-id-3': { + id: 'network-id-3', + chainId: randomChainId2, + nickname: 'Random Network', + ticker: 'FOO', + rpcUrl: 'http://localhost/rpc/different', + rpcPrefs: { + blockExplorerUrl: 'https://localhost/explorer', + }, + }, + }, + }, + }, + }; + + const newState = await migrate(oldState); + const expectedState = defaultPostMigrationState(); + expectedState.networkConfigurationsByChainId[randomChainId1] = { + chainId: randomChainId1, + rpcEndpoints: [ + { + networkClientId: 'network-id-1', + url: 'http://localhost/rpc', + type: 'custom', + name: 'Random Network', + }, + ], + defaultRpcEndpointIndex: 0, + blockExplorerUrls: ['https://localhost/explorer'], + defaultBlockExplorerUrlIndex: 0, + name: 'Random Network', + nativeCurrency: 'FOO', + }; + + expectedState.networkConfigurationsByChainId[randomChainId2] = { + blockExplorerUrls: ['https://localhost/explorer'], + defaultBlockExplorerUrlIndex: 0, + chainId: '0x123456789', + defaultRpcEndpointIndex: 0, + name: 'Random Network', + nativeCurrency: 'FOO', + rpcEndpoints: [ + { + name: 'Random Network', + networkClientId: 'network-id-3', + type: 'custom', + url: 'http://localhost/rpc/different', + }, + ], + }; + + expect(newState.data.NetworkController).toStrictEqual(expectedState); + }); + + it('dedupes if there are duplicate rpc urls across chain ids and one is the selected network', async () => { + const randomChainId1 = '0x123456'; + const randomChainId2 = '0x123456789'; + + const oldState = { + meta: { version: oldVersion }, + data: { + TransactionController: {}, + NetworkController: { + selectedNetworkClientId: 'network-id-2', + networkConfigurations: { + // This endpoint is duplicated but not the selected network, and should be omitted + 'network-id-1': { + id: 'network-id-1', + chainId: randomChainId1, + nickname: 'Random Network', + ticker: 'FOO', + rpcUrl: 'http://localhost/rpc', + rpcPrefs: { + blockExplorerUrl: 'https://localhost/explorer', + }, + }, + // This is the selected network and should tie break duplicate endpoints + 'network-id-2': { + id: 'network-id-2', + chainId: randomChainId2, + nickname: 'Random Network', + ticker: 'FOO', + rpcUrl: 'http://localhost/rpc', + rpcPrefs: { + blockExplorerUrl: 'https://localhost/explorer', + }, + }, + // I'm just an extra unique endpoint that should stay + 'network-id-3': { + id: 'network-id-3', + chainId: randomChainId2, + nickname: 'Random Network', + ticker: 'FOO', + rpcUrl: 'http://localhost/rpc/different', + rpcPrefs: { + blockExplorerUrl: 'https://localhost/explorer', + }, + }, + }, + }, + }, + }; + + const newState = await migrate(oldState); + const expectedState = defaultPostMigrationState(); + expectedState.selectedNetworkClientId = + oldState.data.NetworkController.selectedNetworkClientId; + expectedState.networkConfigurationsByChainId[randomChainId2] = { + chainId: randomChainId2, + rpcEndpoints: [ + { + networkClientId: 'network-id-2', + url: 'http://localhost/rpc', + type: 'custom', + name: 'Random Network', + }, + { + networkClientId: 'network-id-3', + url: 'http://localhost/rpc/different', + type: 'custom', + name: 'Random Network', + }, + ], + defaultRpcEndpointIndex: 0, + blockExplorerUrls: ['https://localhost/explorer'], + defaultBlockExplorerUrlIndex: 0, + name: 'Random Network', + nativeCurrency: 'FOO', + }; + + expect(newState.data.NetworkController).toStrictEqual(expectedState); + }); + + it('handles not well formed rpc urls', async () => { + const randomChainId = '0x123456'; + + const oldState = { + meta: { version: oldVersion }, + data: { + TransactionController: {}, + NetworkController: { + selectedNetworkClientId: 'sepolia', + networkConfigurations: { + 'network-id-1': { + id: 'network-id-1', + chainId: randomChainId, + nickname: 'Random Network', + ticker: 'FOO', + rpcUrl: 'http://test.endpoint/bar', + }, + // This RPC url is not well formed and should be omitted + 'network-id-2': { + id: 'network-id-2', + chainId: randomChainId, + nickname: 'Random Network', + ticker: 'FOO', + rpcUrl: 'not_well_formed', + }, + }, + }, + }, + }; + + const newState = await migrate(oldState); + + const { networkConfigurationsByChainId } = newState.data + .NetworkController as { + networkConfigurationsByChainId: Record< + string, + { + defaultRpcEndpointIndex: number; + rpcEndpoints: { + url: string; + type: string; + name: string; + networkClientId: string; + }[]; + } + >; + }; + + const networkConfiguration = networkConfigurationsByChainId[randomChainId]; + expect(networkConfiguration.defaultRpcEndpointIndex).toStrictEqual(0); + expect(networkConfiguration.rpcEndpoints).toStrictEqual([ + { + url: 'http://test.endpoint/bar', + type: 'custom', + name: 'Random Network', + networkClientId: 'network-id-1', + }, + ]); + }); + + it('handles the case where no rpc url is well formed', async () => { + const randomChainId = '0x123456'; + + const oldState = { + meta: { version: oldVersion }, + data: { + TransactionController: {}, + NetworkController: { + selectedNetworkClientId: 'mainnet', + networkConfigurations: { + 'network-id-1': { + id: 'network-id-1', + chainId: randomChainId, + nickname: 'Random Network', + ticker: 'FOO', + rpcUrl: 'not_well_formed', + }, + 'network-id-2': { + id: 'network-id-2', + chainId: randomChainId, + nickname: 'Random Network', + ticker: 'FOO', + rpcUrl: 'also_not_well_formed', + }, + }, + }, + }, + }; + + const newState = await migrate(oldState); + + // `randomChainId` had no well formed urls, so it should be omitted + expect(newState.data.NetworkController).toStrictEqual( + defaultPostMigrationState(), + ); + }); + + it('handles the case where selectedNetworkClientId doesnt point to any endpoint', async () => { + const oldState = { + meta: { version: oldVersion }, + data: { + TransactionController: {}, + NetworkController: { + selectedNetworkClientId: 'dont-point-to-anything', + }, + }, + }; + + const newState = await migrate(oldState); + + // selectedNetworkClientId should fall back to mainnet + expect(newState.data.NetworkController).toStrictEqual( + defaultPostMigrationState(), + ); + }); + + it('handles the case where selectedNetworkClientId doesnt point to any endpoint and a custom endpoint is the default', async () => { + const oldState = { + meta: { version: oldVersion }, + data: { + NetworkController: { + selectedNetworkClientId: 'dont-point-to-anything', + networkConfigurations: { + 'custom-mainnet': { + id: 'custom-mainnet', + chainId: '0x1', + nickname: 'Custom Mainnet', + ticker: 'ETH', + rpcUrl: 'http://localhost/rpc', + }, + }, + }, + TransactionController: { + transactions: [ + { + chainId: '0x1', + time: 1, + networkClientId: 'custom-mainnet', + }, + ], + }, + }, + }; + + const newState = await migrate(oldState); + + // selectedNetworkClientId should fall back to custom mainnet + expect( + (newState.data.NetworkController as { selectedNetworkClientId: string }) + .selectedNetworkClientId, + ).toStrictEqual('custom-mainnet'); + }); + + it('handles the case where selectedNetworkClientId doesnt point to a valid endpoint and theres no fallback', async () => { + const randomChainId = '0x123456'; + + const oldState = { + meta: { version: oldVersion }, + data: { + NetworkController: { + selectedNetworkClientId: 'invalid-url', + networkConfigurations: + // The selected network client has an invaid url + { + 'invalid-url': { + id: 'invalid-url', + chainId: randomChainId, + nickname: 'Random Chain', + ticker: 'ETH', + rpcUrl: 'foobar', + }, + // And there are no other configurations on the same chain to fall back to + }, + }, + TransactionController: {}, + }, + }; + + const newState = await migrate(oldState); + + // Fall back to mainnet + expect( + (newState.data.NetworkController as { selectedNetworkClientId: string }) + .selectedNetworkClientId, + ).toStrictEqual('mainnet'); + }); + + it('handles the case where selectedNetworkClientId doesnt point to a valid endpoint but theres a fallback', async () => { + const randomChainId = '0x123456'; + + const oldState = { + meta: { version: oldVersion }, + data: { + NetworkController: { + selectedNetworkClientId: 'invalid-url', + networkConfigurations: + // The selected network client has an invaid url + { + 'invalid-url': { + id: 'invalid-url', + chainId: randomChainId, + nickname: 'Random Chain', + ticker: 'ETH', + rpcUrl: 'foobar', + }, + // So it should fall back to me, on the same chain + 'pick-me': { + id: 'pick-me', + chainId: randomChainId, + nickname: 'Random Chain', + ticker: 'ETH', + rpcUrl: 'http://localhost/rpc', + }, + }, + }, + TransactionController: {}, + }, + }; + + const newState = await migrate(oldState); + + expect( + (newState.data.NetworkController as { selectedNetworkClientId: string }) + .selectedNetworkClientId, + ).toStrictEqual('pick-me'); + }); + + it('migrates the netork order controller', async () => { + const oldState = { + meta: { version: oldVersion }, + data: { + NetworkController: {}, + TransactionController: {}, + NetworkOrderController: { + orderedNetworkList: [ + { + networkId: '0x1', + networkRpcUrl: 'http://localhost/a', + }, + { + networkId: '0x2', + networkRpcUrl: 'http://localhost/b', + }, + { + networkId: '0x3', + networkRpcUrl: 'http://localhost/c', + }, + { + networkId: '0x2', + networkRpcUrl: 'http://localhost/d', + }, + { + networkId: '0x1', + networkRpcUrl: 'http://localhost/e', + }, + ], + }, + }, + }; + + // Expect chain IDs to maintain order, but deduped and with `networkRpcUrl` removed + const newState = await migrate(oldState); + expect(newState.data.NetworkOrderController).toStrictEqual({ + orderedNetworkList: [ + { networkId: '0x1' }, + { networkId: '0x2' }, + { networkId: '0x3' }, + ], + }); + }); + + it('sets `preferences.showMultiRpcModal` to false when there are no networks with multiple endpoints', async () => { + const randomChainId = '0x123456'; + + const oldState = { + meta: { version: oldVersion }, + data: { + TransactionController: {}, + PreferencesController: { preferences: {} }, + NetworkController: { + selectedNetworkClientId: 'mainnet', + networkConfigurations: { + 'network-id-1': { + id: 'network-id-1', + chainId: randomChainId, + nickname: 'Random Network', + ticker: 'FOO', + rpcUrl: 'https://localhost/rpc', + }, + }, + }, + }, + }; + + const newState = await migrate(oldState); + expect(newState.data.PreferencesController).toStrictEqual({ + preferences: { showMultiRpcModal: false }, + }); + }); + + it('sets `preferences.showMultiRpcModal` to true when there are networks with multiple endpoints', async () => { + const randomChainId = '0x123456'; + + const oldState = { + meta: { version: oldVersion }, + data: { + TransactionController: {}, + PreferencesController: { preferences: {} }, + NetworkController: { + selectedNetworkClientId: 'mainnet', + networkConfigurations: { + 'network-id-1': { + id: 'network-id-1', + chainId: randomChainId, + nickname: 'Ethereum Network', + ticker: 'ETH', + rpcUrl: 'https://localhost/rpc/1', + }, + 'network-id-2': { + id: 'network-id-2', + chainId: randomChainId, + nickname: 'Random Network', + ticker: 'FOO', + rpcUrl: 'https://localhost/rpc/2', + }, + }, + }, + }, + }; + + const newState = await migrate(oldState); + expect(newState.data.PreferencesController).toStrictEqual({ + preferences: { showMultiRpcModal: true }, + }); + }); + + it('updates the selected network controller to remove stale network client ids', async () => { + const randomChainId = '0x123456'; + + const oldState = { + meta: { version: oldVersion }, + data: { + TransactionController: {}, + SelectedNetworkController: { + domains: { + // I should stay in the selected network controller + 'normal.com': 'normal-network-id', + // I should be removed, as I never pointed to a network + 'neverexisted.com': 'never-existed-network-id', + }, + }, + NetworkController: { + selectedNetworkClientId: 'mainnet', + networkConfigurations: { + 'normal-network-id': { + id: 'normal-network-id', + chainId: randomChainId, + nickname: 'Normal Network', + ticker: 'TICK', + rpcUrl: 'https://localhost/rpc', + }, + }, + }, + }, + }; + + const newState = await migrate(oldState); + expect(newState.data.SelectedNetworkController).toStrictEqual({ + domains: { 'normal.com': 'normal-network-id' }, + }); + }); +}); + +it('updates the selected network controller to point domains to the default RPC endpoint', async () => { + const untouchedChainId = '0x123'; + const redirectedChainId = '0x456'; + + const oldState = { + meta: { version: oldVersion }, + data: { + TransactionController: {}, + SelectedNetworkController: { + domains: { + 'untouched.com': 'untouched-network-id', + 'already-default.com': 'already-default-network-id', + 'redirected.com': 'redirected-network-id', + // Test the case where it pointed to a built in + // network, which would not have been in state before + 'mainnet.com': 'mainnet', + }, + }, + NetworkController: { + selectedNetworkClientId: 'already-default-network-id', + networkConfigurations: { + 'untouched-network-id': { + id: 'untouched-network-id', + chainId: untouchedChainId, + nickname: 'Untouched Network', + ticker: 'TICK', + rpcUrl: 'https://localhost/rpc/1', + }, + 'already-default-network-id': { + id: 'already-default-network-id', + chainId: redirectedChainId, + nickname: 'Default Network', + ticker: 'TICK', + rpcUrl: 'https://localhost/rpc/2', + }, + 'redirected-network-id': { + id: 'redirected-network-id', + chainId: redirectedChainId, + nickname: 'Redirected Network', + ticker: 'TICK', + rpcUrl: 'https://localhost/rpc/3', + }, + }, + }, + }, + }; + + const newState = await migrate(oldState); + expect(newState.data.SelectedNetworkController).toStrictEqual({ + domains: { + 'untouched.com': 'untouched-network-id', + 'already-default.com': 'already-default-network-id', + 'redirected.com': 'already-default-network-id', + 'mainnet.com': 'mainnet', + }, + }); +}); + +// The state of the network controller post migration for just the +// built-in networks. As if there were no custom networks defined. +function defaultPostMigrationState() { + const state = { + selectedNetworkClientId: 'mainnet', + networksMetadata: {}, + networkConfigurationsByChainId: { + '0x1': { + chainId: '0x1', + rpcEndpoints: [ + { + networkClientId: 'mainnet', + url: 'https://mainnet.infura.io/v3/{infuraProjectId}', + type: 'infura', + }, + ], + defaultRpcEndpointIndex: 0, + blockExplorerUrls: ['https://etherscan.io'], + defaultBlockExplorerUrlIndex: 0, + name: 'Ethereum Mainnet', + nativeCurrency: 'ETH', + }, + '0xaa36a7': { + chainId: '0xaa36a7', + rpcEndpoints: [ + { + networkClientId: 'sepolia', + url: 'https://sepolia.infura.io/v3/{infuraProjectId}', + type: 'infura', + }, + ], + defaultRpcEndpointIndex: 0, + blockExplorerUrls: ['https://sepolia.etherscan.io'], + defaultBlockExplorerUrlIndex: 0, + name: 'Sepolia', + nativeCurrency: 'SepoliaETH', + }, + '0xe705': { + chainId: '0xe705', + rpcEndpoints: [ + { + networkClientId: 'linea-sepolia', + url: 'https://linea-sepolia.infura.io/v3/{infuraProjectId}', + type: 'infura', + }, + ], + defaultRpcEndpointIndex: 0, + blockExplorerUrls: ['https://sepolia.lineascan.build'], + defaultBlockExplorerUrlIndex: 0, + name: 'Linea Sepolia', + nativeCurrency: 'LineaETH', + }, + '0xe708': { + chainId: '0xe708', + rpcEndpoints: [ + { + networkClientId: 'linea-mainnet', + url: 'https://linea-mainnet.infura.io/v3/{infuraProjectId}', + type: 'infura', + }, + ], + defaultRpcEndpointIndex: 0, + blockExplorerUrls: ['https://lineascan.build'], + defaultBlockExplorerUrlIndex: 0, + name: 'Linea Mainnet', + nativeCurrency: 'ETH', + }, + }, + }; + + // Expand types to include optional fields that + // aren't defined on the above built-in networks + type Networks = typeof state.networkConfigurationsByChainId; + type Network = Networks[keyof Networks]; + return state as typeof state & { + networkConfigurationsByChainId: { + [key: string]: Network & { + rpcEndpoints: (Network['rpcEndpoints'][number] & { name?: string })[]; + }; + }; + }; +} diff --git a/app/scripts/migrations/127.ts b/app/scripts/migrations/127.ts new file mode 100644 index 000000000000..fa5c380c6619 --- /dev/null +++ b/app/scripts/migrations/127.ts @@ -0,0 +1,453 @@ +import { hasProperty, isObject, RuntimeObject } from '@metamask/utils'; +import { cloneDeep } from 'lodash'; +// Note: This is the library the network controller uses for URL +// validity / equality. Using here to ensure we match its validations. +import * as URI from 'uri-js'; +import { + CHAIN_ID_TO_CURRENCY_SYMBOL_MAP, + LINEA_MAINNET_DISPLAY_NAME, + LINEA_SEPOLIA_DISPLAY_NAME, + MAINNET_DISPLAY_NAME, + NETWORK_TO_NAME_MAP, + SEPOLIA_DISPLAY_NAME, +} from '../../../shared/constants/network'; + +type VersionedData = { + meta: { version: number }; + data: Record; +}; + +export const version = 127; + +/** + * This migration converts the network controller's + * `networkConfigurations` to a new field `networkConfigurationsByChainId`. + * + * Built-in Infura network configurations are now represented in this state, + * where they weren't before. These Infura configurations are merged with the user's + * custom configurations. Then all configurations are grouped by chain id, + * and merged to produce one network configuration per chain id. + * + * The `SelectedNetworkController` is also migrated, so that dapp domains + * point to the new default RPC endpoint for the chain they were on. + * + * The `NetworkOrderController` is also migrated, which manages + * the user's drag + drop preference order for the network menu. + * + * @param originalVersionedData - Versioned MetaMask extension state, exactly what we persist to dist. + * @param originalVersionedData.meta - State metadata. + * @param originalVersionedData.meta.version - The current state version. + * @param originalVersionedData.data - The persisted MetaMask state, keyed by controller. + * @returns Updated versioned MetaMask extension state. + */ +export async function migrate( + originalVersionedData: VersionedData, +): Promise { + const versionedData = cloneDeep(originalVersionedData); + versionedData.meta.version = version; + transformState(versionedData.data); + return versionedData; +} + +function transformState( + state: Record, +): Record { + if (!hasProperty(state, 'NetworkController')) { + global.sentry?.captureException?.( + new Error(`state.NetworkController is not defined`), + ); + return state; + } else if (!isObject(state.NetworkController)) { + global.sentry?.captureException?.( + new Error( + `typeof state.NetworkController is ${typeof state.NetworkController}`, + ), + ); + return state; + } else if (!hasProperty(state, 'TransactionController')) { + global.sentry?.captureException?.( + new Error(`state.TransactionController is not defined`), + ); + return state; + } else if (!isObject(state.TransactionController)) { + global.sentry?.captureException?.( + new Error( + `typeof state.TransactionController is ${typeof state.TransactionController}`, + ), + ); + return state; + } + + const networkState = state.NetworkController; + + // Get the existing custom network configurations + let networkConfigurations = isObject(networkState.networkConfigurations) + ? Object.values(networkState.networkConfigurations) + : []; + + // Prepend the built-in Infura network configurations, + // since they are now included in the network controller state + networkConfigurations = [ + { + type: 'infura', + id: 'mainnet', + chainId: '0x1', + ticker: 'ETH', + nickname: MAINNET_DISPLAY_NAME, + rpcUrl: 'https://mainnet.infura.io/v3/{infuraProjectId}', + rpcPrefs: { blockExplorerUrl: 'https://etherscan.io' }, + }, + { + type: 'infura', + id: 'sepolia', + chainId: '0xaa36a7', + ticker: 'SepoliaETH', + nickname: SEPOLIA_DISPLAY_NAME, + rpcUrl: 'https://sepolia.infura.io/v3/{infuraProjectId}', + rpcPrefs: { blockExplorerUrl: 'https://sepolia.etherscan.io' }, + }, + { + type: 'infura', + id: 'linea-sepolia', + chainId: '0xe705', + ticker: 'LineaETH', + nickname: LINEA_SEPOLIA_DISPLAY_NAME, + rpcUrl: 'https://linea-sepolia.infura.io/v3/{infuraProjectId}', + rpcPrefs: { blockExplorerUrl: 'https://sepolia.lineascan.build' }, + }, + { + type: 'infura', + id: 'linea-mainnet', + chainId: '0xe708', + ticker: 'ETH', + nickname: LINEA_MAINNET_DISPLAY_NAME, + rpcUrl: 'https://linea-mainnet.infura.io/v3/{infuraProjectId}', + rpcPrefs: { blockExplorerUrl: 'https://lineascan.build' }, + }, + ...networkConfigurations, + ]; + + // Group the network configurations by by chain id, producing + // a mapping from chain id to an array of network configurations + const networkConfigurationArraysByChainId = networkConfigurations.reduce( + (acc: Record, networkConfiguration) => { + if ( + isObject(networkConfiguration) && + typeof networkConfiguration.chainId === 'string' + ) { + (acc[networkConfiguration.chainId] ??= []).push(networkConfiguration); + } + return acc; + }, + {}, + ); + + // Get transaction history in reverse chronological order to help with tie breaks + const transactions: RuntimeObject[] = Array.isArray( + state.TransactionController.transactions, + ) + ? state.TransactionController.transactions + .filter( + (tx) => + isObject(tx) && + typeof tx.time === 'number' && + typeof tx.networkClientId === 'string', + ) + .sort((a, b) => b.time - a.time) + : []; + + // For each chain id, merge the array of network configurations + const networkConfigurationsByChainId = Object.entries( + networkConfigurationArraysByChainId, + ).reduce((acc: Record, [chainId, networks]) => { + // + // Calculate the tie breaker network, whose values will be preferred + let tieBreaker: RuntimeObject | undefined; + + // If one of the networks is the globally selected network, use that + tieBreaker = networks.find( + (network) => network.id === networkState.selectedNetworkClientId, + ); + + // Otherwise use the network that was most recently transacted on + if (!tieBreaker) { + transactions + .filter((tx) => tx.chainId === chainId) + .some( + (tx) => + (tieBreaker = networks.find( + (network) => network.id === tx.networkClientId, + )), + ); + } + + // If no transactions were found for the chain id, fall back + // to an arbitrary custom network that is not built in infura + if (!tieBreaker) { + tieBreaker = networks.find((network) => network.type !== 'infura'); + } + + // Calculate the unique set of valid rpc endpoints for this chain id + const rpcEndpoints = networks.reduce( + (endpoints: RuntimeObject[], network) => { + if ( + network.id && + network.rpcUrl && + typeof network.rpcUrl === 'string' && + isValidUrl(network.rpcUrl) + ) { + // Check if there's a different duplicate that's also the selected + // network. If so, it will be the preferred one we'll take. + const duplicateAndSelected = networkConfigurations.some( + (otherNetwork) => + isObject(otherNetwork) && + typeof otherNetwork.rpcUrl === 'string' && + typeof network.rpcUrl === 'string' && + otherNetwork.id !== network.id && // A different endpoint + URI.equal(otherNetwork.rpcUrl, network.rpcUrl) && // With the same URL + otherNetwork.id === networkState.selectedNetworkClientId, // That's currently selected + ); + + // Check if there's a duplicate that we've already processed. If none of + // the duplicates are the selected network, we'll take the first one seen. + const duplicateAlreadyAdded = [ + // Chains we've already proccessed + ...Object.values(acc).flatMap((n) => + isObject(n) ? n.rpcEndpoints : [], + ), + // Or the current chain we're processing + ...endpoints, + ].some( + (existingEndpoint) => + isObject(existingEndpoint) && + typeof existingEndpoint.url === 'string' && + typeof network.rpcUrl === 'string' && + URI.equal(existingEndpoint.url, network.rpcUrl), + ); + + if (!duplicateAndSelected && !duplicateAlreadyAdded) { + // The endpoint is unique and valid, so add it to the list + endpoints.push({ + networkClientId: network.id, + url: network.rpcUrl, + type: network.type === 'infura' ? 'infura' : 'custom', + ...(network.type !== 'infura' && + typeof network.nickname === 'string' && + // The old network name becomes the endpoint name + network.nickname && { name: network.nickname }), + }); + } + } + return endpoints; + }, + [], + ); + + // If there were no valid unique endpoints, then omit the network + // configuration for this chain id. The network controller requires + // configurations to have at least 1 endpoint. + if (rpcEndpoints.length === 0) { + return acc; + } + + // Use the tie breaker network as the default rpc endpoint + const defaultRpcEndpointIndex = Math.max( + rpcEndpoints.findIndex( + (endpoint) => endpoint.networkClientId === tieBreaker?.id, + ), + // Or arbitrarily default to the first endpoint if we don't have a tie breaker + 0, + ); + + // Calculate the unique array of non-empty block explorer urls + const blockExplorerUrls = [ + ...networks.reduce((urls, network) => { + if ( + isObject(network.rpcPrefs) && + typeof network.rpcPrefs.blockExplorerUrl === 'string' && + network.rpcPrefs.blockExplorerUrl + ) { + urls.add(network.rpcPrefs.blockExplorerUrl); + } + return urls; + }, new Set()), + ]; + + // Use the tie breaker network as the default block explorer, if it has one + const defaultBlockExplorerUrlIndex = + blockExplorerUrls.length === 0 + ? undefined + : Math.max( + blockExplorerUrls.findIndex( + (url) => + isObject(tieBreaker?.rpcPrefs) && + url === tieBreaker.rpcPrefs.blockExplorerUrl, + ), + // Or arbitrarily default to the first url + 0, + ); + + // Use the cononical network name and currency, if we have constants for them. + // Otherwise prefer the tie breaker's name + currency, if it defines them. + // Otherwise fall back to the name + currency from arbitrary networks that define them. + const name = + NETWORK_TO_NAME_MAP[chainId as keyof typeof NETWORK_TO_NAME_MAP] ?? + tieBreaker?.nickname ?? + networks.find((n) => n.nickname)?.nickname; + + const nativeCurrency = + CHAIN_ID_TO_CURRENCY_SYMBOL_MAP[ + chainId as keyof typeof CHAIN_ID_TO_CURRENCY_SYMBOL_MAP + ] ?? + tieBreaker?.ticker ?? + networks.find((n) => n.ticker)?.ticker; + + acc[chainId] = { + chainId, + rpcEndpoints, + defaultRpcEndpointIndex, + blockExplorerUrls, + ...(defaultBlockExplorerUrlIndex !== undefined && { + defaultBlockExplorerUrlIndex, + }), + name, + nativeCurrency, + }; + return acc; + }, {}); + + // Given a network client id, returns the chain id it used to point to + const networkClientIdToChainId = (networkClientId: unknown) => { + const networkConfiguration = networkConfigurations.find( + (n) => isObject(n) && n.id === networkClientId, + ); + + return isObject(networkConfiguration) && + typeof networkConfiguration?.chainId === 'string' + ? networkConfiguration?.chainId + : undefined; + }; + + // Ensure that selectedNetworkClientId points to + // some endpoint of some network configuration. + let selectedNetworkClientId = Object.values(networkConfigurationsByChainId) + .flatMap((n) => + isObject(n) && Array.isArray(n.rpcEndpoints) ? n.rpcEndpoints : [], + ) + .find( + (e) => e.networkClientId === networkState.selectedNetworkClientId, + )?.networkClientId; + + // It may not, if it's endpoint was not well formed. + if (!selectedNetworkClientId) { + // + // In that case, try to fallback to the default endpoint for the same chain + const chainId = networkClientIdToChainId( + networkState.selectedNetworkClientId, + ); + + // Or mainnet, if the entire chain had to be omitted due to invalid URLs + const networkConfiguration = + networkConfigurationsByChainId[chainId ?? '0x1']; + + selectedNetworkClientId = + isObject(networkConfiguration) && + Array.isArray(networkConfiguration.rpcEndpoints) && + typeof networkConfiguration.defaultRpcEndpointIndex === 'number' + ? networkConfiguration.rpcEndpoints[ + networkConfiguration.defaultRpcEndpointIndex + ].networkClientId + : 'mainnet'; + } + + // Redirect domains in the selected network controller to + // point to the default RPC endpoint for the corresponding chain + if ( + hasProperty(state, 'SelectedNetworkController') && + isObject(state.SelectedNetworkController) && + hasProperty(state.SelectedNetworkController, 'domains') && + isObject(state.SelectedNetworkController.domains) + ) { + for (const [domain, networkClientId] of Object.entries( + state.SelectedNetworkController.domains, + )) { + let newNetworkClientId; + + // Fetch the chain id associated with the domain's network client + const chainId = networkClientIdToChainId(networkClientId); + + if (chainId) { + // Fetch the default rpc endpoint associated with that chain id + const networkConfiguration = networkConfigurationsByChainId[chainId]; + if ( + isObject(networkConfiguration) && + Array.isArray(networkConfiguration.rpcEndpoints) && + typeof networkConfiguration.defaultRpcEndpointIndex === 'number' + ) { + newNetworkClientId = + networkConfiguration.rpcEndpoints[ + networkConfiguration.defaultRpcEndpointIndex + ].networkClientId; + } + } + + // Point the domain to the chain's default rpc endpoint, or remove the + // entry if the whole chain had to be deleted due to duplicates/invalidity. + if (newNetworkClientId) { + state.SelectedNetworkController.domains[domain] = newNetworkClientId; + } else { + delete state.SelectedNetworkController.domains[domain]; + } + } + } + + state.NetworkController = { + selectedNetworkClientId, + networkConfigurationsByChainId, + networksMetadata: networkState.networksMetadata ?? {}, + }; + + // Set `showMultiRpcModal` based on whether there are any networks with multiple rpc endpoints + if ( + hasProperty(state, 'PreferencesController') && + isObject(state.PreferencesController) && + hasProperty(state.PreferencesController, 'preferences') && + isObject(state.PreferencesController.preferences) + ) { + state.PreferencesController.preferences.showMultiRpcModal = Object.values( + networkConfigurationsByChainId, + ).some( + (networkConfiguration) => + isObject(networkConfiguration) && + Array.isArray(networkConfiguration.rpcEndpoints) && + networkConfiguration.rpcEndpoints.length > 1, + ); + } + + // Migrate the user's drag + drop preference order for the network menu + if ( + hasProperty(state, 'NetworkOrderController') && + isObject(state.NetworkOrderController) && + Array.isArray(state.NetworkOrderController.orderedNetworkList) + ) { + // Dedupe the list by chain id, and remove `networkRpcUrl` + // since it's no longer needed to distinguish networks + state.NetworkOrderController.orderedNetworkList = [ + ...new Set( + state.NetworkOrderController.orderedNetworkList.map( + (network) => network.networkId, + ), + ), + ].map((networkId) => ({ networkId })); + } + + return state; +} + +// Matches network controller validation +function isValidUrl(url: string) { + const uri = URI.parse(url); + return ( + uri.error === undefined && (uri.scheme === 'http' || uri.scheme === 'https') + ); +} diff --git a/app/scripts/migrations/index.js b/app/scripts/migrations/index.js index e5cfb6218019..80407ecf232e 100644 --- a/app/scripts/migrations/index.js +++ b/app/scripts/migrations/index.js @@ -146,6 +146,7 @@ const migrations = [ require('./125'), require('./125.1'), require('./126'), + require('./127'), ]; export default migrations; diff --git a/builds.yml b/builds.yml index 89d41923540c..acee49063822 100644 --- a/builds.yml +++ b/builds.yml @@ -268,8 +268,6 @@ env: - CHAIN_PERMISSIONS: '' # Enables use of test gas fee flow to debug gas fee estimation - TEST_GAS_FEE_FLOWS: false - # Determines if feature flagged network ui new design - - ENABLE_NETWORK_UI_REDESIGN: '' # Temporary mechanism to enable security alerts API prior to release - SECURITY_ALERTS_API_ENABLED: '' # URL of security alerts API used to validate dApp requests diff --git a/lavamoat/browserify/beta/policy.json b/lavamoat/browserify/beta/policy.json index 0e13938c6111..86253b3fa63c 100644 --- a/lavamoat/browserify/beta/policy.json +++ b/lavamoat/browserify/beta/policy.json @@ -1539,33 +1539,55 @@ }, "@metamask/network-controller": { "globals": { - "URL": true, "btoa": true, "fetch": true, "setTimeout": true }, "packages": { + "@metamask/base-controller": true, "@metamask/controller-utils": true, "@metamask/eth-query": true, - "@metamask/eth-token-tracker>@metamask/eth-block-tracker": true, - "@metamask/network-controller>@metamask/base-controller": true, + "@metamask/network-controller>@metamask/eth-block-tracker": true, "@metamask/network-controller>@metamask/eth-json-rpc-infura": true, "@metamask/network-controller>@metamask/eth-json-rpc-middleware": true, "@metamask/network-controller>@metamask/eth-json-rpc-provider": true, "@metamask/network-controller>@metamask/swappable-obj-proxy": true, + "@metamask/network-controller>reselect": true, "@metamask/rpc-errors": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, "@metamask/utils": true, "browserify>assert": true, + "browserify>util": true, + "uri-js": true, "uuid": true } }, - "@metamask/network-controller>@metamask/base-controller": { + "@metamask/network-controller>@metamask/eth-block-tracker": { "globals": { + "clearTimeout": true, + "console.error": true, "setTimeout": true }, "packages": { - "immer": true + "@metamask/eth-query>json-rpc-random-id": true, + "@metamask/network-controller>@metamask/eth-block-tracker>@metamask/utils": true, + "@metamask/safe-event-emitter": true, + "pify": true + } + }, + "@metamask/network-controller>@metamask/eth-block-tracker>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/utils>@metamask/superstruct": true, + "@metamask/utils>@scure/base": true, + "@metamask/utils>pony-cause": true, + "@noble/hashes": true, + "browserify>buffer": true, + "nock>debug": true, + "semver": true } }, "@metamask/network-controller>@metamask/eth-json-rpc-infura": { @@ -1618,19 +1640,13 @@ "@metamask/eth-json-rpc-middleware>klona": true, "@metamask/eth-json-rpc-middleware>safe-stable-stringify": true, "@metamask/eth-sig-util": true, - "@metamask/network-controller>@metamask/eth-json-rpc-middleware>@metamask/json-rpc-engine": true, "@metamask/network-controller>@metamask/eth-json-rpc-middleware>@metamask/utils": true, "@metamask/rpc-errors": true, + "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, + "bn.js": true, "pify": true } }, - "@metamask/network-controller>@metamask/eth-json-rpc-middleware>@metamask/json-rpc-engine": { - "packages": { - "@metamask/network-controller>@metamask/eth-json-rpc-middleware>@metamask/utils": true, - "@metamask/rpc-errors": true, - "@metamask/safe-event-emitter": true - } - }, "@metamask/network-controller>@metamask/eth-json-rpc-middleware>@metamask/utils": { "globals": { "TextDecoder": true, @@ -1654,6 +1670,13 @@ "uuid": true } }, + "@metamask/network-controller>reselect": { + "globals": { + "WeakRef": true, + "console.warn": true, + "unstable_autotrackMemoize": true + } + }, "@metamask/notification-controller": { "packages": { "@metamask/notification-controller>@metamask/base-controller": true, @@ -2213,16 +2236,8 @@ }, "@metamask/selected-network-controller": { "packages": { - "@metamask/network-controller>@metamask/swappable-obj-proxy": true, - "@metamask/selected-network-controller>@metamask/base-controller": true - } - }, - "@metamask/selected-network-controller>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true + "@metamask/base-controller": true, + "@metamask/network-controller>@metamask/swappable-obj-proxy": true } }, "@metamask/signature-controller": { @@ -5469,6 +5484,11 @@ "browserify>buffer": true } }, + "uri-js": { + "globals": { + "define": true + } + }, "uuid": { "globals": { "crypto": true, diff --git a/lavamoat/browserify/flask/policy.json b/lavamoat/browserify/flask/policy.json index 0e13938c6111..86253b3fa63c 100644 --- a/lavamoat/browserify/flask/policy.json +++ b/lavamoat/browserify/flask/policy.json @@ -1539,33 +1539,55 @@ }, "@metamask/network-controller": { "globals": { - "URL": true, "btoa": true, "fetch": true, "setTimeout": true }, "packages": { + "@metamask/base-controller": true, "@metamask/controller-utils": true, "@metamask/eth-query": true, - "@metamask/eth-token-tracker>@metamask/eth-block-tracker": true, - "@metamask/network-controller>@metamask/base-controller": true, + "@metamask/network-controller>@metamask/eth-block-tracker": true, "@metamask/network-controller>@metamask/eth-json-rpc-infura": true, "@metamask/network-controller>@metamask/eth-json-rpc-middleware": true, "@metamask/network-controller>@metamask/eth-json-rpc-provider": true, "@metamask/network-controller>@metamask/swappable-obj-proxy": true, + "@metamask/network-controller>reselect": true, "@metamask/rpc-errors": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, "@metamask/utils": true, "browserify>assert": true, + "browserify>util": true, + "uri-js": true, "uuid": true } }, - "@metamask/network-controller>@metamask/base-controller": { + "@metamask/network-controller>@metamask/eth-block-tracker": { "globals": { + "clearTimeout": true, + "console.error": true, "setTimeout": true }, "packages": { - "immer": true + "@metamask/eth-query>json-rpc-random-id": true, + "@metamask/network-controller>@metamask/eth-block-tracker>@metamask/utils": true, + "@metamask/safe-event-emitter": true, + "pify": true + } + }, + "@metamask/network-controller>@metamask/eth-block-tracker>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/utils>@metamask/superstruct": true, + "@metamask/utils>@scure/base": true, + "@metamask/utils>pony-cause": true, + "@noble/hashes": true, + "browserify>buffer": true, + "nock>debug": true, + "semver": true } }, "@metamask/network-controller>@metamask/eth-json-rpc-infura": { @@ -1618,19 +1640,13 @@ "@metamask/eth-json-rpc-middleware>klona": true, "@metamask/eth-json-rpc-middleware>safe-stable-stringify": true, "@metamask/eth-sig-util": true, - "@metamask/network-controller>@metamask/eth-json-rpc-middleware>@metamask/json-rpc-engine": true, "@metamask/network-controller>@metamask/eth-json-rpc-middleware>@metamask/utils": true, "@metamask/rpc-errors": true, + "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, + "bn.js": true, "pify": true } }, - "@metamask/network-controller>@metamask/eth-json-rpc-middleware>@metamask/json-rpc-engine": { - "packages": { - "@metamask/network-controller>@metamask/eth-json-rpc-middleware>@metamask/utils": true, - "@metamask/rpc-errors": true, - "@metamask/safe-event-emitter": true - } - }, "@metamask/network-controller>@metamask/eth-json-rpc-middleware>@metamask/utils": { "globals": { "TextDecoder": true, @@ -1654,6 +1670,13 @@ "uuid": true } }, + "@metamask/network-controller>reselect": { + "globals": { + "WeakRef": true, + "console.warn": true, + "unstable_autotrackMemoize": true + } + }, "@metamask/notification-controller": { "packages": { "@metamask/notification-controller>@metamask/base-controller": true, @@ -2213,16 +2236,8 @@ }, "@metamask/selected-network-controller": { "packages": { - "@metamask/network-controller>@metamask/swappable-obj-proxy": true, - "@metamask/selected-network-controller>@metamask/base-controller": true - } - }, - "@metamask/selected-network-controller>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true + "@metamask/base-controller": true, + "@metamask/network-controller>@metamask/swappable-obj-proxy": true } }, "@metamask/signature-controller": { @@ -5469,6 +5484,11 @@ "browserify>buffer": true } }, + "uri-js": { + "globals": { + "define": true + } + }, "uuid": { "globals": { "crypto": true, diff --git a/lavamoat/browserify/main/policy.json b/lavamoat/browserify/main/policy.json index 0e13938c6111..86253b3fa63c 100644 --- a/lavamoat/browserify/main/policy.json +++ b/lavamoat/browserify/main/policy.json @@ -1539,33 +1539,55 @@ }, "@metamask/network-controller": { "globals": { - "URL": true, "btoa": true, "fetch": true, "setTimeout": true }, "packages": { + "@metamask/base-controller": true, "@metamask/controller-utils": true, "@metamask/eth-query": true, - "@metamask/eth-token-tracker>@metamask/eth-block-tracker": true, - "@metamask/network-controller>@metamask/base-controller": true, + "@metamask/network-controller>@metamask/eth-block-tracker": true, "@metamask/network-controller>@metamask/eth-json-rpc-infura": true, "@metamask/network-controller>@metamask/eth-json-rpc-middleware": true, "@metamask/network-controller>@metamask/eth-json-rpc-provider": true, "@metamask/network-controller>@metamask/swappable-obj-proxy": true, + "@metamask/network-controller>reselect": true, "@metamask/rpc-errors": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, "@metamask/utils": true, "browserify>assert": true, + "browserify>util": true, + "uri-js": true, "uuid": true } }, - "@metamask/network-controller>@metamask/base-controller": { + "@metamask/network-controller>@metamask/eth-block-tracker": { "globals": { + "clearTimeout": true, + "console.error": true, "setTimeout": true }, "packages": { - "immer": true + "@metamask/eth-query>json-rpc-random-id": true, + "@metamask/network-controller>@metamask/eth-block-tracker>@metamask/utils": true, + "@metamask/safe-event-emitter": true, + "pify": true + } + }, + "@metamask/network-controller>@metamask/eth-block-tracker>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/utils>@metamask/superstruct": true, + "@metamask/utils>@scure/base": true, + "@metamask/utils>pony-cause": true, + "@noble/hashes": true, + "browserify>buffer": true, + "nock>debug": true, + "semver": true } }, "@metamask/network-controller>@metamask/eth-json-rpc-infura": { @@ -1618,19 +1640,13 @@ "@metamask/eth-json-rpc-middleware>klona": true, "@metamask/eth-json-rpc-middleware>safe-stable-stringify": true, "@metamask/eth-sig-util": true, - "@metamask/network-controller>@metamask/eth-json-rpc-middleware>@metamask/json-rpc-engine": true, "@metamask/network-controller>@metamask/eth-json-rpc-middleware>@metamask/utils": true, "@metamask/rpc-errors": true, + "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, + "bn.js": true, "pify": true } }, - "@metamask/network-controller>@metamask/eth-json-rpc-middleware>@metamask/json-rpc-engine": { - "packages": { - "@metamask/network-controller>@metamask/eth-json-rpc-middleware>@metamask/utils": true, - "@metamask/rpc-errors": true, - "@metamask/safe-event-emitter": true - } - }, "@metamask/network-controller>@metamask/eth-json-rpc-middleware>@metamask/utils": { "globals": { "TextDecoder": true, @@ -1654,6 +1670,13 @@ "uuid": true } }, + "@metamask/network-controller>reselect": { + "globals": { + "WeakRef": true, + "console.warn": true, + "unstable_autotrackMemoize": true + } + }, "@metamask/notification-controller": { "packages": { "@metamask/notification-controller>@metamask/base-controller": true, @@ -2213,16 +2236,8 @@ }, "@metamask/selected-network-controller": { "packages": { - "@metamask/network-controller>@metamask/swappable-obj-proxy": true, - "@metamask/selected-network-controller>@metamask/base-controller": true - } - }, - "@metamask/selected-network-controller>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true + "@metamask/base-controller": true, + "@metamask/network-controller>@metamask/swappable-obj-proxy": true } }, "@metamask/signature-controller": { @@ -5469,6 +5484,11 @@ "browserify>buffer": true } }, + "uri-js": { + "globals": { + "define": true + } + }, "uuid": { "globals": { "crypto": true, diff --git a/lavamoat/browserify/mmi/policy.json b/lavamoat/browserify/mmi/policy.json index e5913a27f248..032906bee80b 100644 --- a/lavamoat/browserify/mmi/policy.json +++ b/lavamoat/browserify/mmi/policy.json @@ -1631,33 +1631,55 @@ }, "@metamask/network-controller": { "globals": { - "URL": true, "btoa": true, "fetch": true, "setTimeout": true }, "packages": { + "@metamask/base-controller": true, "@metamask/controller-utils": true, "@metamask/eth-query": true, - "@metamask/eth-token-tracker>@metamask/eth-block-tracker": true, - "@metamask/network-controller>@metamask/base-controller": true, + "@metamask/network-controller>@metamask/eth-block-tracker": true, "@metamask/network-controller>@metamask/eth-json-rpc-infura": true, "@metamask/network-controller>@metamask/eth-json-rpc-middleware": true, "@metamask/network-controller>@metamask/eth-json-rpc-provider": true, "@metamask/network-controller>@metamask/swappable-obj-proxy": true, + "@metamask/network-controller>reselect": true, "@metamask/rpc-errors": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, "@metamask/utils": true, "browserify>assert": true, + "browserify>util": true, + "uri-js": true, "uuid": true } }, - "@metamask/network-controller>@metamask/base-controller": { + "@metamask/network-controller>@metamask/eth-block-tracker": { "globals": { + "clearTimeout": true, + "console.error": true, "setTimeout": true }, "packages": { - "immer": true + "@metamask/eth-query>json-rpc-random-id": true, + "@metamask/network-controller>@metamask/eth-block-tracker>@metamask/utils": true, + "@metamask/safe-event-emitter": true, + "pify": true + } + }, + "@metamask/network-controller>@metamask/eth-block-tracker>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/utils>@metamask/superstruct": true, + "@metamask/utils>@scure/base": true, + "@metamask/utils>pony-cause": true, + "@noble/hashes": true, + "browserify>buffer": true, + "nock>debug": true, + "semver": true } }, "@metamask/network-controller>@metamask/eth-json-rpc-infura": { @@ -1710,19 +1732,13 @@ "@metamask/eth-json-rpc-middleware>klona": true, "@metamask/eth-json-rpc-middleware>safe-stable-stringify": true, "@metamask/eth-sig-util": true, - "@metamask/network-controller>@metamask/eth-json-rpc-middleware>@metamask/json-rpc-engine": true, "@metamask/network-controller>@metamask/eth-json-rpc-middleware>@metamask/utils": true, "@metamask/rpc-errors": true, + "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, + "bn.js": true, "pify": true } }, - "@metamask/network-controller>@metamask/eth-json-rpc-middleware>@metamask/json-rpc-engine": { - "packages": { - "@metamask/network-controller>@metamask/eth-json-rpc-middleware>@metamask/utils": true, - "@metamask/rpc-errors": true, - "@metamask/safe-event-emitter": true - } - }, "@metamask/network-controller>@metamask/eth-json-rpc-middleware>@metamask/utils": { "globals": { "TextDecoder": true, @@ -1746,6 +1762,13 @@ "uuid": true } }, + "@metamask/network-controller>reselect": { + "globals": { + "WeakRef": true, + "console.warn": true, + "unstable_autotrackMemoize": true + } + }, "@metamask/notification-controller": { "packages": { "@metamask/notification-controller>@metamask/base-controller": true, @@ -2305,16 +2328,8 @@ }, "@metamask/selected-network-controller": { "packages": { - "@metamask/network-controller>@metamask/swappable-obj-proxy": true, - "@metamask/selected-network-controller>@metamask/base-controller": true - } - }, - "@metamask/selected-network-controller>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true + "@metamask/base-controller": true, + "@metamask/network-controller>@metamask/swappable-obj-proxy": true } }, "@metamask/signature-controller": { @@ -5537,6 +5552,11 @@ "browserify>buffer": true } }, + "uri-js": { + "globals": { + "define": true + } + }, "uuid": { "globals": { "crypto": true, diff --git a/lavamoat/build-system/policy.json b/lavamoat/build-system/policy.json index f4927e63f48c..9bb4675d14c9 100644 --- a/lavamoat/build-system/policy.json +++ b/lavamoat/build-system/policy.json @@ -3115,8 +3115,8 @@ "packages": { "@metamask/snaps-utils>fast-json-stable-stringify": true, "eslint>@eslint/eslintrc>ajv>json-schema-traverse": true, - "eslint>ajv>uri-js": true, - "eslint>fast-deep-equal": true + "eslint>fast-deep-equal": true, + "uri-js": true } }, "eslint>@eslint/eslintrc>import-fresh": { @@ -3212,13 +3212,8 @@ "packages": { "@metamask/snaps-utils>fast-json-stable-stringify": true, "eslint>ajv>json-schema-traverse": true, - "eslint>ajv>uri-js": true, - "eslint>fast-deep-equal": true - } - }, - "eslint>ajv>uri-js": { - "globals": { - "define": true + "eslint>fast-deep-equal": true, + "uri-js": true } }, "eslint>doctrine": { @@ -8926,6 +8921,11 @@ "terser>source-map-support": true } }, + "uri-js": { + "globals": { + "define": true + } + }, "vinyl": { "builtin": { "buffer.Buffer.isBuffer": true, diff --git a/package.json b/package.json index a4cbaf938aec..c6889c6fec0e 100644 --- a/package.json +++ b/package.json @@ -257,13 +257,14 @@ "sucrase@npm:3.34.0": "^3.35.0", "@expo/config/glob": "^10.3.10", "@expo/config-plugins/glob": "^10.3.10", - "@metamask/network-controller": "patch:@metamask/network-controller@npm%3A20.2.0#~/.yarn/patches/@metamask-network-controller-npm-20.2.0-98b1a5ae59.patch", "@solana/web3.js/rpc-websockets": "^8.0.1", "@metamask/message-manager": "^10.1.0", - "@metamask/network-controller@npm:^19.0.0": "patch:@metamask/network-controller@npm%3A20.2.0#~/.yarn/patches/@metamask-network-controller-npm-20.2.0-98b1a5ae59.patch", "@metamask/gas-fee-controller@npm:^15.1.1": "patch:@metamask/gas-fee-controller@npm%3A15.1.2#~/.yarn/patches/@metamask-gas-fee-controller-npm-15.1.2-db4d2976aa.patch", "@metamask/nonce-tracker@npm:^5.0.0": "patch:@metamask/nonce-tracker@npm%3A5.0.0#~/.yarn/patches/@metamask-nonce-tracker-npm-5.0.0-d81478218e.patch", "@trezor/connect-web@npm:^9.1.11": "patch:@trezor/connect-web@npm%3A9.3.0#~/.yarn/patches/@trezor-connect-web-npm-9.3.0-040ab10d9a.patch", + "@metamask/network-controller@npm:^17.0.0": "patch:@metamask/network-controller@npm%3A21.0.0#~/.yarn/patches/@metamask-network-controller-npm-21.0.0-559aa8e395.patch", + "@metamask/network-controller@npm:^19.0.0": "patch:@metamask/network-controller@npm%3A21.0.0#~/.yarn/patches/@metamask-network-controller-npm-21.0.0-559aa8e395.patch", + "@metamask/network-controller@npm:^20.0.0": "patch:@metamask/network-controller@npm%3A21.0.0#~/.yarn/patches/@metamask-network-controller-npm-21.0.0-559aa8e395.patch", "path-to-regexp": "1.9.0" }, "dependencies": { @@ -331,7 +332,7 @@ "@metamask/message-signing-snap": "^0.3.3", "@metamask/metamask-eth-abis": "^3.1.1", "@metamask/name-controller": "^8.0.0", - "@metamask/network-controller": "patch:@metamask/network-controller@npm%3A20.2.0#~/.yarn/patches/@metamask-network-controller-npm-20.2.0-98b1a5ae59.patch", + "@metamask/network-controller": "patch:@metamask/network-controller@npm%3A21.0.0#~/.yarn/patches/@metamask-network-controller-npm-21.0.0-559aa8e395.patch", "@metamask/notification-controller": "^6.0.0", "@metamask/notification-services-controller": "^0.7.0", "@metamask/object-multiplex": "^2.0.0", @@ -348,7 +349,7 @@ "@metamask/rpc-errors": "^6.2.1", "@metamask/safe-event-emitter": "^3.1.1", "@metamask/scure-bip39": "^2.0.3", - "@metamask/selected-network-controller": "^15.0.2", + "@metamask/selected-network-controller": "^18.0.1", "@metamask/signature-controller": "^19.0.0", "@metamask/smart-transactions-controller": "^13.0.0", "@metamask/snaps-controllers": "^9.7.0", @@ -438,6 +439,7 @@ "single-call-balance-checker-abi": "^1.0.0", "ts-mixer": "patch:ts-mixer@npm%3A6.0.4#~/.yarn/patches/ts-mixer-npm-6.0.4-5d9747bdf5.patch", "unicode-confusables": "^0.1.1", + "uri-js": "^4.4.1", "uuid": "^8.3.2", "web3-stream-provider": "^5.0.0", "zxcvbn": "^4.4.2" diff --git a/shared/constants/network.test.ts b/shared/constants/network.test.ts index ac427914fbaa..20a13ccfb273 100644 --- a/shared/constants/network.test.ts +++ b/shared/constants/network.test.ts @@ -30,7 +30,7 @@ describe('NetworkConstants', () => { const expectedChainIds: { [key: string]: string } = { 'Arbitrum One': CHAIN_IDS.ARBITRUM, 'Avalanche Network C-Chain': CHAIN_IDS.AVALANCHE, - 'BNB Chain': CHAIN_IDS.BSC, + 'Binance Smart Chain': CHAIN_IDS.BSC, 'OP Mainnet': CHAIN_IDS.OPTIMISM, 'Polygon Mainnet': CHAIN_IDS.POLYGON, 'zkSync Era Mainnet': CHAIN_IDS.ZKSYNC_ERA, @@ -38,57 +38,59 @@ describe('NetworkConstants', () => { }; FEATURED_RPCS.forEach((rpc) => { - expect(rpc.chainId).toBe(expectedChainIds[rpc.nickname]); + expect(rpc.chainId).toBe(expectedChainIds[rpc.name]); }); }); }); describe('FEATURED_RPCS Infura Usage Tests', () => { it('arbitrum entry should use Infura', () => { - const arbitrumRpc = FEATURED_RPCS.find( + const [arbitrumRpc] = FEATURED_RPCS.filter( (rpc) => rpc.chainId === CHAIN_IDS.ARBITRUM, ); - expect(arbitrumRpc?.rpcUrl).toContain('infura.io'); + expect(arbitrumRpc.rpcEndpoints[0].url).toContain('infura.io'); }); it('avalanche entry should use Infura', () => { - const avalancheRpc = FEATURED_RPCS.find( + const [avalancheRpc] = FEATURED_RPCS.filter( (rpc) => rpc.chainId === CHAIN_IDS.AVALANCHE, ); - expect(avalancheRpc?.rpcUrl).toContain('infura.io'); + expect(avalancheRpc.rpcEndpoints[0].url).toContain('infura.io'); }); it('bsc entry should not use Infura', () => { - const bscRpc = FEATURED_RPCS.find((rpc) => rpc.chainId === CHAIN_IDS.BSC); - expect(bscRpc?.rpcUrl).not.toContain('infura.io'); + const [bscRpc] = FEATURED_RPCS.filter( + (rpc) => rpc.chainId === CHAIN_IDS.BSC, + ); + expect(bscRpc.rpcEndpoints[0].url).not.toContain('infura.io'); }); it('optimism entry should use Infura', () => { - const optimismRpc = FEATURED_RPCS.find( + const [optimismRpc] = FEATURED_RPCS.filter( (rpc) => rpc.chainId === CHAIN_IDS.OPTIMISM, ); - expect(optimismRpc?.rpcUrl).toContain('infura.io'); + expect(optimismRpc.rpcEndpoints[0].url).toContain('infura.io'); }); it('polygon entry should use Infura', () => { - const polygonRpc = FEATURED_RPCS.find( + const [polygonRpc] = FEATURED_RPCS.filter( (rpc) => rpc.chainId === CHAIN_IDS.POLYGON, ); - expect(polygonRpc?.rpcUrl).toContain('infura.io'); + expect(polygonRpc.rpcEndpoints[0].url).toContain('infura.io'); }); it('zkSync Era entry should not use Infura', () => { - const zksyncEraRpc = FEATURED_RPCS.find( + const [zksyncEraRpc] = FEATURED_RPCS.filter( (rpc) => rpc.chainId === CHAIN_IDS.ZKSYNC_ERA, ); - expect(zksyncEraRpc?.rpcUrl).not.toContain('infura.io'); + expect(zksyncEraRpc.rpcEndpoints[0].url).not.toContain('infura.io'); }); it('base entry should not use Infura', () => { - const baseRpc = FEATURED_RPCS.find( + const [baseRpc] = FEATURED_RPCS.filter( (rpc) => rpc.chainId === CHAIN_IDS.BASE, ); - expect(baseRpc?.rpcUrl).not.toContain('infura.io'); + expect(baseRpc.rpcEndpoints[0].url).not.toContain('infura.io'); }); }); }); diff --git a/shared/constants/network.ts b/shared/constants/network.ts index 641e310517cb..b79c4a27d8cd 100644 --- a/shared/constants/network.ts +++ b/shared/constants/network.ts @@ -1,3 +1,7 @@ +import { + AddNetworkFields, + RpcEndpointType, +} from '@metamask/network-controller'; import { capitalize, pick } from 'lodash'; /** * A type representing built-in network types, used as an identifier. @@ -724,6 +728,7 @@ export const CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP = { [CHAIN_IDS.PALM]: PALM_TOKEN_IMAGE_URL, [CHAIN_IDS.CELO]: CELO_TOKEN_IMAGE_URL, [CHAIN_IDS.GNOSIS]: GNOSIS_TOKEN_IMAGE_URL, + [CHAIN_IDS.ZKSYNC_ERA]: ZK_SYNC_ERA_TOKEN_IMAGE_URL, [CHAIN_IDS.NEAR]: NEAR_IMAGE_URL, [CHAIN_IDS.NEAR_TESTNET]: NEAR_IMAGE_URL, [CHAINLIST_CHAIN_IDS_MAP.ACALA_NETWORK]: ACALA_TOKEN_IMAGE_URL, @@ -923,76 +928,104 @@ export const UNSUPPORTED_RPC_METHODS = new Set([ export const IPFS_DEFAULT_GATEWAY_URL = 'dweb.link'; -export const FEATURED_RPCS: RPCDefinition[] = [ +export const FEATURED_RPCS: AddNetworkFields[] = [ { chainId: CHAIN_IDS.ARBITRUM, - nickname: ARBITRUM_DISPLAY_NAME, - rpcUrl: `https://arbitrum-mainnet.infura.io/v3/${infuraProjectId}`, - ticker: CURRENCY_SYMBOLS.ARBITRUM, - rpcPrefs: { - blockExplorerUrl: 'https://explorer.arbitrum.io', - imageUrl: AETH_TOKEN_IMAGE_URL, - }, + name: ARBITRUM_DISPLAY_NAME, + nativeCurrency: CURRENCY_SYMBOLS.ARBITRUM, + rpcEndpoints: [ + { + url: `https://arbitrum-mainnet.infura.io/v3/${infuraProjectId}`, + type: RpcEndpointType.Custom, + }, + ], + defaultRpcEndpointIndex: 0, + blockExplorerUrls: ['https://explorer.arbitrum.io'], + defaultBlockExplorerUrlIndex: 0, }, { chainId: CHAIN_IDS.AVALANCHE, - nickname: AVALANCHE_DISPLAY_NAME, - rpcUrl: `https://avalanche-mainnet.infura.io/v3/${infuraProjectId}`, - ticker: CURRENCY_SYMBOLS.AVALANCHE, - rpcPrefs: { - blockExplorerUrl: 'https://snowtrace.io/', - imageUrl: AVAX_TOKEN_IMAGE_URL, - }, + name: AVALANCHE_DISPLAY_NAME, + nativeCurrency: CURRENCY_SYMBOLS.AVALANCHE, + rpcEndpoints: [ + { + url: `https://avalanche-mainnet.infura.io/v3/${infuraProjectId}`, + type: RpcEndpointType.Custom, + }, + ], + defaultRpcEndpointIndex: 0, + blockExplorerUrls: ['https://snowtrace.io/'], + defaultBlockExplorerUrlIndex: 0, }, { chainId: CHAIN_IDS.BSC, - nickname: BNB_DISPLAY_NAME, - rpcUrl: 'https://bsc-dataseed.binance.org/', - ticker: CURRENCY_SYMBOLS.BNB, - rpcPrefs: { - blockExplorerUrl: 'https://bscscan.com/', - imageUrl: BNB_TOKEN_IMAGE_URL, - }, + name: BSC_DISPLAY_NAME, + nativeCurrency: CURRENCY_SYMBOLS.BNB, + rpcEndpoints: [ + { + url: 'https://bsc-dataseed.binance.org/', + type: RpcEndpointType.Custom, + }, + ], + defaultRpcEndpointIndex: 0, + blockExplorerUrls: ['https://bscscan.com/'], + defaultBlockExplorerUrlIndex: 0, }, { chainId: CHAIN_IDS.OPTIMISM, - nickname: OPTIMISM_DISPLAY_NAME, - rpcUrl: `https://optimism-mainnet.infura.io/v3/${infuraProjectId}`, - ticker: CURRENCY_SYMBOLS.ETH, - rpcPrefs: { - blockExplorerUrl: 'https://optimistic.etherscan.io/', - imageUrl: OPTIMISM_TOKEN_IMAGE_URL, - }, + name: OPTIMISM_DISPLAY_NAME, + nativeCurrency: CURRENCY_SYMBOLS.ETH, + rpcEndpoints: [ + { + url: `https://optimism-mainnet.infura.io/v3/${infuraProjectId}`, + type: RpcEndpointType.Custom, + }, + ], + defaultRpcEndpointIndex: 0, + blockExplorerUrls: ['https://optimistic.etherscan.io/'], + defaultBlockExplorerUrlIndex: 0, }, { chainId: CHAIN_IDS.POLYGON, - nickname: `${POLYGON_DISPLAY_NAME} ${capitalize(NETWORK_TYPES.MAINNET)}`, - rpcUrl: `https://polygon-mainnet.infura.io/v3/${infuraProjectId}`, - ticker: CURRENCY_SYMBOLS.POL, - rpcPrefs: { - blockExplorerUrl: 'https://polygonscan.com/', - imageUrl: POL_TOKEN_IMAGE_URL, - }, + name: `${POLYGON_DISPLAY_NAME} ${capitalize(NETWORK_TYPES.MAINNET)}`, + nativeCurrency: CURRENCY_SYMBOLS.POL, + rpcEndpoints: [ + { + url: `https://polygon-mainnet.infura.io/v3/${infuraProjectId}`, + type: RpcEndpointType.Custom, + }, + ], + defaultRpcEndpointIndex: 0, + blockExplorerUrls: ['https://polygonscan.com/'], + defaultBlockExplorerUrlIndex: 0, }, { chainId: CHAIN_IDS.ZKSYNC_ERA, - nickname: ZK_SYNC_ERA_DISPLAY_NAME, - rpcUrl: `https://mainnet.era.zksync.io`, - ticker: CURRENCY_SYMBOLS.ETH, - rpcPrefs: { - blockExplorerUrl: 'https://explorer.zksync.io/', - imageUrl: ZK_SYNC_ERA_TOKEN_IMAGE_URL, - }, + name: ZK_SYNC_ERA_DISPLAY_NAME, + nativeCurrency: CURRENCY_SYMBOLS.ETH, + rpcEndpoints: [ + { + url: `https://mainnet.era.zksync.io`, + type: RpcEndpointType.Custom, + }, + ], + defaultRpcEndpointIndex: 0, + blockExplorerUrls: ['https://explorer.zksync.io/'], + defaultBlockExplorerUrlIndex: 0, }, { chainId: CHAIN_IDS.BASE, - nickname: BASE_DISPLAY_NAME, - rpcUrl: `https://mainnet.base.org`, - ticker: CURRENCY_SYMBOLS.ETH, - rpcPrefs: { - blockExplorerUrl: 'https://basescan.org', - imageUrl: BASE_TOKEN_IMAGE_URL, - }, + name: BASE_DISPLAY_NAME, + nativeCurrency: CURRENCY_SYMBOLS.ETH, + rpcEndpoints: [ + { + url: `https://mainnet.base.org`, + type: RpcEndpointType.Custom, + }, + ], + defaultRpcEndpointIndex: 0, + blockExplorerUrls: ['https://basescan.org'], + defaultBlockExplorerUrlIndex: 0, }, ]; diff --git a/shared/constants/swaps.ts b/shared/constants/swaps.ts index db58e82c4048..3868c7b6e2f0 100644 --- a/shared/constants/swaps.ts +++ b/shared/constants/swaps.ts @@ -175,14 +175,14 @@ export const GAS_API_BASE_URL = 'https://gas.api.cx.metamask.io'; export const GAS_DEV_API_BASE_URL = 'https://gas.uat-api.cx.metamask.io'; const BSC_DEFAULT_BLOCK_EXPLORER_URL = 'https://bscscan.com/'; -const MAINNET_DEFAULT_BLOCK_EXPLORER_URL = 'https://etherscan.io/'; +export const MAINNET_DEFAULT_BLOCK_EXPLORER_URL = 'https://etherscan.io/'; const GOERLI_DEFAULT_BLOCK_EXPLORER_URL = 'https://goerli.etherscan.io/'; const POLYGON_DEFAULT_BLOCK_EXPLORER_URL = 'https://polygonscan.com/'; const AVALANCHE_DEFAULT_BLOCK_EXPLORER_URL = 'https://snowtrace.io/'; const OPTIMISM_DEFAULT_BLOCK_EXPLORER_URL = 'https://optimistic.etherscan.io/'; const ARBITRUM_DEFAULT_BLOCK_EXPLORER_URL = 'https://arbiscan.io/'; const ZKSYNC_DEFAULT_BLOCK_EXPLORER_URL = 'https://explorer.zksync.io/'; -const LINEA_DEFAULT_BLOCK_EXPLORER_URL = 'https://lineascan.build/'; +export const LINEA_DEFAULT_BLOCK_EXPLORER_URL = 'https://lineascan.build/'; const BASE_DEFAULT_BLOCK_EXPLORER_URL = 'https://basescan.org/'; export const ALLOWED_PROD_SWAPS_CHAIN_IDS = [ diff --git a/shared/modules/selectors/smart-transactions.ts b/shared/modules/selectors/smart-transactions.ts index 81c480f94977..0de20511db49 100644 --- a/shared/modules/selectors/smart-transactions.ts +++ b/shared/modules/selectors/smart-transactions.ts @@ -1,4 +1,3 @@ -import type { Hex } from '@metamask/utils'; import { getAllowedSmartTransactionsChainIds, SKIP_STX_RPC_URL_CHECK_CHAIN_IDS, @@ -50,13 +49,6 @@ type SmartTransactionsMetaMaskState = { smartTransactionsState: { liveness: boolean; }; - selectedNetworkClientId: string; - networkConfigurations?: { - [key: string]: { - chainId: Hex; - rpcUrl: string; - }; - }; }; }; diff --git a/test/data/confirmations/helper.ts b/test/data/confirmations/helper.ts index 014734aba0aa..c542ee92feab 100644 --- a/test/data/confirmations/helper.ts +++ b/test/data/confirmations/helper.ts @@ -6,6 +6,7 @@ import { SignatureRequestType, } from '../../../ui/pages/confirmations/types/confirm'; import mockState from '../mock-state.json'; +import { CHAIN_IDS } from '../../../shared/constants/network'; import { genUnapprovedApproveConfirmation, genUnapprovedContractInteractionConfirmation, @@ -165,7 +166,7 @@ export const getMockContractInteractionConfirmState = ( args: RootState = { metamask: {} }, ) => { const contractInteraction = genUnapprovedContractInteractionConfirmation({ - chainId: mockState.metamask.networkConfigurations.goerli.chainId, + chainId: CHAIN_IDS.GOERLI, }); return getMockConfirmStateForTransaction(contractInteraction, args); }; diff --git a/test/data/mock-send-state.json b/test/data/mock-send-state.json index 09b9cad15757..59d24a1f5f54 100644 --- a/test/data/mock-send-state.json +++ b/test/data/mock-send-state.json @@ -144,17 +144,24 @@ "0x539": true }, "showTestnetMessageInDropdown": true, - "networkConfigurations": { - "goerli": { - "id": "goerli", - "chainId": "0x5" - } - }, "alertEnabledness": { "unconnectedAccount": true }, "featureFlags": {}, "network": "5", + + "networkConfigurationsByChainId": { + "0x5": { + "nativeCurrency": "ETH", + "chainId": "0x5", + "defaultRpcEndpointIndex": 0, + "rpcEndpoints": [ + { + "networkClientId": "goerli" + } + ] + } + }, "internalAccounts": { "accounts": { "cf8dace4-9439-4bd4-b3a8-88c821c8fcb3": { diff --git a/test/data/mock-state.json b/test/data/mock-state.json index c15e9b121965..5a86bbb4970b 100644 --- a/test/data/mock-state.json +++ b/test/data/mock-state.json @@ -380,20 +380,32 @@ "unconnectedAccount": true }, "featureFlags": {}, - "networkConfigurations": { - "testNetworkConfigurationId": { - "rpcUrl": "https://testrpc.com", + "networkConfigurationsByChainId": { + "0x1": { "chainId": "0x1", - "nickname": "Custom Mainnet RPC", - "type": "rpc", - "id": "testNetworkConfigurationId" + "name": "Custom Mainnet RPC", + "nativeCurrency": "ETH", + "defaultRpcEndpointIndex": 0, + "rpcEndpoints": [ + { + "type": "custom", + "url": "https://testrpc.com", + "networkClientId": "testNetworkConfigurationId" + } + ] }, - "goerli": { - "type": "rpc", + "0x5": { "chainId": "0x5", - "ticker": "ETH", - "nickname": "Chain 5", - "id": "goerli" + "name": "Goerli", + "nativeCurrency": "ETH", + "defaultRpcEndpointIndex": 0, + "rpcEndpoints": [ + { + "type": "custom", + "url": "https://goerli.com", + "networkClientId": "goerli" + } + ] } }, "internalAccounts": { diff --git a/test/e2e/default-fixture.js b/test/e2e/default-fixture.js index 3b8ebd46de35..470e260ff959 100644 --- a/test/e2e/default-fixture.js +++ b/test/e2e/default-fixture.js @@ -1,7 +1,11 @@ -const { mockNetworkState } = require('../stub/networks'); +const { mockNetworkStateOld } = require('../stub/networks'); const { CHAIN_IDS } = require('../../shared/constants/network'); const { FirstTimeFlowType } = require('../../shared/constants/onboarding'); +// TODO: Should we bump this? +// The e2e tests currently configure state in the schema of migration 74. +// This requires us to specify network state in the old schema, so it can run through the migrations. +// We could bump this to latest, but it breaks too many other things to handle right now. const FIXTURE_STATE_METADATA_VERSION = 74; const E2E_SRP = @@ -158,7 +162,7 @@ function defaultFixture(inputChainId = CHAIN_IDS.LOCALHOST) { metaMetricsDataDeletionTimestamp: 0, }, NetworkController: { - ...mockNetworkState({ + ...mockNetworkStateOld({ id: 'networkConfigurationId', chainId: inputChainId, nickname: 'Localhost 8545', @@ -204,6 +208,7 @@ function defaultFixture(inputChainId = CHAIN_IDS.LOCALHOST) { smartTransactionsOptInStatus: false, useNativeCurrencyAsPrimaryCurrency: true, petnamesEnabled: true, + showMultiRpcModal: false, isRedesignedConfirmationsDeveloperEnabled: false, showConfirmationAdvancedDetails: false, }, diff --git a/test/e2e/fixture-builder.js b/test/e2e/fixture-builder.js index 1d9401bf227f..2c977816e6fe 100644 --- a/test/e2e/fixture-builder.js +++ b/test/e2e/fixture-builder.js @@ -4,7 +4,7 @@ const { } = require('@metamask/snaps-utils'); const { merge, mergeWith } = require('lodash'); const { toHex } = require('@metamask/controller-utils'); -const { mockNetworkState } = require('../stub/networks'); +const { mockNetworkStateOld } = require('../stub/networks'); const { CHAIN_IDS } = require('../../shared/constants/network'); const { SMART_CONTRACTS } = require('./seeder/smart-contracts'); @@ -44,7 +44,7 @@ function onboardingFixture() { }, }, NetworkController: { - ...mockNetworkState({ + ...mockNetworkStateOld({ id: 'networkConfigurationId', chainId: CHAIN_IDS.LOCALHOST, nickname: 'Localhost 8545', @@ -55,7 +55,7 @@ function onboardingFixture() { providerConfig: { id: 'networkConfigurationId' }, }, PreferencesController: { - advancedGasFee: null, + advancedGasFee: {}, currentLocale: 'en', dismissSeedBackUpReminder: false, featureFlags: {}, @@ -74,6 +74,7 @@ function onboardingFixture() { smartTransactionsOptInStatus: false, useNativeCurrencyAsPrimaryCurrency: true, petnamesEnabled: true, + showMultiRpcModal: false, isRedesignedConfirmationsDeveloperEnabled: false, showConfirmationAdvancedDetails: false, }, @@ -111,6 +112,7 @@ function onboardingFixture() { ignoredTokens: [], tokens: [], }, + TransactionController: {}, config: {}, firstTimeInfo: { date: 1665507600000, @@ -255,37 +257,29 @@ class FixtureBuilder { } withNetworkControllerDoubleGanache() { - const ganacheNetworks = mockNetworkState( - { - chainId: CHAIN_IDS.LOCALHOST, - nickname: 'Localhost 8545', - rpcUrl: 'http://localhost:8545', - ticker: 'ETH', - }, - { - id: '76e9cd59-d8e2-47e7-b369-9c205ccb602c', - rpcUrl: 'http://localhost:8546', - chainId: '0x53a', - ticker: 'ETH', - nickname: 'Localhost 8546', - }, - ); + const ganacheNetworks = mockNetworkStateOld({ + id: '76e9cd59-d8e2-47e7-b369-9c205ccb602c', + rpcUrl: 'http://localhost:8546', + chainId: '0x53a', + ticker: 'ETH', + nickname: 'Localhost 8546', + }); delete ganacheNetworks.selectedNetworkClientId; return this.withNetworkController(ganacheNetworks); } withNetworkControllerTripleGanache() { this.withNetworkControllerDoubleGanache(); - merge( - this.fixture.data.NetworkController, - mockNetworkState({ - rpcUrl: 'http://localhost:7777', - chainId: '0x3e8', - ticker: 'ETH', - nickname: 'Localhost 7777', - blockExplorerUrl: undefined, - }), - ); + const thirdGanache = mockNetworkStateOld({ + rpcUrl: 'http://localhost:7777', + chainId: '0x3e8', + ticker: 'ETH', + nickname: 'Localhost 7777', + blockExplorerUrl: undefined, + }); + + delete thirdGanache.selectedNetworkClientId; + merge(this.fixture.data.NetworkController, thirdGanache); return this; } diff --git a/test/e2e/tests/dapp-interactions/block-explorer.spec.js b/test/e2e/tests/dapp-interactions/block-explorer.spec.js index bde8eb0cf485..d9d21e92ee87 100644 --- a/test/e2e/tests/dapp-interactions/block-explorer.spec.js +++ b/test/e2e/tests/dapp-interactions/block-explorer.spec.js @@ -1,5 +1,5 @@ const { strict: assert } = require('assert'); -const { mockNetworkState } = require('../../../stub/networks'); +const { mockNetworkStateOld } = require('../../../stub/networks'); const { defaultGanacheOptions, @@ -15,7 +15,7 @@ describe('Block Explorer', function () { { fixtures: new FixtureBuilder() .withNetworkController( - mockNetworkState({ + mockNetworkStateOld({ chainId: '0x539', nickname: 'Localhost 8545', rpcUrl: 'http://localhost:8545', @@ -60,15 +60,15 @@ describe('Block Explorer', function () { { dapp: true, fixtures: new FixtureBuilder() - .withNetworkController( - mockNetworkState({ + .withNetworkController({ + ...mockNetworkStateOld({ chainId: '0x539', nickname: 'Localhost 8545', rpcUrl: 'http://localhost:8545', ticker: 'ETH', blockExplorerUrl: 'https://etherscan.io/', }), - ) + }) .withTokensControllerERC20() .build(), ganacheOptions: defaultGanacheOptions, @@ -114,8 +114,8 @@ describe('Block Explorer', function () { await withFixtures( { fixtures: new FixtureBuilder() - .withNetworkController( - mockNetworkState({ + .withNetworkController({ + ...mockNetworkStateOld({ id: 'localhost-client-id', chainId: '0x539', nickname: 'Localhost 8545', @@ -123,7 +123,7 @@ describe('Block Explorer', function () { ticker: 'ETH', blockExplorerUrl: 'https://etherscan.io', }), - ) + }) .withTransactionControllerCompletedTransaction() .build(), ganacheOptions: defaultGanacheOptions, diff --git a/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-background-state.json b/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-background-state.json index 9a2c76210b4d..c133de6128ca 100644 --- a/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-background-state.json +++ b/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-background-state.json @@ -143,10 +143,10 @@ "status": "available" } }, - "networkConfigurations": "object" + "networkConfigurationsByChainId": "object" }, "NetworkOrderController": { - "orderedNetworkList": { "0": "object", "1": "object", "2": "object" } + "orderedNetworkList": { "0": "object", "1": "object" } }, "NftController": { "allNftContracts": "object", @@ -215,7 +215,8 @@ "petnamesEnabled": true, "isRedesignedConfirmationsDeveloperEnabled": "boolean", "redesignedConfirmationsEnabled": true, - "redesignedTransactionsEnabled": "boolean" + "redesignedTransactionsEnabled": "boolean", + "showMultiRpcModal": "boolean" }, "ipfsGateway": "string", "isIpfsGatewayEnabled": "boolean", diff --git a/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-ui-state.json b/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-ui-state.json index f474b2492ba4..dfee54fbd6cb 100644 --- a/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-ui-state.json +++ b/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-ui-state.json @@ -17,6 +17,7 @@ "internalAccounts": { "accounts": "object", "selectedAccount": "string" }, "transactions": "object", "networkConfigurations": "object", + "networkConfigurationsByChainId": "object", "addressBook": "object", "confirmationExchangeRates": {}, "pendingTokens": "object", @@ -35,7 +36,8 @@ "petnamesEnabled": true, "isRedesignedConfirmationsDeveloperEnabled": "boolean", "redesignedConfirmationsEnabled": true, - "redesignedTransactionsEnabled": "boolean" + "redesignedTransactionsEnabled": "boolean", + "showMultiRpcModal": "boolean" }, "firstTimeFlowType": "import", "completedOnboarding": true, @@ -146,7 +148,7 @@ "permissionActivityLog": "object", "subjectMetadata": "object", "announcements": "object", - "orderedNetworkList": { "0": "object", "1": "object", "2": "object" }, + "orderedNetworkList": { "0": "object", "1": "object" }, "pinnedAccountList": {}, "hiddenAccountList": {}, "gasFeeEstimatesByChainId": {}, diff --git a/test/e2e/tests/metrics/state-snapshots/errors-before-init-opt-in-background-state.json b/test/e2e/tests/metrics/state-snapshots/errors-before-init-opt-in-background-state.json index 6d3608c22960..b6354922add0 100644 --- a/test/e2e/tests/metrics/state-snapshots/errors-before-init-opt-in-background-state.json +++ b/test/e2e/tests/metrics/state-snapshots/errors-before-init-opt-in-background-state.json @@ -115,7 +115,8 @@ "useNativeCurrencyAsPrimaryCurrency": true, "petnamesEnabled": true, "showConfirmationAdvancedDetails": false, - "isRedesignedConfirmationsDeveloperEnabled": "boolean" + "isRedesignedConfirmationsDeveloperEnabled": "boolean", + "showMultiRpcModal": "boolean" }, "selectedAddress": "string", "theme": "light", diff --git a/test/e2e/tests/metrics/state-snapshots/errors-before-init-opt-in-ui-state.json b/test/e2e/tests/metrics/state-snapshots/errors-before-init-opt-in-ui-state.json index 1f607a7c9d46..51910b2057a7 100644 --- a/test/e2e/tests/metrics/state-snapshots/errors-before-init-opt-in-ui-state.json +++ b/test/e2e/tests/metrics/state-snapshots/errors-before-init-opt-in-ui-state.json @@ -115,7 +115,8 @@ "useNativeCurrencyAsPrimaryCurrency": true, "petnamesEnabled": true, "showConfirmationAdvancedDetails": false, - "isRedesignedConfirmationsDeveloperEnabled": "boolean" + "isRedesignedConfirmationsDeveloperEnabled": "boolean", + "showMultiRpcModal": "boolean" }, "selectedAddress": "string", "theme": "light", diff --git a/test/e2e/tests/network/add-custom-network.spec.js b/test/e2e/tests/network/add-custom-network.spec.js index df87c915a742..70325cb5155b 100644 --- a/test/e2e/tests/network/add-custom-network.spec.js +++ b/test/e2e/tests/network/add-custom-network.spec.js @@ -1,6 +1,5 @@ const { strict: assert } = require('assert'); const { toHex } = require('@metamask/controller-utils'); -const { mockNetworkState } = require('../../../stub/networks'); const FixtureBuilder = require('../../fixture-builder'); const { defaultGanacheOptions, @@ -10,6 +9,7 @@ const { regularDelayMs, unlockWallet, WINDOW_TITLES, + tinyDelayMs, } = require('../../helpers'); const TEST_CHAIN_ID = toHex(100); @@ -90,14 +90,14 @@ const selectors = { tickerWarning: '[data-testid="network-form-ticker-warning"]', suggestedTickerForXDAI: { css: '[data-testid="network-form-ticker-suggestion"]', - text: 'Suggested ticker symbol: XDAI', + text: 'Suggested currency symbol: XDAI', }, tickerWarningTokenSymbol: { css: '[data-testid="network-form-ticker-warning"]', - text: "This token symbol doesn't match the network name or chain ID entered.", + text: "This token symbol doesn't match the network name or chain ID entered. Many popular tokens use similar symbols, which scammers can use to trick you into sending them a more valuable token in return. Verify everything before you continue.", }, tickerButton: { text: 'PETH', tag: 'button' }, - networkAdded: { text: 'Network added successfully!', tag: 'h4' }, + networkAdded: { text: 'Network added successfully!' }, networkNameInputField: '[data-testid="network-form-network-name"]', networkNameInputFieldSetToEthereumMainnet: { @@ -111,15 +111,6 @@ const selectors = { errorContainer: '.settings-tab__error', }; -async function navigateToAddNetwork(driver) { - await openMenuSafe(driver); - - await driver.clickElement(selectors.settingsOption); - await driver.clickElement(selectors.networkOption); - await driver.clickElement(selectors.addNetwork); - await driver.clickElement(selectors.addNetworkManually); -} - const inputData = { networkName: 'Collision network', rpcUrl: 'https://responsive-rpc.test/', @@ -467,7 +458,6 @@ describe('Custom network', function () { await driver.clickElement('[data-testid="network-display"]'); - await driver.clickElement({ tag: 'button', text: 'Add network' }); await driver.clickElement({ tag: 'button', text: 'Add', @@ -475,7 +465,7 @@ describe('Custom network', function () { // verify network details const title = await driver.findElement({ - tag: 'h6', + tag: 'span', text: 'Arbitrum One', }); @@ -520,64 +510,11 @@ describe('Custom network', function () { await driver.clickElement({ tag: 'button', text: 'Close' }); await driver.clickElement({ tag: 'button', text: 'Approve' }); - await driver.clickElement({ - tag: 'h6', - text: 'Switch to Arbitrum One', - }); - // verify network switched - const networkDisplayed = await driver.findElement({ - tag: 'span', - text: 'Arbitrum One', - }); - assert.equal( - await networkDisplayed.getText(), - 'Arbitrum One', - 'You have not switched to Arbitrum Network', - ); - }, - ); - }); - - it('add custom network and not switch the network', async function () { - await withFixtures( - { - fixtures: new FixtureBuilder().build(), - ganacheOptions: defaultGanacheOptions, - title: this.test.fullTitle(), - }, - async ({ driver }) => { - await unlockWallet(driver); - - // Avoid a stale element error - await driver.delay(regularDelayMs); - - await driver.clickElement('[data-testid="network-display"]'); - await driver.clickElement({ tag: 'button', text: 'Add network' }); - - // had to put all Add elements in list since list is changing and networks are not always in same order - await driver.clickElement({ - tag: 'button', - text: 'Add', - }); - - await driver.clickElement({ tag: 'button', text: 'Approve' }); - - await driver.clickElement({ - tag: 'h6', - text: 'Dismiss', - }); - // verify if added network is in list of networks - const networkDisplay = await driver.findElement( - '[data-testid="network-display"]', + // verify network switched + await driver.waitForSelector( + 'button[data-testid="network-display"][aria-label="Network Menu Arbitrum One"]', ); - await networkDisplay.click(); - - const arbitrumNetwork = await driver.findElements({ - text: 'Arbitrum One', - tag: 'p', - }); - assert.ok(arbitrumNetwork.length, 1); }, ); }); @@ -587,13 +524,27 @@ describe('Custom network', function () { { fixtures: new FixtureBuilder() .withNetworkController({ - ...mockNetworkState({ - rpcUrl: networkURL, - chainId: chainID, - nickname: networkNAME, - ticker: currencySYMBOL, - }), - selectedNetworkClientId: 'mainnet', + providerConfig: { + rpcPrefs: { blockExplorerUrl: 'https://etherscan.io/' }, + }, + networkConfigurations: { + networkConfigurationId: { + chainId: '0x539', + nickname: 'Localhost 8545', + rpcUrl: 'http://localhost:8545', + ticker: 'ETH', + rpcPrefs: { blockExplorerUrl: 'https://etherscan.io/' }, + }, + '2ce66016-8aab-47df-b27f-318c80865eb0': { + chainId: '0xa4b1', + id: '2ce66016-8aab-47df-b27f-318c80865eb0', + nickname: 'Arbitrum mainnet', + rpcPrefs: {}, + rpcUrl: 'https://arbitrum-mainnet.infura.io', + ticker: 'ETH', + }, + }, + selectedNetworkClientId: 'networkConfigurationId', }) .build(), ganacheOptions: defaultGanacheOptions, @@ -601,26 +552,37 @@ describe('Custom network', function () { }, async ({ driver }) => { await unlockWallet(driver); + // Avoid a stale element error + await driver.delay(regularDelayMs); + await driver.clickElement('[data-testid="network-display"]'); + // ===========================================================> + + // Go to Edit Menu + const networkMenu = await driver.findElement( + '[data-testid="network-list-item-options-button-0xa4b1"]', + ); - await openMenuSafe(driver); + await networkMenu.click(); - await driver.clickElement('[data-testid="global-menu-settings"]'); - await driver.clickElement({ text: 'Networks', tag: 'div' }); + const deleteButton = await driver.findElement( + '[data-testid="network-list-item-options-delete"]', + ); + deleteButton.click(); - const arbitrumNetwork = await driver.clickElement({ - text: 'Arbitrum One', - tag: 'div', + await driver.clickElement({ + tag: 'button', + text: 'Delete', }); - // Click first Delete button - await driver.clickElement('button.btn-danger'); + await driver.clickElement('[data-testid="network-display"]'); - // Click modal Delete button - await driver.clickElement('button.btn-danger-primary'); + // check if arbitrum is on the list of popular network + const popularNetworkArbitrum = await driver.findElement( + '[data-testid="popular-network-0xa4b1"]', + ); - // Checks if Arbitrum is deleted - const existNetwork = await driver.isElementPresent(arbitrumNetwork); - assert.equal(existNetwork, false, 'Network is not deleted'); + const existNetwork = popularNetworkArbitrum !== undefined; + assert.equal(existNetwork, true, 'Network is not deleted'); }, ); }); @@ -727,25 +689,76 @@ describe('Custom network', function () { async ({ driver }) => { await unlockWallet(driver); - await navigateToAddNetwork(driver); - await driver.fill( - selectors.networkNameInputField, - 'Ethereum mainnet', - ); - await driver.fill( - selectors.rpcUrlInputField, - 'https://responsive-rpc.test', - ); + await driver.clickElement('[data-testid="network-display"]'); + await driver.clickElement({ + text: 'Add a custom network', + tag: 'button', + }); + + await driver.fill(selectors.networkNameInputField, 'Gnosis'); await driver.fill(selectors.chainIdInputField, TEST_CHAIN_ID); await driver.fill(selectors.tickerInputField, 'XDAI'); - await driver.fill(selectors.explorerInputField, 'https://test.com'); + + // Add rpc url + const rpcUrlInputDropDown = await driver.waitForSelector( + '[data-testid="test-add-rpc-drop-down"]', + ); + await rpcUrlInputDropDown.click(); + await driver.delay(tinyDelayMs); + await driver.clickElement({ + text: 'Add RPC URL', + tag: 'button', + }); + + const rpcUrlInput = await driver.waitForSelector( + '[data-testid="rpc-url-input-test"]', + ); + await rpcUrlInput.clear(); + await rpcUrlInput.sendKeys('https://responsive-rpc.test'); + + const rpcNameInput = await driver.waitForSelector( + '[data-testid="rpc-name-input-test"]', + ); + await rpcNameInput.sendKeys('testName'); + + await driver.clickElement({ + text: 'Add URL', + tag: 'button', + }); + + // Add explorer URL + const explorerUrlInputDropDown = await driver.waitForSelector( + '[data-testid="test-explorer-drop-down"]', + ); + + await driver.scrollToElement(explorerUrlInputDropDown); + await driver.delay(tinyDelayMs); + + await explorerUrlInputDropDown.click(); + await driver.delay(tinyDelayMs); + + await driver.clickElement({ + text: 'Add a block explorer URL', + tag: 'button', + }); + + const blockExplorerInput = await driver.waitForSelector( + '[data-testid="explorer-url-input"]', + ); + await blockExplorerInput.clear(); + await blockExplorerInput.sendKeys('https://test.com'); + + await driver.clickElement({ + text: 'Add URL', + tag: 'button', + }); const suggestedTicker = await driver.isElementPresent( - selectors.suggestedTickerForXDAI, + selectors.suggestedTickerForXDAI.css, ); const tickerWarning = await driver.isElementPresent( - selectors.tickerWarningTokenSymbol, + selectors.tickerWarningTokenSymbol.css, ); assert.equal(suggestedTicker, false); @@ -754,10 +767,10 @@ describe('Custom network', function () { await driver.clickElement(selectors.saveButton); // Validate the network was added - const networkAdded = await driver.isElementPresent( - selectors.networkAdded, - ); - assert.equal(networkAdded, true, 'Network added successfully!'); + const networkAdded = await driver.isElementPresent({ + text: '“Gnosis” was successfully added!', + }); + assert.equal(networkAdded, true, '“Gnosis” was successfully added!'); }, ); }); @@ -788,32 +801,78 @@ describe('Custom network', function () { async ({ driver }) => { await unlockWallet(driver); - await navigateToAddNetwork(driver); + + await driver.clickElement('[data-testid="network-display"]'); + await driver.clickElement({ + text: 'Add a custom network', + tag: 'button', + }); + await driver.fill( selectors.networkNameInputField, 'Ethereum mainnet', ); - await driver.fill( - selectors.rpcUrlInputField, - 'https://responsive-rpc.test', - ); + await driver.fill(selectors.chainIdInputField, '1'); await driver.fill(selectors.tickerInputField, 'TST'); - // fix flaky test - await driver.delay(regularDelayMs); - await driver.fill(selectors.explorerInputField, 'https://test.com'); - const suggestedTicker = await driver.isElementPresent( - selectors.suggestedTicker, + // Add rpc url + const rpcUrlInputDropDown = await driver.waitForSelector( + '[data-testid="test-add-rpc-drop-down"]', ); + await rpcUrlInputDropDown.click(); + await driver.delay(tinyDelayMs); + await driver.clickElement({ + text: 'Add RPC URL', + tag: 'button', + }); - const tickerWarning = await driver.isElementPresent( - selectors.tickerWarning, + const rpcUrlInput = await driver.waitForSelector( + '[data-testid="rpc-url-input-test"]', ); + await rpcUrlInput.clear(); + await rpcUrlInput.sendKeys('https://responsive-rpc.test'); + + const rpcNameInput = await driver.waitForSelector( + '[data-testid="rpc-name-input-test"]', + ); + await rpcNameInput.sendKeys('testName'); + + await driver.clickElement({ + text: 'Add URL', + tag: 'button', + }); // suggestion and warning ticker should be displayed - assert.equal(suggestedTicker, true); - assert.equal(tickerWarning, true); + await driver.waitForSelector(selectors.suggestedTicker); + await driver.waitForSelector(selectors.tickerWarning); + + // Add explorer URL + const explorerUrlInputDropDown = await driver.waitForSelector( + '[data-testid="test-explorer-drop-down"]', + ); + + await driver.scrollToElement(explorerUrlInputDropDown); + await driver.delay(tinyDelayMs); + + await explorerUrlInputDropDown.click(); + await driver.delay(tinyDelayMs); + + await driver.clickElement({ + text: 'Add a block explorer URL', + tag: 'button', + }); + + const blockExplorerInput = await driver.waitForSelector( + '[data-testid="explorer-url-input"]', + ); + await blockExplorerInput.clear(); + await blockExplorerInput.sendKeys('https://test.com'); + + await driver.clickElement({ + text: 'Add URL', + tag: 'button', + }); }, ); }); @@ -842,18 +901,48 @@ describe('Custom network', function () { async ({ driver }) => { await unlockWallet(driver); - await navigateToAddNetwork(driver); + + await driver.clickElement('[data-testid="network-display"]'); + await driver.clickElement({ + text: 'Add a custom network', + tag: 'button', + }); + await driver.fill( selectors.networkNameInputField, inputData.networkName, ); - await driver.fill(selectors.rpcUrlInputField, inputData.rpcUrl); - // fix flaky test - await driver.delay(regularDelayMs); await driver.fill(selectors.chainIdInputField, inputData.chainId); await driver.fill(selectors.tickerInputField, inputData.ticker); + // Add rpc url + const rpcUrlInputDropDown = await driver.waitForSelector( + '[data-testid="test-add-rpc-drop-down"]', + ); + await rpcUrlInputDropDown.click(); + await driver.delay(tinyDelayMs); + await driver.clickElement({ + text: 'Add RPC URL', + tag: 'button', + }); + + const rpcUrlInput = await driver.waitForSelector( + '[data-testid="rpc-url-input-test"]', + ); + await rpcUrlInput.clear(); + await rpcUrlInput.sendKeys(inputData.rpcUrl); + + const rpcNameInput = await driver.waitForSelector( + '[data-testid="rpc-name-input-test"]', + ); + await rpcNameInput.sendKeys('testName'); + + await driver.clickElement({ + text: 'Add URL', + tag: 'button', + }); + const suggestedTicker = await driver.isElementPresent( selectors.suggestedTicker, ); @@ -865,14 +954,19 @@ describe('Custom network', function () { assert.equal(suggestedTicker, true); assert.equal(tickerWarning, true); - await driver.clickElement(selectors.tickerButton); - await driver.clickElement(selectors.saveButton); + driver.clickElement(selectors.tickerButton); + driver.clickElement(selectors.saveButton); // Validate the network was added - const networkAdded = await driver.isElementPresent( - selectors.networkAdded, + const networkAdded = await driver.isElementPresent({ + text: `“${inputData.networkName}” was successfully added!`, + }); + + assert.equal( + networkAdded, + true, + `“${inputData.networkName}” was successfully added!`, ); - assert.equal(networkAdded, true, 'Network added successfully!'); }, ); }); @@ -921,35 +1015,76 @@ async function failCandidateNetworkValidation(driver) { await driver.waitForSelector(networkMenuSelector); await driver.clickElement(networkMenuSelector); - await driver.clickElement({ text: 'Add network', tag: 'button' }); - - const addNetworkManuallyButtonSelector = - '[data-testid="add-network-manually"]'; - await driver.waitForSelector(addNetworkManuallyButtonSelector); - - await driver.clickElement(`${addNetworkManuallyButtonSelector} > h6`); + await driver.clickElement({ text: 'Add a custom network', tag: 'button' }); const [ - , // first element is the search input that we don't need to fill networkNameInputEl, - newRPCURLInputEl, - chainIDInputEl, , - blockExplorerURLInputEl, + chainIDInputEl, ] = await driver.findElements('input'); await networkNameInputEl.fill('cheapETH'); - await newRPCURLInputEl.fill('https://unresponsive-rpc.test'); await chainIDInputEl.fill(toHex(777)); await driver.fill('[data-testid="network-form-ticker-input"]', 'cTH'); - await blockExplorerURLInputEl.fill('https://block-explorer.url'); + + // Add rpc URL + const rpcUrlInputDropDown = await driver.waitForSelector( + '[data-testid="test-add-rpc-drop-down"]', + ); + await rpcUrlInputDropDown.click(); + await driver.delay(tinyDelayMs); + await driver.clickElement({ + text: 'Add RPC URL', + tag: 'button', + }); + + const rpcUrlInput = await driver.waitForSelector( + '[data-testid="rpc-url-input-test"]', + ); + await rpcUrlInput.clear(); + await rpcUrlInput.sendKeys('https://unresponsive-rpc.test'); + + const rpcNameInput = await driver.waitForSelector( + '[data-testid="rpc-name-input-test"]', + ); + await rpcNameInput.sendKeys('testName'); + + await driver.clickElement({ + text: 'Add URL', + tag: 'button', + }); + + // Add explorer URL + const explorerUrlInputDropDown = await driver.waitForSelector( + '[data-testid="test-explorer-drop-down"]', + ); + + await driver.scrollToElement(explorerUrlInputDropDown); + await explorerUrlInputDropDown.click(); + await driver.delay(tinyDelayMs); + + await driver.clickElement({ + text: 'Add a block explorer URL', + tag: 'button', + }); + + const blockExplorerInput = await driver.waitForSelector( + '[data-testid="explorer-url-input"]', + ); + blockExplorerInput.clear(); + await blockExplorerInput.sendKeys('https://block-explorer.url'); + + await driver.clickElement({ + text: 'Add URL', + tag: 'button', + }); const chainIdValidationMessageRawLocator = { text: 'Could not fetch chain ID. Is your RPC URL correct?', }; + await driver.waitForSelector(chainIdValidationMessageRawLocator); - await driver.waitForSelector('[data-testid="network-form-ticker-warning"]'); const saveButtonRawLocator = { text: 'Save', @@ -1012,7 +1147,7 @@ async function toggleOffSafeChainsListValidation(driver) { 'Safe chains list validation toggle is ON', ); - await driver.delay(regularDelayMs); + driver.delay(regularDelayMs); // return to the home screen const appHeaderSelector = '[data-testid="app-header-logo"]'; @@ -1025,28 +1160,70 @@ async function candidateNetworkIsNotValidated(driver) { await driver.waitForSelector(networkMenuSelector); await driver.clickElement(networkMenuSelector); - await driver.clickElement({ text: 'Add network', tag: 'button' }); - - const addNetworkManuallyButtonSelector = - '[data-testid="add-network-manually"]'; - await driver.waitForSelector(addNetworkManuallyButtonSelector); - await driver.clickElement(`${addNetworkManuallyButtonSelector} > h6`); + await driver.clickElement({ text: 'Add a custom network', tag: 'button' }); const [ - , // first element is the search input that we don't need to fill networkNameInputEl, - newRPCURLInputEl, - chainIDInputEl, , - blockExplorerURLInputEl, + chainIDInputEl, ] = await driver.findElements('input'); await networkNameInputEl.fill('cheapETH'); - await newRPCURLInputEl.fill('https://responsive-rpc.test/'); await chainIDInputEl.fill(TEST_CHAIN_ID); await driver.fill('[data-testid="network-form-ticker-input"]', 'cTH'); - await blockExplorerURLInputEl.fill('https://block-explorer.url'); + + // Add rpc URL + const rpcUrlInputDropDown = await driver.waitForSelector( + '[data-testid="test-add-rpc-drop-down"]', + ); + await rpcUrlInputDropDown.click(); + await driver.delay(tinyDelayMs); + await driver.clickElement({ + text: 'Add RPC URL', + tag: 'button', + }); + + const rpcUrlInput = await driver.waitForSelector( + '[data-testid="rpc-url-input-test"]', + ); + await rpcUrlInput.clear(); + await rpcUrlInput.sendKeys('https://responsive-rpc.test'); + + const rpcNameInput = await driver.waitForSelector( + '[data-testid="rpc-name-input-test"]', + ); + await rpcNameInput.sendKeys('testName'); + + await driver.clickElement({ + text: 'Add URL', + tag: 'button', + }); + + // Add explorer URL + const explorerUrlInputDropDown = await driver.waitForSelector( + '[data-testid="test-explorer-drop-down"]', + ); + + await driver.scrollToElement(explorerUrlInputDropDown); + await explorerUrlInputDropDown.click(); + await driver.delay(tinyDelayMs); + + await driver.clickElement({ + text: 'Add a block explorer URL', + tag: 'button', + }); + + const blockExplorerInput = await driver.waitForSelector( + '[data-testid="explorer-url-input"]', + ); + blockExplorerInput.clear(); + await blockExplorerInput.sendKeys('https://block-explorer.url'); + + await driver.clickElement({ + text: 'Add URL', + tag: 'button', + }); // fix flaky test await driver.delay(regularDelayMs); diff --git a/test/e2e/tests/network/custom-rpc-history.spec.js b/test/e2e/tests/network/custom-rpc-history.spec.js index b8bda1a2f14f..6e92f532698f 100644 --- a/test/e2e/tests/network/custom-rpc-history.spec.js +++ b/test/e2e/tests/network/custom-rpc-history.spec.js @@ -1,5 +1,5 @@ const { strict: assert } = require('assert'); -const { mockNetworkState } = require('../../../stub/networks'); +const { mockNetworkStateOld } = require('../../../stub/networks'); const { defaultGanacheOptions, @@ -7,6 +7,7 @@ const { withFixtures, regularDelayMs, unlockWallet, + tinyDelayMs, } = require('../../helpers'); const FixtureBuilder = require('../../fixture-builder'); @@ -15,6 +16,7 @@ describe('Custom RPC history', function () { const port = 8546; const chainId = 1338; const symbol = 'TEST'; + await withFixtures( { fixtures: new FixtureBuilder().build(), @@ -29,43 +31,62 @@ describe('Custom RPC history', function () { const rpcUrl = `http://127.0.0.1:${port}`; const networkName = 'Secondary Ganache Testnet'; - await driver.assertElementNotPresent('.loading-overlay'); await driver.clickElement('[data-testid="network-display"]'); - - await driver.clickElement({ text: 'Add network', tag: 'button' }); - - await driver.findElement('.add-network__networks-container'); - await driver.clickElement({ - text: 'Add a network manually', - tag: 'h6', + text: 'Add a custom network', + tag: 'button', }); - await driver.findElement('.networks-tab__subheader'); - - const customRpcInputs = await driver.findElements('input[type="text"]'); - const networkNameInput = customRpcInputs[1]; - const rpcUrlInput = customRpcInputs[2]; - const chainIdInput = customRpcInputs[3]; - const symbolInput = customRpcInputs[4]; + await driver.fill( + '[data-testid="network-form-network-name"]', + networkName, + ); + await driver.fill( + '[data-testid="network-form-chain-id"]', + chainId.toString(), + ); + await driver.fill('[data-testid="network-form-ticker-input"]', symbol); - await networkNameInput.clear(); - await networkNameInput.sendKeys(networkName); + // Add rpc url + const rpcUrlInputDropDown = await driver.waitForSelector( + '[data-testid="test-add-rpc-drop-down"]', + ); + await rpcUrlInputDropDown.click(); + await driver.delay(tinyDelayMs); + await driver.clickElement({ + text: 'Add RPC URL', + tag: 'button', + }); + const rpcUrlInput = await driver.waitForSelector( + '[data-testid="rpc-url-input-test"]', + ); await rpcUrlInput.clear(); await rpcUrlInput.sendKeys(rpcUrl); - await chainIdInput.clear(); - await chainIdInput.sendKeys(chainId.toString()); + const rpcNameInput = await driver.waitForSelector( + '[data-testid="rpc-name-input-test"]', + ); + await rpcNameInput.sendKeys('test-name'); - await symbolInput.clear(); - await symbolInput.sendKeys(symbol); + await driver.clickElement({ + text: 'Add URL', + tag: 'button', + }); - await driver.clickElement( - '.networks-tab__add-network-form-footer .btn-primary', - ); + await driver.clickElement({ text: 'Save', tag: 'button' }); await driver.findElement({ text: networkName, tag: 'h6' }); + + // Validate the network was added + const networkAdded = await driver.isElementPresent({ + text: '“Secondary Ganache Testnet” was successfully added!', + }); + assert.equal( + networkAdded, + true, + '“Secondary Ganache Testnet” was successfully added!', + ); }, ); }); @@ -86,24 +107,42 @@ describe('Custom RPC history', function () { await driver.assertElementNotPresent('.loading-overlay'); await driver.clickElement('[data-testid="network-display"]'); - await driver.clickElement({ text: 'Add network', tag: 'button' }); - - await driver.findElement('.add-network__networks-container'); - await driver.clickElement({ - text: 'Add a network manually', - tag: 'h6', + text: 'Add a custom network', + tag: 'button', }); - await driver.findElement('.networks-tab__subheader'); - - const customRpcInputs = await driver.findElements('input[type="text"]'); - const rpcUrlInput = customRpcInputs[2]; + // Add rpc url + const rpcUrlInputDropDown = await driver.waitForSelector( + '[data-testid="test-add-rpc-drop-down"]', + ); + await rpcUrlInputDropDown.click(); + await driver.delay(tinyDelayMs); + await driver.clickElement({ + text: 'Add RPC URL', + tag: 'button', + }); + const rpcUrlInput = await driver.waitForSelector( + '[data-testid="rpc-url-input-test"]', + ); await rpcUrlInput.clear(); await rpcUrlInput.sendKeys(duplicateRpcUrl); + + const rpcNameInput = await driver.waitForSelector( + '[data-testid="rpc-name-input-test"]', + ); + await rpcNameInput.sendKeys('test-name'); + + await driver.clickElement({ + text: 'Add URL', + tag: 'button', + }); + + await driver.fill('[data-testid="network-form-chain-id"]', '1'); + await driver.findElement({ - text: 'This URL is currently used by the mainnet network.', + text: 'This Chain ID is currently used by the Ethereum Mainnet network.', }); }, ); @@ -120,46 +159,49 @@ describe('Custom RPC history', function () { await unlockWallet(driver); // duplicate network - const newRpcUrl = 'http://localhost:8544'; const duplicateChainId = '1'; await driver.assertElementNotPresent('.loading-overlay'); await driver.clickElement('[data-testid="network-display"]'); - await driver.clickElement({ text: 'Add network', tag: 'button' }); - - await driver.findElement('.add-network__networks-container'); - await driver.clickElement({ - text: 'Add a network manually', - tag: 'h6', + text: 'Add a custom network', + tag: 'button', }); - await driver.findElement('.networks-tab__subheader'); - - const customRpcInputs = await driver.findElements('input[type="text"]'); - const rpcUrlInput = customRpcInputs[2]; - const chainIdInput = customRpcInputs[3]; + await driver.fill( + '[data-testid="network-form-chain-id"]', + duplicateChainId, + ); - await chainIdInput.clear(); - await chainIdInput.sendKeys(duplicateChainId); await driver.findElement({ - text: 'This Chain ID is currently used by the mainnet network.', + text: 'This Chain ID is currently used by the Ethereum Mainnet network.', + }); + + // Add invalid rcp url + const rpcUrlInputDropDown = await driver.waitForSelector( + '[data-testid="test-add-rpc-drop-down"]', + ); + await rpcUrlInputDropDown.click(); + await driver.delay(tinyDelayMs); + await driver.clickElement({ + text: 'Add RPC URL', + tag: 'button', }); + const rpcUrlInput = await driver.waitForSelector( + '[data-testid="rpc-url-input-test"]', + ); await rpcUrlInput.clear(); + await rpcUrlInput.sendKeys('test'); - // We cannot use sendKeys() here, because a network request will be fired after each - // keypress, and the privacy snapshot will show: - // `New hosts found: l,lo,loc,loca,local,localh,localho,localhos` - // In the longer term, we may want to debounce this - await driver.pasteIntoField( - '.form-field:nth-of-type(2) input[type="text"]', - newRpcUrl, + const rpcNameInput = await driver.waitForSelector( + '[data-testid="rpc-name-input-test"]', ); + await rpcNameInput.sendKeys('test-name'); await driver.findElement({ - text: 'Could not fetch chain ID. Is your RPC URL correct?', + text: 'URLs require the appropriate HTTP/HTTPS prefix.', }); }, ); @@ -184,7 +226,7 @@ describe('Custom RPC history', function () { }); it('finds all recent RPCs in history', async function () { - const networkState = mockNetworkState( + const networkState = mockNetworkStateOld( { rpcUrl: 'http://127.0.0.1:8545/1', chainId: '0x539', @@ -220,20 +262,21 @@ describe('Custom RPC history', function () { // only recent 3 are found and in correct order (most recent at the top) const customRpcs = await driver.findElements({ - text: 'http://127.0.0.1:8545/', - tag: 'div', + text: 'Localhost 8545', + tag: 'p', }); // click Mainnet to dismiss network dropdown await driver.clickElement({ text: 'Ethereum Mainnet', tag: 'p' }); - assert.equal(customRpcs.length, 2); + // custom rpcs length is 1 because networks has been merged + assert.equal(customRpcs.length, 1); }, ); }); it('deletes a custom RPC', async function () { - const networkState = mockNetworkState( + const networkState = mockNetworkStateOld( { rpcUrl: 'http://127.0.0.1:8545/1', chainId: '0x539', @@ -242,7 +285,7 @@ describe('Custom RPC history', function () { }, { rpcUrl: 'http://127.0.0.1:8545/2', - chainId: '0x539', + chainId: '0x540', ticker: 'ETH', nickname: 'http://127.0.0.1:8545/2', }, @@ -263,42 +306,34 @@ describe('Custom RPC history', function () { await driver.assertElementNotPresent('.loading-overlay'); // Click add network from network options await driver.clickElement('[data-testid="network-display"]'); - await driver.clickElement({ text: 'Add network', tag: 'button' }); - // Open network settings page - await driver.findElement('.add-network__networks-container'); - // Click Add network manually to trigger form - await driver.clickElement({ - text: 'Add a network manually', - tag: 'h6', - }); - // cancel new custom rpc - await driver.clickElement( - '.networks-tab__add-network-form-footer button.btn-secondary', - ); - // find custom network http://127.0.0.1:8545/2 - const networkItemClassName = '.networks-tab__networks-list-name'; + const customNetworkName = 'http://127.0.0.1:8545/2'; + const networkItemClassName = '.multichain-network-list-item'; + const networkListItems = await driver.findClickableElements( networkItemClassName, ); - const lastNetworkListItem = - networkListItems[networkListItems.length - 1]; - await lastNetworkListItem.click(); - await driver.waitForSelector({ - css: '.form-field .form-field__input:nth-of-type(1)', - value: customNetworkName, - }); - // delete custom network in a modal + + // click on menu button await driver.clickElement( - '.networks-tab__network-form-footer .btn-danger', + '[data-testid="network-list-item-options-button-0x540"]', ); - await driver.findVisibleElement( - '[data-testid="confirm-delete-network-modal"]', + + // click on delere button + await driver.clickElement( + '[data-testid="network-list-item-options-delete"]', ); - await driver.clickElement({ text: 'Delete', tag: 'button' }); + + // click on delete button + await driver.clickElement({ + text: 'Delete', + tag: 'button', + }); + await driver.assertElementNotPresent( '[data-testid="confirm-delete-network-modal"]', ); + // There's a short slot to process deleting the network, // hence there's a need to wait for the element to be removed to guarantee the action is executed completely await driver.assertElementNotPresent({ @@ -306,6 +341,9 @@ describe('Custom RPC history', function () { text: customNetworkName, }); + // Click add network from network options + await driver.clickElement('[data-testid="network-display"]'); + // custom network http://127.0.0.1:8545/2 is removed from network list const newNetworkListItems = await driver.findElements( networkItemClassName, diff --git a/test/e2e/tests/network/update-network.spec.ts b/test/e2e/tests/network/update-network.spec.ts index 4242dfe1533f..54ee7a0a3afe 100644 --- a/test/e2e/tests/network/update-network.spec.ts +++ b/test/e2e/tests/network/update-network.spec.ts @@ -3,6 +3,7 @@ import { Suite } from 'mocha'; import FixtureBuilder from '../../fixture-builder'; import { defaultGanacheOptions, + tinyDelayMs, unlockWallet, withFixtures, } from '../../helpers'; @@ -42,9 +43,11 @@ const inputData = { }; async function navigateToEditNetwork(driver: Driver) { - await driver.clickElement(selectors.accountOptionsMenuButton); - await driver.clickElement(selectors.settingsOption); - await driver.clickElement(selectors.networkOption); + await driver.clickElement('.mm-picker-network'); + await driver.clickElement( + '[data-testid="network-list-item-options-button-0x539"]', + ); + await driver.clickElement('[data-testid="network-list-item-options-edit"]'); } describe('Update Network:', function (this: Suite) { @@ -59,21 +62,25 @@ describe('Update Network:', function (this: Suite) { async ({ driver }: { driver: Driver }) => { await unlockWallet(driver); await navigateToEditNetwork(driver); + + // Verify chain id is not editable when updating a network + const chainIdInput = await driver.findElement( + selectors.chainIdInputField, + ); + assert.equal( + await chainIdInput.isEnabled(), + false, + 'chain id input should be disabled', + ); + + // Update the network name await driver.fill( selectors.networkNameInputField, inputData.networkName, ); - // We fill in the chain ID in two steps, allowing the error message time to disappear once the field is correctly completed. - await driver.fill(selectors.chainIdInputField, inputData.chainId_part1); - const chainIdInputField = await driver.findElement( - selectors.chainIdInputField, - ); - await chainIdInputField.sendKeys(inputData.chainId_part2); - + // Save, and verify the new network name is visible await driver.clickElement(selectors.saveButton); - - // Validate the network name is updated const updatedNetworkNamePresent = await driver.isElementPresent( selectors.updatedNetworkDropDown, ); @@ -83,8 +90,23 @@ describe('Update Network:', function (this: Suite) { 'Network name is not updated', ); + // Start another edit await navigateToEditNetwork(driver); - await driver.fill(selectors.rpcUrlInputField, inputData.rpcUrl); + + // Edit the RPC URL to something invalid + const rpcUrlInputDropDown = await driver.waitForSelector( + '[data-testid="test-add-rpc-drop-down"]', + ); + await rpcUrlInputDropDown.click(); + await driver.delay(tinyDelayMs); + await driver.clickElement({ + text: 'Add RPC URL', + tag: 'button', + }); + const rpcUrlInput = await driver.waitForSelector( + '[data-testid="rpc-url-input-test"]', + ); + await rpcUrlInput.sendKeys(inputData.rpcUrl); // Validate the error message that appears for the invalid url format const errorMessage = await driver.isElementPresent( @@ -97,35 +119,15 @@ describe('Update Network:', function (this: Suite) { ); // Validate the Save button is disabled for the invalid url format - const saveButton = await driver.findElement(selectors.saveButton); - const saveButtonEnabled = await saveButton.isEnabled(); - assert.equal(saveButtonEnabled, false, 'Save button is enabled'); - - // Validate the information symbol appears for chain id - const informationSymbolAppears = await driver.isElementPresent( - selectors.informationSymbol, - ); + const addUrlButton = await driver.findElement({ + text: 'Add URL', + tag: 'button', + }); assert.equal( - informationSymbolAppears, - true, - 'Information symbol did not appear for chain id', + await addUrlButton.isEnabled(), + false, + 'Add url button should not be enabled', ); - - await driver.clickElement(selectors.ethereumNetwork); - - // Validate the Save, Cancel, and Delete buttons are not present for the default network - await driver.assertElementNotPresent(selectors.deleteButton, { - findElementGuard: selectors.networkNameInputFieldSetToEthereumMainnet, // Wait for the network selection to complete - }); - // The findElementGuard above is sufficient for the next two assertions - await driver.assertElementNotPresent(selectors.cancelButton); - await driver.assertElementNotPresent(selectors.saveButton); - - // Validate the error does not appear on the General tab - await driver.clickElement(selectors.generalOption); - await driver.assertElementNotPresent(selectors.errorContainer, { - findElementGuard: selectors.generalTabHeader, // Wait for the General tab to load - }); }, ); }); diff --git a/test/e2e/tests/onboarding/onboarding.spec.js b/test/e2e/tests/onboarding/onboarding.spec.js index c9cb594f403d..1aa716953703 100644 --- a/test/e2e/tests/onboarding/onboarding.spec.js +++ b/test/e2e/tests/onboarding/onboarding.spec.js @@ -1,4 +1,5 @@ const { strict: assert } = require('assert'); +const { toHex } = require('@metamask/controller-utils'); const { By } = require('selenium-webdriver'); const { TEST_SEED_PHRASE, @@ -19,6 +20,7 @@ const { onboardingCompleteWalletCreation, regularDelayMs, unlockWallet, + tinyDelayMs, } = require('../../helpers'); const FixtureBuilder = require('../../fixture-builder'); const { @@ -277,29 +279,54 @@ describe('MetaMask onboarding @no-mmi', function () { // Add custom network localhost 8546 during onboarding await driver.clickElement({ text: 'Advanced configuration', tag: 'a' }); - await driver.clickElement('.mm-picker-network'); + await driver.clickElement({ text: 'Add a network' }); + await driver.waitForSelector( + '.multichain-network-list-menu-content-wrapper__dialog', + ); + + await driver.fill( + '[data-testid="network-form-network-name"]', + networkName, + ); + await driver.fill( + '[data-testid="network-form-chain-id"]', + chainId.toString(), + ); + await driver.fill( + '[data-testid="network-form-ticker-input"]', + currencySymbol, + ); + + // Add rpc url + const rpcUrlInputDropDown = await driver.waitForSelector( + '[data-testid="test-add-rpc-drop-down"]', + ); + await rpcUrlInputDropDown.click(); + await driver.delay(tinyDelayMs); await driver.clickElement({ - text: 'Add network', + text: 'Add RPC URL', + tag: 'button', + }); + const rpcUrlInput = await driver.waitForSelector( + '[data-testid="rpc-url-input-test"]', + ); + await rpcUrlInput.clear(); + await rpcUrlInput.sendKeys(networkUrl); + await driver.clickElement({ + text: 'Add URL', tag: 'button', }); - - await driver.waitForSelector('[data-testid="add-network-modal"]'); - const [ - networkNameField, - networkUrlField, - chainIdField, - currencySymbolField, - ] = await driver.findElements('input[type="text"]'); - await networkNameField.sendKeys(networkName); - await networkUrlField.sendKeys(networkUrl); - await chainIdField.sendKeys(chainId.toString()); - await currencySymbolField.sendKeys(currencySymbol); await driver.clickElement({ text: 'Save', tag: 'button' }); - await driver.assertElementNotPresent( - '[data-testid="add-network-modal"]', + await driver.clickElement({ + text: 'Done', + tag: 'button', + }); + + await driver.clickElement('.mm-picker-network'); + await driver.clickElement( + `[data-rbd-draggable-id="${toHex(chainId)}"]`, ); - await driver.clickElement({ text: 'Done', tag: 'button' }); // Check localhost 8546 is selected and its balance value is correct await driver.findElement({ diff --git a/test/e2e/tests/request-queuing/ui.spec.js b/test/e2e/tests/request-queuing/ui.spec.js index 5da270427ca5..cb7b2320286f 100644 --- a/test/e2e/tests/request-queuing/ui.spec.js +++ b/test/e2e/tests/request-queuing/ui.spec.js @@ -1,8 +1,6 @@ const { strict: assert } = require('assert'); const { Browser, until } = require('selenium-webdriver'); -const { - BUILT_IN_INFURA_NETWORKS, -} = require('../../../../shared/constants/network'); +const { CHAIN_IDS } = require('../../../../shared/constants/network'); const FixtureBuilder = require('../../fixture-builder'); const { withFixtures, @@ -108,12 +106,15 @@ async function switchToDialogPopoverValidateDetails(driver, expectedDetails) { ); const { - metamask: { selectedNetworkClientId, networkConfigurations }, + metamask: { selectedNetworkClientId, networkConfigurationsByChainId }, } = notificationWindowState; - const { chainId } = - BUILT_IN_INFURA_NETWORKS[selectedNetworkClientId] ?? - networkConfigurations[selectedNetworkClientId]; + const { chainId } = Object.values(networkConfigurationsByChainId).find( + ({ rpcEndpoints }) => + rpcEndpoints.some( + ({ networkClientId }) => networkClientId === selectedNetworkClientId, + ), + ); assert.equal(chainId, expectedDetails.chainId); } @@ -371,6 +372,9 @@ describe('Request-queue UI changes', function () { dapp: true, fixtures: new FixtureBuilder() .withNetworkControllerDoubleGanache() + .withPreferencesController({ + preferences: { showTestNetworks: true }, + }) .withPreferencesControllerUseRequestQueueEnabled() .withSelectedNetworkControllerPerDomain() .build(), @@ -408,24 +412,24 @@ describe('Request-queue UI changes', function () { text: 'Ethereum Mainnet', }); - // Go to Settings, delete the first dapp's network - await driver.clickElement( - '[data-testid="account-options-menu-button"]', - ); - await driver.clickElement('[data-testid="global-menu-settings"]'); - await driver.clickElement({ - css: '.tab-bar__tab__content__title', - text: 'Networks', - }); - await driver.clickElement({ - css: '.networks-tab__networks-list-name', + await driver.clickElement('[data-testid="network-display"]'); + + const networkRow = await driver.findElement({ + css: '.multichain-network-list-item', text: 'Localhost 8545', }); - await driver.clickElement({ css: '.btn-danger', text: 'Delete' }); - await driver.clickElement({ - css: '.modal-container__footer-button', - text: 'Delete', - }); + + const networkMenu = await driver.findNestedElement( + networkRow, + `[data-testid="network-list-item-options-button-${CHAIN_IDS.LOCALHOST}"]`, + ); + + await networkMenu.click(); + await driver.clickElement( + '[data-testid="network-list-item-options-delete"]', + ); + + await driver.clickElement({ tag: 'button', text: 'Delete' }); // Go back to first dapp, try an action, ensure deleted network doesn't block UI // The current globally selected network, Ethereum Mainnet, should be used diff --git a/test/e2e/tests/settings/backup-restore.spec.js b/test/e2e/tests/settings/backup-restore.spec.js index 5d50d6030c90..02a36b638884 100644 --- a/test/e2e/tests/settings/backup-restore.spec.js +++ b/test/e2e/tests/settings/backup-restore.spec.js @@ -72,7 +72,7 @@ describe('Backup and Restore', function () { assert.notEqual(info, null); // Verify Json assert.equal( - Object.values(info?.network?.networkConfigurations)?.[0].chainId, + info?.network?.networkConfigurationsByChainId?.['0x539']?.chainId, '0x539', ); }, diff --git a/test/e2e/tests/settings/settings-search.spec.js b/test/e2e/tests/settings/settings-search.spec.js index 9e7dceafabfa..7a9207dcbf9f 100644 --- a/test/e2e/tests/settings/settings-search.spec.js +++ b/test/e2e/tests/settings/settings-search.spec.js @@ -148,32 +148,6 @@ describe('Settings Search', function () { }, ); }); - it('should find element inside the Networks tab', async function () { - await withFixtures( - { - fixtures: new FixtureBuilder().build(), - ganacheOptions: defaultGanacheOptions, - title: this.test.fullTitle(), - }, - async ({ driver }) => { - await unlockWallet(driver); - - await openMenuSafe(driver); - - await driver.clickElement({ text: 'Settings', tag: 'div' }); - await driver.fill('#search-settings', settingsSearch.networks); - - // Check if element redirects to the correct page - const page = 'Networks'; - await driver.clickElement({ text: page, tag: 'span' }); - assert.equal( - await driver.isElementPresent({ text: page, tag: 'div' }), - true, - `${settingsSearch.networks} item does not redirect to ${page} view`, - ); - }, - ); - }); it('should find element inside the Experimental tab', async function () { await withFixtures( { diff --git a/test/integration/data/integration-init-state.json b/test/integration/data/integration-init-state.json index 212ea5702868..b031611a06ea 100644 --- a/test/integration/data/integration-init-state.json +++ b/test/integration/data/integration-init-state.json @@ -655,28 +655,54 @@ } } }, - "networkConfigurations": { - "testNetworkConfigurationId": { + "networkConfigurationsByChainId": { + "0x1": { "chainId": "0x1", - "id": "testNetworkConfigurationId", - "nickname": "Custom Mainnet RPC", - "rpcUrl": "https://testrpc.com", - "ticker": "ETH", - "type": "rpc" + "rpcEndpoints": [ + { + "networkClientId": "testNetworkConfigurationId", + "url": "https://testrpc.com", + "type": "custom", + "name": "Custom Mainnet RPC" + } + ], + "defaultRpcEndpointIndex": 0, + "blockExplorerUrls": [], + "defaultBlockExplorerUrlIndex": 0, + "name": "Custom Mainnet RPC", + "nativeCurrency": "ETH" }, - "goerli-network-id": { + "0x5": { "chainId": "0x5", - "id": "goerli-network-id", - "nickname": "Chain 5", - "ticker": "ETH", - "type": "rpc" + "rpcEndpoints": [ + { + "networkClientId": "goerli-network-id", + "url": "https://testrpc2.com", + "type": "custom", + "name": "Chain 5" + } + ], + "defaultRpcEndpointIndex": 0, + "blockExplorerUrls": [], + "defaultBlockExplorerUrlIndex": 0, + "name": "Chain 5", + "nativeCurrency": "ETH" }, - "sepolia": { - "type": "rpc", + "0xaa36a7": { "chainId": "0xaa36a7", - "ticker": "ETH", - "nickname": "Sepolia", - "id": "sepolia" + "rpcEndpoints": [ + { + "networkClientId": "sepolia", + "url": "https://sepolia.infura.io/v3/{infuraProjectId}", + "type": "infura", + "name": "Sepolia" + } + ], + "defaultRpcEndpointIndex": 0, + "blockExplorerUrls": ["https://sepolia.etherscan.io"], + "defaultBlockExplorerUrlIndex": 0, + "name": "Sepolia", + "nativeCurrency": "SepoliaETH" } }, "networksMetadata": { @@ -763,13 +789,6 @@ "preventPollingOnNetworkRestart": true, "previousAppVersion": "11.14.4", "previousMigrationVersion": 112, - "providerConfig": { - "type": "sepolia", - "nickname": "sepolia", - "chainId": "0xaa36a7", - "ticker": "ETH", - "id": "sepolia" - }, "securityAlertsEnabled": true, "seedPhraseBackedUp": true, "selectedAddress": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc", @@ -2038,7 +2057,7 @@ "useTransactionSimulations": true, "usedNetworks": { "0xaa36a7": { - "rpcUrl": "https://sepolia.infura.io/v3/8f8f7f9f6f4c4f3e8f8f7f9f6f4c4f3e", + "rpcUrl": "https://sepolia.infura.io/v3/dummy_key", "chainId": "0xaa36a7", "nickname": "Sepolia Test Network", "ticker": "ETH", diff --git a/test/integration/data/onboarding-completion-route.json b/test/integration/data/onboarding-completion-route.json index 1b2379af3a47..06d85e298409 100644 --- a/test/integration/data/onboarding-completion-route.json +++ b/test/integration/data/onboarding-completion-route.json @@ -182,7 +182,24 @@ } } }, - "networkConfigurations": {}, + "networkConfigurationsByChainId": { + "0xaa36a7": { + "chainId": "0xaa36a7", + "rpcEndpoints": [ + { + "networkClientId": "sepolia", + "url": "https://sepolia.infura.io/v3/{infuraProjectId}", + "type": "infura", + "name": "Sepolia" + } + ], + "defaultRpcEndpointIndex": 0, + "blockExplorerUrls": ["https://sepolia.etherscan.io"], + "defaultBlockExplorerUrlIndex": 0, + "name": "Sepolia", + "nativeCurrency": "SepoliaETH" + } + }, "networksMetadata": { "sepolia": { "status": "unknown", "EIPS": {} } }, "nextNonce": null, "nftsDetectionNoticeDismissed": false, diff --git a/test/jest/mock-store.js b/test/jest/mock-store.js index e61ae0fdce81..625b6dcf6c83 100644 --- a/test/jest/mock-store.js +++ b/test/jest/mock-store.js @@ -138,6 +138,7 @@ export const createSwapsMockStore = () => { preferences: { showFiatInTestnets: true, smartTransactionsOptInStatus: true, + showMultiRpcModal: false, }, transactions: [ { @@ -364,7 +365,9 @@ export const createSwapsMockStore = () => { ticker: CURRENCY_SYMBOLS.ETH, rpcUrl: 'https://mainnet.infura.io/v3/', blockExplorerUrl: 'https://etherscan.io', + id: 'mainnet', }), + selectedNetworkClientId: 'mainnet', tokens: [ { erc20: true, @@ -674,6 +677,10 @@ export const createBridgeMockStore = ( ...featureFlagOverrides, }, }, + ...mockNetworkState( + { chainId: CHAIN_IDS.MAINNET }, + { chainId: CHAIN_IDS.LINEA_MAINNET }, + ), }, }; }; diff --git a/test/stub/networks.ts b/test/stub/networks.ts index fe58a0dfe285..bae4798d001f 100644 --- a/test/stub/networks.ts +++ b/test/stub/networks.ts @@ -2,6 +2,7 @@ import { NetworkMetadata, NetworkState, NetworkStatus, + RpcEndpointType, } from '@metamask/network-controller'; import { v4 as uuidv4 } from 'uuid'; import { Hex } from '@metamask/utils'; @@ -10,7 +11,9 @@ import { CHAIN_ID_TO_CURRENCY_SYMBOL_MAP, } from '../../shared/constants/network'; -export const mockNetworkState = ( +// TODO: This is intentionally the old network state, and could be +// removed if the e2e tests bump `FIXTURE_STATE_METADATA_VERSION` to >= 127 +export const mockNetworkStateOld = ( ...networks: { id?: string; type?: string; @@ -21,7 +24,7 @@ export const mockNetworkState = ( blockExplorerUrl?: string; metadata?: NetworkMetadata; }[] -): NetworkState => { +) => { const networkConfigurations = networks.map((network) => ({ id: network.id ?? uuidv4(), chainId: network.chainId, @@ -68,3 +71,80 @@ export const mockNetworkState = ( networksMetadata, }; }; + +export const mockNetworkState = ( + ...networks: { + id?: string; + chainId: Hex; + rpcUrl?: string; + nickname?: string; + ticker?: string; + blockExplorerUrl?: string; + metadata?: NetworkMetadata; + }[] +): NetworkState => { + if ( + new Set(networks.map((network) => network.chainId)).size !== networks.length + ) { + throw 'mockNetworkState doesnt currently support multiple rpc urls per chain id'; + } + + const networkConfigurations = networks.map((network) => { + const blockExplorer = + !('blockExplorerUrl' in network) || network.blockExplorerUrl + ? network.blockExplorerUrl ?? + `https://localhost/blockExplorer/${network.chainId}` + : undefined; + + const rpc = + 'rpcUrl' in network + ? network.rpcUrl + : `https://localhost/rpc/${network.chainId}`; + + return { + chainId: network.chainId, + blockExplorerUrls: blockExplorer ? [blockExplorer] : [], + defaultBlockExplorerUrlIndex: blockExplorer ? 0 : undefined, + rpcEndpoints: [ + { + networkClientId: network.id ?? uuidv4(), + type: RpcEndpointType.Custom, + url: rpc, + }, + ], + defaultRpcEndpointIndex: 0, + name: + 'nickname' in network + ? network.nickname + : (NETWORK_TO_NAME_MAP as Record)[network.chainId], + nativeCurrency: + 'ticker' in network + ? network.ticker + : (CHAIN_ID_TO_CURRENCY_SYMBOL_MAP as Record)[ + network.chainId + ], + }; + }); + + const networksMetadata = networks.reduce( + (acc, network, i) => ({ + ...acc, + [networkConfigurations[i].rpcEndpoints[0].networkClientId]: + network.metadata ?? { + EIPS: {}, + status: NetworkStatus.Available, + }, + }), + {}, + ); + + return { + selectedNetworkClientId: + networkConfigurations[0].rpcEndpoints[0].networkClientId, + networkConfigurationsByChainId: networkConfigurations.reduce( + (acc, network) => ({ ...acc, [network.chainId]: network }), + {}, + ), + networksMetadata, + }; +}; diff --git a/ui/components/app/add-network/add-network.js b/ui/components/app/add-network/add-network.js deleted file mode 100644 index a8a2bd5dc7bf..000000000000 --- a/ui/components/app/add-network/add-network.js +++ /dev/null @@ -1,324 +0,0 @@ -import React, { useContext, useEffect, useState } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; -import { useHistory } from 'react-router-dom'; -import { ApprovalType } from '@metamask/controller-utils'; -import { I18nContext } from '../../../contexts/i18n'; -import Box from '../../ui/box'; -import { - AlignItems, - Display, - FlexDirection, - TextVariant, - JustifyContent, - BorderRadius, - BackgroundColor, - TextColor, - IconColor, -} from '../../../helpers/constants/design-system'; -import Button from '../../ui/button'; -import Tooltip from '../../ui/tooltip'; -import { - getNetworkConfigurations, - getUnapprovedConfirmations, -} from '../../../selectors'; - -import { - ENVIRONMENT_TYPE_FULLSCREEN, - ENVIRONMENT_TYPE_POPUP, - ORIGIN_METAMASK, -} from '../../../../shared/constants/app'; -import { requestUserApproval } from '../../../store/actions'; -import Popover from '../../ui/popover'; -import ConfirmationPage from '../../../pages/confirmations/confirmation/confirmation'; -import { FEATURED_RPCS } from '../../../../shared/constants/network'; -import { ADD_NETWORK_ROUTE } from '../../../helpers/constants/routes'; -import { getEnvironmentType } from '../../../../app/scripts/lib/util'; -import ZENDESK_URLS from '../../../helpers/constants/zendesk-url'; -import { - Icon, - IconName, - IconSize, - AvatarNetwork, - AvatarNetworkSize, - Text, -} from '../../component-library'; -import { MetaMetricsNetworkEventSource } from '../../../../shared/constants/metametrics'; - -const AddNetwork = () => { - const t = useContext(I18nContext); - const dispatch = useDispatch(); - const history = useHistory(); - const networkConfigurations = useSelector(getNetworkConfigurations); - - const networkConfigurationChainIds = Object.values(networkConfigurations).map( - (net) => net.chainId, - ); - - const infuraRegex = /infura.io/u; - - const nets = FEATURED_RPCS.sort((a, b) => - a.nickname > b.nickname ? 1 : -1, - ).slice(0, FEATURED_RPCS.length); - - const notExistingNetworkConfigurations = nets.filter( - (net) => networkConfigurationChainIds.indexOf(net.chainId) === -1, - ); - const unapprovedConfirmations = useSelector(getUnapprovedConfirmations); - const [showPopover, setShowPopover] = useState(false); - - useEffect(() => { - const anAddNetworkConfirmationFromMetaMaskExists = - unapprovedConfirmations?.find((confirmation) => { - return ( - confirmation.origin === 'metamask' && - confirmation.type === ApprovalType.AddEthereumChain - ); - }); - if (!showPopover && anAddNetworkConfirmationFromMetaMaskExists) { - setShowPopover(true); - } - - if (showPopover && !anAddNetworkConfirmationFromMetaMaskExists) { - setShowPopover(false); - } - }, [unapprovedConfirmations, showPopover]); - - return ( - <> - {Object.keys(notExistingNetworkConfigurations).length === 0 ? ( - - - - - - - {t('youHaveAddedAll', [ - - {t('here')}. - , - , - ])} - - - - ) : ( - - {getEnvironmentType() === ENVIRONMENT_TYPE_FULLSCREEN && ( - - - {t('networks')} - - {' > '} - - {t('addANetwork')} - - - )} - - - {t('addFromAListOfPopularNetworks')} - - - {t('popularCustomNetworks')} - - {notExistingNetworkConfigurations.map((item, index) => ( - - - - - - {item.nickname} - - - - - { - // Warning for the networks that doesn't use infura.io as the RPC - !infuraRegex.test(item.rpcUrl) && ( - - {t('addNetworkTooltipWarning', [ - - {t('learnMoreUpperCase')} - , - ])} - - } - trigger="mouseenter" - > - - - ) - } - - - - ))} - - - - - - )} - {showPopover && ( - - - - )} - - ); -}; - -export default AddNetwork; diff --git a/ui/components/app/add-network/add-network.stories.js b/ui/components/app/add-network/add-network.stories.js deleted file mode 100644 index 76d10b569d23..000000000000 --- a/ui/components/app/add-network/add-network.stories.js +++ /dev/null @@ -1,115 +0,0 @@ -import React from 'react'; -import AddNetwork from './add-network'; - -const POL_TOKEN_IMAGE_URL = './images/pol-token.svg'; -const ARBITRUM_IMAGE_URL = './images/arbitrum.svg'; -const OPTIMISM_IMAGE_URL = './images/optimism.svg'; -const AVALANCHE_IMAGE_URL = './images/avax-token.svg'; -const PALM_IMAGE_URL = './images/palm.svg'; -const BSC_IMAGE_URL = './images/bsc-filled.svg'; - -export default { - title: 'Components/App/AddNetwork', - argTypes: { - onBackClick: { - action: 'onBackClick', - }, - onAddNetworkClick: { - action: 'onAddNetworkClick', - }, - onAddNetworkManuallyClick: { - action: 'onAddNetworkManuallyClick', - }, - featuredRPCS: { - control: 'array', - }, - }, - args: { - featuredRPCS: [ - { - chainId: '42161', - nickname: 'Arbitrum One', - rpcUrl: 'https://arbitrum-mainnet.infura.io/v3/{INFURA_API_KEY}', - ticker: 'AETH', - rpcPrefs: { - blockExplorerUrl: 'https://explorer.arbitrum.io', - imageUrl: ARBITRUM_IMAGE_URL, - }, - }, - { - chainId: '43114', - nickname: 'Avalanche Mainnet C-Chain', - rpcUrl: 'https://api.avax.network/ext/bc/C/rpc', - ticker: 'AVAX', - rpcPrefs: { - blockExplorerUrl: 'https://snowtrace.io/', - imageUrl: AVALANCHE_IMAGE_URL, - }, - }, - { - chainId: '56', - nickname: 'BNB Smart Chain', - rpcUrl: 'https://bsc-dataseed.binance.org/', - ticker: 'BNB', - rpcPrefs: { - blockExplorerUrl: 'https://bscscan.com/', - imageUrl: BSC_IMAGE_URL, - }, - }, - { - chainId: '250', - nickname: 'Fantom Opera', - rpcUrl: 'https://rpc.ftm.tools/', - ticker: 'FTM', - rpcPrefs: { - blockExplorerUrl: 'https://ftmscan.com/', - imageUrl: '', - }, - }, - { - chainId: '1666600000', - nickname: 'Harmony Mainnet Shard 0', - rpcUrl: 'https://api.harmony.one/', - ticker: 'ONE', - rpcPrefs: { - blockExplorerUrl: 'https://explorer.harmony.one/', - imageUrl: '', - }, - }, - { - chainId: '10', - nickname: 'Optimism', - rpcUrl: 'https://optimism-mainnet.infura.io/v3/{INFURA_API_KEY}', - ticker: 'KOR', - rpcPrefs: { - blockExplorerUrl: 'https://optimistic.etherscan.io/', - imageUrl: OPTIMISM_IMAGE_URL, - }, - }, - { - chainId: '137', - nickname: 'Polygon Mainnet', - rpcUrl: 'https://polygon-mainnet.infura.io/v3/{INFURA_API_KEY}', - ticker: 'MATIC', - rpcPrefs: { - blockExplorerUrl: 'https://polygonscan.com/', - imageUrl: POL_TOKEN_IMAGE_URL, - }, - }, - { - chainId: '11297108109', - nickname: 'Palm', - rpcUrl: 'https://palm-mainnet.infura.io/v3/{INFURA_API_KEY}', - ticker: 'PALM', - rpcPrefs: { - blockExplorerUrl: 'https://explorer.palm.io/', - imageUrl: PALM_IMAGE_URL, - }, - }, - ], - }, -}; - -export const DefaultStory = (args) => ; - -DefaultStory.storyName = 'Default'; diff --git a/ui/components/app/add-network/add-network.test.js b/ui/components/app/add-network/add-network.test.js deleted file mode 100644 index f3084b1db065..000000000000 --- a/ui/components/app/add-network/add-network.test.js +++ /dev/null @@ -1,66 +0,0 @@ -import React from 'react'; -import { screen } from '@testing-library/react'; -import { renderWithProvider } from '../../../../test/jest'; -import configureStore from '../../../store/store'; -import mockState from '../../../../test/data/mock-state.json'; -import AddNetwork from './add-network'; - -jest.mock('../../../selectors', () => ({ - ...jest.requireActual('../../../selectors'), - getNetworkConfigurations: () => ({ - networkConfigurationId: { - chainId: '0x539', - nickname: 'Localhost 8545', - rpcPrefs: {}, - rpcUrl: 'http://localhost:8545', - ticker: 'ETH', - }, - networkConfigurationId2: { - chainId: '0xA4B1', - nickname: 'Arbitrum One', - rpcPrefs: { blockExplorerUrl: 'https://explorer.arbitrum.io' }, - rpcUrl: - 'https://arbitrum-mainnet.infura.io/v3/7e127583378c4732a858df2550aff333', - ticker: 'AETH', - }, - }), - getUnapprovedConfirmations: jest.fn(), - getTheme: () => 'light', -})); - -jest.mock( - '../../../pages/confirmations/components/simulation-details/useBalanceChanges', - () => ({ - useBalanceChanges: jest.fn(), - }), -); - -const render = () => { - const store = configureStore({ - metamask: { - ...mockState.metamask, - }, - }); - return renderWithProvider(, store); -}; - -describe('AddNetwork', () => { - it('should show Add from a list.. text', () => { - render(); - expect( - screen.getByText( - 'Add from a list of popular networks or add a network manually. Only interact with the entities you trust.', - ), - ).toBeInTheDocument(); - }); - - it('should show Popular custom networks text', () => { - render(); - expect(screen.getByText('Popular custom networks')).toBeInTheDocument(); - }); - - it('should show Arbitrum One network nickname', () => { - render(); - expect(screen.getByText('Arbitrum One')).toBeInTheDocument(); - }); -}); diff --git a/ui/components/app/add-network/index.js b/ui/components/app/add-network/index.js deleted file mode 100644 index 8727c8809582..000000000000 --- a/ui/components/app/add-network/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './add-network'; diff --git a/ui/components/app/add-network/index.scss b/ui/components/app/add-network/index.scss deleted file mode 100644 index 35b620d8e2bf..000000000000 --- a/ui/components/app/add-network/index.scss +++ /dev/null @@ -1,94 +0,0 @@ -@use "design-system"; - -.add-network { - &__networks-container { - padding-inline-end: 24px; - - @include design-system.screen-sm-max { - padding: 0; - } - } - - &__header { - border-bottom: 1px solid var(--color-border-default); - - @include design-system.screen-sm-max { - padding-inline-start: 24px; - padding-inline-end: 24px; - } - - &__subtitle { - margin-inline-start: 10px; - margin-inline-end: 10px; - } - } - - &__main-container { - @include design-system.screen-sm-max { - padding-inline-start: 24px; - padding-inline-end: 24px; - } - } - - &__list-of-networks { - @include design-system.screen-sm-min { - width: 75%; - } - } - - &__warning-tooltip { - color: var(--color-text-alternative); - width: 180px; - - a { - color: var(--color-primary-default); - } - } - - &__add-icon { - color: var(--color-text-alternative); - margin-inline-start: auto; - margin-inline-end: 0; - cursor: pointer; - } - - &__add-button.button { - color: var(--color-primary-default); - font-size: design-system.$font-size-h7; - margin-inline-start: 24px; - } - - &__footer { - border-top: 1px solid var(--color-border-muted); - width: 100%; - padding-bottom: 8px; - - @include design-system.screen-sm-max { - padding-inline-start: 24px !important; - } - - & .btn-link { - display: initial; - padding: 0; - } - - &__link { - color: var(--color-primary-default); - } - - & .actionable-message--warning .actionable-message__message, - .actionable-message--warning .actionable-message__action { - color: var(--color-text-alternative); - } - } - - &__edge-case-box { - border: 1px solid var(--color-border-muted); - - &__link { - color: var(--color-info-default); - display: inline; - padding: 0; - } - } -} diff --git a/ui/components/app/app-components.scss b/ui/components/app/app-components.scss index c5083af45104..d7d07b9afb55 100644 --- a/ui/components/app/app-components.scss +++ b/ui/components/app/app-components.scss @@ -1,6 +1,5 @@ /** Please import your files in alphabetical order **/ @import 'account-list-item/index'; -@import 'add-network/index'; @import 'app-loading-spinner/index'; @import 'alerts/alerts'; @import 'beta-header/index'; diff --git a/ui/components/app/assets/nfts/nft-details/__snapshots__/nft-details.test.js.snap b/ui/components/app/assets/nfts/nft-details/__snapshots__/nft-details.test.js.snap index 0a025bc47ff0..22c5342d2026 100644 --- a/ui/components/app/assets/nfts/nft-details/__snapshots__/nft-details.test.js.snap +++ b/ui/components/app/assets/nfts/nft-details/__snapshots__/nft-details.test.js.snap @@ -69,7 +69,7 @@ exports[`NFT Details should match minimal props and state snapshot 1`] = ` class="mm-box mm-text mm-avatar-base mm-avatar-base--size-sm mm-avatar-network nft-item__network-badge mm-text--body-sm mm-text--text-transform-uppercase mm-box--display-flex mm-box--justify-content-center mm-box--align-items-center mm-box--color-text-default mm-box--background-color-background-alternative mm-box--rounded-full mm-box--border-color-background-default mm-box--border-width-2 box--border-style-solid" data-testid="nft-network-badge" > - C + G @@ -247,7 +247,7 @@ exports[`NFT Details should match minimal props and state snapshot 2`] = ` class="mm-box mm-text mm-avatar-base mm-avatar-base--size-sm mm-avatar-network nft-item__network-badge mm-text--body-sm mm-text--text-transform-uppercase mm-box--display-flex mm-box--justify-content-center mm-box--align-items-center mm-box--color-text-default mm-box--background-color-background-alternative mm-box--rounded-full mm-box--border-color-background-default mm-box--border-width-2 box--border-style-solid" data-testid="nft-network-badge" > - C + G diff --git a/ui/components/app/assets/nfts/nft-details/__snapshots__/nft-full-image.test.js.snap b/ui/components/app/assets/nfts/nft-details/__snapshots__/nft-full-image.test.js.snap index dfedee737c93..086754f9b489 100644 --- a/ui/components/app/assets/nfts/nft-details/__snapshots__/nft-full-image.test.js.snap +++ b/ui/components/app/assets/nfts/nft-details/__snapshots__/nft-full-image.test.js.snap @@ -67,7 +67,7 @@ exports[`NFT full image should match snapshot 1`] = ` class="mm-box mm-text mm-avatar-base mm-avatar-base--size-sm mm-avatar-network nft-item__network-badge mm-text--body-sm mm-text--text-transform-uppercase mm-box--display-flex mm-box--justify-content-center mm-box--align-items-center mm-box--color-text-default mm-box--background-color-background-alternative mm-box--rounded-full mm-box--border-color-background-default mm-box--border-width-2 box--border-style-solid" data-testid="nft-network-badge" > - C + G diff --git a/ui/components/app/assets/nfts/nfts-items/nfts-items.stories.tsx b/ui/components/app/assets/nfts/nfts-items/nfts-items.stories.tsx index 62aa3b1bf906..f13af8c13ea4 100644 --- a/ui/components/app/assets/nfts/nfts-items/nfts-items.stories.tsx +++ b/ui/components/app/assets/nfts/nfts-items/nfts-items.stories.tsx @@ -4,6 +4,9 @@ import configureStore from 'redux-mock-store'; import thunk from 'redux-thunk'; import type { Meta, StoryObj } from '@storybook/react'; import NftsItems from './nfts-items'; +import { mockNetworkState } from '../../../../../../test/stub/networks'; +import { CHAIN_IDS } from '../../../../../../shared/constants/network'; + // Custom middleware to ensure actions are plain objects const ensurePlainObjectMiddleware = () => (next) => (action) => { @@ -73,30 +76,7 @@ const createMockState = () => ({ network: '1', nftContracts: [], nfts: [], - providerConfig: { - type: 'mainnet', - chainId: '0x1', - nickname: 'Ethereum Mainnet', - ticker: 'ETH', - rpcUrl: 'https://mainnet.infura.io/v3/', - rpcPrefs: { - blockExplorerUrl: 'https://etherscan.io', - }, - }, - networksMetadata: { - '1': { - status: 'available', - }, - }, - selectedNetworkClientId: '1', - networkConfigurations: { - '1': { - chainId: '0x1', - nickname: 'Ethereum Mainnet', - ticker: 'ETH', - rpcUrl: 'https://mainnet.infura.io/v3/', - }, - }, + ...mockNetworkState({ chainId: CHAIN_IDS.MAINNET }), useRequestQueue: true, }, appState: { diff --git a/ui/components/app/incoming-trasaction-toggle/__snapshots__/incoming-transaction-toggle.test.js.snap b/ui/components/app/incoming-trasaction-toggle/__snapshots__/incoming-transaction-toggle.test.js.snap index faf8faf6dedf..27ee8bbf6b69 100644 --- a/ui/components/app/incoming-trasaction-toggle/__snapshots__/incoming-transaction-toggle.test.js.snap +++ b/ui/components/app/incoming-trasaction-toggle/__snapshots__/incoming-transaction-toggle.test.js.snap @@ -199,7 +199,11 @@ exports[`IncomingTransactionToggle should render existing incoming transaction p
- F + FANTOM logo
= () => { setIncomingTransactionsPreferences={(chainId, value) => { console.log(chainId, value); }} - allNetworks={ALL_NETWORKS_DATA} + networkConfigurations={ + ALL_NETWORKS_DATA as unknown as Record + } incomingTransactionsPreferences={INCOMING_DATA} /> ); diff --git a/ui/components/app/incoming-trasaction-toggle/incoming-transaction-toggle.test.js b/ui/components/app/incoming-trasaction-toggle/incoming-transaction-toggle.test.js index a0f8e4ed4f69..eab43bcc7113 100644 --- a/ui/components/app/incoming-trasaction-toggle/incoming-transaction-toggle.test.js +++ b/ui/components/app/incoming-trasaction-toggle/incoming-transaction-toggle.test.js @@ -25,7 +25,7 @@ describe('IncomingTransactionToggle', () => { setIncomingTransactionsPreferences={ setIncomingTransactionsPreferencesStub } - allNetworks={ALL_NETWORKS_DATA} + networkConfigurations={ALL_NETWORKS_DATA} incomingTransactionsPreferences={INCOMING_DATA} /> , @@ -34,31 +34,31 @@ describe('IncomingTransactionToggle', () => { expect(container).toMatchSnapshot(); const ethMainnetCheckbox = within( - getByTestId(`network-toggle-${ALL_NETWORKS_DATA[0].chainId}`), + getByTestId(`network-toggle-${ALL_NETWORKS_DATA['0x1'].chainId}`), ).getByRole('checkbox'); expect(ethMainnetCheckbox.value).toStrictEqual('true'); const lineaMainnetCheckbox = within( - getByTestId(`network-toggle-${ALL_NETWORKS_DATA[1].chainId}`), + getByTestId(`network-toggle-${ALL_NETWORKS_DATA['0xe708'].chainId}`), ).getByRole('checkbox'); expect(lineaMainnetCheckbox.value).toStrictEqual('false'); const fantomCheckbox = within( - getByTestId(`network-toggle-${ALL_NETWORKS_DATA[2].chainId}`), + getByTestId(`network-toggle-${ALL_NETWORKS_DATA['0xfa'].chainId}`), ).getByRole('checkbox'); expect(fantomCheckbox.value).toStrictEqual('true'); const goerliCheckbox = within( - getByTestId(`network-toggle-${ALL_NETWORKS_DATA[3].chainId}`), + getByTestId(`network-toggle-${ALL_NETWORKS_DATA['0x5'].chainId}`), ).getByRole('checkbox'); expect(goerliCheckbox.value).toStrictEqual('false'); const sepoliaCheckbox = within( - getByTestId(`network-toggle-${ALL_NETWORKS_DATA[4].chainId}`), + getByTestId(`network-toggle-${ALL_NETWORKS_DATA['0xaa36a7'].chainId}`), ).getByRole('checkbox'); expect(sepoliaCheckbox.value).toStrictEqual('true'); const lineaGoerliCheckbox = within( - getByTestId(`network-toggle-${ALL_NETWORKS_DATA[5].chainId}`), + getByTestId(`network-toggle-${ALL_NETWORKS_DATA['0xe704'].chainId}`), ).getByRole('checkbox'); expect(lineaGoerliCheckbox.value).toStrictEqual('true'); const lineaSepoliaCheckbox = within( - getByTestId(`network-toggle-${ALL_NETWORKS_DATA[6].chainId}`), + getByTestId(`network-toggle-${ALL_NETWORKS_DATA['0xe705'].chainId}`), ).getByRole('checkbox'); expect(lineaSepoliaCheckbox.value).toStrictEqual('true'); }); @@ -70,14 +70,14 @@ describe('IncomingTransactionToggle', () => { setIncomingTransactionsPreferences={ setIncomingTransactionsPreferencesStub } - allNetworks={ALL_NETWORKS_DATA} + networkConfigurations={ALL_NETWORKS_DATA} incomingTransactionsPreferences={INCOMING_DATA} /> , mockStore, ); const lineaMainnetCheckbox = within( - getByTestId(`network-toggle-${ALL_NETWORKS_DATA[1].chainId}`), + getByTestId(`network-toggle-${ALL_NETWORKS_DATA['0xe708'].chainId}`), ).getByRole('checkbox'); fireEvent.click(lineaMainnetCheckbox); // set 1 false to true @@ -91,7 +91,7 @@ describe('IncomingTransactionToggle', () => { // set 1 false to true const goerliCheckbox = within( - getByTestId(`network-toggle-${ALL_NETWORKS_DATA[3].chainId}`), + getByTestId(`network-toggle-${ALL_NETWORKS_DATA['0x5'].chainId}`), ).getByRole('checkbox'); fireEvent.click(goerliCheckbox); expect( diff --git a/ui/components/app/incoming-trasaction-toggle/incoming-transaction-toggle.tsx b/ui/components/app/incoming-trasaction-toggle/incoming-transaction-toggle.tsx index 3b44a56b262f..d1274075d056 100644 --- a/ui/components/app/incoming-trasaction-toggle/incoming-transaction-toggle.tsx +++ b/ui/components/app/incoming-trasaction-toggle/incoming-transaction-toggle.tsx @@ -1,6 +1,8 @@ import React, { useContext, useEffect, useState } from 'react'; import PropTypes from 'prop-types'; +import { NetworkConfiguration } from '@metamask/network-controller'; +import { Hex } from '@metamask/utils'; import { I18nContext } from '../../../contexts/i18n'; import { Box, Text } from '../../component-library'; @@ -10,7 +12,10 @@ import { } from '../../../helpers/constants/design-system'; import { PolymorphicRef } from '../../component-library/box'; -import { TEST_CHAINS } from '../../../../shared/constants/network'; +import { + CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP, + TEST_CHAINS, +} from '../../../../shared/constants/network'; import NetworkToggle from './network-toggle'; type IncomingTransactionToggleProps = { @@ -18,9 +23,7 @@ type IncomingTransactionToggleProps = { // eslint-disable-next-line @typescript-eslint/no-explicit-any wrapperRef?: PolymorphicRef; incomingTransactionsPreferences: Record; - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any - allNetworks: Record[]; + networkConfigurations: Record; setIncomingTransactionsPreferences: ( chainId: string, isAllEnabledValue: boolean, @@ -30,7 +33,7 @@ type IncomingTransactionToggleProps = { const IncomingTransactionToggle = ({ wrapperRef, incomingTransactionsPreferences, - allNetworks, + networkConfigurations, setIncomingTransactionsPreferences, }: IncomingTransactionToggleProps) => { const t = useContext(I18nContext); @@ -38,7 +41,7 @@ const IncomingTransactionToggle = ({ const [networkPreferences, setNetworkPreferences] = useState( generateIncomingNetworkPreferences( incomingTransactionsPreferences, - allNetworks, + networkConfigurations, ), ); @@ -46,10 +49,10 @@ const IncomingTransactionToggle = ({ setNetworkPreferences( generateIncomingNetworkPreferences( incomingTransactionsPreferences, - allNetworks, + networkConfigurations, ), ); - }, [incomingTransactionsPreferences, allNetworks]); + }, [incomingTransactionsPreferences, networkConfigurations]); const toggleSingleNetwork = (chainId: string, value: boolean): void => { setIncomingTransactionsPreferences(chainId, value); @@ -80,15 +83,13 @@ export default IncomingTransactionToggle; IncomingTransactionToggle.propTypes = { wrapperRef: PropTypes.object, incomingTransactionsPreferences: PropTypes.object.isRequired, - allNetworks: PropTypes.array.isRequired, + networkConfigurations: PropTypes.object.isRequired, setIncomingTransactionsPreferences: PropTypes.func.isRequired, }; function generateIncomingNetworkPreferences( incomingTransactionsPreferences: Record, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any - allNetworks: Record, + networkConfigurations: Record, // TODO: Replace `any` with type // eslint-disable-next-line @typescript-eslint/no-explicit-any ): Record { @@ -96,13 +97,18 @@ function generateIncomingNetworkPreferences( // eslint-disable-next-line @typescript-eslint/no-explicit-any const incomingTxnPreferences: Record = {}; - Object.keys(allNetworks).forEach((id) => { - const { chainId } = allNetworks[id]; - incomingTxnPreferences[chainId] = { - isShowIncomingTransactions: incomingTransactionsPreferences[chainId], - isATestNetwork: TEST_CHAINS.includes(chainId), - label: allNetworks[id].nickname, - imageUrl: allNetworks[id].rpcPrefs?.imageUrl, + Object.values(networkConfigurations).forEach((network) => { + incomingTxnPreferences[network.chainId] = { + isShowIncomingTransactions: + incomingTransactionsPreferences[network.chainId], + isATestNetwork: TEST_CHAINS.includes( + network.chainId as (typeof TEST_CHAINS)[number], + ), + label: network.name, + imageUrl: + CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP[ + network.chainId as keyof typeof CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP + ], }; }); diff --git a/ui/components/app/incoming-trasaction-toggle/mock-data.ts b/ui/components/app/incoming-trasaction-toggle/mock-data.ts index 3e048c9c026e..29a704c7ce3c 100644 --- a/ui/components/app/incoming-trasaction-toggle/mock-data.ts +++ b/ui/components/app/incoming-trasaction-toggle/mock-data.ts @@ -1,80 +1,89 @@ -export const ALL_NETWORKS_DATA = [ - { +export const ALL_NETWORKS_DATA = { + '0x1': { + nativeCurrency: 'ETH', chainId: '0x1', - nickname: 'Ethereum Mainnet', - rpcUrl: 'https://mainnet.infura.io/v3/6c21df2a8dcb4a77b9bbcc1b65ee9ded', - rpcPrefs: { - imageUrl: './images/eth_logo.svg', - }, - providerType: 'mainnet', - ticker: 'ETH', - id: 'mainnet', - removable: false, + defaultRpcEndpointIndex: 0, + name: 'Ethereum Mainnet', + blockExplorerUrls: [], + rpcEndpoints: [ + { + networkClientId: 'mainnet', + }, + ], }, - { + '0xe708': { + nativeCurrency: 'ETH', chainId: '0xe708', - nickname: 'Linea Mainnet', - rpcUrl: - 'https://linea-mainnet.infura.io/v3/6c21df2a8dcb4a77b9bbcc1b65ee9ded', - rpcPrefs: { - imageUrl: './images/linea-logo-mainnet.svg', - }, - providerType: 'linea-mainnet', - id: 'linea-mainnet', - removable: false, + defaultRpcEndpointIndex: 0, + name: 'Linea Mainnet', + blockExplorerUrls: [], + rpcEndpoints: [ + { + networkClientId: 'linea-mainnet', + }, + ], }, - { + '0xfa': { + nativeCurrency: 'FTM', chainId: '0xfa', - nickname: 'FANTOM', - rpcPrefs: {}, - rpcUrl: 'http://ftmscan.com5', - ticker: 'FTM', + defaultRpcEndpointIndex: 0, + name: 'FANTOM', + blockExplorerUrls: [], + rpcEndpoints: [ + { + networkClientId: 'fantom-network-client-id', + }, + ], }, - { + '0x5': { + nativeCurrency: 'GoerliETH', chainId: '0x5', - nickname: 'Goerli', - rpcUrl: 'https://goerli.infura.io/v3/6c21df2a8dcb4a77b9bbcc1b65ee9ded', - providerType: 'goerli', - ticker: 'GoerliETH', - id: 'goerli', - removable: false, + defaultRpcEndpointIndex: 0, + name: 'Goerli', + blockExplorerUrls: [], + rpcEndpoints: [ + { + networkClientId: 'goerli', + }, + ], }, - { + '0xaa36a7': { + nativeCurrency: 'SepoliaETH', chainId: '0xaa36a7', - nickname: 'Sepolia', - rpcUrl: 'https://sepolia.infura.io/v3/6c21df2a8dcb4a77b9bbcc1b65ee9ded', - providerType: 'sepolia', - ticker: 'SepoliaETH', - id: 'sepolia', - removable: false, + name: 'Sepolia', + blockExplorerUrls: [], + defaultRpcEndpointIndex: 0, + rpcEndpoints: [ + { + networkClientId: 'sepolia', + }, + ], }, - { + '0xe704': { + nativeCurrency: 'LineaETH', chainId: '0xe704', - nickname: 'Linea Goerli', - rpcUrl: - 'https://linea-goerli.infura.io/v3/6c21df2a8dcb4a77b9bbcc1b65ee9ded', - rpcPrefs: { - imageUrl: './images/linea-logo-testnet.png', - }, - providerType: 'linea-goerli', - ticker: 'LineaETH', - id: 'linea-goerli', - removable: false, + name: 'Linea Goerli', + blockExplorerUrls: [], + defaultRpcEndpointIndex: 0, + rpcEndpoints: [ + { + networkClientId: 'linea-goerli', + }, + ], }, - { + '0xe705': { + nativeCurrency: 'LineaETH', chainId: '0xe705', - nickname: 'Linea Sepolia', - rpcUrl: - 'https://linea-sepolia.infura.io/v3/6c21df2a8dcb4a77b9bbcc1b65ee9ded', - rpcPrefs: { - imageUrl: './images/linea-logo-testnet.png', - }, - providerType: 'linea-sepolia', - ticker: 'LineaETH', - id: 'linea-sepolia', - removable: false, + name: 'Linea Sepolia', + blockExplorerUrls: [], + defaultRpcEndpointIndex: 0, + rpcEndpoints: [ + { + networkClientId: 'linea-sepolia', + }, + ], }, -]; +}; export const INCOMING_DATA = { '0x1': true, diff --git a/ui/components/app/loading-network-screen/loading-network-screen.container.js b/ui/components/app/loading-network-screen/loading-network-screen.container.js index dd671f8d3261..34a626bc1b5e 100644 --- a/ui/components/app/loading-network-screen/loading-network-screen.container.js +++ b/ui/components/app/loading-network-screen/loading-network-screen.container.js @@ -30,11 +30,9 @@ const mapStateToProps = (state) => { let networkName = nickname; if (networkName === undefined) { const networks = getAllEnabledNetworks(state); - const desiredNetwork = networks.find( - (network) => network.chainId === chainId, - ); + const desiredNetwork = networks[chainId]; if (desiredNetwork) { - networkName = desiredNetwork.nickname; + networkName = desiredNetwork.name; } } diff --git a/ui/components/app/modals/confirm-delete-network/confirm-delete-network.component.js b/ui/components/app/modals/confirm-delete-network/confirm-delete-network.component.js index a898488591e7..341e9a29f2c6 100644 --- a/ui/components/app/modals/confirm-delete-network/confirm-delete-network.component.js +++ b/ui/components/app/modals/confirm-delete-network/confirm-delete-network.component.js @@ -5,10 +5,10 @@ import Modal, { ModalContent } from '../../modal'; export default class ConfirmDeleteNetwork extends PureComponent { static propTypes = { hideModal: PropTypes.func.isRequired, - removeNetworkConfiguration: PropTypes.func.isRequired, + removeNetwork: PropTypes.func.isRequired, onConfirm: PropTypes.func.isRequired, - target: PropTypes.string.isRequired, networkNickname: PropTypes.string.isRequired, + chainId: PropTypes.string.isRequired, }; static contextTypes = { @@ -16,7 +16,7 @@ export default class ConfirmDeleteNetwork extends PureComponent { }; handleDelete = async () => { - await this.props.removeNetworkConfiguration(this.props.target); + await this.props.removeNetwork(this.props.chainId); this.props.onConfirm(); this.props.hideModal(); }; diff --git a/ui/components/app/modals/confirm-delete-network/confirm-delete-network.container.js b/ui/components/app/modals/confirm-delete-network/confirm-delete-network.container.js index 5ee9ff4e2087..ac4c444692d2 100644 --- a/ui/components/app/modals/confirm-delete-network/confirm-delete-network.container.js +++ b/ui/components/app/modals/confirm-delete-network/confirm-delete-network.container.js @@ -1,21 +1,21 @@ import { connect } from 'react-redux'; import { compose } from 'redux'; import withModalProps from '../../../../helpers/higher-order-components/with-modal-props'; -import { removeNetworkConfiguration } from '../../../../store/actions'; -import { getNetworkConfigurations } from '../../../../selectors'; +import { removeNetwork } from '../../../../store/actions'; +import { getNetworkConfigurationsByChainId } from '../../../../selectors'; import ConfirmDeleteNetwork from './confirm-delete-network.component'; const mapStateToProps = (state, ownProps) => { - const networkConfigurations = getNetworkConfigurations(state); - const networkNickname = networkConfigurations[ownProps.target].nickname; - - return { networkNickname }; + const networks = getNetworkConfigurationsByChainId(state); + const { chainId, name: networkNickname } = networks[ownProps.target]; + return { chainId, networkNickname }; }; const mapDispatchToProps = (dispatch) => { return { - removeNetworkConfiguration: (target) => - dispatch(removeNetworkConfiguration(target)), + removeNetwork: (chainId) => { + dispatch(removeNetwork(chainId)); + }, }; }; diff --git a/ui/components/app/modals/confirm-delete-network/confirm-delete-network.test.js b/ui/components/app/modals/confirm-delete-network/confirm-delete-network.test.js index 467171181dac..7bae50d2b188 100644 --- a/ui/components/app/modals/confirm-delete-network/confirm-delete-network.test.js +++ b/ui/components/app/modals/confirm-delete-network/confirm-delete-network.test.js @@ -9,8 +9,8 @@ describe('Confirm Delete Network', () => { const props = { hideModal: jest.fn(), onConfirm: jest.fn(), - removeNetworkConfiguration: jest.fn().mockResolvedValue(), - target: 'testNetworkConfigurationId', + removeNetwork: jest.fn().mockResolvedValue(), + target: '0x1', }; it('should match snapshot', () => { @@ -41,7 +41,7 @@ describe('Confirm Delete Network', () => { fireEvent.click(queryByText('[cancel]')); - expect(props.removeNetworkConfiguration).not.toHaveBeenCalled(); + expect(props.removeNetwork).not.toHaveBeenCalled(); expect(props.onConfirm).not.toHaveBeenCalled(); expect(props.hideModal).toHaveBeenCalled(); @@ -55,7 +55,7 @@ describe('Confirm Delete Network', () => { fireEvent.click(queryByText('[delete]')); await waitFor(() => { - expect(props.removeNetworkConfiguration).toHaveBeenCalled(); + expect(props.removeNetwork).toHaveBeenCalled(); expect(props.onConfirm).toHaveBeenCalled(); expect(props.hideModal).toHaveBeenCalled(); }); diff --git a/ui/components/app/modals/confirm-delete-rpc-url-modal/confirm-delete-rpc-url-modal.tsx b/ui/components/app/modals/confirm-delete-rpc-url-modal/confirm-delete-rpc-url-modal.tsx deleted file mode 100644 index 3eb9a9323048..000000000000 --- a/ui/components/app/modals/confirm-delete-rpc-url-modal/confirm-delete-rpc-url-modal.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import React from 'react'; -import { useDispatch } from 'react-redux'; -import { - BlockSize, - Display, -} from '../../../../helpers/constants/design-system'; -import { - Box, - ButtonPrimary, - ButtonPrimarySize, - ButtonSecondary, - ButtonSecondarySize, - Modal, - ModalBody, - ModalContent, - ModalHeader, - ModalOverlay, -} from '../../../component-library'; -import { useI18nContext } from '../../../../hooks/useI18nContext'; -import { - hideModal, - setEditedNetwork, - toggleNetworkMenu, -} from '../../../../store/actions'; - -const ConfirmDeleteRpcUrlModal = () => { - const t = useI18nContext(); - const dispatch = useDispatch(); - return ( - { - dispatch(setEditedNetwork()); - dispatch(hideModal()); - }} - > - - - {t('confirmDeletion')} - - {t('confirmRpcUrlDeletionMessage')} - - { - dispatch(hideModal()); - dispatch(toggleNetworkMenu()); - }} - > - {t('back')} - - { - console.log('TODO: Delete RPc URL'); - }} - > - {t('deleteRpcUrl')} - - - - - - ); -}; - -export default ConfirmDeleteRpcUrlModal; diff --git a/ui/components/app/modals/modal.js b/ui/components/app/modals/modal.js index a093b969c8ff..ecae704a2270 100644 --- a/ui/components/app/modals/modal.js +++ b/ui/components/app/modals/modal.js @@ -10,8 +10,6 @@ import * as actions from '../../../store/actions'; import { mmiActionsFactory } from '../../../store/institutional/institution-background'; ///: END:ONLY_INCLUDE_IF -// Modal Components -import AddNetworkModal from '../../../pages/onboarding-flow/add-network-modal'; ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) import ConfirmRemoveJWT from '../../institutional/confirm-remove-jwt-modal'; import CustodyConfirmLink from '../../institutional/custody-confirm-link-modal'; @@ -37,7 +35,6 @@ import TransactionAlreadyConfirmed from './transaction-already-confirmed'; // Metamask Notifications import ConfirmTurnOffProfileSyncing from './confirm-turn-off-profile-syncing'; import TurnOnMetamaskNotifications from './turn-on-metamask-notifications/turn-on-metamask-notifications'; -import ConfirmDeleteRpcUrlModal from './confirm-delete-rpc-url-modal/confirm-delete-rpc-url-modal'; const modalContainerBaseStyle = { transform: 'translate3d(-50%, 0, 0px)', @@ -59,34 +56,6 @@ const modalContainerMobileStyle = { top: '12.5%', }; -const accountModalStyle = { - mobileModalStyle: { - width: '95%', - // top: isPopupOrNotification() === 'popup' ? '52vh' : '36.5vh', - boxShadow: 'var(--shadow-size-xs) var(--color-shadow-default)', - borderRadius: '4px', - top: '10%', - transform: 'none', - left: '0', - right: '0', - margin: '0 auto', - }, - laptopModalStyle: { - width: '335px', - // top: 'calc(33% + 45px)', - boxShadow: 'var(--shadow-size-xs) var(--color-shadow-default)', - borderRadius: '4px', - top: '10%', - transform: 'none', - left: '0', - right: '0', - margin: '0 auto', - }, - contentStyle: { - borderRadius: '4px', - }, -}; - ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) const custodyConfirmModalStyle = { mobileModalStyle: { @@ -116,11 +85,6 @@ const custodyConfirmModalStyle = { ///: END:ONLY_INCLUDE_IF const MODALS = { - ONBOARDING_ADD_NETWORK: { - contents: , - testId: 'add-network-modal', - ...accountModalStyle, - }, NEW_ACCOUNT: { contents: , mobileModalStyle: { @@ -218,19 +182,6 @@ const MODALS = { }, }, - CONFIRM_DELETE_RPC_URL: { - contents: , - mobileModalStyle: { - ...modalContainerMobileStyle, - }, - laptopModalStyle: { - ...modalContainerLaptopStyle, - }, - contentStyle: { - borderRadius: '8px', - }, - }, - EDIT_APPROVAL_PERMISSION: { contents: , mobileModalStyle: { diff --git a/ui/components/app/multi-rpc-edit-modal/__snapshots__/multi-rpc-edit-modal.test.tsx.snap b/ui/components/app/multi-rpc-edit-modal/__snapshots__/multi-rpc-edit-modal.test.tsx.snap index 7c77aff77a3e..9aa4f875431b 100644 --- a/ui/components/app/multi-rpc-edit-modal/__snapshots__/multi-rpc-edit-modal.test.tsx.snap +++ b/ui/components/app/multi-rpc-edit-modal/__snapshots__/multi-rpc-edit-modal.test.tsx.snap @@ -55,7 +55,58 @@ exports[`MultiRpcEditModal renders correctly with required props 1`] = ` >
+ > +
+
+
+ Ethereum Mainnet logo +
+
+
+

+ Ethereum Mainnet +

+
+
+ +
+
+
+
+ +
+
+
{ }); expect(screen.getByTestId('multi-rpc-edit-modal')).toBeInTheDocument(); - // TODO: enable with network controller v21 upgrade after `getNetworkConfigurationsByChainId` is implemented - // expect(screen.getByText('Ethereum Mainnet')).toBeInTheDocument(); + expect(screen.getByText('Ethereum Mainnet')).toBeInTheDocument(); expect(screen.getByText('supportMultiRpcInformation')).toBeInTheDocument(); }); }); diff --git a/ui/components/app/multi-rpc-edit-modal/multi-rpc-edit-modal.tsx b/ui/components/app/multi-rpc-edit-modal/multi-rpc-edit-modal.tsx index 7dab45d4a9d7..6df73c0b86d8 100644 --- a/ui/components/app/multi-rpc-edit-modal/multi-rpc-edit-modal.tsx +++ b/ui/components/app/multi-rpc-edit-modal/multi-rpc-edit-modal.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import { useDispatch } from 'react-redux'; -import { Hex } from '@metamask/utils'; +import { useDispatch, useSelector } from 'react-redux'; +import { NetworkConfiguration } from '@metamask/network-controller'; import { Modal, ModalContent, @@ -22,6 +22,7 @@ import { } from '../../../helpers/constants/design-system'; import { setShowMultiRpcModal } from '../../../store/actions'; import { getEnvironmentType } from '../../../../app/scripts/lib/util'; +import { getNetworkConfigurationsByChainId } from '../../../selectors'; import { ENVIRONMENT_TYPE_POPUP } from '../../../../shared/constants/app'; import NetworkListItem from './network-list-item/network-list-item'; @@ -29,10 +30,7 @@ function MultiRpcEditModal() { const t = useI18nContext(); const dispatch = useDispatch(); const isPopUp = getEnvironmentType() === ENVIRONMENT_TYPE_POPUP; - - // TODO: useSelector(getNetworkConfigurationsByChainId) with network controller v21 upgrade - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const networkConfigurations = {} as Record; + const networkConfigurations = useSelector(getNetworkConfigurationsByChainId); return ( {Object.values(networkConfigurations).map( - (networkConfiguration) => + (networkConfiguration: NetworkConfiguration) => networkConfiguration.rpcEndpoints.length > 1 ? ( { rpcEndpoints: [ { name: 'Infura Mainnet', + type: RpcEndpointType.Infura, url: 'https://mainnet.infura.io/v3/YOUR_INFURA_KEY', }, ], diff --git a/ui/components/app/multi-rpc-edit-modal/network-list-item/network-list-item.tsx b/ui/components/app/multi-rpc-edit-modal/network-list-item/network-list-item.tsx index 551d34d70643..e90aac3a25d8 100644 --- a/ui/components/app/multi-rpc-edit-modal/network-list-item/network-list-item.tsx +++ b/ui/components/app/multi-rpc-edit-modal/network-list-item/network-list-item.tsx @@ -1,5 +1,9 @@ import React, { useState } from 'react'; import { useDispatch } from 'react-redux'; +import { + NetworkConfiguration, + RpcEndpointType, +} from '@metamask/network-controller'; import { Box, Text, @@ -25,18 +29,10 @@ import { useI18nContext } from '../../../../hooks/useI18nContext'; import { setEditedNetwork, toggleNetworkMenu } from '../../../../store/actions'; import { CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP } from '../../../../../shared/constants/network'; -// TODO: Use version from network controller with v21 upgrade -enum RpcEndpointType { - Custom = 'custom', - Infura = 'infura', -} - const NetworkListItem = ({ networkConfiguration, }: { - // TODO: `NetworkConfiguration` with network controller v21 upgrade - // eslint-disable-next-line @typescript-eslint/no-explicit-any - networkConfiguration: any; + networkConfiguration: NetworkConfiguration; }) => { const rpcEndpoint = networkConfiguration.rpcEndpoints[ diff --git a/ui/components/app/network-display/network-display.js b/ui/components/app/network-display/network-display.js index 11c56a60fcb7..e69ff57b2bed 100644 --- a/ui/components/app/network-display/network-display.js +++ b/ui/components/app/network-display/network-display.js @@ -10,6 +10,7 @@ import { PickerNetwork, AvatarNetworkSize } from '../../component-library'; export default function NetworkDisplay() { const currentNetwork = useSelector(getCurrentNetwork); + return ( + +
+`; diff --git a/ui/components/app/selected-account/selected-account-component.test.js b/ui/components/app/selected-account/selected-account-component.test.js index 6097ab7f6bc9..290e737c0808 100644 --- a/ui/components/app/selected-account/selected-account-component.test.js +++ b/ui/components/app/selected-account/selected-account-component.test.js @@ -1,7 +1,7 @@ import React from 'react'; import configureMockStore from 'redux-mock-store'; import copyToClipboard from 'copy-to-clipboard'; -import { fireEvent } from '@testing-library/react'; +import { fireEvent, waitFor } from '@testing-library/react'; import { renderWithProvider } from '../../../../test/lib/render-helpers'; import mockState from '../../../../test/data/mock-state.json'; import { COPY_OPTIONS } from '../../../../shared/constants/copy'; @@ -49,24 +49,35 @@ jest.mock('../../../selectors', () => { const mockGetSelectedAccount = jest.fn(() => mockSelectedAccount); return { - ...jest.requireActual('../../../selectors'), getAccountType: mockGetAccountType, getSelectedInternalAccount: mockGetSelectedAccount, - getCurrentChainId: jest.fn(() => '0x1'), + getCurrentChainId: jest.fn(() => '0x5'), + getSelectedNetworkClientId: jest.fn(() => 'goerli'), + getNetworkConfigurationsByChainId: jest.fn(() => ({ + '0x5': { + chainId: '0x5', + rpcEndpoints: [{ networkClientId: 'goerli' }], + }, + })), }; }); describe('SelectedAccount Component', () => { const mockStore = configureMockStore()(mockState); - it('should render correctly', () => { + it('should match snapshot', () => { + const { container } = renderWithProvider(, mockStore); + expect(container).toMatchSnapshot(); + }); + + it('should render correctly', async () => { const { container, getByText, getByTestId } = renderWithProvider( , mockStore, ); - const tooltipTitle = container.querySelector( - '[data-original-title="Copy to clipboard"]', + const tooltipTitle = await waitFor(() => + container.querySelector('[data-original-title="Copy to clipboard"]'), ); expect(tooltipTitle).toBeInTheDocument(); @@ -90,7 +101,7 @@ describe('SelectedAccount Component', () => { ); }); - it('should render correctly if isCustodianSupportedChain to false', () => { + it('should render correctly if isCustodianSupportedChain to false', async () => { getIsCustodianSupportedChain.mockReturnValue(false); const { container, queryByTestId } = renderWithProvider( @@ -98,8 +109,10 @@ describe('SelectedAccount Component', () => { mockStore, ); - const tooltipTitle = container.querySelector( - '[data-original-title="This account is not set up for use with Chain 5"]', + const tooltipTitle = await waitFor(() => + container.querySelector( + '[data-original-title="This account is not set up for use with goerli"]', + ), ); const button = queryByTestId('selected-account-click'); diff --git a/ui/components/app/snaps/keyring-snap-removal-warning/keyring-snap-removal-warning.tsx b/ui/components/app/snaps/keyring-snap-removal-warning/keyring-snap-removal-warning.tsx index d2a7335178bc..2377076690b0 100644 --- a/ui/components/app/snaps/keyring-snap-removal-warning/keyring-snap-removal-warning.tsx +++ b/ui/components/app/snaps/keyring-snap-removal-warning/keyring-snap-removal-warning.tsx @@ -25,7 +25,7 @@ import { } from '../../../../helpers/constants/design-system'; import { useI18nContext } from '../../../../hooks/useI18nContext'; import InfoTooltip from '../../../ui/info-tooltip'; -import { getProviderConfig } from '../../../../ducks/metamask/metamask'; +import { getCurrentChainId } from '../../../../selectors'; import { KeyringAccountListItem } from './keyring-account-list-item'; export default function KeyringRemovalSnapWarning({ @@ -50,7 +50,7 @@ export default function KeyringRemovalSnapWarning({ const [confirmedRemoval, setConfirmedRemoval] = useState(false); const [confirmationInput, setConfirmationInput] = useState(''); const [error, setError] = useState(false); - const { chainId } = useSelector(getProviderConfig); + const chainId = useSelector(getCurrentChainId); useEffect(() => { setShowConfirmation(keyringAccounts.length === 0); diff --git a/ui/components/app/snaps/snap-permission-adapter/snap-permission-adapter.test.js b/ui/components/app/snaps/snap-permission-adapter/snap-permission-adapter.test.js index 47703de424d7..4ae5f59a7ab7 100644 --- a/ui/components/app/snaps/snap-permission-adapter/snap-permission-adapter.test.js +++ b/ui/components/app/snaps/snap-permission-adapter/snap-permission-adapter.test.js @@ -2,6 +2,8 @@ import React from 'react'; import { screen } from '@testing-library/react'; import { renderWithProvider } from '../../../../../test/jest'; import configureStore from '../../../../store/store'; +import { mockNetworkState } from '../../../../../test/stub/networks'; +import { CHAIN_IDS } from '../../../../../shared/constants/network'; import SnapPermissionAdapter from './snap-permission-adapter'; describe('Snap Permission Adapter', () => { @@ -17,6 +19,7 @@ describe('Snap Permission Adapter', () => { }; const mockState = { metamask: { + ...mockNetworkState({ chainId: CHAIN_IDS.MAINNET }), subjectMetadata: { 'npm:@metamask/notifications-example-snap': { name: 'Notifications Example Snap', diff --git a/ui/components/app/snaps/snap-permissions-list/snap-permissions-list.test.js b/ui/components/app/snaps/snap-permissions-list/snap-permissions-list.test.js index ddfdc33970a4..c8529a5be9e9 100644 --- a/ui/components/app/snaps/snap-permissions-list/snap-permissions-list.test.js +++ b/ui/components/app/snaps/snap-permissions-list/snap-permissions-list.test.js @@ -1,5 +1,6 @@ import React from 'react'; import { screen } from '@testing-library/react'; +import { mockNetworkState } from '../../../../../test/stub/networks'; import { renderWithProvider } from '../../../../../test/jest'; import configureStore from '../../../../store/store'; import SnapPermissionsList from './snap-permissions-list'; @@ -43,6 +44,7 @@ describe('Snap Permission List', () => { }, }, }, + ...mockNetworkState({}), }, }; diff --git a/ui/components/institutional/wrong-network-notification/wrong-network-notification.tsx b/ui/components/institutional/wrong-network-notification/wrong-network-notification.tsx index 4de11e4c153b..217ce2e575c0 100644 --- a/ui/components/institutional/wrong-network-notification/wrong-network-notification.tsx +++ b/ui/components/institutional/wrong-network-notification/wrong-network-notification.tsx @@ -18,13 +18,13 @@ const WrongNetworkNotification: React.FC = () => { const t = useI18nContext(); const providerConfig = useSelector< object, - { nickname: string; type: string } + { nickname?: string; type: string } | undefined >(getProviderConfig); - const balance = useSelector(getSelectedAccountCachedBalance); + const balance = useSelector(getSelectedAccountCachedBalance); const isCustodianSupportedChain = useSelector(getIsCustodianSupportedChain); - const network = providerConfig.nickname || providerConfig.type; + const network = providerConfig?.nickname || providerConfig?.type; return !isCustodianSupportedChain && balance ? ( +
+
+ +
+
+ +`; diff --git a/ui/components/multichain/address-copy-button/address-copy-button.test.js b/ui/components/multichain/address-copy-button/address-copy-button.test.js index 62158712753a..c7d7c869ff5b 100644 --- a/ui/components/multichain/address-copy-button/address-copy-button.test.js +++ b/ui/components/multichain/address-copy-button/address-copy-button.test.js @@ -32,6 +32,18 @@ const SAMPLE_ADDRESS = '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc'; describe('AccountListItem', () => { const mockStore = configureMockStore()(mockState); + afterEach(() => { + jest.clearAllMocks(); // Clear mocks to ensure no test interference + }); + + it('should match snapshot', () => { + const { container } = renderWithProvider( + , + mockStore, + ); + expect(container).toMatchSnapshot(); + }); + it('renders the full address by default', () => { renderWithProvider( , @@ -103,7 +115,7 @@ describe('AccountListItem', () => { ); const tooltipTitle = container.querySelector( - '[data-original-title="This account is not set up for use with Chain 5"]', + '[data-original-title="This account is not set up for use with Goerli"]', ); const button = queryByTestId('address-copy-button-text'); diff --git a/ui/components/multichain/app-header/app-header.test.js b/ui/components/multichain/app-header/app-header.test.js index 025bbb81e255..0d1f5a8a1034 100644 --- a/ui/components/multichain/app-header/app-header.test.js +++ b/ui/components/multichain/app-header/app-header.test.js @@ -198,7 +198,7 @@ describe('App Header', () => { const mockProviderConfig = { chainId: '0x1', rpcUrl: 'https://localhost:8545', - nickname: null, + nickname: undefined, }; const { getByText } = render({ diff --git a/ui/components/multichain/edit-networks-modal/edit-networks-modal.js b/ui/components/multichain/edit-networks-modal/edit-networks-modal.js index 3cce3266ad5e..efb50fe8baee 100644 --- a/ui/components/multichain/edit-networks-modal/edit-networks-modal.js +++ b/ui/components/multichain/edit-networks-modal/edit-networks-modal.js @@ -1,9 +1,9 @@ import PropTypes from 'prop-types'; -import React from 'react'; +import React, { useMemo } from 'react'; import { useSelector } from 'react-redux'; import { TextVariant } from '../../../helpers/constants/design-system'; import { useI18nContext } from '../../../hooks/useI18nContext'; -import { getNonTestNetworks, getTestNetworks } from '../../../selectors'; +import { getNetworkConfigurationsByChainId } from '../../../selectors'; import { Modal, ModalOverlay, @@ -14,11 +14,28 @@ import { Box, } from '../../component-library'; import { NetworkListItem } from '..'; +import { + CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP, + TEST_CHAINS, +} from '../../../../shared/constants/network'; export const EditNetworksModal = ({ onClose }) => { const t = useI18nContext(); - const nonTestNetworks = useSelector(getNonTestNetworks); - const testNetworks = useSelector(getTestNetworks); + + const networkConfigurations = useSelector(getNetworkConfigurationsByChainId); + const [nonTestNetworks, testNetworks] = useMemo( + () => + Object.entries(networkConfigurations).reduce( + ([nonTestNetworksList, testNetworksList], [chainId, network]) => { + const isTest = TEST_CHAINS.includes(chainId); + (isTest ? testNetworksList : nonTestNetworksList).push(network); + return [nonTestNetworksList, testNetworksList]; + }, + [[], []], + ), + [networkConfigurations], + ); + return ( {
{nonTestNetworks.map((network) => ( { - console.log(network.id); + console.log(network.chainId); }} startAccessory={} /> @@ -61,11 +78,11 @@ export const EditNetworksModal = ({ onClose }) => { {testNetworks.map((network) => ( { - console.log(network.id); + console.log(network.chainId); }} startAccessory={} showEndAccessory={false} diff --git a/ui/components/multichain/network-list-item-menu/network-list-item-menu.js b/ui/components/multichain/network-list-item-menu/network-list-item-menu.js index 32b648eebaab..124be1999f1f 100644 --- a/ui/components/multichain/network-list-item-menu/network-list-item-menu.js +++ b/ui/components/multichain/network-list-item-menu/network-list-item-menu.js @@ -28,8 +28,8 @@ export const NetworkListItemMenu = ({ onClickOutside={onClose} referenceElement={anchorElement} role={PopoverRole.Dialog} - position={PopoverPosition.Bottom} - offset={[0, 0]} + position={PopoverPosition.BottomEnd} + offset={[8, 0]} padding={0} isOpen={isOpen} isPortal @@ -43,8 +43,6 @@ export const NetworkListItemMenu = ({ iconName={IconName.Edit} onClick={(e) => { e.stopPropagation(); - - // Pass network info? onEditClick(); }} data-testid="network-list-item-options-edit" @@ -58,8 +56,6 @@ export const NetworkListItemMenu = ({ iconColor={IconColor.errorDefault} onClick={(e) => { e.stopPropagation(); - - // Pass network info? onDeleteClick(); }} data-testid="network-list-item-options-delete" diff --git a/ui/components/multichain/network-list-item/__snapshots__/network-list-item.test.js.snap b/ui/components/multichain/network-list-item/__snapshots__/network-list-item.test.js.snap index 755b6b7fad80..fdf65d6d9337 100644 --- a/ui/components/multichain/network-list-item/__snapshots__/network-list-item.test.js.snap +++ b/ui/components/multichain/network-list-item/__snapshots__/network-list-item.test.js.snap @@ -3,10 +3,10 @@ exports[`NetworkListItem renders properly 1`] = `
Polygon logo
-

- Polygon -

+

+ Polygon +

+
diff --git a/ui/components/multichain/network-list-item/index.scss b/ui/components/multichain/network-list-item/index.scss index a7a1e6aa158e..6597487c9cf6 100644 --- a/ui/components/multichain/network-list-item/index.scss +++ b/ui/components/multichain/network-list-item/index.scss @@ -22,17 +22,6 @@ } } - &__network-name { - width: 100%; - flex: 1; - overflow: hidden; - text-align: start; - - button:hover { - opacity: 1; - } - } - &__tooltip { display: inline; } diff --git a/ui/components/multichain/network-list-item/network-list-item.test.js b/ui/components/multichain/network-list-item/network-list-item.test.js index 924148b36244..4219aab9590d 100644 --- a/ui/components/multichain/network-list-item/network-list-item.test.js +++ b/ui/components/multichain/network-list-item/network-list-item.test.js @@ -1,16 +1,15 @@ /* eslint-disable jest/require-top-level-describe */ import React from 'react'; import { fireEvent, render } from '@testing-library/react'; -import { useSelector } from 'react-redux'; import { POL_TOKEN_IMAGE_URL, POLYGON_DISPLAY_NAME, } from '../../../../shared/constants/network'; -import { getLocalNetworkMenuRedesignFeatureFlag } from '../../../helpers/utils/feature-flags'; import { NetworkListItem } from '.'; const DEFAULT_PROPS = { name: POLYGON_DISPLAY_NAME, + chainId: '0x1', iconSrc: POL_TOKEN_IMAGE_URL, selected: false, onClick: () => undefined, @@ -22,30 +21,13 @@ jest.mock('react-redux', () => ({ useSelector: jest.fn(), })); -const generateUseSelectorRouter = (opts) => (selector) => { - if (selector === getLocalNetworkMenuRedesignFeatureFlag) { - return opts.networkMenuRedesign ?? false; - } - return undefined; -}; - describe('NetworkListItem', () => { it('renders properly', () => { - useSelector.mockImplementation( - generateUseSelectorRouter({ - networkMenuRedesign: false, - }), - ); const { container } = render(); expect(container).toMatchSnapshot(); }); it('does not render the delete icon when no onDeleteClick is clicked', () => { - useSelector.mockImplementation( - generateUseSelectorRouter({ - networkMenuRedesign: false, - }), - ); const { container } = render( , ); @@ -55,11 +37,6 @@ describe('NetworkListItem', () => { }); it('shows as selected when selected', () => { - useSelector.mockImplementation( - generateUseSelectorRouter({ - networkMenuRedesign: false, - }), - ); const { container } = render( , ); @@ -71,11 +48,6 @@ describe('NetworkListItem', () => { }); it('renders a tooltip when the network name is very long', () => { - useSelector.mockImplementation( - generateUseSelectorRouter({ - networkMenuRedesign: false, - }), - ); const { container } = render( { }); it('executes onClick when the item is clicked', () => { - useSelector.mockImplementation( - generateUseSelectorRouter({ - networkMenuRedesign: false, - }), - ); const onClick = jest.fn(); const { container } = render( , @@ -102,11 +69,6 @@ describe('NetworkListItem', () => { }); it('executes onDeleteClick when the delete button is clicked', () => { - useSelector.mockImplementation( - generateUseSelectorRouter({ - networkMenuRedesign: true, - }), - ); const onDeleteClick = jest.fn(); const onClick = jest.fn(); @@ -118,7 +80,7 @@ describe('NetworkListItem', () => { />, ); - fireEvent.click(getByTestId('network-list-item-options-button')); + fireEvent.click(getByTestId('network-list-item-options-button-0x1')); fireEvent.click(getByTestId('network-list-item-options-delete')); expect(onDeleteClick).toHaveBeenCalledTimes(1); diff --git a/ui/components/multichain/network-list-item/network-list-item.js b/ui/components/multichain/network-list-item/network-list-item.tsx similarity index 50% rename from ui/components/multichain/network-list-item/network-list-item.js rename to ui/components/multichain/network-list-item/network-list-item.tsx index 5f52fcd19832..f80572eb1e4a 100644 --- a/ui/components/multichain/network-list-item/network-list-item.js +++ b/ui/components/multichain/network-list-item/network-list-item.tsx @@ -1,18 +1,18 @@ -import React, { useEffect, useRef, useState } from 'react'; +import React, { ReactNode, useEffect, useRef, useState } from 'react'; import classnames from 'classnames'; import PropTypes from 'prop-types'; -import { useSelector } from 'react-redux'; import { AlignItems, BackgroundColor, BlockSize, BorderRadius, - Color, Display, JustifyContent, TextColor, - Size, IconColor, + FlexDirection, + TextVariant, + BorderColor, } from '../../../helpers/constants/design-system'; import { AvatarNetwork, @@ -20,70 +20,74 @@ import { Box, ButtonIcon, ButtonIconSize, + Icon, IconName, + IconSize, Text, } from '../../component-library'; import { useI18nContext } from '../../../hooks/useI18nContext'; import { getAvatarNetworkColor } from '../../../helpers/utils/accounts'; import Tooltip from '../../ui/tooltip/tooltip'; import { NetworkListItemMenu } from '../network-list-item-menu'; -import { getLocalNetworkMenuRedesignFeatureFlag } from '../../../helpers/utils/feature-flags'; +// TODO: Consider increasing this. This tooltip is +// rendering when it has enough room to see everything const MAXIMUM_CHARACTERS_WITHOUT_TOOLTIP = 20; export const NetworkListItem = ({ name, iconSrc, iconSize = AvatarNetworkSize.Md, + rpcEndpoint, + chainId, selected = false, focus = true, onClick, onDeleteClick, onEditClick, + onRpcEndpointClick, startAccessory, showEndAccessory = true, +}: { + name: string; + iconSrc?: string; + iconSize?: AvatarNetworkSize; + rpcEndpoint?: { name?: string; url: string }; + chainId?: string; + selected?: boolean; + onClick: () => void; + onRpcEndpointClick?: () => void; + onDeleteClick?: () => void; + onEditClick?: () => void; + focus?: boolean; + startAccessory?: ReactNode; + showEndAccessory?: boolean; }) => { const t = useI18nContext(); - const networkRef = useRef(); + const networkRef = useRef(null); const [networkListItemMenuElement, setNetworkListItemMenuElement] = useState(); - const setNetworkListItemMenuRef = (ref) => { + + // I can't find a type that satisfies this. + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const setNetworkListItemMenuRef = (ref: any) => { setNetworkListItemMenuElement(ref); }; const [networkOptionsMenuOpen, setNetworkOptionsMenuOpen] = useState(false); - const networkMenuRedesign = useSelector( - getLocalNetworkMenuRedesignFeatureFlag, - ); const renderButton = () => { - if (networkMenuRedesign) { - return onDeleteClick || onEditClick ? ( - { - e.stopPropagation(); - setNetworkOptionsMenuOpen(true); - }} - size={ButtonIconSize.Sm} - /> - ) : null; - } - - return onDeleteClick ? ( + return onDeleteClick || onEditClick ? ( { + iconName={IconName.MoreVertical} + ref={setNetworkListItemMenuRef} + data-testid={`network-list-item-options-button-${chainId}`} + ariaLabel={t('networkOptions')} + onClick={(e: React.MouseEvent) => { e.stopPropagation(); - onDeleteClick(); + setNetworkOptionsMenuOpen(true); }} + size={ButtonIconSize.Sm} /> ) : null; }; @@ -93,7 +97,7 @@ export const NetworkListItem = ({ } }, [networkRef, focus]); - const handleKeyPress = (e) => { + const handleKeyPress = (e: React.KeyboardEvent) => { if (e.key === 'Enter') { e.stopPropagation(); // Prevent the event from reaching the parent container onClick(); @@ -102,9 +106,14 @@ export const NetworkListItem = ({ return ( )} - - {name.length > MAXIMUM_CHARACTERS_WITHOUT_TOOLTIP ? ( - + {name?.length > MAXIMUM_CHARACTERS_WITHOUT_TOOLTIP ? ( + + {name} + + ) : ( + name + )} + + + {rpcEndpoint && ( + { + e.stopPropagation(); + onRpcEndpointClick?.(); + }} + > + - {name} - - ) : ( - name - )} - + {rpcEndpoint.name ?? new URL(rpcEndpoint.url).host} + + + + )} + {renderButton()} {showEndAccessory ? ( setUrl(e.target.value)} + autoFocus /> {error && ( {error} diff --git a/ui/components/multichain/network-list-menu/add-rpc-url-modal/__snapshots__/add-rpc-url-modal.test.tsx.snap b/ui/components/multichain/network-list-menu/add-rpc-url-modal/__snapshots__/add-rpc-url-modal.test.tsx.snap index 6f151cfe0c0e..3450ef1c5eeb 100644 --- a/ui/components/multichain/network-list-menu/add-rpc-url-modal/__snapshots__/add-rpc-url-modal.test.tsx.snap +++ b/ui/components/multichain/network-list-menu/add-rpc-url-modal/__snapshots__/add-rpc-url-modal.test.tsx.snap @@ -18,13 +18,13 @@ exports[`AddRpcUrlModal should render correctly 1`] = ` rpcUrl
setUrl(e.target.value)} + autoFocus /> {error && ( {error} diff --git a/ui/components/multichain/network-list-menu/index.scss b/ui/components/multichain/network-list-menu/index.scss index 32a7c394e6f7..33e4c2b6caae 100644 --- a/ui/components/multichain/network-list-menu/index.scss +++ b/ui/components/multichain/network-list-menu/index.scss @@ -1,10 +1,19 @@ .multichain-network-list-menu-content-wrapper { &__dialog { + height: 100vh; max-height: 100%; } } .multichain-network-list-menu { - max-height: 100%; + height: 100%; overflow: auto; } + +.rpc-list-item { + overflow: hidden; +} + +.rpc-list-item button { + text-align: start; +} diff --git a/ui/components/multichain/network-list-menu/network-list-menu.js b/ui/components/multichain/network-list-menu/network-list-menu.js deleted file mode 100644 index 3900ba4a067a..000000000000 --- a/ui/components/multichain/network-list-menu/network-list-menu.js +++ /dev/null @@ -1,647 +0,0 @@ -import React, { useContext, useEffect, useMemo, useState } from 'react'; -import PropTypes from 'prop-types'; -import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd'; -import { useDispatch, useSelector } from 'react-redux'; -import { useHistory } from 'react-router-dom'; -import Fuse from 'fuse.js'; -import { useI18nContext } from '../../../hooks/useI18nContext'; -import { NetworkListItem } from '../network-list-item'; -import { - hideNetworkBanner, - setActiveNetwork, - setShowTestNetworks, - showModal, - toggleNetworkMenu, - updateNetworksList, - setNetworkClientIdForDomain, - setEditedNetwork, -} from '../../../store/actions'; -import { - FEATURED_RPCS, - TEST_CHAINS, -} from '../../../../shared/constants/network'; -import { - getCurrentChainId, - getCurrentNetwork, - getNonTestNetworks, - getShowTestNetworks, - getTestNetworks, - getOrderedNetworksList, - getOnboardedInThisUISession, - getShowNetworkBanner, - getOriginOfCurrentTab, - getUseRequestQueue, - getNetworkConfigurations, - getEditedNetwork, - getAllDomains, -} from '../../../selectors'; -import ToggleButton from '../../ui/toggle-button'; -import { - AlignItems, - BackgroundColor, - Display, - FlexDirection, - JustifyContent, - TextColor, -} from '../../../helpers/constants/design-system'; -import { - Box, - ButtonSecondary, - ButtonSecondarySize, - Modal, - ModalOverlay, - Text, - BannerBase, - IconName, - ModalContent, - ModalHeader, - AvatarNetworkSize, -} from '../../component-library'; -import { ADD_POPULAR_CUSTOM_NETWORK } from '../../../helpers/constants/routes'; -import { getEnvironmentType } from '../../../../app/scripts/lib/util'; -import { ENVIRONMENT_TYPE_FULLSCREEN } from '../../../../shared/constants/app'; -import { MetaMetricsContext } from '../../../contexts/metametrics'; -import { - MetaMetricsEventCategory, - MetaMetricsEventName, -} from '../../../../shared/constants/metametrics'; -import { - getCompletedOnboarding, - getIsUnlocked, -} from '../../../ducks/metamask/metamask'; -import { getLocalNetworkMenuRedesignFeatureFlag } from '../../../helpers/utils/feature-flags'; -import AddNetworkModal from '../../../pages/onboarding-flow/add-network-modal'; -import PopularNetworkList from './popular-network-list/popular-network-list'; -import NetworkListSearch from './network-list-search/network-list-search'; -import AddRpcUrlModal from './add-rpc-url-modal/add-rpc-url-modal'; - -export const ACTION_MODES = { - // Displays the search box and network list - LIST: 'list', - // Displays the Add form - ADD: 'add', - // Displays the Edit form - EDIT: 'edit', - // Displays the page for adding an additional RPC URL - ADD_RPC: 'add_rpc', -}; - -export const NetworkListMenu = ({ onClose }) => { - const t = useI18nContext(); - - const nonTestNetworks = useSelector(getNonTestNetworks); - const testNetworks = useSelector(getTestNetworks); - const showTestNetworks = useSelector(getShowTestNetworks); - const currentChainId = useSelector(getCurrentChainId); - const networkMenuRedesign = useSelector( - getLocalNetworkMenuRedesignFeatureFlag, - ); - - const selectedTabOrigin = useSelector(getOriginOfCurrentTab); - const useRequestQueue = useSelector(getUseRequestQueue); - const networkConfigurations = useSelector(getNetworkConfigurations); - const domains = useSelector(getAllDomains); - - const dispatch = useDispatch(); - const history = useHistory(); - const trackEvent = useContext(MetaMetricsContext); - - const currentNetwork = useSelector(getCurrentNetwork); - const currentlyOnTestNetwork = TEST_CHAINS.includes(currentChainId); - - const environmentType = getEnvironmentType(); - const isFullScreen = environmentType === ENVIRONMENT_TYPE_FULLSCREEN; - - const completedOnboarding = useSelector(getCompletedOnboarding); - - const isUnlocked = useSelector(getIsUnlocked); - - const orderedNetworksList = useSelector(getOrderedNetworksList); - - const editedNetwork = useSelector(getEditedNetwork); - - const [actionMode, setActionMode] = useState( - editedNetwork ? ACTION_MODES.EDIT : ACTION_MODES.LIST, - ); - const [networkFormInformation, setNetworkFormInformation] = useState({ - networkNameForm: '', - networkChainIdForm: '', - networkTickerForm: '', - }); - - const [prevActionMode, setPrevActionMode] = useState(null); - - const networkToEdit = useMemo(() => { - const network = [...nonTestNetworks, ...testNetworks].find( - (n) => n.id === editedNetwork?.networkConfigurationId, - ); - return network ? { ...network, label: network.nickname } : undefined; - }, [editedNetwork, nonTestNetworks, testNetworks]); - - const networkConfigurationChainIds = Object.values(networkConfigurations).map( - (net) => net.chainId, - ); - - const sortedFeaturedNetworks = FEATURED_RPCS.sort((a, b) => - a.nickname > b.nickname ? 1 : -1, - ).slice(0, FEATURED_RPCS.length); - - const notExistingNetworkConfigurations = sortedFeaturedNetworks.filter( - ({ chainId }) => !networkConfigurationChainIds.includes(chainId), - ); - - const newOrderNetworks = () => { - if (!orderedNetworksList || orderedNetworksList.length === 0) { - return nonTestNetworks; - } - - // Create a mapping of chainId to index in orderedNetworksList - const orderedIndexMap = {}; - orderedNetworksList.forEach((network, index) => { - orderedIndexMap[`${network.networkId}_${network.networkRpcUrl}`] = index; - }); - - // Sort nonTestNetworks based on the order in orderedNetworksList - const sortedNonTestNetworks = nonTestNetworks.sort((a, b) => { - const keyA = `${a.chainId}_${a.rpcUrl}`; - const keyB = `${b.chainId}_${b.rpcUrl}`; - return orderedIndexMap[keyA] - orderedIndexMap[keyB]; - }); - - return sortedNonTestNetworks; - }; - - const networksList = newOrderNetworks(); - const [items, setItems] = useState([...networksList]); - - useEffect(() => { - setActionMode(ACTION_MODES.LIST); - setPrevActionMode(null); - if (currentlyOnTestNetwork) { - dispatch(setShowTestNetworks(currentlyOnTestNetwork)); - } - }, [dispatch, currentlyOnTestNetwork]); - - const [searchQuery, setSearchQuery] = useState(''); - const [focusSearch, setFocusSearch] = useState(false); - const onboardedInThisUISession = useSelector(getOnboardedInThisUISession); - const showNetworkBanner = useSelector(getShowNetworkBanner); - const showBanner = - completedOnboarding && !onboardedInThisUISession && showNetworkBanner; - - const onDragEnd = (result) => { - if (!result.destination) { - return; - } - - const newItems = [...items]; - const [removed] = newItems.splice(result.source.index, 1); - newItems.splice(result.destination.index, 0, removed); - - // Convert the updated array back to NetworksInfo format - const orderedArray = newItems.map((obj) => ({ - networkId: obj.chainId, // Assuming chainId is the networkId - networkRpcUrl: obj.rpcUrl, - })); - - dispatch(updateNetworksList(orderedArray)); - - setItems(newItems); - }; - - let searchResults = - [...networksList].length === items.length ? items : [...networksList]; - - let searchAddNetworkResults = - [...notExistingNetworkConfigurations].length === items.length - ? items - : [...notExistingNetworkConfigurations]; - - let searchTestNetworkResults = [...testNetworks]; - - if (focusSearch && searchQuery !== '') { - const fuse = new Fuse(searchResults, { - threshold: 0.2, - location: 0, - distance: 100, - maxPatternLength: 32, - minMatchCharLength: 1, - shouldSort: true, - keys: ['nickname', 'chainId', 'ticker'], - }); - const fuseForPopularNetworks = new Fuse(searchAddNetworkResults, { - threshold: 0.2, - location: 0, - distance: 100, - maxPatternLength: 32, - minMatchCharLength: 1, - shouldSort: true, - keys: ['nickname', 'chainId', 'ticker'], - }); - - const fuseForTestsNetworks = new Fuse(searchTestNetworkResults, { - threshold: 0.2, - location: 0, - distance: 100, - maxPatternLength: 32, - minMatchCharLength: 1, - shouldSort: true, - keys: ['nickname', 'chainId', 'ticker'], - }); - - fuse.setCollection(searchResults); - fuseForPopularNetworks.setCollection(searchAddNetworkResults); - fuseForTestsNetworks.setCollection(searchTestNetworkResults); - - const fuseResults = fuse.search(searchQuery); - const fuseForPopularNetworksResults = - fuseForPopularNetworks.search(searchQuery); - const fuseForTestsNetworksResults = - fuseForTestsNetworks.search(searchQuery); - - searchResults = searchResults.filter((network) => - fuseResults.includes(network), - ); - searchAddNetworkResults = searchAddNetworkResults.filter((network) => - fuseForPopularNetworksResults.includes(network), - ); - searchTestNetworkResults = searchTestNetworkResults.filter((network) => - fuseForTestsNetworksResults.includes(network), - ); - } - - const getOnEditCallback = (network) => { - dispatch( - setEditedNetwork({ - networkConfigurationId: network.id, - nickname: network.nickname, - }), - ); - setActionMode(ACTION_MODES.EDIT); - setPrevActionMode(ACTION_MODES.LIST); - }; - - const getOnEdit = (network) => { - return () => getOnEditCallback(network); - }; - - const generateNetworkListItem = ({ - network, - isCurrentNetwork, - canDeleteNetwork, - }) => { - return ( - { - dispatch(toggleNetworkMenu()); - dispatch(setActiveNetwork(network.providerType || network.id)); - - // If presently on and connected to a dapp, communicate a change to - // the dapp via silent switchEthereumChain that the network has - // changed due to user action - if ( - useRequestQueue && - selectedTabOrigin && - domains[selectedTabOrigin] - ) { - setNetworkClientIdForDomain(selectedTabOrigin, network.id); - } - - trackEvent({ - event: MetaMetricsEventName.NavNetworkSwitched, - category: MetaMetricsEventCategory.Network, - properties: { - location: 'Network Menu', - chain_id: currentChainId, - from_network: currentChainId, - to_network: network.chainId, - }, - }); - }} - onDeleteClick={ - canDeleteNetwork - ? () => { - dispatch(toggleNetworkMenu()); - dispatch( - showModal({ - name: 'CONFIRM_DELETE_NETWORK', - target: network.id, - onConfirm: () => undefined, - }), - ); - } - : null - } - onEditClick={() => getOnEditCallback(network)} - /> - ); - }; - - const generateMenuItems = (desiredNetworks) => { - return desiredNetworks.map((network) => { - const isCurrentNetwork = - currentNetwork.id === network.id && - currentNetwork.rpcUrl === network.rpcUrl; - - const canDeleteNetwork = - isUnlocked && !isCurrentNetwork && network.removable; - - return generateNetworkListItem({ - network, - isCurrentNetwork, - canDeleteNetwork, - }); - }); - }; - - const handleToggle = (value) => { - const shouldShowTestNetworks = !value; - dispatch(setShowTestNetworks(shouldShowTestNetworks)); - if (shouldShowTestNetworks) { - trackEvent({ - event: MetaMetricsEventName.TestNetworksDisplayed, - category: MetaMetricsEventCategory.Network, - }); - } - }; - - const goToRpcFormEdit = () => { - setActionMode(ACTION_MODES.ADD_RPC); - setPrevActionMode(ACTION_MODES.EDIT); - }; - const goToRpcFormAdd = () => { - setActionMode(ACTION_MODES.ADD_RPC); - setPrevActionMode(ACTION_MODES.ADD); - }; - - const renderListNetworks = () => { - if (actionMode === ACTION_MODES.LIST) { - return ( - <> - - - - {showBanner ? ( - - drag-and-drop - - } - onClose={() => hideNetworkBanner()} - description={t('dragAndDropBanner')} - /> - ) : null} - - {searchResults.length > 0 ? ( - - - {t('enabledNetworks')} - - - ) : null} - - {searchResults.length === 0 && - searchAddNetworkResults.length === 0 && - searchTestNetworkResults.length === 0 && - focusSearch ? ( - - {t('noNetworksFound')} - - ) : ( - - - {(provided) => ( - - {searchResults.map((network, index) => { - const isCurrentNetwork = - currentNetwork.id === network.id; - - const canDeleteNetwork = - isUnlocked && - !isCurrentNetwork && - network.removable; - - const networkListItem = generateNetworkListItem({ - network, - isCurrentNetwork, - canDeleteNetwork, - }); - - return ( - - {(providedDrag) => ( - - {networkListItem} - - )} - - ); - })} - {provided.placeholder} - - )} - - - )} - {networkMenuRedesign ? ( - - ) : null} - - {searchTestNetworkResults.length > 0 ? ( - - - {t('showTestnetNetworks')} - - - - ) : null} - - {showTestNetworks || currentlyOnTestNetwork ? ( - - {generateMenuItems(searchTestNetworkResults)} - - ) : null} - - - - - { - if (!networkMenuRedesign) { - if (isFullScreen) { - if (completedOnboarding) { - history.push(ADD_POPULAR_CUSTOM_NETWORK); - } else { - dispatch(showModal({ name: 'ONBOARDING_ADD_NETWORK' })); - } - } else { - global.platform.openExtensionInBrowser( - ADD_POPULAR_CUSTOM_NETWORK, - ); - } - dispatch(toggleNetworkMenu()); - return; - } - trackEvent({ - event: MetaMetricsEventName.AddNetworkButtonClick, - category: MetaMetricsEventCategory.Network, - }); - setActionMode(ACTION_MODES.ADD); - setPrevActionMode(ACTION_MODES.LIST); - }} - > - {networkMenuRedesign ? t('addCustomNetwork') : t('addNetwork')} - - - - ); - } else if (actionMode === ACTION_MODES.ADD) { - return ( - - ); - } else if (actionMode === ACTION_MODES.EDIT) { - return ( - - ); - } else if (actionMode === ACTION_MODES.ADD_RPC) { - return ; - } - return null; // Unreachable, but satisfies linter - }; - - // Modal back button - let onBack; - if (actionMode === ACTION_MODES.EDIT || actionMode === ACTION_MODES.ADD) { - onBack = () => setActionMode(ACTION_MODES.LIST); - } else if ( - actionMode === ACTION_MODES.ADD_RPC && - prevActionMode === ACTION_MODES.EDIT - ) { - onBack = () => setActionMode(ACTION_MODES.EDIT); - } else if ( - actionMode === ACTION_MODES.ADD_RPC && - prevActionMode === ACTION_MODES.ADD - ) { - onBack = () => setActionMode(ACTION_MODES.ADD); - } - - // Modal title - let title; - if (actionMode === ACTION_MODES.LIST) { - title = t('networkMenuHeading'); - } else if (actionMode === ACTION_MODES.ADD) { - title = t('addCustomNetwork'); - } else if (actionMode === ACTION_MODES.ADD_RPC) { - title = t('addRpcUrl'); - } else { - title = editedNetwork?.nickname ?? ''; - } - - return ( - - - - - {title} - - {renderListNetworks()} - - - ); -}; - -NetworkListMenu.propTypes = { - /** - * Executes when the menu should be closed - */ - onClose: PropTypes.func.isRequired, -}; diff --git a/ui/components/multichain/network-list-menu/network-list-menu.test.js b/ui/components/multichain/network-list-menu/network-list-menu.test.js index f7c346fb2070..702f5b00bf75 100644 --- a/ui/components/multichain/network-list-menu/network-list-menu.test.js +++ b/ui/components/multichain/network-list-menu/network-list-menu.test.js @@ -8,6 +8,9 @@ import { MAINNET_DISPLAY_NAME, SEPOLIA_DISPLAY_NAME, NETWORK_TYPES, + LINEA_MAINNET_DISPLAY_NAME, + BNB_DISPLAY_NAME, + LINEA_SEPOLIA_DISPLAY_NAME, } from '../../../../shared/constants/network'; import { NetworkListMenu } from '.'; @@ -36,8 +39,77 @@ const render = ({ const state = { metamask: { ...mockState.metamask, + // this could use the network controllers default state instead + networkConfigurationsByChainId: { + '0x1': { + nativeCurrency: 'ETH', + chainId: '0x1', + name: MAINNET_DISPLAY_NAME, + defaultRpcEndpointIndex: 0, + rpcEndpoints: [ + { + networkClientId: NETWORK_TYPES.MAINNET, + }, + ], + }, + '0xe708': { + nativeCurrency: 'ETH', + chainId: '0xe708', + name: LINEA_MAINNET_DISPLAY_NAME, + defaultRpcEndpointIndex: 0, + rpcEndpoints: [ + { + networkClientId: 'linea-mainnet', + }, + ], + }, + '0x38': { + nativeCurrency: 'BNB', + chainId: '0x38', + name: BNB_DISPLAY_NAME, + defaultRpcEndpointIndex: 0, + rpcEndpoints: [ + { + networkClientId: 'bnb-network', + }, + ], + }, + '0x5': { + nativeCurrency: 'ETH', + chainId: '0x5', + name: 'Chain 5', + defaultRpcEndpointIndex: 0, + rpcEndpoints: [ + { + networkClientId: 'goerli', + }, + ], + }, + '0x539': { + nativeCurrency: 'ETH', + chainId: '0x539', + name: SEPOLIA_DISPLAY_NAME, + defaultRpcEndpointIndex: 0, + rpcEndpoints: [ + { + networkClientId: 'sepolia', + }, + ], + }, + '0xe705': { + nativeCurrency: 'ETH', + chainId: '0xe705', + name: LINEA_SEPOLIA_DISPLAY_NAME, + defaultRpcEndpointIndex: 0, + rpcEndpoints: [ + { + networkClientId: 'linea-sepolia', + }, + ], + }, + }, isUnlocked, - selectedNetworkClientId, + selectedNetworkClientId: NETWORK_TYPES.MAINNET, preferences: { showTestNetworks, }, @@ -49,7 +121,7 @@ const render = ({ }, }, activeTab: { - origin, + origin: selectedTabOriginInDomainsState ? origin : undefined, }, }; @@ -59,7 +131,6 @@ const render = ({ describe('NetworkListMenu', () => { beforeEach(() => { - process.env.ENABLE_NETWORK_UI_REDESIGN = 'false'; jest.clearAllMocks(); }); @@ -70,7 +141,7 @@ describe('NetworkListMenu', () => { it('displays important controls', () => { const { getByText, getByPlaceholderText } = render(); - expect(getByText('Add network')).toBeInTheDocument(); + expect(getByText('Add a custom network')).toBeInTheDocument(); expect(getByText('Show test networks')).toBeInTheDocument(); expect(getByPlaceholderText('Search')).toBeInTheDocument(); }); @@ -106,7 +177,7 @@ describe('NetworkListMenu', () => { it('shows the correct selected network when networks share the same chain ID', () => { // Mainnet and Custom Mainnet RPC both use chain ID 0x1 - render({ + const { queryByText } = render({ showTestNetworks: false, currentChainId: CHAIN_IDS.MAINNET, selectedNetworkClientId: 'testNetworkConfigurationId', @@ -123,10 +194,7 @@ describe('NetworkListMenu', () => { ); expect(selectedNodes).toHaveLength(1); - const selectedNodeText = selectedNodes[0].querySelector( - '.multichain-network-list-item__network-name', - ).textContent; - expect(selectedNodeText).toStrictEqual('Custom Mainnet RPC'); + expect(queryByText('Ethereum Mainnet')).toBeInTheDocument(); }); it('narrows down search results', () => { @@ -141,14 +209,14 @@ describe('NetworkListMenu', () => { expect(queryByText('Chain 5')).not.toBeInTheDocument(); }); - it('enables the "Add Network" button when MetaMask is locked', () => { + it('enables the "Add a custom network" button when MetaMask is locked', () => { const { queryByText } = render({ isUnlocked: false }); - expect(queryByText('Add network')).toBeEnabled(); + expect(queryByText('Add a custom network')).toBeEnabled(); }); - it('enables the "Add Network" button when MetaMask is true', () => { + it('enables the "AAdd a custom network" button when MetaMask is true', () => { const { queryByText } = render({ isUnlocked: true }); - expect(queryByText('Add network')).toBeEnabled(); + expect(queryByText('Add a custom network')).toBeEnabled(); }); it('does not allow deleting networks when locked', () => { @@ -187,20 +255,6 @@ describe('NetworkListMenu', () => { }); describe('NetworkListMenu with ENABLE_NETWORK_UI_REDESIGN', () => { - // Set the environment variable before tests run - beforeEach(() => { - window.metamaskFeatureFlags = { - networkMenuRedesign: true, - }; - }); - - // Reset the environment variable after tests complete - afterEach(() => { - window.metamaskFeatureFlags = { - networkMenuRedesign: false, - }; - }); - it('should display "Arbitrum" when ENABLE_NETWORK_UI_REDESIGN is true', async () => { const { queryByText, getByPlaceholderText } = render(); diff --git a/ui/components/multichain/network-list-menu/network-list-menu.tsx b/ui/components/multichain/network-list-menu/network-list-menu.tsx new file mode 100644 index 000000000000..3e9892a9a9f3 --- /dev/null +++ b/ui/components/multichain/network-list-menu/network-list-menu.tsx @@ -0,0 +1,601 @@ +import React, { useContext, useEffect, useMemo, useState } from 'react'; +import { + DragDropContext, + Droppable, + Draggable, + DropResult, +} from 'react-beautiful-dnd'; +import { useDispatch, useSelector } from 'react-redux'; +import Fuse from 'fuse.js'; +import { + NetworkConfiguration, + RpcEndpointType, +} from '@metamask/network-controller'; +import { Hex } from '@metamask/utils'; +import { useI18nContext } from '../../../hooks/useI18nContext'; +import { NetworkListItem } from '../network-list-item'; +import { + hideNetworkBanner, + setActiveNetwork, + setShowTestNetworks, + showModal, + toggleNetworkMenu, + updateNetworksList, + setNetworkClientIdForDomain, + setEditedNetwork, +} from '../../../store/actions'; +import { + CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP, + CHAIN_IDS, + FEATURED_RPCS, + TEST_CHAINS, +} from '../../../../shared/constants/network'; +import { + getCurrentChainId, + getShowTestNetworks, + getOnboardedInThisUISession, + getShowNetworkBanner, + getOriginOfCurrentTab, + getUseRequestQueue, + getEditedNetwork, + getNetworkConfigurationsByChainId, + getOrderedNetworksList, + getIsAddingNewNetwork, + getIsMultiRpcOnboarding, + getAllDomains, +} from '../../../selectors'; +import ToggleButton from '../../ui/toggle-button'; +import { + AlignItems, + BackgroundColor, + BorderRadius, + Display, + FlexDirection, + JustifyContent, + TextAlign, + TextColor, + TextVariant, +} from '../../../helpers/constants/design-system'; +import { + Box, + ButtonSecondary, + ButtonSecondarySize, + Modal, + ModalOverlay, + Text, + BannerBase, + IconName, + ModalContent, + ModalHeader, + AvatarNetworkSize, +} from '../../component-library'; +import { MetaMetricsContext } from '../../../contexts/metametrics'; +import { + MetaMetricsEventCategory, + MetaMetricsEventName, +} from '../../../../shared/constants/metametrics'; +import { + getCompletedOnboarding, + getIsUnlocked, +} from '../../../ducks/metamask/metamask'; +import NetworksForm from '../../../pages/settings/networks-tab/networks-form'; +import { useNetworkFormState } from '../../../pages/settings/networks-tab/networks-form/networks-form-state'; +import PopularNetworkList from './popular-network-list/popular-network-list'; +import NetworkListSearch from './network-list-search/network-list-search'; +import AddRpcUrlModal from './add-rpc-url-modal/add-rpc-url-modal'; +import { SelectRpcUrlModal } from './select-rpc-url-modal/select-rpc-url-modal'; +import AddBlockExplorerModal from './add-block-explorer-modal/add-block-explorer-modal'; + +export enum ACTION_MODES { + // Displays the search box and network list + LIST, + // Displays the form to add or edit a network + ADD_EDIT, + // Displays the page for adding an additional RPC URL + ADD_RPC, + // Displays the page for adding an additional explorer URL + ADD_EXPLORER_URL, + // Displays the page for selecting an RPC URL + SELECT_RPC, +} + +export const NetworkListMenu = ({ onClose }: { onClose: () => void }) => { + const t = useI18nContext(); + const dispatch = useDispatch(); + const trackEvent = useContext(MetaMetricsContext); + + const showTestNetworks = useSelector(getShowTestNetworks); + const currentChainId = useSelector(getCurrentChainId); + const selectedTabOrigin = useSelector(getOriginOfCurrentTab); + const useRequestQueue = useSelector(getUseRequestQueue); + const isUnlocked = useSelector(getIsUnlocked); + const domains = useSelector(getAllDomains); + const orderedNetworksList = useSelector(getOrderedNetworksList); + const isAddingNewNetwork = useSelector(getIsAddingNewNetwork); + const isMultiRpcOnboarding = useSelector(getIsMultiRpcOnboarding); + const completedOnboarding = useSelector(getCompletedOnboarding); + const onboardedInThisUISession = useSelector(getOnboardedInThisUISession); + const showNetworkBanner = useSelector(getShowNetworkBanner); + const networkConfigurations = useSelector(getNetworkConfigurationsByChainId); + const { chainId: editingChainId, editCompleted } = + useSelector(getEditedNetwork) ?? {}; + + const currentlyOnTestNetwork = (TEST_CHAINS as Hex[]).includes( + currentChainId, + ); + + const [nonTestNetworks, testNetworks] = useMemo( + () => + Object.entries(networkConfigurations).reduce( + ([nonTestNetworksList, testNetworksList], [chainId, network]) => { + const isTest = (TEST_CHAINS as string[]).includes(chainId); + (isTest ? testNetworksList : nonTestNetworksList)[chainId] = network; + return [nonTestNetworksList, testNetworksList]; + }, + [ + {} as Record, + {} as Record, + ], + ), + [networkConfigurations], + ); + + // The network currently being edited, or undefined + // if the user is not currently editing a network. + const editedNetwork = useMemo( + () => + !editingChainId || editCompleted + ? undefined + : Object.entries(networkConfigurations).find( + ([chainId]) => chainId === editingChainId, + )?.[1], + [editingChainId, editCompleted, networkConfigurations], + ); + + // Tracks which page the user is on + const [actionMode, setActionMode] = useState( + isAddingNewNetwork || editedNetwork + ? ACTION_MODES.ADD_EDIT + : ACTION_MODES.LIST, + ); + + const networkFormState = useNetworkFormState(editedNetwork); + const { rpcUrls, setRpcUrls, blockExplorers, setBlockExplorers } = + networkFormState; + + const sortNetworks = (networks: Record) => + Object.values(networks).sort( + (a, b) => + orderedNetworksList.findIndex( + ({ networkId }) => networkId === a.chainId, + ) - + orderedNetworksList.findIndex( + ({ networkId }) => networkId === b.chainId, + ), + ); + + const [orderedNetworks, setOrderedNetworks] = useState( + sortNetworks(nonTestNetworks), + ); + useEffect( + () => setOrderedNetworks(sortNetworks(nonTestNetworks)), + [nonTestNetworks, orderedNetworksList], + ); + + // Re-orders networks when the user drag + drops them + const onDragEnd = (result: DropResult) => { + if (result.destination) { + const newOrderedNetworks = [...orderedNetworks]; + const [removed] = newOrderedNetworks.splice(result.source.index, 1); + newOrderedNetworks.splice(result.destination.index, 0, removed); + dispatch(updateNetworksList(newOrderedNetworks.map((n) => n.chainId))); + setOrderedNetworks(newOrderedNetworks); + } + }; + + const featuredNetworksNotYetEnabled = useMemo( + () => + FEATURED_RPCS.filter( + ({ chainId }) => !networkConfigurations[chainId], + ).sort((a, b) => a.name.localeCompare(b.name)), + [networkConfigurations], + ); + + // Searches networks by user input + const [searchQuery, setSearchQuery] = useState(''); + const [focusSearch, setFocusSearch] = useState(false); + + const searchNetworks = (networks: T[], query: string) => + searchQuery === '' + ? networks + : new Fuse(networks, { + threshold: 0.2, + location: 0, + distance: 100, + maxPatternLength: 32, + minMatchCharLength: 1, + shouldSort: false, // Maintain network order instead of ordering by search score + keys: ['name', 'chainId', 'nativeCrrency'], + }).search(query); + + const searchedEnabledNetworks = searchNetworks(orderedNetworks, searchQuery); + const searchedFeaturedNetworks = searchNetworks( + featuredNetworksNotYetEnabled, + searchQuery, + ); + const searchedTestNetworks = searchNetworks( + Object.values(testNetworks), + searchQuery, + ); + + // If any network has multiple RPC endpoints, show multi-rpc selectors for all networks + const showMultiRpcSelectors = [ + ...searchedEnabledNetworks, + ...searchedTestNetworks, + ].some((network) => network.rpcEndpoints.length > 1); + + // Renders a network in the network list + const generateNetworkListItem = (network: NetworkConfiguration) => { + const isCurrentNetwork = network.chainId === currentChainId; + const canDeleteNetwork = + isUnlocked && + !isCurrentNetwork && + network.chainId !== CHAIN_IDS.MAINNET && + network.chainId !== CHAIN_IDS.LINEA_MAINNET; + + return ( + { + const { networkClientId } = + network.rpcEndpoints[network.defaultRpcEndpointIndex]; + dispatch(setActiveNetwork(networkClientId)); + dispatch(toggleNetworkMenu()); + + // If presently on a dapp, communicate a change to + // the dapp via silent switchEthereumChain that the + // network has changed due to user action + if ( + useRequestQueue && + selectedTabOrigin && + domains[selectedTabOrigin] + ) { + setNetworkClientIdForDomain(selectedTabOrigin, networkClientId); + } + + trackEvent({ + event: MetaMetricsEventName.NavNetworkSwitched, + category: MetaMetricsEventCategory.Network, + properties: { + location: 'Network Menu', + chain_id: currentChainId, + from_network: currentChainId, + to_network: network.chainId, + }, + }); + }} + onDeleteClick={ + canDeleteNetwork + ? () => { + dispatch(toggleNetworkMenu()); + dispatch( + showModal({ + name: 'CONFIRM_DELETE_NETWORK', + target: network.chainId, + onConfirm: () => undefined, + }), + ); + } + : undefined + } + onEditClick={() => { + dispatch( + setEditedNetwork({ + chainId: network.chainId, + nickname: network.name, + }), + ); + setActionMode(ACTION_MODES.ADD_EDIT); + }} + onRpcEndpointClick={() => { + setActionMode(ACTION_MODES.SELECT_RPC); + dispatch(setEditedNetwork({ chainId: network.chainId })); + }} + /> + ); + }; + + const render = () => { + if (actionMode === ACTION_MODES.LIST) { + return ( + <> + + + {completedOnboarding && + !onboardedInThisUISession && + showNetworkBanner && + !searchQuery && ( + + drag-and-drop + + } + onClose={() => hideNetworkBanner()} + description={t('dragAndDropBanner')} + /> + )} + + {searchedEnabledNetworks.length > 0 && ( + + + {t('enabledNetworks')} + + + )} + + {searchedEnabledNetworks.length === 0 && + searchedFeaturedNetworks.length === 0 && + searchedTestNetworks.length === 0 && + focusSearch ? ( + + {t('noNetworksFound')} + + ) : ( + + + {(provided) => ( + + {searchedEnabledNetworks.map((network, index) => { + return ( + + {(providedDrag) => ( + + {generateNetworkListItem(network)} + + )} + + ); + })} + {provided.placeholder} + + )} + + + )} + + + {searchedTestNetworks.length > 0 ? ( + + + {t('showTestnetNetworks')} + + { + dispatch(setShowTestNetworks(!value)); + if (!value) { + trackEvent({ + event: MetaMetricsEventName.TestNetworksDisplayed, + category: MetaMetricsEventCategory.Network, + }); + } + }} + /> + + ) : null} + + {showTestNetworks || currentlyOnTestNetwork ? ( + + {searchedTestNetworks.map((network) => + generateNetworkListItem(network), + )} + + ) : null} + + + + + { + trackEvent({ + event: MetaMetricsEventName.AddNetworkButtonClick, + category: MetaMetricsEventCategory.Network, + }); + setActionMode(ACTION_MODES.ADD_EDIT); + }} + > + {t('addACustomNetwork')} + + + + ); + } else if (actionMode === ACTION_MODES.ADD_EDIT) { + return ( + setActionMode(ACTION_MODES.ADD_RPC)} + onBlockExplorerAdd={() => + setActionMode(ACTION_MODES.ADD_EXPLORER_URL) + } + /> + ); + } else if (actionMode === ACTION_MODES.ADD_RPC) { + return ( + { + // Note: We could choose to rename the URL if it already exists with a different name + if (rpcUrls.rpcEndpoints?.every((e) => e.url !== url)) { + setRpcUrls({ + rpcEndpoints: [ + ...rpcUrls.rpcEndpoints, + { url, name, type: RpcEndpointType.Custom }, + ], + defaultRpcEndpointIndex: rpcUrls.rpcEndpoints.length, + }); + } + setActionMode(ACTION_MODES.ADD_EDIT); + }} + /> + ); + } else if (actionMode === ACTION_MODES.ADD_EXPLORER_URL) { + return ( + { + if (blockExplorers.blockExplorerUrls?.every((u) => u !== url)) { + setBlockExplorers({ + blockExplorerUrls: [...blockExplorers.blockExplorerUrls, url], + defaultBlockExplorerUrlIndex: + blockExplorers.blockExplorerUrls.length, + }); + } + setActionMode(ACTION_MODES.ADD_EDIT); + }} + /> + ); + } else if (actionMode === ACTION_MODES.SELECT_RPC && editedNetwork) { + return ( + + ); + } + return null; // Should not be reachable + }; + + let title; + if (actionMode === ACTION_MODES.LIST) { + title = t('networkMenuHeading'); + } else if (actionMode === ACTION_MODES.ADD_EDIT && !editedNetwork) { + title = t('addACustomNetwork'); + } else if (actionMode === ACTION_MODES.ADD_RPC) { + title = t('addRpcUrl'); + } else if (actionMode === ACTION_MODES.ADD_EXPLORER_URL) { + title = t('addBlockExplorerUrl'); + } else if (actionMode === ACTION_MODES.SELECT_RPC) { + title = t('selectRpcUrl'); + } else { + title = editedNetwork?.name ?? ''; + } + + let onBack; + if (actionMode === ACTION_MODES.ADD_EDIT) { + onBack = () => { + editedNetwork ? dispatch(setEditedNetwork()) : networkFormState.clear(); + + setActionMode(ACTION_MODES.LIST); + }; + } else if ( + actionMode === ACTION_MODES.ADD_RPC || + actionMode === ACTION_MODES.ADD_EXPLORER_URL + ) { + onBack = () => setActionMode(ACTION_MODES.ADD_EDIT); + } + + if (isMultiRpcOnboarding) { + onBack = onClose; + } + + return ( + + + + + + {title} + + + {render()} + + + ); +}; diff --git a/ui/components/multichain/network-list-menu/popular-network-list/popular-network-list.test.tsx b/ui/components/multichain/network-list-menu/popular-network-list/popular-network-list.test.tsx index 468563214c6d..e67444d1eece 100644 --- a/ui/components/multichain/network-list-menu/popular-network-list/popular-network-list.test.tsx +++ b/ui/components/multichain/network-list-menu/popular-network-list/popular-network-list.test.tsx @@ -3,12 +3,10 @@ import { render, screen, fireEvent } from '@testing-library/react'; import '@testing-library/jest-dom/extend-expect'; import { useDispatch, useSelector } from 'react-redux'; import configureStore from 'redux-mock-store'; +import { RpcEndpointType } from '@metamask/network-controller'; import { renderWithProvider } from '../../../../../test/lib/render-helpers'; import { getUnapprovedConfirmations } from '../../../../selectors'; -import { - CHAIN_IDS, - RPCDefinition, -} from '../../../../../shared/constants/network'; +import { CHAIN_IDS } from '../../../../../shared/constants/network'; import PopularNetworkList from './popular-network-list'; jest.mock('react-redux', () => ({ @@ -57,26 +55,36 @@ describe('PopularNetworkList', () => { ...defaultProps, searchAddNetworkResults: [ { + blockExplorerUrls: [], chainId: CHAIN_IDS.MAINNET, - nickname: 'Network 1', - rpcPrefs: { - blockExplorerUrl: 'https://etherscan.com/', - imageUrl: 'https://example.com/image1.png', - }, - ticker: 'ETH', - rpcUrl: 'https://exampleEth.org/', + defaultBlockExplorerUrlIndex: 0, + defaultRpcEndpointIndex: 0, + name: 'Network 1', + nativeCurrency: 'ETH', + rpcEndpoints: [ + { + url: 'https://exampleEth.org/', + type: RpcEndpointType.Custom as const, + networkClientId: 'network1', + }, + ], }, { + blockExplorerUrls: [], chainId: CHAIN_IDS.BSC_TESTNET, - nickname: 'Network 2', - rpcPrefs: { - blockExplorerUrl: 'https://examplescan.com/', - imageUrl: 'https://example.com/image2.png', - }, - ticker: 'TST', - rpcUrl: 'https://example.org/', + defaultBlockExplorerUrlIndex: 0, + defaultRpcEndpointIndex: 0, + name: 'Network 2', + nativeCurrency: 'TST', + rpcEndpoints: [ + { + url: 'https://example.org/', + type: RpcEndpointType.Custom as const, + networkClientId: 'network2', + }, + ], }, - ] as RPCDefinition[], + ], }; render(); @@ -88,16 +96,21 @@ describe('PopularNetworkList', () => { ...defaultProps, searchAddNetworkResults: [ { + blockExplorerUrls: [], chainId: CHAIN_IDS.BSC_TESTNET, - nickname: 'Network 2', - rpcPrefs: { - blockExplorerUrl: 'https://examplescan.com/', - imageUrl: 'https://example.com/image2.png', - }, - ticker: 'TST', - rpcUrl: 'https://example.org/', + defaultBlockExplorerUrlIndex: 0, + defaultRpcEndpointIndex: 0, + name: 'Network 2', + nativeCurrency: 'TST', + rpcEndpoints: [ + { + url: 'https://example.org/', + type: RpcEndpointType.Custom as const, + networkClientId: 'network2', + }, + ], }, - ] as RPCDefinition[], + ], }; render(); diff --git a/ui/components/multichain/network-list-menu/popular-network-list/popular-network-list.tsx b/ui/components/multichain/network-list-menu/popular-network-list/popular-network-list.tsx index 62560c1866bd..99d2774659d8 100644 --- a/ui/components/multichain/network-list-menu/popular-network-list/popular-network-list.tsx +++ b/ui/components/multichain/network-list-menu/popular-network-list/popular-network-list.tsx @@ -1,6 +1,7 @@ -import React, { useState } from 'react'; +import React, { useState, useMemo } from 'react'; import { ApprovalType } from '@metamask/controller-utils'; import { useDispatch } from 'react-redux'; +import { AddNetworkFields } from '@metamask/network-controller'; import { useI18nContext } from '../../../../hooks/useI18nContext'; import { Box, @@ -35,14 +36,15 @@ import { TextColor, IconColor, TextVariant, + BorderColor, } from '../../../../helpers/constants/design-system'; -import { RPCDefinition } from '../../../../../shared/constants/network'; +import { CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP } from '../../../../../shared/constants/network'; import ZENDESK_URLS from '../../../../helpers/constants/zendesk-url'; const PopularNetworkList = ({ searchAddNetworkResults, }: { - searchAddNetworkResults: RPCDefinition[]; + searchAddNetworkResults: AddNetworkFields[]; }) => { const t = useI18nContext(); const isPopUp = getEnvironmentType() === ENVIRONMENT_TYPE_POPUP; @@ -64,6 +66,64 @@ const PopularNetworkList = ({ setReferenceElement(ref); }; + // Memoize the popover content so it only updates when searchAddNetworkResults changes + const popoverContent = useMemo(() => { + if (Object.keys(searchAddNetworkResults).length === 0) { + return null; + } + + return ( + + + + {t('additionalNetworks')} + + + + + + {t('popularNetworkAddToolTip', [ + + { + global.platform.openTab({ + url: ZENDESK_URLS.UNKNOWN_NETWORK, + }); + }} + > + {t('learnMoreUpperCase')} + + , + ])} + + + + + ); + }, [searchAddNetworkResults, referenceElement, isOpen]); + return ( - {Object.keys(searchAddNetworkResults).length === 0 ? null : ( - - - {t('additionalNetworks')} - - - - {t('popularNetworkAddToolTip', [ - - { - global.platform.openTab({ - url: ZENDESK_URLS.UNKNOWN_NETWORK, - }); - }} - > - {t('learnMoreUpperCase')} - - , - ])} - - - - - )} - {searchAddNetworkResults.map((item: RPCDefinition, index: number) => ( + {popoverContent} + {searchAddNetworkResults.map((network) => ( - {item.nickname} + {network.name} @@ -169,12 +184,24 @@ const PopularNetworkList = ({ origin: ORIGIN_METAMASK, type: ApprovalType.AddEthereumChain, requestData: { - chainId: item.chainId, - rpcUrl: item.rpcUrl, - ticker: item.ticker, - rpcPrefs: item.rpcPrefs, - imageUrl: item.rpcPrefs?.imageUrl, - chainName: item.nickname, + chainId: network.chainId, + rpcUrl: + network.rpcEndpoints[network.defaultRpcEndpointIndex] + .url, + ticker: network.nativeCurrency, + rpcPrefs: { + blockExplorerUrl: + network.defaultBlockExplorerUrlIndex === undefined + ? undefined + : network.blockExplorerUrls[ + network.defaultBlockExplorerUrlIndex + ], + }, + imageUrl: + CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP[ + network.chainId as keyof typeof CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP + ], + chainName: network.name, referrer: ORIGIN_METAMASK, source: MetaMetricsNetworkEventSource.NewAddNetworkFlow, }, diff --git a/ui/components/multichain/network-list-menu/rpc-list-item.tsx b/ui/components/multichain/network-list-menu/rpc-list-item.tsx index 086f9838d142..f6297d32145f 100644 --- a/ui/components/multichain/network-list-menu/rpc-list-item.tsx +++ b/ui/components/multichain/network-list-menu/rpc-list-item.tsx @@ -1,3 +1,4 @@ +import { RpcEndpointType } from '@metamask/network-controller'; import React from 'react'; import { infuraProjectId } from '../../../../shared/constants/network'; import { Box, Text } from '../../component-library'; @@ -12,12 +13,6 @@ import { BlockSize, } from '../../../helpers/constants/design-system'; -// TODO: Use version from network controller with v21 upgrade -enum RpcEndpointType { - Custom = 'custom', - Infura = 'infura', -} - export const stripKeyFromInfuraUrl = (endpoint: string) => { let modifiedEndpoint = endpoint; diff --git a/ui/components/multichain/network-list-menu/select-rpc-url-modal/select-rpc-url-modal.test.tsx b/ui/components/multichain/network-list-menu/select-rpc-url-modal/select-rpc-url-modal.test.tsx index 8f3de3d488f9..69e88f88f372 100644 --- a/ui/components/multichain/network-list-menu/select-rpc-url-modal/select-rpc-url-modal.test.tsx +++ b/ui/components/multichain/network-list-menu/select-rpc-url-modal/select-rpc-url-modal.test.tsx @@ -1,10 +1,10 @@ import React from 'react'; import { fireEvent, screen } from '@testing-library/react'; import configureMockStore from 'redux-mock-store'; +import { NetworkConfiguration } from '@metamask/network-controller'; import { renderWithProvider } from '../../../../../test/lib/render-helpers'; import { - // TODO: Add this API with network controller v21 upgrade - // updateNetwork, + updateNetwork, setActiveNetwork, setEditedNetwork, toggleNetworkMenu, @@ -20,8 +20,7 @@ jest.mock('react-redux', () => ({ })); jest.mock('../../../../store/actions', () => ({ - // TODO: Add this API with network controller v21 upgrade - // updateNetwork: jest.fn(), + updateNetwork: jest.fn(), setActiveNetwork: jest.fn(), setEditedNetwork: jest.fn(), toggleNetworkMenu: jest.fn(), @@ -36,7 +35,7 @@ const networkConfiguration = { { url: 'https://rpc.flashbots.net/', networkClientId: 'flashbots' }, ], defaultRpcEndpointIndex: 0, -}; +} as unknown as NetworkConfiguration; const store = mockStore({ metamask: { @@ -104,13 +103,12 @@ describe('SelectRpcUrlModal Component', () => { ); fireEvent.click(rpcEndpoint); - // TODO: Add this API with network controller v21 upgrade - // expect(mockDispatch).toHaveBeenCalledWith( - // updateNetwork({ - // ...networkConfiguration, - // defaultRpcEndpointIndex: 1, - // }), - // ); + expect(mockDispatch).toHaveBeenCalledWith( + updateNetwork({ + ...networkConfiguration, + defaultRpcEndpointIndex: 1, + }), + ); expect(mockDispatch).toHaveBeenCalledWith(setActiveNetwork('flashbots')); expect(mockDispatch).toHaveBeenCalledWith(setEditedNetwork()); expect(mockDispatch).toHaveBeenCalledWith(toggleNetworkMenu()); @@ -154,13 +152,12 @@ describe('SelectRpcUrlModal Component', () => { screen.getByText(stripProtocol(networkConfiguration.rpcEndpoints[1].url)), ); - // TODO: Add this API with network controller v21 upgrade - // expect(mockDispatch).toHaveBeenCalledWith( - // updateNetwork({ - // ...networkConfiguration, - // defaultRpcEndpointIndex: 1, - // }), - // ); + expect(mockDispatch).toHaveBeenCalledWith( + updateNetwork({ + ...networkConfiguration, + defaultRpcEndpointIndex: 1, + }), + ); expect(mockDispatch).toHaveBeenCalledWith( setActiveNetwork(networkConfiguration.rpcEndpoints[1].networkClientId), ); diff --git a/ui/components/multichain/network-list-menu/select-rpc-url-modal/select-rpc-url-modal.tsx b/ui/components/multichain/network-list-menu/select-rpc-url-modal/select-rpc-url-modal.tsx index b16dddf5f86e..7fc8e5e6fe3e 100644 --- a/ui/components/multichain/network-list-menu/select-rpc-url-modal/select-rpc-url-modal.tsx +++ b/ui/components/multichain/network-list-menu/select-rpc-url-modal/select-rpc-url-modal.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import { NetworkConfiguration } from '@metamask/network-controller'; import classnames from 'classnames'; import { useDispatch } from 'react-redux'; import { @@ -20,15 +21,14 @@ import { setActiveNetwork, setEditedNetwork, toggleNetworkMenu, + updateNetwork, } from '../../../../store/actions'; import RpcListItem from '../rpc-list-item'; export const SelectRpcUrlModal = ({ networkConfiguration, }: { - // TODO: `NetworkConfiguration` with network controller v21 upgrade - // eslint-disable-next-line @typescript-eslint/no-explicit-any - networkConfiguration: any; + networkConfiguration: NetworkConfiguration; }) => { const dispatch = useDispatch(); @@ -61,44 +61,39 @@ export const SelectRpcUrlModal = ({ - {networkConfiguration.rpcEndpoints.map( - // TODO: types will be inferred with network controller v21 upgrade - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (rpcEndpoint: any, index: number) => ( - { - // TODO: When this API becomes available with network controller v21 upgrade - // dispatch( - // updateNetwork({ - // ...networkConfiguration, - // defaultRpcEndpointIndex: index, - // }), - // ); - dispatch(setActiveNetwork(rpcEndpoint.networkClientId)); - dispatch(setEditedNetwork()); - dispatch(toggleNetworkMenu()); - }} - className={classnames('select-rpc-url__item', { - 'select-rpc-url__item--selected': - index === networkConfiguration.defaultRpcEndpointIndex, - })} - > - {index === networkConfiguration.defaultRpcEndpointIndex && ( - - )} - - - ), - )} + {networkConfiguration.rpcEndpoints.map((rpcEndpoint, index) => ( + { + dispatch( + updateNetwork({ + ...networkConfiguration, + defaultRpcEndpointIndex: index, + }), + ); + dispatch(setActiveNetwork(rpcEndpoint.networkClientId)); + dispatch(setEditedNetwork()); + dispatch(toggleNetworkMenu()); + }} + className={classnames('select-rpc-url__item', { + 'select-rpc-url__item--selected': + index === networkConfiguration.defaultRpcEndpointIndex, + })} + > + {index === networkConfiguration.defaultRpcEndpointIndex && ( + + )} + + + ))} ); }; diff --git a/ui/components/multichain/notification-detail-block-explorer-button/notification-detail-block-explorer-button.tsx b/ui/components/multichain/notification-detail-block-explorer-button/notification-detail-block-explorer-button.tsx index 02250c4ec36b..6ca6e27c491f 100644 --- a/ui/components/multichain/notification-detail-block-explorer-button/notification-detail-block-explorer-button.tsx +++ b/ui/components/multichain/notification-detail-block-explorer-button/notification-detail-block-explorer-button.tsx @@ -1,11 +1,10 @@ -import React, { useMemo } from 'react'; +import React from 'react'; import { useSelector } from 'react-redux'; -import type { NetworkConfiguration } from '@metamask/network-controller'; import type { NotificationServicesController } from '@metamask/notification-services-controller'; -import { getAllNetworks } from '../../../selectors'; +import { toHex } from '@metamask/controller-utils'; +import { getNetworkConfigurationsByChainId } from '../../../selectors'; import { CHAIN_IDS } from '../../../../shared/constants/network'; import { ButtonVariant } from '../../component-library'; -import { decimalToHex } from '../../../../shared/modules/conversion.utils'; import { useI18nContext } from '../../../hooks/useI18nContext'; import { getNetworkDetailsByChainId } from '../../../helpers/utils/notification.util'; import { NotificationDetailButton } from '../notification-detail-button'; @@ -27,21 +26,22 @@ export const NotificationDetailBlockExplorerButton = ({ }: NotificationDetailBlockExplorerButtonProps) => { const t = useI18nContext(); - const chainIdHex = decimalToHex(chainId); + const chainIdHex = toHex(chainId); const { blockExplorerConfig } = getNetworkDetailsByChainId( - `0x${chainIdHex}` as keyof typeof CHAIN_IDS, + chainIdHex as keyof typeof CHAIN_IDS, ); - const defaultNetworks: NetworkConfiguration[] = useSelector(getAllNetworks); - const defaultNetwork = useMemo(() => { - return defaultNetworks.find((n) => n.chainId === chainIdHex); - }, [defaultNetworks]); + const networkConfigurations = useSelector(getNetworkConfigurationsByChainId); + const networkConfiguration = networkConfigurations[chainIdHex]; + const configuredBlockExplorer = + networkConfiguration?.blockExplorerUrls?.[ + networkConfiguration.defaultBlockExplorerUrlIndex ?? -1 + ]; - const blockExplorerUrl = - defaultNetwork?.rpcPrefs?.blockExplorerUrl ?? blockExplorerConfig?.url; + const blockExplorerUrl = configuredBlockExplorer ?? blockExplorerConfig?.url; const getBlockExplorerButtonText = () => { - if (defaultNetwork?.rpcPrefs?.blockExplorerUrl) { + if (configuredBlockExplorer) { return t('notificationItemCheckBlockExplorer'); } if (blockExplorerConfig?.name) { diff --git a/ui/components/multichain/pages/permissions-page/permissions-page.test.js b/ui/components/multichain/pages/permissions-page/permissions-page.test.js index dacdda2f25d3..026cddeff34d 100644 --- a/ui/components/multichain/pages/permissions-page/permissions-page.test.js +++ b/ui/components/multichain/pages/permissions-page/permissions-page.test.js @@ -2,6 +2,8 @@ import React from 'react'; import configureStore from '../../../../store/store'; import mockState from '../../../../../test/data/mock-state.json'; import { renderWithProvider } from '../../../../../test/lib/render-helpers'; +import { mockNetworkState } from '../../../../../test/stub/networks'; +import { CHAIN_IDS } from '../../../../../shared/constants/network'; import { PermissionsPage } from './permissions-page'; mockState.metamask.subjectMetadata = { @@ -86,7 +88,13 @@ mockState.metamask.domains = { 'npm:@metamask/testSnap3': 'mainnet', }; -let store = configureStore(mockState); +let store = configureStore({ + ...mockState, + metamask: { + ...mockState.metamask, + ...mockNetworkState({ chainId: CHAIN_IDS.MAINNET, id: 'mainnet' }), + }, +}); describe('All Connections', () => { describe('render', () => { diff --git a/ui/components/multichain/pages/send/components/__snapshots__/network-picker.test.tsx.snap b/ui/components/multichain/pages/send/components/__snapshots__/network-picker.test.tsx.snap index aaf424f4ee44..37ca306bc436 100644 --- a/ui/components/multichain/pages/send/components/__snapshots__/network-picker.test.tsx.snap +++ b/ui/components/multichain/pages/send/components/__snapshots__/network-picker.test.tsx.snap @@ -12,12 +12,12 @@ exports[`SendPageNetworkPicker render renders correctly 1`] = `
- C + G
- Chain 5 + Goerli { }, }, }; - const store = configureStore()(mockState); + const store = configureStore()({ + ...mockState, + metamask: { ...mockNetworkState({ chainId: CHAIN_IDS.MAINNET }) }, + }); const onClick = jest.fn(); const args = { diff --git a/ui/components/multichain/token-list-item/token-list-item.tsx b/ui/components/multichain/token-list-item/token-list-item.tsx index c227a1806245..5c6fb0f56799 100644 --- a/ui/components/multichain/token-list-item/token-list-item.tsx +++ b/ui/components/multichain/token-list-item/token-list-item.tsx @@ -58,10 +58,7 @@ import { import { CURRENCY_SYMBOLS } from '../../../../shared/constants/network'; import { NETWORKS_ROUTE } from '../../../helpers/constants/routes'; -import { setSelectedNetworkConfigurationId } from '../../../store/actions'; -import { ENVIRONMENT_TYPE_FULLSCREEN } from '../../../../shared/constants/app'; -import { getEnvironmentType } from '../../../../app/scripts/lib/util'; -import { getProviderConfig } from '../../../ducks/metamask/metamask'; +import { setEditedNetwork } from '../../../store/actions'; import { getPortfolioUrl } from '../../../helpers/utils/portfolio'; import { PercentageChange } from './price/percentage-change/percentage-change'; @@ -110,9 +107,6 @@ export const TokenListItem = ({ const dispatch = useDispatch(); const [showScamWarningModal, setShowScamWarningModal] = useState(false); - const environmentType = getEnvironmentType(); - const providerConfig = useSelector(getProviderConfig); - const isFullScreen = environmentType === ENVIRONMENT_TYPE_FULLSCREEN; const history = useHistory(); const getTokenTitle = () => { @@ -416,14 +410,8 @@ export const TokenListItem = ({ { - dispatch( - setSelectedNetworkConfigurationId(providerConfig.id), - ); - if (isFullScreen) { - history.push(NETWORKS_ROUTE); - } else { - global.platform.openExtensionInBrowser?.(NETWORKS_ROUTE); - } + dispatch(setEditedNetwork({ chainId })); + history.push(NETWORKS_ROUTE); }} block > diff --git a/ui/components/ui/deprecated-networks/deprecated-networks.js b/ui/components/ui/deprecated-networks/deprecated-networks.js index a67f8258317e..736b5badb6a0 100644 --- a/ui/components/ui/deprecated-networks/deprecated-networks.js +++ b/ui/components/ui/deprecated-networks/deprecated-networks.js @@ -7,21 +7,21 @@ import { Severity, } from '../../../helpers/constants/design-system'; -import { getCurrentNetwork } from '../../../selectors'; +import { + getCurrentNetwork, + getNetworkConfigurationsByChainId, +} from '../../../selectors'; import { getCompletedOnboarding } from '../../../ducks/metamask/metamask'; import { BannerAlert, Box } from '../../component-library'; import { - AURORA_DISPLAY_NAME, CHAIN_IDS, - CURRENCY_SYMBOLS, DEPRECATED_NETWORKS, - NEAR_AURORA_MAINNET_IMAGE_URL, } from '../../../../shared/constants/network'; -import { upsertNetworkConfiguration } from '../../../store/actions'; -import { MetaMetricsNetworkEventSource } from '../../../../shared/constants/metametrics'; +import { updateNetwork } from '../../../store/actions'; export default function DeprecatedNetworks() { - const { id, chainId, rpcUrl } = useSelector(getCurrentNetwork) ?? {}; + const { chainId, rpcUrl } = useSelector(getCurrentNetwork) ?? {}; + const networkConfigurations = useSelector(getNetworkConfigurationsByChainId); const [isClosed, setIsClosed] = useState(false); const completedOnboarding = useSelector(getCompletedOnboarding); const t = useI18nContext(); @@ -58,25 +58,13 @@ export default function DeprecatedNetworks() { actionButtonLabel: t('switchToNetwork', ['mainnet.aurora.dev']), actionButtonOnClick: async () => { setIsClosed(true); - await dispatch( - upsertNetworkConfiguration( - { - id, - chainId: CHAIN_IDS.AURORA, - nickname: AURORA_DISPLAY_NAME, - rpcUrl: 'https://mainnet.aurora.dev', - ticker: CURRENCY_SYMBOLS.ETH, - rpcPrefs: { - imageUrl: NEAR_AURORA_MAINNET_IMAGE_URL, - blockExplorerUrl: 'https://aurorascan.dev', - }, - }, - { - source: MetaMetricsNetworkEventSource.DeprecatedNetworkModal, - setActive: true, - }, - ), - ); + + const networkConfiguration = networkConfigurations[chainId]; + networkConfiguration.rpcEndpoints[ + networkConfiguration.defaultRpcEndpointIndex + ].url = 'https://mainnet.aurora.dev'; + + await dispatch(updateNetwork(networkConfiguration)); }, }; } diff --git a/ui/components/ui/new-network-info/new-network-info.test.js b/ui/components/ui/new-network-info/new-network-info.test.js index 027c0e2721a5..59d98ab358fd 100644 --- a/ui/components/ui/new-network-info/new-network-info.test.js +++ b/ui/components/ui/new-network-info/new-network-info.test.js @@ -108,7 +108,12 @@ describe('NewNetworkInfo', () => { .get('/tokens/0x3?occurrenceFloor=100&includeNativeAssets=false') .reply(200, '{"error":"ChainId 0x3 is not supported"}'); - const store = configureMockStore()(state); + const store = configureMockStore()({ + metamask: { + ...state.metamask, + ...mockNetworkState({ chainId: '0x3', ticker: undefined }), + }, + }); const { container, getByTestId } = renderWithProvider( , store, diff --git a/ui/ducks/app/app.ts b/ui/ducks/app/app.ts index d053b32e4e6e..fd74618b5330 100644 --- a/ui/ducks/app/app.ts +++ b/ui/ducks/app/app.ts @@ -84,9 +84,10 @@ type AppState = { newNetworkAddedName: string; editedNetwork: | { - networkConfigurationId: string; - nickname: string; - editCompleted: boolean; + chainId: string; + nickname?: string; + editCompleted?: boolean; + newNetwork?: boolean; } | undefined; newNetworkAddedConfigurationId: string; diff --git a/ui/ducks/bridge/selectors.test.ts b/ui/ducks/bridge/selectors.test.ts index 0962ea4bcde9..50a5ad4beb33 100644 --- a/ui/ducks/bridge/selectors.test.ts +++ b/ui/ducks/bridge/selectors.test.ts @@ -63,12 +63,22 @@ describe('Bridge selectors', () => { it('uses config from allNetworks if network is in both FEATURED_RPCS and allNetworks', () => { const addedFeaturedNetwork = { ...FEATURED_RPCS[FEATURED_RPCS.length - 1], - id: 'testid', }; + const state = { ...createBridgeMockStore(), metamask: { networkConfigurations: [addedFeaturedNetwork], + ...mockNetworkState( + { chainId: CHAIN_IDS.MAINNET }, + { chainId: CHAIN_IDS.LINEA_MAINNET }, + { + ...FEATURED_RPCS[FEATURED_RPCS.length - 1], + id: 'testid', + blockExplorerUrl: 'https://basescan.org', + rpcUrl: 'https://mainnet.base.org', + }, + ), }, }; const result = getAllBridgeableNetworks(state as never); @@ -81,28 +91,31 @@ describe('Bridge selectors', () => { expect.objectContaining({ chainId: CHAIN_IDS.LINEA_MAINNET }), ); expect(result[2]).toStrictEqual({ - ...addedFeaturedNetwork, - removable: true, - blockExplorerUrl: 'https://basescan.org', + blockExplorerUrls: addedFeaturedNetwork.blockExplorerUrls, + chainId: addedFeaturedNetwork.chainId, + defaultBlockExplorerUrlIndex: + addedFeaturedNetwork.defaultBlockExplorerUrlIndex, + defaultRpcEndpointIndex: addedFeaturedNetwork.defaultRpcEndpointIndex, + name: addedFeaturedNetwork.name, + nativeCurrency: addedFeaturedNetwork.nativeCurrency, + rpcEndpoints: [ + { + networkClientId: 'testid', + ...addedFeaturedNetwork.rpcEndpoints[0], + }, + ], }); expect(result.slice(3)).toStrictEqual(FEATURED_RPCS.slice(0, -1)); }); it('returns network if included in ALLOWED_BRIDGE_CHAIN_IDS', () => { - const addedFeaturedNetwork = { - chainId: '0x11212131241523151', - nickname: 'scroll', - rpcUrl: 'https://a', - ticker: 'ETH', - rpcPrefs: { - blockExplorerUrl: 'https://a', - imageUrl: 'https://a', - }, - }; const state = { ...createBridgeMockStore(), metamask: { - networkConfigurations: [addedFeaturedNetwork], + ...mockNetworkState( + { chainId: CHAIN_IDS.MAINNET }, + { chainId: CHAIN_IDS.LINEA_MAINNET }, + ), }, }; const result = getAllBridgeableNetworks(state as never); diff --git a/ui/ducks/bridge/selectors.ts b/ui/ducks/bridge/selectors.ts index 9a5cdc18151f..9b355bd0df74 100644 --- a/ui/ducks/bridge/selectors.ts +++ b/ui/ducks/bridge/selectors.ts @@ -1,15 +1,15 @@ import { NetworkState } from '@metamask/network-controller'; import { uniqBy } from 'lodash'; -import { getAllNetworks, getIsBridgeEnabled } from '../../selectors'; +import { + getIsBridgeEnabled, + getNetworkConfigurationsByChainId, +} from '../../selectors'; import { ALLOWED_BRIDGE_CHAIN_IDS } from '../../../shared/constants/bridge'; import { BridgeControllerState, BridgeFeatureFlagsKey, } from '../../../app/scripts/controllers/bridge/types'; -import { - FEATURED_RPCS, - RPCDefinition, -} from '../../../shared/constants/network'; +import { FEATURED_RPCS } from '../../../shared/constants/network'; import { createDeepEqualSelector } from '../../selectors/util'; import { getProviderConfig } from '../metamask/metamask'; import { BridgeState } from './bridge'; @@ -26,21 +26,22 @@ export const getFromChain = (state: BridgeAppState) => getProviderConfig(state); export const getToChain = (state: BridgeAppState) => state.bridge.toChain; export const getAllBridgeableNetworks = createDeepEqualSelector( - (state: BridgeAppState) => - // includes networks user has added - getAllNetworks({ - metamask: { networkConfigurations: state.metamask.networkConfigurations }, - }), - (allNetworks): RPCDefinition[] => { - return uniqBy([...allNetworks, ...FEATURED_RPCS], 'chainId').filter( - ({ chainId }) => ALLOWED_BRIDGE_CHAIN_IDS.includes(chainId), + getNetworkConfigurationsByChainId, + (networkConfigurationsByChainId) => { + return uniqBy( + [...Object.values(networkConfigurationsByChainId), ...FEATURED_RPCS], + 'chainId', + ).filter(({ chainId }) => + ALLOWED_BRIDGE_CHAIN_IDS.includes( + chainId as (typeof ALLOWED_BRIDGE_CHAIN_IDS)[number], + ), ); }, ); export const getFromChains = createDeepEqualSelector( getAllBridgeableNetworks, (state: BridgeAppState) => state.metamask.bridgeState?.bridgeFeatureFlags, - (allBridgeableNetworks, bridgeFeatureFlags): RPCDefinition[] => + (allBridgeableNetworks, bridgeFeatureFlags) => allBridgeableNetworks.filter(({ chainId }) => bridgeFeatureFlags[BridgeFeatureFlagsKey.NETWORK_SRC_ALLOWLIST].includes( chainId, @@ -50,7 +51,7 @@ export const getFromChains = createDeepEqualSelector( export const getToChains = createDeepEqualSelector( getAllBridgeableNetworks, (state: BridgeAppState) => state.metamask.bridgeState?.bridgeFeatureFlags, - (allBridgeableNetworks, bridgeFeatureFlags): RPCDefinition[] => + (allBridgeableNetworks, bridgeFeatureFlags) => allBridgeableNetworks.filter(({ chainId }) => bridgeFeatureFlags[BridgeFeatureFlagsKey.NETWORK_DEST_ALLOWLIST].includes( chainId, @@ -65,5 +66,6 @@ export const getIsBridgeTx = createDeepEqualSelector( (fromChain, toChain, isBridgeEnabled: boolean) => isBridgeEnabled && toChain !== null && + fromChain !== undefined && fromChain.chainId !== toChain.chainId, ); diff --git a/ui/ducks/metamask/metamask.js b/ui/ducks/metamask/metamask.js index e3d16b6fa78d..4765a87df61d 100644 --- a/ui/ducks/metamask/metamask.js +++ b/ui/ducks/metamask/metamask.js @@ -1,6 +1,7 @@ import { addHexPrefix, isHexString } from 'ethereumjs-util'; import { createSelector } from 'reselect'; import { mergeGasFeeEstimates } from '@metamask/transaction-controller'; +import { RpcEndpointType } from '@metamask/network-controller'; import { AlertTypes } from '../../../shared/constants/alerts'; import { GasEstimateTypes, @@ -17,15 +18,11 @@ import { getAddressBook, getSelectedNetworkClientId, getSelectedInternalAccount, - getNetworkConfigurations, + getNetworkConfigurationsByChainId, } from '../../selectors'; import * as actionConstants from '../../store/actionConstants'; import { updateTransactionGasFees } from '../../store/actions'; import { setCustomGasLimit, setCustomGasPrice } from '../gas/gas.duck'; -import { - BUILT_IN_INFURA_NETWORKS, - NETWORK_TYPES, -} from '../../../shared/constants/network'; const initialState = { isInitialized: false, @@ -54,6 +51,7 @@ const initialState = { useNativeCurrencyAsPrimaryCurrency: true, petnamesEnabled: true, featureNotificationsEnabled: false, + showMultiRpcModal: false, }, firstTimeFlowType: null, completedOnboarding: false, @@ -281,20 +279,36 @@ export const getAlertEnabledness = (state) => state.metamask.alertEnabledness; * * @param {object} state - Redux state object. */ -export function getProviderConfig(state) { - const networkClientId = getSelectedNetworkClientId(state); - const builtInNetwork = BUILT_IN_INFURA_NETWORKS[networkClientId]; - return builtInNetwork - ? { - ...builtInNetwork, - type: networkClientId, - rpcPrefs: { blockExplorerUrl: builtInNetwork.blockExplorerUrl }, +export const getProviderConfig = createSelector( + (state) => getNetworkConfigurationsByChainId(state), + (state) => getSelectedNetworkClientId(state), + (networkConfigurationsByChainId, selectedNetworkClientId) => { + for (const network of Object.values(networkConfigurationsByChainId)) { + for (const rpcEndpoint of network.rpcEndpoints) { + if (rpcEndpoint.networkClientId === selectedNetworkClientId) { + const blockExplorerUrl = + network.blockExplorerUrls?.[network.defaultBlockExplorerUrlIndex]; + + return { + chainId: network.chainId, + ticker: network.nativeCurrency, + rpcPrefs: { ...(blockExplorerUrl && { blockExplorerUrl }) }, + type: + rpcEndpoint.type === RpcEndpointType.Custom + ? 'rpc' + : rpcEndpoint.networkClientId, + ...(rpcEndpoint.type === RpcEndpointType.Custom && { + id: rpcEndpoint.networkClientId, + nickname: network.name, + rpcUrl: rpcEndpoint.url, + }), + }; + } } - : { - ...getNetworkConfigurations(state)[networkClientId], - type: NETWORK_TYPES.RPC, - }; -} + } + return undefined; // should not be reachable + }, +); export const getUnconnectedAccountAlertEnabledness = (state) => getAlertEnabledness(state)[AlertTypes.unconnectedAccount]; diff --git a/ui/ducks/send/helpers.test.js b/ui/ducks/send/helpers.test.js index 7129ffca8194..74f660991cd7 100644 --- a/ui/ducks/send/helpers.test.js +++ b/ui/ducks/send/helpers.test.js @@ -64,6 +64,7 @@ jest.mock('../metamask/metamask', () => ({ ...jest.requireActual('../metamask/metamask'), getGasFeeEstimates: jest.fn(), getNativeCurrency: jest.fn(), + getProviderConfig: jest.fn(), })); jest.mock('../swaps/swaps', () => ({ diff --git a/ui/ducks/send/send.js b/ui/ducks/send/send.js index 09cc2412d995..cdbe7d2daa86 100644 --- a/ui/ducks/send/send.js +++ b/ui/ducks/send/send.js @@ -3546,7 +3546,7 @@ export const getIsSwapAndSendDisabledForNetwork = createSelector( ); export const getSendAnalyticProperties = createSelector( - getProviderConfig, + (state) => getProviderConfig(state), getCurrentDraftTransaction, getBestQuote, ({ chainId, ticker: nativeCurrencySymbol }, draftTransaction, bestQuote) => { diff --git a/ui/helpers/constants/zendesk-url.js b/ui/helpers/constants/zendesk-url.js index 790f2894fcc7..882d5232a473 100644 --- a/ui/helpers/constants/zendesk-url.js +++ b/ui/helpers/constants/zendesk-url.js @@ -35,7 +35,7 @@ const ZENDESK_URLS = { TOKEN_SAFETY_PRACTICES: 'https://support.metamask.io/managing-my-tokens/token-safety-practices/', UNKNOWN_NETWORK: - 'https://support.metamask.io/nl/networks-and-sidechains/managing-networks/the-risks-of-connecting-to-an-unknown-network/', + 'https://support.metamask.io/networks-and-sidechains/managing-networks/the-risks-of-connecting-to-an-unknown-network/', USER_GUIDE_CUSTOM_NETWORKS: 'https://support.metamask.io/networks-and-sidechains/managing-networks/user-guide-custom-networks-and-sidechains/', USER_GUIDE_DAPPS: diff --git a/ui/helpers/utils/feature-flags.js b/ui/helpers/utils/feature-flags.js deleted file mode 100644 index 1956ee75c4bc..000000000000 --- a/ui/helpers/utils/feature-flags.js +++ /dev/null @@ -1,3 +0,0 @@ -export function getLocalNetworkMenuRedesignFeatureFlag() { - return window.metamaskFeatureFlags?.networkMenuRedesign; -} diff --git a/ui/helpers/utils/notification.util.ts b/ui/helpers/utils/notification.util.ts index b48893ee8e0e..489f1ca2f272 100644 --- a/ui/helpers/utils/notification.util.ts +++ b/ui/helpers/utils/notification.util.ts @@ -381,7 +381,7 @@ export function getRpcUrlByChainId(chainId: HexChainId): string { // If rpc is found, return its URL. Otherwise, return a default URL based on the chainId. if (rpc) { - return rpc.rpcUrl; + return rpc.rpcEndpoints[0].url; } // Fallback RPC URLs based on the chainId switch (chainId) { diff --git a/ui/hooks/ramps/useRamps/useRamps.test.tsx b/ui/hooks/ramps/useRamps/useRamps.test.tsx index 2f1739fd36e8..0b0b8124666b 100644 --- a/ui/hooks/ramps/useRamps/useRamps.test.tsx +++ b/ui/hooks/ramps/useRamps/useRamps.test.tsx @@ -38,6 +38,7 @@ describe('useRamps', () => { ...mockStoreState, metamask: { ...mockStoreState.metamask, + ...mockNetworkState({ chainId: mockChainId }), }, }; @@ -60,6 +61,7 @@ describe('useRamps', () => { ...mockStoreState, metamask: { ...mockStoreState.metamask, + ...mockNetworkState({ chainId: mockChainId }), }, }; diff --git a/ui/hooks/useTokenTracker.js b/ui/hooks/useTokenTracker.js index 93b8fa08214e..0ce2c9cbcac2 100644 --- a/ui/hooks/useTokenTracker.js +++ b/ui/hooks/useTokenTracker.js @@ -1,9 +1,10 @@ import { useState, useEffect, useRef, useCallback } from 'react'; import TokenTracker from '@metamask/eth-token-tracker'; import { shallowEqual, useSelector } from 'react-redux'; -import { getCurrentChainId, getSelectedInternalAccount } from '../selectors'; +import { getSelectedInternalAccount } from '../selectors'; import { SECOND } from '../../shared/constants/time'; import { isEqualCaseInsensitive } from '../../shared/modules/string-utils'; +import { getProviderConfig } from '../ducks/metamask/metamask'; import { useEqualityCheck } from './useEqualityCheck'; export function useTokenTracker({ @@ -12,7 +13,7 @@ export function useTokenTracker({ includeFailedTokens = false, hideZeroBalanceTokens = false, }) { - const chainId = useSelector(getCurrentChainId); + const { chainId, rpcUrl } = useSelector(getProviderConfig); const { address: selectedAddress } = useSelector( getSelectedInternalAccount, shallowEqual, @@ -97,8 +98,9 @@ export function useTokenTracker({ useEffect(() => { // This effect will only run initially and when: // 1. chainId is updated, - // 2. userAddress is changed, - // 3. token list is updated and not equal to previous list + // 2. rpc url is changd, + // 3. userAddress is changed, + // 4. token list is updated and not equal to previous list // in any of these scenarios, we should indicate to the user that their token // values are in the process of updating by setting loading state. setLoading(true); @@ -121,6 +123,7 @@ export function useTokenTracker({ userAddress, teardownTracker, chainId, + rpcUrl, memoizedTokens, updateBalances, buildTracker, diff --git a/ui/index.js b/ui/index.js index 0198e586bfdf..4f98eab94209 100644 --- a/ui/index.js +++ b/ui/index.js @@ -308,12 +308,6 @@ function setupStateHooks(store) { }; } -// Check for local feature flags and represent them so they're avialable -// to the front-end of the app -window.metamaskFeatureFlags = { - networkMenuRedesign: Boolean(process.env.ENABLE_NETWORK_UI_REDESIGN), -}; - window.logStateString = async function (cb) { const state = await window.stateHooks.getCleanAppState(); const logs = window.stateHooks.getLogs(); diff --git a/ui/pages/asset/components/native-asset.tsx b/ui/pages/asset/components/native-asset.tsx index 0ebf3f02c317..cdc38afcd240 100644 --- a/ui/pages/asset/components/native-asset.tsx +++ b/ui/pages/asset/components/native-asset.tsx @@ -2,6 +2,7 @@ import React, { useContext } from 'react'; import { useSelector } from 'react-redux'; import { getAccountLink } from '@metamask/etherscan-link'; import { + getCurrentChainId, getCurrentCurrency, getNativeCurrencyImage, getRpcPrefsForCurrentProvider, @@ -29,7 +30,8 @@ const NativeAsset = () => { const image = useSelector(getNativeCurrencyImage); const showFiat = useSelector(getShouldShowFiat); const currentCurrency = useSelector(getCurrentCurrency); - const { chainId, ticker, type } = useSelector(getProviderConfig); + const chainId = useSelector(getCurrentChainId); + const { ticker, type } = useSelector(getProviderConfig) ?? {}; const { address } = useSelector(getSelectedInternalAccount); const rpcPrefs = useSelector(getRpcPrefsForCurrentProvider); diff --git a/ui/pages/confirm-add-suggested-nft/confirm-add-suggested-nft.test.js b/ui/pages/confirm-add-suggested-nft/confirm-add-suggested-nft.test.js index 003e88f16351..909b2caf76ed 100644 --- a/ui/pages/confirm-add-suggested-nft/confirm-add-suggested-nft.test.js +++ b/ui/pages/confirm-add-suggested-nft/confirm-add-suggested-nft.test.js @@ -74,7 +74,9 @@ const renderComponent = (pendingNfts = {}) => { metamask: { ...mockState.metamask, pendingApprovals: pendingNfts, - ...mockNetworkState({ chainId: CHAIN_IDS.MAINNET }), + ...mockNetworkState({ + chainId: CHAIN_IDS.MAINNET, + }), }, history: { mostRecentOverviewPage: '/', diff --git a/ui/pages/confirmations/components/advanced-gas-controls/advanced-gas-controls.test.js b/ui/pages/confirmations/components/advanced-gas-controls/advanced-gas-controls.test.js index 9e8aff0e7d4c..13e2637ecb96 100644 --- a/ui/pages/confirmations/components/advanced-gas-controls/advanced-gas-controls.test.js +++ b/ui/pages/confirmations/components/advanced-gas-controls/advanced-gas-controls.test.js @@ -1,11 +1,15 @@ import React from 'react'; +import configureMockStore from 'redux-mock-store'; import { renderWithProvider } from '../../../../../test/jest/rendering'; import AdvancedGasControls from './advanced-gas-controls.component'; const renderComponent = (props) => { - return renderWithProvider(); + const store = configureMockStore([])({ + metamask: {}, + }); + return renderWithProvider(, store); }; describe('AdvancedGasControls Component', () => { diff --git a/ui/pages/confirmations/components/advanced-gas-fee-popover/advanced-gas-fee-defaults/advanced-gas-fee-defaults.test.js b/ui/pages/confirmations/components/advanced-gas-fee-popover/advanced-gas-fee-defaults/advanced-gas-fee-defaults.test.js index d6bcb4495950..5e330a8b90c7 100644 --- a/ui/pages/confirmations/components/advanced-gas-fee-popover/advanced-gas-fee-defaults/advanced-gas-fee-defaults.test.js +++ b/ui/pages/confirmations/components/advanced-gas-fee-popover/advanced-gas-fee-defaults/advanced-gas-fee-defaults.test.js @@ -17,10 +17,10 @@ import configureStore from '../../../../../store/store'; import AdvancedGasFeeInputs from '../advanced-gas-fee-inputs'; import { CHAIN_IDS } from '../../../../../../shared/constants/network'; import { getSelectedInternalAccountFromMockState } from '../../../../../../test/jest/mocks'; +import { mockNetworkState } from '../../../../../../test/stub/networks'; import AdvancedGasFeeDefaults from './advanced-gas-fee-defaults'; -const TEXT_SELECTOR = - 'Save these values as my default for the Chain 5 network.'; +const TEXT_SELECTOR = 'Save these values as my default for the Goerli network.'; jest.mock('../../../../../store/actions', () => ({ gasFeeStartPollingByNetworkClientId: jest @@ -43,6 +43,7 @@ const render = async (defaultGasParams, contextParams) => { metamask: { ...mockState.metamask, ...defaultGasParams, + ...mockNetworkState({ chainId: CHAIN_IDS.GOERLI }), accounts: { [mockSelectedInternalAccount.address]: { address: mockSelectedInternalAccount.address, diff --git a/ui/pages/confirmations/components/advanced-gas-inputs/advanced-gas-inputs.test.js b/ui/pages/confirmations/components/advanced-gas-inputs/advanced-gas-inputs.test.js index 92af1a3bd4d8..aecf6fa14912 100644 --- a/ui/pages/confirmations/components/advanced-gas-inputs/advanced-gas-inputs.test.js +++ b/ui/pages/confirmations/components/advanced-gas-inputs/advanced-gas-inputs.test.js @@ -20,7 +20,7 @@ describe('AdvancedGasInputs', () => { minimumGasLimit: 21000, }; - const store = configureStore({}); + const store = configureStore({ metamask: {} }); beforeEach(() => { clock = sinon.useFakeTimers(); diff --git a/ui/pages/confirmations/components/confirm-subtitle/confirm-subtitle.test.js b/ui/pages/confirmations/components/confirm-subtitle/confirm-subtitle.test.js index 3c45b42cc717..baa2d112b671 100644 --- a/ui/pages/confirmations/components/confirm-subtitle/confirm-subtitle.test.js +++ b/ui/pages/confirmations/components/confirm-subtitle/confirm-subtitle.test.js @@ -5,6 +5,7 @@ import mockState from '../../../../../test/data/mock-state.json'; import { renderWithProvider } from '../../../../../test/lib/render-helpers'; import configureStore from '../../../../store/store'; import { getSelectedInternalAccountFromMockState } from '../../../../../test/jest/mocks'; +import { getProviderConfig } from '../../../../ducks/metamask/metamask'; import ConfirmSubTitle from './confirm-subtitle'; const mockSelectedInternalAccount = @@ -50,9 +51,7 @@ describe('ConfirmSubTitle', () => { mockState.metamask.preferences.showFiatInTestnets = false; mockState.metamask.allNftContracts = { [mockSelectedInternalAccount.address]: { - [mockState.metamask.networkConfigurations[ - mockState.metamask.selectedNetworkClientId - ].chainId]: [{ address: '0x9' }], + [getProviderConfig(mockState).chainId]: [{ address: '0x9' }], }, }; store = configureStore(mockState); diff --git a/ui/pages/confirmations/components/confirm/header/__snapshots__/header.test.tsx.snap b/ui/pages/confirmations/components/confirm/header/__snapshots__/header.test.tsx.snap index 5c627c06c972..46bf53c2a7bc 100644 --- a/ui/pages/confirmations/components/confirm/header/__snapshots__/header.test.tsx.snap +++ b/ui/pages/confirmations/components/confirm/header/__snapshots__/header.test.tsx.snap @@ -57,9 +57,9 @@ exports[`Header should match snapshot with signature confirmation 1`] = `
- C + G
- Chain 5 + Goerli

@@ -170,9 +170,9 @@ exports[`Header should match snapshot with transaction confirmation 1`] = `
- C + G
- Chain 5 + Goerli

diff --git a/ui/pages/confirmations/components/confirm/header/header.test.tsx b/ui/pages/confirmations/components/confirm/header/header.test.tsx index 508089d7ed8d..c6b8481c01fc 100644 --- a/ui/pages/confirmations/components/confirm/header/header.test.tsx +++ b/ui/pages/confirmations/components/confirm/header/header.test.tsx @@ -31,7 +31,7 @@ describe('Header', () => { it('contains network name and account name', () => { const { getByText } = render(); expect(getByText('Test Account')).toBeInTheDocument(); - expect(getByText('Chain 5')).toBeInTheDocument(); + expect(getByText('Goerli')).toBeInTheDocument(); }); it('contains account info icon', async () => { diff --git a/ui/pages/confirmations/components/confirm/network-change-toast/network-change-toast-legacy.tsx b/ui/pages/confirmations/components/confirm/network-change-toast/network-change-toast-legacy.tsx index 3a6f6d2f88d2..fca355a9b063 100644 --- a/ui/pages/confirmations/components/confirm/network-change-toast/network-change-toast-legacy.tsx +++ b/ui/pages/confirmations/components/confirm/network-change-toast/network-change-toast-legacy.tsx @@ -1,13 +1,17 @@ import React, { useCallback, useEffect, useState } from 'react'; import { useSelector } from 'react-redux'; +import { Hex } from '@metamask/utils'; import { Box } from '../../../../../components/component-library'; import { Toast } from '../../../../../components/multichain'; import { getLastInteractedConfirmationInfo, setLastInteractedConfirmationInfo, } from '../../../../../store/actions'; -import { getCurrentChainId, getNetworkDetails } from '../../../../../selectors'; +import { + getCurrentChainId, + getNetworkConfigurationsByChainId, +} from '../../../../../selectors'; import { useI18nContext } from '../../../../../hooks/useI18nContext'; const CHAIN_CHANGE_THRESHOLD_MILLISECONDS = 60 * 1000; // 1 Minute @@ -22,9 +26,8 @@ const NetworkChangeToastLegacy = ({ const newChainId = confirmation?.chainId ?? chainId; const [toastVisible, setToastVisible] = useState(false); const t = useI18nContext(); - const networkInfo = useSelector((state) => - getNetworkDetails(state, newChainId), - ); + const networkConfigurations = useSelector(getNetworkConfigurationsByChainId); + const network = networkConfigurations[newChainId as Hex]; const hideToast = useCallback(() => { setToastVisible(false); @@ -78,7 +81,7 @@ const NetworkChangeToastLegacy = ({ diff --git a/ui/pages/confirmations/components/security-provider-banner-alert/blockaid-banner-alert/blockaid-banner-alert.test.js b/ui/pages/confirmations/components/security-provider-banner-alert/blockaid-banner-alert/blockaid-banner-alert.test.js index 203b930673e4..f0f9ff15bf92 100644 --- a/ui/pages/confirmations/components/security-provider-banner-alert/blockaid-banner-alert/blockaid-banner-alert.test.js +++ b/ui/pages/confirmations/components/security-provider-banner-alert/blockaid-banner-alert/blockaid-banner-alert.test.js @@ -6,6 +6,7 @@ import BlockaidPackage from '@blockaid/ppom_release/package.json'; import { renderWithProvider } from '../../../../../../test/lib/render-helpers'; import { Severity } from '../../../../../helpers/constants/design-system'; import configureStore from '../../../../../store/store'; +import { mockNetworkState } from '../../../../../../test/stub/networks'; import { BlockaidReason, @@ -42,6 +43,11 @@ const mockSecurityAlertResponse = { }; describe('Blockaid Banner Alert', () => { + const mockStore = { + metamask: { + ...mockNetworkState({}), + }, + }; it('should not render when securityAlertResponse is not present', () => { const { container } = renderWithProvider( { securityAlertResponse: undefined, }} />, - configureStore(), + configureStore(mockStore), ); expect(container.querySelector('.mm-banner-alert')).toBeNull(); @@ -65,7 +71,7 @@ describe('Blockaid Banner Alert', () => { }, }} />, - configureStore(), + configureStore(mockStore), ); expect(container.querySelector('.mm-banner-alert')).toBeNull(); @@ -81,7 +87,7 @@ describe('Blockaid Banner Alert', () => { }, }} />, - configureStore(), + configureStore(mockStore), ); const warningBannerAlert = container.querySelector( '.mm-banner-alert--severity-warning', @@ -98,7 +104,7 @@ describe('Blockaid Banner Alert', () => { securityAlertResponse: mockSecurityAlertResponse, }} />, - configureStore(), + configureStore(mockStore), ); const warningBannerAlert = container.querySelector( '.mm-banner-alert--severity-warning', @@ -118,7 +124,7 @@ describe('Blockaid Banner Alert', () => { }, }} />, - configureStore(), + configureStore(mockStore), ); const dangerBannerAlert = container.querySelector( '.mm-banner-alert--severity-danger', @@ -138,7 +144,7 @@ describe('Blockaid Banner Alert', () => { }, }} />, - configureStore(), + configureStore(mockStore), ); expect(getByText('This is a deceptive request')).toBeInTheDocument(); @@ -154,7 +160,7 @@ describe('Blockaid Banner Alert', () => { }, }} />, - configureStore(), + configureStore(mockStore), ); expect(getByText('Be careful')).toBeInTheDocument(); @@ -171,7 +177,7 @@ describe('Blockaid Banner Alert', () => { }, }} />, - configureStore(), + configureStore(mockStore), ); expect(getByText('This is a suspicious request')).toBeInTheDocument(); @@ -192,7 +198,7 @@ describe('Blockaid Banner Alert', () => { }, }} />, - configureStore(), + configureStore(mockStore), ); expect(container).toMatchSnapshot(); @@ -210,7 +216,7 @@ describe('Blockaid Banner Alert', () => { features: undefined, }} />, - configureStore(), + configureStore(mockStore), ); expect(container).toMatchSnapshot(); @@ -225,7 +231,7 @@ describe('Blockaid Banner Alert', () => { features: undefined, }} />, - configureStore(), + configureStore(mockStore), ); expect(container).toMatchSnapshot(); @@ -243,7 +249,7 @@ describe('Blockaid Banner Alert', () => { features: undefined, }} />, - configureStore(), + configureStore(mockStore), ); const elm = getByRole('link', { name: 'Report an issue' }); @@ -264,7 +270,7 @@ describe('Blockaid Banner Alert', () => { }, }} />, - configureStore(), + configureStore(mockStore), ); const elm = getByRole('link', { name: 'Report an issue' }); @@ -315,7 +321,7 @@ describe('Blockaid Banner Alert', () => { }, }} />, - configureStore(), + configureStore(mockStore), ); expect(getByText(expectedDescription)).toBeInTheDocument(); @@ -337,7 +343,7 @@ describe('Blockaid Banner Alert', () => { }, }} />, - configureStore(), + configureStore(mockStore), ); expect(getByText(stubOtherDescription)).toBeInTheDocument(); @@ -362,7 +368,7 @@ describe('Blockaid Banner Alert', () => { }, }} />, - configureStore(), + configureStore(mockStore), ); fireEvent.click(screen.queryByText('See details')); diff --git a/ui/pages/confirmations/components/signature-request-header/__snapshots__/signature-request-header.test.js.snap b/ui/pages/confirmations/components/signature-request-header/__snapshots__/signature-request-header.test.js.snap index ef1797f6e8b5..3baa846043ed 100644 --- a/ui/pages/confirmations/components/signature-request-header/__snapshots__/signature-request-header.test.js.snap +++ b/ui/pages/confirmations/components/signature-request-header/__snapshots__/signature-request-header.test.js.snap @@ -62,7 +62,7 @@ exports[`SignatureRequestHeader should match snapshot 1`] = ` - C + G @@ -73,7 +73,7 @@ exports[`SignatureRequestHeader should match snapshot 1`] = ` class="mm-box mm-text mm-text--body-sm mm-box--color-text-alternative" data-testid="signature-request-network-display" > - Chain 5 + Goerli
- C + G @@ -149,7 +149,7 @@ exports[`SignatureRequestOriginal should match snapshot 1`] = ` class="mm-box mm-text mm-text--body-sm mm-box--color-text-alternative" data-testid="signature-request-network-display" > - Chain 5 + Goerli
- C + G @@ -146,7 +146,7 @@ exports[`SignatureRequestSIWE (Sign in with Ethereum) should match snapshot 1`] class="mm-box mm-text mm-text--body-sm mm-box--color-text-alternative" data-testid="signature-request-network-display" > - Chain 5 + Goerli
- C + G - Chain 5 + Goerli - C + G - Chain 5 + Goerli (selector) => { return {}; case pendingApprovalsSortedSelector: return Object.values(opts.metamask.pendingApprovals); + case getNetworkConfigurationsByChainId: + return opts.metamask.networkConfigurationsByChainId; default: return undefined; } diff --git a/ui/pages/confirmations/confirm-send-ether/__snapshots__/confirm-send-ether.test.js.snap b/ui/pages/confirmations/confirm-send-ether/__snapshots__/confirm-send-ether.test.js.snap index 1da44ad87146..2bbceca19ec8 100644 --- a/ui/pages/confirmations/confirm-send-ether/__snapshots__/confirm-send-ether.test.js.snap +++ b/ui/pages/confirmations/confirm-send-ether/__snapshots__/confirm-send-ether.test.js.snap @@ -85,13 +85,13 @@ exports[`ConfirmSendEther should render correct information for for confirm send
- C + G
- Chain 5 + Goerli - C + G
Chain 5 logo
@@ -49,7 +48,7 @@ exports[`Confirm matches snapshot for signature - personal sign type 1`] = ` class="mm-box mm-text mm-text--body-md mm-box--color-text-alternative" data-testid="header-network-display-name" > - Chain 5 + Goerli

@@ -261,9 +260,9 @@ exports[`Confirm should match snapshot for signature - typed sign - V4 - PermitB
- C + G
- Chain 5 + Goerli

@@ -1307,9 +1306,9 @@ exports[`Confirm should match snapshot for signature - typed sign - V4 - PermitS
- C + G
- Chain 5 + Goerli

@@ -2069,12 +2068,11 @@ exports[`Confirm should match snapshot for signature - typed sign - V4 1`] = ` />
Chain 5 logo
@@ -2091,7 +2089,7 @@ exports[`Confirm should match snapshot for signature - typed sign - V4 1`] = ` class="mm-box mm-text mm-text--body-md mm-box--color-text-alternative" data-testid="header-network-display-name" > - Chain 5 + Goerli

@@ -2753,9 +2751,9 @@ exports[`Confirm should match snapshot for signature - typed sign - permit 1`] =
- C + G
- Chain 5 + Goerli

@@ -3503,9 +3501,9 @@ exports[`Confirm should match snapshot signature - typed sign - order 1`] = `
- C + G
- Chain 5 + Goerli

diff --git a/ui/pages/confirmations/confirmation/components/confirmation-network-switch/confirmation-network-switch.js b/ui/pages/confirmations/confirmation/components/confirmation-network-switch/confirmation-network-switch.js index 0d8b9936ab11..6413337216be 100644 --- a/ui/pages/confirmations/confirmation/components/confirmation-network-switch/confirmation-network-switch.js +++ b/ui/pages/confirmations/confirmation/components/confirmation-network-switch/confirmation-network-switch.js @@ -20,7 +20,7 @@ import { const getNetworkDetails = (network) => { return { ...network, - nickname: network.nickname ?? NETWORK_TO_NAME_MAP[network.chainId], + name: network.name ?? NETWORK_TO_NAME_MAP[network.chainId], iconUrl: network.iconUrl ?? CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP[network.chainId], }; @@ -44,7 +44,7 @@ export default function ConfirmationNetworkSwitch({ toNetwork, fromNetwork }) { > @@ -53,7 +53,7 @@ export default function ConfirmationNetworkSwitch({ toNetwork, fromNetwork }) { justifyContent={JustifyContent.center} data-testid="network-switch-from-network" > - {fromNetworkDetails.nickname} + {fromNetworkDetails.name} @@ -80,7 +80,7 @@ export default function ConfirmationNetworkSwitch({ toNetwork, fromNetwork }) { justifyContent={JustifyContent.center} data-testid="network-switch-to-network" > - {toNetworkDetails.nickname} + {toNetworkDetails.name} @@ -90,12 +90,10 @@ export default function ConfirmationNetworkSwitch({ toNetwork, fromNetwork }) { ConfirmationNetworkSwitch.propTypes = { toNetwork: PropTypes.shape({ chainId: PropTypes.string.isRequired, - nickname: PropTypes.string.isRequired, - type: PropTypes.string, + name: PropTypes.string.isRequired, }), fromNetwork: PropTypes.shape({ chainId: PropTypes.string.isRequired, - nickname: PropTypes.string.isRequired, - type: PropTypes.string, + name: PropTypes.string.isRequired, }), }; diff --git a/ui/pages/confirmations/confirmation/components/confirmation-network-switch/confirmation-network-switch.stories.js b/ui/pages/confirmations/confirmation/components/confirmation-network-switch/confirmation-network-switch.stories.js index 372b715f7fb5..a3963055f860 100644 --- a/ui/pages/confirmations/confirmation/components/confirmation-network-switch/confirmation-network-switch.stories.js +++ b/ui/pages/confirmations/confirmation/components/confirmation-network-switch/confirmation-network-switch.stories.js @@ -15,13 +15,13 @@ export default { toNetwork: { chainId: '0xa', ticker: 'OP', - nickname: 'Optimism', + name: 'Optimism', rpcUrl: 'https://optimism-mainnet.infura.io', }, fromNetwork: { chainId: '1', ticker: 'ETH', - nickname: 'Ethereum Mainnet', + name: 'Ethereum Mainnet', }, }, }; diff --git a/ui/pages/confirmations/confirmation/confirmation.js b/ui/pages/confirmations/confirmation/confirmation.js index 90a4e5fb4ed6..b37743c256bc 100644 --- a/ui/pages/confirmations/confirmation/confirmation.js +++ b/ui/pages/confirmations/confirmation/confirmation.js @@ -31,6 +31,7 @@ import { getTotalUnapprovedCount, useSafeChainsListValidationSelector, getSnapsMetadata, + getNetworkConfigurationsByChainId, getHideSnapBranding, } from '../../../selectors'; import NetworkDisplay from '../../../components/app/network-display/network-display'; @@ -211,6 +212,9 @@ export default function ConfirmationPage({ const useSafeChainsListValidation = useSelector( useSafeChainsListValidationSelector, ); + const networkConfigurationsByChainId = useSelector( + getNetworkConfigurationsByChainId, + ); const [approvalFlowLoadingText, setApprovalFlowLoadingText] = useState(null); const [currentPendingConfirmation, setCurrentPendingConfirmation] = @@ -299,6 +303,10 @@ export default function ConfirmationPage({ { matchedChain, currencySymbolWarning, + existingNetworkConfiguration: + networkConfigurationsByChainId?.[ + pendingConfirmation.requestData?.chainId + ], }, // Passing `t` in the contexts object is a bit redundant but since it's a // context too, it makes sense (for completeness) @@ -315,6 +323,7 @@ export default function ConfirmationPage({ trackEvent, isSnapDialog, snapName, + networkConfigurationsByChainId, ]); useEffect(() => { diff --git a/ui/pages/confirmations/confirmation/stories/util.js b/ui/pages/confirmations/confirmation/stories/util.js index a5d8d1ba136b..bb0a661ae470 100644 --- a/ui/pages/confirmations/confirmation/stories/util.js +++ b/ui/pages/confirmations/confirmation/stories/util.js @@ -4,6 +4,7 @@ import { NetworkStatus } from '@metamask/network-controller'; import configureStore from '../../../../store/store'; import testData from '../../../../../.storybook/test-data'; import { Box } from '../../../../components/component-library'; +import { mockNetworkState } from '../../../../../test/stub/networks'; const STORE_MOCK = { ...testData, @@ -22,6 +23,18 @@ const STORE_MOCK = { status: NetworkStatus.Available, }, }, + ...mockNetworkState({ + id: 'testNetworkClientId', + rpcUrl: 'https://testrpc.com', + chainId: '0x1', + nickname: 'mainnet', + name: 'mainnet', + blockExplorerUrl: 'https://etherscan.io', + metadata: { + EIPS: { 1559: true }, + status: NetworkStatus.Available, + }, + }), pendingApprovals: { testId: { id: 'testId', diff --git a/ui/pages/confirmations/confirmation/templates/__snapshots__/add-ethereum-chain.test.js.snap b/ui/pages/confirmations/confirmation/templates/__snapshots__/add-ethereum-chain.test.js.snap index 003ef05a9469..38bdc74279f8 100644 --- a/ui/pages/confirmations/confirmation/templates/__snapshots__/add-ethereum-chain.test.js.snap +++ b/ui/pages/confirmations/confirmation/templates/__snapshots__/add-ethereum-chain.test.js.snap @@ -35,12 +35,12 @@ exports[`add-ethereum-chain confirmation should match snapshot 1`] = `

- Allow this site to add a network? + Update Test initial state

- This will allow this network to be used within MetaMask. + This site is requesting to update your default network URL. You can edit defaults and network information any time.
- ETH + GoerliETH @@ -363,7 +363,7 @@ exports[`remove-snap-account confirmation should match snapshot 1`] = `
- E + G
- ETH + GoerliETH
diff --git a/ui/pages/confirmations/confirmation/templates/add-ethereum-chain.js b/ui/pages/confirmations/confirmation/templates/add-ethereum-chain.js index 656d016f0a34..d14048897e39 100644 --- a/ui/pages/confirmations/confirmation/templates/add-ethereum-chain.js +++ b/ui/pages/confirmations/confirmation/templates/add-ethereum-chain.js @@ -1,5 +1,6 @@ import { ethErrors } from 'eth-rpc-errors'; import React from 'react'; +import { RpcEndpointType } from '@metamask/network-controller'; import { infuraProjectId, @@ -224,9 +225,31 @@ function getState(pendingApproval) { function getValues(pendingApproval, t, actions, history, data) { const originIsMetaMask = pendingApproval.origin === 'metamask'; const customRpcUrl = pendingApproval.requestData.rpcUrl; - const childrenTitleText = process.env.CHAIN_PERMISSIONS - ? t('addNetworkConfirmationTitle', [pendingApproval.requestData.chainName]) - : t('addEthereumChainConfirmationTitle'); + + let title; + if (originIsMetaMask) { + title = t('wantToAddThisNetwork'); + } else if (data.existingNetworkConfiguration) { + title = t('updateNetworkConfirmationTitle', [ + data.existingNetworkConfiguration.name, + ]); + } else { + title = process.env.CHAIN_PERMISSIONS + ? t('addNetworkConfirmationTitle', [ + pendingApproval.requestData.chainName, + ]) + : t('addEthereumChainConfirmationTitle'); + } + + let subtitle; + if (data.existingNetworkConfiguration) { + subtitle = t('updateEthereumChainConfirmationDescription'); + } else { + subtitle = process.env.CHAIN_PERMISSIONS + ? t('multichainAddEthereumChainConfirmationDescription') + : t('addEthereumChainConfirmationDescription'); + } + return { content: [ { @@ -332,9 +355,7 @@ function getValues(pendingApproval, t, actions, history, data) { { element: 'Typography', key: 'title', - children: originIsMetaMask - ? t('wantToAddThisNetwork') - : childrenTitleText, + children: title, props: { variant: TypographyVariant.H3, align: 'center', @@ -347,9 +368,7 @@ function getValues(pendingApproval, t, actions, history, data) { { element: 'Typography', key: 'description', - children: process.env.CHAIN_PERMISSIONS - ? t('multichainAddEthereumChainConfirmationDescription') - : t('addEthereumChainConfirmationDescription'), + children: subtitle, props: { variant: TypographyVariant.H7, align: 'center', @@ -515,18 +534,26 @@ function getValues(pendingApproval, t, actions, history, data) { pendingApproval.requestData, ); if (originIsMetaMask) { - const networkConfigurationId = await actions.upsertNetworkConfiguration( - { - ...pendingApproval.requestData, - nickname: pendingApproval.requestData.chainName, - }, - { - setActive: false, - source: pendingApproval.requestData.source, - }, - ); + const blockExplorer = + pendingApproval.requestData.rpcPrefs.blockExplorerUrl; + + const addedNetwork = await actions.addNetwork({ + chainId: pendingApproval.requestData.chainId, + name: pendingApproval.requestData.chainName, + nativeCurrency: pendingApproval.requestData.ticker, + blockExplorerUrls: blockExplorer ? [blockExplorer] : [], + defaultBlockExplorerUrlIndex: blockExplorer ? 0 : undefined, + defaultRpcEndpointIndex: 0, + rpcEndpoints: [ + { + url: pendingApproval.requestData.rpcUrl, + type: RpcEndpointType.Custom, + }, + ], + }); + await actions.setNewNetworkAdded({ - networkConfigurationId, + networkConfigurationId: addedNetwork.rpcEndpoints[0].networkClientId, nickname: pendingApproval.requestData.chainName, }); diff --git a/ui/pages/confirmations/confirmation/templates/index.js b/ui/pages/confirmations/confirmation/templates/index.js index 31b8d2210d0d..fd6df7e4959b 100644 --- a/ui/pages/confirmations/confirmation/templates/index.js +++ b/ui/pages/confirmations/confirmation/templates/index.js @@ -5,7 +5,7 @@ import { rejectPendingApproval, resolvePendingApproval, setNewNetworkAdded, - upsertNetworkConfiguration, + addNetwork, } from '../../../../store/actions'; import { ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps) @@ -152,8 +152,7 @@ function getAttenuatedDispatch(dispatch) { dispatch(rejectPendingApproval(...args)), resolvePendingApproval: (...args) => dispatch(resolvePendingApproval(...args)), - upsertNetworkConfiguration: (...args) => - dispatch(upsertNetworkConfiguration(...args)), + addNetwork: (...args) => dispatch(addNetwork(...args)), setNewNetworkAdded: (...args) => dispatch(setNewNetworkAdded(...args)), deleteInterface: (...args) => dispatch(deleteInterface(...args)), }; diff --git a/ui/pages/confirmations/confirmation/templates/remove-snap-account.test.js b/ui/pages/confirmations/confirmation/templates/remove-snap-account.test.js index 2d58ca66d128..9cb73c38bfea 100644 --- a/ui/pages/confirmations/confirmation/templates/remove-snap-account.test.js +++ b/ui/pages/confirmations/confirmation/templates/remove-snap-account.test.js @@ -7,6 +7,8 @@ import Confirmation from '../confirmation'; import { renderWithProvider } from '../../../../../test/lib/render-helpers'; import { SNAP_MANAGE_ACCOUNTS_CONFIRMATION_TYPES } from '../../../../../shared/constants/app'; import mockState from '../../../../../test/data/mock-state.json'; +import { mockNetworkState } from '../../../../../test/stub/networks'; +import { CHAIN_IDS } from '../../../../../shared/constants/network'; const middleware = [thunk]; @@ -39,6 +41,7 @@ const mockBaseStore = { }, approvalFlows: [], subjectMetadata: {}, + ...mockNetworkState({ chainId: CHAIN_IDS.GOERLI }), }, }; diff --git a/ui/pages/confirmations/confirmation/templates/switch-ethereum-chain.test.js b/ui/pages/confirmations/confirmation/templates/switch-ethereum-chain.test.js index e53a8e7de190..4abcf700196f 100644 --- a/ui/pages/confirmations/confirmation/templates/switch-ethereum-chain.test.js +++ b/ui/pages/confirmations/confirmation/templates/switch-ethereum-chain.test.js @@ -19,20 +19,12 @@ const mockApproval = { origin: 'https://test-dapp.metamask.io', requestData: { toNetworkConfiguration: { - rpcUrl: 'https://rpcurl.test.chain', - rpcPrefs: { - blockExplorerUrl: 'https://blockexplorer.test.chain', - }, - chainName: 'Test chain', - ticker: 'TST', chainId: '0x9999', - nickname: 'Test chain', + name: 'Test chain', }, fromNetworkConfiguration: { - type: 'rpc', - rpcUrl: 'http://example-custom-rpc.metamask.io', chainId: '0x9999', - nickname: 'Test initial state', + name: 'Test initial state', }, }, }; diff --git a/ui/pages/confirmations/hooks/alerts/useConfirmationOriginAlerts.test.ts b/ui/pages/confirmations/hooks/alerts/useConfirmationOriginAlerts.test.ts index ade12ca00328..ff2cd10cfe60 100644 --- a/ui/pages/confirmations/hooks/alerts/useConfirmationOriginAlerts.test.ts +++ b/ui/pages/confirmations/hooks/alerts/useConfirmationOriginAlerts.test.ts @@ -9,6 +9,7 @@ import { genUnapprovedContractInteractionConfirmation } from '../../../../../tes import { renderHookWithConfirmContextProvider } from '../../../../../test/lib/confirmations/render-helpers'; import { unapprovedPersonalSignMsg } from '../../../../../test/data/confirmations/personal_sign'; import { SignatureRequestType } from '../../types/confirm'; +import { CHAIN_IDS } from '../../../../../shared/constants/network'; import useConfirmationOriginAlerts from './useConfirmationOriginAlerts'; const expectedAlert = [ @@ -51,7 +52,7 @@ describe('useConfirmationOriginAlerts', () => { it('returns an alert for transaction with special characters in origin', () => { const contractInteraction = genUnapprovedContractInteractionConfirmation({ - chainId: mockState.metamask.networkConfigurations.goerli.chainId, + chainId: CHAIN_IDS.GOERLI, }); const { result } = renderHookWithConfirmContextProvider( () => useConfirmationOriginAlerts(), diff --git a/ui/pages/confirmations/hooks/useConfirmationNetworkInfo.ts b/ui/pages/confirmations/hooks/useConfirmationNetworkInfo.ts index 1fab535733a1..4c6dddfa7a5a 100644 --- a/ui/pages/confirmations/hooks/useConfirmationNetworkInfo.ts +++ b/ui/pages/confirmations/hooks/useConfirmationNetworkInfo.ts @@ -1,52 +1,48 @@ import { useSelector } from 'react-redux'; +import { Hex } from '@metamask/utils'; import { + CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP, NETWORK_TO_NAME_MAP, - NETWORK_TYPES, } from '../../../../shared/constants/network'; -import { getAllNetworks } from '../../../selectors'; -import { getProviderConfig } from '../../../ducks/metamask/metamask'; +import { + getCurrentChainId, + getNetworkConfigurationsByChainId, +} from '../../../selectors'; import { useI18nContext } from '../../../hooks/useI18nContext'; import { useConfirmContext } from '../context/confirm'; -type KeyOfNetworkName = keyof typeof NETWORK_TO_NAME_MAP; - function useConfirmationNetworkInfo() { const t = useI18nContext(); const { currentConfirmation } = useConfirmContext(); - const allNetworks = useSelector(getAllNetworks); - const providerConfig = useSelector(getProviderConfig); + const networkConfigurations = useSelector(getNetworkConfigurationsByChainId); + const currentChainId = useSelector(getCurrentChainId); let networkDisplayName = ''; - let confirmationNetwork; + let networkImageUrl = ''; if (currentConfirmation) { // use the current confirmation chainId, else use the current network chainId - const currentChainId = - currentConfirmation?.chainId ?? providerConfig.chainId; - confirmationNetwork = allNetworks.find( - ({ id, chainId }) => - chainId === currentChainId && - (providerConfig.type === NETWORK_TYPES.RPC - ? id === providerConfig.id - : id === providerConfig.type), - ); - - if (confirmationNetwork) { - const { nickname } = confirmationNetwork; - if (providerConfig.type === NETWORK_TYPES.RPC) { - networkDisplayName = nickname ?? t('privateNetwork'); - } else { - networkDisplayName = - NETWORK_TO_NAME_MAP[confirmationNetwork?.chainId as KeyOfNetworkName]; - } - } + const chainId = + (currentConfirmation?.chainId as Hex | undefined) ?? currentChainId; + + const networkConfiguration = networkConfigurations[chainId]; + + networkDisplayName = + networkConfiguration?.name ?? + NETWORK_TO_NAME_MAP[chainId as keyof typeof NETWORK_TO_NAME_MAP] ?? + t('privateNetwork'); + + networkImageUrl = + CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP[ + chainId as keyof typeof CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP + ]; } return { - networkImageUrl: confirmationNetwork?.rpcPrefs?.imageUrl ?? '', + networkImageUrl, networkDisplayName, }; } diff --git a/ui/pages/confirmations/hooks/useTransactionInfo.test.js b/ui/pages/confirmations/hooks/useTransactionInfo.test.js index 59aaf7b167c3..61ae036c4000 100644 --- a/ui/pages/confirmations/hooks/useTransactionInfo.test.js +++ b/ui/pages/confirmations/hooks/useTransactionInfo.test.js @@ -1,6 +1,7 @@ import { renderHookWithProvider } from '../../../../test/lib/render-helpers'; import mockState from '../../../../test/data/mock-state.json'; import { getSelectedInternalAccountFromMockState } from '../../../../test/jest/mocks'; +import { getCurrentChainId } from '../../../selectors'; import { useTransactionInfo } from './useTransactionInfo'; const mockSelectedInternalAccount = @@ -21,9 +22,7 @@ describe('useTransactionInfo', () => { it('should return true if transaction is NFT transfer', () => { mockState.metamask.allNftContracts = { [mockSelectedInternalAccount.address]: { - [mockState.metamask.networkConfigurations[ - mockState.metamask.selectedNetworkClientId - ].chainId]: [{ address: '0x9' }], + [getCurrentChainId(mockState)]: [{ address: '0x9' }], }, }; diff --git a/ui/pages/home/home.component.js b/ui/pages/home/home.component.js index 19b76a881c6c..2df3f2907266 100644 --- a/ui/pages/home/home.component.js +++ b/ui/pages/home/home.component.js @@ -50,6 +50,9 @@ import { ModalHeader, ModalOverlay, } from '../../components/component-library'; +///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask) +import MultiRpcEditModal from '../../components/app/multi-rpc-edit-modal/multi-rpc-edit-modal'; +///: END:ONLY_INCLUDE_IF import { RESTORE_VAULT_ROUTE, CONFIRM_TRANSACTION_ROUTE, @@ -154,6 +157,7 @@ export default class Home extends PureComponent { announcementsToShow: PropTypes.bool.isRequired, onboardedInThisUISession: PropTypes.bool, isSmartTransactionsOptInModalAvailable: PropTypes.bool.isRequired, + showMultiRpcModal: PropTypes.bool.isRequired, ///: END:ONLY_INCLUDE_IF newNetworkAddedConfigurationId: PropTypes.string, isNotification: PropTypes.bool.isRequired, @@ -177,7 +181,6 @@ export default class Home extends PureComponent { pendingConfirmations: PropTypes.arrayOf(PropTypes.object).isRequired, pendingConfirmationsPrioritized: PropTypes.arrayOf(PropTypes.object) .isRequired, - networkMenuRedesign: PropTypes.bool, hasApprovalFlows: PropTypes.bool.isRequired, infuraBlocked: PropTypes.bool.isRequired, setRecoveryPhraseReminderHasBeenShown: PropTypes.func.isRequired, @@ -374,7 +377,6 @@ export default class Home extends PureComponent { closeNotificationPopup, isNotification, hasAllowedPopupRedirectApprovals, - networkMenuRedesign, newNetworkAddedConfigurationId, setActiveNetwork, clearNewNetworkAdded, @@ -392,8 +394,8 @@ export default class Home extends PureComponent { const { notificationClosing } = this.state; if ( - prevNewNetworkAddedConfigurationId !== newNetworkAddedConfigurationId && - networkMenuRedesign + newNetworkAddedConfigurationId && + prevNewNetworkAddedConfigurationId !== newNetworkAddedConfigurationId ) { setActiveNetwork(newNetworkAddedConfigurationId); clearNewNetworkAdded(); @@ -498,11 +500,8 @@ export default class Home extends PureComponent { newTokensImportedError, setNewTokensImported, setNewTokensImportedError, - newNetworkAddedConfigurationId, - networkMenuRedesign, clearNewNetworkAdded, clearEditedNetwork, - setActiveNetwork, } = this.props; const onAutoHide = () => { @@ -627,7 +626,9 @@ export default class Home extends PureComponent { - {t('newNetworkEdited', [editedNetwork.nickname])} + {editedNetwork.newNetwork + ? t('newNetworkAdded', [editedNetwork.nickname]) + : t('newNetworkEdited', [editedNetwork.nickname])} ) : null} - {newNetworkAddedConfigurationId && !networkMenuRedesign && ( - clearNewNetworkAdded()} - > - - - {t('networkAddedSuccessfully')} - - - - - - - )} ); } @@ -986,6 +941,7 @@ export default class Home extends PureComponent { firstTimeFlowType, newNetworkAddedConfigurationId, isSmartTransactionsOptInModalAvailable, + showMultiRpcModal, ///: END:ONLY_INCLUDE_IF } = this.props; @@ -1012,6 +968,12 @@ export default class Home extends PureComponent { showWhatsNewPopup && !showSmartTransactionsOptInModal; + const showMultiRpcEditModal = + canSeeModals && + showMultiRpcModal && + !showSmartTransactionsOptInModal && + !showWhatsNew; + const showTermsOfUse = completedOnboarding && !onboardedInThisUISession && showTermsOfUsePopup; ///: END:ONLY_INCLUDE_IF @@ -1036,6 +998,8 @@ export default class Home extends PureComponent { isOpen={showSmartTransactionsOptInModal} hideWhatsNewPopup={hideWhatsNewPopup} /> + + {showMultiRpcEditModal && } {showWhatsNew ? : null} {!showWhatsNew && showRecoveryPhraseReminder ? ( { @@ -118,7 +117,6 @@ const mapStateToProps = (state) => { const pendingConfirmations = getUnapprovedTemplatedConfirmations(state); const pendingConfirmationsPrioritized = getPrioritizedUnapprovedTemplatedConfirmations(state); - const networkMenuRedesign = getLocalNetworkMenuRedesignFeatureFlag(state); ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) const institutionalConnectRequests = getInstitutionalConnectRequests(state); @@ -195,7 +193,6 @@ const mapStateToProps = (state) => { shouldShowWeb3ShimUsageNotification, pendingConfirmations, pendingConfirmationsPrioritized, - networkMenuRedesign, infuraBlocked: getInfuraBlocked(state), announcementsToShow: getSortedAnnouncementsToShow(state).length > 0, showWhatsNewPopup, @@ -226,6 +223,7 @@ const mapStateToProps = (state) => { ///: END:ONLY_INCLUDE_IF isSmartTransactionsOptInModalAvailable: getIsSmartTransactionsOptInModalAvailable(state), + showMultiRpcModal: state.metamask.preferences.showMultiRpcModal, }; }; diff --git a/ui/pages/institutional/institutional-entity-done-page/institutional-entity-done-page.test.tsx b/ui/pages/institutional/institutional-entity-done-page/institutional-entity-done-page.test.tsx index d459f9893e7e..6601fc1eabdb 100644 --- a/ui/pages/institutional/institutional-entity-done-page/institutional-entity-done-page.test.tsx +++ b/ui/pages/institutional/institutional-entity-done-page/institutional-entity-done-page.test.tsx @@ -18,6 +18,7 @@ const props = { const render = () => { const store = configureStore({ ...mockState, + metamask: {}, history: { mostRecentOverviewPage: 'test', }, diff --git a/ui/pages/onboarding-flow/add-network-modal/__snapshots__/add-network-modal.test.js.snap b/ui/pages/onboarding-flow/add-network-modal/__snapshots__/add-network-modal.test.js.snap deleted file mode 100644 index b6a29f3cedbf..000000000000 --- a/ui/pages/onboarding-flow/add-network-modal/__snapshots__/add-network-modal.test.js.snap +++ /dev/null @@ -1,175 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Add Network Modal should render 1`] = ` -
-
-

- Add custom network -

-
-
-
-
- - - - -
- A malicious network provider can lie about the state of the blockchain and record your network activity. Only add custom networks you trust. -
-
-
-
- -
- -
-
-
- -
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
-
-
- -
-
-
-`; diff --git a/ui/pages/onboarding-flow/add-network-modal/add-network-modal.stories.tsx b/ui/pages/onboarding-flow/add-network-modal/add-network-modal.stories.tsx deleted file mode 100644 index 5153ccb2ca56..000000000000 --- a/ui/pages/onboarding-flow/add-network-modal/add-network-modal.stories.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react'; -import AddNetworkModal from './index'; - -const meta: Meta = { - title: 'Pages/OnboardingFlow/AddNetworkModal', - component: AddNetworkModal, - argTypes: { - showHeader: { control: 'boolean' }, - addNewNetwork: { control: 'boolean' }, - onEditNetwork: { action: 'onEditNetwork' }, - networkToEdit: { control: 'object' }, - onRpcUrlAdd: { action: 'onRpcUrlAdd' }, - prevActionMode: { control: 'text' }, - networkFormInformation: { control: 'object' }, - setNetworkFormInformation: { action: 'setNetworkFormInformation' }, - }, - args: { - showHeader: false, - addNewNetwork: true, - onEditNetwork: undefined, - networkToEdit: undefined, - prevActionMode: undefined, - networkFormInformation: {}, - setNetworkFormInformation: () => null, - onRpcUrlAdd: () => {}, - }, -}; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - args: {}, -}; - -export const ShowHeader: Story = { - args: { - showHeader: true, - }, -}; diff --git a/ui/pages/onboarding-flow/add-network-modal/add-network-modal.test.js b/ui/pages/onboarding-flow/add-network-modal/add-network-modal.test.js deleted file mode 100644 index ba126bbeb2b0..000000000000 --- a/ui/pages/onboarding-flow/add-network-modal/add-network-modal.test.js +++ /dev/null @@ -1,76 +0,0 @@ -import React from 'react'; -import { waitFor } from '@testing-library/react'; -import configureMockStore from 'redux-mock-store'; -import thunk from 'redux-thunk'; -import { renderWithProvider } from '../../../../test/lib/render-helpers'; -import * as useSafeChainsModule from '../../settings/networks-tab/networks-form/use-safe-chains'; -import AddNetworkModal from '.'; - -const mockHideModal = jest.fn(); -jest.mock('../../../store/actions', () => ({ - ...jest.requireActual('../../../store/actions'), - hideModal: () => mockHideModal, -})); - -jest.mock('../../../pages/settings/networks-tab/networks-form/use-safe-chains'); - -const mockNetworkMenuRedesignToggle = jest.fn(); - -jest.mock('../../../helpers/utils/feature-flags', () => ({ - ...jest.requireActual('../../../helpers/utils/feature-flags'), - getLocalNetworkMenuRedesignFeatureFlag: () => mockNetworkMenuRedesignToggle, -})); - -describe('Add Network Modal', () => { - it('should render', async () => { - mockNetworkMenuRedesignToggle.mockImplementation(() => false); - - jest.spyOn(useSafeChainsModule, 'useSafeChains').mockReturnValue({ - safeChains: [ - { - chainId: '1', - name: 'Mocked Ethereum Mainnet', - nativeCurrency: { symbol: 'MOCKETH' }, - rpc: ['https://mocked.example.com/rpc'], - }, - { - chainId: '3', - name: 'Mocked Another Chain', - nativeCurrency: { symbol: 'MOCKANC' }, - rpc: ['https://mocked.another-example.com/rpc'], - }, - ], - }); - - const mockStore = configureMockStore([])({ - metamask: { useSafeChainsListValidation: true, orderedNetworkList: {} }, - }); - - const { container } = renderWithProvider( - , - mockStore, - ); - - await waitFor(() => { - expect(container).toMatchSnapshot(); - }); - }); - - it('should not render the new network flow modal', async () => { - mockNetworkMenuRedesignToggle.mockReturnValue(true); - - const mockStore = configureMockStore([thunk])({ - metamask: { useSafeChainsListValidation: true, orderedNetworkList: {} }, - }); - - const { queryByText } = renderWithProvider( - , - mockStore, - ); - - await waitFor(() => { - expect(queryByText('Cancel')).not.toBeInTheDocument(); - expect(queryByText('Next')).toBeInTheDocument(); - }); - }); -}); diff --git a/ui/pages/onboarding-flow/add-network-modal/index.js b/ui/pages/onboarding-flow/add-network-modal/index.js deleted file mode 100644 index 911bb89eee04..000000000000 --- a/ui/pages/onboarding-flow/add-network-modal/index.js +++ /dev/null @@ -1,89 +0,0 @@ -import React from 'react'; -import { useDispatch } from 'react-redux'; -import PropTypes from 'prop-types'; -import { useI18nContext } from '../../../hooks/useI18nContext'; - -import { hideModal } from '../../../store/actions'; - -import Typography from '../../../components/ui/typography/typography'; -import Box from '../../../components/ui/box/box'; -import { - TEXT_ALIGN, - TypographyVariant, - FONT_WEIGHT, -} from '../../../helpers/constants/design-system'; -import NetworksForm from '../../settings/networks-tab/networks-form/networks-form'; - -export default function AddNetworkModal({ - showHeader = false, - onEditNetwork = null, - addNewNetwork = true, - networkToEdit = null, - onRpcUrlAdd, - prevActionMode = null, - networkFormInformation = {}, - setNetworkFormInformation = () => null, -}) { - const dispatch = useDispatch(); - const t = useI18nContext(); - - const closeCallback = () => - dispatch(hideModal({ name: 'ONBOARDING_ADD_NETWORK' })); - - const additionalProps = networkToEdit - ? { selectedNetwork: networkToEdit } - : {}; - - return ( - <> - {showHeader ? ( - - - {t('onboardingMetametricsModalTitle')} - - - ) : null} - - - ); -} - -AddNetworkModal.propTypes = { - showHeader: PropTypes.bool, - isNewNetworkFlow: PropTypes.bool, - addNewNetwork: PropTypes.bool, - onEditNetwork: PropTypes.func, - networkToEdit: PropTypes.object, - onRpcUrlAdd: PropTypes.func, - prevActionMode: PropTypes.string, - networkFormInformation: PropTypes.object, - setNetworkFormInformation: PropTypes.func, -}; - -AddNetworkModal.defaultProps = { - showHeader: false, - isNewNetworkFlow: false, - addNewNetwork: true, - onEditNetwork: null, - networkToEdit: null, - prevActionMode: null, - networkFormInformation: {}, - setNetworkFormInformation: () => null, -}; diff --git a/ui/pages/onboarding-flow/onboarding-flow.test.js b/ui/pages/onboarding-flow/onboarding-flow.test.js index eb5672b68c73..bfafbac7f070 100644 --- a/ui/pages/onboarding-flow/onboarding-flow.test.js +++ b/ui/pages/onboarding-flow/onboarding-flow.test.js @@ -41,7 +41,11 @@ describe('Onboarding Flow', () => { accounts: {}, selectedAccount: '', }, - ...mockNetworkState({ chainId: CHAIN_IDS.GOERLI }), + ...mockNetworkState( + { chainId: CHAIN_IDS.GOERLI }, + { chainId: CHAIN_IDS.MAINNET }, + { chainId: CHAIN_IDS.LINEA_MAINNET }, + ), incomingTransactionsPreferences: { [CHAIN_IDS.MAINNET]: true, diff --git a/ui/pages/onboarding-flow/privacy-settings/privacy-settings.js b/ui/pages/onboarding-flow/privacy-settings/privacy-settings.js index 9f1bb0dcf8c3..fffd5c00feda 100644 --- a/ui/pages/onboarding-flow/privacy-settings/privacy-settings.js +++ b/ui/pages/onboarding-flow/privacy-settings/privacy-settings.js @@ -19,19 +19,16 @@ import { } from '../../../../shared/lib/ui-utils'; import { Box, - PickerNetwork, Text, TextField, ButtonPrimary, ButtonPrimarySize, - ButtonSecondary, - ButtonSecondarySize, - Icon, IconName, ButtonLink, AvatarNetwork, ButtonIcon, IconSize, + Icon, } from '../../../components/component-library'; import { MetaMetricsContext } from '../../../contexts/metametrics'; import { @@ -46,10 +43,9 @@ import { import { ONBOARDING_PIN_EXTENSION_ROUTE } from '../../../helpers/constants/routes'; import { useI18nContext } from '../../../hooks/useI18nContext'; import { - getAllNetworks, - getCurrentNetwork, getPetnamesEnabled, getExternalServicesOnboardingToggleState, + getNetworkConfigurationsByChainId, } from '../../../selectors'; import { selectIsProfileSyncingEnabled } from '../../../selectors/metamask-notifications/profile-syncing'; import { selectParticipateInMetaMetrics } from '../../../selectors/metamask-notifications/authentication'; @@ -69,6 +65,7 @@ import { setUseTransactionSimulations, setPetnamesEnabled, performSignIn, + setEditedNetwork, } from '../../../store/actions'; import { onboardingToggleBasicFunctionalityOn, @@ -76,11 +73,9 @@ import { } from '../../../ducks/app/app'; import IncomingTransactionToggle from '../../../components/app/incoming-trasaction-toggle/incoming-transaction-toggle'; import { - CHAIN_IDS, CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP, - NETWORK_TO_NAME_MAP, + TEST_CHAINS, } from '../../../../shared/constants/network'; -import { getLocalNetworkMenuRedesignFeatureFlag } from '../../../helpers/utils/feature-flags'; import { Setting } from './setting'; /** @@ -155,8 +150,7 @@ export default function PrivacySettings() { const [turnOnPetnames, setTurnOnPetnames] = useState(petnamesEnabled); const trackEvent = useContext(MetaMetricsContext); - const currentNetwork = useSelector(getCurrentNetwork); - const allNetworks = useSelector(getAllNetworks); + const networkConfigurations = useSelector(getNetworkConfigurationsByChainId); const externalServicesOnboardingToggleState = useSelector( getExternalServicesOnboardingToggleState, @@ -171,10 +165,6 @@ export default function PrivacySettings() { externalServicesOnboardingToggleState, ); - const networkMenuRedesign = useSelector( - getLocalNetworkMenuRedesignFeatureFlag, - ); - const handleSubmit = () => { dispatch(toggleExternalServices(externalServicesOnboardingToggleState)); dispatch(setUsePhishDetect(phishingToggleState)); @@ -300,7 +290,7 @@ export default function PrivacySettings() { /> dispatch(setIncomingTransactionsPreferences(chainId, value)) } @@ -393,103 +383,85 @@ export default function PrivacySettings() { , ])} - {networkMenuRedesign ? ( - - - {[CHAIN_IDS.MAINNET, CHAIN_IDS.LINEA_MAINNET].map( - (chainId) => ( + + + {Object.values(networkConfigurations) + .filter(({ chainId }) => !TEST_CHAINS.includes(chainId)) + .map((network) => ( + { + dispatch( + setEditedNetwork({ chainId: network.chainId }), + ); + dispatch(toggleNetworkMenu()); + }} + display={Display.Flex} + alignItems={AlignItems.center} + justifyContent={JustifyContent.spaceBetween} + > - console.log(`chain ${chainId} clicked`) - } display={Display.Flex} alignItems={AlignItems.center} - justifyContent={JustifyContent.spaceBetween} > - - - - - {NETWORK_TO_NAME_MAP[chainId]} - - - { - // Get just the protocol + domain, not the infura key in path - new URL( - allNetworks.find( - (network) => - network.chainId === chainId, - )?.rpcUrl, - )?.origin - } - - - - + + + {network.name} + + + { + // Get just the protocol + domain, not the infura key in path + new URL( + network?.rpcEndpoints[ + network?.defaultRpcEndpointIndex + ]?.url, + )?.origin + } + + - ), - )} - console.log('add a network clicked')} - justifyContent={JustifyContent.Left} - variant={ButtonVariant.link} - > - - - - {t('addANetwork')} - - - - - - ) : ( - - {currentNetwork ? ( -
- <> - dispatch(toggleNetworkMenu())} + - -
- ) : ( - { - e.preventDefault(); - dispatch( - showModal({ name: 'ONBOARDING_ADD_NETWORK' }), - ); - }} +
+ ))} + { + dispatch( + toggleNetworkMenu({ isAddingNewNetwork: true }), + ); + }} + justifyContent={JustifyContent.Left} + variant={ButtonVariant.link} + > + - {t('onboardingAdvancedPrivacyNetworkButton')} - - )} + + + {t('addANetwork')} + + +
- )} +
} /> diff --git a/ui/pages/onboarding-flow/privacy-settings/privacy-settings.test.js b/ui/pages/onboarding-flow/privacy-settings/privacy-settings.test.js index d85a0c53029b..80561e9376ae 100644 --- a/ui/pages/onboarding-flow/privacy-settings/privacy-settings.test.js +++ b/ui/pages/onboarding-flow/privacy-settings/privacy-settings.test.js @@ -26,8 +26,12 @@ jest.mock('../../../ducks/app/app.ts', () => { describe('Privacy Settings Onboarding View', () => { const mockStore = { metamask: { - ...mockNetworkState({ chainId: CHAIN_IDS.MAINNET }), - + ...mockNetworkState( + { chainId: CHAIN_IDS.MAINNET }, + { chainId: CHAIN_IDS.LINEA_MAINNET }, + { chainId: CHAIN_IDS.SEPOLIA }, + { chainId: CHAIN_IDS.LINEA_SEPOLIA }, + ), preferences: { petnamesEnabled: true, }, diff --git a/ui/pages/routes/routes.component.js b/ui/pages/routes/routes.component.js index d28b99a9a5c9..5814d7a5759e 100644 --- a/ui/pages/routes/routes.component.js +++ b/ui/pages/routes/routes.component.js @@ -208,7 +208,6 @@ export default class Routes extends Component { currentExtensionPopupId: PropTypes.number, useRequestQueue: PropTypes.bool, showSurveyToast: PropTypes.bool.isRequired, - networkMenuRedesign: PropTypes.bool.isRequired, showPrivacyPolicyToast: PropTypes.bool.isRequired, newPrivacyPolicyToastShownDate: PropTypes.number, setSurveyLinkLastClickedOrClosed: PropTypes.func.isRequired, @@ -831,7 +830,6 @@ export default class Routes extends Component { hideDeprecatedNetworkModal, switchedNetworkDetails, clearSwitchedNetworkDetails, - networkMenuRedesign, clearEditedNetwork, ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps) isShowKeyringSnapRemovalResultModal, @@ -922,7 +920,7 @@ export default class Routes extends Component { }} /> ) : null} - {networkMenuRedesign ? : null} + {accountDetailsAddress ? ( ) : null} diff --git a/ui/pages/routes/routes.component.test.js b/ui/pages/routes/routes.component.test.js index 0d5e5c789f17..ec8c4e96c864 100644 --- a/ui/pages/routes/routes.component.test.js +++ b/ui/pages/routes/routes.component.test.js @@ -77,11 +77,6 @@ jest.mock( '../../components/app/metamask-template-renderer/safe-component-list', ); -jest.mock('../../helpers/utils/feature-flags', () => ({ - ...jest.requireActual('../../helpers/utils/feature-flags'), - getLocalNetworkMenuRedesignFeatureFlag: () => false, -})); - const render = async (route, state) => { const store = configureMockStore(middlewares)({ ...mockSendState, diff --git a/ui/pages/routes/routes.container.js b/ui/pages/routes/routes.container.js index dd88ca780596..856aa8b53ade 100644 --- a/ui/pages/routes/routes.container.js +++ b/ui/pages/routes/routes.container.js @@ -29,7 +29,6 @@ import { getUseNftDetection, getNftDetectionEnablementToast, } from '../../selectors'; -import { getLocalNetworkMenuRedesignFeatureFlag } from '../../helpers/utils/feature-flags'; import { getSmartTransactionsOptInStatus } from '../../../shared/modules/selectors'; import { lockMetamask, @@ -142,7 +141,6 @@ function mapStateToProps(state) { newPrivacyPolicyToastShownDate: getNewPrivacyPolicyToastShownDate(state), showPrivacyPolicyToast: getShowPrivacyPolicyToast(state), showSurveyToast: getShowSurveyToast(state), - networkMenuRedesign: getLocalNetworkMenuRedesignFeatureFlag(state), ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps) isShowKeyringSnapRemovalResultModal: state.appState.showKeyringRemovalSnapModal, diff --git a/ui/pages/settings/developer-options-tab/__snapshots__/developer-options-tab.test.tsx.snap b/ui/pages/settings/developer-options-tab/__snapshots__/developer-options-tab.test.tsx.snap index 2015a0f0893d..f8cd5cd61006 100644 --- a/ui/pages/settings/developer-options-tab/__snapshots__/developer-options-tab.test.tsx.snap +++ b/ui/pages/settings/developer-options-tab/__snapshots__/developer-options-tab.test.tsx.snap @@ -169,76 +169,6 @@ exports[`Develop options tab should match snapshot 1`] = ` -
-
-
- - Network Menu Redesign - -
- Toggles the new design of the Networks menu -
-
-
-
-
-
-
-
- Linea Mainnet logo -
-
-

- Linea Mainnet -

-

- - Lineascan.build - -

-
-
-
-