From c81284f2d99e1204b60ff3eba3bdeaf4245cbad5 Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Tue, 11 Jun 2024 16:16:17 +0530 Subject: [PATCH 01/12] feat: update @blockaid/ppom_release to version 1.4.7 (#25173) --- package.json | 2 +- .../__snapshots__/blockaid-banner-alert.test.js.snap | 12 ++++++------ yarn.lock | 10 +++++----- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index 246bb72202ab..b4ece58b2e55 100644 --- a/package.json +++ b/package.json @@ -257,7 +257,7 @@ }, "dependencies": { "@babel/runtime": "patch:@babel/runtime@npm%3A7.24.0#~/.yarn/patches/@babel-runtime-npm-7.24.0-7eb1dd11a2.patch", - "@blockaid/ppom_release": "^1.4.6", + "@blockaid/ppom_release": "^1.4.7", "@contentful/rich-text-html-renderer": "^16.3.5", "@ensdomains/content-hash": "^2.5.7", "@ethereumjs/tx": "^4.1.1", diff --git a/ui/pages/confirmations/components/security-provider-banner-alert/blockaid-banner-alert/__snapshots__/blockaid-banner-alert.test.js.snap b/ui/pages/confirmations/components/security-provider-banner-alert/blockaid-banner-alert/__snapshots__/blockaid-banner-alert.test.js.snap index 26758de34d17..4111226eea01 100644 --- a/ui/pages/confirmations/components/security-provider-banner-alert/blockaid-banner-alert/__snapshots__/blockaid-banner-alert.test.js.snap +++ b/ui/pages/confirmations/components/security-provider-banner-alert/blockaid-banner-alert/__snapshots__/blockaid-banner-alert.test.js.snap @@ -54,7 +54,7 @@ exports[`Blockaid Banner Alert should render 'danger' UI when securityAlertRespo Something doesn't look right? @@ -151,7 +151,7 @@ exports[`Blockaid Banner Alert should render 'warning' UI when securityAlertResp Something doesn't look right? @@ -248,7 +248,7 @@ exports[`Blockaid Banner Alert should render 'warning' UI when securityAlertResp Something doesn't look right? @@ -346,7 +346,7 @@ exports[`Blockaid Banner Alert should render details section even when features Something doesn't look right? @@ -457,7 +457,7 @@ exports[`Blockaid Banner Alert should render details when provided 1`] = ` Something doesn't look right? @@ -556,7 +556,7 @@ exports[`Blockaid Banner Alert should render link to report url 1`] = ` Something doesn't look right? diff --git a/yarn.lock b/yarn.lock index f68f0999f41c..c332e4141c2b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1710,10 +1710,10 @@ __metadata: languageName: node linkType: hard -"@blockaid/ppom_release@npm:^1.4.6": - version: 1.4.6 - resolution: "@blockaid/ppom_release@npm:1.4.6" - checksum: 10/53b9774f97ba24d98dee4a66653c83223afd2db8598d8b631ee4851731b1f68a99d05a8a2b68584d7b9a8e71370a09e79aba024d171af8a823c1053371009756 +"@blockaid/ppom_release@npm:^1.4.7": + version: 1.4.7 + resolution: "@blockaid/ppom_release@npm:1.4.7" + checksum: 10/2d376796afe4dc4e008418120977f22202b52d32ab2d036e8ab511a7673afeffff8b36e9b194cceeb4ffceb3808dcc6a276e1e55fd39e37833b3e7c288fec295 languageName: node linkType: hard @@ -24838,7 +24838,7 @@ __metadata: "@babel/preset-typescript": "npm:^7.23.2" "@babel/register": "npm:^7.22.15" "@babel/runtime": "patch:@babel/runtime@npm%3A7.24.0#~/.yarn/patches/@babel-runtime-npm-7.24.0-7eb1dd11a2.patch" - "@blockaid/ppom_release": "npm:^1.4.6" + "@blockaid/ppom_release": "npm:^1.4.7" "@contentful/rich-text-html-renderer": "npm:^16.3.5" "@ensdomains/content-hash": "npm:^2.5.7" "@ethereumjs/tx": "npm:^4.1.1" From 1be8a26764d9726aba717682009616f08fbb66a5 Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Tue, 11 Jun 2024 16:40:34 +0530 Subject: [PATCH 02/12] fix: addressing feedbacks in typed sign alerts (#25163) --- app/_locales/en/messages.json | 2 +- .../app/alert-system/alert-modal/alert-modal.tsx | 6 +++++- .../app/alert-system/general-alert/general-alert.tsx | 8 +++++++- ui/hooks/useAlerts.ts | 5 +++-- .../components/confirm/footer/footer.test.tsx | 2 +- .../confirmations/components/confirm/footer/footer.tsx | 4 ++-- 6 files changed, 19 insertions(+), 8 deletions(-) diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 8b44e3daa74d..4ac143e9554b 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -689,7 +689,7 @@ "message": "Blockaid" }, "blockaidAlertInfo": { - "message": "If you sign in, a third party known for scams might take all your assets. Please review the alerts before you proceed." + "message": "We don't recommend proceeding with this request." }, "blockaidDescriptionApproveFarming": { "message": "If you approve this request, a third party known for scams might take all your assets." diff --git a/ui/components/app/alert-system/alert-modal/alert-modal.tsx b/ui/components/app/alert-system/alert-modal/alert-modal.tsx index df6cb2b0e647..b53a49585649 100644 --- a/ui/components/app/alert-system/alert-modal/alert-modal.tsx +++ b/ui/components/app/alert-system/alert-modal/alert-modal.tsx @@ -137,7 +137,11 @@ function AlertHeader({ function BlockaidAlertDetails() { const t = useI18nContext(); - return {t('blockaidAlertInfo')}; + return ( + + {t('blockaidAlertInfo')} + + ); } function AlertDetails({ diff --git a/ui/components/app/alert-system/general-alert/general-alert.tsx b/ui/components/app/alert-system/general-alert/general-alert.tsx index 50a1975f861d..e446a5ba9dd2 100644 --- a/ui/components/app/alert-system/general-alert/general-alert.tsx +++ b/ui/components/app/alert-system/general-alert/general-alert.tsx @@ -11,6 +11,7 @@ import { DisclosureVariant } from '../../../ui/disclosure/disclosure.constants'; import { useI18nContext } from '../../../../hooks/useI18nContext'; import { Display, + FontWeight, TextVariant, } from '../../../../helpers/constants/design-system'; import { SecurityProvider } from '../../../../../shared/constants/security-provider'; @@ -79,7 +80,12 @@ function AlertDetails({ {details.map((detail, index) => ( - {detail} + + {detail} + ))} diff --git a/ui/hooks/useAlerts.ts b/ui/hooks/useAlerts.ts index ba10a371f2b6..92440822bbdd 100644 --- a/ui/hooks/useAlerts.ts +++ b/ui/hooks/useAlerts.ts @@ -62,7 +62,7 @@ const useAlerts = (ownerId: string) => { !isAlertConfirmed(alert.key) && alert.severity === Severity.Danger, ); const hasAlerts = alerts.length > 0; - const hasDangerAlerts = alerts.some( + const dangerAlerts = alerts.filter( (alert) => alert.severity === Severity.Danger, ); const hasUnconfirmedDangerAlerts = unconfirmedDangerAlerts.length > 0; @@ -73,7 +73,8 @@ const useAlerts = (ownerId: string) => { generalAlerts, getFieldAlerts, hasAlerts, - hasDangerAlerts, + dangerAlerts, + hasDangerAlerts: dangerAlerts?.length > 0, hasUnconfirmedDangerAlerts, isAlertConfirmed, setAlertConfirmed, diff --git a/ui/pages/confirmations/components/confirm/footer/footer.test.tsx b/ui/pages/confirmations/components/confirm/footer/footer.test.tsx index c0216d45e66d..1645a7684d09 100644 --- a/ui/pages/confirmations/components/confirm/footer/footer.test.tsx +++ b/ui/pages/confirmations/components/confirm/footer/footer.test.tsx @@ -202,7 +202,7 @@ describe('ConfirmFooter', () => { }; it('renders the review alerts button when there are unconfirmed alerts', () => { const { getByText } = render(stateWithAlertsMock); - expect(getByText('Review alerts')).toBeInTheDocument(); + expect(getByText('Confirm')).toBeInTheDocument(); }); it('renders the confirm button when there are no unconfirmed alerts', () => { diff --git a/ui/pages/confirmations/components/confirm/footer/footer.tsx b/ui/pages/confirmations/components/confirm/footer/footer.tsx index ee4a58634a92..b47c6fcacff1 100644 --- a/ui/pages/confirmations/components/confirm/footer/footer.tsx +++ b/ui/pages/confirmations/components/confirm/footer/footer.tsx @@ -43,7 +43,7 @@ const ConfirmButton = ({ const [confirmModalVisible, setConfirmModalVisible] = useState(false); - const { alerts, hasDangerAlerts, hasUnconfirmedDangerAlerts } = + const { alerts, dangerAlerts, hasDangerAlerts, hasUnconfirmedDangerAlerts } = useAlerts(alertOwnerId); const handleCloseConfirmModal = useCallback(() => { @@ -76,7 +76,7 @@ const ConfirmButton = ({ size={ButtonSize.Lg} disabled={hasUnconfirmedDangerAlerts ? false : disabled} > - {hasUnconfirmedDangerAlerts ? t('reviewAlerts') : t('confirm')} + {dangerAlerts?.length > 1 ? t('reviewAlerts') : t('confirm')} ); From bc6acbe65919c7877ed12bcfddf8e72d7f3e5259 Mon Sep 17 00:00:00 2001 From: Frederik Bolding Date: Tue, 11 Jun 2024 13:45:26 +0200 Subject: [PATCH 03/12] fix: Disable MV3 for MV2 Flask production build (#25209) ## **Description** Disables MV3 for the MV2 Flask production build. This was seemingly missed when setting up the CI to build for production and caused problems with the latest build submitted to Firefox. Also updates the release script to use MV2 builds for Firefox. [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/25209?quickstart=1) --- .circleci/config.yml | 2 +- .circleci/scripts/release-create-gh-release.sh | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index ad4958166e68..2c583ad09cad 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -691,7 +691,7 @@ jobs: steps: - run: name: build:prod - command: yarn build --build-type flask prod + command: ENABLE_MV3=false yarn build --build-type flask prod - run: name: build:debug command: find dist/ -type f -exec md5sum {} \; | sort -k 2 diff --git a/.circleci/scripts/release-create-gh-release.sh b/.circleci/scripts/release-create-gh-release.sh index e138e20c5b59..37a654798b9f 100755 --- a/.circleci/scripts/release-create-gh-release.sh +++ b/.circleci/scripts/release-create-gh-release.sh @@ -69,9 +69,9 @@ then release_body="$(awk -v version="${tag##v}" -f .circleci/scripts/show-changelog.awk CHANGELOG.md)" hub release create \ --attach builds/metamask-chrome-*.zip \ - --attach builds/metamask-firefox-*.zip \ + --attach builds-mv2/metamask-firefox-*.zip \ --attach builds-flask/metamask-flask-chrome-*.zip \ - --attach builds-flask/metamask-flask-firefox-*.zip \ + --attach builds-flask-mv2/metamask-flask-firefox-*.zip \ --attach builds-mmi/metamask-mmi-chrome-*.zip \ --attach builds-mmi/metamask-mmi-firefox-*.zip \ --message "Version ${tag##v}" \ From c23654d100a72695e2def556b7b228525fced6f2 Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Tue, 11 Jun 2024 17:52:33 +0530 Subject: [PATCH 04/12] feat: adding pluggable section to confirmation page (#25061) --- .../confirm/pluggable-section/index.ts | 1 + .../pluggable-section.test.tsx | 26 +++++++++++++++++++ .../pluggable-section/pluggable-section.tsx | 22 ++++++++++++++++ ui/pages/confirmations/confirm/confirm.tsx | 18 +++++++------ 4 files changed, 59 insertions(+), 8 deletions(-) create mode 100644 ui/pages/confirmations/components/confirm/pluggable-section/index.ts create mode 100644 ui/pages/confirmations/components/confirm/pluggable-section/pluggable-section.test.tsx create mode 100644 ui/pages/confirmations/components/confirm/pluggable-section/pluggable-section.tsx diff --git a/ui/pages/confirmations/components/confirm/pluggable-section/index.ts b/ui/pages/confirmations/components/confirm/pluggable-section/index.ts new file mode 100644 index 000000000000..a99bc86297c4 --- /dev/null +++ b/ui/pages/confirmations/components/confirm/pluggable-section/index.ts @@ -0,0 +1 @@ +export { default as PluggableSection } from './pluggable-section'; diff --git a/ui/pages/confirmations/components/confirm/pluggable-section/pluggable-section.test.tsx b/ui/pages/confirmations/components/confirm/pluggable-section/pluggable-section.test.tsx new file mode 100644 index 000000000000..085e8392c2b1 --- /dev/null +++ b/ui/pages/confirmations/components/confirm/pluggable-section/pluggable-section.test.tsx @@ -0,0 +1,26 @@ +import React from 'react'; + +import mockState from '../../../../../../test/data/mock-state.json'; +import { renderWithProvider } from '../../../../../../test/jest'; +import { unapprovedPersonalSignMsg } from '../../../../../../test/data/confirmations/personal_sign'; +import configureStore from '../../../../../store/store'; +import PluggableSection from './pluggable-section'; + +const render = () => { + const store = configureStore({ + metamask: { + ...mockState.metamask, + }, + confirm: { + currentConfirmation: unapprovedPersonalSignMsg, + }, + }); + + return renderWithProvider(, store); +}; + +describe('PluggableSection', () => { + it('should render correctly', () => { + expect(() => render()).not.toThrow(); + }); +}); diff --git a/ui/pages/confirmations/components/confirm/pluggable-section/pluggable-section.tsx b/ui/pages/confirmations/components/confirm/pluggable-section/pluggable-section.tsx new file mode 100644 index 000000000000..c167cdb83c0f --- /dev/null +++ b/ui/pages/confirmations/components/confirm/pluggable-section/pluggable-section.tsx @@ -0,0 +1,22 @@ +import React from 'react'; +import { ReactComponentLike } from 'prop-types'; +import { useSelector } from 'react-redux'; + +import { currentConfirmationSelector } from '../../../selectors'; + +// Components to be plugged into confirmation page can be added to the array below +const pluggedInSections: ReactComponentLike[] = []; + +const PluggableSection = () => { + const currentConfirmation = useSelector(currentConfirmationSelector); + + return ( + <> + {pluggedInSections.map((Section, index) => ( +
+ ))} + + ); +}; + +export default PluggableSection; diff --git a/ui/pages/confirmations/confirm/confirm.tsx b/ui/pages/confirmations/confirm/confirm.tsx index 8dd75a692def..505cda5e3042 100644 --- a/ui/pages/confirmations/confirm/confirm.tsx +++ b/ui/pages/confirmations/confirm/confirm.tsx @@ -1,21 +1,22 @@ import React from 'react'; import { AlertActionHandlerProvider } from '../../../components/app/alert-system/contexts/alertActionHandler'; -import ScrollToBottom from '../components/confirm/scroll-to-bottom'; -import { Footer } from '../components/confirm/footer'; -import { Header } from '../components/confirm/header'; -import { Info } from '../components/confirm/info'; +import { Page } from '../../../components/multichain/pages/page'; ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) import { MMISignatureMismatchBanner } from '../../../components/app/mmi-signature-mismatch-banner'; ///: END:ONLY_INCLUDE_IF -import { Nav } from '../components/confirm/nav'; -import { Title } from '../components/confirm/title'; -import { Page } from '../../../components/multichain/pages/page'; +import ScrollToBottom from '../components/confirm/scroll-to-bottom'; import setCurrentConfirmation from '../hooks/setCurrentConfirmation'; import syncConfirmPath from '../hooks/syncConfirmPath'; -import { LedgerInfo } from '../components/confirm/ledger-info'; import setConfirmationAlerts from '../hooks/setConfirmationAlerts'; import useConfirmationAlertActions from '../hooks/useConfirmationAlertActions'; +import { Footer } from '../components/confirm/footer'; +import { Header } from '../components/confirm/header'; +import { Info } from '../components/confirm/info'; +import { LedgerInfo } from '../components/confirm/ledger-info'; +import { Nav } from '../components/confirm/nav'; +import { Title } from '../components/confirm/title'; +import { PluggableSection } from '../components/confirm/pluggable-section'; const Confirm = () => { setCurrentConfirmation(); @@ -37,6 +38,7 @@ const Confirm = () => { <Info /> + <PluggableSection /> </ScrollToBottom> <Footer /> </Page> From 832ae1cb0a794f37ebf65304449d217a6adc178e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Regadas?= <apregadas@gmail.com> Date: Tue, 11 Jun 2024 14:43:40 +0100 Subject: [PATCH 05/12] fix: updates MMI e2e confirm transaction flow (#25053) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit <!-- Please submit this PR as a draft initially. Do not mark it as "Ready for review" until the template has been completely filled out, and PR status checks have passed at least once. --> ## **Description** Updates MMI e2e tests for the transaction confirmation flow. ## **Related issues** Fixes: ## **Manual testing steps** 1. Go to this page... 2. 3. ## **Screenshots/Recordings** <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> ### **Before** <!-- [screenshots/recordings] --> ### **After** <!-- [screenshots/recordings] --> ## **Pre-merge author checklist** - [ ] I’ve followed [MetaMask 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/mmi/pageObjects/mmi-main-page.ts | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/test/e2e/mmi/pageObjects/mmi-main-page.ts b/test/e2e/mmi/pageObjects/mmi-main-page.ts index 6d3ab315488f..8d78ceafd39d 100644 --- a/test/e2e/mmi/pageObjects/mmi-main-page.ts +++ b/test/e2e/mmi/pageObjects/mmi-main-page.ts @@ -69,17 +69,16 @@ export class MMIMainPage { } async sendFunds(account: string, amount: string) { - await this.page - .getByTestId('recipient-group') - .locator(`text="${account}"`) - .click(); + await this.page.locator(`text="${account}"`).click(); await expect( this.page.locator('.ens-input__selected-input__title'), - ).toHaveText(`${account}`); - await this.page.locator('input.unit-input__input').type(`${amount}`); - await this.page.locator('text="Next"').click(); + ).toContainText(`${account}`); + await this.page + .locator('[data-testid="currency-input"]') + .first() + .type(`${amount}`); + await this.page.locator('text="Continue"').click(); await this.page.locator('text="Confirm"').click(); - await this.page.locator('text="Approve"').click(); } async mainPageScreenshot(screenshotName: string, accountName: string) { From b3c810e18ff79cd41e2a15a868ddd703d5a00780 Mon Sep 17 00:00:00 2001 From: Charly Chevalier <charly.chevalier@consensys.net> Date: Tue, 11 Jun 2024 16:11:30 +0200 Subject: [PATCH 06/12] feat(multichain): add new selectors (#25205) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit <!-- Please submit this PR as a draft initially. Do not mark it as "Ready for review" until the template has been completely filled out, and PR status checks have passed at least once. --> ## **Description** Adds new selectors for multichain context (and more specifically, for non-EVM networks for now). Those selectors follow the same naming than some existing selectors (that are EVM only). [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/25205?quickstart=1) ## **Related issues** None ## **Manual testing steps** 1. This PR rely only on unit tests ## **Screenshots/Recordings** <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> ### **Before** <!-- [screenshots/recordings] --> ### **After** <!-- [screenshots/recordings] --> ## **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 - [ ] 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** - [ ] 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. --- app/images/bitcoin-logo.svg | 14 ++ shared/constants/multichain/networks.ts | 38 +++++ test/data/mock-accounts.ts | 59 +++++++ ui/selectors/accounts.test.ts | 63 +------ ui/selectors/multichain.test.ts | 212 ++++++++++++++++++++++++ ui/selectors/multichain.ts | 154 +++++++++++++++++ 6 files changed, 483 insertions(+), 57 deletions(-) create mode 100644 app/images/bitcoin-logo.svg create mode 100644 shared/constants/multichain/networks.ts create mode 100644 test/data/mock-accounts.ts create mode 100644 ui/selectors/multichain.test.ts create mode 100644 ui/selectors/multichain.ts diff --git a/app/images/bitcoin-logo.svg b/app/images/bitcoin-logo.svg new file mode 100644 index 000000000000..4306787921c7 --- /dev/null +++ b/app/images/bitcoin-logo.svg @@ -0,0 +1,14 @@ +<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> +<g clip-path="url(#clip0_491_4015)"> +<rect width="16" height="16" fill="url(#pattern0_491_4015)"/> +</g> +<defs> +<pattern id="pattern0_491_4015" patternContentUnits="objectBoundingBox" width="1" height="1"> +<use xlink:href="#image0_491_4015" transform="scale(0.0123457)"/> +</pattern> +<clipPath id="clip0_491_4015"> +<rect width="16" height="16" fill="white"/> +</clipPath> +<image id="image0_491_4015" width="81" height="81" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFEAAABRCAYAAACqj0o2AAAMP2lDQ1BJQ0MgUHJvZmlsZQAASImVVwdYU8kWnluSkJDQQpcSehNEpASQEkILvSOISkgChBJjIKjYy6KCaxcVsKGrIoqdZkERO4ti74sFFWVdLNiVNymg677yvfm+ufPff87858y5M/feAUD9OFcszkM1AMgXFUriQgIYY1JSGaQngAqIQBW4Ahsur0DMiomJALAMtn8v764DRNZecZRp/bP/vxZNvqCABwASA3EGv4CXD/FBAPAqnlhSCABRxltMLhTLMKxAWwIDhHihDGcpcJUMZyjwXrlNQhwb4jYAVKhcriQLALVLkGcU8bKghlofxM4ivlAEgDoDYt/8/Il8iNMhtoU2Yohl+syMH3Sy/qaZMaTJ5WYNYcVc5EUlUFggzuNO/T/T8b9Lfp500Ic1rNRsSWicbM4wbzdzJ4bLMBXiXlFGVDTEWhB/EPLl9hCjlGxpaKLCHjXiFbBhzoAuxM58bmA4xEYQB4vyoiKUfEamMJgDMVwh6BRhIScBYn2IFwoKguKVNpskE+OUvtD6TAmbpeTPciVyvzJf96W5iSyl/utsAUepj6kVZyckQ0yB2LJImBQFsRrETgW58eFKm9HF2eyoQRuJNE4WvyXEcQJRSIBCHyvKlATHKe1L8wsG54ttyhZyopR4f2F2QqgiP1gbjyuPH84FuyQQsRIHdQQFYyIG58IXBAYp5o49E4gS45U6H8SFAXGKsThFnBejtMfNBXkhMt4cYteConjlWDypEC5IhT6eKS6MSVDEiRfncMNiFPHgy0AEYINAwABSWDPARJADhB29Db3wTtETDLhAArKAADgqmcERyfIeEbzGg2LwJ0QCUDA0LkDeKwBFkP86xCqujiBT3lskH5ELnkCcD8JBHryXykeJhrwlgceQEf7DOxdWHow3D1ZZ/7/nB9nvDAsyEUpGOuiRoT5oSQwiBhJDicFEO9wQ98W98Qh49YfVBWfinoPz+G5PeELoJDwkXCN0EW5NEM6V/BRlJOiC+sHKXGT8mAvcGmq64QG4D1SHyrgubggccVfoh4X7Qc9ukGUr45ZlhfGT9t9m8MPTUNqRnckoWY/sT7b9eaSavZrbkIos1z/mRxFrxlC+2UM9P/tn/5B9PmzDf7bEFmIHsDPYCewcdgRrAAysBWvE2rGjMjy0uh7LV9egtzh5PLlQR/gPf4NPVpbJAuda5x7nL4q+QsEU2TsasCeKp0qEWdmFDBb8IggYHBHPaTjDxdnFBQDZ90Xx+noTK/9uILrt37l5fwDg0zIwMHD4OxfWAsA+D7j9m75ztkz46VAF4GwTTyopUnC47EKAbwl1uNMMgAmwALZwPi7AHXgDfxAEwkA0SAApYDyMPhuucwmYDKaDOaAElIFlYDWoABvBFrAD7Ab7QQM4Ak6A0+ACuASugTtw9XSDF6APvAOfEQQhITSEjhggpogV4oC4IEzEFwlCIpA4JAVJR7IQESJFpiPzkDJkBVKBbEZqkH1IE3ICOYd0IreQB0gP8hr5hGIoFdVGjVFrdATKRFloOJqAjkOz0EloMTofXYKuRavRXWg9egK9gF5Du9AXaD8GMFVMFzPDHDEmxsaisVQsE5NgM7FSrByrxuqwZvicr2BdWC/2ESfidJyBO8IVHIon4jx8Ej4TX4xX4DvwerwNv4I/wPvwbwQawYjgQPAicAhjCFmEyYQSQjlhG+EQ4RTcS92Ed0QiUZdoQ/SAezGFmEOcRlxMXE/cQzxO7CQ+IvaTSCQDkgPJhxRN4pIKSSWkdaRdpBbSZVI36YOKqoqpiotKsEqqikhlrkq5yk6VYyqXVZ6qfCZrkK3IXuRoMp88lbyUvJXcTL5I7iZ/pmhSbCg+lARKDmUOZS2ljnKKcpfyRlVV1VzVUzVWVag6W3Wt6l7Vs6oPVD9Staj2VDY1jSqlLqFupx6n3qK+odFo1jR/WiqtkLaEVkM7SbtP+6BGV3NS46jx1WapVarVq11We6lOVrdSZ6mPVy9WL1c/oH5RvVeDrGGtwdbgaszUqNRo0rih0a9J1xypGa2Zr7lYc6fmOc1nWiQta60gLb7WfK0tWie1HtExugWdTefR59G30k/Ru7WJ2jbaHO0c7TLt3dod2n06WjquOkk6U3QqdY7qdOliuta6HN083aW6+3Wv637SM9Zj6Qn0FunV6V3We68/TN9fX6Bfqr9H/5r+JwOGQZBBrsFygwaDe4a4ob1hrOFkww2Gpwx7h2kP8x7GG1Y6bP+w20aokb1RnNE0oy1G7Ub9xibGIcZi43XGJ417TXRN/E1yTFaZHDPpMaWb+poKTVeZtpg+Z+gwWIw8xlpGG6PPzMgs1Exqttmsw+yzuY15ovlc8z3m9ywoFkyLTItVFq0WfZamlpGW0y1rLW9bka2YVtlWa6zOWL23trFOtl5g3WD9zEbfhmNTbFNrc9eWZutnO8m22vaqHdGOaZdrt97ukj1q72afbV9pf9EBdXB3EDqsd+gcThjuOVw0vHr4DUeqI8uxyLHW8YGTrlOE01ynBqeXIyxHpI5YPuLMiG/Obs55zlud74zUGhk2cu7I5pGvXexdeC6VLldH0UYFj5o1qnHUK1cHV4HrBtebbnS3SLcFbq1uX9093CXude49HpYe6R5VHjeY2swY5mLmWU+CZ4DnLM8jnh+93L0KvfZ7/eXt6J3rvdP72Wib0YLRW0c/8jH34fps9unyZfim+27y7fIz8+P6Vfs99Lfw5/tv83/KsmPlsHaxXgY4B0gCDgW8Z3uxZ7CPB2KBIYGlgR1BWkGJQRVB94PNg7OCa4P7QtxCpoUcDyWEhocuD73BMebwODWcvjCPsBlhbeHU8PjwivCHEfYRkojmSDQyLHJl5N0oqyhRVEM0iOZEr4y+F2MTMynmcCwxNia2MvZJ3Mi46XFn4unxE+J3xr9LCEhYmnAn0TZRmtiapJ6UllST9D45MHlFcteYEWNmjLmQYpgiTGlMJaUmpW5L7R8bNHb12O40t7SStOvjbMZNGXduvOH4vPFHJ6hP4E44kE5IT07fmf6FG82t5vZncDKqMvp4bN4a3gu+P38Vv0fgI1gheJrpk7ki81mWT9bKrJ5sv+zy7F4hW1ghfJUTmrMx531udO723IG85Lw9+Sr56flNIi1RrqhtosnEKRM7xQ7iEnHXJK9Jqyf1ScIl2wqQgnEFjYXa8Ee+XWor/UX6oMi3qLLow+SkyQemaE4RTWmfaj910dSnxcHFv03Dp/GmtU43mz5n+oMZrBmbZyIzM2a2zrKYNX9W9+yQ2TvmUObkzvl9rvPcFXPfzkue1zzfeP7s+Y9+CfmltkStRFJyY4H3go0L8YXChR2LRi1at+hbKb/0fJlzWXnZl8W8xed/Hfnr2l8HlmQu6VjqvnTDMuIy0bLry/2W71ihuaJ4xaOVkSvrVzFWla56u3rC6nPlruUb11DWSNd0rY1Y27jOct2ydV8qsiuuVQZU7qkyqlpU9X49f/3lDf4b6jYabyzb+GmTcNPNzSGb66utq8u3ELcUbXmyNWnrmd+Yv9VsM9xWtu3rdtH2rh1xO9pqPGpqdhrtXFqL1kpre3al7bq0O3B3Y51j3eY9unvK9oK90r3P96Xvu74/fH/rAeaBuoNWB6sO0Q+V1iP1U+v7GrIbuhpTGjubwppam72bDx12Orz9iNmRyqM6R5ceoxybf2ygpbil/7j4eO+JrBOPWie03jk55uTVtti2jlPhp86eDj598gzrTMtZn7NHznmdazrPPN9wwf1Cfbtb+6Hf3X4/1OHeUX/R42LjJc9LzZ2jO49d9rt84krgldNXOVcvXIu61nk98frNG2k3um7ybz67lXfr1e2i25/vzL5LuFt6T+Ne+X2j+9V/2P2xp8u96+iDwAftD+Mf3nnEe/TiccHjL93zn9CelD81fVrzzOXZkZ7gnkvPxz7vfiF+8bm35E/NP6te2r48+Jf/X+19Y/q6X0leDbxe/Mbgzfa3rm9b+2P677/Lf/f5fekHgw87PjI/nvmU/Onp58lfSF/WfrX72vwt/NvdgfyBATFXwpX/CmCwopmZALzeDgAtBQA6PJ9RxirOf/KCKM6scgT+E1acEeXFHYA6+P8e2wv/bm4AsHcrPH5BffU0AGJoACR4AnTUqKE6eFaTnytlhQjPAZtivmbkZ4B/UxRnzh/i/rkFMlVX8HP7L63RfIDvyMpLAAAAOGVYSWZNTQAqAAAACAABh2kABAAAAAEAAAAaAAAAAAACoAIABAAAAAEAAABRoAMABAAAAAEAAABRAAAAAKjujIYAABFISURBVHgB1V0LlFXVef7/fe7cxzDAMAzDvMAhgkhILSjGJNQXRpO0NrVJJdp0xRqjVmG0SZZZmoZ0bJdpovUJmPiqLqpWG5Y0K61Lm0QJGOsDUYn4AILjMDPMOMAA87pz7z1799vnzhnOuXPvua9z78Beazj7+e99/rv3/94HpuMoqQc+MX1wNNEUUIkaxaKOhAyy5LC1RIOkMnlQCDEg2OyriAf3041X9DG3ycl+BZ6sBahHW8LxgdiiONFSQ/CnpVJnYDH1iijCioJYV4hYCSIOjK0RTZRAnwQxx5RSUTwPo+5NpfhNlur1sEE7eHXXwbH+ZXuUFYlqw2lToocPXoqXv4hILiFFzcRUhbf1Yx34PWg/AO0ymV8URsWTkeva28uBST8Wn3Wdo+vmLlZktkqpvsZM1VkH+Nfhf7Cr7wnVz9jCK3fG/APrhlQyJKr75k8bFiPnGEpdhXN4Maa1j6V7BWUp8S7J6o7KeHAjf7tdkwBfk+9IVHc1R6JB80xS4mYidSFWO4nIS8EVUzuTuDtUMfthvvaN4ZTWgou+IRGEnqMP17eoaMX1rOR1oHJTCl5VaQfiYPAWpejmSOvVr/nB3X1BolJtYnT9A9/C6m4mxfNKiwPfoA/i5TeEgvSPfG33gWKgFo3E/rtbqsMV8R+RUt/EQkLFLGZSxjJtk8q4bkrrvm2Fzl8UEgfAdQPKfBi07ywsoChYhb6AP+O4yxD0g4pPLnicz9+cyBdmwS8+vHbucubE05D1mvKd9DjtHydBPwrP6v5nXklmPmssCImD65u/aCj5OBA4M5/Jjvu+rJGn1oTrau7MR67MG4mja5u/air5MwjNtcc9UgpcIMSgdSEx/RZetXMwFxB5IXFkbdMK/FKbAHhaLsBP5D6K+O7I7K6bcjnaUPBzS0Pr55ypSD2B3mVDoKg/g0TDmcThGiJRXpmdSbWO9DSvgjyZdaNl7aBRHL1/3kIlRx8FDfxsbij3oZdRQcEvPUKi5QJSg/tJHdhJcu/zlHj3SR+A5wwiCp3/bytbe5/2GpF1Jx5dd+pMIHBNWRGIFfOUBqLqedbauaoByPw88dxzXe8iZi8lnt5CVFEy5SjMylirDSiuiVMKWc9IUA1+D5rI5SnjSl7k6k+QmDbXNY/c/9qxciBMgXNvI1G7OLlTj7STuWsTmR9shJUtLwnlGMx0OaZZkhL3w/75Bb6yPZqui+dOHFzftESR/DsM9OyXDnCxdaLx0xPooOp7dxwsR2pJzFhg9eFpc0jMORsIXeQvAu3ZFJ0zOpRotYupz4zIUffVTDOkugMDfGUkjN1lzPsi8dQ5OLMZpgcTEbNPd61VDR8g1b97vE7UL8Mxrhwv64zc95Kr7GdBKfmD2P1Nn0kHM+1bqDYSIxy5CgNWpBtUTJ2Ycy5V/OlDFLzsfyn4lU0U+Oz3LXpHkbpxsBycBlqXpId2per7PanRI8kiGySaUnjc6FGS6FPCNM005RrV1jYBZ2lpYrShsRleDL19JwwodpHWy2MHcgiIalgGEQY7KvEtUrEjpD7eQbL7/0gNHyKuqndNZdFDqT0A2MDBKuK6P3K3D+wjFR1DsqvFzwKvGJz50HmA+IITalokqri4hEm6t4JzVKF5cFGeuXDi6AB8UoE6YnBgzYVJIwtH2pn0cSYD/isTVn5ND2vccNSB99CWlu47wRSbDxtM/6LWL77Aqc24V4optBcuOhi7pdjZ0o3Xu0+MiS3p2sfrRMV41s5UrLiDAsshafW+gWN9FPbypCfVbpedv7OzJX2yUqdH5eG/wiSP2RNNOK4jg7HVaHSfJbt3kU8lISxsv5/MD58nefSj5I7LA6b1I8w9n4wFf+EelYiSGurJzKjcvYstgeupKzXfsAGxndFP9cAZFaOx/e9ALjzFWV+SvBEm0fw5qjj7n4hz2Z3ZFhHtJ/MPz5IJjUb27cz7B8oGPqV9RDL9yZTV3dt1/Tg2dWE01vv5siBQTwb6JT96geK//T4Yiw+0LDyDjMVfB8d/hoIX3ktalMooQun5i0sRlvTXNggXEpVQl9kN5Xqqw3tJJYYzTwcmo4Z6gfQkZ87ccazFCJHAcQ9d/msKnHUTUXB61iGFdMARvnj4p81Neuw4YwHHqYqa/edkt1kUMmXmMfooa7kwU1KH9lDsV6uhS88m0XiWJR+KmlPhzck8xoIFSSCw9FpYgKop/vKPiSBC+ZqYThajailgdo0jMSqPQGCjRl8nygGYaITQnCLOOIfJvh2kDr5v/cmO30LMwU6rOYUYaqGx6PKkqucc4Myjr7HoayD2iuJb/sF6OpuLzAeUQX8GGP89fpwRaXUBdiEEsfImrfN6Jdn5krvZHLU0E/PtRyj+X5fC4PAMmIiHwUEj8tSVMGac5IbjQwk2x7N1JNs4EiXxeT7AzQuENnFxdUvmMVpfPrQ7Y7sCR47/5ttkvg9zH3ZbxlQRIbHwKxmbC23AjPOGRmInWUgcvL2lHvbC+YUCK3Qc1/2xJz2UA52kjkCe9EoyQYlX/5V0X6+k6akX2fAa69FWaVSo0y0kGuHEQnCbqR6dS9Jk6dEe9FD1vgmdGtpJlqS5tzr0gXcviECpVh/vAbm1SlMtSx5nlvOxNd12pdxgFN4LyJtgiXFCww4zu1521mTOg+5xNus2aGnOYlLmmSa0CObFNk3URxmbsXyJK2FwmJJZu7Rkw/49OS2IZ56adBN49FZHOiDUj3j0KKwJhpo6C4nYhS2FgSh8lLZGa104U1IS1hrNdaEeeiX9QwQ+9Q2Yzhq8upHc/7pne6GNisTUgB7MxNgSHtyt0Bk8xonGz4DQT7TW2EMEjLLBy2CoGOiCp+9dS6yxLNsjh4BchMsEKmH9XkpiEcSXGVl4ojbYtv/KBu3rE27kJBKBvuqynmVNDxvgQ8mWAhBNNILwZyz4crJ3ArRNaSRGctaNEzsR8QK3aykSM4eTO5HhRynjRuSpiHefMa+wd4IBN+cIPujdlmXn9XuB+BLd1FAqaCERM9gMprAXy3MUV58MGlZiDRP2xcRbD5H5+w3eBo48156mu4eQlqa3X1U6PMSLHkr4js32Fyz/i/aliJmfzMtBL/dtgSbzXVh/cIS9NBmfXih5nImOlu004zha2kOmF9DyYcdmMnf/wvrTBgdtwWEYHYx5F5GxEJZ560hnAoDfpwnG3i89RIkXbyIJplTSpDiWFHEUexj0/F0CVzV5WrJVbAB+lB3HJoWQrI52gLv+muIvfo+ijy6xXAyegrNmXLOXUPDP/x27GA79UiZW0SQtZNVRynmcsBleOq6c5axy5RV0YHm03VXnKkBcSbx8G8U2fhnu1VddTRMKWoY8//ZkVNmERr8qeMhCIuTEbr9AZoNjzFnuKZpYXjsv09bYBNrOGH/uOlKQI72SqDsNpjDtnCtNgjmsP7kTpfpDaaZIgYpjxrM+lVLpKIIeym5H0JKjKV1WDfeSueORdE3H6jCncbK2nZYmKaYxJBJ/CGF7yM9pdGCmqEeAJpiCnXjqXDjvM9OoJD18w+6e01N24Uh7+Wg0lKlNloaTE8C8O/HupJwYUHugBAxC/5uSN4wMA4wzVlHgtCut4ya1eR8OKTHvQlhbqjKMgDSinVYjBzO2p21A8HhW24kwoIPjVfO+XJF2RlclpJq3LSSGamd8ONLb34PlHNs2rq75FbQxwDjlLy1/iI4zNPCXS1IH3slbrhONZyZVQK8JtBks7r8FB1OOsDLfSjIWXGMVJLZ6rSOfNu0cch7jXMeqob6sMqATFk+fS2Lx3zir0ubVwQ9K4szHpvuQpNibPM6YWpLagsqrkdXKaeEJxlGx4JKCxmsSoIVp2bsdsYZb4eGD9UZz31HcrnVqHjChGXNXkAGXqJieZZdDZ5Ydvu2PlPfi98L9C7rHkQg1+iWOJWBnUt6GuRQwqUWepi/Tg1JohT9TEGfqoLEya8sMHFeG/oNjyXLqx6AHjPYng5i0UTVUbfmSdWQYpwQ1pQOrnVmywxUJl65bQXW4WPs8t23Wn1Q4lkbWNT6H9//CsZrCc9pYqmNtRDPCgJuXIzIWHHISUuJtqH9b20owM3ey4PPDqzr3jO9EPQs+L7AJPgNfkKijtLRP2PIL66BOHDuNVB3YabTgJkCoNOEdTmwpRJ6Zbz/qrPIxr7aG+rraNUCLsdiQK43EE8gP2mXfnjja6vAeMt/ZQInffIdU1ytu0NpSrY2tfibA065UjcgSJOw3+QS3JYUmFxJ5Vd8gJPB/K8Gk4yAtrp0SSmd2bKHYs9+kxLb7wFTgJo0XZw/RQaA6bMQ6BeMz+5hR1Flphn9nQ3QhUVeySXfikytRu4PfT315JzVaVnZuBfHfTIlXfkKxTZdacTeuebGrLO+f3rGZkt7tg92IT3yKYj+/GM//yNSz6HpEz93t/CCHiyZq6JEbuzuG1jY+DexeUfRsaQBYVydSHFT6ypmdNK3kmvl20Xqae34J89c6WH/gZkXcoeVqhTOeK8Jw7kNbRbiJBLlQvW8lke0a7XMBH+OIxEOPOaFOQKJulCzvEEpA5fD3DosO47Au+ThWoKKHXPE2rK+apYTaaU+djslJxuWMnyIHlLJlYxDeXLtQzzzhOOvKqlU97+Jype9szbo6gePsTHoXKi1M6wQurjm4K8WHSPZsd1VNVgEI3BmJBzekzp8WidDpFcvQXejckTqgqLIWkFOc7BaC9LUKnbS2U3taMj/2r7Lup/S76iarwKa6y0kL7XWkRaJujNy4t0Ox+ntkPai5DSa3pxr+GFzzhxB1HiepQ0SgyjnjD7X5jGfBKeVIUofWlSD8wzFFTlkYrp8JH174VLrOaWmi3TFyYP8vR2Y2/QIb86t2XVFPiB4mHOl2SKbWYtTQx+MgtTOKU+/rlel+yvgi0mc6E6JiVRgqXrpmGNoyp1s3k1xzUeU2qDcXgGDVZe5ZYAucUk6nugL902KKRSNxf087o8y3Hiw9x/Ve/pCQ4prK1o5tmbq5dOdMnYbXNZ+Fo/csdmRNpj6+12skagf/yIHJPM4mCb41bEZu5xv2ZFSpckKiRtDwvU0rWVjfgPAkAb4jc/IAQnqnjSPD4Wtqbt57xGsZGRlL6qBIf9dGCERrUJ89dDV18AlZVs+Fqfsb2RCoXy1nJELZluFZM+5SkrV7rTjl9jhHKmTkV9iQrXwDZTzCzlfIGYl6kP5qUSQQ+yG+/3oPLK82k3XCO9HzsL/wMwapK8PX9+7N9WVypolOgOo/LzWGe16+RjDdA/Za9rsvzrX4mMedR96UELHVVav6evKBWxAS9QSqjUS0puEqfMr5dnDt6nwmPQ77jkJLexChU7cW8nXkgpFoI2JkXcM58DXcCUDL7LoT7KnNfqsPxMWTc77TWZBftWgkaoSpnyycGq0c+BmyK/F3AolA/JogdUWotft9/R6FJl+QqCdXL54XGH5n18XwptyCEjzqYD3HZwLzRQAX02Nhqv6x81sOhS7X9xe1viFxNP5dCE/XA5kljinO97X5EKL9nzNU4LZga8d72lqVL4R0/X1Hoj3J0No5jczxG/Atwquw1Fq7fpKeUSBsM9bx09DB7mdtB5NfaykZEu0FDq6fVc8yeIlg9XW8xBLUZ45osgf58YSVDUf2IzC9TSKgHgzVLt/DK39eEtm25Ei08aEeaKwdSqhl+F8tVkK+/BwQ2oK2kN3u01Mfz49hNNgOVeApFaEXKq/u6vQJdkYwZUOivQItX9L0k2ZHjdh8yJjLQJbwqSVejPaFoKF5evRZy3e78IPsxv+YsRuW51dlUL0Z7u3p0GqqPWepn2VHYuoLae2nf+8bVeHpsarAKDcmAiYuuXATkFsLDaIaW8u63If/iCEG9A+jrgee8x4SZjs+FLAvnogNHE4EBguV8VLXU0j5/wGJaMgdvWp71wAAAABJRU5ErkJggg=="/> +</defs> +</svg> diff --git a/shared/constants/multichain/networks.ts b/shared/constants/multichain/networks.ts new file mode 100644 index 000000000000..92b9d7a89452 --- /dev/null +++ b/shared/constants/multichain/networks.ts @@ -0,0 +1,38 @@ +import { ProviderConfig } from '@metamask/network-controller'; +import { CaipChainId } from '@metamask/utils'; + +type ProviderConfigWithImageUrl = Omit<ProviderConfig, 'chainId'> & { + rpcPrefs?: { imageUrl?: string }; +}; + +export type MultichainProviderConfig = ProviderConfigWithImageUrl & { + chainId: CaipChainId; +}; + +export enum MultichainNetworks { + BITCOIN = 'bip122:000000000019d6689c085ae165831e93', + BITCOIN_TESTNET = 'bip122:000000000933ea01ad0ee984209779ba', +} + +export const BITCOIN_TOKEN_IMAGE_URL = './images/bitcoin-logo.svg'; + +export const MULTICHAIN_TOKEN_IMAGE_MAP = { + [MultichainNetworks.BITCOIN]: BITCOIN_TOKEN_IMAGE_URL, +} as const; + +export const MULTICHAIN_PROVIDER_CONFIGS: Record< + CaipChainId, + MultichainProviderConfig +> = { + [MultichainNetworks.BITCOIN]: { + chainId: MultichainNetworks.BITCOIN, + rpcUrl: '', // not used + ticker: 'BTC', + nickname: 'Bitcoin', + id: 'btc-mainnet', + type: 'rpc', + rpcPrefs: { + imageUrl: MULTICHAIN_TOKEN_IMAGE_MAP[MultichainNetworks.BITCOIN], + }, + }, +}; diff --git a/test/data/mock-accounts.ts b/test/data/mock-accounts.ts new file mode 100644 index 000000000000..ff6009ebd555 --- /dev/null +++ b/test/data/mock-accounts.ts @@ -0,0 +1,59 @@ +import { KeyringTypes } from '@metamask/keyring-controller'; +import { + InternalAccount, + EthAccountType, + BtcMethod, + BtcAccountType, +} from '@metamask/keyring-api'; +import { + ETH_EOA_METHODS, + ETH_4337_METHODS, +} from '../../shared/constants/eth-methods'; + +export const MOCK_ACCOUNT_EOA: InternalAccount = { + id: '4974fc00-c0fb-4a18-8535-8407ec6d1952', + address: '0x123', + options: {}, + methods: ETH_EOA_METHODS, + type: EthAccountType.Eoa, + metadata: { + name: 'Account 1', + keyring: { type: KeyringTypes.hd }, + importTime: 1691565967600, + lastSelected: 1691565967656, + }, +}; + +export const MOCK_ACCOUNT_ERC4337: InternalAccount = { + id: '4d5921f2-2022-44ce-a84f-9f6a0f142a5c', + address: '0x123', + options: {}, + methods: ETH_EOA_METHODS.concat(ETH_4337_METHODS), + type: EthAccountType.Erc4337, + metadata: { + name: 'Account 2', + keyring: { type: KeyringTypes.snap }, + importTime: 1691565967600, + lastSelected: 1691565967656, + }, +}; + +export const MOCK_ACCOUNT_BIP122_P2WPKH: InternalAccount = { + id: 'ae247df6-3911-47f7-9e36-28e6a7d96078', + address: 'bc1qaabb', + options: {}, + methods: [BtcMethod.SendMany], + type: BtcAccountType.P2wpkh, + metadata: { + name: 'Bitcoin Account', + keyring: { type: KeyringTypes.snap }, + importTime: 1691565967600, + lastSelected: 1955565967656, + }, +}; + +export const MOCK_ACCOUNTS = { + [MOCK_ACCOUNT_EOA.id]: MOCK_ACCOUNT_EOA, + [MOCK_ACCOUNT_ERC4337.id]: MOCK_ACCOUNT_ERC4337, + [MOCK_ACCOUNT_BIP122_P2WPKH.id]: MOCK_ACCOUNT_BIP122_P2WPKH, +}; diff --git a/ui/selectors/accounts.test.ts b/ui/selectors/accounts.test.ts index bf9e9038b655..8f6bc4e14b17 100644 --- a/ui/selectors/accounts.test.ts +++ b/ui/selectors/accounts.test.ts @@ -1,67 +1,16 @@ -import { KeyringTypes } from '@metamask/keyring-controller'; import { - InternalAccount, - EthAccountType, - BtcMethod, - BtcAccountType, -} from '@metamask/keyring-api'; -import { - ETH_EOA_METHODS, - ETH_4337_METHODS, -} from '../../shared/constants/eth-methods'; + MOCK_ACCOUNTS, + MOCK_ACCOUNT_EOA, + MOCK_ACCOUNT_ERC4337, + MOCK_ACCOUNT_BIP122_P2WPKH, +} from '../../test/data/mock-accounts'; import { AccountsState, isSelectedInternalAccountEth } from './accounts'; -const MOCK_ACCOUNT_EOA: InternalAccount = { - id: '4974fc00-c0fb-4a18-8535-8407ec6d1952', - address: '0x123', - options: {}, - methods: ETH_EOA_METHODS, - type: EthAccountType.Eoa, - metadata: { - name: 'Account 1', - keyring: { type: KeyringTypes.hd }, - importTime: 1691565967600, - lastSelected: 1691565967656, - }, -}; - -const MOCK_ACCOUNT_ERC4337: InternalAccount = { - id: '4d5921f2-2022-44ce-a84f-9f6a0f142a5c', - address: '0x123', - options: {}, - methods: ETH_EOA_METHODS.concat(ETH_4337_METHODS), - type: EthAccountType.Erc4337, - metadata: { - name: 'Account 2', - keyring: { type: KeyringTypes.snap }, - importTime: 1691565967600, - lastSelected: 1691565967656, - }, -}; - -const MOCK_ACCOUNT_BIP122_P2WPKH: InternalAccount = { - id: 'ae247df6-3911-47f7-9e36-28e6a7d96078', - address: 'bc1qaabb', - options: {}, - methods: [BtcMethod.SendMany], - type: BtcAccountType.P2wpkh, - metadata: { - name: 'Bitcoin Account', - keyring: { type: KeyringTypes.snap }, - importTime: 1691565967600, - lastSelected: 1955565967656, - }, -}; - const MOCK_STATE: AccountsState = { metamask: { internalAccounts: { selectedAccount: MOCK_ACCOUNT_EOA.id, - accounts: { - [MOCK_ACCOUNT_EOA.id]: MOCK_ACCOUNT_EOA, - [MOCK_ACCOUNT_ERC4337.id]: MOCK_ACCOUNT_ERC4337, - [MOCK_ACCOUNT_BIP122_P2WPKH.id]: MOCK_ACCOUNT_BIP122_P2WPKH, - }, + accounts: MOCK_ACCOUNTS, }, }, }; diff --git a/ui/selectors/multichain.test.ts b/ui/selectors/multichain.test.ts new file mode 100644 index 000000000000..582ba681403a --- /dev/null +++ b/ui/selectors/multichain.test.ts @@ -0,0 +1,212 @@ +import { + getNativeCurrency, + getProviderConfig, +} from '../ducks/metamask/metamask'; +import { + MULTICHAIN_PROVIDER_CONFIGS, + MultichainNetworks, + MultichainProviderConfig, +} from '../../shared/constants/multichain/networks'; +import { + MOCK_ACCOUNTS, + MOCK_ACCOUNT_EOA, + MOCK_ACCOUNT_BIP122_P2WPKH, +} from '../../test/data/mock-accounts'; +import { AccountsState } from './accounts'; +import { + getMultichainCurrentCurrency, + getMultichainDefaultToken, + getMultichainIsEvm, + getMultichainNativeCurrency, + getMultichainNetwork, + getMultichainNetworkProviders, + getMultichainProviderConfig, + getMultichainShouldShowFiat, +} from './multichain'; +import { getCurrentCurrency, getShouldShowFiat } from '.'; + +type TestState = AccountsState & { + metamask: { + preferences: { showFiatInTestnets: boolean }; + providerConfig: { ticker: string; chainId: string }; + currentCurrency: string; + currencyRates: Record<string, { conversionRate: string }>; + }; +}; + +const MOCK_EVM_STATE: TestState = { + metamask: { + preferences: { + showFiatInTestnets: false, + }, + providerConfig: { + ticker: 'ETH', + chainId: '0x1', + }, + currentCurrency: 'ETH', + currencyRates: { + ETH: { + conversionRate: 'usd', + }, + }, + internalAccounts: { + selectedAccount: MOCK_ACCOUNT_EOA.id, + accounts: MOCK_ACCOUNTS, + }, + }, +}; + +const MOCK_NON_EVM_STATE: AccountsState = { + metamask: { + ...MOCK_EVM_STATE.metamask, + internalAccounts: { + selectedAccount: MOCK_ACCOUNT_BIP122_P2WPKH.id, + accounts: MOCK_ACCOUNTS, + }, + }, +}; + +function getBip122ProviderConfig(): MultichainProviderConfig { + // For now, we only have Bitcoin non-EVM network, so we are expecting to have + // this one with `bip122:*` account type + return MULTICHAIN_PROVIDER_CONFIGS[MultichainNetworks.BITCOIN]; +} + +describe('Multichain Selectors', () => { + describe('getMultichainNetworkProviders', () => { + it('has some providers', () => { + const state = MOCK_EVM_STATE; + + const networkProviders = getMultichainNetworkProviders(state); + expect(Array.isArray(networkProviders)).toBe(true); + expect(networkProviders.length).toBeGreaterThan(0); + }); + }); + + describe('getMultichainNetwork', () => { + it('returns an EVM network provider if account is EVM', () => { + const state = MOCK_EVM_STATE; + + const network = getMultichainNetwork(state); + expect(network.isEvmNetwork).toBe(true); + }); + + it('returns an non-EVM network provider if account is non-EVM', () => { + const state = MOCK_NON_EVM_STATE; + + const network = getMultichainNetwork(state); + expect(network.isEvmNetwork).toBe(false); + }); + }); + + describe('getMultichainIsEvm', () => { + it('returns true if selected account is EVM compatible', () => { + const state = MOCK_EVM_STATE; + + expect(getMultichainIsEvm(state)).toBe(true); + }); + + it('returns false if selected account is not EVM compatible', () => { + const state = MOCK_NON_EVM_STATE; + + expect(getMultichainIsEvm(state)).toBe(false); + }); + }); + + describe('getMultichain{ProviderConfig,CurrentNetwork}', () => { + it('returns a ProviderConfig if account is EVM', () => { + const state = MOCK_EVM_STATE; + + expect(getMultichainProviderConfig(state)).toBe(getProviderConfig(state)); + }); + + it('returns a MultichainProviderConfig if account is non-EVM (bip122:*)', () => { + const state = MOCK_NON_EVM_STATE; + + const bip122ProviderConfig = getBip122ProviderConfig(); + expect(getMultichainProviderConfig(state)).toBe(bip122ProviderConfig); + }); + }); + + describe('getMultichainNativeCurrency', () => { + it('returns same native currency if account is EVM', () => { + const state = MOCK_EVM_STATE; + + expect(getMultichainNativeCurrency(state)).toBe(getNativeCurrency(state)); + }); + + it('returns MultichainProviderConfig.ticker if account is non-EVM (bip122:*)', () => { + const state = MOCK_NON_EVM_STATE; + + const bip122ProviderConfig = getBip122ProviderConfig(); + expect(getMultichainNativeCurrency(state)).toBe( + bip122ProviderConfig.ticker, + ); + }); + }); + + describe('getMultichainCurrentCurrency', () => { + it('returns same currency currency if account is EVM', () => { + const state = MOCK_EVM_STATE; + + expect(getMultichainCurrentCurrency(state)).toBe( + getCurrentCurrency(state), + ); + }); + + // @ts-expect-error This is missing from the Mocha type definitions + it.each(['usd', 'ETH'])( + "returns current currency '%s' if account is EVM", + (currency: string) => { + const state = MOCK_EVM_STATE; + + state.metamask.currentCurrency = currency; + expect(getCurrentCurrency(state)).toBe(currency); + expect(getMultichainCurrentCurrency(state)).toBe(currency); + }, + ); + + it('fallbacks to ticker as currency if account is non-EVM (bip122:*)', () => { + const state = MOCK_NON_EVM_STATE; // .currentCurrency = 'ETH' + + const bip122ProviderConfig = getBip122ProviderConfig(); + expect(getCurrentCurrency(state).toLowerCase()).not.toBe('usd'); + expect(getMultichainCurrentCurrency(state)).toBe( + bip122ProviderConfig.ticker, + ); + }); + }); + + describe('getMultichainShouldShowFiat', () => { + it('returns same value as getShouldShowFiat if account is EVM', () => { + const state = MOCK_EVM_STATE; + + expect(getMultichainShouldShowFiat(state)).toBe(getShouldShowFiat(state)); + }); + + it('returns true if account is non-EVM', () => { + const state = MOCK_NON_EVM_STATE; + + expect(getMultichainShouldShowFiat(state)).toBe(true); + }); + }); + + describe('getMultichainDefaultToken', () => { + it('returns ETH if account is EVM', () => { + const state = MOCK_EVM_STATE; + + expect(getMultichainDefaultToken(state)).toEqual({ + symbol: 'ETH', + }); + }); + + it('returns true if account is non-EVM (bip122:*)', () => { + const state = MOCK_NON_EVM_STATE; + + const bip122ProviderConfig = getBip122ProviderConfig(); + expect(getMultichainDefaultToken(state)).toEqual({ + symbol: bip122ProviderConfig.ticker, + }); + }); + }); +}); diff --git a/ui/selectors/multichain.ts b/ui/selectors/multichain.ts new file mode 100644 index 000000000000..e97b08b85030 --- /dev/null +++ b/ui/selectors/multichain.ts @@ -0,0 +1,154 @@ +import { isEvmAccountType } from '@metamask/keyring-api'; +import { ProviderConfig } from '@metamask/network-controller'; +import { + CaipChainId, + KnownCaipNamespace, + parseCaipChainId, +} from '@metamask/utils'; +import { + MultichainProviderConfig, + MULTICHAIN_PROVIDER_CONFIGS, +} from '../../shared/constants/multichain/networks'; +import { + getNativeCurrency, + getProviderConfig, +} from '../ducks/metamask/metamask'; +import { AccountsState } from './accounts'; +import { + getAllNetworks, + getCurrentCurrency, + getNativeCurrencyImage, + getSelectedInternalAccount, + getShouldShowFiat, +} from '.'; + +export type MultichainState = AccountsState & { + metamask: { + // TODO: Use states from new {Rates,Balances,Chain}Controller + }; +}; + +export type MultichainNetwork = { + nickname: string; + isEvmNetwork: boolean; + chainId?: CaipChainId; + network?: ProviderConfig | MultichainProviderConfig; +}; + +export function getMultichainNetworkProviders( + _state: MultichainState, +): MultichainProviderConfig[] { + // TODO: need state from the ChainController? + return Object.values(MULTICHAIN_PROVIDER_CONFIGS); +} + +export function getMultichainNetwork( + state: MultichainState, +): MultichainNetwork { + const selectedAccount = getSelectedInternalAccount(state); + const isEvm = isEvmAccountType(selectedAccount.type); + + // EVM networks + const evmNetworks: ProviderConfig[] = getAllNetworks(state); + const evmProvider: ProviderConfig = getProviderConfig(state); + + if (isEvm) { + const evmChainId = + `${KnownCaipNamespace.Eip155}:${evmProvider.chainId}` as CaipChainId; + const evmNetwork = evmNetworks.find( + (network) => network.chainId === evmProvider.chainId, + ); + + return { + nickname: 'Ethereum', + isEvmNetwork: true, + chainId: evmChainId, + network: evmNetwork, + }; + } + + // Non-EVM networks + // (Hardcoded for testing) + // HACK: For now, we rely on the account type being "sort-of" CAIP compliant, so use + // this as a CAIP-2 namespace and apply our filter with it + const nonEvmNetworks = getMultichainNetworkProviders(state); + const nonEvmNetwork = nonEvmNetworks.find((provider) => { + const { namespace } = parseCaipChainId(provider.chainId); + return selectedAccount.type.startsWith(namespace); + }); + + return { + // TODO: Adapt this for other non-EVM networks + // TODO: We need to have a way of setting nicknames of other non-EVM networks + nickname: 'Bitcoin', + isEvmNetwork: false, + // FIXME: We should use CAIP-2 chain ID here, and not only the reference part + chainId: nonEvmNetwork?.chainId, + network: nonEvmNetwork, + }; +} + +// FIXME: All the following might have side-effect, like if the current account is a bitcoin one and that +// a popup (for ethereum related stuffs) is being shown (and uses this function), then the native +// currency will be BTC.. + +export function getMultichainIsEvm(state: MultichainState) { + const selectedAccount = getSelectedInternalAccount(state); + + // There are no selected account during onboarding. we default to the current EVM provider. + return !selectedAccount || isEvmAccountType(selectedAccount.type); +} + +export function getMultichainProviderConfig( + state: MultichainState, +): ProviderConfig | MultichainProviderConfig { + return getMultichainIsEvm(state) + ? getProviderConfig(state) + : getMultichainNetwork(state).network; +} + +export function getMultichainCurrentNetwork(state: MultichainState) { + return getMultichainProviderConfig(state); +} + +export function getMultichainNativeCurrency(state: MultichainState) { + return getMultichainIsEvm(state) + ? getNativeCurrency(state) + : getMultichainProviderConfig(state).ticker; +} + +export function getMultichainCurrentCurrency(state: MultichainState) { + const currentCurrency = getCurrentCurrency(state).toLowerCase(); + + // To mimic `getCurrentCurrency` we only consider fiat values, otherwise we + // fallback to the current ticker symbol value + return currentCurrency === 'usd' + ? 'usd' + : getMultichainProviderConfig(state).ticker; +} + +export function getMultichainCurrencyImage(state: MultichainState) { + if (getMultichainIsEvm(state)) { + return getNativeCurrencyImage(state); + } + + const provider = getMultichainProviderConfig( + state, + ) as MultichainProviderConfig; + return provider.rpcPrefs?.imageUrl; +} + +export function getMultichainShouldShowFiat(state: MultichainState) { + return getMultichainIsEvm(state) + ? getShouldShowFiat(state) + : // For now we force this for non-EVM + true; +} + +export function getMultichainDefaultToken(state: MultichainState) { + const symbol = getMultichainIsEvm(state) + ? getProviderConfig(state).ticker + : getMultichainProviderConfig(state).ticker; + + return { symbol }; +} From 2b2ee43178173ecb8beccd0e902a11aca7f158e7 Mon Sep 17 00:00:00 2001 From: Victor Thomas <10986371+vthomas13@users.noreply.github.com> Date: Tue, 11 Jun 2024 10:54:55 -0400 Subject: [PATCH 07/12] fix: Swap-Send flaky test fix (#25041) --- .../tests/swap-send/swap-send-test-utils.ts | 6 +++--- test/e2e/webdriver/driver.js | 20 +++++++++++++------ .../app/currency-input/currency-input.js | 3 +-- .../swappable-currency-input.tsx | 2 +- 4 files changed, 19 insertions(+), 12 deletions(-) diff --git a/test/e2e/tests/swap-send/swap-send-test-utils.ts b/test/e2e/tests/swap-send/swap-send-test-utils.ts index 0cfff6449b7f..4140d6bb92be 100644 --- a/test/e2e/tests/swap-send/swap-send-test-utils.ts +++ b/test/e2e/tests/swap-send/swap-send-test-utils.ts @@ -106,13 +106,13 @@ export class SwapSendPage { // eslint-disable-next-line @typescript-eslint/no-explicit-any inputAmounts.map(async (e: any, index: number) => { await this.driver.delay(delayInMs); - const i = await e.nestedFindElement('input'); + const i = await this.driver.findNestedElement(e, 'input'); assert.ok(i); const v = await i.getProperty('value'); assert.equal(v, expectedInputValues[index]); - const isDisabled = !(await i.isEnabled()); if (index > 0) { - assert.ok(isDisabled); + const isDisabled = await i.getProperty('disabled'); + assert.equal(isDisabled, true); } }), ); diff --git a/test/e2e/webdriver/driver.js b/test/e2e/webdriver/driver.js index 00ed3c0e74b2..6815460f3091 100644 --- a/test/e2e/webdriver/driver.js +++ b/test/e2e/webdriver/driver.js @@ -64,12 +64,6 @@ function wrapElementWithAPI(element, driver) { } }; - element.nestedFindElement = async (rawLocator) => { - const locator = driver.buildLocator(rawLocator); - const newElement = await element.findElement(locator); - return wrapElementWithAPI(newElement, driver); - }; - // We need to hold a pointer to the original click() method so that we can call it in the replaced click() method if (!element.originalClick) { element.originalClick = element.click; @@ -466,6 +460,20 @@ class Driver { return wrapElementWithAPI(element, this); } + /** + * Finds a nested element within a parent element using the given locator. + * This is useful when the parent element is already known and you want to find an element within it. + * + * @param {WebElement} element - Parent element + * @param {string | object} nestedLocator - Nested element locator + * @returns {Promise<WebElement>} A promise that resolves to the found nested element. + */ + async findNestedElement(element, nestedLocator) { + const locator = this.buildLocator(nestedLocator); + const nestedElement = await element.findElement(locator); + return wrapElementWithAPI(nestedElement, this); + } + /** * Finds a visible element on the page using the given locator. * diff --git a/ui/components/app/currency-input/currency-input.js b/ui/components/app/currency-input/currency-input.js index 187ca7fd060f..71425f797e9f 100644 --- a/ui/components/app/currency-input/currency-input.js +++ b/ui/components/app/currency-input/currency-input.js @@ -96,8 +96,7 @@ export default function CurrencyInput({ tokenToFiatConversionRate, ); - const isDisabled = !onChange; - + const isDisabled = onChange === undefined; const swap = async () => { await onPreferenceToggle(); }; diff --git a/ui/components/multichain/asset-picker-amount/swappable-currency-input/swappable-currency-input.tsx b/ui/components/multichain/asset-picker-amount/swappable-currency-input/swappable-currency-input.tsx index 543a6ab84b82..ff587fe6c1c1 100644 --- a/ui/components/multichain/asset-picker-amount/swappable-currency-input/swappable-currency-input.tsx +++ b/ui/components/multichain/asset-picker-amount/swappable-currency-input/swappable-currency-input.tsx @@ -87,7 +87,7 @@ export function SwappableCurrencyInput({ <CurrencyInput className="asset-picker-amount__input" isFiatPreferred={isFiatPrimary} - onChange={onAmountChange} + onChange={onAmountChange} // onChange controls disabled state, disabled if undefined hexValue={value} swapIcon={(onClick: React.MouseEventHandler) => ( <SwapIcon onClick={onClick} /> From 8a04980c9467ec4c9b397e6e3fa590bc6f324666 Mon Sep 17 00:00:00 2001 From: George Marshall <george.marshall@consensys.net> Date: Tue, 11 Jun 2024 10:37:36 -0700 Subject: [PATCH 08/12] chore: upgrading to design tokens v4 (#24953) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** This pull request aims to upgrade the extension to [design tokens v4](https://github.com/MetaMask/design-tokens/blob/main/MIGRATION.md#from-version-300-to-400). This upgrade ensures the most up-to-date colors are being used that align with design and primes the extension for the upcoming brand evolution. Included in this PR are third-party network color CSS variables that have been removed from the design tokens package and renamed CSS variables for shadow. This PR is the final update from a series of PRs that replace deprecated CSS colors that have been removed in v4. **Dependency PRs that should be merged before this one:** - https://github.com/MetaMask/metamask-extension/pull/24970 - https://github.com/MetaMask/metamask-extension/pull/24970 - https://github.com/MetaMask/metamask-extension/pull/25125 - https://github.com/MetaMask/metamask-extension/pull/25124 - https://github.com/MetaMask/metamask-extension/pull/25122 - https://github.com/MetaMask/metamask-extension/pull/25011 - https://github.com/MetaMask/metamask-extension/pull/25010 - https://github.com/MetaMask/metamask-extension/pull/25083 - https://github.com/MetaMask/metamask-extension/pull/24971 - https://github.com/MetaMask/metamask-extension/pull/25158 [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/24953?quickstart=1) ![image](https://github.com/MetaMask/metamask-extension/assets/8112138/3c5cb96b-e473-4f3a-9b9c-5aa8020c2610) ![image (1)](https://github.com/MetaMask/metamask-extension/assets/8112138/c16d28f7-f05c-4132-8268-b3c48fe9bd4a) ## **Related issues** Fixes: https://github.com/MetaMask/metamask-extension/issues/24916 ## **Manual testing steps** Check all [removed and changed](https://github.com/MetaMask/design-tokens/blob/main/MIGRATION.md#from-version-300-to-400) CSS variables are no longer in the codebase: 1. Pull this branch. 2. Copy [this script](https://gist.github.com/georgewrmarshall/1ca0d7044bfa3c91343cadbf2bd5c828) that checks for all deprecated colors to the root of the extension. 3. Run `node searchDeprecatedTokens.js`. 4. Ensure there are no results. Check shadows and network colors work as expected: 1. Navigate to stories that use primary and error shadows (`ButtonPrimary`, `ButtonSecondary`, `Button` (deprecated)) as well as network colors (`Box` BackgroundColors story). 2. Verify that shadows and network colors work as expected. Run the extension and navigate around to ensure colors work as expected: 1. Pull this branch. 2. Run `yarn start`. 3. Navigate around the extension in light and dark mode to ensure colors work as expected. ## **Screenshots/Recordings** ### **Before** Checking the codebase for any existing deprecated colors returns many results in the `develop` branch. https://github.com/MetaMask/metamask-extension/assets/8112138/8ad23392-a0cc-4be5-8a50-12a126f5b71c ### **After** Checking the codebase for any deprecated removed colors that could break the UI using the provided script, after removing network colors, returns no results. https://github.com/MetaMask/metamask-extension/assets/8112138/3b82862d-5302-466f-bc55-bbb05a924ac0 Checking components that use updated CSS variables and newly added still work as expected https://github.com/MetaMask/metamask-extension/assets/8112138/b70279b9-07f3-4315-abcd-83090ca03eef Checking extension colors are working as expected in light and dark mode. In dark mode primary and error are a shade lighter in v4 https://github.com/MetaMask/metamask-extension/assets/8112138/d748f8cb-f2f2-4bd5-b9f4-f3bb376f22cd ## **Pre-merge author checklist** - [x] I’ve followed [MetaMask 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** - [ ] 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. --- .storybook/3.COLORS.stories.mdx | 10 ++--- .storybook/4.SHADOW.stories.mdx | 16 ++++---- package.json | 2 +- .../incoming-transaction-toggle.test.js.snap | 14 +++---- .../button-primary/button-primary.scss | 4 +- .../button-secondary/button-secondary.scss | 4 +- .../multichain/address-copy-button/index.scss | 2 +- .../multichain/ramps-card/index.scss | 6 +-- ui/components/ui/button/buttons.scss | 8 ++-- ui/css/base-styles.scss | 2 +- ui/css/utilities/colors.scss | 14 +++++-- .../advanced-tab.component.test.js.snap | 16 ++++---- .../developer-options-tab.test.tsx.snap | 4 +- .../__snapshots__/security-tab.test.js.snap | 40 +++++++++---------- ui/pages/unlock-page/index.scss | 5 +-- yarn.lock | 10 ++--- 16 files changed, 82 insertions(+), 75 deletions(-) diff --git a/.storybook/3.COLORS.stories.mdx b/.storybook/3.COLORS.stories.mdx index 506983c34df5..04f3c980d38c 100644 --- a/.storybook/3.COLORS.stories.mdx +++ b/.storybook/3.COLORS.stories.mdx @@ -18,7 +18,7 @@ We follow a 3 tiered system for color design tokens and css variables. <div style={{ textAlign: 'center', - backgroundColor: 'var(--brand-colors-white-white000)', + backgroundColor: 'var(--brand-colors-white)', padding: 32, }} > @@ -36,9 +36,9 @@ These colors **SHOULD NOT** be used in your styles directly. They are used as a ```css /** !!!DO NOT USE BRAND COLORS DIRECTLY IN YOUR CODE!!! */ -var(--brand-colors-white-white000) -var(--brand-colors-white-white010) -var(--brand-colors-grey-grey030) +var(--brand-colors-white) +var(--brand-colors-black) +var(--brand-colors-grey-grey800) ``` ### **Theme colors** (tier 2) @@ -168,7 +168,7 @@ Don't use static hex values or brand color tokens in your code. * Not theme compatible and will break UI when using dark theme **/ .card { - background-color: var(--brand-colors-white-white000); + background-color: var(--brand-colors-white); color: var(--brand-colors-grey-grey800); } ``` diff --git a/.storybook/4.SHADOW.stories.mdx b/.storybook/4.SHADOW.stories.mdx index 3401b8b0d964..95ecac5c3f15 100644 --- a/.storybook/4.SHADOW.stories.mdx +++ b/.storybook/4.SHADOW.stories.mdx @@ -115,7 +115,7 @@ As well as the neutral colors for shadow 2 other colors exist that are used for style={{ height: 100, backgroundColor: 'var(--color-primary-default)', - boxShadow: 'var(--shadow-size-lg) var(--color-primary-shadow)', + boxShadow: 'var(--shadow-size-lg) var(--color-shadow-primary)', borderRadius: '4px', display: 'grid', alignContent: 'center', @@ -129,7 +129,7 @@ As well as the neutral colors for shadow 2 other colors exist that are used for style={{ height: 100, backgroundColor: 'var(--color-error-default)', - boxShadow: 'var(--shadow-size-lg) var(--color-error-shadow)', + boxShadow: 'var(--shadow-size-lg) var(--color-shadow-error)', borderRadius: '4px', display: 'grid', alignContent: 'center', @@ -144,8 +144,8 @@ As well as the neutral colors for shadow 2 other colors exist that are used for | Color | CSS | | ----------- | ----------------------------- | | **neutral** | `var(--color-shadow-default)` | -| **primary** | `var(--color-primary-shadow)` | -| **danger** | `var(--color-error-shadow)` | +| **primary** | `var(--color-shadow-primary)` | +| **danger** | `var(--color-shadow-error)` | ## Example usage @@ -232,7 +232,7 @@ Using both size and color tokens, different shadows can be applied to components justifyContent: 'center', height: 100, textAlign: 'center', - boxShadow: 'var(--shadow-size-sm) var(--color-primary-shadow)', + boxShadow: 'var(--shadow-size-sm) var(--color-shadow-primary)', backgroundColor: 'var(--color-primary-default)', color: 'var(--color-primary-inverse)', }} @@ -247,7 +247,7 @@ Using both size and color tokens, different shadows can be applied to components justifyContent: 'center', height: 100, textAlign: 'center', - boxShadow: 'var(--shadow-size-sm) var(--color-error-shadow)', + boxShadow: 'var(--shadow-size-sm) var(--color-shadow-error)', backgroundColor: 'var(--color-error-default)', color: 'var(--color-error-inverse)', }} @@ -263,8 +263,8 @@ Using both size and color tokens, different shadows can be applied to components | **Dropdown** | `box-shadow: var(--shadow-size-sm) var(--color-shadow-default);` | | **Toast** | `box-shadow: var(--shadow-size-md) var(--color-shadow-default);` | | **Modal** | `box-shadow: var(--shadow-size-lg) var(--color-shadow-default);` | -| **Button Primary Hover** | `box-shadow: var(--shadow-size-sm) var(--color-primary-shadow);` | -| **Button Danger Hover** | `box-shadow: var(--shadow-size-sm) var(--color-error-shadow);` | +| **Button Primary Hover** | `box-shadow: var(--shadow-size-sm) var(--color-shadow-primary);` | +| **Button Danger Hover** | `box-shadow: var(--shadow-size-sm) var(--color-shadow-error);` | ## Takeaways diff --git a/package.json b/package.json index b4ece58b2e55..2d9836b3c06e 100644 --- a/package.json +++ b/package.json @@ -290,7 +290,7 @@ "@metamask/browser-passworder": "^4.3.0", "@metamask/contract-metadata": "^2.5.0", "@metamask/controller-utils": "^10.0.0", - "@metamask/design-tokens": "^3.0.0", + "@metamask/design-tokens": "^4.0.0", "@metamask/ens-controller": "^10.0.1", "@metamask/eth-json-rpc-filters": "^7.0.0", "@metamask/eth-json-rpc-middleware": "^12.1.1", 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 0f6521e00795..faf8faf6dedf 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 @@ -63,7 +63,7 @@ exports[`IncomingTransactionToggle should render existing incoming transaction p style="display: flex; width: 52px; align-items: center; justify-content: flex-start; position: relative; cursor: pointer; background-color: transparent; border: 0px; padding: 0px; user-select: none;" > <div - style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(16, 152, 252);" + style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(67, 174, 252);" > <div style="font-size: 11px; display: flex; align-items: center; justify-content: center; font-family: 'Helvetica Neue', Helvetica, sans-serif; position: relative; color: rgb(250, 250, 250); margin-top: auto; margin-bottom: auto; line-height: 0; opacity: 1; width: 26px; height: 20px; left: 4px;" @@ -150,7 +150,7 @@ exports[`IncomingTransactionToggle should render existing incoming transaction p style="display: flex; width: 52px; align-items: center; justify-content: flex-start; position: relative; cursor: pointer; background-color: transparent; border: 0px; padding: 0px; user-select: none;" > <div - style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(159, 166, 174);" + style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(132, 140, 150);" > <div style="font-size: 11px; display: flex; align-items: center; justify-content: center; font-family: 'Helvetica Neue', Helvetica, sans-serif; position: relative; color: rgb(250, 250, 250); margin-top: auto; margin-bottom: auto; line-height: 0; opacity: 0; width: 26px; height: 20px; left: 4px;" @@ -233,7 +233,7 @@ exports[`IncomingTransactionToggle should render existing incoming transaction p style="display: flex; width: 52px; align-items: center; justify-content: flex-start; position: relative; cursor: pointer; background-color: transparent; border: 0px; padding: 0px; user-select: none;" > <div - style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(16, 152, 252);" + style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(67, 174, 252);" > <div style="font-size: 11px; display: flex; align-items: center; justify-content: center; font-family: 'Helvetica Neue', Helvetica, sans-serif; position: relative; color: rgb(250, 250, 250); margin-top: auto; margin-bottom: auto; line-height: 0; opacity: 1; width: 26px; height: 20px; left: 4px;" @@ -316,7 +316,7 @@ exports[`IncomingTransactionToggle should render existing incoming transaction p style="display: flex; width: 52px; align-items: center; justify-content: flex-start; position: relative; cursor: pointer; background-color: transparent; border: 0px; padding: 0px; user-select: none;" > <div - style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(159, 166, 174);" + style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(132, 140, 150);" > <div style="font-size: 11px; display: flex; align-items: center; justify-content: center; font-family: 'Helvetica Neue', Helvetica, sans-serif; position: relative; color: rgb(250, 250, 250); margin-top: auto; margin-bottom: auto; line-height: 0; opacity: 0; width: 26px; height: 20px; left: 4px;" @@ -399,7 +399,7 @@ exports[`IncomingTransactionToggle should render existing incoming transaction p style="display: flex; width: 52px; align-items: center; justify-content: flex-start; position: relative; cursor: pointer; background-color: transparent; border: 0px; padding: 0px; user-select: none;" > <div - style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(16, 152, 252);" + style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(67, 174, 252);" > <div style="font-size: 11px; display: flex; align-items: center; justify-content: center; font-family: 'Helvetica Neue', Helvetica, sans-serif; position: relative; color: rgb(250, 250, 250); margin-top: auto; margin-bottom: auto; line-height: 0; opacity: 1; width: 26px; height: 20px; left: 4px;" @@ -486,7 +486,7 @@ exports[`IncomingTransactionToggle should render existing incoming transaction p style="display: flex; width: 52px; align-items: center; justify-content: flex-start; position: relative; cursor: pointer; background-color: transparent; border: 0px; padding: 0px; user-select: none;" > <div - style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(16, 152, 252);" + style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(67, 174, 252);" > <div style="font-size: 11px; display: flex; align-items: center; justify-content: center; font-family: 'Helvetica Neue', Helvetica, sans-serif; position: relative; color: rgb(250, 250, 250); margin-top: auto; margin-bottom: auto; line-height: 0; opacity: 1; width: 26px; height: 20px; left: 4px;" @@ -573,7 +573,7 @@ exports[`IncomingTransactionToggle should render existing incoming transaction p style="display: flex; width: 52px; align-items: center; justify-content: flex-start; position: relative; cursor: pointer; background-color: transparent; border: 0px; padding: 0px; user-select: none;" > <div - style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(16, 152, 252);" + style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(67, 174, 252);" > <div style="font-size: 11px; display: flex; align-items: center; justify-content: center; font-family: 'Helvetica Neue', Helvetica, sans-serif; position: relative; color: rgb(250, 250, 250); margin-top: auto; margin-bottom: auto; line-height: 0; opacity: 1; width: 26px; height: 20px; left: 4px;" diff --git a/ui/components/component-library/button-primary/button-primary.scss b/ui/components/component-library/button-primary/button-primary.scss index 2ffe80992027..ef3eb3e7947c 100644 --- a/ui/components/component-library/button-primary/button-primary.scss +++ b/ui/components/component-library/button-primary/button-primary.scss @@ -3,7 +3,7 @@ color: var(--color-primary-inverse); box-shadow: var(--shadow-size-sm) - var(--color-primary-shadow); + var(--color-shadow-primary); } &:active { @@ -17,7 +17,7 @@ color: var(--color-error-inverse); box-shadow: var(--shadow-size-sm) - var(--color-error-shadow); + var(--color-shadow-error); } &:active { diff --git a/ui/components/component-library/button-secondary/button-secondary.scss b/ui/components/component-library/button-secondary/button-secondary.scss index 3a0c43d17526..89530f3eb0a5 100644 --- a/ui/components/component-library/button-secondary/button-secondary.scss +++ b/ui/components/component-library/button-secondary/button-secondary.scss @@ -4,7 +4,7 @@ background-color: var(--color-primary-default); box-shadow: var(--shadow-size-sm) - var(--color-primary-shadow);; + var(--color-shadow-primary);; } &:active { @@ -24,7 +24,7 @@ background-color: var(--color-error-default); box-shadow: var(--shadow-size-sm) - var(--color-error-shadow); + var(--color-shadow-error); } &:active { diff --git a/ui/components/multichain/address-copy-button/index.scss b/ui/components/multichain/address-copy-button/index.scss index 4d1b84b2898c..ceb47dbdc5bc 100644 --- a/ui/components/multichain/address-copy-button/index.scss +++ b/ui/components/multichain/address-copy-button/index.scss @@ -8,7 +8,7 @@ .custody-logo { height: 20px; - background-color: var(--brand-colors-white-white000); + background-color: var(--brand-colors-white); padding: 1px; border-radius: 5px; } diff --git a/ui/components/multichain/ramps-card/index.scss b/ui/components/multichain/ramps-card/index.scss index a394311eeda9..d46a317e9357 100644 --- a/ui/components/multichain/ramps-card/index.scss +++ b/ui/components/multichain/ramps-card/index.scss @@ -4,15 +4,15 @@ &__cta-button { width: fit-content; color: var(--brand-colors-grey-grey800); - background-color: var(--brand-colors-white-white000); + background-color: var(--brand-colors-white); } &__title { - color: var(--brand-colors-white-white000); + color: var(--brand-colors-white); } &__body { - color: var(--brand-colors-white-white000); + color: var(--brand-colors-white); width: 80%; } } diff --git a/ui/components/ui/button/buttons.scss b/ui/components/ui/button/buttons.scss index 6c6fe36bd8e7..8594cb04f4da 100644 --- a/ui/components/ui/button/buttons.scss +++ b/ui/components/ui/button/buttons.scss @@ -196,7 +196,7 @@ input[type="submit"][disabled] { border: 1px solid var(--color-primary-default); &:hover { - box-shadow: var(--shadow-size-sm) var(--color-primary-shadow); + box-shadow: var(--shadow-size-sm) var(--color-shadow-primary); } &:active { @@ -220,7 +220,7 @@ input[type="submit"][disabled] { border: 1px solid var(--color-error-default); &:hover { - box-shadow: var(--shadow-size-sm) var(--color-error-shadow); + box-shadow: var(--shadow-size-sm) var(--color-shadow-error); } &:active { @@ -250,7 +250,7 @@ input[type="submit"][disabled] { background-color: var(--color-primary-default); &:hover { - box-shadow: var(--shadow-size-sm) var(--color-primary-shadow); + box-shadow: var(--shadow-size-sm) var(--color-shadow-primary); } &:active { @@ -262,7 +262,7 @@ input[type="submit"][disabled] { background-color: var(--color-error-default); &:hover { - box-shadow: var(--shadow-size-sm) var(--color-error-shadow); + box-shadow: var(--shadow-size-sm) var(--color-shadow-error); } &:active { diff --git a/ui/css/base-styles.scss b/ui/css/base-styles.scss index 048a0d69040b..a98e965e74b8 100644 --- a/ui/css/base-styles.scss +++ b/ui/css/base-styles.scss @@ -31,7 +31,7 @@ html { */ @media (prefers-color-scheme: dark) { &:not([data-theme]) { - color: var(--brand-colors-white-white000); + color: var(--brand-colors-white); background-color: var(--brand-colors-grey-grey900); } diff --git a/ui/css/utilities/colors.scss b/ui/css/utilities/colors.scss index facb925e431c..634a484d736a 100644 --- a/ui/css/utilities/colors.scss +++ b/ui/css/utilities/colors.scss @@ -9,13 +9,21 @@ Before adding a color here make sure that there isn't a design token available. --mainnet: #29b6af; --inherit: inherit; --transparent: transparent; + // DO NOT CHANGE + // Required for the QR reader to work properly + --qr-code-white-background: #fff; + // DEPRECATED + // These third party network colors have been deprecated and should be removed once they are no longer in use. We should be using images to represent these networks instead. + --color-network-goerli-default: #1098fc; + --color-network-sepolia-default: #cfb5f0; + --color-network-goerli-inverse: #fcfcfc; + --color-network-sepolia-inverse: #fcfcfc; + --color-network-localhost-default: #bbc0c5; + --color-network-localhost-inverse: #fcfcfc; --color-network-linea-goerli-default: #61dfff; --color-network-linea-goerli-inverse: #fcfcfc; --color-network-linea-sepolia-default: #61dfff; --color-network-linea-sepolia-inverse: #fcfcfc; --color-network-linea-mainnet-default: #121212; --color-network-linea-mainnet-inverse: #fcfcfc; - // DO NOT CHANGE - // Required for the QR reader to work properly - --qr-code-white-background: #fff; } diff --git a/ui/pages/settings/advanced-tab/__snapshots__/advanced-tab.component.test.js.snap b/ui/pages/settings/advanced-tab/__snapshots__/advanced-tab.component.test.js.snap index 39f2a38c9f88..72c46529b4db 100644 --- a/ui/pages/settings/advanced-tab/__snapshots__/advanced-tab.component.test.js.snap +++ b/ui/pages/settings/advanced-tab/__snapshots__/advanced-tab.component.test.js.snap @@ -105,7 +105,7 @@ exports[`AdvancedTab Component should match snapshot 1`] = ` style="display: flex; width: 52px; align-items: center; justify-content: flex-start; position: relative; cursor: pointer; background-color: transparent; border: 0px; padding: 0px; user-select: none;" > <div - style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(159, 166, 174);" + style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(132, 140, 150);" > <div style="font-size: 11px; display: flex; align-items: center; justify-content: center; font-family: 'Helvetica Neue', Helvetica, sans-serif; position: relative; color: rgb(250, 250, 250); margin-top: auto; margin-bottom: auto; line-height: 0; opacity: 0; width: 26px; height: 20px; left: 4px;" @@ -172,7 +172,7 @@ exports[`AdvancedTab Component should match snapshot 1`] = ` style="display: flex; width: 52px; align-items: center; justify-content: flex-start; position: relative; cursor: pointer; background-color: transparent; border: 0px; padding: 0px; user-select: none;" > <div - style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(159, 166, 174);" + style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(132, 140, 150);" > <div style="font-size: 11px; display: flex; align-items: center; justify-content: center; font-family: 'Helvetica Neue', Helvetica, sans-serif; position: relative; color: rgb(250, 250, 250); margin-top: auto; margin-bottom: auto; line-height: 0; opacity: 0; width: 26px; height: 20px; left: 4px;" @@ -238,7 +238,7 @@ exports[`AdvancedTab Component should match snapshot 1`] = ` style="display: flex; width: 52px; align-items: center; justify-content: flex-start; position: relative; cursor: pointer; background-color: transparent; border: 0px; padding: 0px; user-select: none;" > <div - style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(159, 166, 174);" + style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(132, 140, 150);" > <div style="font-size: 11px; display: flex; align-items: center; justify-content: center; font-family: 'Helvetica Neue', Helvetica, sans-serif; position: relative; color: rgb(250, 250, 250); margin-top: auto; margin-bottom: auto; line-height: 0; opacity: 0; width: 26px; height: 20px; left: 4px;" @@ -304,7 +304,7 @@ exports[`AdvancedTab Component should match snapshot 1`] = ` style="display: flex; width: 52px; align-items: center; justify-content: flex-start; position: relative; cursor: pointer; background-color: transparent; border: 0px; padding: 0px; user-select: none;" > <div - style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(16, 152, 252);" + style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(67, 174, 252);" > <div style="font-size: 11px; display: flex; align-items: center; justify-content: center; font-family: 'Helvetica Neue', Helvetica, sans-serif; position: relative; color: rgb(250, 250, 250); margin-top: auto; margin-bottom: auto; line-height: 0; opacity: 1; width: 26px; height: 20px; left: 4px;" @@ -370,7 +370,7 @@ exports[`AdvancedTab Component should match snapshot 1`] = ` style="display: flex; width: 52px; align-items: center; justify-content: flex-start; position: relative; cursor: pointer; background-color: transparent; border: 0px; padding: 0px; user-select: none;" > <div - style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(159, 166, 174);" + style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(132, 140, 150);" > <div style="font-size: 11px; display: flex; align-items: center; justify-content: center; font-family: 'Helvetica Neue', Helvetica, sans-serif; position: relative; color: rgb(250, 250, 250); margin-top: auto; margin-bottom: auto; line-height: 0; opacity: 0; width: 26px; height: 20px; left: 4px;" @@ -436,7 +436,7 @@ exports[`AdvancedTab Component should match snapshot 1`] = ` style="display: flex; width: 52px; align-items: center; justify-content: flex-start; position: relative; cursor: pointer; background-color: transparent; border: 0px; padding: 0px; user-select: none;" > <div - style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(159, 166, 174);" + style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(132, 140, 150);" > <div style="font-size: 11px; display: flex; align-items: center; justify-content: center; font-family: 'Helvetica Neue', Helvetica, sans-serif; position: relative; color: rgb(250, 250, 250); margin-top: auto; margin-bottom: auto; line-height: 0; opacity: 0; width: 26px; height: 20px; left: 4px;" @@ -622,7 +622,7 @@ exports[`AdvancedTab Component should match snapshot 1`] = ` style="display: flex; width: 52px; align-items: center; justify-content: flex-start; position: relative; cursor: pointer; background-color: transparent; border: 0px; padding: 0px; user-select: none;" > <div - style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(159, 166, 174);" + style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(132, 140, 150);" > <div style="font-size: 11px; display: flex; align-items: center; justify-content: center; font-family: 'Helvetica Neue', Helvetica, sans-serif; position: relative; color: rgb(250, 250, 250); margin-top: auto; margin-bottom: auto; line-height: 0; opacity: 0; width: 26px; height: 20px; left: 4px;" @@ -688,7 +688,7 @@ exports[`AdvancedTab Component should match snapshot 1`] = ` style="display: flex; width: 52px; align-items: center; justify-content: flex-start; position: relative; cursor: pointer; background-color: transparent; border: 0px; padding: 0px; user-select: none;" > <div - style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(159, 166, 174);" + style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(132, 140, 150);" > <div style="font-size: 11px; display: flex; align-items: center; justify-content: center; font-family: 'Helvetica Neue', Helvetica, sans-serif; position: relative; color: rgb(250, 250, 250); margin-top: auto; margin-bottom: auto; line-height: 0; opacity: 0; width: 26px; height: 20px; left: 4px;" 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 ad9599831cd8..ff16d1cf726d 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 @@ -127,7 +127,7 @@ exports[`Develop options tab should match snapshot 1`] = ` style="display: flex; width: 52px; align-items: center; justify-content: flex-start; position: relative; cursor: pointer; background-color: transparent; border: 0px; padding: 0px; user-select: none;" > <div - style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(16, 152, 252);" + style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(67, 174, 252);" > <div style="font-size: 11px; display: flex; align-items: center; justify-content: center; font-family: 'Helvetica Neue', Helvetica, sans-serif; position: relative; color: rgb(250, 250, 250); margin-top: auto; margin-bottom: auto; line-height: 0; opacity: 1; width: 26px; height: 20px; left: 4px;" @@ -197,7 +197,7 @@ exports[`Develop options tab should match snapshot 1`] = ` style="display: flex; width: 52px; align-items: center; justify-content: flex-start; position: relative; cursor: pointer; background-color: transparent; border: 0px; padding: 0px; user-select: none;" > <div - style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(159, 166, 174);" + style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(132, 140, 150);" > <div style="font-size: 11px; display: flex; align-items: center; justify-content: center; font-family: 'Helvetica Neue', Helvetica, sans-serif; position: relative; color: rgb(250, 250, 250); margin-top: auto; margin-bottom: auto; line-height: 0; opacity: 0; width: 26px; height: 20px; left: 4px;" diff --git a/ui/pages/settings/security-tab/__snapshots__/security-tab.test.js.snap b/ui/pages/settings/security-tab/__snapshots__/security-tab.test.js.snap index ab4fc6467faa..d2d94434223a 100644 --- a/ui/pages/settings/security-tab/__snapshots__/security-tab.test.js.snap +++ b/ui/pages/settings/security-tab/__snapshots__/security-tab.test.js.snap @@ -44,7 +44,7 @@ exports[`Security Tab should match snapshot 1`] = ` style="display: flex; width: 52px; align-items: center; justify-content: flex-start; position: relative; cursor: pointer; background-color: transparent; border: 0px; padding: 0px; user-select: none;" > <div - style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(159, 166, 174);" + style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(132, 140, 150);" > <div style="font-size: 11px; display: flex; align-items: center; justify-content: center; font-family: 'Helvetica Neue', Helvetica, sans-serif; position: relative; color: rgb(250, 250, 250); margin-top: auto; margin-bottom: auto; line-height: 0; opacity: 0; width: 26px; height: 20px; left: 4px;" @@ -155,7 +155,7 @@ exports[`Security Tab should match snapshot 1`] = ` style="display: flex; width: 52px; align-items: center; justify-content: flex-start; position: relative; cursor: pointer; background-color: transparent; border: 0px; padding: 0px; user-select: none;" > <div - style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(159, 166, 174);" + style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(132, 140, 150);" > <div style="font-size: 11px; display: flex; align-items: center; justify-content: center; font-family: 'Helvetica Neue', Helvetica, sans-serif; position: relative; color: rgb(250, 250, 250); margin-top: auto; margin-bottom: auto; line-height: 0; opacity: 0; width: 26px; height: 20px; left: 4px;" @@ -248,7 +248,7 @@ exports[`Security Tab should match snapshot 1`] = ` style="display: flex; width: 52px; align-items: center; justify-content: flex-start; position: relative; cursor: pointer; background-color: transparent; border: 0px; padding: 0px; user-select: none;" > <div - style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(159, 166, 174);" + style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(132, 140, 150);" > <div style="font-size: 11px; display: flex; align-items: center; justify-content: center; font-family: 'Helvetica Neue', Helvetica, sans-serif; position: relative; color: rgb(250, 250, 250); margin-top: auto; margin-bottom: auto; line-height: 0; opacity: 0; width: 26px; height: 20px; left: 4px;" @@ -327,7 +327,7 @@ exports[`Security Tab should match snapshot 1`] = ` style="display: flex; width: 52px; align-items: center; justify-content: flex-start; position: relative; cursor: pointer; background-color: transparent; border: 0px; padding: 0px; user-select: none;" > <div - style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(16, 152, 252);" + style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(67, 174, 252);" > <div style="font-size: 11px; display: flex; align-items: center; justify-content: center; font-family: 'Helvetica Neue', Helvetica, sans-serif; position: relative; color: rgb(250, 250, 250); margin-top: auto; margin-bottom: auto; line-height: 0; opacity: 1; width: 26px; height: 20px; left: 4px;" @@ -404,7 +404,7 @@ exports[`Security Tab should match snapshot 1`] = ` style="display: flex; width: 52px; align-items: center; justify-content: flex-start; position: relative; cursor: pointer; background-color: transparent; border: 0px; padding: 0px; user-select: none;" > <div - style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(16, 152, 252);" + style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(67, 174, 252);" > <div style="font-size: 11px; display: flex; align-items: center; justify-content: center; font-family: 'Helvetica Neue', Helvetica, sans-serif; position: relative; color: rgb(250, 250, 250); margin-top: auto; margin-bottom: auto; line-height: 0; opacity: 1; width: 26px; height: 20px; left: 4px;" @@ -507,7 +507,7 @@ exports[`Security Tab should match snapshot 1`] = ` style="display: flex; width: 52px; align-items: center; justify-content: flex-start; position: relative; cursor: pointer; background-color: transparent; border: 0px; padding: 0px; user-select: none;" > <div - style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(16, 152, 252);" + style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(67, 174, 252);" > <div style="font-size: 11px; display: flex; align-items: center; justify-content: center; font-family: 'Helvetica Neue', Helvetica, sans-serif; position: relative; color: rgb(250, 250, 250); margin-top: auto; margin-bottom: auto; line-height: 0; opacity: 1; width: 26px; height: 20px; left: 4px;" @@ -607,7 +607,7 @@ exports[`Security Tab should match snapshot 1`] = ` style="display: flex; width: 52px; align-items: center; justify-content: flex-start; position: relative; cursor: pointer; background-color: transparent; border: 0px; padding: 0px; user-select: none;" > <div - style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(16, 152, 252);" + style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(67, 174, 252);" > <div style="font-size: 11px; display: flex; align-items: center; justify-content: center; font-family: 'Helvetica Neue', Helvetica, sans-serif; position: relative; color: rgb(250, 250, 250); margin-top: auto; margin-bottom: auto; line-height: 0; opacity: 1; width: 26px; height: 20px; left: 4px;" @@ -694,7 +694,7 @@ exports[`Security Tab should match snapshot 1`] = ` style="display: flex; width: 52px; align-items: center; justify-content: flex-start; position: relative; cursor: pointer; background-color: transparent; border: 0px; padding: 0px; user-select: none;" > <div - style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(159, 166, 174);" + style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(132, 140, 150);" > <div style="font-size: 11px; display: flex; align-items: center; justify-content: center; font-family: 'Helvetica Neue', Helvetica, sans-serif; position: relative; color: rgb(250, 250, 250); margin-top: auto; margin-bottom: auto; line-height: 0; opacity: 0; width: 26px; height: 20px; left: 4px;" @@ -777,7 +777,7 @@ exports[`Security Tab should match snapshot 1`] = ` style="display: flex; width: 52px; align-items: center; justify-content: flex-start; position: relative; cursor: pointer; background-color: transparent; border: 0px; padding: 0px; user-select: none;" > <div - style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(159, 166, 174);" + style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(132, 140, 150);" > <div style="font-size: 11px; display: flex; align-items: center; justify-content: center; font-family: 'Helvetica Neue', Helvetica, sans-serif; position: relative; color: rgb(250, 250, 250); margin-top: auto; margin-bottom: auto; line-height: 0; opacity: 0; width: 26px; height: 20px; left: 4px;" @@ -860,7 +860,7 @@ exports[`Security Tab should match snapshot 1`] = ` style="display: flex; width: 52px; align-items: center; justify-content: flex-start; position: relative; cursor: pointer; background-color: transparent; border: 0px; padding: 0px; user-select: none;" > <div - style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(16, 152, 252);" + style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(67, 174, 252);" > <div style="font-size: 11px; display: flex; align-items: center; justify-content: center; font-family: 'Helvetica Neue', Helvetica, sans-serif; position: relative; color: rgb(250, 250, 250); margin-top: auto; margin-bottom: auto; line-height: 0; opacity: 1; width: 26px; height: 20px; left: 4px;" @@ -947,7 +947,7 @@ exports[`Security Tab should match snapshot 1`] = ` style="display: flex; width: 52px; align-items: center; justify-content: flex-start; position: relative; cursor: pointer; background-color: transparent; border: 0px; padding: 0px; user-select: none;" > <div - style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(159, 166, 174);" + style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(132, 140, 150);" > <div style="font-size: 11px; display: flex; align-items: center; justify-content: center; font-family: 'Helvetica Neue', Helvetica, sans-serif; position: relative; color: rgb(250, 250, 250); margin-top: auto; margin-bottom: auto; line-height: 0; opacity: 0; width: 26px; height: 20px; left: 4px;" @@ -1026,7 +1026,7 @@ exports[`Security Tab should match snapshot 1`] = ` style="display: flex; width: 52px; align-items: center; justify-content: flex-start; position: relative; cursor: pointer; background-color: transparent; border: 0px; padding: 0px; user-select: none;" > <div - style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(159, 166, 174);" + style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(132, 140, 150);" > <div style="font-size: 11px; display: flex; align-items: center; justify-content: center; font-family: 'Helvetica Neue', Helvetica, sans-serif; position: relative; color: rgb(250, 250, 250); margin-top: auto; margin-bottom: auto; line-height: 0; opacity: 0; width: 26px; height: 20px; left: 4px;" @@ -1152,7 +1152,7 @@ exports[`Security Tab should match snapshot 1`] = ` style="display: flex; width: 52px; align-items: center; justify-content: flex-start; position: relative; cursor: pointer; background-color: transparent; border: 0px; padding: 0px; user-select: none;" > <div - style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(159, 166, 174);" + style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(132, 140, 150);" > <div style="font-size: 11px; display: flex; align-items: center; justify-content: center; font-family: 'Helvetica Neue', Helvetica, sans-serif; position: relative; color: rgb(250, 250, 250); margin-top: auto; margin-bottom: auto; line-height: 0; opacity: 0; width: 26px; height: 20px; left: 4px;" @@ -1223,7 +1223,7 @@ exports[`Security Tab should match snapshot 1`] = ` style="display: flex; width: 52px; align-items: center; justify-content: flex-start; position: relative; cursor: pointer; background-color: transparent; border: 0px; padding: 0px; user-select: none;" > <div - style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(16, 152, 252);" + style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(67, 174, 252);" > <div style="font-size: 11px; display: flex; align-items: center; justify-content: center; font-family: 'Helvetica Neue', Helvetica, sans-serif; position: relative; color: rgb(250, 250, 250); margin-top: auto; margin-bottom: auto; line-height: 0; opacity: 1; width: 26px; height: 20px; left: 4px;" @@ -1337,7 +1337,7 @@ exports[`Security Tab should match snapshot 1`] = ` style="display: flex; width: 52px; align-items: center; justify-content: flex-start; position: relative; cursor: pointer; background-color: transparent; border: 0px; padding: 0px; user-select: none;" > <div - style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(159, 166, 174);" + style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(132, 140, 150);" > <div style="font-size: 11px; display: flex; align-items: center; justify-content: center; font-family: 'Helvetica Neue', Helvetica, sans-serif; position: relative; color: rgb(250, 250, 250); margin-top: auto; margin-bottom: auto; line-height: 0; opacity: 0; width: 26px; height: 20px; left: 4px;" @@ -1427,7 +1427,7 @@ exports[`Security Tab should match snapshot 1`] = ` style="display: flex; width: 52px; align-items: center; justify-content: flex-start; position: relative; cursor: pointer; background-color: transparent; border: 0px; padding: 0px; user-select: none;" > <div - style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(16, 152, 252);" + style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(67, 174, 252);" > <div style="font-size: 11px; display: flex; align-items: center; justify-content: center; font-family: 'Helvetica Neue', Helvetica, sans-serif; position: relative; color: rgb(250, 250, 250); margin-top: auto; margin-bottom: auto; line-height: 0; opacity: 1; width: 26px; height: 20px; left: 4px;" @@ -1493,7 +1493,7 @@ exports[`Security Tab should match snapshot 1`] = ` style="display: flex; width: 52px; align-items: center; justify-content: flex-start; position: relative; cursor: pointer; background-color: transparent; border: 0px; padding: 0px; user-select: none;" > <div - style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(159, 166, 174);" + style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(132, 140, 150);" > <div style="font-size: 11px; display: flex; align-items: center; justify-content: center; font-family: 'Helvetica Neue', Helvetica, sans-serif; position: relative; color: rgb(250, 250, 250); margin-top: auto; margin-bottom: auto; line-height: 0; opacity: 0; width: 26px; height: 20px; left: 4px;" @@ -1560,7 +1560,7 @@ exports[`Security Tab should match snapshot 1`] = ` style="display: flex; width: 52px; align-items: center; justify-content: flex-start; position: relative; cursor: pointer; background-color: transparent; border: 0px; padding: 0px; user-select: none;" > <div - style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(16, 152, 252);" + style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(67, 174, 252);" > <div style="font-size: 11px; display: flex; align-items: center; justify-content: center; font-family: 'Helvetica Neue', Helvetica, sans-serif; position: relative; color: rgb(250, 250, 250); margin-top: auto; margin-bottom: auto; line-height: 0; opacity: 1; width: 26px; height: 20px; left: 4px;" @@ -1626,7 +1626,7 @@ exports[`Security Tab should match snapshot 1`] = ` style="display: flex; width: 52px; align-items: center; justify-content: flex-start; position: relative; cursor: pointer; background-color: transparent; border: 0px; padding: 0px; user-select: none;" > <div - style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(16, 152, 252);" + style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(67, 174, 252);" > <div style="font-size: 11px; display: flex; align-items: center; justify-content: center; font-family: 'Helvetica Neue', Helvetica, sans-serif; position: relative; color: rgb(250, 250, 250); margin-top: auto; margin-bottom: auto; line-height: 0; opacity: 1; width: 26px; height: 20px; left: 4px;" @@ -1707,7 +1707,7 @@ exports[`Security Tab should match snapshot 1`] = ` style="display: flex; width: 52px; align-items: center; justify-content: flex-start; position: relative; cursor: pointer; background-color: transparent; border: 0px; padding: 0px; user-select: none;" > <div - style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(159, 166, 174);" + style="width: 40px; height: 24px; padding: 0px; border-radius: 26px; display: flex; align-items: center; justify-content: center; background-color: rgb(132, 140, 150);" > <div style="font-size: 11px; display: flex; align-items: center; justify-content: center; font-family: 'Helvetica Neue', Helvetica, sans-serif; position: relative; color: rgb(250, 250, 250); margin-top: auto; margin-bottom: auto; line-height: 0; opacity: 0; width: 26px; height: 20px; left: 4px;" diff --git a/ui/pages/unlock-page/index.scss b/ui/pages/unlock-page/index.scss index 44081cc81b05..abb1f837bdef 100644 --- a/ui/pages/unlock-page/index.scss +++ b/ui/pages/unlock-page/index.scss @@ -23,9 +23,8 @@ position: relative; &__beta { - /* these colors should be used on both light and dark mode */ - background: var(--brand-colors-blue-blue500); - color: var(--brand-colors-white-white000); + background: var(--color-primary-default); + color: var(--color-primary-inverse); padding: 3px 6px; font-size: 16px; position: absolute; diff --git a/yarn.lock b/yarn.lock index c332e4141c2b..cf50e0a9db6d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4975,10 +4975,10 @@ __metadata: languageName: node linkType: hard -"@metamask/design-tokens@npm:^3.0.0": - version: 3.0.0 - resolution: "@metamask/design-tokens@npm:3.0.0" - checksum: 10/65c809fb5877398a0e45f3a22c09c8f7b64972961497f465a4c85eb6492e7e7d1168d4718cd21442dfb54a1f4b79d0287c6d5f5f5faa652dfcc5ababb1894095 +"@metamask/design-tokens@npm:^4.0.0": + version: 4.0.0 + resolution: "@metamask/design-tokens@npm:4.0.0" + checksum: 10/337968d86bf963ccdf7ab416cc8f87ec1d35d9fb56f686dea954964edd6f5cb0067a920cb2c1f9008150d3decac51cb1b392b3e67dc1d46ca308b503ffe7eabd languageName: node linkType: hard @@ -24877,7 +24877,7 @@ __metadata: "@metamask/build-utils": "npm:^1.0.0" "@metamask/contract-metadata": "npm:^2.5.0" "@metamask/controller-utils": "npm:^10.0.0" - "@metamask/design-tokens": "npm:^3.0.0" + "@metamask/design-tokens": "npm:^4.0.0" "@metamask/ens-controller": "npm:^10.0.1" "@metamask/eslint-config": "npm:^9.0.0" "@metamask/eslint-config-jest": "npm:^9.0.0" From 4f7f8f0322927d9968d0608e9ef7ca4e338d2973 Mon Sep 17 00:00:00 2001 From: salimtb <salim.toubal@outlook.com> Date: Tue, 11 Jun 2024 20:09:03 +0200 Subject: [PATCH 09/12] feat: add popular network list modal (#25160) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit <!-- Please submit this PR as a draft initially. Do not mark it as "Ready for review" until the template has been completely filled out, and PR status checks have passed at least once. --> ## **Description** This PR introduces the addition of a Popular Networks list to the Add Network modal. <!-- Write a short description of the changes included in this pull request, also include relevant motivation and context. Have in mind the following questions: 1. What is the reason for the change? 2. What is the improvement/solution? --> [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/25160?quickstart=1) ## **Related issues** Fixes: ## **Manual testing steps** 1. Run `yarn && ENABLE_NETWORK_UI_REDESIGN=1 yarn start` 2. Go to Settings -> Developer Options 3. Tun on the network new toggle 4. Go to the wallet page 5. Click on the network button ( see the video below ) 6. you should see the list of popular network 7. you should be able to add a network from the modal ## **Screenshots/Recordings** <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> ### **Before** https://github.com/MetaMask/metamask-extension/assets/26223211/bde545ef-c5a3-4cc5-9508-16edfd8507dd ### **After** https://github.com/MetaMask/metamask-extension/assets/26223211/114076c2-e336-4b96-b061-defd6d886380 ## **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** - [ ] 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: David Walsh <davidwalsh83@gmail.com> --- app/_locales/en/messages.json | 3 + shared/constants/metametrics.ts | 1 + ...network-confirmation-popover.test.tsx.snap | 3 + .../network-confirmation-popover.test.tsx | 45 +++++++ .../network-confirmation-popover.tsx | 40 ++++++ .../network-list-menu/network-list-menu.js | 72 +++++++--- .../network-list-menu.test.js | 10 ++ .../PopularNetworkList.stories.js | 85 ++++++++++++ .../popular-network-list.test.tsx.snap | 13 ++ .../popular-network-list.test.tsx | 113 ++++++++++++++++ .../popular-network-list.tsx | 125 ++++++++++++++++++ ui/pages/routes/routes.component.js | 4 + ui/pages/routes/routes.component.test.js | 5 + ui/pages/routes/routes.container.js | 2 + 14 files changed, 501 insertions(+), 20 deletions(-) create mode 100644 ui/components/multichain/network-list-menu/network-confirmation-popover/__snapshots__/network-confirmation-popover.test.tsx.snap create mode 100644 ui/components/multichain/network-list-menu/network-confirmation-popover/network-confirmation-popover.test.tsx create mode 100644 ui/components/multichain/network-list-menu/network-confirmation-popover/network-confirmation-popover.tsx create mode 100644 ui/components/multichain/network-list-menu/popular-network-list/PopularNetworkList.stories.js create mode 100644 ui/components/multichain/network-list-menu/popular-network-list/__snapshots__/popular-network-list.test.tsx.snap create mode 100644 ui/components/multichain/network-list-menu/popular-network-list/popular-network-list.test.tsx create mode 100644 ui/components/multichain/network-list-menu/popular-network-list/popular-network-list.tsx diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 4ac143e9554b..44434c77b44a 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -314,6 +314,9 @@ "addingTokens": { "message": "Adding tokens" }, + "additionalNetworks": { + "message": "Additional networks" + }, "address": { "message": "Address" }, diff --git a/shared/constants/metametrics.ts b/shared/constants/metametrics.ts index 0e46bcf5046d..ed07ad3a4b58 100644 --- a/shared/constants/metametrics.ts +++ b/shared/constants/metametrics.ts @@ -793,6 +793,7 @@ export enum MetaMetricsNetworkEventSource { PopularNetworkList = 'popular_network_list', Dapp = 'dapp', DeprecatedNetworkModal = 'deprecated_network_modal', + NewAddNetworkFlow = 'new_add_network_flow', } export enum MetaMetricsSwapsEventSource { diff --git a/ui/components/multichain/network-list-menu/network-confirmation-popover/__snapshots__/network-confirmation-popover.test.tsx.snap b/ui/components/multichain/network-list-menu/network-confirmation-popover/__snapshots__/network-confirmation-popover.test.tsx.snap new file mode 100644 index 000000000000..193bbae014a2 --- /dev/null +++ b/ui/components/multichain/network-list-menu/network-confirmation-popover/__snapshots__/network-confirmation-popover.test.tsx.snap @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`NetworkConfirmationPopover renders popular list component 1`] = `<div />`; diff --git a/ui/components/multichain/network-list-menu/network-confirmation-popover/network-confirmation-popover.test.tsx b/ui/components/multichain/network-list-menu/network-confirmation-popover/network-confirmation-popover.test.tsx new file mode 100644 index 000000000000..8efca30b61fc --- /dev/null +++ b/ui/components/multichain/network-list-menu/network-confirmation-popover/network-confirmation-popover.test.tsx @@ -0,0 +1,45 @@ +import React from 'react'; +import configureMockStore from 'redux-mock-store'; +import { ApprovalType } from '@metamask/controller-utils'; +import { renderWithProvider } from '../../../../../test/lib/render-helpers'; +import mockState from '../../../../../test/data/mock-state.json'; +import NetworkConfirmationPopover from './network-confirmation-popover'; + +describe('NetworkConfirmationPopover', () => { + const mockUnapprovedConfirmations = [ + { + id: '1', + origin: 'metamask', + type: ApprovalType.AddEthereumChain, + }, + ]; + + const STATE_MOCK = { + ...mockState, + unapprovedConfirmations: mockUnapprovedConfirmations, + }; + const mockStore = configureMockStore([])(STATE_MOCK); + + it('renders popular list component', () => { + const { container } = renderWithProvider( + <NetworkConfirmationPopover />, + mockStore, + ); + + expect(container).toMatchSnapshot(); + }); + + it('does not render the popover when there are no unapproved AddEthereumChain confirmations', () => { + const emptyStateMock = { + ...mockState, + unapprovedConfirmations: [], + }; + const emptyMockStore = configureMockStore([])(emptyStateMock); + + const { queryByText } = renderWithProvider( + <NetworkConfirmationPopover />, + emptyMockStore, + ); + expect(queryByText('ConfirmationPage content')).not.toBeInTheDocument(); + }); +}); diff --git a/ui/components/multichain/network-list-menu/network-confirmation-popover/network-confirmation-popover.tsx b/ui/components/multichain/network-list-menu/network-confirmation-popover/network-confirmation-popover.tsx new file mode 100644 index 000000000000..520394fb8e03 --- /dev/null +++ b/ui/components/multichain/network-list-menu/network-confirmation-popover/network-confirmation-popover.tsx @@ -0,0 +1,40 @@ +import React, { useEffect, useState } from 'react'; +import { useSelector } from 'react-redux'; +import { ApprovalType } from '@metamask/controller-utils'; +import { ORIGIN_METAMASK } from '@metamask/approval-controller'; +import Popover from '../../../ui/popover'; +import ConfirmationPage from '../../../../pages/confirmations/confirmation/confirmation'; +import { getUnapprovedConfirmations } from '../../../../selectors'; + +const NetworkConfirmationPopover = () => { + const [showPopover, setShowPopover] = useState(false); + + const unapprovedConfirmations = useSelector(getUnapprovedConfirmations); + + useEffect(() => { + const anAddNetworkConfirmationFromMetaMaskExists = + unapprovedConfirmations?.find( + (confirmation) => + confirmation.origin === ORIGIN_METAMASK && + confirmation.type === ApprovalType.AddEthereumChain, + ); + + if (!showPopover && anAddNetworkConfirmationFromMetaMaskExists) { + setShowPopover(true); + } else if (showPopover && !anAddNetworkConfirmationFromMetaMaskExists) { + setShowPopover(false); + } + }, [unapprovedConfirmations, showPopover]); + + if (!showPopover) { + return null; + } + + return ( + <Popover data-testid="network-popover"> + <ConfirmationPage redirectToHomeOnZeroConfirmations={false} /> + </Popover> + ); +}; + +export default NetworkConfirmationPopover; diff --git a/ui/components/multichain/network-list-menu/network-list-menu.js b/ui/components/multichain/network-list-menu/network-list-menu.js index 26e6e3d94007..549164b7b21a 100644 --- a/ui/components/multichain/network-list-menu/network-list-menu.js +++ b/ui/components/multichain/network-list-menu/network-list-menu.js @@ -16,7 +16,10 @@ import { updateNetworksList, setNetworkClientIdForDomain, } from '../../../store/actions'; -import { TEST_CHAINS } from '../../../../shared/constants/network'; +import { + FEATURED_RPCS, + TEST_CHAINS, +} from '../../../../shared/constants/network'; import { getCurrentChainId, getCurrentNetwork, @@ -28,6 +31,7 @@ import { getShowNetworkBanner, getOriginOfCurrentTab, getUseRequestQueue, + getNetworkConfigurations, } from '../../../selectors'; import ToggleButton from '../../ui/toggle-button'; import { @@ -49,9 +53,9 @@ import { Text, BannerBase, IconName, + ModalContent, + ModalHeader, } from '../../component-library'; -import { ModalContent } from '../../component-library/modal-content/deprecated'; -import { ModalHeader } from '../../component-library/modal-header/deprecated'; import { TextFieldSearch } from '../../component-library/text-field-search/deprecated'; import { ADD_POPULAR_CUSTOM_NETWORK } from '../../../helpers/constants/routes'; import { getEnvironmentType } from '../../../../app/scripts/lib/util'; @@ -65,6 +69,8 @@ import { getCompletedOnboarding, getIsUnlocked, } from '../../../ducks/metamask/metamask'; +import { getLocalNetworkMenuRedesignFeatureFlag } from '../../../helpers/utils/feature-flags'; +import PopularNetworkList from './popular-network-list/popular-network-list'; export const NetworkListMenu = ({ onClose }) => { const t = useI18nContext(); @@ -73,9 +79,13 @@ export const NetworkListMenu = ({ onClose }) => { 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 dispatch = useDispatch(); const history = useHistory(); @@ -95,6 +105,17 @@ export const NetworkListMenu = ({ onClose }) => { const orderedNetworksList = useSelector(getOrderedNetworksList); + 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; @@ -153,6 +174,12 @@ export const NetworkListMenu = ({ onClose }) => { let searchResults = [...networksList].length === items.length ? items : [...networksList]; + + const searchAddNetworkResults = + [...notExistingNetworkConfigurations].length === items.length + ? items + : [...notExistingNetworkConfigurations]; + const isSearching = searchQuery !== ''; if (isSearching) { @@ -405,24 +432,29 @@ export const NetworkListMenu = ({ onClose }) => { </Droppable> </DragDropContext> )} - </Box> - <Box - padding={4} - display={Display.Flex} - justifyContent={JustifyContent.spaceBetween} - > - <Text>{t('showTestnetNetworks')}</Text> - <ToggleButton - value={showTestNetworks} - disabled={currentlyOnTestNetwork} - onToggle={handleToggle} - /> - </Box> - {showTestNetworks || currentlyOnTestNetwork ? ( - <Box className="multichain-network-list-menu"> - {generateMenuItems(testNetworks)} + {networkMenuRedesign ? ( + <PopularNetworkList + searchAddNetworkResults={searchAddNetworkResults} + /> + ) : null} + <Box + padding={4} + display={Display.Flex} + justifyContent={JustifyContent.spaceBetween} + > + <Text>{t('showTestnetNetworks')}</Text> + <ToggleButton + value={showTestNetworks} + disabled={currentlyOnTestNetwork} + onToggle={handleToggle} + /> </Box> - ) : null} + {showTestNetworks || currentlyOnTestNetwork ? ( + <Box className="multichain-network-list-menu"> + {generateMenuItems(testNetworks)} + </Box> + ) : null} + </Box> <Box padding={4}> <ButtonSecondary size={ButtonSecondarySize.Lg} 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 83ebf8d03250..031ad257317c 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 @@ -13,6 +13,7 @@ import { NetworkListMenu } from '.'; const mockSetShowTestNetworks = jest.fn(); const mockSetProviderType = jest.fn(); const mockToggleNetworkMenu = jest.fn(); +const mockNetworkMenuRedesignToggle = jest.fn(); jest.mock('../../../store/actions.ts', () => ({ setShowTestNetworks: () => mockSetShowTestNetworks, @@ -20,6 +21,11 @@ jest.mock('../../../store/actions.ts', () => ({ toggleNetworkMenu: () => mockToggleNetworkMenu, })); +jest.mock('../../../helpers/utils/feature-flags', () => ({ + ...jest.requireActual('../../../helpers/utils/feature-flags'), + getLocalNetworkMenuRedesignFeatureFlag: () => mockNetworkMenuRedesignToggle, +})); + const render = ({ showTestNetworks = false, currentChainId = '0x5', @@ -51,6 +57,10 @@ const render = ({ }; describe('NetworkListMenu', () => { + beforeEach(() => { + mockNetworkMenuRedesignToggle.mockReturnValue(false); + }); + it('displays important controls', () => { const { getByText, getByPlaceholderText } = render(); diff --git a/ui/components/multichain/network-list-menu/popular-network-list/PopularNetworkList.stories.js b/ui/components/multichain/network-list-menu/popular-network-list/PopularNetworkList.stories.js new file mode 100644 index 000000000000..320e951b0679 --- /dev/null +++ b/ui/components/multichain/network-list-menu/popular-network-list/PopularNetworkList.stories.js @@ -0,0 +1,85 @@ +import React from 'react'; +import { Provider } from 'react-redux'; +import testData from '../../../../../.storybook/test-data'; +import configureStore from '../../../../store/store'; +import { + CHAIN_IDS, + CURRENCY_SYMBOLS, + OPTIMISM_DISPLAY_NAME, + OPTIMISM_TOKEN_IMAGE_URL, + ZK_SYNC_ERA_DISPLAY_NAME, + ZK_SYNC_ERA_TOKEN_IMAGE_URL, +} from '../../../../../shared/constants/network'; +import PopularNetworkList from './popular-network-list'; + +const customNetworkStore = configureStore({ + ...testData, + metamask: { + ...testData.metamask, + preferences: { + showTestNetworks: true, + }, + orderedNetworkList: [], + networkConfigurations: { + ...testData.metamask.networkConfigurations, + ...{ + 'test-networkConfigurationId-1': { + chainId: CHAIN_IDS.OPTIMISM, + nickname: OPTIMISM_DISPLAY_NAME, + rpcUrl: `https://optimism-mainnet.infura.io/v3`, + ticker: CURRENCY_SYMBOLS.ETH, + rpcPrefs: { + blockExplorerUrl: 'https://optimistic.etherscan.io/', + imageUrl: OPTIMISM_TOKEN_IMAGE_URL, + }, + }, + 'test-networkConfigurationId-2': { + 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, + }, + }, + }, + }, + }, +}); + +export default { + title: 'Components/PopularNetworkList', + component: PopularNetworkList, +}; + +export const Default = (args) => ( + <Provider store={customNetworkStore}> + <PopularNetworkList {...args} /> + </Provider> +); + +Default.args = { + searchAddNetworkResults: [ + { + nickname: OPTIMISM_DISPLAY_NAME, + rpcPrefs: { + imageUrl: OPTIMISM_TOKEN_IMAGE_URL, + }, + }, + { + nickname: ZK_SYNC_ERA_DISPLAY_NAME, + rpcPrefs: { + imageUrl: ZK_SYNC_ERA_TOKEN_IMAGE_URL, + }, + }, + ], +}; + +Default.decorators = [ + (Story) => ( + <Provider store={customNetworkStore}> + <Story /> + </Provider> + ), +]; diff --git a/ui/components/multichain/network-list-menu/popular-network-list/__snapshots__/popular-network-list.test.tsx.snap b/ui/components/multichain/network-list-menu/popular-network-list/__snapshots__/popular-network-list.test.tsx.snap new file mode 100644 index 000000000000..41085f19a48c --- /dev/null +++ b/ui/components/multichain/network-list-menu/popular-network-list/__snapshots__/popular-network-list.test.tsx.snap @@ -0,0 +1,13 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`PopularNetworkList renders popular list component 1`] = ` +<div> + <div + class="mm-box new-network-list__networks-container" + > + <div + class="mm-box mm-box--margin-top-4 mm-box--margin-bottom-1 mm-box--padding-right-4 mm-box--padding-left-4" + /> + </div> +</div> +`; 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 new file mode 100644 index 000000000000..f7354e0acefc --- /dev/null +++ b/ui/components/multichain/network-list-menu/popular-network-list/popular-network-list.test.tsx @@ -0,0 +1,113 @@ +import React from 'react'; +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 { renderWithProvider } from '../../../../../test/lib/render-helpers'; +import { getUnapprovedConfirmations } from '../../../../selectors'; +import { + CHAIN_IDS, + RPCDefinition, +} from '../../../../../shared/constants/network'; +import PopularNetworkList from './popular-network-list'; + +jest.mock('react-redux', () => ({ + ...jest.requireActual('react-redux'), + useDispatch: jest.fn(), + useSelector: jest.fn(), +})); + +const STATE_MOCK = { + metamask: { + providerConfig: { + chainId: '0x1', + }, + }, +}; + +describe('PopularNetworkList', () => { + const store = configureStore()(STATE_MOCK); + const useDispatchMock = useDispatch as jest.Mock; + const useSelectorMock = useSelector as jest.Mock; + const mockDispatch = jest.fn(); + + beforeEach(() => { + jest.resetAllMocks(); + useDispatchMock.mockReturnValue(mockDispatch); + + useSelectorMock.mockImplementation((selector) => { + if (selector === getUnapprovedConfirmations) { + return []; + } + return undefined; + }); + }); + + const defaultProps = { + searchAddNetworkResults: [], + }; + + it('renders popular list component', () => { + const { container } = renderWithProvider( + <PopularNetworkList {...defaultProps} />, + store, + ); + + expect(container).toMatchSnapshot(); + }); + + it('displays the network list when networks are provided', () => { + const props = { + ...defaultProps, + searchAddNetworkResults: [ + { + chainId: CHAIN_IDS.MAINNET, + nickname: 'Network 1', + rpcPrefs: { + blockExplorerUrl: 'https://etherscan.com/', + imageUrl: 'https://example.com/image1.png', + }, + ticker: 'ETH', + rpcUrl: 'https://exampleEth.org/', + }, + { + 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/', + }, + ] as RPCDefinition[], + }; + + render(<PopularNetworkList {...props} />); + expect(screen.getByText('Network 1')).toBeInTheDocument(); + expect(screen.getByText('Network 2')).toBeInTheDocument(); + }); + it('calls the dispatch function when the add button is clicked', async () => { + const props = { + ...defaultProps, + searchAddNetworkResults: [ + { + 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/', + }, + ] as RPCDefinition[], + }; + + render(<PopularNetworkList {...props} />); + const addButton = screen.getByTestId('test-add-button'); + fireEvent.click(addButton); + + expect(useDispatchMock).toHaveBeenCalled(); + }); +}); 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 new file mode 100644 index 000000000000..6d2cd02f5170 --- /dev/null +++ b/ui/components/multichain/network-list-menu/popular-network-list/popular-network-list.tsx @@ -0,0 +1,125 @@ +import React from 'react'; +import { ApprovalType } from '@metamask/controller-utils'; +import { useDispatch } from 'react-redux'; +import { useI18nContext } from '../../../../hooks/useI18nContext'; +import { + Box, + Text, + AvatarNetwork, + Button, + AvatarNetworkSize, + ButtonVariant, +} from '../../../component-library'; +import { MetaMetricsNetworkEventSource } from '../../../../../shared/constants/metametrics'; +import { + ENVIRONMENT_TYPE_POPUP, + ORIGIN_METAMASK, +} from '../../../../../shared/constants/app'; +import { + requestUserApproval, + toggleNetworkMenu, +} from '../../../../store/actions'; +import { getEnvironmentType } from '../../../../../app/scripts/lib/util'; +import { + AlignItems, + BackgroundColor, + Display, + JustifyContent, + TextColor, +} from '../../../../helpers/constants/design-system'; +import { RPCDefinition } from '../../../../../shared/constants/network'; + +const PopularNetworkList = ({ + searchAddNetworkResults, +}: { + searchAddNetworkResults: RPCDefinition[]; +}) => { + const t = useI18nContext(); + const isPopUp = getEnvironmentType() === ENVIRONMENT_TYPE_POPUP; + const dispatch = useDispatch(); + + return ( + <Box className="new-network-list__networks-container"> + <Box + marginTop={isPopUp ? 0 : 4} + marginBottom={1} + paddingLeft={4} + paddingRight={4} + > + {Object.keys(searchAddNetworkResults).length === 0 ? null : ( + <Box + marginTop={4} + marginBottom={8} + display={Display.Flex} + justifyContent={JustifyContent.spaceBetween} + > + <Text> {t('additionalNetworks')}</Text> + </Box> + )} + + {searchAddNetworkResults.map((item: RPCDefinition, index: number) => ( + <Box + key={index} + display={Display.Flex} + alignItems={AlignItems.center} + justifyContent={JustifyContent.spaceBetween} + marginBottom={6} + className="new-network-list__list-of-networks" + > + <Box display={Display.Flex} alignItems={AlignItems.center}> + <AvatarNetwork + size={AvatarNetworkSize.Md} + src={item.rpcPrefs?.imageUrl} + name={item.nickname} + /> + <Box marginLeft={2}> + <Text + color={TextColor.textDefault} + backgroundColor={BackgroundColor.transparent} + ellipsis + > + {item.nickname} + </Text> + </Box> + </Box> + <Box + display={Display.Flex} + alignItems={AlignItems.center} + marginLeft={1} + > + <Button + type={ButtonVariant.Link} + className="add-network__add-button" + variant={ButtonVariant.Link} + data-testid="test-add-button" + onClick={async () => { + dispatch(toggleNetworkMenu()); + await dispatch( + requestUserApproval({ + 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, + referrer: ORIGIN_METAMASK, + source: MetaMetricsNetworkEventSource.NewAddNetworkFlow, + }, + }), + ); + }} + > + {t('add')} + </Button> + </Box> + </Box> + ))} + </Box> + </Box> + ); +}; + +export default PopularNetworkList; diff --git a/ui/pages/routes/routes.component.js b/ui/pages/routes/routes.component.js index e93b417c83fd..f33aed54aa12 100644 --- a/ui/pages/routes/routes.component.js +++ b/ui/pages/routes/routes.component.js @@ -129,6 +129,7 @@ import { getURLHost } from '../../helpers/utils/util'; import { BorderColor, IconColor } from '../../helpers/constants/design-system'; import { MILLISECOND } from '../../../shared/constants/time'; import { MultichainMetaFoxLogo } from '../../components/multichain/app-header/multichain-meta-fox-logo'; +import NetworkConfirmationPopover from '../../components/multichain/network-list-menu/network-confirmation-popover/network-confirmation-popover'; const isConfirmTransactionRoute = (pathname) => Boolean( @@ -199,6 +200,7 @@ 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, @@ -782,6 +784,7 @@ export default class Routes extends Component { hideDeprecatedNetworkModal, switchedNetworkDetails, clearSwitchedNetworkDetails, + networkMenuRedesign, ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps) isShowKeyringSnapRemovalResultModal, hideShowKeyringSnapRemovalResultModal, @@ -864,6 +867,7 @@ export default class Routes extends Component { {isNetworkMenuOpen ? ( <NetworkListMenu onClose={() => toggleNetworkMenu()} /> ) : null} + {networkMenuRedesign ? <NetworkConfirmationPopover /> : null} {accountDetailsAddress ? ( <AccountDetails address={accountDetailsAddress} /> ) : null} diff --git a/ui/pages/routes/routes.component.test.js b/ui/pages/routes/routes.component.test.js index 79941ea87fd3..e6274bfc3223 100644 --- a/ui/pages/routes/routes.component.test.js +++ b/ui/pages/routes/routes.component.test.js @@ -63,6 +63,11 @@ 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()({ ...mockSendState, diff --git a/ui/pages/routes/routes.container.js b/ui/pages/routes/routes.container.js index da708e147bcc..efa7afad4908 100644 --- a/ui/pages/routes/routes.container.js +++ b/ui/pages/routes/routes.container.js @@ -27,6 +27,7 @@ import { getShowPrivacyPolicyToast, getUseRequestQueue, } from '../../selectors'; +import { getLocalNetworkMenuRedesignFeatureFlag } from '../../helpers/utils/feature-flags'; import { getSmartTransactionsOptInStatus } from '../../../shared/modules/selectors'; import { lockMetamask, @@ -132,6 +133,7 @@ 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, From fbd9bbdb9f90217e2bfdc149ca8358eb10c5a858 Mon Sep 17 00:00:00 2001 From: Danica Shen <zhaodanica@gmail.com> Date: Tue, 11 Jun 2024 20:11:00 +0100 Subject: [PATCH 10/12] fix: add deprecated tag back to callBackgroundMethod (#25216) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit <!-- Please submit this PR as a draft initially. Do not mark it as "Ready for review" until the template has been completely filled out, and PR status checks have passed at least once. --> ## **Description** Removed wrongly in https://github.com/MetaMask/metamask-extension/pull/21410, this PR aims to add `deprecated` tag back to `callBackgroundMethod` for migration purpose. <!-- Write a short description of the changes included in this pull request, also include relevant motivation and context. Have in mind the following questions: 1. What is the reason for the change? 2. What is the improvement/solution? --> [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/25216?quickstart=1) ## **Related issues** Fixes: ## **Manual testing steps** 1. Go to this page... 2. 3. ## **Screenshots/Recordings** <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> ### **Before** <!-- [screenshots/recordings] --> ### **After** <!-- [screenshots/recordings] --> ## **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. --- ui/store/background-connection.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/store/background-connection.ts b/ui/store/background-connection.ts index 5c7222c040e6..3928675419a8 100644 --- a/ui/store/background-connection.ts +++ b/ui/store/background-connection.ts @@ -41,6 +41,7 @@ type CallbackMethod<R = unknown> = (error?: unknown, result?: R) => void; * [Deprecated] Callback-style call to background method * invokes promisifiedBackground method directly. * + * @deprecated Use async `submitRequestToBackground` function instead. * @param method - name of the background method * @param [args] - arguments to that method, if any * @param callback - Node style (error, result) callback for finishing the operation From c67aa3feb9949526735db30a45078a60b53b3e18 Mon Sep 17 00:00:00 2001 From: David Walsh <davidwalsh83@gmail.com> Date: Tue, 11 Jun 2024 17:38:26 -0500 Subject: [PATCH 11/12] fix: UX: NetworkList: Consolidate NetworkListItem usages (#25195) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** This PR removes duplicate code for NetworkListItem creation, instead using a helper function to create a single item. [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/25195?quickstart=1) ## **Related issues** Fixes: N/A ## **Manual testing steps** 1. Click "Polygon" and see chain switch to Polygon 2. Click "Ethereum Mainnet" and see chain switch to Ethereum Mainnet 3. Drag and drop networks around -- should work 4. Click the toggle to view test networks 5. Click a test network -- should change to correct network ## **Screenshots/Recordings** <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> ### **Before** <!-- [screenshots/recordings] --> ### **After** <!-- [screenshots/recordings] --> ## **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: Brian Bergeron <brian.e.bergeron@gmail.com> Co-authored-by: Nidhi Kumari <nidhi.kumari@consensys.net> --- .../network-list-menu/network-list-menu.js | 163 ++++++++---------- 1 file changed, 68 insertions(+), 95 deletions(-) diff --git a/ui/components/multichain/network-list-menu/network-list-menu.js b/ui/components/multichain/network-list-menu/network-list-menu.js index 549164b7b21a..da7f145e7389 100644 --- a/ui/components/multichain/network-list-menu/network-list-menu.js +++ b/ui/components/multichain/network-list-menu/network-list-menu.js @@ -200,6 +200,62 @@ export const NetworkListMenu = ({ onClose }) => { ); } + const generateNetworkListItem = ({ + network, + isCurrentNetwork, + canDeleteNetwork, + }) => { + return ( + <NetworkListItem + name={network.nickname} + iconSrc={network?.rpcPrefs?.imageUrl} + key={network.id} + selected={isCurrentNetwork} + focus={isCurrentNetwork && !showSearch} + onClick={() => { + dispatch(toggleNetworkMenu()); + if (network.providerType) { + dispatch(setProviderType(network.providerType)); + } else { + dispatch(setActiveNetwork(network.id)); + } + + // 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) { + 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 + } + /> + ); + }; + const generateMenuItems = (desiredNetworks) => { return desiredNetworks.map((network) => { const isCurrentNetwork = @@ -209,47 +265,11 @@ export const NetworkListMenu = ({ onClose }) => { const canDeleteNetwork = isUnlocked && !isCurrentNetwork && network.removable; - return ( - <NetworkListItem - name={network.nickname} - iconSrc={network?.rpcPrefs?.imageUrl} - key={network.id} - selected={isCurrentNetwork} - focus={isCurrentNetwork && !showSearch} - onClick={() => { - dispatch(toggleNetworkMenu()); - if (network.providerType) { - dispatch(setProviderType(network.providerType)); - } else { - dispatch(setActiveNetwork(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 - } - /> - ); + return generateNetworkListItem({ + network, + isCurrentNetwork, + canDeleteNetwork, + }); }); }; @@ -355,6 +375,12 @@ export const NetworkListMenu = ({ onClose }) => { const canDeleteNetwork = isUnlocked && !isCurrentNetwork && network.removable; + const networkListItem = generateNetworkListItem({ + network, + isCurrentNetwork, + canDeleteNetwork, + }); + return ( <Draggable key={network.id} @@ -367,60 +393,7 @@ export const NetworkListMenu = ({ onClose }) => { {...providedDrag.draggableProps} {...providedDrag.dragHandleProps} > - <NetworkListItem - name={network.nickname} - iconSrc={network?.rpcPrefs?.imageUrl} - key={network.id} - selected={isCurrentNetwork} - focus={isCurrentNetwork && !showSearch} - onClick={() => { - dispatch(toggleNetworkMenu()); - if (network.providerType) { - dispatch( - setProviderType(network.providerType), - ); - } else { - dispatch(setActiveNetwork(network.id)); - } - - // 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) { - 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 - } - /> + {networkListItem} </Box> )} </Draggable> From 9a5aeae1944814bdc7a3db28bf5bf60cba55aa41 Mon Sep 17 00:00:00 2001 From: Ariella Vu <20778143+digiwand@users.noreply.github.com> Date: Wed, 12 Jun 2024 10:02:41 +0900 Subject: [PATCH 12/12] feat: Support various redesign signatures in ConfirmPage Storybook (#25054) --- .../confirmations/confirm/confirm.stories.tsx | 99 ++++++++++++++++--- 1 file changed, 84 insertions(+), 15 deletions(-) diff --git a/ui/pages/confirmations/confirm/confirm.stories.tsx b/ui/pages/confirmations/confirm/confirm.stories.tsx index 7062fb49201f..3e428eba997f 100644 --- a/ui/pages/confirmations/confirm/confirm.stories.tsx +++ b/ui/pages/confirmations/confirm/confirm.stories.tsx @@ -1,27 +1,96 @@ import React from 'react'; import { Provider } from 'react-redux'; -import configureStore from '../../../store/store'; +import { cloneDeep } from 'lodash'; +import { unapprovedPersonalSignMsg, signatureRequestSIWE } from '../../../../test/data/confirmations/personal_sign'; +import { unapprovedTypedSignMsgV1, unapprovedTypedSignMsgV4, permitSignatureMsg } from '../../../../test/data/confirmations/typed_sign'; import mockState from '../../../../test/data/mock-state.json'; +import configureStore from '../../../store/store'; import ConfirmPage from './confirm'; -const store = configureStore({ - confirm: { - currentConfirmation: { - type: 'personal_sign', - }, - }, - metamask: { - ...mockState.metamask, - }, -}); - +/** + * @note When we extend this storybook page to support more confirmation types, + * consider creating a new storybook pages. + */ const ConfirmPageStory = { title: 'Pages/Confirm/ConfirmPage', - decorators: [(story) => <Provider store={store}>{story()}</Provider>], + decorators: [(story) => <div style={{ height: '600px' }}>{story()}</div>], +} + +const ARGS_SIGNATURE = { + msgParams: { ...unapprovedPersonalSignMsg.msgParams }, +} + +const ARG_TYPES_SIGNATURE = { + msgParams: { + control: 'object', + description: '(non-param) overrides currentConfirmation.msgParams', + }, +} + +function SignatureStoryTemplate(args, confirmation) { + const mockConfirmation = cloneDeep(confirmation); + mockConfirmation.msgParams = args.msgParams; + + const store = configureStore({ + confirm: { + currentConfirmation: mockConfirmation, + }, + metamask: { ...mockState.metamask }, + }); + + return <Provider store={store}><ConfirmPage /></Provider>; +} + +export const PersonalSignStory = (args) => { + return SignatureStoryTemplate(args, unapprovedPersonalSignMsg); }; -export const DefaultStory = (args) => <ConfirmPage {...args} />; +PersonalSignStory.storyName = 'Personal Sign'; +PersonalSignStory.argTypes = ARG_TYPES_SIGNATURE; +PersonalSignStory.args = ARGS_SIGNATURE; -DefaultStory.storyName = 'Default'; +export const SignInWithEthereumSIWEStory = (args) => { + return SignatureStoryTemplate(args, signatureRequestSIWE); +}; + +SignInWithEthereumSIWEStory.storyName = 'Sign-in With Ethereum (SIWE)'; +SignInWithEthereumSIWEStory.argTypes = ARG_TYPES_SIGNATURE; +SignInWithEthereumSIWEStory.args = { + ...ARGS_SIGNATURE, + msgParams: signatureRequestSIWE.msgParams, +}; + +export const SignTypedDataStory = (args) => { + return SignatureStoryTemplate(args, unapprovedTypedSignMsgV1); +}; + +SignTypedDataStory.storyName = 'SignTypedData'; +SignTypedDataStory.argTypes = ARG_TYPES_SIGNATURE; +SignTypedDataStory.args = { + ...ARGS_SIGNATURE, + msgParams: unapprovedTypedSignMsgV1.msgParams, +}; + +export const PermitStory = (args) => { + return SignatureStoryTemplate(args, permitSignatureMsg); +}; + +PermitStory.storyName = 'SignTypedData Permit'; +PermitStory.argTypes = ARG_TYPES_SIGNATURE; +PermitStory.args = { + ...ARGS_SIGNATURE, + msgParams: permitSignatureMsg.msgParams, +}; + +export const SignTypedDataV4Story = (args) => { + return SignatureStoryTemplate(args, unapprovedTypedSignMsgV4); +}; + +SignTypedDataV4Story.storyName = 'SignTypedData V4'; +SignTypedDataV4Story.argTypes = ARG_TYPES_SIGNATURE; +SignTypedDataV4Story.args = { + ...ARGS_SIGNATURE, + msgParams: unapprovedTypedSignMsgV4.msgParams, +}; export default ConfirmPageStory;