diff --git a/app/_locales/de/messages.json b/app/_locales/de/messages.json index c9d1e383a3c3..d2fc0b02b0de 100644 --- a/app/_locales/de/messages.json +++ b/app/_locales/de/messages.json @@ -7519,19 +7519,9 @@ "walletConnectionGuide": { "message": "unsere Hardware-Wallet-Verbindungsanleitung" }, - "walletDetails": { - "message": "Wallet-Details" - }, "walletName": { "message": "Wallet-Name" }, - "walletNotFoundDescription": { - "message": "Die Wallet mit der ID $1 wurde nicht gefunden.", - "description": "$1 is the wallet ID" - }, - "walletNotFoundTitle": { - "message": "Wallet nicht gefunden" - }, "walletReadyLearn": { "message": "$1 Sie können diese Phrase sicher aufbewahren, damit Sie nie den Zugriff auf Ihr Geld verlieren.", "description": "$1 is the link to Learn how" diff --git a/app/_locales/el/messages.json b/app/_locales/el/messages.json index ecd8dac06060..95256900b443 100644 --- a/app/_locales/el/messages.json +++ b/app/_locales/el/messages.json @@ -7519,19 +7519,9 @@ "walletConnectionGuide": { "message": "ο οδηγός μας σύνδεσης πορτοφολιού υλικού" }, - "walletDetails": { - "message": "Λεπτομέρειες πορτοφολιού" - }, "walletName": { "message": "Όνομα πορτοφολιού" }, - "walletNotFoundDescription": { - "message": "Το πορτοφόλι με αρ. $1 δεν βρέθηκε.", - "description": "$1 is the wallet ID" - }, - "walletNotFoundTitle": { - "message": "Το πορτοφόλι δεν βρέθηκε" - }, "walletReadyLearn": { "message": "$1 να φυλάξετε σε ασφαλές μέρος τη μυστική φράση για να διασφαλίσετε την πρόσβαση στα χρήματά σας.", "description": "$1 is the link to Learn how" diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 22d33f8a1d9d..5e1d78c6639d 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -8434,19 +8434,9 @@ "walletConnectionGuide": { "message": "our hardware wallet connection guide" }, - "walletDetails": { - "message": "Wallet details" - }, "walletName": { "message": "Wallet name" }, - "walletNotFoundDescription": { - "message": "The wallet with ID $1 was not found.", - "description": "$1 is the wallet ID" - }, - "walletNotFoundTitle": { - "message": "Wallet not found" - }, "walletReadyLearn": { "message": "$1 you can keep this phrase safe so you never lose access to your money.", "description": "$1 is the link to Learn how" diff --git a/app/_locales/en_GB/messages.json b/app/_locales/en_GB/messages.json index 22d33f8a1d9d..5e1d78c6639d 100644 --- a/app/_locales/en_GB/messages.json +++ b/app/_locales/en_GB/messages.json @@ -8434,19 +8434,9 @@ "walletConnectionGuide": { "message": "our hardware wallet connection guide" }, - "walletDetails": { - "message": "Wallet details" - }, "walletName": { "message": "Wallet name" }, - "walletNotFoundDescription": { - "message": "The wallet with ID $1 was not found.", - "description": "$1 is the wallet ID" - }, - "walletNotFoundTitle": { - "message": "Wallet not found" - }, "walletReadyLearn": { "message": "$1 you can keep this phrase safe so you never lose access to your money.", "description": "$1 is the link to Learn how" diff --git a/app/_locales/es/messages.json b/app/_locales/es/messages.json index 9339bd17c07a..4d92757b4aa3 100644 --- a/app/_locales/es/messages.json +++ b/app/_locales/es/messages.json @@ -7519,19 +7519,9 @@ "walletConnectionGuide": { "message": "nuestra guía de conexión del monedero físico" }, - "walletDetails": { - "message": "Detalles de la billetera" - }, "walletName": { "message": "Nombre de la billetera" }, - "walletNotFoundDescription": { - "message": "No se encontró la billetera con ID $1.", - "description": "$1 is the wallet ID" - }, - "walletNotFoundTitle": { - "message": "Billetera no encontrada" - }, "walletReadyLearn": { "message": "$1 puedes guardar esta frase de forma segura para nunca perder el acceso a tu dinero.", "description": "$1 is the link to Learn how" diff --git a/app/_locales/fr/messages.json b/app/_locales/fr/messages.json index 02f52bf54419..a3a573c25cdd 100644 --- a/app/_locales/fr/messages.json +++ b/app/_locales/fr/messages.json @@ -7519,19 +7519,9 @@ "walletConnectionGuide": { "message": "notre guide de connexion des portefeuilles matériels" }, - "walletDetails": { - "message": "Détails du portefeuille" - }, "walletName": { "message": "Nom du portefeuille" }, - "walletNotFoundDescription": { - "message": "Le portefeuille avec l’identifiant $1 n’a pas été trouvé.", - "description": "$1 is the wallet ID" - }, - "walletNotFoundTitle": { - "message": "Portefeuille introuvable" - }, "walletReadyLearn": { "message": "$1 conservez cette phrase en lieu sûr pour ne jamais perdre l’accès à votre argent.", "description": "$1 is the link to Learn how" diff --git a/app/_locales/ga/messages.json b/app/_locales/ga/messages.json index a6a5b29b2356..85600956d437 100644 --- a/app/_locales/ga/messages.json +++ b/app/_locales/ga/messages.json @@ -7626,19 +7626,9 @@ "walletConnectionGuide": { "message": "ár dtreoir nasctha sparán crua-earraí" }, - "walletDetails": { - "message": "Sonraí sparán" - }, "walletName": { "message": "Ainm an sparáin" }, - "walletNotFoundDescription": { - "message": "Níor aimsíodh an sparán leis an ID $1.", - "description": "$1 is the wallet ID" - }, - "walletNotFoundTitle": { - "message": "Níor aimsíodh an sparán" - }, "walletReadyLearn": { "message": "$1 is féidir leat an frása seo a choinneáil slán ionas nach gcaillfidh tú rochtain ar do chuid airgid choíche.", "description": "$1 is the link to Learn how" diff --git a/app/_locales/hi/messages.json b/app/_locales/hi/messages.json index 8b98e5151942..bbdb227b7995 100644 --- a/app/_locales/hi/messages.json +++ b/app/_locales/hi/messages.json @@ -7516,19 +7516,9 @@ "walletConnectionGuide": { "message": "हमारी hardware wallet कनेक्शन गाइड" }, - "walletDetails": { - "message": "वॉलेट विवरण" - }, "walletName": { "message": "वॉलेट का नाम" }, - "walletNotFoundDescription": { - "message": "$1 ID वाला वॉलेट नहीं मिला।", - "description": "$1 is the wallet ID" - }, - "walletNotFoundTitle": { - "message": "वॉलेट नहीं मिला" - }, "walletReadyLearn": { "message": "$1 आप इस फ्रेज़ को सुरक्षित रख सकते हैं ताकि आप कभी भी अपने पैसे तक पहुंच न खोएं।", "description": "$1 is the link to Learn how" diff --git a/app/_locales/id/messages.json b/app/_locales/id/messages.json index 2d592ebadaa7..4b423d4a7fea 100644 --- a/app/_locales/id/messages.json +++ b/app/_locales/id/messages.json @@ -7519,19 +7519,9 @@ "walletConnectionGuide": { "message": "panduan koneksi dompet perangkat keras kami" }, - "walletDetails": { - "message": "Detail dompet" - }, "walletName": { "message": "Nama dompet" }, - "walletNotFoundDescription": { - "message": "Dompet dengan ID $1 tidak ditemukan.", - "description": "$1 is the wallet ID" - }, - "walletNotFoundTitle": { - "message": "Dompet tidak ditemukan" - }, "walletReadyLearn": { "message": "$1 Anda dapat menyimpan frasa ini dengan aman sehingga Anda tidak akan pernah kehilangan akses ke uang Anda.", "description": "$1 is the link to Learn how" diff --git a/app/_locales/ja/messages.json b/app/_locales/ja/messages.json index ae81cf4a8c56..acc8339396f8 100644 --- a/app/_locales/ja/messages.json +++ b/app/_locales/ja/messages.json @@ -7519,19 +7519,9 @@ "walletConnectionGuide": { "message": "弊社のハードウェアウォレット接続ガイド" }, - "walletDetails": { - "message": "ウォレットの詳細" - }, "walletName": { "message": "ウォレット名" }, - "walletNotFoundDescription": { - "message": "IDが$1のウォレットは見つかりませんでした。", - "description": "$1 is the wallet ID" - }, - "walletNotFoundTitle": { - "message": "ウォレットが見つかりません" - }, "walletReadyLearn": { "message": "$1 ご自身の資金にアクセスできなくなることを防ぐため、このフレーズは安全に保管しましょう。", "description": "$1 is the link to Learn how" diff --git a/app/_locales/ko/messages.json b/app/_locales/ko/messages.json index 1f7ef735f5ff..5da601eed05f 100644 --- a/app/_locales/ko/messages.json +++ b/app/_locales/ko/messages.json @@ -7519,19 +7519,9 @@ "walletConnectionGuide": { "message": "당사의 하드웨어 지갑 연결 가이드" }, - "walletDetails": { - "message": "지갑 세부 정보" - }, "walletName": { "message": "지갑 이름" }, - "walletNotFoundDescription": { - "message": "ID가 $1인 지갑을 찾을 수 없습니다.", - "description": "$1 is the wallet ID" - }, - "walletNotFoundTitle": { - "message": "지갑을 찾을 수 없음" - }, "walletReadyLearn": { "message": "$1 이 구문을 안전하게 보관하면 자산 접근 권한을 잃지 않을 수 있습니다.", "description": "$1 is the link to Learn how" diff --git a/app/_locales/pt/messages.json b/app/_locales/pt/messages.json index c0e21eb5716e..4801c0693985 100644 --- a/app/_locales/pt/messages.json +++ b/app/_locales/pt/messages.json @@ -7519,19 +7519,9 @@ "walletConnectionGuide": { "message": "nosso guia de conexão com a carteira de hardware" }, - "walletDetails": { - "message": "Detalhes da carteira" - }, "walletName": { "message": "Nome da carteira" }, - "walletNotFoundDescription": { - "message": "A carteira com o ID $1 não foi encontrada.", - "description": "$1 is the wallet ID" - }, - "walletNotFoundTitle": { - "message": "Carteira não encontrada" - }, "walletReadyLearn": { "message": "$1 você pode guardar esta frase em segurança para nunca perder o acesso ao seu dinheiro.", "description": "$1 is the link to Learn how" diff --git a/app/_locales/ru/messages.json b/app/_locales/ru/messages.json index 8d0f8fed66be..61bff66fc81d 100644 --- a/app/_locales/ru/messages.json +++ b/app/_locales/ru/messages.json @@ -7519,19 +7519,9 @@ "walletConnectionGuide": { "message": "наше руководство по подключению аппаратного кошелька" }, - "walletDetails": { - "message": "Реквизиты кошелька" - }, "walletName": { "message": "Имя кошелька" }, - "walletNotFoundDescription": { - "message": "Кошелек с идентификатором $1 не найден.", - "description": "$1 is the wallet ID" - }, - "walletNotFoundTitle": { - "message": "Кошелек не найден" - }, "walletReadyLearn": { "message": "$1 Вы можете сохранить эту фразу в надежном месте, чтобы никогда не потерять доступ к своим деньгам.", "description": "$1 is the link to Learn how" diff --git a/app/_locales/tl/messages.json b/app/_locales/tl/messages.json index 8f2e4ea4acf8..51d282ac19f0 100644 --- a/app/_locales/tl/messages.json +++ b/app/_locales/tl/messages.json @@ -7519,19 +7519,9 @@ "walletConnectionGuide": { "message": "ang aming gabay sa pagkonekta ng wallet na hardware" }, - "walletDetails": { - "message": "Mga detalye ng wallet" - }, "walletName": { "message": "Pangalan ng wallet" }, - "walletNotFoundDescription": { - "message": "Hindi nahanap ang wallet na may ID na $1.", - "description": "$1 is the wallet ID" - }, - "walletNotFoundTitle": { - "message": "Hindi nahanap ang wallet" - }, "walletReadyLearn": { "message": "$1 maaari mong panatiling ligtas ang pariralang ito nang sa gayon hindi mawawala ang access mo sa iyong pera.", "description": "$1 is the link to Learn how" diff --git a/app/_locales/tr/messages.json b/app/_locales/tr/messages.json index 5391903a0c0f..dded86481d9c 100644 --- a/app/_locales/tr/messages.json +++ b/app/_locales/tr/messages.json @@ -7519,19 +7519,9 @@ "walletConnectionGuide": { "message": "donanım cüzdanı bağlantı kılavuzumuz" }, - "walletDetails": { - "message": "Cüzdan bilgileri" - }, "walletName": { "message": "Cüzdan adı" }, - "walletNotFoundDescription": { - "message": "$1 kimliği ile cüzdan bulunamadı.", - "description": "$1 is the wallet ID" - }, - "walletNotFoundTitle": { - "message": "Cüzdan bulunamadı" - }, "walletReadyLearn": { "message": "$1 paranıza erişimi asla kaybetmemeniz için bu ifadeyi güvenli bir şekilde saklayabilirsiniz.", "description": "$1 is the link to Learn how" diff --git a/app/_locales/vi/messages.json b/app/_locales/vi/messages.json index 385228a05d9c..4b5b3979913e 100644 --- a/app/_locales/vi/messages.json +++ b/app/_locales/vi/messages.json @@ -7519,19 +7519,9 @@ "walletConnectionGuide": { "message": "hướng dẫn của chúng tôi về cách kết nối ví cứng" }, - "walletDetails": { - "message": "Chi tiết ví" - }, "walletName": { "message": "Tên ví" }, - "walletNotFoundDescription": { - "message": "Không tìm thấy ví có ID $1.", - "description": "$1 is the wallet ID" - }, - "walletNotFoundTitle": { - "message": "Không tìm thấy ví" - }, "walletReadyLearn": { "message": "$1 bạn có thể lưu giữ cụm từ này ở nơi an toàn để không bao giờ mất quyền truy cập vào tiền của mình.", "description": "$1 is the link to Learn how" diff --git a/app/_locales/zh_CN/messages.json b/app/_locales/zh_CN/messages.json index 35ba6c024bfe..ef73c3ac9d49 100644 --- a/app/_locales/zh_CN/messages.json +++ b/app/_locales/zh_CN/messages.json @@ -7516,19 +7516,9 @@ "walletConnectionGuide": { "message": "我们的硬件钱包连接指南" }, - "walletDetails": { - "message": "钱包详情" - }, "walletName": { "message": "钱包名称" }, - "walletNotFoundDescription": { - "message": "未找到 ID 为 $1 的钱包。", - "description": "$1 is the wallet ID" - }, - "walletNotFoundTitle": { - "message": "未找到钱包" - }, "walletReadyLearn": { "message": "$1 您需要妥善保管该助记词,这样永远不会失去对您的资金的访问权限。", "description": "$1 is the link to Learn how" diff --git a/test/e2e/flask/multi-srp/import-srp.spec.ts b/test/e2e/flask/multi-srp/import-srp.spec.ts index d5f4b2e563b0..a54deb410fd3 100644 --- a/test/e2e/flask/multi-srp/import-srp.spec.ts +++ b/test/e2e/flask/multi-srp/import-srp.spec.ts @@ -8,6 +8,7 @@ import { loginWithBalanceValidation } from '../../page-objects/flows/login.flow' import HeaderNavbar from '../../page-objects/pages/header-navbar'; import AccountListPage from '../../page-objects/pages/account-list-page'; import HomePage from '../../page-objects/pages/home/homepage'; +import MultichainAccountDetailsPage from '../../page-objects/pages/multichain/multichain-account-details-page'; import PrivacySettings from '../../page-objects/pages/settings/privacy-settings'; import { SECOND_TEST_E2E_SRP, @@ -56,7 +57,11 @@ describe('Multi SRP - Import SRP', function (this: Suite) { const accountListPage = new AccountListPage(driver); await accountListPage.checkPageIsLoaded(); - await accountListPage.startExportSrpForAccount('Account 2'); + await accountListPage.openAccountDetailsModal('Account 2'); + + const accountDetailsPage = new MultichainAccountDetailsPage(driver); + await accountDetailsPage.checkPageIsLoaded(); + await accountDetailsPage.clickSecretRecoveryPhraseRow(); const privacySettings = new PrivacySettings(driver); await privacySettings.completeRevealSrpQuiz(); diff --git a/test/e2e/page-objects/pages/multichain/address-list-modal.ts b/test/e2e/page-objects/pages/multichain/address-list-modal.ts index ba2b0d925129..c3dcc2dbbe17 100644 --- a/test/e2e/page-objects/pages/multichain/address-list-modal.ts +++ b/test/e2e/page-objects/pages/multichain/address-list-modal.ts @@ -3,6 +3,9 @@ import { Driver } from '../../../webdriver/driver'; class AddressListModal { private driver: Driver; + private readonly accountAddress = + '[data-testid="multichain-address-row-address"]'; + private readonly copyButton = '[data-testid="multichain-address-row-copy-button"]'; @@ -62,6 +65,17 @@ class AddressListModal { await qrButton.click(); } + async getTruncatedAccountAddress(addressIndex: number = 0): Promise { + console.log('Get truncated account address'); + const addressElements = await this.driver.findElements(this.accountAddress); + if (addressIndex < 0 || addressIndex >= addressElements.length) { + throw new Error('Invalid account row index'); + } + const addressElement = addressElements[addressIndex]; + const address = await addressElement.getText(); + return address; + } + async goBack(): Promise { await this.driver.clickElement(this.backButton); } diff --git a/test/e2e/page-objects/pages/multichain/multichain-account-details-page.ts b/test/e2e/page-objects/pages/multichain/multichain-account-details-page.ts index 9fcb8690462a..de790a9e0f28 100644 --- a/test/e2e/page-objects/pages/multichain/multichain-account-details-page.ts +++ b/test/e2e/page-objects/pages/multichain/multichain-account-details-page.ts @@ -41,6 +41,9 @@ class MultichainAccountDetailsPage { // Account-specific features private readonly showSrpButton = '[data-testid="account-show-srp-button"]'; + private readonly secretRecoveryPhraseRow = + '[data-testid="multichain-srp-backup"]'; + private readonly showPrivateKeyButton = '[data-testid="account-show-private-key-button"]'; @@ -233,6 +236,12 @@ class MultichainAccountDetailsPage { await this.driver.delay(largeDelayMs); } + async clickSecretRecoveryPhraseRow(): Promise { + console.log('Click on the Secret Recovery Phrase row'); + await this.driver.clickElement(this.secretRecoveryPhraseRow); + await this.driver.delay(largeDelayMs); + } + /** * Navigate back from account details */ diff --git a/test/e2e/tests/multichain-accounts/account-details.spec.ts b/test/e2e/tests/multichain-accounts/account-details.spec.ts index 6251896e95b1..993de3094598 100644 --- a/test/e2e/tests/multichain-accounts/account-details.spec.ts +++ b/test/e2e/tests/multichain-accounts/account-details.spec.ts @@ -128,7 +128,6 @@ describe('Multichain Accounts - Account Details', function (this: Suite) { { fixtures: new FixtureBuilder().build(), title: this.test?.fullTitle(), - forceBip44Version: 2, }, async ({ driver }) => { await loginWithoutBalanceValidation(driver); diff --git a/test/e2e/tests/vault-corruption/vault-corruption.spec.ts b/test/e2e/tests/vault-corruption/vault-corruption.spec.ts index d86b0a563bfb..7810cfe1c08f 100644 --- a/test/e2e/tests/vault-corruption/vault-corruption.spec.ts +++ b/test/e2e/tests/vault-corruption/vault-corruption.spec.ts @@ -8,8 +8,9 @@ import { import HomePage from '../../page-objects/pages/home/homepage'; import HeaderNavbar from '../../page-objects/pages/header-navbar'; import AccountListPage from '../../page-objects/pages/account-list-page'; -import AccountDetailsModal from '../../page-objects/pages/dialog/account-details-modal'; import LoginPage from '../../page-objects/pages/login-page'; +import MultichainAccountDetailsPage from '../../page-objects/pages/multichain/multichain-account-details-page'; +import AddressListModal from '../../page-objects/pages/multichain/address-list-modal'; // eslint-disable-next-line mocha/no-skipped-tests describe.skip('Vault Corruption', function () { @@ -258,10 +259,15 @@ describe.skip('Vault Corruption', function () { await accountListPage.checkPageIsLoaded(); await accountListPage.openAccountDetailsModal('Account 1'); - const accountDetailsModal = new AccountDetailsModal(driver); - await accountDetailsModal.checkPageIsLoaded(); + const accountDetailsPage = new MultichainAccountDetailsPage(driver); + await accountDetailsPage.checkPageIsLoaded(); + await accountDetailsPage.clickNetworksRow(); + + const addressListModal = new AddressListModal(driver); + const accountAddress = await addressListModal.getTruncatedAccountAddress(0); + await addressListModal.goBack(); + await accountDetailsPage.navigateBack(); - const accountAddress = await accountDetailsModal.getAccountAddress(); return accountAddress; } diff --git a/ui/components/multichain-accounts/multichain-accounts-tree/multichain-accounts-tree.tsx b/ui/components/multichain-accounts/multichain-accounts-tree/multichain-accounts-tree.tsx index 9ad5d98cf465..d7cedccef04f 100644 --- a/ui/components/multichain-accounts/multichain-accounts-tree/multichain-accounts-tree.tsx +++ b/ui/components/multichain-accounts/multichain-accounts-tree/multichain-accounts-tree.tsx @@ -17,7 +17,7 @@ import { import { ConsolidatedWallets } from '../../../selectors/multichain-accounts/account-tree.types'; import { MergedInternalAccount } from '../../../selectors/selectors.types'; import { HiddenAccountList } from '../../multichain/account-list-menu/hidden-account-list'; -import { WALLET_DETAILS_ROUTE } from '../../../helpers/constants/routes'; +import { MULTICHAIN_WALLET_DETAILS_PAGE_ROUTE } from '../../../helpers/constants/routes'; import { matchesSearchPattern } from './utils'; export type MultichainAccountsTreeProps = { @@ -50,7 +50,7 @@ export const MultichainAccountsTree = ({ const handleWalletDetailsClick = useCallback( (walletId: string) => { navigate( - WALLET_DETAILS_ROUTE.replace(':id', encodeURIComponent(walletId)), + `${MULTICHAIN_WALLET_DETAILS_PAGE_ROUTE}/${encodeURIComponent(walletId)}`, ); onClose(); }, diff --git a/ui/components/multichain/account-details/account-details.test.js b/ui/components/multichain/account-details/account-details.test.js index c4353bb8305e..43901e1f2036 100644 --- a/ui/components/multichain/account-details/account-details.test.js +++ b/ui/components/multichain/account-details/account-details.test.js @@ -10,7 +10,6 @@ import { clearAccountDetails, exportAccount, hideWarning, - setAccountDetailsAddress, } from '../../../store/actions'; import configureStore from '../../../store/store'; import { toChecksumHexAddress } from '../../../../shared/modules/hexstring-utils'; @@ -28,7 +27,6 @@ describe('AccountDetails', () => { mockState.metamask.internalAccounts.accounts, )[0]; const { address } = account; - const mockSetAccountDetailsAddress = jest.fn(); const mockClearAccountDetails = jest.fn(); const mockExportAccount = jest.fn().mockResolvedValue(true); const mockHideWarning = jest.fn(); @@ -37,7 +35,6 @@ describe('AccountDetails', () => { clearAccountDetails.mockReturnValue(mockClearAccountDetails); exportAccount.mockReturnValue(mockExportAccount); hideWarning.mockReturnValue(mockHideWarning); - setAccountDetailsAddress.mockReturnValue(mockSetAccountDetailsAddress); }); afterEach(() => jest.clearAllMocks()); diff --git a/ui/components/multichain/account-details/account-details.tsx b/ui/components/multichain/account-details/account-details.tsx index cb885fb78590..dfe4faccc7b4 100644 --- a/ui/components/multichain/account-details/account-details.tsx +++ b/ui/components/multichain/account-details/account-details.tsx @@ -24,11 +24,7 @@ import { getMetaMaskAccountsOrdered, getMetaMaskKeyrings, } from '../../../selectors'; -import { - clearAccountDetails, - hideWarning, - setAccountDetailsAddress, -} from '../../../store/actions'; +import { clearAccountDetails, hideWarning } from '../../../store/actions'; import HoldToRevealModal from '../../app/modals/hold-to-reveal-modal/hold-to-reveal-modal'; import { Box, @@ -110,7 +106,6 @@ export const AccountDetails = ({ address, navigate }: AccountDetailsProps) => { const [privateKey, setPrivateKey] = useState(''); const onClose = useCallback(() => { - dispatch(setAccountDetailsAddress('')); dispatch(clearAccountDetails()); dispatch(hideWarning()); }, [dispatch]); diff --git a/ui/components/multichain/global-menu/global-menu.test.tsx b/ui/components/multichain/global-menu/global-menu.test.tsx index 7130465619c5..3d0d9cf0f169 100644 --- a/ui/components/multichain/global-menu/global-menu.test.tsx +++ b/ui/components/multichain/global-menu/global-menu.test.tsx @@ -51,10 +51,9 @@ jest.mock('react-router-dom-v5-compat', () => ({ })); const mockLockMetaMask = jest.fn(); -const mockSetAccountDetailsAddress = jest.fn(); + jest.mock('../../../store/actions', () => ({ lockMetamask: () => mockLockMetaMask, - setAccountDetailsAddress: () => mockSetAccountDetailsAddress, })); jest.mock('../../../../shared/modules/environment'); diff --git a/ui/components/multichain/menu-items/account-details-menu-item.js b/ui/components/multichain/menu-items/account-details-menu-item.js index 8c20d9414985..0bb01deb1935 100644 --- a/ui/components/multichain/menu-items/account-details-menu-item.js +++ b/ui/components/multichain/menu-items/account-details-menu-item.js @@ -1,9 +1,8 @@ import React, { useCallback, useContext } from 'react'; import PropTypes from 'prop-types'; -import { useDispatch, useSelector } from 'react-redux'; +import { useSelector } from 'react-redux'; import { useNavigate } from 'react-router-dom-v5-compat'; -import { setAccountDetailsAddress } from '../../../store/actions'; import { MenuItem } from '../../ui/menu'; import { useI18nContext } from '../../../hooks/useI18nContext'; @@ -15,37 +14,22 @@ import { import { IconName, Text } from '../../component-library'; import { getSelectedAccountGroup } from '../../../selectors/multichain-accounts/account-tree'; import { getHDEntropyIndex } from '../../../selectors/selectors'; -import { - getIsMultichainAccountsState1Enabled, - getIsMultichainAccountsState2Enabled, -} from '../../../selectors/multichain-accounts/feature-flags'; -import { - ACCOUNT_DETAILS_ROUTE, - MULTICHAIN_ACCOUNT_DETAILS_PAGE_ROUTE, -} from '../../../helpers/constants/routes'; +import { MULTICHAIN_ACCOUNT_DETAILS_PAGE_ROUTE } from '../../../helpers/constants/routes'; export const AccountDetailsMenuItem = ({ metricsLocation, closeMenu, - address, textProps, }) => { const t = useI18nContext(); - const dispatch = useDispatch(); const trackEvent = useContext(MetaMetricsContext); const selectedAccountGroup = useSelector(getSelectedAccountGroup); const hdEntropyIndex = useSelector(getHDEntropyIndex); const navigate = useNavigate(); - const isMultichainAccountsState1Enabled = useSelector( - getIsMultichainAccountsState1Enabled, - ); - const isMultichainAccountsState2Enabled = useSelector( - getIsMultichainAccountsState2Enabled, - ); + const LABEL = t('accountDetails'); const handleNavigation = useCallback(() => { - dispatch(setAccountDetailsAddress(address)); trackEvent({ event: MetaMetricsEventName.AccountDetailsOpened, category: MetaMetricsEventCategory.Navigation, @@ -54,22 +38,16 @@ export const AccountDetailsMenuItem = ({ hd_entropy_index: hdEntropyIndex, }, }); - if (isMultichainAccountsState2Enabled) { - navigate( - `${MULTICHAIN_ACCOUNT_DETAILS_PAGE_ROUTE}/${encodeURIComponent(selectedAccountGroup)}`, - ); - } else if (isMultichainAccountsState1Enabled) { - navigate(`${ACCOUNT_DETAILS_ROUTE}/${address}`); - } + + navigate( + `${MULTICHAIN_ACCOUNT_DETAILS_PAGE_ROUTE}/${encodeURIComponent(selectedAccountGroup)}`, + ); + closeMenu?.(); }, [ - address, closeMenu, - dispatch, hdEntropyIndex, navigate, - isMultichainAccountsState1Enabled, - isMultichainAccountsState2Enabled, metricsLocation, selectedAccountGroup, trackEvent, diff --git a/ui/components/multichain/menu-items/account-details-menu-item.test.js b/ui/components/multichain/menu-items/account-details-menu-item.test.js index 39684c849a4d..6fc2a317cec8 100644 --- a/ui/components/multichain/menu-items/account-details-menu-item.test.js +++ b/ui/components/multichain/menu-items/account-details-menu-item.test.js @@ -3,12 +3,26 @@ import { fireEvent } from '../../../../test/jest'; import { renderWithProvider } from '../../../../test/lib/render-helpers-navigate'; import configureStore from '../../../store/store'; import mockState from '../../../../test/data/mock-state.json'; -import * as actions from '../../../store/actions'; +import { MULTICHAIN_ACCOUNT_DETAILS_PAGE_ROUTE } from '../../../helpers/constants/routes'; import { getSelectedInternalAccountFromMockState } from '../../../../test/jest/mocks'; import { AccountDetailsMenuItem } from '.'; const mockInternalAccount = getSelectedInternalAccountFromMockState(mockState); +const mockNavigate = jest.fn(); + +jest.mock('react-router-dom-v5-compat', () => ({ + ...jest.requireActual('react-router-dom-v5-compat'), + useNavigate: () => mockNavigate, +})); + +jest.mock('../../../selectors/multichain-accounts/account-tree', () => ({ + ...jest.requireActual( + '../../../selectors/multichain-accounts/account-tree.ts', + ), + getSelectedAccountGroup: () => mockInternalAccount.address, +})); + const render = () => { const store = configureStore(mockState); return renderWithProvider( @@ -21,13 +35,8 @@ const render = () => { ); }; -jest.mock('../../../store/actions', () => ({ - ...jest.requireActual('../../../store/actions.ts'), - setAccountDetailsAddress: jest.fn().mockReturnValue({ type: 'TYPE' }), -})); - describe('AccountDetailsMenuItem', () => { - it('opens the Account Details modal with the correct address', () => { + it('navigates to the multichain account details page with selected account group', () => { global.platform = { openTab: jest.fn() }; const { getByText, getByTestId } = render(); @@ -35,8 +44,10 @@ describe('AccountDetailsMenuItem', () => { fireEvent.click(getByTestId('account-list-menu-details')); - expect(actions.setAccountDetailsAddress).toHaveBeenCalledWith( - mockInternalAccount.address, + expect(mockNavigate).toHaveBeenCalledWith( + `${MULTICHAIN_ACCOUNT_DETAILS_PAGE_ROUTE}/${encodeURIComponent( + mockInternalAccount.address, + )}`, ); }); }); diff --git a/ui/ducks/app/app.ts b/ui/ducks/app/app.ts index c3be8d5c3051..93c90313ff8e 100644 --- a/ui/ducks/app/app.ts +++ b/ui/ducks/app/app.ts @@ -426,13 +426,6 @@ export default function reduceApp( alertMessage: null, }; - case actionConstants.SET_ACCOUNT_DETAILS_ADDRESS: { - return { - ...appState, - accountDetailsAddress: action.payload, - }; - } - // qr scanner methods case actionConstants.QR_CODE_DETECTED: return { diff --git a/ui/helpers/constants/routes.ts b/ui/helpers/constants/routes.ts index bc1bdc67b1f2..3ea29a48d55a 100644 --- a/ui/helpers/constants/routes.ts +++ b/ui/helpers/constants/routes.ts @@ -68,8 +68,6 @@ export const MULTICHAIN_WALLET_DETAILS_PAGE_ROUTE = '/multichain-wallet-details-page'; export const MULTICHAIN_SMART_ACCOUNT_PAGE_ROUTE = '/multichain-smart-account'; export const NEW_ACCOUNT_ROUTE = '/new-account'; -export const ACCOUNT_DETAILS_ROUTE = '/account-details'; -export const ACCOUNT_DETAILS_QR_CODE_ROUTE = '/account-details/qr-code'; export const CONFIRM_ADD_SUGGESTED_NFT_ROUTE = '/confirm-add-suggested-nft'; export const CONNECT_HARDWARE_ROUTE = '/new-account/connect'; export const SEND_ROUTE = '/send'; @@ -147,7 +145,6 @@ export const ONBOARDING_EXPERIMENTAL_AREA = '/onboarding/experimental-area'; ///: END:ONLY_INCLUDE_IF export const DEEP_LINK_ROUTE = '/link'; -export const WALLET_DETAILS_ROUTE = '/wallet-details/:id'; export const DEFI_ROUTE = '/defi'; export const SHIELD_PLAN_ROUTE = '/shield-plan'; @@ -329,16 +326,6 @@ export const ROUTES = [ label: 'New Account Page', trackInAnalytics: true, }, - { - path: ACCOUNT_DETAILS_ROUTE, - label: 'Account Details Page', - trackInAnalytics: true, - }, - { - path: ACCOUNT_DETAILS_QR_CODE_ROUTE, - label: 'Account Details QR Code Page', - trackInAnalytics: true, - }, { path: CONFIRM_ADD_SUGGESTED_NFT_ROUTE, label: 'Confirm Add Suggested NFT Page', @@ -540,11 +527,6 @@ export const ROUTES = [ label: 'Deep link Redirect Page', trackInAnalytics: true, }, - { - path: WALLET_DETAILS_ROUTE, - label: 'Wallet Details Page', - trackInAnalytics: true, - }, // Onboarding routes { path: ONBOARDING_ROUTE, label: 'Onboarding', trackInAnalytics: false }, { diff --git a/ui/pages/confirmations/components/confirm/smart-account-update/smart-account-update.test.tsx b/ui/pages/confirmations/components/confirm/smart-account-update/smart-account-update.test.tsx index b03d1052f5eb..1d9a478c4e3f 100644 --- a/ui/pages/confirmations/components/confirm/smart-account-update/smart-account-update.test.tsx +++ b/ui/pages/confirmations/components/confirm/smart-account-update/smart-account-update.test.tsx @@ -25,7 +25,6 @@ jest.mock('../../../../../hooks/useMultiPolling', () => ({ })); jest.mock('../../../../../store/actions', () => ({ - setAccountDetailsAddress: jest.fn(), setSmartAccountOptIn: jest.fn(), })); diff --git a/ui/pages/confirmations/components/confirm/splash/smart-account-update-splash/smart-account-update-splash.test.tsx b/ui/pages/confirmations/components/confirm/splash/smart-account-update-splash/smart-account-update-splash.test.tsx index 9eb7e3d35f4d..f5e0440d02c9 100644 --- a/ui/pages/confirmations/components/confirm/splash/smart-account-update-splash/smart-account-update-splash.test.tsx +++ b/ui/pages/confirmations/components/confirm/splash/smart-account-update-splash/smart-account-update-splash.test.tsx @@ -29,7 +29,6 @@ jest.mock('../../../../../../hooks/useMultiPolling', () => ({ })); jest.mock('../../../../../../store/actions', () => ({ - setAccountDetailsAddress: jest.fn(), rejectPendingApproval: jest.fn().mockReturnValue({}), setSmartAccountOptIn: jest.fn(), })); diff --git a/ui/pages/confirmations/components/confirm/title/title.test.tsx b/ui/pages/confirmations/components/confirm/title/title.test.tsx index 8489558d6d39..281235ee93a3 100644 --- a/ui/pages/confirmations/components/confirm/title/title.test.tsx +++ b/ui/pages/confirmations/components/confirm/title/title.test.tsx @@ -50,7 +50,6 @@ jest.mock('../info/approve/hooks/use-is-nft', () => ({ jest.mock('../../../../../store/actions', () => ({ getContractMethodData: jest.fn().mockReturnValue({ type: 'dummy' }), - setAccountDetailsAddress: jest.fn().mockReturnValue({ type: 'dummy' }), })); describe('ConfirmTitle', () => { diff --git a/ui/pages/confirmations/context/confirm/index.tsx b/ui/pages/confirmations/context/confirm/index.tsx index 1037aae2bd02..cedd8f98dfff 100644 --- a/ui/pages/confirmations/context/confirm/index.tsx +++ b/ui/pages/confirmations/context/confirm/index.tsx @@ -2,14 +2,10 @@ import React, { ReactElement, createContext, useContext, - useEffect, useMemo, useState, } from 'react'; -import { TransactionType } from '@metamask/transaction-controller'; -import { useDispatch } from 'react-redux'; -import { setAccountDetailsAddress } from '../../../../store/actions'; import useCurrentConfirmation from '../../hooks/useCurrentConfirmation'; import useSyncConfirmPath from '../../hooks/useSyncConfirmPath'; import { Confirmation } from '../../types/confirm'; @@ -32,7 +28,6 @@ export const ConfirmContextProvider: React.FC<{ useState(true); const { currentConfirmation } = useCurrentConfirmation(confirmationId); useSyncConfirmPath(currentConfirmation, confirmationId); - const dispatch = useDispatch(); const value = useMemo( () => ({ @@ -47,19 +42,6 @@ export const ConfirmContextProvider: React.FC<{ ], ); - // The code below is added to close address details modal when opening confirmation from account details modal - // The was account details modal is build has a complexity in routing and closing it from within account details modal - // routes it back to home page which also closes confirmation modal. - useEffect(() => { - if ( - currentConfirmation && - (currentConfirmation.type === TransactionType.revokeDelegation || - currentConfirmation.type === TransactionType.batch) - ) { - dispatch(setAccountDetailsAddress('')); - } - }, [dispatch, currentConfirmation]); - return ( {children} ); diff --git a/ui/pages/confirmations/hooks/useSmartAccountActions.test.ts b/ui/pages/confirmations/hooks/useSmartAccountActions.test.ts index a5eccbd1519d..cd19a965841d 100644 --- a/ui/pages/confirmations/hooks/useSmartAccountActions.test.ts +++ b/ui/pages/confirmations/hooks/useSmartAccountActions.test.ts @@ -8,7 +8,6 @@ import { useSmartAccountActions } from './useSmartAccountActions'; jest.mock('../../../store/actions', () => ({ rejectPendingApproval: jest.fn().mockReturnValue({}), - setAccountDetailsAddress: jest.fn(), })); const mockDispatch = jest.fn(); @@ -35,7 +34,7 @@ describe('useSmartAccountActions', () => { result.current.handleRejectUpgrade(); await flushPromises(); expect(rejectPendingApproval).toHaveBeenCalledTimes(1); - expect(mockDispatch).toHaveBeenCalledTimes(2); + expect(mockDispatch).toHaveBeenCalledTimes(1); }); }); }); diff --git a/ui/pages/home/home.component.js b/ui/pages/home/home.component.js index 470c4a6c4a9f..cfa563f33e76 100644 --- a/ui/pages/home/home.component.js +++ b/ui/pages/home/home.component.js @@ -166,7 +166,6 @@ export default class Home extends PureComponent { fetchBuyableChains: PropTypes.func.isRequired, redirectAfterDefaultPage: PropTypes.object, clearRedirectAfterDefaultPage: PropTypes.func, - setAccountDetailsAddress: PropTypes.func, isSeedlessPasswordOutdated: PropTypes.bool, isPrimarySeedPhraseBackedUp: PropTypes.bool, showShieldEntryModal: PropTypes.bool, @@ -266,18 +265,12 @@ export default class Home extends PureComponent { redirectAfterDefaultPage, navigate, clearRedirectAfterDefaultPage, - setAccountDetailsAddress, } = this.props; if ( redirectAfterDefaultPage?.shouldRedirect && redirectAfterDefaultPage?.path ) { - // Set the account details address if provided - if (redirectAfterDefaultPage?.address) { - setAccountDetailsAddress(redirectAfterDefaultPage.address); - } - navigate(redirectAfterDefaultPage.path); clearRedirectAfterDefaultPage(); } diff --git a/ui/pages/home/home.component.stories.tsx b/ui/pages/home/home.component.stories.tsx index 829459c6031e..751cd0a8e9ae 100644 --- a/ui/pages/home/home.component.stories.tsx +++ b/ui/pages/home/home.component.stories.tsx @@ -104,7 +104,6 @@ const meta: Meta = { setBasicFunctionalityModalOpen: () => {}, fetchBuyableChains: () => {}, clearRedirectAfterDefaultPage: () => {}, - setAccountDetailsAddress: () => {}, lookupSelectedNetworks: () => {}, }, }; diff --git a/ui/pages/home/home.container.js b/ui/pages/home/home.container.js index d285d849c62a..17b78271e83c 100644 --- a/ui/pages/home/home.container.js +++ b/ui/pages/home/home.container.js @@ -51,7 +51,6 @@ import { setNewTokensImportedError, setDataCollectionForMarketing, setEditedNetwork, - setAccountDetailsAddress, lookupSelectedNetworks, setPendingShieldCohort, } from '../../store/actions'; @@ -255,8 +254,6 @@ const mapDispatchToProps = (dispatch) => { fetchBuyableChains: () => dispatch(fetchBuyableChains()), clearRedirectAfterDefaultPage: () => dispatch(clearRedirectAfterDefaultPage()), - setAccountDetailsAddress: (address) => - dispatch(setAccountDetailsAddress(address)), lookupSelectedNetworks: () => dispatch(lookupSelectedNetworks()), setPendingShieldCohort: (cohort) => dispatch(setPendingShieldCohort(cohort)), diff --git a/ui/pages/multichain-accounts/account-details/btc-account-details.tsx b/ui/pages/multichain-accounts/account-details/btc-account-details.tsx deleted file mode 100644 index 3db8bcb2bc2f..000000000000 --- a/ui/pages/multichain-accounts/account-details/btc-account-details.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import React from 'react'; -import { InternalAccount } from '@metamask/keyring-internal-api'; -import { BaseAccountDetails } from '../base-account-details/base-account-details'; - -type BitcoinAccountDetailsProps = { - address: string; - account: InternalAccount; -}; - -export const BitcoinAccountDetails = ({ - address, - account, -}: BitcoinAccountDetailsProps) => { - return ; -}; diff --git a/ui/pages/multichain-accounts/account-details/evm-account-details.tsx b/ui/pages/multichain-accounts/account-details/evm-account-details.tsx deleted file mode 100644 index 09bf1d3be78a..000000000000 --- a/ui/pages/multichain-accounts/account-details/evm-account-details.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import React from 'react'; -import { InternalAccount } from '@metamask/keyring-internal-api'; -import { BaseAccountDetails } from '../base-account-details/base-account-details'; -import { SmartContractAccountToggleSection } from '../../../components/multichain-accounts/smart-contract-account-toggle-section'; -import { ACCOUNT_DETAILS_ROUTE } from '../../../helpers/constants/routes'; -import { AccountShowSrpRow } from '../../../components/multichain-accounts/account-show-srp-row/account-show-srp-row'; -import { Box } from '../../../components/component-library'; -import { AccountShowPrivateKeyRow } from '../../../components/multichain-accounts/account-show-private-key-row/account-show-private-key-row'; - -type EVMAccountDetailsProps = { - address: string; - account: InternalAccount; -}; - -export const EVMAccountDetails = ({ - address, - account, -}: EVMAccountDetailsProps) => { - return ( - - - - - - - - ); -}; diff --git a/ui/pages/multichain-accounts/account-details/hardware-account-details.tsx b/ui/pages/multichain-accounts/account-details/hardware-account-details.tsx deleted file mode 100644 index 7b734a0518d1..000000000000 --- a/ui/pages/multichain-accounts/account-details/hardware-account-details.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import React from 'react'; -import { InternalAccount } from '@metamask/keyring-internal-api'; -import { BaseAccountDetails } from '../base-account-details/base-account-details'; -import { SmartContractAccountToggleSection } from '../../../components/multichain-accounts/smart-contract-account-toggle-section'; -import { ACCOUNT_DETAILS_ROUTE } from '../../../helpers/constants/routes'; - -type HardwareAccountDetailsProps = { - address: string; - account: InternalAccount; -}; - -export const HardwareAccountDetails = ({ - address, - account, -}: HardwareAccountDetailsProps) => { - return ( - - - - ); -}; diff --git a/ui/pages/multichain-accounts/account-details/index.ts b/ui/pages/multichain-accounts/account-details/index.ts index f4138da185af..83fa94193d04 100644 --- a/ui/pages/multichain-accounts/account-details/index.ts +++ b/ui/pages/multichain-accounts/account-details/index.ts @@ -1,8 +1 @@ -export { MultichainAccountDetails } from './multichain-account-details'; -export { EVMAccountDetails } from './evm-account-details'; -export { SolanaAccountDetails } from './solana-account-details'; -export { HardwareAccountDetails } from './hardware-account-details'; -export { PrivateKeyAccountDetails } from './private-key-account-details'; -export { InstitutionalEVMAccountDetails } from './institutional-evm-account-details'; -export { BitcoinAccountDetails } from './btc-account-details'; export * from './account-type-utils'; diff --git a/ui/pages/multichain-accounts/account-details/institutional-evm-account-details.tsx b/ui/pages/multichain-accounts/account-details/institutional-evm-account-details.tsx deleted file mode 100644 index 9545f0d9a4c3..000000000000 --- a/ui/pages/multichain-accounts/account-details/institutional-evm-account-details.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import React from 'react'; -import { InternalAccount } from '@metamask/keyring-internal-api'; -import { BaseAccountDetails } from '../base-account-details/base-account-details'; - -type InstitutionalEVMAccountDetailsProps = { - address: string; - account: InternalAccount; -}; - -export const InstitutionalEVMAccountDetails = ({ - address, - account, -}: InstitutionalEVMAccountDetailsProps) => { - return ; -}; diff --git a/ui/pages/multichain-accounts/account-details/multichain-account-details.test.tsx b/ui/pages/multichain-accounts/account-details/multichain-account-details.test.tsx deleted file mode 100644 index a74116e204be..000000000000 --- a/ui/pages/multichain-accounts/account-details/multichain-account-details.test.tsx +++ /dev/null @@ -1,158 +0,0 @@ -import React from 'react'; -import { screen } from '@testing-library/react'; -import configureMockStore from 'redux-mock-store'; -import thunk from 'redux-thunk'; -import { renderWithProvider } from '../../../../test/lib/render-helpers-navigate'; -import { - MOCK_ACCOUNT_EOA, - MOCK_ACCOUNT_ERC4337, - MOCK_ACCOUNT_SOLANA_MAINNET, -} from '../../../../test/data/mock-accounts'; -import { MultichainAccountDetails } from './multichain-account-details'; - -const middleware = [thunk]; -const mockStore = configureMockStore(middleware); - -const mockUseParams = jest.fn(); -jest.mock('react-router-dom-v5-compat', () => { - return { - ...jest.requireActual('react-router-dom-v5-compat'), - useParams: () => mockUseParams(), - }; -}); - -const createMockState = (address: string, account = MOCK_ACCOUNT_EOA) => ({ - appState: { - accountDetailsAddress: address, - }, - activeTab: { - origin: 'test', - }, - metamask: { - internalAccounts: { - accounts: { - [account.id]: { - ...account, - address, - }, - }, - selectedAccount: account.id, - }, - networkConfigurationsByChainId: { - '0x1': { - chainId: '0x1', - name: 'Ethereum Mainnet', - nativeCurrency: 'ETH', - rpcEndpoints: [ - { - networkClientId: 'mainnet', - url: 'https://mainnet.infura.io/v3/', - type: 'infura', - }, - ], - defaultRpcEndpointIndex: 0, - blockExplorerUrls: ['https://etherscan.io'], - defaultBlockExplorerUrlIndex: 0, - }, - }, - selectedNetworkClientId: 'mainnet', - networksMetadata: { - mainnet: { - status: 'available', - }, - }, - keyrings: [ - { - type: 'HD Key Tree', - accounts: [address], - metadata: { - id: 'keyring1', - name: 'HD Key Tree', - }, - }, - ], - accountTree: { - wallets: { - 'wallet:1': { - metadata: { - name: 'Wallet 1', - }, - groups: { - 'group:1': { - metadata: { - name: 'Group 1', - }, - accounts: [account.id], - }, - }, - }, - }, - }, - accountsByChainId: { - '0x1': { - [address]: { - balance: '0x0', - }, - }, - }, - pinnedAccounts: [], - hiddenAccounts: [], - permissionHistory: {}, - }, -}); - -describe('AccountDetails', () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - - describe('Account Type Detection', () => { - it('should render EVM account details for EOA accounts', () => { - mockUseParams.mockReturnValue({ - address: MOCK_ACCOUNT_EOA.address, - }); - const state = createMockState(MOCK_ACCOUNT_EOA.address, MOCK_ACCOUNT_EOA); - const store = mockStore(state); - - renderWithProvider(, store); - - // Should render the base account details (which includes account name in header and details) - const accountNameElements = screen.getAllByText('Account 1'); - expect(accountNameElements).toHaveLength(2); - }); - - it('should render EVM account details for ERC-4337 accounts', () => { - mockUseParams.mockReturnValue({ - address: MOCK_ACCOUNT_ERC4337.address, - }); - const state = createMockState( - MOCK_ACCOUNT_ERC4337.address, - MOCK_ACCOUNT_ERC4337, - ); - const store = mockStore(state); - - renderWithProvider(, store); - - // Should render the base account details (which includes account name in header and details) - const accountNameElements = screen.getAllByText('Account 2'); - expect(accountNameElements).toHaveLength(2); - }); - - it('should render account details for Solana accounts', () => { - mockUseParams.mockReturnValue({ - address: MOCK_ACCOUNT_SOLANA_MAINNET.address, - }); - const state = createMockState( - MOCK_ACCOUNT_SOLANA_MAINNET.address, - MOCK_ACCOUNT_SOLANA_MAINNET, - ); - const store = mockStore(state); - - renderWithProvider(, store); - - // Should render the base account details (which includes account name in header and details) - const accountNameElements = screen.getAllByText('Solana Account'); - expect(accountNameElements).toHaveLength(2); - }); - }); -}); diff --git a/ui/pages/multichain-accounts/account-details/multichain-account-details.tsx b/ui/pages/multichain-accounts/account-details/multichain-account-details.tsx deleted file mode 100644 index d25575129094..000000000000 --- a/ui/pages/multichain-accounts/account-details/multichain-account-details.tsx +++ /dev/null @@ -1,85 +0,0 @@ -import React from 'react'; -import { useSelector } from 'react-redux'; -import { useParams } from 'react-router-dom-v5-compat'; -import { getInternalAccountByAddress } from '../../../selectors'; -import { EVMAccountDetails } from './evm-account-details'; -import { getAccountTypeCategory } from './account-type-utils'; -import { SolanaAccountDetails } from './solana-account-details'; -import { HardwareAccountDetails } from './hardware-account-details'; -import { PrivateKeyAccountDetails } from './private-key-account-details'; -import { InstitutionalEVMAccountDetails } from './institutional-evm-account-details'; -import { BitcoinAccountDetails } from './btc-account-details'; -import { TronAccountDetails } from './tron-account-details'; - -type MultichainAccountDetailsProps = { - params?: { address: string }; -}; - -export const MultichainAccountDetails = ({ - params: propsParams, -}: MultichainAccountDetailsProps = {}) => { - const hookParams = useParams(); - - const { address } = propsParams || hookParams; - const account = useSelector((state) => - getInternalAccountByAddress(state, address), - ); - - const accountTypeCategory = getAccountTypeCategory(account); - - const renderAccountDetailsByType = () => { - switch (accountTypeCategory) { - case 'evm': - return ( - - ); - - case 'solana': - return ( - - ); - - case 'hardware': - return ( - - ); - - case 'private-key': - return ( - - ); - - case 'institutional-evm': - return ( - - ); - - case 'bitcoin': - return ( - - ); - - case 'tron': - return ( - - ); - - default: - return null; - } - }; - - return renderAccountDetailsByType(); -}; diff --git a/ui/pages/multichain-accounts/account-details/private-key-account-details.tsx b/ui/pages/multichain-accounts/account-details/private-key-account-details.tsx deleted file mode 100644 index 01a4cc4257de..000000000000 --- a/ui/pages/multichain-accounts/account-details/private-key-account-details.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import React from 'react'; -import { InternalAccount } from '@metamask/keyring-internal-api'; -import { BaseAccountDetails } from '../base-account-details/base-account-details'; -import { SmartContractAccountToggleSection } from '../../../components/multichain-accounts/smart-contract-account-toggle-section'; -import { ACCOUNT_DETAILS_ROUTE } from '../../../helpers/constants/routes'; -import { AccountShowPrivateKeyRow } from '../../../components/multichain-accounts/account-show-private-key-row/account-show-private-key-row'; -import { Box } from '../../../components/component-library'; - -type PrivateKeyAccountDetailsProps = { - address: string; - account: InternalAccount; -}; - -export const PrivateKeyAccountDetails = ({ - address, - account, -}: PrivateKeyAccountDetailsProps) => { - return ( - - - - - - - ); -}; diff --git a/ui/pages/multichain-accounts/account-details/solana-account-details.tsx b/ui/pages/multichain-accounts/account-details/solana-account-details.tsx deleted file mode 100644 index 150d9d0a783d..000000000000 --- a/ui/pages/multichain-accounts/account-details/solana-account-details.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import React from 'react'; -import { InternalAccount } from '@metamask/keyring-internal-api'; -import { BaseAccountDetails } from '../base-account-details/base-account-details'; -import { AccountShowSrpRow } from '../../../components/multichain-accounts/account-show-srp-row/account-show-srp-row'; -import { Box } from '../../../components/component-library'; - -type SolanaAccountDetailsProps = { - address: string; - account: InternalAccount; -}; - -export const SolanaAccountDetails = ({ - address, - account, -}: SolanaAccountDetailsProps) => { - return ( - - - - - - ); -}; diff --git a/ui/pages/multichain-accounts/account-details/tron-account-details.tsx b/ui/pages/multichain-accounts/account-details/tron-account-details.tsx deleted file mode 100644 index 01991de7b654..000000000000 --- a/ui/pages/multichain-accounts/account-details/tron-account-details.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import React from 'react'; -import { InternalAccount } from '@metamask/keyring-internal-api'; -import { BaseAccountDetails } from '../base-account-details/base-account-details'; -import { AccountShowSrpRow } from '../../../components/multichain-accounts/account-show-srp-row/account-show-srp-row'; -import { Box } from '../../../components/component-library'; - -type TronAccountDetailsProps = { - address: string; - account: InternalAccount; -}; - -export const TronAccountDetails = ({ - address, - account, -}: TronAccountDetailsProps) => { - return ( - - - - - - ); -}; diff --git a/ui/pages/multichain-accounts/address-qr-code/address-qr-code.scss b/ui/pages/multichain-accounts/address-qr-code/address-qr-code.scss deleted file mode 100644 index c43867f4e0b4..000000000000 --- a/ui/pages/multichain-accounts/address-qr-code/address-qr-code.scss +++ /dev/null @@ -1,3 +0,0 @@ -.address-qr-code-page { - max-width: 600px; -} diff --git a/ui/pages/multichain-accounts/address-qr-code/address-qr-code.stories.tsx b/ui/pages/multichain-accounts/address-qr-code/address-qr-code.stories.tsx deleted file mode 100644 index 5e45e82f65f6..000000000000 --- a/ui/pages/multichain-accounts/address-qr-code/address-qr-code.stories.tsx +++ /dev/null @@ -1,228 +0,0 @@ -import React from 'react'; -import { Provider } from 'react-redux'; -import { EthAccountType, SolAccountType } from '@metamask/keyring-api'; -import { KeyringTypes } from '@metamask/keyring-controller'; -import configureStore from '../../../store/store'; -import { Box } from '../../../components/component-library'; -import { AddressQRCode } from './address-qr-code'; - -// Mock Ethereum Account -const MOCK_ETH_ACCOUNT = { - id: 'mock-eth-account-id', - address: '0x71C7656EC7ab88b098defB751B7401B5f6d8976F', - metadata: { - name: 'Account 1', - keyring: { - type: KeyringTypes.hd, - }, - importTime: Date.now(), - }, - options: {}, - methods: [ - 'personal_sign', - 'eth_sign', - 'eth_signTransaction', - 'eth_signTypedData_v1', - 'eth_signTypedData_v3', - 'eth_signTypedData_v4', - ], - type: EthAccountType.Eoa, -}; - -// Mock Solana Account -const MOCK_SOLANA_ACCOUNT = { - id: 'mock-solana-account-id', - address: 'DdHGa63k3vcH6kqDbX834GpeRUUef81Q8bUrBPdF937k', - metadata: { - name: 'Solana Account 1', - keyring: { - type: KeyringTypes.snap, - }, - snap: { - id: 'npm:@solana/wallet-snap', - name: 'Solana Wallet', - enabled: true, - }, - importTime: Date.now(), - }, - options: { - entropySource: 'mock-hd-keyring-id', - }, - methods: [ - 'solana_signTransaction', - 'solana_signAllTransactions', - 'solana_signMessage', - ], - type: SolAccountType.DataAccount, -}; - -// Minimal mock store data with network configurations -const createBaseMockStore = (account, address) => ({ - appState: { - accountDetailsAddress: address, - }, - metamask: { - internalAccounts: { - accounts: { - [account.id]: account, - }, - selectedAccount: account.id, - }, - accounts: { - [account.address]: { - address: account.address, - balance: '0x1bc16d674ec80000', // 2 ETH in hex - }, - }, - keyrings: [ - { - type: KeyringTypes.hd, - accounts: account.type === EthAccountType.Eoa ? [account.address] : [], - }, - { - type: KeyringTypes.snap, - accounts: account.type === SolAccountType.DataAccount ? [account.address] : [], - }, - ], - useBlockie: false, - providerConfig: { - chainId: '0x1', - type: 'mainnet', - rpcUrl: account.type === EthAccountType.Eoa - ? 'https://mainnet.infura.io/v3/abc123' - : 'https://api.mainnet-beta.solana.com', - nickname: account.type === EthAccountType.Eoa ? 'Ethereum Mainnet' : 'Solana Mainnet', - ticker: account.type === EthAccountType.Eoa ? 'ETH' : 'SOL', - rpcPrefs: { - blockExplorerUrl: account.type === EthAccountType.Eoa - ? 'https://etherscan.io' - : 'https://explorer.solana.com', - }, - }, - networkConfigurations: { - mainnet: { - chainId: '0x1', - nickname: 'Ethereum Mainnet', - rpcUrl: 'https://mainnet.infura.io/v3/abc123', - ticker: 'ETH', - type: 'custom', - blockExplorerUrl: 'https://etherscan.io', - rpcEndpoints: [ - { - url: 'https://mainnet.infura.io/v3/abc123', - type: 'custom', - networkClientId: 'mainnet', - }, - ], - defaultRpcEndpointIndex: 0, - name: 'Ethereum Mainnet', - nativeCurrency: 'ETH', - }, - }, - networkConfigurationsByChainId: { - '0x1': { - chainId: '0x1', - nickname: 'Ethereum Mainnet', - rpcUrl: 'https://mainnet.infura.io/v3/abc123', - ticker: 'ETH', - type: 'custom', - blockExplorerUrl: 'https://etherscan.io', - rpcEndpoints: [ - { - url: 'https://mainnet.infura.io/v3/abc123', - type: 'custom', - networkClientId: 'mainnet', - }, - ], - defaultRpcEndpointIndex: 0, - name: 'Ethereum Mainnet', - nativeCurrency: 'ETH', - }, - }, - selectedNetworkClientId: 'mainnet', - isEvmSelected: account.type !== SolAccountType.DataAccount, - selectedMultichainNetworkChainId: account.type === SolAccountType.DataAccount - ? 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp' - : '0x1', - multichainNetworkConfigurationsByChainId: account.type === SolAccountType.DataAccount ? { - 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp': { - chainId: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp', - name: 'Solana Mainnet', - nativeCurrency: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/slip44:501', - isEvm: false, - }, - } : {}, - }, -}); - -// Story wrapper component -function StoryWrapper({ children, mockStore }) { - return ( - - - - - {children} - - - - - ); -} - -export default { - title: 'Pages/MultichainAccounts/AddressQRCode', - component: AddressQRCode, - parameters: { - initialEntries: ['/'], - path: '*', - }, -}; - -// Ethereum Account QR Code Story -export const EthereumAddressQR = { - render: () => ( - - - - ), - parameters: { - initialEntries: [`/address-qr-code/${MOCK_ETH_ACCOUNT.address}`], - path: '/address-qr-code/:address', - }, -}; - -// Solana Account QR Code Story -export const SolanaAddressQR = { - render: () => ( - - - - ), - parameters: { - initialEntries: [`/address-qr-code/${MOCK_SOLANA_ACCOUNT.address}`], - path: '/address-qr-code/:address', - }, -}; diff --git a/ui/pages/multichain-accounts/address-qr-code/address-qr-code.test.tsx b/ui/pages/multichain-accounts/address-qr-code/address-qr-code.test.tsx deleted file mode 100644 index 4c315031ece6..000000000000 --- a/ui/pages/multichain-accounts/address-qr-code/address-qr-code.test.tsx +++ /dev/null @@ -1,184 +0,0 @@ -import React from 'react'; -import { screen, fireEvent, waitFor } from '@testing-library/react'; -import configureStore from 'redux-mock-store'; -import thunk from 'redux-thunk'; -import { renderWithProvider } from '../../../../test/lib/render-helpers-navigate'; -import { openBlockExplorer } from '../../../components/multichain/menu-items/view-explorer-menu-item'; -import { getMultichainAccountUrl } from '../../../helpers/utils/multichain/blockExplorer'; -import { useMultichainSelector } from '../../../hooks/useMultichainSelector'; - -import { AddressQRCode } from './address-qr-code'; - -// Mock the block explorer utility -jest.mock( - '../../../components/multichain/menu-items/view-explorer-menu-item', - () => ({ - openBlockExplorer: jest.fn(), - }), -); - -// Mock the multichain block explorer helper -jest.mock('../../../helpers/utils/multichain/blockExplorer', () => ({ - getMultichainAccountUrl: jest.fn(), -})); - -// Mock the useMultichainSelector hook -jest.mock('../../../hooks/useMultichainSelector', () => ({ - useMultichainSelector: jest.fn(), -})); - -// Mock react-router-dom-v5-compat -const mockUseNavigate = jest.fn(); -const mockUseParams = jest.fn(); -jest.mock('react-router-dom-v5-compat', () => { - const actual = jest.requireActual('react-router-dom-v5-compat'); - return { - ...actual, - useNavigate: () => mockUseNavigate, - useParams: () => mockUseParams(), - }; -}); - -// Mock i18n -jest.mock('../../../hooks/useI18nContext', () => ({ - useI18nContext: () => (key: string, substitutions?: string[]) => { - const translations: Record = { - address: '[address]', - viewOnExplorer: 'View on explorer', - viewAddressOnExplorer: `View on ${substitutions?.[0]}`, - }; - return translations[key] || key; - }, -})); - -const mockStore = configureStore([thunk]); - -// Cast the imported functions to mocked versions -const mockUseMultichainSelector = useMultichainSelector as jest.MockedFunction< - typeof useMultichainSelector ->; -const mockGetMultichainAccountUrl = - getMultichainAccountUrl as jest.MockedFunction< - typeof getMultichainAccountUrl - >; -const mockOpenBlockExplorer = openBlockExplorer as jest.MockedFunction< - typeof openBlockExplorer ->; - -const mockBlockExplorerUrl = - 'https://etherscan.io/address/0x1234567890abcdef1234567890abcdef12345678'; - -const mockAccount = { - id: 'account-1', - address: '0x1234567890abcdef1234567890abcdef12345678', - metadata: { - name: 'Test Account', - keyring: { - type: 'HD Key Tree', - }, - }, - options: {}, - methods: [], - type: 'eip155:eoa', -}; - -const mockMultichainNetwork = { - chainId: 'eip155:1', - name: 'Ethereum Mainnet', - nativeCurrency: { - symbol: 'ETH', - name: 'Ethereum', - decimals: 18, - }, - blockExplorerUrls: ['https://etherscan.io'], -}; - -const mockState = { - appState: { - accountDetailsAddress: mockAccount.address, - }, - metamask: { - internalAccounts: { - accounts: { - [mockAccount.id]: mockAccount, - }, - selectedAccount: mockAccount.id, - }, - keyrings: [ - { - type: 'HD Key Tree', - accounts: [mockAccount.address], - }, - ], - }, -}; - -const renderComponent = (state = mockState, address = mockAccount.address) => { - const store = mockStore(state); - - // Set up useParams mock to return the address - mockUseParams.mockReturnValue({ address }); - - return renderWithProvider(, store); -}; - -describe('AddressQRCode', () => { - beforeEach(() => { - // Clear all mock calls but not implementations - mockUseNavigate.mockClear(); - mockUseParams.mockClear(); - mockUseMultichainSelector.mockClear(); - mockGetMultichainAccountUrl.mockClear(); - - // Set up default mock return values - mockUseMultichainSelector.mockReturnValue(mockMultichainNetwork); - mockGetMultichainAccountUrl.mockReturnValue(mockBlockExplorerUrl); - }); - - describe('Component Rendering', () => { - it('should render the page with back button', () => { - renderComponent(); - - expect(screen.getByLabelText('Back')).toBeInTheDocument(); - }); - - it('should render view on etherscan button', () => { - renderComponent(); - - const explorerButton = screen.getByRole('button', { - name: 'View on Etherscan', - }); - expect(explorerButton).toBeInTheDocument(); - }); - }); - - describe('Navigation', () => { - it('should navigate back to account details when back button is clicked', () => { - renderComponent(); - - const backButton = screen.getByLabelText('Back'); - fireEvent.click(backButton); - - expect(mockUseNavigate).toHaveBeenCalledWith(-1); - }); - }); - - describe('Block Explorer Integration', () => { - it('should open block explorer when view on etherscan button is clicked', async () => { - renderComponent(); - - const explorerButton = screen.getByRole('button', { - name: 'View on Etherscan', - }); - fireEvent.click(explorerButton); - - await waitFor(() => { - expect(mockOpenBlockExplorer).toHaveBeenCalledWith( - mockBlockExplorerUrl, - 'Account Details QR Code Page', - expect.any(Function), - ); - }); - }); - }); -}); diff --git a/ui/pages/multichain-accounts/address-qr-code/address-qr-code.tsx b/ui/pages/multichain-accounts/address-qr-code/address-qr-code.tsx deleted file mode 100644 index 918bfe8b718a..000000000000 --- a/ui/pages/multichain-accounts/address-qr-code/address-qr-code.tsx +++ /dev/null @@ -1,132 +0,0 @@ -import React, { useCallback, useContext } from 'react'; -import { parseCaipChainId } from '@metamask/utils'; -import { useSelector } from 'react-redux'; -import { useNavigate, useParams } from 'react-router-dom-v5-compat'; -import { - Page, - Header, - Content, - Footer, -} from '../../../components/multichain/pages/page'; -import { useI18nContext } from '../../../hooks/useI18nContext'; -import { - ButtonIcon, - ButtonIconSize, - ButtonSecondary, - ButtonSecondarySize, - IconName, -} from '../../../components/component-library'; -import { - BackgroundColor, - TextVariant, -} from '../../../helpers/constants/design-system'; -import { PREVIOUS_ROUTE } from '../../../helpers/constants/routes'; -import QrCodeView from '../../../components/ui/qr-code-view'; -import { getInternalAccountByAddress } from '../../../selectors'; -import { getMultichainNetwork } from '../../../selectors/multichain'; -import { useMultichainSelector } from '../../../hooks/useMultichainSelector'; -import { getMultichainAccountUrl } from '../../../helpers/utils/multichain/blockExplorer'; -import { openBlockExplorer } from '../../../components/multichain/menu-items/view-explorer-menu-item'; -import { MetaMetricsContext } from '../../../contexts/metametrics'; -import { - MetaMetricsEventName, - MetaMetricsEventCategory, -} from '../../../../shared/constants/metametrics'; -import { getAccountTypeCategory } from '../account-details'; - -type AddressQRCodeProps = { - params?: { address: string }; -}; - -export const AddressQRCode = ({ - params: propsParams, -}: AddressQRCodeProps = {}) => { - const t = useI18nContext(); - const navigate = useNavigate(); - const hookParams = useParams(); - - const { address } = propsParams || hookParams; - const trackEvent = useContext(MetaMetricsContext); - const account = useSelector((state) => - getInternalAccountByAddress(state, address), - ); - - const multichainNetwork = useMultichainSelector( - getMultichainNetwork, - account, - ); - - const addressLink = getMultichainAccountUrl( - account.address, - multichainNetwork, - ); - - const chainId = parseCaipChainId(multichainNetwork.chainId).reference; - - const metricsLocation = 'Account Details QR Code Page'; - - const handleNavigation = useCallback(() => { - trackEvent({ - event: MetaMetricsEventName.BlockExplorerLinkClicked, - category: MetaMetricsEventCategory.Accounts, - properties: { - location: metricsLocation, - // TODO: Fix in https://github.com/MetaMask/metamask-extension/issues/31860 - // eslint-disable-next-line @typescript-eslint/naming-convention - chain_id: chainId, - }, - }); - openBlockExplorer(addressLink, metricsLocation, trackEvent); - }, [chainId, trackEvent, addressLink]); - - const getExplorerButtonText = (): string => { - switch (getAccountTypeCategory(account)) { - case 'evm': - return t('viewAddressOnExplorer', ['Etherscan']); - case 'solana': - return t('viewAddressOnExplorer', ['Solscan']); - default: - return t('viewOnExplorer'); - } - }; - - return ( - -
navigate(PREVIOUS_ROUTE)} - /> - } - > - {t('address')} -
- - - -
- - {getExplorerButtonText()} - -
-
- ); -}; diff --git a/ui/pages/multichain-accounts/address-qr-code/index.ts b/ui/pages/multichain-accounts/address-qr-code/index.ts deleted file mode 100644 index cb2e0bbc3190..000000000000 --- a/ui/pages/multichain-accounts/address-qr-code/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './address-qr-code'; diff --git a/ui/pages/multichain-accounts/base-account-details/base-account-details.scss b/ui/pages/multichain-accounts/base-account-details/base-account-details.scss deleted file mode 100644 index 519377799a26..000000000000 --- a/ui/pages/multichain-accounts/base-account-details/base-account-details.scss +++ /dev/null @@ -1,20 +0,0 @@ -.multichain-account-details-page { - max-width: 600px; - - .multichain-account-details__section { - .multichain-account-details__row { - margin-bottom: 1px; - - &:first-of-type { - border-top-left-radius: 8px; - border-top-right-radius: 8px; - } - - &:last-of-type { - border-bottom-left-radius: 8px; - border-bottom-right-radius: 8px; - margin-bottom: 0; - } - } - } -} diff --git a/ui/pages/multichain-accounts/base-account-details/base-account-details.stories.tsx b/ui/pages/multichain-accounts/base-account-details/base-account-details.stories.tsx deleted file mode 100644 index d8d6c141d5a3..000000000000 --- a/ui/pages/multichain-accounts/base-account-details/base-account-details.stories.tsx +++ /dev/null @@ -1,220 +0,0 @@ -import React from 'react'; -import { Provider } from 'react-redux'; -import { EthAccountType, SolAccountType } from '@metamask/keyring-api'; -import { KeyringTypes } from '@metamask/keyring-controller'; -import { InternalAccount } from '@metamask/keyring-internal-api'; -import configureStore from '../../../store/store'; -import { Box } from '../../../components/component-library'; -import { BaseAccountDetails } from './base-account-details'; - -// Mock Ethereum Account -const MOCK_ETH_ACCOUNT = { - id: 'mock-eth-account-id', - address: '0x71C7656EC7ab88b098defB751B7401B5f6d8976F', - metadata: { - name: 'Account 1', - keyring: { - type: KeyringTypes.hd, - }, - importTime: Date.now(), - }, - options: {}, - methods: [ - 'personal_sign', - 'eth_sign', - 'eth_signTransaction', - 'eth_signTypedData_v1', - 'eth_signTypedData_v3', - 'eth_signTypedData_v4', - ], - scopes: ['eip155:1'], - type: EthAccountType.Eoa, -} as InternalAccount; - -// Mock Solana Account -const MOCK_SOLANA_ACCOUNT = { - id: 'mock-solana-account-id', - address: 'DdHGa63k3vcH6kqDbX834GpeRUUef81Q8bUrBPdF937k', - metadata: { - name: 'Solana Account 1', - keyring: { - type: KeyringTypes.snap, - }, - snap: { - id: 'npm:@solana/wallet-snap', - name: 'Solana Wallet', - enabled: true, - }, - importTime: Date.now(), - }, - options: { - entropySource: 'mock-hd-keyring-id', - }, - methods: [ - 'solana_signTransaction', - 'solana_signAllTransactions', - 'solana_signMessage', - ], - scopes: ['solana:mainnet'] as const, - type: SolAccountType.DataAccount, -} as InternalAccount; - -// Minimal mock store data -const createBaseMockStore = (account, address, walletName = 'Mock Wallet') => ({ - appState: { - accountDetailsAddress: address, - }, - activeTab: { - id: 1, - title: 'Test Dapp', - origin: 'https://test-dapp.com', - protocol: 'https:', - url: 'https://test-dapp.com', - }, - metamask: { - useBlockie: false, - internalAccounts: { - accounts: { - [account.id]: { - ...account, - address, - }, - }, - selectedAccount: account.id, - }, - accountTree: { - wallets: { - 'mock-wallet-id': { - id: 'mock-wallet-id', - metadata: { - name: walletName, - }, - groups: { - 'mock-wallet-id:default': { - id: 'mock-wallet-id:default', - metadata: { - name: 'Default', - }, - accounts: [account.id], - }, - }, - }, - }, - }, - networkConfigurationsByChainId: { - '0x1': { - chainId: '0x1', - name: 'Ethereum Mainnet', - nativeCurrency: 'ETH', - blockExplorerUrls: ['https://etherscan.io'], - rpcEndpoints: [ - { - url: 'https://mainnet.infura.io/v3/your-project-id', - type: 'infura', - networkClientId: 'mainnet', - }, - ], - defaultRpcEndpointIndex: 0, - defaultBlockExplorerUrlIndex: 0, - }, - }, - selectedNetworkClientId: 'mainnet', - accountsByChainId: { - '0x1': { - [address]: { - balance: '0x0', - address, - }, - }, - }, - keyrings: [ - { - type: 'HD Key Tree', - accounts: [address], - index: 0, - metadata: { - id: 'mock-hd-keyring-id', - name: 'HD Key Tree', - }, - }, - ], - permissionHistory: { - 'https://test-dapp.com': { - eth_accounts: { - accounts: { - [address]: Date.now(), - }, - }, - }, - }, - pinnedAccountsList: [], - hiddenAccountsList: [], - connectedAccounts: [], - }, -}); - -// Story wrapper component similar to PendingApproval -function StoryWrapper({ children, mockStore }) { - return ( - - - - - {children} - - - - - ); -} - -export default { - title: 'Pages/MultichainAccounts/BaseAccountDetails', - component: BaseAccountDetails, -}; - -// Ethereum Account Story -export const EthereumAccount = { - render: () => ( - - - - ), -}; - -// Solana Account Story -export const SolanaAccount = { - render: () => ( - - - - ), -}; diff --git a/ui/pages/multichain-accounts/base-account-details/base-account-details.test.tsx b/ui/pages/multichain-accounts/base-account-details/base-account-details.test.tsx deleted file mode 100644 index 8ccf2829a4de..000000000000 --- a/ui/pages/multichain-accounts/base-account-details/base-account-details.test.tsx +++ /dev/null @@ -1,485 +0,0 @@ -import React from 'react'; -import { fireEvent, screen } from '@testing-library/react'; -import configureMockStore from 'redux-mock-store'; -import thunk from 'redux-thunk'; -import { renderWithProvider } from '../../../../test/lib/render-helpers-navigate'; -import { - MOCK_ACCOUNT_EOA, - MOCK_ACCOUNT_SOLANA_MAINNET, -} from '../../../../test/data/mock-accounts'; -import { ACCOUNT_DETAILS_QR_CODE_ROUTE } from '../../../helpers/constants/routes'; -import { KeyringType } from '../../../../shared/constants/keyring'; -import { FirstTimeFlowType } from '../../../../shared/constants/onboarding'; -import { BaseAccountDetails } from './base-account-details'; - -const middleware = [thunk]; -const mockStore = configureMockStore(middleware); - -const mockUseNavigate = jest.fn(); -jest.mock('react-router-dom-v5-compat', () => { - return { - ...jest.requireActual('react-router-dom-v5-compat'), - useNavigate: () => mockUseNavigate, - }; -}); - -const createMockState = ( - address: string, - account = MOCK_ACCOUNT_EOA, - useBlockie = false, - walletName = 'Mock Wallet', -) => ({ - appState: { - accountDetailsAddress: address, - }, - activeTab: { - id: 1, - title: 'Test Dapp', - origin: 'https://test-dapp.com', - protocol: 'https:', - url: 'https://test-dapp.com', - }, - metamask: { - useBlockie, - internalAccounts: { - accounts: { - [account.id]: { - ...account, - address, - }, - }, - selectedAccount: account.id, - }, - accountTree: { - wallets: { - 'mock-wallet-id': { - id: 'mock-wallet-id', - metadata: { - name: walletName, - }, - groups: { - 'mock-wallet-id:default': { - id: 'mock-wallet-id:default', - metadata: { - name: 'Default', - }, - accounts: [account.id], - }, - }, - }, - }, - }, - // Required for network selectors - networkConfigurationsByChainId: { - '0x1': { - chainId: '0x1', - name: 'Ethereum Mainnet', - nativeCurrency: 'ETH', - blockExplorerUrls: ['https://etherscan.io'], - rpcEndpoints: [ - { - url: 'https://mainnet.infura.io/v3/your-project-id', - type: 'infura', - networkClientId: 'mainnet', - }, - ], - defaultRpcEndpointIndex: 0, - defaultBlockExplorerUrlIndex: 0, - }, - }, - selectedNetworkClientId: 'mainnet', - // Required for account balance selectors - accountsByChainId: { - '0x1': { - [address]: { - balance: '0x0', - address, - }, - }, - }, - // Required for keyring selectors - keyrings: [ - { - type: 'HD Key Tree', - accounts: [address], - index: 0, - metadata: { - id: 'mock-hd-keyring-id', - name: 'HD Key Tree', - }, - }, - ], - // Required for permission selectors - permissionHistory: { - 'https://test-dapp.com': { - // TODO: Fix in https://github.com/MetaMask/metamask-extension/issues/31860 - // eslint-disable-next-line @typescript-eslint/naming-convention - eth_accounts: { - accounts: { - [address]: Date.now(), - }, - }, - }, - }, - // Required for account lists - pinnedAccountsList: [], - hiddenAccountsList: [], - connectedAccounts: [], - }, -}); - -describe('BaseAccountDetails', () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - - describe('Component Rendering', () => { - it('should render with EVM account correctly', () => { - const state = createMockState(MOCK_ACCOUNT_EOA.address); - const store = mockStore(state); - - renderWithProvider( - , - store, - ); - - // Check if account name is displayed in header - expect(screen.getAllByText('Account 1')).toHaveLength(2); // Header + details section - - // Check if account details section is rendered - expect(screen.getByText('Account name')).toBeInTheDocument(); - expect(screen.getByText('Address')).toBeInTheDocument(); - expect(screen.getByText('Wallet')).toBeInTheDocument(); - - // Check if shortened address is displayed (short address stays as-is) - expect(screen.getByText('0xa0b86...f5e4b')).toBeInTheDocument(); - - // Check if wallet name is displayed - expect(screen.getByText('Mock Wallet')).toBeInTheDocument(); - }); - - it('should render with non-EVM (Solana) account correctly', () => { - const state = createMockState( - MOCK_ACCOUNT_SOLANA_MAINNET.address, - MOCK_ACCOUNT_SOLANA_MAINNET, - ); - const store = mockStore(state); - - renderWithProvider( - , - store, - ); - - // Check if Solana account name is displayed (allow multiple occurrences) - expect(screen.getAllByText('Solana Account')).toHaveLength(2); - - // Check if Solana address is displayed correctly (7 chars + ... + 5 chars) - expect(screen.getByText('8A4AptC...aaLGC')).toBeInTheDocument(); - - // Check if wallet name is displayed - expect(screen.getByText('Mock Wallet')).toBeInTheDocument(); - }); - - it('should render children when passed', () => { - const state = createMockState(MOCK_ACCOUNT_EOA.address); - const store = mockStore(state); - - renderWithProvider( - -
Test Child Component
-
, - store, - ); - - expect(screen.getByTestId('test-child')).toBeInTheDocument(); - expect(screen.getByText('Test Child Component')).toBeInTheDocument(); - }); - }); - - describe('Navigation', () => { - it('should go back when back button is clicked', () => { - const state = createMockState(MOCK_ACCOUNT_EOA.address); - const store = mockStore(state); - - renderWithProvider( - , - store, - ); - - const backButton = screen.getByLabelText('Back'); - fireEvent.click(backButton); - - expect(mockUseNavigate).toHaveBeenCalledWith(-1); - }); - - it('should navigate to QR code route when address row is clicked', () => { - const state = createMockState(MOCK_ACCOUNT_EOA.address); - const store = mockStore(state); - - renderWithProvider( - , - store, - ); - - // Find all "next" buttons and click the first one (address row) - const nextButtons = screen.getAllByLabelText('Next'); - const addressRowButton = nextButtons[0]; - fireEvent.click(addressRowButton); - - expect(mockUseNavigate).toHaveBeenCalledWith( - `${ACCOUNT_DETAILS_QR_CODE_ROUTE}/${MOCK_ACCOUNT_EOA.address}`, - ); - }); - - it('should navigate to wallet details when wallet row is clicked', () => { - const state = createMockState(MOCK_ACCOUNT_EOA.address); - const store = mockStore(state); - - renderWithProvider( - , - store, - ); - - // Find all "next" buttons and click the second one (wallet row) - const nextButtons = screen.getAllByLabelText('Next'); - const walletRowButton = nextButtons[1]; - fireEvent.click(walletRowButton); - - expect(mockUseNavigate).toHaveBeenCalledWith( - '/wallet-details/mock-wallet-id', - ); - }); - }); - - describe('Account Name Editing', () => { - it('should open edit account name modal when edit button is clicked', () => { - const state = createMockState(MOCK_ACCOUNT_EOA.address); - const store = mockStore(state); - - renderWithProvider( - , - store, - ); - - const editButton = screen.getByLabelText('Edit'); - fireEvent.click(editButton); - - // Check if modal is opened by looking for the edit modal text - expect(screen.getByText('Edit account name')).toBeInTheDocument(); - }); - }); - - describe('Address Formatting', () => { - it('should display checksummed and shortened address for EVM accounts', () => { - const mockEvmAccount = { - ...MOCK_ACCOUNT_EOA, - address: '0xABCDEF1234567890ABCDEF1234567890ABCDEF12', - }; - const state = createMockState(mockEvmAccount.address, mockEvmAccount); - const store = mockStore(state); - - renderWithProvider( - , - store, - ); - - // Should display shortened checksummed address (7 chars + ... + 5 chars) - expect(screen.getByText('0xabcde...def12')).toBeInTheDocument(); - }); - - it('should display non-EVM address as-is without checksumming', () => { - const state = createMockState( - MOCK_ACCOUNT_SOLANA_MAINNET.address, - MOCK_ACCOUNT_SOLANA_MAINNET, - ); - const store = mockStore(state); - - renderWithProvider( - , - store, - ); - - // Should display shortened Solana address without checksumming (7 chars + ... + 5 chars) - expect(screen.getByText('8A4AptC...aaLGC')).toBeInTheDocument(); - }); - }); - - describe('Modal Integration', () => { - it('should render EditAccountNameModal with correct props when editing', () => { - const state = createMockState(MOCK_ACCOUNT_EOA.address); - const store = mockStore(state); - - renderWithProvider( - , - store, - ); - - // Open the modal - const editButton = screen.getByLabelText('Edit'); - fireEvent.click(editButton); - - // Check if modal is rendered by looking for the modal text content - expect(screen.getByText('Edit account name')).toBeInTheDocument(); - }); - }); - - describe('Account Removal', () => { - it('should display remove account button for removable accounts', () => { - const hardwareAccount = { - ...MOCK_ACCOUNT_EOA, - id: 'hardware-account', - metadata: { - ...MOCK_ACCOUNT_EOA.metadata, - name: 'Hardware Account', - keyring: { - type: KeyringType.trezor, - }, - }, - }; - - const state = createMockState(hardwareAccount.address, hardwareAccount); - const store = mockStore(state); - - renderWithProvider( - , - store, - ); - - const removeButton = screen.getByText('Remove account'); - expect(removeButton).toBeInTheDocument(); - - // Verify that the modal is triggered and present - fireEvent.click(removeButton); - - expect( - screen.getByText('This account will be removed from MetaMask.'), - ).toBeInTheDocument(); - expect( - screen.getByText( - 'Make sure you have the Secret Recovery Phrase or private key for this account before removing.', - ), - ).toBeInTheDocument(); - }); - - it('should not display remove account button for non-removable accounts', () => { - const hdKeyTreeAccount = { - ...MOCK_ACCOUNT_EOA, - id: 'hd-account', - metadata: { - ...MOCK_ACCOUNT_EOA.metadata, - name: 'HD Account', - keyring: { - type: KeyringType.hdKeyTree, - }, - }, - }; - - const state = createMockState(hdKeyTreeAccount.address, hdKeyTreeAccount); - const store = mockStore(state); - - renderWithProvider( - , - store, - ); - - const removeButton = screen.queryByText('Remove account'); - expect(removeButton).not.toBeInTheDocument(); - - const solanaAccount = { - ...MOCK_ACCOUNT_SOLANA_MAINNET, - id: 'solana-account', - metadata: { - ...MOCK_ACCOUNT_SOLANA_MAINNET.metadata, - name: 'Solana Account', - keyring: { - type: KeyringType.snap, - }, - }, - }; - - const solanaState = createMockState(solanaAccount.address, solanaAccount); - const solanaStore = mockStore(solanaState); - - renderWithProvider( - , - solanaStore, - ); - - const solanaRemoveButton = screen.queryByText('Remove account'); - expect(solanaRemoveButton).not.toBeInTheDocument(); - }); - - it('should not display remove account button for social login accounts', () => { - const hardwareAccount = { - ...MOCK_ACCOUNT_EOA, - id: 'hardware-account', - metadata: { - ...MOCK_ACCOUNT_EOA.metadata, - name: 'Hardware Account', - keyring: { - type: KeyringType.trezor, - }, - }, - }; - - const state = createMockState(hardwareAccount.address, hardwareAccount); - const store = mockStore({ - ...state, - metamask: { - ...state.metamask, - firstTimeFlowType: FirstTimeFlowType.socialCreate, - }, - }); - - renderWithProvider( - , - store, - ); - - const removeButton = screen.queryByText('Remove account'); - expect(removeButton).not.toBeInTheDocument(); - }); - }); -}); diff --git a/ui/pages/multichain-accounts/base-account-details/base-account-details.tsx b/ui/pages/multichain-accounts/base-account-details/base-account-details.tsx deleted file mode 100644 index 056f62b80521..000000000000 --- a/ui/pages/multichain-accounts/base-account-details/base-account-details.tsx +++ /dev/null @@ -1,264 +0,0 @@ -import React, { useCallback, useContext, useState } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; -import { useNavigate } from 'react-router-dom-v5-compat'; -import { isEvmAccountType } from '@metamask/keyring-api'; -import { InternalAccount } from '@metamask/keyring-internal-api'; -import { formatChainIdToCaip } from '@metamask/bridge-controller'; -import { AvatarAccountSize } from '@metamask/design-system-react'; -import { - getAccountTypeForKeyring, - getHardwareWalletType, - getHDEntropyIndex, - getIsSocialLoginFlow, - isSolanaAccount, -} from '../../../selectors'; -import { - Box, - Button, - ButtonIcon, - ButtonIconSize, - ButtonSize, - ButtonVariant, -} from '../../../components/component-library'; -import { - Content, - Header, - Page, -} from '../../../components/multichain/pages/page'; -import { - BackgroundColor, - BlockSize, - IconColor, -} from '../../../helpers/constants/design-system'; -import { - ACCOUNT_DETAILS_QR_CODE_ROUTE, - DEFAULT_ROUTE, - PREVIOUS_ROUTE, -} from '../../../helpers/constants/routes'; -import { IconName } from '../../../components/component-library/icon'; -import { toChecksumHexAddress } from '../../../../shared/modules/hexstring-utils'; -import { shortenAddress } from '../../../helpers/utils/util'; -import { useI18nContext } from '../../../hooks/useI18nContext'; -import { AccountDetailsRow } from '../../../components/multichain-accounts/account-details-row'; -import { EditAccountNameModal } from '../../../components/multichain-accounts/edit-account-name-modal'; -import { - removeAccount, - setAccountDetailsAddress, -} from '../../../store/actions'; -import { getWalletIdAndNameByAccountAddress } from '../../../selectors/multichain-accounts/account-tree'; -import { WalletMetadata } from '../../../selectors/multichain-accounts/account-tree.types'; -import { KeyringType } from '../../../../shared/constants/keyring'; -import { AccountRemoveModal } from '../../../components/multichain-accounts/account-remove-modal'; -import { - MetaMetricsEventCategory, - MetaMetricsEventName, -} from '../../../../shared/constants/metametrics'; -import { MetaMetricsContext } from '../../../contexts/metametrics'; -import { getCurrentChainId } from '../../../../shared/modules/selectors/networks'; -import { formatAccountType } from '../../../helpers/utils/metrics'; -import { PreferredAvatar } from '../../../components/app/preferred-avatar'; - -type BaseAccountDetailsProps = { - children?: React.ReactNode | React.ReactNode[]; - account: InternalAccount; - address: string; -}; - -export const BaseAccountDetails = ({ - children, - account, - address, -}: BaseAccountDetailsProps) => { - const navigate = useNavigate(); - const dispatch = useDispatch(); - const t = useI18nContext(); - const trackEvent = useContext(MetaMetricsContext); - const chainId = useSelector(getCurrentChainId); - const hdEntropyIndex = useSelector(getHDEntropyIndex); - const deviceName = useSelector(getHardwareWalletType); - const socialLoginFlow = useSelector(getIsSocialLoginFlow); - - const { - metadata: { name }, - type, - } = account; - const formattedAddress = isEvmAccountType(type) - ? toChecksumHexAddress(address as string)?.toLowerCase() - : address; - const shortenedAddress = shortenAddress(formattedAddress); - - const [isEditingAccountName, setIsEditingAccountName] = useState(false); - - const handleShowAddress = () => { - navigate(`${ACCOUNT_DETAILS_QR_CODE_ROUTE}/${address}`); - }; - - const { keyring } = account.metadata; - const accountType = formatAccountType(getAccountTypeForKeyring(keyring)); - - const handleNavigation = useCallback(() => { - dispatch(setAccountDetailsAddress('')); - navigate(PREVIOUS_ROUTE); - }, [navigate, dispatch]); - - // we can never have a scenario where an account is not associated with a wallet. - const { id: walletId, name: walletName } = useSelector((state) => - getWalletIdAndNameByAccountAddress(state, address), - ) as WalletMetadata; - - const walletRoute = `/wallet-details/${encodeURIComponent(walletId)}`; - - const isRemovable = - account.metadata.keyring.type !== KeyringType.hdKeyTree && - !isSolanaAccount(account) && - !socialLoginFlow; // social login accounts are not removable - - const [showAccountRemoveModal, setShowAccountRemoveModal] = useState(false); - - const handleAccountRemoveAction = useCallback(() => { - dispatch(removeAccount(account.address)); - - trackEvent({ - event: MetaMetricsEventName.AccountRemoved, - category: MetaMetricsEventCategory.Accounts, - properties: { - // TODO: Fix in https://github.com/MetaMask/metamask-extension/issues/31860 - // eslint-disable-next-line @typescript-eslint/naming-convention - account_hardware_type: deviceName, - // TODO: Fix in https://github.com/MetaMask/metamask-extension/issues/31860 - // eslint-disable-next-line @typescript-eslint/naming-convention - chain_id: chainId, - // TODO: Fix in https://github.com/MetaMask/metamask-extension/issues/31860 - // eslint-disable-next-line @typescript-eslint/naming-convention - account_type: accountType, - // TODO: Fix in https://github.com/MetaMask/metamask-extension/issues/31860 - // eslint-disable-next-line @typescript-eslint/naming-convention - hd_entropy_index: hdEntropyIndex, - // TODO: Fix in https://github.com/MetaMask/metamask-extension/issues/31860 - // eslint-disable-next-line @typescript-eslint/naming-convention - caip_chain_id: formatChainIdToCaip(chainId), - }, - }); - - dispatch(setAccountDetailsAddress('')); - navigate(DEFAULT_ROUTE); - }, [ - dispatch, - account.address, - trackEvent, - deviceName, - chainId, - accountType, - hdEntropyIndex, - navigate, - ]); - - return ( - -
- } - > - {name} -
- - - - - - - } - onClick={() => setIsEditingAccountName(true)} - /> - - } - onClick={handleShowAddress} - /> - - } - onClick={() => { - navigate(walletRoute); - }} - /> - - {children} - {isRemovable && ( - - - - )} - {isEditingAccountName && ( - setIsEditingAccountName(false)} - currentAccountName={name} - address={address} - /> - )} - {showAccountRemoveModal && ( - setShowAccountRemoveModal(false)} - onSubmit={handleAccountRemoveAction} - accountName={account.metadata.name} - accountAddress={account.address} - /> - )} - -
- ); -}; diff --git a/ui/pages/multichain-accounts/base-account-details/index.ts b/ui/pages/multichain-accounts/base-account-details/index.ts deleted file mode 100644 index f0409ed377e1..000000000000 --- a/ui/pages/multichain-accounts/base-account-details/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { BaseAccountDetails } from './base-account-details'; diff --git a/ui/pages/multichain-accounts/multichain-account-details-page/multichain-account-details-page.test.tsx b/ui/pages/multichain-accounts/multichain-account-details-page/multichain-account-details-page.test.tsx index 4bf5aafbf483..5456b4bbf14b 100644 --- a/ui/pages/multichain-accounts/multichain-account-details-page/multichain-account-details-page.test.tsx +++ b/ui/pages/multichain-accounts/multichain-account-details-page/multichain-account-details-page.test.tsx @@ -200,15 +200,9 @@ describe('MultichainAccountDetailsPage', () => { fireEvent.click(removeButton); // Verify that dispatch was called with removeAccount action - expect(mockDispatch).toHaveBeenCalledTimes(2); + expect(mockDispatch).toHaveBeenCalledTimes(1); // First call should be the removeAccount thunk (AsyncFunction) expect(mockDispatch).toHaveBeenNthCalledWith(1, expect.any(Function)); - - // Second call should be setAccountDetailsAddress action - expect(mockDispatch).toHaveBeenNthCalledWith(2, { - type: 'SET_ACCOUNT_DETAILS_ADDRESS', - payload: '', - }); }); }); diff --git a/ui/pages/multichain-accounts/multichain-account-details-page/multichain-account-details-page.tsx b/ui/pages/multichain-accounts/multichain-account-details-page/multichain-account-details-page.tsx index 9148baee6dce..2db1840ef3ff 100644 --- a/ui/pages/multichain-accounts/multichain-account-details-page/multichain-account-details-page.tsx +++ b/ui/pages/multichain-accounts/multichain-account-details-page/multichain-account-details-page.tsx @@ -46,10 +46,7 @@ import { MultichainSrpBackup } from '../../../components/multichain-accounts/mul import { useWalletInfo } from '../../../hooks/multichain-accounts/useWalletInfo'; import { MultichainAccountEditModal } from '../../../components/multichain-accounts/multichain-account-edit-modal'; import { AccountRemoveModal } from '../../../components/multichain-accounts/account-remove-modal'; -import { - removeAccount, - setAccountDetailsAddress, -} from '../../../store/actions'; +import { removeAccount } from '../../../store/actions'; import { MetaMetricsEventCategory, MetaMetricsEventName, @@ -143,7 +140,6 @@ export const MultichainAccountDetailsPage = ({ }, }); - dispatch(setAccountDetailsAddress('')); navigate(DEFAULT_ROUTE); } }, [dispatch, trackEvent, navigate, wallet?.type, accountsWithAddresses]); diff --git a/ui/pages/multichain-accounts/wallet-details/index.ts b/ui/pages/multichain-accounts/wallet-details/index.ts deleted file mode 100644 index 0429f84e2c07..000000000000 --- a/ui/pages/multichain-accounts/wallet-details/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import WalletDetails from './wallet-details.component'; - -export default WalletDetails; diff --git a/ui/pages/multichain-accounts/wallet-details/wallet-details.component.stories.tsx b/ui/pages/multichain-accounts/wallet-details/wallet-details.component.stories.tsx deleted file mode 100644 index 46fa348dd6af..000000000000 --- a/ui/pages/multichain-accounts/wallet-details/wallet-details.component.stories.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import React from 'react'; -import type { Meta, StoryObj } from '@storybook/react'; -import { Provider } from 'react-redux'; -import configureStore from '../../../store/store'; -import testData from '../../../../.storybook/test-data'; -import WalletDetails from './wallet-details.component'; - -// Mock the accountTree data that WalletDetails expects -const mockAccountTree = { - wallets: { - 'entropy:test-entropy-wallet': { - id: 'entropy:test-entropy-wallet', - metadata: { - name: 'Test Wallet', - }, - groups: { - 'entropy:test-entropy-wallet': { - id: 'entropy:test-entropy-wallet', - metadata: { - name: 'Default Group', - }, - accounts: ['cf8dace4-9439-4bd4-b3a8-88c821c8fcb3'], - }, - }, - }, - }, -}; - -const store = configureStore({ - ...testData, - metamask: { - ...testData.metamask, - // Add the accountTree to the metamask state - accountTree: mockAccountTree, - }, -}); - -const walletId = encodeURIComponent('entropy:test-entropy-wallet'); - -const meta: Meta = { - title: 'Pages/MultichainAccounts/WalletDetails', - component: WalletDetails, - parameters: { - initialEntries: [`/wallet-details/${walletId}`], - path: '/wallet-details/:id', - }, - decorators: [ - (story) => ( - - {story()} - - ), - ], -}; - -export default meta; -type Story = StoryObj; - -export const Default: Story = {}; diff --git a/ui/pages/multichain-accounts/wallet-details/wallet-details.component.test.tsx b/ui/pages/multichain-accounts/wallet-details/wallet-details.component.test.tsx deleted file mode 100644 index 42582213a4eb..000000000000 --- a/ui/pages/multichain-accounts/wallet-details/wallet-details.component.test.tsx +++ /dev/null @@ -1,489 +0,0 @@ -import '@testing-library/jest-dom'; -import React from 'react'; -import { render, fireEvent, waitFor } from '@testing-library/react'; -import { Provider } from 'react-redux'; -import { useNavigate, useParams } from 'react-router-dom-v5-compat'; -import { SolScope } from '@metamask/keyring-api'; -import type { InternalAccount } from '@metamask/keyring-internal-api'; -import type { AccountGroupId, AccountWalletId } from '@metamask/account-api'; -import configureStore from '../../../store/store'; -import mockState from '../../../../test/data/mock-state.json'; -import { getWalletsWithAccounts } from '../../../selectors/multichain-accounts/account-tree'; -import { getIsPrimarySeedPhraseBackedUp } from '../../../ducks/metamask/metamask'; -import { getMetaMaskHdKeyrings } from '../../../selectors'; -import { createMockInternalAccount } from '../../../../test/jest/mocks'; -import WalletDetails from './wallet-details.component'; - -// Shared mock functions -const mockCreateAccount = jest.fn(); -const mockAddNewAccount = jest.fn(); -const mockSetAccountLabel = jest.fn(); -const mockGetNextAvailableAccountName = jest.fn(); - -// Shared mock clients -const mockSolanaClient = { createAccount: mockCreateAccount }; -const mockBitcoinClient = { createAccount: mockCreateAccount }; - -// Shared component mock factory -const createComponentMock = - (tag: string) => - ({ - children, - ...props - }: { - children?: React.ReactNode; - [key: string]: unknown; - }) => - React.createElement(tag, props, children); - -// Consolidated mocks -jest.mock('../../../hooks/useI18nContext', () => ({ - useI18nContext: () => (key: string) => key, -})); - -jest.mock('../../../hooks/accounts/useMultichainWalletSnapClient', () => ({ - WalletClientType: { - Bitcoin: 'bitcoin-wallet-snap', - Solana: 'solana-wallet-snap', - }, - EVM_WALLET_TYPE: 'evm', - useMultichainWalletSnapClient: jest.fn((clientType) => { - if (clientType === 'solana-wallet-snap') { - return mockSolanaClient; - } - if (clientType === 'bitcoin-wallet-snap') { - return mockBitcoinClient; - } - return null; - }), -})); - -jest.mock('../../../store/actions', () => ({ - setAccountDetailsAddress: jest.fn(() => ({ - type: 'SET_ACCOUNT_DETAILS_ADDRESS', - })), - addNewAccount: - (...args: unknown[]) => - () => - mockAddNewAccount(...args), - setAccountLabel: - (...args: unknown[]) => - () => - mockSetAccountLabel(...args), - getNextAvailableAccountName: (...args: unknown[]) => - mockGetNextAvailableAccountName(...args), -})); - -jest.mock('../../../selectors', () => ({ - getMetaMaskHdKeyrings: jest.fn(), - getIsBitcoinSupportEnabled: jest.fn(() => true), - getIsSolanaSupportEnabled: jest.fn(() => true), -})); - -jest.mock('react-router-dom-v5-compat', () => ({ - ...jest.requireActual('react-router-dom-v5-compat'), - useNavigate: jest.fn(), - useParams: jest.fn(), -})); - -jest.mock('../../../selectors/multichain-accounts/account-tree', () => ({ - getWalletsWithAccounts: jest.fn(), -})); - -jest.mock('../../../ducks/metamask/metamask', () => ({ - getIsPrimarySeedPhraseBackedUp: jest.fn(), -})); - -// Consolidated component mocks -jest.mock('../../../components/component-library', () => ({ - Box: createComponentMock('div'), - ButtonIcon: ({ - children, - ariaLabel, - ...props - }: { - children?: React.ReactNode; - ariaLabel?: string; - [key: string]: unknown; - }) => { - const buttonProps = { ...props }; - if (ariaLabel) { - buttonProps['aria-label'] = ariaLabel; - } - return createComponentMock('button')({ children, ...buttonProps }); - }, - ButtonIconSize: { Sm: 'sm' }, - Icon: createComponentMock('span'), - IconName: { ArrowLeft: 'arrow-left', ArrowRight: 'arrow-right', Add: 'add' }, - IconSize: { Sm: 'sm', Md: 'md' }, - IconColor: { iconAlternative: 'alternative', primaryDefault: 'primary' }, - Text: createComponentMock('span'), - BannerAlert: createComponentMock('div'), - BannerAlertSeverity: { Danger: 'danger' }, - Modal: ({ - children, - isOpen, - ...props - }: { - children?: React.ReactNode; - isOpen?: boolean; - [key: string]: unknown; - }) => (isOpen ?
{children}
: null), - ModalOverlay: createComponentMock('div'), -})); - -jest.mock( - '../../../components/component-library/modal-content/deprecated', - () => ({ - ModalContent: createComponentMock('div'), - }), -); - -jest.mock('../../../components/component-library/modal-header', () => ({ - ModalHeader: ({ - children, - onClose, - ...props - }: { - children?: React.ReactNode; - onClose?: () => void; - [key: string]: unknown; - }) => ( -
- {children} - -
- ), -})); - -jest.mock('../../../components/multichain/pages/page', () => ({ - Content: createComponentMock('div'), - Header: ({ - children, - startAccessory, - ...props - }: { - children?: React.ReactNode; - startAccessory?: React.ReactNode; - [key: string]: unknown; - }) => ( -
- {startAccessory} - {children} -
- ), - Page: createComponentMock('div'), -})); - -jest.mock( - '../../../components/multichain/multichain-accounts/wallet-details-account-item/wallet-details-account-item', - () => - ({ - account, - onClick, - className, - }: { - account: InternalAccount; - onClick: (account: InternalAccount) => void; - className: string; - }) => ( -
onClick(account)} - className={className} - > - {account.metadata.name} -
- ), -); - -jest.mock( - '../../../components/app/user-preferenced-currency-display/user-preferenced-currency-display.component', - () => - ({ value }: { value: string }) => ( -
{value}
- ), -); - -jest.mock( - '../../../components/app/srp-quiz-modal', - () => - ({ isOpen, onClose }: { isOpen: boolean; onClose: () => void }) => - isOpen ? ( -
- -
- ) : null, -); - -jest.mock( - '../../../components/multichain/multichain-accounts/wallet-details-account-type-selection', - () => ({ - WalletDetailsAccountTypeSelection: ({ - onAccountTypeSelect, - onClose, - }: { - onAccountTypeSelect: (accountType: string) => void; - onClose: () => void; - }) => ( -
- - - - -
- ), - }), -); - -describe('WalletDetails', () => { - const mockNavigate = jest.fn(); - const mockParams = { id: 'entropy:test-wallet' }; - const GROUP_ID = 'entropy:test-wallet:default' as unknown as AccountGroupId; - - // Helper functions - const createMockWallet = (id: string, groups: Record) => ({ - id: id as unknown as AccountWalletId, - metadata: { name: 'Test Wallet' }, - groups, - }); - - const createMockConsolidatedAccountGroup = ( - groupId: string, - accounts: InternalAccount[], - ) => ({ - id: groupId as unknown as AccountGroupId, - metadata: { name: 'Test Group' }, - accounts, - }); - - const setupMocks = (wallets: Record = {}) => { - const mockAccount = createMockInternalAccount({ - address: '0x123', - name: 'Test Account', - }); - const defaultWallet = createMockWallet('entropy:test-wallet', { - [GROUP_ID]: createMockConsolidatedAccountGroup(GROUP_ID, [mockAccount]), - }); - const mockWallets = { - ['entropy:test-wallet' as unknown as AccountWalletId]: defaultWallet, - ...wallets, - }; - - ( - getWalletsWithAccounts as jest.MockedFunction< - typeof getWalletsWithAccounts - > - ).mockReturnValue( - mockWallets as unknown as ReturnType, - ); - (getMetaMaskHdKeyrings as jest.Mock).mockReturnValue([ - { metadata: { id: 'test-wallet' } }, - ]); - (getIsPrimarySeedPhraseBackedUp as jest.Mock).mockReturnValue(true); - }; - - const setupEntropyWalletTest = (isFirstHdKeyring: boolean = false) => { - const entropyGroupId = - 'entropy:test-entropy-wallet:default' as unknown as AccountGroupId; - const entropyWallet = createMockWallet('entropy:test-entropy-wallet', { - [entropyGroupId]: createMockConsolidatedAccountGroup(entropyGroupId, [ - createMockInternalAccount({ - address: '0x123', - name: 'Test Account', - }), - ]), - }); - - ( - getWalletsWithAccounts as jest.MockedFunction< - typeof getWalletsWithAccounts - > - ).mockReturnValue({ - ['entropy:test-entropy-wallet' as unknown as AccountWalletId]: - entropyWallet, - } as unknown as ReturnType); - - (getMetaMaskHdKeyrings as jest.Mock).mockReturnValue([ - { - metadata: { - id: isFirstHdKeyring ? 'test-entropy-wallet' : 'other-wallet', - }, - }, - ]); - (getIsPrimarySeedPhraseBackedUp as jest.Mock).mockReturnValue(false); - (useParams as jest.Mock).mockReturnValue({ - id: 'entropy:test-entropy-wallet', - }); - - return render( - - - , - ); - }; - - const renderComponent = () => - render( - - - , - ); - - beforeEach(() => { - (useNavigate as jest.Mock).mockReturnValue(mockNavigate); - (useParams as jest.Mock).mockReturnValue(mockParams); - setupMocks(); - [ - mockAddNewAccount, - mockSetAccountLabel, - mockGetNextAvailableAccountName, - mockCreateAccount, - ].forEach((mock) => mock.mockClear()); - jest.spyOn(console, 'error').mockImplementation(() => undefined); - }); - - afterEach(() => { - jest.clearAllMocks(); - (console.error as jest.Mock).mockRestore(); - }); - - it('renders wallet details correctly', () => { - const { getByText } = renderComponent(); - expect(getByText('Test Wallet')).toBeInTheDocument(); - expect(getByText('Test Account')).toBeInTheDocument(); - }); - - it('shows SRP button for entropy wallets', () => { - const { getByText } = setupEntropyWalletTest(); - expect(getByText('secretRecoveryPhrase')).toBeInTheDocument(); - }); - - it('opens SRP quiz modal when SRP button is clicked', () => { - const { getByText, getByTestId } = setupEntropyWalletTest(); - fireEvent.click(getByText('secretRecoveryPhrase')); - expect(getByTestId('mock-srp-quiz')).toBeInTheDocument(); - }); - - it('dispatches setAccountDetailsAddress when account is clicked', () => { - const { getByText } = renderComponent(); - const { setAccountDetailsAddress } = jest.requireMock( - '../../../store/actions', - ); - fireEvent.click(getByText('Test Account')); - expect(setAccountDetailsAddress).toHaveBeenCalledWith('0x123'); - }); - - it('navigates back when back button is clicked', () => { - const { getByLabelText } = renderComponent(); - fireEvent.click(getByLabelText('back')); - expect(mockNavigate).toHaveBeenCalledWith(-1); - }); - - describe('Add Account Button', () => { - it('renders add account button when wallet has accounts', () => { - const { getByText } = renderComponent(); - expect(getByText('addAccount')).toBeInTheDocument(); - }); - - it('does not render add account button when wallet has no accounts', () => { - const testWallet = createMockWallet('entropy:test-wallet', { - [GROUP_ID]: createMockConsolidatedAccountGroup(GROUP_ID, []), - }); - setupMocks({ - ['entropy:test-wallet' as unknown as AccountWalletId]: testWallet, - }); - const { queryByText } = renderComponent(); - expect(queryByText('addAccount')).not.toBeInTheDocument(); - }); - - it('opens account type selection modal when add account button is clicked', () => { - const { getByText, getByTestId } = renderComponent(); - fireEvent.click(getByText('addAccount')); - expect(getByTestId('mock-account-type-selection')).toBeInTheDocument(); - }); - - it('creates Ethereum account directly when Ethereum button is clicked', async () => { - const mockNewAccount = { address: '0x456', id: 'new-account-id' }; - mockAddNewAccount.mockResolvedValue(mockNewAccount); - - const { getByText, getByTestId, queryByTestId } = renderComponent(); - fireEvent.click(getByText('addAccount')); - fireEvent.click(getByTestId('select-ethereum-account')); - - await waitFor(() => { - expect(mockAddNewAccount).toHaveBeenCalledWith('test-wallet', false); - expect( - queryByTestId('mock-account-type-selection'), - ).not.toBeInTheDocument(); - }); - }); - - it('handles Ethereum account creation error gracefully', async () => { - const error = new Error('Account creation failed'); - mockAddNewAccount.mockRejectedValue(error); - mockGetNextAvailableAccountName.mockResolvedValue('Account 2'); - - const { getByText, getByTestId } = renderComponent(); - fireEvent.click(getByText('addAccount')); - fireEvent.click(getByTestId('select-ethereum-account')); - - await waitFor(() => { - expect(mockAddNewAccount).toHaveBeenCalledWith('test-wallet', false); - expect(mockSetAccountLabel).not.toHaveBeenCalled(); - }); - }); - - it('uses correct keyringId for entropy wallets', async () => { - const mockNewAccount = { address: '0x456', id: 'new-account-id' }; - mockAddNewAccount.mockResolvedValue(mockNewAccount); - mockGetNextAvailableAccountName.mockResolvedValue('Account 2'); - - const { getByText, getByTestId } = setupEntropyWalletTest(true); - fireEvent.click(getByText('addAccount')); - fireEvent.click(getByTestId('select-ethereum-account')); - - await waitFor(() => { - expect(mockAddNewAccount).toHaveBeenCalledWith( - 'test-entropy-wallet', - false, - ); - }); - }); - - it('creates Solana account directly when Solana button is clicked', async () => { - const { getByText, getByTestId, queryByTestId } = renderComponent(); - fireEvent.click(getByText('addAccount')); - fireEvent.click(getByTestId('select-solana-account')); - - await waitFor(() => { - expect(mockCreateAccount).toHaveBeenCalledWith( - { scope: SolScope.Mainnet, entropySource: 'test-wallet' }, - { - displayConfirmation: false, - displayAccountNameSuggestion: false, - setSelectedAccount: false, - }, - ); - expect( - queryByTestId('mock-account-type-selection'), - ).not.toBeInTheDocument(); - }); - }); - }); -}); diff --git a/ui/pages/multichain-accounts/wallet-details/wallet-details.component.tsx b/ui/pages/multichain-accounts/wallet-details/wallet-details.component.tsx deleted file mode 100644 index 6b21de852f92..000000000000 --- a/ui/pages/multichain-accounts/wallet-details/wallet-details.component.tsx +++ /dev/null @@ -1,480 +0,0 @@ -import React, { useState, useMemo, useCallback } from 'react'; -import { useNavigate, useParams } from 'react-router-dom-v5-compat'; -import { useSelector, useDispatch } from 'react-redux'; -import { AccountWalletId } from '@metamask/account-api'; -import { CaipChainId } from '@metamask/utils'; -import { - SolScope, - ///: BEGIN:ONLY_INCLUDE_IF(bitcoin) - BtcScope, - ///: END:ONLY_INCLUDE_IF - ///: BEGIN:ONLY_INCLUDE_IF(tron) - TrxScope, - ///: END:ONLY_INCLUDE_IF -} from '@metamask/keyring-api'; -import { - Box, - ButtonIcon, - ButtonIconSize, - Icon, - IconName, - IconSize, - Text, - BannerAlert, - BannerAlertSeverity, - Modal, - ModalOverlay, -} from '../../../components/component-library'; -import { ModalContent } from '../../../components/component-library/modal-content/deprecated'; -import { - AlignItems, - BlockSize, - IconColor, - Display, - TextVariant, - TextColor, - TextAlign, - JustifyContent, - BackgroundColor, - FlexDirection, -} from '../../../helpers/constants/design-system'; -import { - Content, - Header, - Page, -} from '../../../components/multichain/pages/page'; -import { getMetaMaskHdKeyrings } from '../../../selectors'; -import { useI18nContext } from '../../../hooks/useI18nContext'; -import { getWalletsWithAccounts } from '../../../selectors/multichain-accounts/account-tree'; -import { getIsPrimarySeedPhraseBackedUp } from '../../../ducks/metamask/metamask'; -import WalletDetailsAccountItem from '../../../components/multichain/multichain-accounts/wallet-details-account-item/wallet-details-account-item'; -import UserPreferencedCurrencyDisplay from '../../../components/app/user-preferenced-currency-display/user-preferenced-currency-display.component'; -import SRPQuiz from '../../../components/app/srp-quiz-modal'; -import { WalletDetailsAccountTypeSelection } from '../../../components/multichain/multichain-accounts/wallet-details-account-type-selection'; -import { - setAccountDetailsAddress, - addNewAccount, -} from '../../../store/actions'; -import { - ACCOUNT_DETAILS_ROUTE, - ONBOARDING_REVIEW_SRP_ROUTE, - PREVIOUS_ROUTE, -} from '../../../helpers/constants/routes'; -import { endTrace, trace, TraceName } from '../../../../shared/lib/trace'; -import { - EVM_WALLET_TYPE, - WalletClientType, - useMultichainWalletSnapClient, -} from '../../../hooks/accounts/useMultichainWalletSnapClient'; - -type AccountBalance = { - [key: string]: string | number; -}; - -type WalletDetailsProps = { - params?: { id: string }; -}; - -const WalletDetails = ({ params: propsParams }: WalletDetailsProps = {}) => { - const t = useI18nContext(); - const navigate = useNavigate(); - const dispatch = useDispatch(); - const hookParams = useParams(); - - const { id } = propsParams || hookParams; - const decodedId = decodeURIComponent(id as string); - const walletsWithAccounts = useSelector(getWalletsWithAccounts); - const seedPhraseBackedUp = useSelector(getIsPrimarySeedPhraseBackedUp); - const hdKeyrings = useSelector(getMetaMaskHdKeyrings); - const [srpQuizModalVisible, setSrpQuizModalVisible] = useState(false); - const wallet = walletsWithAccounts[decodedId as AccountWalletId]; - const [accountBalances, setAccountBalances] = useState({}); - const [isModalOpen, setIsModalOpen] = useState(false); - - // Initialize wallet snap clients - const solanaClient = useMultichainWalletSnapClient(WalletClientType.Solana); - ///: BEGIN:ONLY_INCLUDE_IF(bitcoin) - const bitcoinClient = useMultichainWalletSnapClient(WalletClientType.Bitcoin); - ///: END:ONLY_INCLUDE_IF - ///: BEGIN:ONLY_INCLUDE_IF(tron) - const tronClient = useMultichainWalletSnapClient(WalletClientType.Tron); - ///: END:ONLY_INCLUDE_IF - - const totalBalance = useMemo( - () => - Object.values(accountBalances) - .reduce((sum, balance) => sum + Number(balance || 0), 0) - .toString(), - [accountBalances], - ); - - const handleBalanceUpdate = useCallback( - (accountId: string, balance: string | number) => { - setAccountBalances((prev) => ({ - ...prev, - [accountId]: balance, - })); - }, - [], - ); - - const handleAccountClick = (account: { id: string; address: string }) => { - dispatch(setAccountDetailsAddress(account.address)); - navigate(`${ACCOUNT_DETAILS_ROUTE}/${account.address}`); - }; - - const handleBack = () => { - navigate(PREVIOUS_ROUTE); - }; - - if (!wallet) { - return ( - -
- } - > - {t('walletDetails')} -
- - - {t('walletNotFoundDescription', [id])} - - -
- ); - } - - const keyringId = wallet.id.split(':')[1]; - - const isEntropyWallet = wallet.id.includes('entropy'); - const isFirstHdKeyring = hdKeyrings[0]?.metadata?.id === keyringId; - const shouldShowBackupReminder = !seedPhraseBackedUp && isFirstHdKeyring; - - // Now, wallets are composed of multiple groups, so we have to flatten everything. - const accounts = Object.values(wallet.groups).flatMap( - (group) => group.accounts, - ); - - const handleAddAccount = () => { - setIsModalOpen(true); - }; - - const handleCloseModal = () => { - setIsModalOpen(false); - }; - - const handleCreateEthereumAccount = async (): Promise => { - trace({ name: TraceName.AddAccount }); - try { - await dispatch(addNewAccount(keyringId, false)); - return true; - } catch (error) { - console.error('Error creating Ethereum account:', error); - return false; - } finally { - endTrace({ name: TraceName.AddAccount }); - } - }; - - const handleCreateSnapAccount = async ( - clientType: WalletClientType, - chainId: CaipChainId, - ): Promise => { - trace({ name: TraceName.AddAccount }); - try { - let client; - - if (clientType === WalletClientType.Solana) { - client = solanaClient; - } - ///: BEGIN:ONLY_INCLUDE_IF(bitcoin) - else if (clientType === WalletClientType.Bitcoin) { - client = bitcoinClient; - } - ///: END:ONLY_INCLUDE_IF - ///: BEGIN:ONLY_INCLUDE_IF(tron) - else if (clientType === WalletClientType.Tron) { - client = tronClient; - } - ///: END:ONLY_INCLUDE_IF - else { - // TODO: Fix in https://github.com/MetaMask/metamask-extension/issues/31893 - // eslint-disable-next-line @typescript-eslint/restrict-template-expressions - console.error(`Unsupported client type: ${clientType}`); - return false; - } - - if (!client) { - console.error(`Client not available for type: ${clientType}`); - return false; - } - - await client.createAccount( - { - scope: chainId, - entropySource: keyringId, - }, - { - displayConfirmation: false, - displayAccountNameSuggestion: false, - setSelectedAccount: false, - }, - ); - return true; - } catch (error) { - console.error(`Error creating ${clientType} account:`, error); - return false; - } finally { - endTrace({ name: TraceName.AddAccount }); - } - }; - - const handleAccountTypeSelect = async ( - accountType: WalletClientType | typeof EVM_WALLET_TYPE, - ) => { - let success = false; - - if (accountType === EVM_WALLET_TYPE) { - success = await handleCreateEthereumAccount(); - } else if (accountType === WalletClientType.Solana) { - success = await handleCreateSnapAccount( - WalletClientType.Solana, - SolScope.Mainnet as CaipChainId, - ); - } - ///: BEGIN:ONLY_INCLUDE_IF(bitcoin) - else if (accountType === WalletClientType.Bitcoin) { - success = await handleCreateSnapAccount( - WalletClientType.Bitcoin, - BtcScope.Mainnet as CaipChainId, - ); - } - ///: END:ONLY_INCLUDE_IF - ///: BEGIN:ONLY_INCLUDE_IF(tron) - else if (accountType === WalletClientType.Tron) { - success = await handleCreateSnapAccount( - WalletClientType.Tron, - TrxScope.Mainnet as CaipChainId, - ); - } - ///: END:ONLY_INCLUDE_IF - - if (success) { - handleCloseModal(); - } - }; - - const rowStylesProps = { - display: Display.Flex, - justifyContent: JustifyContent.spaceBetween, - alignItems: AlignItems.center, - backgroundColor: BackgroundColor.backgroundAlternative, - }; - - return ( - -
- } - > - {t('walletDetails')} -
- - - - - {t('walletName')} - - - {wallet.metadata.name} - - - - - - {t('balance')} - - - - - - {isEntropyWallet ? ( - - { - if (shouldShowBackupReminder) { - const backUpSRPRoute = `${ONBOARDING_REVIEW_SRP_ROUTE}/?isFromReminder=true`; - navigate(backUpSRPRoute); - } else { - setSrpQuizModalVisible(true); - } - }} - > - - - {t('secretRecoveryPhrase')} - - - - {shouldShowBackupReminder ? ( - - {t('backup')} - - ) : null} - - - - - ) : null} - - {accounts.length > 0 && ( - - {accounts.map((account) => ( - - ))} - {isEntropyWallet ? ( - - - - - {t('addAccount')} - - - - ) : null} - - )} - - {isEntropyWallet && srpQuizModalVisible && ( - setSrpQuizModalVisible(false)} - closeAfterCompleting - navigate={navigate} - /> - )} - {isModalOpen && ( - - - - - - - )} -
- ); -}; - -WalletDetails.propTypes = {}; - -export default WalletDetails; diff --git a/ui/pages/multichain-accounts/wallet-details/wallet-details.scss b/ui/pages/multichain-accounts/wallet-details/wallet-details.scss deleted file mode 100644 index ccd254a47f45..000000000000 --- a/ui/pages/multichain-accounts/wallet-details/wallet-details.scss +++ /dev/null @@ -1,27 +0,0 @@ -@use "design-system"; - -.wallet-details-page { - max-width: 600px; - - .wallet-details-page__rows-container { - .wallet-details-page__row { - margin-bottom: 1px; - - &:first-of-type { - border-top-left-radius: 8px; - border-top-right-radius: 8px; - } - - &:last-of-type { - border-bottom-left-radius: 8px; - border-bottom-right-radius: 8px; - margin-bottom: 0; - } - - &.wallet-details-page__srp-button { - cursor: pointer; - border: none; - } - } - } -} diff --git a/ui/pages/pages.scss b/ui/pages/pages.scss index 6e5250f71671..20e7c623171a 100644 --- a/ui/pages/pages.scss +++ b/ui/pages/pages.scss @@ -26,11 +26,8 @@ @import 'bridge/index'; @import 'unlock-page/index'; @import 'multichain-accounts/account-list/account-list'; -@import 'multichain-accounts/wallet-details/wallet-details'; @import 'multichain-accounts/wallet-details-page/index'; @import 'multi-srp/import-srp/index'; -@import 'multichain-accounts/address-qr-code/address-qr-code'; -@import 'multichain-accounts/base-account-details/base-account-details'; @import 'multichain-accounts/multichain-account-details-page/index'; @import 'shield-plan/index'; @import 'multichain-accounts/multichain-accounts-connect-page/index'; diff --git a/ui/pages/routes/routes.component.tsx b/ui/pages/routes/routes.component.tsx index 2bbee1ebcdcd..cb8429996ec5 100644 --- a/ui/pages/routes/routes.component.tsx +++ b/ui/pages/routes/routes.component.tsx @@ -23,9 +23,7 @@ import Loading from '../../components/ui/loading-screen'; import { Modal } from '../../components/app/modals'; import Alert from '../../components/ui/alert'; import { - AccountListMenu, NetworkListMenu, - AccountDetails, ImportNftsModal, ImportTokensModal, } from '../../components/multichain'; @@ -61,9 +59,6 @@ import { DEFI_ROUTE, DEEP_LINK_ROUTE, SMART_ACCOUNT_UPDATE, - WALLET_DETAILS_ROUTE, - ACCOUNT_DETAILS_ROUTE, - ACCOUNT_DETAILS_QR_CODE_ROUTE, ACCOUNT_LIST_PAGE_ROUTE, MULTICHAIN_ACCOUNT_ADDRESS_LIST_PAGE_ROUTE, MULTICHAIN_ACCOUNT_PRIVATE_KEY_LIST_PAGE_ROUTE, @@ -91,7 +86,6 @@ import { oldestPendingConfirmationSelector, getUnapprovedTransactions, getPendingApprovals, - getIsMultichainAccountsState1Enabled, } from '../../selectors'; import { getApprovalFlows } from '../../selectors/approvals'; @@ -148,8 +142,6 @@ import { } from '../../../shared/lib/confirmation.utils'; import { type Confirmation } from '../confirmations/types/confirm'; import { SmartAccountUpdate } from '../confirmations/components/confirm/smart-account-update'; -import { MultichainAccountDetails } from '../multichain-accounts/account-details'; -import { AddressQRCode } from '../multichain-accounts/address-qr-code'; import { MultichainAccountAddressListPage } from '../multichain-accounts/multichain-account-address-list-page'; import { MultichainAccountPrivateKeyListPage } from '../multichain-accounts/multichain-account-private-key-list-page'; import MultichainAccountIntroModalContainer from '../../components/app/modals/multichain-accounts/intro-modal'; @@ -441,12 +433,6 @@ const DeepLink = mmLazy( // TODO: This is a named export. Fix incorrect type casting once `mmLazy` is updated to handle non-default export types. (() => import('../deep-link/deep-link.tsx')) as unknown as DynamicImportType, ); -const WalletDetails = mmLazy( - (() => - import( - '../multichain-accounts/wallet-details/index.ts' - )) as unknown as DynamicImportType, -); const MultichainAccountDetailsPage = mmLazy( (() => @@ -551,9 +537,6 @@ export default function Routes() { const isDeprecatedNetworkModalOpen = useAppSelector( (state) => state.appState.deprecatedNetworkModalOpen, ); - const accountDetailsAddress = useAppSelector( - (state) => state.appState.accountDetailsAddress, - ); const isImportNftsModalOpen = useAppSelector( (state) => state.appState.importNftsModal.open, ); @@ -576,10 +559,6 @@ export default function Routes() { dispatch(hideKeyringRemovalResultModal()); ///: END:ONLY_INCLUDE_IF - const isMultichainAccountsState1Enabled = useAppSelector( - getIsMultichainAccountsState1Enabled, - ); - // Multichain intro modal logic (extracted to custom hook) const { showMultichainIntroModal, setShowMultichainIntroModal } = useMultichainAccountsIntroModal(isUnlocked, location); @@ -1112,42 +1091,6 @@ export default function Routes() { paramsAsProps: false, })} - - {createV5CompatRoute<{ id: string }>(WalletDetails, { - wrapper: AuthenticatedV5Compat, - includeParams: true, - paramsAsProps: false, - })} - - - {createV5CompatRoute<{ address: string }>( - MultichainAccountDetails, - { - wrapper: AuthenticatedV5Compat, - includeParams: true, - paramsAsProps: false, - }, - )} - - - {createV5CompatRoute<{ address: string }>(AddressQRCode, { - wrapper: AuthenticatedV5Compat, - includeParams: true, - paramsAsProps: false, - })} - { - if (!accountDetailsAddress || isMultichainAccountsState1Enabled) { - return null; - } - return ( - - ); - }; - const loadMessage = loadingMessage ? getConnectingLabel(loadingMessage, { providerType, providerId }, { t }) : null; @@ -1238,16 +1169,11 @@ export default function Routes() { // is already a fullscreen interface. !isShowingDeepLinkRoute; - const accountListMenu = isMultichainAccountsState1Enabled ? ( + const accountListMenu = ( dispatch(toggleAccountMenu())} privacyMode={privacyMode} /> - ) : ( - dispatch(toggleAccountMenu())} - privacyMode={privacyMode} - /> ); const isSidepanel = getEnvironmentType() === ENVIRONMENT_TYPE_SIDEPANEL; @@ -1277,7 +1203,6 @@ export default function Routes() { /> ) : null} - {renderAccountDetails()} {isImportNftsModalOpen ? ( dispatch(hideImportNftsModal())} /> ) : null} diff --git a/ui/pages/routes/utils.js b/ui/pages/routes/utils.js index 18cded774cee..c2464cbc5982 100644 --- a/ui/pages/routes/utils.js +++ b/ui/pages/routes/utils.js @@ -23,9 +23,6 @@ import { SEND_ROUTE, SNAPS_VIEW_ROUTE, DEEP_LINK_ROUTE, - WALLET_DETAILS_ROUTE, - ACCOUNT_DETAILS_ROUTE, - ACCOUNT_DETAILS_QR_CODE_ROUTE, MULTICHAIN_ACCOUNT_DETAILS_PAGE_ROUTE, SHIELD_PLAN_ROUTE, MULTICHAIN_WALLET_DETAILS_PAGE_ROUTE, @@ -247,19 +244,6 @@ export function hideAppHeader(props) { return true; } - const isWalletDetailsPage = Boolean( - matchPath( - { - path: WALLET_DETAILS_ROUTE, - end: false, - }, - location.pathname, - ), - ); - if (isWalletDetailsPage) { - return true; - } - const isSnapsHome = Boolean( matchPath( { @@ -300,34 +284,6 @@ export function hideAppHeader(props) { return true; } - const isMultichainAccountDetailsPage = Boolean( - matchPath( - { - path: ACCOUNT_DETAILS_ROUTE, - end: false, - }, - location.pathname, - ), - ); - - if (isMultichainAccountDetailsPage) { - return true; - } - - const isMultichainAccountDetailsQRCodePage = Boolean( - matchPath( - { - path: ACCOUNT_DETAILS_QR_CODE_ROUTE, - end: false, - }, - location.pathname, - ), - ); - - if (isMultichainAccountDetailsQRCodePage) { - return true; - } - const isHandlingAddEthereumChainRequest = Boolean( matchPath( { diff --git a/ui/store/actionConstants.ts b/ui/store/actionConstants.ts index 08c181c0dda9..08145882f828 100644 --- a/ui/store/actionConstants.ts +++ b/ui/store/actionConstants.ts @@ -60,7 +60,6 @@ export const SHOW_SEND_TOKEN_PAGE = 'SHOW_SEND_TOKEN_PAGE'; export const SHOW_PRIVATE_KEY = 'SHOW_PRIVATE_KEY'; export const SET_ACCOUNT_LABEL = 'SET_ACCOUNT_LABEL'; export const CLEAR_ACCOUNT_DETAILS = 'CLEAR_ACCOUNT_DETAILS'; -export const SET_ACCOUNT_DETAILS_ADDRESS = 'SET_ACCOUNT_DETAILS_ADDRESS'; // tx conf screen export const COMPLETED_TX = 'COMPLETED_TX'; export const TRANSACTION_ERROR = 'TRANSACTION_ERROR'; diff --git a/ui/store/actions.ts b/ui/store/actions.ts index 0e356d40cd65..45b0071aab96 100644 --- a/ui/store/actions.ts +++ b/ui/store/actions.ts @@ -4722,13 +4722,6 @@ export function toggleNetworkMenu(payload?: { }; } -export function setAccountDetailsAddress(address: string) { - return { - type: actionConstants.SET_ACCOUNT_DETAILS_ADDRESS, - payload: address, - }; -} - export function setParticipateInMetaMetrics( participationPreference: boolean, ): ThunkAction<