diff --git a/.github/workflows/wait-for-circleci-workflow-status.yml b/.github/workflows/wait-for-circleci-workflow-status.yml index 18e5ef7825d5..725a4c3b975d 100644 --- a/.github/workflows/wait-for-circleci-workflow-status.yml +++ b/.github/workflows/wait-for-circleci-workflow-status.yml @@ -13,8 +13,13 @@ jobs: OWNER: ${{ github.repository_owner }} REPOSITORY: ${{ github.event.repository.name }} BRANCH: ${{ github.head_ref || github.ref_name }} + # For a `push` event, the HEAD commit hash is `github.sha`. + # For a `pull_request` event, `github.sha` is instead the base branch commit hash. The + # HEAD commit hash is `pull_request.head.sha`. + HEAD_COMMIT_HASH: ${{ github.event.pull_request.head.sha || github.sha }} run: | - pipeline_id=$(curl --silent "https://circleci.com/api/v2/project/gh/$OWNER/$REPOSITORY/pipeline?branch=$BRANCH" | jq -r ".items[0].id") + pipeline_id=$(curl --silent "https://circleci.com/api/v2/project/gh/$OWNER/$REPOSITORY/pipeline?branch=$BRANCH" | jq -r ".items | map(select(.vcs.revision == \"${HEAD_COMMIT_HASH}\" )) | first | .id") + echo "Waiting for pipeline '${pipeline_id}' for commit hash '${HEAD_COMMIT_HASH}'" workflow_status=$(curl --silent "https://circleci.com/api/v2/pipeline/$pipeline_id/workflow" | jq -r ".items[0].status") if [ "$workflow_status" == "running" ]; then diff --git a/.yarn/patches/@metamask-network-controller-npm-22.1.0-621c281f70.patch b/.yarn/patches/@metamask-network-controller-npm-22.1.1-09b6510f1e.patch similarity index 85% rename from .yarn/patches/@metamask-network-controller-npm-22.1.0-621c281f70.patch rename to .yarn/patches/@metamask-network-controller-npm-22.1.1-09b6510f1e.patch index 027c44b96395..a1a3e7401c0c 100644 --- a/.yarn/patches/@metamask-network-controller-npm-22.1.0-621c281f70.patch +++ b/.yarn/patches/@metamask-network-controller-npm-22.1.1-09b6510f1e.patch @@ -1,6 +1,6 @@ diff --git a/PATCH.txt b/PATCH.txt new file mode 100644 -index 0000000000000000000000000000000000000000..ce3b18534f055ee00aa5821793f855fd300fb72c +index 0000000000000000000000000000000000000000..376255036ca54b83a3f3c3e0277c2666473df30e --- /dev/null +++ b/PATCH.txt @@ -0,0 +1,4 @@ @@ -8,9 +8,8 @@ index 0000000000000000000000000000000000000000..ce3b18534f055ee00aa5821793f855fd +The network lookup is done after onboarding is completed, and when the extension reloads if onboarding has been completed. +This patch is part of a temporary fix that will be reverted soon to make way for a more permanent solution. https://github.com/MetaMask/metamask-extension/pull/23005 +You can see the changes before compilation on this branch: https://github.com/MetaMask/core/compare/pnf/ext-23622-review?expand=1 -\ No newline at end of file diff --git a/dist/NetworkController.cjs b/dist/NetworkController.cjs -index cc9793f576eb39a51ab141b7d03de57cf99e5570..c573b5134d40f522217a6ab6df129040d02e9660 100644 +index cc9793f576eb39a51ab141b7d03de57cf99e5570..184153067f2bbd58ea76d7db33c2af56245cd8c0 100644 --- a/dist/NetworkController.cjs +++ b/dist/NetworkController.cjs @@ -422,7 +422,6 @@ class NetworkController extends base_controller_1.BaseController { @@ -22,7 +21,7 @@ index cc9793f576eb39a51ab141b7d03de57cf99e5570..c573b5134d40f522217a6ab6df129040 /** * Refreshes the network meta with EIP-1559 support and the network status diff --git a/dist/NetworkController.mjs b/dist/NetworkController.mjs -index 806f32edeffaad9f7eb1cafa4184368ec95f63e7..9268947cbed4bf717729ca6ac8ea83a8b91b6e8a 100644 +index 806f32edeffaad9f7eb1cafa4184368ec95f63e7..7ba60e613ec8de7d273c32282be564f36873cbd2 100644 --- a/dist/NetworkController.mjs +++ b/dist/NetworkController.mjs @@ -397,7 +397,6 @@ export class NetworkController extends BaseController { diff --git a/CHANGELOG.md b/CHANGELOG.md index 9408b61ce731..eda15db00496 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [12.9.2] +### Changed +- Display the "Amount" row within the advanced view of contract interaction confirmations, and whenever the amount being sent differs from the "You Send" row of the transaction simulation information by more than 5% ([#29131](https://github.com/MetaMask/metamask-extension/pull/29131)) +- Improved phishing detection protections ([#28782](https://github.com/MetaMask/metamask-extension/pull/28782)) + +### Fixed +- Ensure that the correct fallback letter is used for network icons within the token list ([#29121](https://github.com/MetaMask/metamask-extension/pull/29121)) +- Ensure users have to click through a blocking red warning before submitting multiple Smart Transactions while one is already pending ([#29140](https://github.com/MetaMask/metamask-extension/pull/29140)) +- Prevent users from being stuck on an "Invalid string length" error screen, by deleting tokens from their state of the data was invalid because the `decimals` property of the token was `null` ([#29245](https://github.com/MetaMask/metamask-extension/pull/29245)) + ## [12.9.1] ### Changed - The 'All Networks' view of assets on the home screen will now only get data across the 9 'popular networks' ([#29071](https://github.com/MetaMask/metamask-extension/pull/29071)) @@ -5477,7 +5487,8 @@ Update styles and spacing on the critical error page ([#20350](https://github.c - Added the ability to restore accounts from seed words. -[Unreleased]: https://github.com/MetaMask/metamask-extension/compare/v12.9.1...HEAD +[Unreleased]: https://github.com/MetaMask/metamask-extension/compare/v12.9.2...HEAD +[12.9.2]: https://github.com/MetaMask/metamask-extension/compare/v12.9.1...v12.9.2 [12.9.1]: https://github.com/MetaMask/metamask-extension/compare/v12.9.0...v12.9.1 [12.9.0]: https://github.com/MetaMask/metamask-extension/compare/v12.8.1...v12.9.0 [12.8.1]: https://github.com/MetaMask/metamask-extension/compare/v12.8.0...v12.8.1 diff --git a/app/_locales/de/messages.json b/app/_locales/de/messages.json index a738b56b0fe0..946f556f1abd 100644 --- a/app/_locales/de/messages.json +++ b/app/_locales/de/messages.json @@ -330,9 +330,6 @@ "addingCustomNetwork": { "message": "Netzwerk wird hinzugefügt" }, - "addingTokens": { - "message": "Hinzufügen von Token" - }, "additionalNetworks": { "message": "Zusätzliche Netzwerke" }, @@ -627,12 +624,6 @@ "assetsDescription": { "message": "Automatische Erkennung von Tokens in Ihrer Wallet, Anzeige von NFTs und stapelweise Aktualisierung des Kontostands" }, - "attemptSendingAssets": { - "message": "Wenn Sie versuchen, Assets direkt von einem Netzwerk in ein anderes zu senden, kann dies zu einem dauerhaften Asset-Verlust führen. Verwenden Sie unbedingt eine Bridge." - }, - "attemptSendingAssetsWithPortfolio": { - "message": "Sie können Ihre Assets verlieren, wenn Sie versuchen, sie von einem anderen Netzwerk zu versenden. Übertragen Sie Gelder sicher zwischen den Netzwerken, indem Sie eine Bridge wie $1 verwenden." - }, "attemptToCancelSwapForFree": { "message": "Versuch, den Swap kostenlos zu stornieren" }, @@ -840,9 +831,6 @@ "bridgeCalculatingAmount": { "message": "Berechnen ..." }, - "bridgeDontSend": { - "message": "Bridge, nicht senden" - }, "bridgeEnterAmount": { "message": "Betrag eingeben" }, @@ -954,9 +942,6 @@ "message": "Klicken Sie hier, um Ihren Ledger über WebHID zu verbinden.", "description": "Text that can be clicked to open a browser popup for connecting the ledger device via webhid" }, - "clickToManuallyAdd": { - "message": "Sie können Tokens jederzeit manuell hinzufügen." - }, "close": { "message": "Schließen" }, @@ -2138,9 +2123,6 @@ "gasFee": { "message": "Gas-Gebühr" }, - "gasIsETH": { - "message": "Gas ist $1" - }, "gasLimit": { "message": "Gas-Limit" }, @@ -2702,9 +2684,6 @@ "learnScamRisk": { "message": "Betrügereien und Sicherheitsrisiken." }, - "learnToBridge": { - "message": "Lernen Sie zu bridgen" - }, "leaveMetaMask": { "message": "MetaMask verlassen?" }, @@ -2900,9 +2879,6 @@ "metamaskNotificationsAreOff": { "message": "Wallet-Benachrichtigungen sind momentan nicht aktiv." }, - "metamaskPortfolio": { - "message": "MetaMask Portfolio." - }, "metamaskSwapsOfflineDescription": { "message": "MetaMask Swaps wird gewartet. Bitte versuchen Sie es später erneut." }, @@ -3047,10 +3023,6 @@ "message": "$1 bittet um Ihre Zustimmung zu:", "description": "$1 represents dapp name" }, - "nativeToken": { - "message": "Das native Token dieses Netzwerks ist $1. Dieses Token wird für die Gas-Gebühr verwendet. ", - "description": "$1 represents the name of the native token on the current network" - }, "nativeTokenScamWarningConversion": { "message": "Netzwerkdetails bearbeiten" }, @@ -5940,9 +5912,6 @@ "message": "$1 ist jetzt aktiv bei $2", "description": "$1 represents the account name, $2 represents the network name" }, - "switchedTo": { - "message": "Sie verwenden jetzt" - }, "switchingNetworksCancelsPendingConfirmations": { "message": "Das Wechseln der Netzwerke wird alle ausstehenden Bestätigungen stornieren." }, @@ -5979,9 +5948,6 @@ "themeDescription": { "message": "Wählen Sie Ihr bevorzugtes MetaMask-Motiv aus." }, - "thingsToKeep": { - "message": "Was Sie beachten sollten:" - }, "thirdPartySoftware": { "message": "Mitteilung bzgl. Drittanbieter-Software", "description": "Title of a popup modal displayed when installing a snap for the first time." @@ -6063,9 +6029,6 @@ "tokenScamSecurityRisk": { "message": "Token-Betrügereien und Sicherheitsrisiken" }, - "tokenShowUp": { - "message": "Ihre Tokens werden möglicherweise nicht automatisch in Ihrer Wallet angezeigt. " - }, "tokenStandard": { "message": "Token-Standard" }, diff --git a/app/_locales/el/messages.json b/app/_locales/el/messages.json index 7992443a500a..a02ec275c224 100644 --- a/app/_locales/el/messages.json +++ b/app/_locales/el/messages.json @@ -330,9 +330,6 @@ "addingCustomNetwork": { "message": "Προσθήκη δικτύου" }, - "addingTokens": { - "message": "Προσθήκη tokens" - }, "additionalNetworks": { "message": "Επιπλέον δίκτυα" }, @@ -627,12 +624,6 @@ "assetsDescription": { "message": "Αυτόματος εντοπισμός tokens στο πορτοφόλι σας, εμφάνιση NFT και ομαδοποιημένες ενημερώσεις υπολοίπων λογαριασμών" }, - "attemptSendingAssets": { - "message": "Ενδέχεται να χάσετε τα περιουσιακά σας στοιχεία εάν προσπαθήσετε να τα στείλετε από άλλο δίκτυο. Μεταφέρετε κεφάλαια με ασφάλεια μεταξύ δικτύων χρησιμοποιώντας μια διασύνδεση." - }, - "attemptSendingAssetsWithPortfolio": { - "message": "Μπορεί να χάσετε τα περιουσιακά σας στοιχεία αν προσπαθήσετε να τα στείλετε από άλλο δίκτυο. Μεταφέρετε χρήματα με ασφάλεια μεταξύ δικτύων χρησιμοποιώντας μια διασύνδεση, όπως το $1" - }, "attemptToCancelSwapForFree": { "message": "Προσπάθεια ακύρωσης των ανταλλαγών δωρεάν" }, @@ -840,9 +831,6 @@ "bridgeCalculatingAmount": { "message": "Υπολογισμός..." }, - "bridgeDontSend": { - "message": "Μην στείλετε χωρίς διασύνδεση" - }, "bridgeEnterAmount": { "message": "Πληκτρολογήστε το ποσό" }, @@ -954,9 +942,6 @@ "message": "Κάντε κλικ εδώ για να συνδέσετε το Ledger σας μέσω WebHID", "description": "Text that can be clicked to open a browser popup for connecting the ledger device via webhid" }, - "clickToManuallyAdd": { - "message": "Μπορείτε πάντα να προσθέσετε τα tokens χειροκίνητα." - }, "close": { "message": "Κλείσιμο" }, @@ -2138,9 +2123,6 @@ "gasFee": { "message": "Τέλη συναλλαγών" }, - "gasIsETH": { - "message": "Τέλη συναλλαγής $1 " - }, "gasLimit": { "message": "Όριο τέλους συναλλαγής" }, @@ -2702,9 +2684,6 @@ "learnScamRisk": { "message": "απάτες και κίνδυνοι ασφάλειας." }, - "learnToBridge": { - "message": "Μάθετε για την διασύνδεση" - }, "leaveMetaMask": { "message": "Αποχώρηση από το MetaMask;" }, @@ -2900,9 +2879,6 @@ "metamaskNotificationsAreOff": { "message": "Οι ειδοποιήσεις του πορτοφολιού δεν είναι προς το παρόν ενεργές." }, - "metamaskPortfolio": { - "message": "MetaMask Portfolio." - }, "metamaskSwapsOfflineDescription": { "message": "Το MetaMask Swaps είναι υπό συντήρηση. Παρακαλείστε να επιστρέψετε αργότερα." }, @@ -3047,10 +3023,6 @@ "message": "Το $1 ζητάει την έγκρισή σας για:", "description": "$1 represents dapp name" }, - "nativeToken": { - "message": "Το αρχικό token σε αυτό το δίκτυο είναι το $1. Είναι το token που χρησιμοποιείται για τα τέλη συναλλαγών.", - "description": "$1 represents the name of the native token on the current network" - }, "nativeTokenScamWarningConversion": { "message": "Επεξεργασία λεπτομερειών δικτύου" }, @@ -5940,9 +5912,6 @@ "message": "Το $1 είναι τώρα ενεργό στο $2", "description": "$1 represents the account name, $2 represents the network name" }, - "switchedTo": { - "message": "Τώρα χρησιμοποιείτε το" - }, "switchingNetworksCancelsPendingConfirmations": { "message": "Η αλλαγή δικτύων θα ακυρώσει όλες τις εκκρεμείς επιβεβαιώσεις" }, @@ -5979,9 +5948,6 @@ "themeDescription": { "message": "Επιλέξτε το προτιμώμενο θέμα σας για το MetaMask." }, - "thingsToKeep": { - "message": "Πράγματα που πρέπει να έχετε υπόψη σας:" - }, "thirdPartySoftware": { "message": "Ειδοποίηση για λογισμικό τρίτων", "description": "Title of a popup modal displayed when installing a snap for the first time." @@ -6063,9 +6029,6 @@ "tokenScamSecurityRisk": { "message": "απάτες με token και κίνδυνοι ασφάλειας" }, - "tokenShowUp": { - "message": "Τα tokens σας ενδέχεται να μην εμφανιστούν αυτόματα στο πορτοφόλι σας." - }, "tokenStandard": { "message": "Πρότυπο Token" }, diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 3bfd303e5dd0..7ddf609b48f0 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -330,9 +330,6 @@ "addingCustomNetwork": { "message": "Adding Network" }, - "addingTokens": { - "message": "Adding tokens" - }, "additionalNetworks": { "message": "Additional networks" }, @@ -648,12 +645,6 @@ "assetsDescription": { "message": "Autodetect tokens in your wallet, display NFTs, and get batched account balance updates" }, - "attemptSendingAssets": { - "message": "You may lose your assets if you try to send them from another network. Transfer funds safely between networks by using a bridge." - }, - "attemptSendingAssetsWithPortfolio": { - "message": "You may lose your assets if you try to send them from another network. Transfer funds safely between networks by using a bridge, like $1" - }, "attemptToCancelSwapForFree": { "message": "Attempt to cancel swap for free" }, @@ -880,9 +871,6 @@ "bridgeConfirmTwoTransactions": { "message": "You'll need to confirm 2 transactions on your hardware wallet:" }, - "bridgeDontSend": { - "message": "Bridge, don't send" - }, "bridgeEnterAmount": { "message": "Select amount" }, @@ -1089,9 +1077,6 @@ "message": "Click here to connect your Ledger via WebHID", "description": "Text that can be clicked to open a browser popup for connecting the ledger device via webhid" }, - "clickToManuallyAdd": { - "message": "You can always add tokens manually." - }, "close": { "message": "Close" }, @@ -2299,9 +2284,6 @@ "gasFee": { "message": "Gas fee" }, - "gasIsETH": { - "message": "Gas is $1 " - }, "gasLimit": { "message": "Gas limit" }, @@ -2387,7 +2369,9 @@ "gotIt": { "message": "Got it" }, - "grantExactAccess": { "message": "Grant exact access" }, + "grantExactAccess": { + "message": "Grant exact access" + }, "grantedToWithColon": { "message": "Granted to:" }, @@ -2870,9 +2854,6 @@ "learnScamRisk": { "message": "scams and security risks." }, - "learnToBridge": { - "message": "Learn to bridge" - }, "leaveMetaMask": { "message": "Leave MetaMask?" }, @@ -3077,9 +3058,6 @@ "metamaskNotificationsAreOff": { "message": "Wallet notifications are currently not active." }, - "metamaskPortfolio": { - "message": "MetaMask Portfolio." - }, "metamaskSwapsOfflineDescription": { "message": "MetaMask Swaps is undergoing maintenance. Please check back later." }, @@ -3230,10 +3208,6 @@ "message": "$1 is asking for your approval to:", "description": "$1 represents dapp name" }, - "nativeToken": { - "message": "The native token on this network is $1. It is the token used for gas fees. ", - "description": "$1 represents the name of the native token on the current network" - }, "nativeTokenScamWarningConversion": { "message": "Edit network details" }, @@ -4527,7 +4501,9 @@ "quotedReceiveAmount": { "message": "$1 receive amount" }, - "quotedTotalCost": { "message": "$1 total cost" }, + "quotedTotalCost": { + "message": "$1 total cost" + }, "rank": { "message": "Rank" }, @@ -6230,9 +6206,6 @@ "message": "You're now using $1", "description": "$1 represents the network name" }, - "switchedTo": { - "message": "You're now using" - }, "switchingNetworksCancelsPendingConfirmations": { "message": "Switching networks will cancel all pending confirmations" }, @@ -6269,9 +6242,6 @@ "themeDescription": { "message": "Choose your preferred MetaMask theme." }, - "thingsToKeep": { - "message": "Keep in mind:" - }, "thirdPartySoftware": { "message": "Third-party software notice", "description": "Title of a popup modal displayed when installing a snap for the first time." @@ -6353,9 +6323,6 @@ "tokenScamSecurityRisk": { "message": "token scams and security risks" }, - "tokenShowUp": { - "message": "Your tokens may not automatically show up in your wallet. " - }, "tokenStandard": { "message": "Token standard" }, @@ -6876,7 +6843,9 @@ "you": { "message": "You" }, - "youDeclinedTheTransaction": { "message": "You declined the transaction." }, + "youDeclinedTheTransaction": { + "message": "You declined the transaction." + }, "youNeedToAllowCameraAccess": { "message": "You need to allow camera access to use this feature." }, diff --git a/app/_locales/es/messages.json b/app/_locales/es/messages.json index 2475977d0794..8acce0ad02d9 100644 --- a/app/_locales/es/messages.json +++ b/app/_locales/es/messages.json @@ -330,9 +330,6 @@ "addingCustomNetwork": { "message": "Agregando red" }, - "addingTokens": { - "message": "Agregando tokens" - }, "additionalNetworks": { "message": "Redes adicionales" }, @@ -627,12 +624,6 @@ "assetsDescription": { "message": "Detectar automáticamente tokens en su monedero, mostrar NFT y recibir actualizaciones de saldo de cuenta por lotes" }, - "attemptSendingAssets": { - "message": "Puede perder sus activos si intenta enviarlos desde otra red. Transfiera fondos de forma segura entre redes mediante el uso de un puente." - }, - "attemptSendingAssetsWithPortfolio": { - "message": "Puede perder sus activos si intenta enviarlos desde otra red. Transfiera fondos de forma segura entre redes usando un puente, como $1" - }, "attemptToCancelSwapForFree": { "message": "Intente cancelar el intercambio de forma gratuita" }, @@ -840,9 +831,6 @@ "bridgeCalculatingAmount": { "message": "Calculando..." }, - "bridgeDontSend": { - "message": "Puente, no enviar" - }, "bridgeEnterAmount": { "message": "Ingrese el monto" }, @@ -954,9 +942,6 @@ "message": "Haga clic aquí para conectar su Ledger a través de WebHID", "description": "Text that can be clicked to open a browser popup for connecting the ledger device via webhid" }, - "clickToManuallyAdd": { - "message": "Siempre puede agregar tókenes manualmente." - }, "close": { "message": "Cerrar" }, @@ -2138,9 +2123,6 @@ "gasFee": { "message": "Tarifa de gas" }, - "gasIsETH": { - "message": "El gas es $1 " - }, "gasLimit": { "message": "Límite de gas" }, @@ -2702,9 +2684,6 @@ "learnScamRisk": { "message": "estafas y riesgos en seguridad." }, - "learnToBridge": { - "message": "Aprenda a puentear" - }, "leaveMetaMask": { "message": "¿Dejar MetaMask?" }, @@ -2900,9 +2879,6 @@ "metamaskNotificationsAreOff": { "message": "Las notificaciones del monedero no están activas actualmente." }, - "metamaskPortfolio": { - "message": "MetaMask Portfolio." - }, "metamaskSwapsOfflineDescription": { "message": "MetaMask Swaps está en mantenimiento. Vuelva a comprobarlo más tarde." }, @@ -3047,10 +3023,6 @@ "message": "$1 solicita su aprobación para:", "description": "$1 represents dapp name" }, - "nativeToken": { - "message": "El token nativo en esta red es de $1. Es el token utilizado para las tarifas de gas. ", - "description": "$1 represents the name of the native token on the current network" - }, "nativeTokenScamWarningConversion": { "message": "Editar detalles de la red" }, @@ -5940,9 +5912,6 @@ "message": "$1 ahora está activo en $2", "description": "$1 represents the account name, $2 represents the network name" }, - "switchedTo": { - "message": "Ahora está usando" - }, "switchingNetworksCancelsPendingConfirmations": { "message": "Cambiar de red cancelará todas las confirmaciones pendientes" }, @@ -5979,9 +5948,6 @@ "themeDescription": { "message": "Elija su tema MetaMask preferido." }, - "thingsToKeep": { - "message": "Tenga en cuenta:" - }, "thirdPartySoftware": { "message": "Aviso de software de terceros", "description": "Title of a popup modal displayed when installing a snap for the first time." @@ -6063,9 +6029,6 @@ "tokenScamSecurityRisk": { "message": "estafas de tokens y riesgos de seguridad" }, - "tokenShowUp": { - "message": "Es posible que sus tókenes no aparezcan automáticamente en su monedero. " - }, "tokenStandard": { "message": "Estándar de tokenes" }, diff --git a/app/_locales/fr/messages.json b/app/_locales/fr/messages.json index abe83f8ea900..68e6626c4688 100644 --- a/app/_locales/fr/messages.json +++ b/app/_locales/fr/messages.json @@ -330,9 +330,6 @@ "addingCustomNetwork": { "message": "Ajout de réseau" }, - "addingTokens": { - "message": "Ajouter des jetons" - }, "additionalNetworks": { "message": "Réseaux supplémentaires" }, @@ -627,12 +624,6 @@ "assetsDescription": { "message": "Détection automatique des jetons dans votre portefeuille, affichage des NFT et mise à jour du solde de plusieurs comptes" }, - "attemptSendingAssets": { - "message": "Si vous essayez d’envoyer des actifs directement d’un réseau à un autre, une perte permanente des actifs pourrait en résulter. Assurez-vous d’utiliser une passerelle." - }, - "attemptSendingAssetsWithPortfolio": { - "message": "Si vous essayez d’envoyer des actifs directement d’un réseau à un autre, vous pourriez les perdre définitivement. Utilisez une passerelle comme $1 pour transférer en toute sécurité des fonds d’un réseau à un autre" - }, "attemptToCancelSwapForFree": { "message": "Tentative d’annuler gratuitement le swap" }, @@ -840,9 +831,6 @@ "bridgeCalculatingAmount": { "message": "Calcul en cours…" }, - "bridgeDontSend": { - "message": "Passerelle, ne pas envoyer" - }, "bridgeEnterAmount": { "message": "Saisissez le montant" }, @@ -954,9 +942,6 @@ "message": "Cliquez ici pour connecter votre Ledger via WebHID", "description": "Text that can be clicked to open a browser popup for connecting the ledger device via webhid" }, - "clickToManuallyAdd": { - "message": "Vous pouvez ajouter des jetons manuellement." - }, "close": { "message": "Fermer" }, @@ -2138,9 +2123,6 @@ "gasFee": { "message": "Frais de gaz" }, - "gasIsETH": { - "message": "Le gaz est $1 " - }, "gasLimit": { "message": "Montant maximal des frais de transaction" }, @@ -2702,9 +2684,6 @@ "learnScamRisk": { "message": "hameçonnages et risques de sécurité." }, - "learnToBridge": { - "message": "Apprenez à établir une passerelle" - }, "leaveMetaMask": { "message": "Voulez-vous quitter MetaMask ?" }, @@ -2900,9 +2879,6 @@ "metamaskNotificationsAreOff": { "message": "Les notifications du portefeuille ne sont actuellement pas activées." }, - "metamaskPortfolio": { - "message": "Portfolio MetaMask." - }, "metamaskSwapsOfflineDescription": { "message": "MetaMask Swaps est en cours de maintenance. Nous vous invitons à revenir plus tard." }, @@ -3047,10 +3023,6 @@ "message": "$1 vous demande votre approbation pour :", "description": "$1 represents dapp name" }, - "nativeToken": { - "message": "Le jeton natif de ce réseau est $1. C’est le jeton utilisé pour les frais de gaz. ", - "description": "$1 represents the name of the native token on the current network" - }, "nativeTokenScamWarningConversion": { "message": "Modifier les détails du réseau" }, @@ -5940,9 +5912,6 @@ "message": "$1 est maintenant actif sur $2", "description": "$1 represents the account name, $2 represents the network name" }, - "switchedTo": { - "message": "Vous êtes en train d’utiliser" - }, "switchingNetworksCancelsPendingConfirmations": { "message": "Le changement de réseau annulera toutes les confirmations en attente" }, @@ -5979,9 +5948,6 @@ "themeDescription": { "message": "Choisissez votre thème MetaMask préféré." }, - "thingsToKeep": { - "message": "Les choses que vous devez garder à l’esprit :" - }, "thirdPartySoftware": { "message": "Avis sur les logiciels développés par des tiers", "description": "Title of a popup modal displayed when installing a snap for the first time." @@ -6063,9 +6029,6 @@ "tokenScamSecurityRisk": { "message": "les arnaques et les risques de piratage informatique" }, - "tokenShowUp": { - "message": "Il se peut que vos jetons n’apparaissent pas automatiquement dans votre portefeuille. " - }, "tokenStandard": { "message": "Jeton standard" }, diff --git a/app/_locales/hi/messages.json b/app/_locales/hi/messages.json index 4eaf54f8b012..1f96413e81ff 100644 --- a/app/_locales/hi/messages.json +++ b/app/_locales/hi/messages.json @@ -330,9 +330,6 @@ "addingCustomNetwork": { "message": "नेटवर्क जोड़ रहे हैं" }, - "addingTokens": { - "message": "टोकन जोड़ना" - }, "additionalNetworks": { "message": "अतिरिक्त नेटवर्क" }, @@ -627,12 +624,6 @@ "assetsDescription": { "message": "अपने वॉलेट में टोकन ऑटोडिटेक्ट करें, NFT प्रदर्शित करें, और बैच अकाउंट बैलेंस संबंधी अपडेट प्राप्त करें" }, - "attemptSendingAssets": { - "message": "अगर आप एसेट्स को सीधे एक नेटवर्क से दूसरे नेटवर्क पर भेजने की कोशिश करते हैं, तो ऐसा करने से आपको एसेट्स का नुकसान हो सकता है। ब्रिज का इस्तेमाल करके नेटवर्कों के बीच फंड्स को सुरक्षित तरीके से ट्रांसफ़र करें।" - }, - "attemptSendingAssetsWithPortfolio": { - "message": "अगर आप एसेट्स को सीधे एक नेटवर्क से दूसरे नेटवर्क पर भेजने की कोशिश करते हैं, तो ऐसा करने से आपको एसेट्स का नुकसान हो सकता है। ब्रिज, जैसे like $1, का इस्तेमाल करके नेटवर्कों के बीच फंड्स को सुरक्षित तरीके से ट्रांसफ़र करें।" - }, "attemptToCancelSwapForFree": { "message": "स्वैप को मुफ्त में कैंसिल करने की कोशिश करें" }, @@ -840,9 +831,6 @@ "bridgeCalculatingAmount": { "message": "कैलकुलेट किया जा रहा है..." }, - "bridgeDontSend": { - "message": "ब्रिज, न भेजें" - }, "bridgeEnterAmount": { "message": "रकम डालें" }, @@ -954,9 +942,6 @@ "message": "अपने Ledger को WebHID के ज़रिए कनेक्ट करने के लिए यहां क्लिक करें", "description": "Text that can be clicked to open a browser popup for connecting the ledger device via webhid" }, - "clickToManuallyAdd": { - "message": "आप कभी भी मैन्युअल रूप से टोकन जोड़ सकते हैं।" - }, "close": { "message": "बंद करें" }, @@ -2138,9 +2123,6 @@ "gasFee": { "message": "गैस फीस" }, - "gasIsETH": { - "message": "गैस $1 है " - }, "gasLimit": { "message": "गैस लिमिट" }, @@ -2702,9 +2684,6 @@ "learnScamRisk": { "message": "घोटाले और सुरक्षा जोखिम।" }, - "learnToBridge": { - "message": "ब्रिज करना सीखें" - }, "leaveMetaMask": { "message": "MetaMask से बाहर निकलें?" }, @@ -2900,9 +2879,6 @@ "metamaskNotificationsAreOff": { "message": "वॉलेट नोटिफिकेशंस वर्तमान में सक्रिय नहीं हैं।" }, - "metamaskPortfolio": { - "message": "MetaMask Portfolio." - }, "metamaskSwapsOfflineDescription": { "message": "MetaMask स्वैप का रखरखाव किया जा रहा है। कृपया बाद में वापस देखें।" }, @@ -3047,10 +3023,6 @@ "message": "$1 निम्नलिखित के लिए आपका एप्रूवल मांग रहा है:", "description": "$1 represents dapp name" }, - "nativeToken": { - "message": "इस नेटवर्क पर ओरिजिनल टोकन $1 है। यह गैस फ़ीस के लिए इस्तेमाल किया जाने वाला टोकन है।", - "description": "$1 represents the name of the native token on the current network" - }, "nativeTokenScamWarningConversion": { "message": "नेटवर्क का ब्यौरा बदलें" }, @@ -5940,9 +5912,6 @@ "message": "$1 अब $2 पर एक्टिव है", "description": "$1 represents the account name, $2 represents the network name" }, - "switchedTo": { - "message": "आप अब इस्तेमाल कर रहे हैं" - }, "switchingNetworksCancelsPendingConfirmations": { "message": "नेटवर्क स्विच करने से सभी लंबित कन्फर्मेशन कैंसिल हो जाएंगे" }, @@ -5979,9 +5948,6 @@ "themeDescription": { "message": "अपनी पसंदीदा MetaMask थीम चुन लें" }, - "thingsToKeep": { - "message": "ध्यान रखने योग्य बातें:" - }, "thirdPartySoftware": { "message": "थर्ड-पार्टी सॉफ्टवेयर नोटिस", "description": "Title of a popup modal displayed when installing a snap for the first time." @@ -6063,9 +6029,6 @@ "tokenScamSecurityRisk": { "message": "टोकन घोटाले और सुरक्षा जोखिम" }, - "tokenShowUp": { - "message": "हो सकता है आपके वॉलेट में आपके टोकन ऑटोमेटिकली दिखाई नहीं दें। " - }, "tokenStandard": { "message": "टोकन स्टैंडर्ड" }, diff --git a/app/_locales/id/messages.json b/app/_locales/id/messages.json index c93bd496ac12..68bbf6477be5 100644 --- a/app/_locales/id/messages.json +++ b/app/_locales/id/messages.json @@ -330,9 +330,6 @@ "addingCustomNetwork": { "message": "Menambahkan Jaringan" }, - "addingTokens": { - "message": "Menambahkan token" - }, "additionalNetworks": { "message": "Jaringan tambahan" }, @@ -627,12 +624,6 @@ "assetsDescription": { "message": "Autodeteksi token di dompet Anda, tampilkan NFT, dan dapatkan pembaruan saldo akun secara batch" }, - "attemptSendingAssets": { - "message": "Aset Anda berpotensi hilang jika mencoba mengirimnya dari jaringan lain. Transfer dana secara aman antar jaringan menggunakan bridge." - }, - "attemptSendingAssetsWithPortfolio": { - "message": "Aset Anda berpotensi hilang jika mencoba mengirimnya melalui jaringan lain. Transfer dana antar jaringan dengan aman menggunakan bridge, seperti $1" - }, "attemptToCancelSwapForFree": { "message": "Mencoba membatalkan pertukaran secara gratis" }, @@ -840,9 +831,6 @@ "bridgeCalculatingAmount": { "message": "Menghitung..." }, - "bridgeDontSend": { - "message": "Bridge, jangan kirim" - }, "bridgeEnterAmount": { "message": "Masukkan jumlah" }, @@ -954,9 +942,6 @@ "message": "Klik di sini untuk menghubungkan Ledger Anda melalui WebHID", "description": "Text that can be clicked to open a browser popup for connecting the ledger device via webhid" }, - "clickToManuallyAdd": { - "message": "Anda dapat menambahkan token secara manual setiap saat." - }, "close": { "message": "Tutup" }, @@ -2138,9 +2123,6 @@ "gasFee": { "message": "Biaya gas" }, - "gasIsETH": { - "message": "Gas bernilai $1 " - }, "gasLimit": { "message": "Batas gas" }, @@ -2702,9 +2684,6 @@ "learnScamRisk": { "message": "penipuan dan risiko keamanan." }, - "learnToBridge": { - "message": "Pelajari bridge" - }, "leaveMetaMask": { "message": "Keluar dari MetaMask?" }, @@ -2900,9 +2879,6 @@ "metamaskNotificationsAreOff": { "message": "Saat ini notifikasi dompet tidak aktif." }, - "metamaskPortfolio": { - "message": "Portfolio MetaMask." - }, "metamaskSwapsOfflineDescription": { "message": "MetaMask Swap sedang dalam pemeliharaan. Harap periksa kembali nanti." }, @@ -3047,10 +3023,6 @@ "message": "$1 meminta persetujuan Anda untuk:", "description": "$1 represents dapp name" }, - "nativeToken": { - "message": "Token asli di jaringan ini adalah $1. Ini merupakan token yang digunakan untuk biaya gas. ", - "description": "$1 represents the name of the native token on the current network" - }, "nativeTokenScamWarningConversion": { "message": "Edit detail jaringan" }, @@ -5940,9 +5912,6 @@ "message": "$1 kini telah aktif di $2", "description": "$1 represents the account name, $2 represents the network name" }, - "switchedTo": { - "message": "Saat ini Anda menggunakan" - }, "switchingNetworksCancelsPendingConfirmations": { "message": "Mengalihkan jaringan akan membatalkan semua konfirmasi yang berstatus menunggu" }, @@ -5979,9 +5948,6 @@ "themeDescription": { "message": "Pilih tema MetaMask yang Anda sukai." }, - "thingsToKeep": { - "message": "Ingatlah:" - }, "thirdPartySoftware": { "message": "Pemberitahuan perangkat lunak pihak ketiga", "description": "Title of a popup modal displayed when installing a snap for the first time." @@ -6063,9 +6029,6 @@ "tokenScamSecurityRisk": { "message": "penipuan dan risiko keamanan token" }, - "tokenShowUp": { - "message": "Token Anda mungkin tidak muncul secara otomatis di dompet Anda. " - }, "tokenStandard": { "message": "Standar token" }, diff --git a/app/_locales/it/messages.json b/app/_locales/it/messages.json index 05cccdac0359..c00040b84086 100644 --- a/app/_locales/it/messages.json +++ b/app/_locales/it/messages.json @@ -252,9 +252,6 @@ "assetOptions": { "message": "Opzioni asset" }, - "attemptSendingAssets": { - "message": "Se si tenta di inviare risorse direttamente da una rete all'altra, ciò potrebbe comportare una perdita permanente della risorca coinvolta. Assicurati di usare un bridge." - }, "attributions": { "message": "Attribuzioni" }, @@ -369,9 +366,6 @@ "message": "Clicca qui per connettere il tuo Ledger tramite WebHID", "description": "Text that can be clicked to open a browser popup for connecting the ledger device via webhid" }, - "clickToManuallyAdd": { - "message": "Clicca qui per aggiungere manualmente i token." - }, "close": { "message": "Chiudi" }, diff --git a/app/_locales/ja/messages.json b/app/_locales/ja/messages.json index 24a2f60314cd..05f359973442 100644 --- a/app/_locales/ja/messages.json +++ b/app/_locales/ja/messages.json @@ -330,9 +330,6 @@ "addingCustomNetwork": { "message": "ネットワークを追加中" }, - "addingTokens": { - "message": "トークンを追加しています" - }, "additionalNetworks": { "message": "他のネットワーク" }, @@ -627,12 +624,6 @@ "assetsDescription": { "message": "ウォレットのトークンを自動検出し、NFTを表示して、アカウント残高の最新情報を一括で取得します" }, - "attemptSendingAssets": { - "message": "別のネットワークからアセットを送ろうとすると、アセットが失われる可能性があります。ネットワーク間で安全に資金を移動するには、必ずブリッジを使用してください。" - }, - "attemptSendingAssetsWithPortfolio": { - "message": "別のネットワークからアセットを送ろうとすると、アセットが失われる可能性があります。ネットワーク間で安全に資金を移動するには、必ず$1などのブリッジを使用してください。" - }, "attemptToCancelSwapForFree": { "message": "無料でスワップのキャンセルを試行" }, @@ -840,9 +831,6 @@ "bridgeCalculatingAmount": { "message": "計算中..." }, - "bridgeDontSend": { - "message": "ブリッジを使用してください" - }, "bridgeEnterAmount": { "message": "金額を入力" }, @@ -858,7 +846,6 @@ "bridgeTo": { "message": "ブリッジ先:" }, - "browserNotSupported": { "message": "ご使用のブラウザはサポートされていません..." }, @@ -955,9 +942,6 @@ "message": "ここをクリックして、WebHIDでLedgerを接続します", "description": "Text that can be clicked to open a browser popup for connecting the ledger device via webhid" }, - "clickToManuallyAdd": { - "message": "トークンはいつでも手動で追加できます。" - }, "close": { "message": "閉じる" }, @@ -2139,9 +2123,6 @@ "gasFee": { "message": "ガス代" }, - "gasIsETH": { - "message": "ガス代は$1です" - }, "gasLimit": { "message": "ガスリミット" }, @@ -2703,9 +2684,6 @@ "learnScamRisk": { "message": "詐欺やセキュリティのリスク。" }, - "learnToBridge": { - "message": "ブリッジの使い方" - }, "leaveMetaMask": { "message": "MetaMaskから離れますか?" }, @@ -2901,9 +2879,6 @@ "metamaskNotificationsAreOff": { "message": "ウォレットの通知は現在無効になっています" }, - "metamaskPortfolio": { - "message": "MetaMask Portfolio。" - }, "metamaskSwapsOfflineDescription": { "message": "MetaMask Swapsはメンテナンス中です。後でもう一度確認してください。" }, @@ -3048,10 +3023,6 @@ "message": "$1が次の承認を求めています:", "description": "$1 represents dapp name" }, - "nativeToken": { - "message": "このネットワークのネイティブトークンは$1です。ガス代にもこのトークンが使用されます。", - "description": "$1 represents the name of the native token on the current network" - }, "nativeTokenScamWarningConversion": { "message": "ネットワークの詳細を編集" }, @@ -5941,9 +5912,6 @@ "message": "$1が$2で有効になりました", "description": "$1 represents the account name, $2 represents the network name" }, - "switchedTo": { - "message": "次に切り替えました:" - }, "switchingNetworksCancelsPendingConfirmations": { "message": "ネットワークを切り替えると、保留中の承認がすべてキャンセルされます" }, @@ -5980,9 +5948,6 @@ "themeDescription": { "message": "ご希望のMetaMaskテーマを選択してください。" }, - "thingsToKeep": { - "message": "留意点:" - }, "thirdPartySoftware": { "message": "サードパーティソフトウェアに関する通知", "description": "Title of a popup modal displayed when installing a snap for the first time." @@ -6064,9 +6029,6 @@ "tokenScamSecurityRisk": { "message": "トークン関連の詐欺やセキュリティのリスク" }, - "tokenShowUp": { - "message": "トークンはウォレットに自動的に表示されない可能性があります。" - }, "tokenStandard": { "message": "トークン規格" }, diff --git a/app/_locales/ko/messages.json b/app/_locales/ko/messages.json index c261f74b4b5c..f261c0b24c06 100644 --- a/app/_locales/ko/messages.json +++ b/app/_locales/ko/messages.json @@ -330,9 +330,6 @@ "addingCustomNetwork": { "message": "네트워크 추가" }, - "addingTokens": { - "message": "토큰 추가" - }, "additionalNetworks": { "message": "추가 네트워크" }, @@ -627,12 +624,6 @@ "assetsDescription": { "message": "지갑에서 토큰을 자동으로 감지하고, NFT를 표시하며, 계정 잔액을 일괄 업데이트하세요." }, - "attemptSendingAssets": { - "message": "다른 네트워크로 자산을 직접 전송하면 자산이 영구적으로 손실될 수 있습니다. 브릿지를 이용하여 네트워크 간에 자금을 안전하게 전송하세요." - }, - "attemptSendingAssetsWithPortfolio": { - "message": "다른 네트워크에서 자산을 전송하려고 하면 자산이 손실될 수 있습니다. $1 같은 브릿지를 사용하여 네트워크 간에 자산을 안전하게 전송하세요." - }, "attemptToCancelSwapForFree": { "message": "무료 스왑 취소 시도" }, @@ -840,9 +831,6 @@ "bridgeCalculatingAmount": { "message": "계산 중..." }, - "bridgeDontSend": { - "message": "전송하지 마세요, 브릿지 하세요" - }, "bridgeEnterAmount": { "message": "금액 입력" }, @@ -858,7 +846,6 @@ "bridgeTo": { "message": "브릿지 대상" }, - "browserNotSupported": { "message": "지원되지 않는 브라우저입니다..." }, @@ -955,9 +942,6 @@ "message": "WebHID를 통해 Ledger를 연결하려면 여기를 클릭하세요.", "description": "Text that can be clicked to open a browser popup for connecting the ledger device via webhid" }, - "clickToManuallyAdd": { - "message": "토큰은 언제든지 직접 추가할 수 있습니다." - }, "close": { "message": "닫기" }, @@ -2139,9 +2123,6 @@ "gasFee": { "message": "가스비" }, - "gasIsETH": { - "message": "가스는 $1입니다 " - }, "gasLimit": { "message": "가스 한도" }, @@ -2703,9 +2684,6 @@ "learnScamRisk": { "message": "사기 및 보안 위험" }, - "learnToBridge": { - "message": "브릿지 알아보기" - }, "leaveMetaMask": { "message": "MetaMask를 나가시겠습니까?" }, @@ -2901,9 +2879,6 @@ "metamaskNotificationsAreOff": { "message": "현재 지갑 알림이 꺼져 있습니다." }, - "metamaskPortfolio": { - "message": "MetaMask Portfolio." - }, "metamaskSwapsOfflineDescription": { "message": "MetaMask Swaps 점검 중입니다. 나중에 다시 확인하세요." }, @@ -3048,10 +3023,6 @@ "message": "$1에서 다음 승인을 요청합니다:", "description": "$1 represents dapp name" }, - "nativeToken": { - "message": "이 네트워크의 네이티브 토큰은 $1입니다. 이는 가스비 지불에 사용하는 토큰입니다. ", - "description": "$1 represents the name of the native token on the current network" - }, "nativeTokenScamWarningConversion": { "message": "네트워크 세부 정보 편집" }, @@ -5941,9 +5912,6 @@ "message": "$1 계정이 현재 $2 네트워크에서 활성화되어 있습니다", "description": "$1 represents the account name, $2 represents the network name" }, - "switchedTo": { - "message": "다음을 사용 중입니다:" - }, "switchingNetworksCancelsPendingConfirmations": { "message": "네트워크를 전환하면 대기 중인 모든 컨펌 작업이 취소됩니다." }, @@ -5980,9 +5948,6 @@ "themeDescription": { "message": "원하는 MetaMask 테마를 선택하세요." }, - "thingsToKeep": { - "message": "유의 사항:" - }, "thirdPartySoftware": { "message": "타사 소프트웨어 알림", "description": "Title of a popup modal displayed when installing a snap for the first time." @@ -6064,9 +6029,6 @@ "tokenScamSecurityRisk": { "message": "토큰 사기 및 보안 위험" }, - "tokenShowUp": { - "message": "토큰이 지갑에 자동으로 표시되지 않을 수 있습니다. " - }, "tokenStandard": { "message": "토큰 표준" }, diff --git a/app/_locales/pt/messages.json b/app/_locales/pt/messages.json index 5a1389a0d4d6..1c3bba86966d 100644 --- a/app/_locales/pt/messages.json +++ b/app/_locales/pt/messages.json @@ -330,9 +330,6 @@ "addingCustomNetwork": { "message": "Adicionar rede" }, - "addingTokens": { - "message": "Adicionando tokens" - }, "additionalNetworks": { "message": "Redes adicionais" }, @@ -627,12 +624,6 @@ "assetsDescription": { "message": "Detecte automaticamente os tokens em sua carteira, exiba NFTs e receba atualizações de saldo de contas em lote" }, - "attemptSendingAssets": { - "message": "Você poderá perder seus ativos se tentar enviá-los a partir de outra rede. Transfira fundos entre redes com segurança usando uma ponte." - }, - "attemptSendingAssetsWithPortfolio": { - "message": "Você poderá perder seus ativos se tentar enviá-los a partir de outra rede. Transfira os fundos com segurança entre redes usando uma ponte, como $1" - }, "attemptToCancelSwapForFree": { "message": "Tentar cancelar a troca sem custo" }, @@ -840,9 +831,6 @@ "bridgeCalculatingAmount": { "message": "Calculando..." }, - "bridgeDontSend": { - "message": "Usar uma ponte, não enviar" - }, "bridgeEnterAmount": { "message": "Insira o valor" }, @@ -858,7 +846,6 @@ "bridgeTo": { "message": "Ponte para" }, - "browserNotSupported": { "message": "Seu navegador não é compatível..." }, @@ -955,9 +942,6 @@ "message": "Clique aqui para conectar seu Ledger por meio do WebHID", "description": "Text that can be clicked to open a browser popup for connecting the ledger device via webhid" }, - "clickToManuallyAdd": { - "message": "Sempre é possível adicionar tokens manualmente." - }, "close": { "message": "Fechar" }, @@ -2139,9 +2123,6 @@ "gasFee": { "message": "Taxa de gás" }, - "gasIsETH": { - "message": "O gás é $1 " - }, "gasLimit": { "message": "Limite de gás" }, @@ -2703,9 +2684,6 @@ "learnScamRisk": { "message": "golpes e riscos de segurança." }, - "learnToBridge": { - "message": "Aprenda a usar uma ponte" - }, "leaveMetaMask": { "message": "Sair da MetaMask?" }, @@ -2901,9 +2879,6 @@ "metamaskNotificationsAreOff": { "message": "As notificações de carteiras estão inativas no momento." }, - "metamaskPortfolio": { - "message": "MetaMask Portfolio." - }, "metamaskSwapsOfflineDescription": { "message": "O recurso de Trocas da MetaMask está em manutenção. Verifique novamente mais tarde." }, @@ -3048,10 +3023,6 @@ "message": "$1 solicita sua aprovação para:", "description": "$1 represents dapp name" }, - "nativeToken": { - "message": "O token nativo dessa rede é $1. Esse é o token usado para taxas de gás.", - "description": "$1 represents the name of the native token on the current network" - }, "nativeTokenScamWarningConversion": { "message": "Editar detalhes da rede" }, @@ -5941,9 +5912,6 @@ "message": "$1 agora está ativa na $2", "description": "$1 represents the account name, $2 represents the network name" }, - "switchedTo": { - "message": "Você alternou para" - }, "switchingNetworksCancelsPendingConfirmations": { "message": "A alternância de redes cancelará todas as confirmações pendentes" }, @@ -5980,9 +5948,6 @@ "themeDescription": { "message": "Escolha o seu tema preferido para a MetaMask." }, - "thingsToKeep": { - "message": "Informações importantes:" - }, "thirdPartySoftware": { "message": "Aviso de software de terceiros", "description": "Title of a popup modal displayed when installing a snap for the first time." @@ -6064,9 +6029,6 @@ "tokenScamSecurityRisk": { "message": "golpes e riscos de segurança envolvendo tokens" }, - "tokenShowUp": { - "message": "Seus tokens podem não aparecer automaticamente em sua carteira." - }, "tokenStandard": { "message": "Padrão de token" }, diff --git a/app/_locales/ru/messages.json b/app/_locales/ru/messages.json index 37a672ec798a..fd0b0b84fcb4 100644 --- a/app/_locales/ru/messages.json +++ b/app/_locales/ru/messages.json @@ -330,9 +330,6 @@ "addingCustomNetwork": { "message": "Добавление сети" }, - "addingTokens": { - "message": "Добавление токенов" - }, "additionalNetworks": { "message": "Дополнительные сети" }, @@ -627,12 +624,6 @@ "assetsDescription": { "message": "Автоматически обнаруживайте токены в своем кошельке, отображайте NFT и получайте пакетные обновления баланса счета" }, - "attemptSendingAssets": { - "message": "Вы можете потерять свои активы, если попытаетесь отправить их из другой сети. Безопасно переводите средства между сетями с помощью моста." - }, - "attemptSendingAssetsWithPortfolio": { - "message": "Попытка отправки активов напрямую из одной сети в другую может привести к необратимой потере активов. Следует использовать мост, такой как $1" - }, "attemptToCancelSwapForFree": { "message": "Бесплатная попытка отменить своп" }, @@ -840,9 +831,6 @@ "bridgeCalculatingAmount": { "message": "Расчет..." }, - "bridgeDontSend": { - "message": "Мост, не отправлять" - }, "bridgeEnterAmount": { "message": "Введите сумму" }, @@ -954,9 +942,6 @@ "message": "Нажмите здесь, чтобы подключить свой Ledger через WebHID", "description": "Text that can be clicked to open a browser popup for connecting the ledger device via webhid" }, - "clickToManuallyAdd": { - "message": "Вы также можете добавлять токены вручную." - }, "close": { "message": "Закрыть" }, @@ -2138,9 +2123,6 @@ "gasFee": { "message": "Плата за газ" }, - "gasIsETH": { - "message": "Газ стоит $1 " - }, "gasLimit": { "message": "Лимит газа" }, @@ -2702,9 +2684,6 @@ "learnScamRisk": { "message": "мошенничество и угрозы безопасности." }, - "learnToBridge": { - "message": "Научитесь создавать мост для" - }, "leaveMetaMask": { "message": "Выйти из MetaMask?" }, @@ -2900,9 +2879,6 @@ "metamaskNotificationsAreOff": { "message": "Уведомления кошелька в настоящее время неактивны." }, - "metamaskPortfolio": { - "message": "MetaMask Portfolio." - }, "metamaskSwapsOfflineDescription": { "message": "Сервис свопов MetaMask на техобслуживании. Зайдите позже." }, @@ -3047,10 +3023,6 @@ "message": "$1 запрашивает ваше одобрение на:", "description": "$1 represents dapp name" }, - "nativeToken": { - "message": "Нативный токен этой сети — $1. Этот токен используется для внесения платы за газ. ", - "description": "$1 represents the name of the native token on the current network" - }, "nativeTokenScamWarningConversion": { "message": "Изменить сведения о сети" }, @@ -5940,9 +5912,6 @@ "message": "$1 теперь активен в $2", "description": "$1 represents the account name, $2 represents the network name" }, - "switchedTo": { - "message": "Сейчас вы используете" - }, "switchingNetworksCancelsPendingConfirmations": { "message": "В случае смены сетей все ожидающие подтверждения будут отменены" }, @@ -5979,9 +5948,6 @@ "themeDescription": { "message": "Выберите предпочитаемую тему MetaMask." }, - "thingsToKeep": { - "message": "Что нужно помнить:" - }, "thirdPartySoftware": { "message": "Уведомление о стороннем ПО", "description": "Title of a popup modal displayed when installing a snap for the first time." @@ -6063,9 +6029,6 @@ "tokenScamSecurityRisk": { "message": "мошенничество с токенами и угрозы безопасности" }, - "tokenShowUp": { - "message": "Ваши токены могут не отображаться автоматически в вашем кошельке. " - }, "tokenStandard": { "message": "Стандарт токена" }, diff --git a/app/_locales/tl/messages.json b/app/_locales/tl/messages.json index 20b318456d77..4c201294dcdb 100644 --- a/app/_locales/tl/messages.json +++ b/app/_locales/tl/messages.json @@ -330,9 +330,6 @@ "addingCustomNetwork": { "message": "Nagdaragdag ng Network" }, - "addingTokens": { - "message": "Nagdaragdag ng mga token" - }, "additionalNetworks": { "message": "Mga karagdagang network" }, @@ -627,12 +624,6 @@ "assetsDescription": { "message": "I-autodetect ang mga token sa wallet mo, ipakita ang mga NFT, at kumuha ng naka-batch na mga update sa balanse ng account" }, - "attemptSendingAssets": { - "message": "Maaari mong mawala ang mga asset kung susubukan mo itong ipadala mula sa isang network papunta sa isa pa. Ligtas ng maglipat ng pondo sa pagitan ng mga network sa pamamagitan ng paggamit ng bridge." - }, - "attemptSendingAssetsWithPortfolio": { - "message": "Maaaring mawala ang iyong mga asset kung susubukan mong ipadala ito mula sa ibang network. Ilipat ang mga pondo nang ligtas sa pagitan ng mga network gamit ang isang bridge, tulad ng $1" - }, "attemptToCancelSwapForFree": { "message": "Subukang kanselahin ang swap nang libre" }, @@ -840,9 +831,6 @@ "bridgeCalculatingAmount": { "message": "Nagkakalkula..." }, - "bridgeDontSend": { - "message": "Bridge, huwag ipadala" - }, "bridgeEnterAmount": { "message": "Ilagay ang halaga" }, @@ -858,7 +846,6 @@ "bridgeTo": { "message": "I-bridge papunta sa" }, - "browserNotSupported": { "message": "Hindi sinusuportahan ang iyong browser..." }, @@ -955,9 +942,6 @@ "message": "Mag-click dito upang ikonekta ang iyong Ledger sa pamamagitan ng WebHID", "description": "Text that can be clicked to open a browser popup for connecting the ledger device via webhid" }, - "clickToManuallyAdd": { - "message": "Maaari ka ring magdagdag ng mga token nang manu-mano." - }, "close": { "message": "Isara" }, @@ -2139,9 +2123,6 @@ "gasFee": { "message": "Bayad sa gas" }, - "gasIsETH": { - "message": "Ang gas ay $1 " - }, "gasLimit": { "message": "Limitasyon ng gas" }, @@ -2703,9 +2684,6 @@ "learnScamRisk": { "message": "mga panloloko at panganib sa seguridad." }, - "learnToBridge": { - "message": "Matutong mag-bridge" - }, "leaveMetaMask": { "message": "Umalis sa MetaMask?" }, @@ -2901,9 +2879,6 @@ "metamaskNotificationsAreOff": { "message": "Hindi active sa kasalukuyan ang mga notipikasyon sa wallet." }, - "metamaskPortfolio": { - "message": "MetaMask Portfolio." - }, "metamaskSwapsOfflineDescription": { "message": "Kasalukuyang minementina ang MetaMask Swaps. Bumalik sa ibang pagkakataon." }, @@ -3048,10 +3023,6 @@ "message": "Ang $1 ay humihiling ng iyong pag-apruba para:", "description": "$1 represents dapp name" }, - "nativeToken": { - "message": "Ang native token sa network na ito ay $1. Ito ang token na ginagamit para sa mga gas fee. ", - "description": "$1 represents the name of the native token on the current network" - }, "nativeTokenScamWarningConversion": { "message": "I-edit ang mga detalye ng network" }, @@ -5941,9 +5912,6 @@ "message": "Ang $1 ay aktibo ngayon sa $2", "description": "$1 represents the account name, $2 represents the network name" }, - "switchedTo": { - "message": "Lumipat ka sa" - }, "switchingNetworksCancelsPendingConfirmations": { "message": "Ang paglipat ng network ay magkakansela ng lahat ng nakabinbing kumpirmasyon" }, @@ -5980,9 +5948,6 @@ "themeDescription": { "message": "Piliin ang mas gusto mong tema ng MetaMask." }, - "thingsToKeep": { - "message": "Mga bagay na dapat tandaan:" - }, "thirdPartySoftware": { "message": "Paunawa ng third-party na software", "description": "Title of a popup modal displayed when installing a snap for the first time." @@ -6064,9 +6029,6 @@ "tokenScamSecurityRisk": { "message": "mga panloloko sa token at panganib sa seguridad" }, - "tokenShowUp": { - "message": "Maaaring hindi awtomatikong lumabas ang iyong mga token sa iyong wallet. " - }, "tokenStandard": { "message": "Pamantayan ng token" }, diff --git a/app/_locales/tr/messages.json b/app/_locales/tr/messages.json index 470bb934f74b..f55ea0c69c14 100644 --- a/app/_locales/tr/messages.json +++ b/app/_locales/tr/messages.json @@ -330,9 +330,6 @@ "addingCustomNetwork": { "message": "Ağ Ekleniyor" }, - "addingTokens": { - "message": "Token'leri ekleme" - }, "additionalNetworks": { "message": "İlave ağlar" }, @@ -627,12 +624,6 @@ "assetsDescription": { "message": "Cüzdanınızdaki token'ler otomatik algılansın, NFT'ler gösterilsin ve toplu hesap bakiye güncellemeleri alınsın" }, - "attemptSendingAssets": { - "message": "Varlıklarınızı doğrudan bir ağdan diğerine göndermeye çalışırsanız onları kaybedebilirsiniz. Fonları köprü kullanarak ağlar arasında güvenli bir şekilde transfer edin." - }, - "attemptSendingAssetsWithPortfolio": { - "message": "Varlıkları doğrudan bir ağdan diğerine göndermeye çalışırsanız bu durum kalıcı varlık kaybına neden olabilir. $1 gibi bir köprü kullandığınızdan emin olun" - }, "attemptToCancelSwapForFree": { "message": "Swap işlemini ücretsiz iptal etme girişimi" }, @@ -840,9 +831,6 @@ "bridgeCalculatingAmount": { "message": "Hesaplanıyor..." }, - "bridgeDontSend": { - "message": "Köprü yap, gönderme" - }, "bridgeEnterAmount": { "message": "Miktar girin" }, @@ -858,7 +846,6 @@ "bridgeTo": { "message": "Şuraya köprü:" }, - "browserNotSupported": { "message": "Tarayıcınız desteklenmiyor..." }, @@ -955,9 +942,6 @@ "message": "WebHID üzerinden Ledger'ınızı bağlamak için tıklayın", "description": "Text that can be clicked to open a browser popup for connecting the ledger device via webhid" }, - "clickToManuallyAdd": { - "message": "Dilediğiniz zaman tokenleri manuel olarak ekleyebilirsiniz." - }, "close": { "message": "Kapat" }, @@ -2139,9 +2123,6 @@ "gasFee": { "message": "Gaz ücreti" }, - "gasIsETH": { - "message": "Gaz $1 " - }, "gasLimit": { "message": "Gaz limiti" }, @@ -2703,9 +2684,6 @@ "learnScamRisk": { "message": "dolandırıcılıklar ve güvenlik riskleri." }, - "learnToBridge": { - "message": "Köprü yapmayı öğren" - }, "leaveMetaMask": { "message": "MetaMask'ten ayrıl?" }, @@ -2901,9 +2879,6 @@ "metamaskNotificationsAreOff": { "message": "Cüzdan bildirimleri şu anda aktif değil." }, - "metamaskPortfolio": { - "message": "MetaMask Portfolio." - }, "metamaskSwapsOfflineDescription": { "message": "MetaMask Swap İşlemleri bakımda. Lütfen daha sonra tekrar kontrol edin." }, @@ -3048,10 +3023,6 @@ "message": "$1 sizden şunun için onay istiyor:", "description": "$1 represents dapp name" }, - "nativeToken": { - "message": "Bu ağdaki yerli token $1. Bu, gaz ücretleri için kullanılan tokendir. ", - "description": "$1 represents the name of the native token on the current network" - }, "nativeTokenScamWarningConversion": { "message": "Ağ bilgilerini düzenle" }, @@ -5941,9 +5912,6 @@ "message": "$1, $2 üzerinde aktif", "description": "$1 represents the account name, $2 represents the network name" }, - "switchedTo": { - "message": "Şu anda kullandığınız ağ" - }, "switchingNetworksCancelsPendingConfirmations": { "message": "Ağ değiştirmek bekleyen tüm onayları iptal eder" }, @@ -5980,9 +5948,6 @@ "themeDescription": { "message": "Tercih ettiğin MetaMask temasını seç." }, - "thingsToKeep": { - "message": "Unutulmaması gerekenler:" - }, "thirdPartySoftware": { "message": "Üçüncü taraf yazılım uyarısı", "description": "Title of a popup modal displayed when installing a snap for the first time." @@ -6064,9 +6029,6 @@ "tokenScamSecurityRisk": { "message": "token dolandırıcılıkları ve güvenlik riskleri" }, - "tokenShowUp": { - "message": "Tokenleriniz cüzdanınızda otomatik olarak görünmeyebilir. " - }, "tokenStandard": { "message": "Token standardı" }, diff --git a/app/_locales/vi/messages.json b/app/_locales/vi/messages.json index d331ab42b9b5..07262f16cbd2 100644 --- a/app/_locales/vi/messages.json +++ b/app/_locales/vi/messages.json @@ -330,9 +330,6 @@ "addingCustomNetwork": { "message": "Thêm mạng" }, - "addingTokens": { - "message": "Đang thêm token" - }, "additionalNetworks": { "message": "Mạng bổ sung" }, @@ -627,12 +624,6 @@ "assetsDescription": { "message": "Tự động phát hiện token trong ví của bạn, hiển thị NFT và nhận hàng loạt thông tin cập nhật về số dư tài khoản" }, - "attemptSendingAssets": { - "message": "Bạn có thể bị mất tài sản nếu cố gắng gửi tài sản từ một mạng khác. Chuyển tiền an toàn giữa các mạng bằng cách sử dụng cầu nối." - }, - "attemptSendingAssetsWithPortfolio": { - "message": "Bạn có thể bị mất tài sản nếu cố gắng gửi tài sản từ một mạng khác. Chuyển tiền an toàn giữa các mạng bằng cách sử dụng cầu nối, chẳng hạn như $1" - }, "attemptToCancelSwapForFree": { "message": "Cố gắng hủy hoán đổi miễn phí" }, @@ -840,9 +831,6 @@ "bridgeCalculatingAmount": { "message": "Đang tính toán..." }, - "bridgeDontSend": { - "message": "Cầu nối, không gửi" - }, "bridgeEnterAmount": { "message": "Nhập số tiền" }, @@ -858,7 +846,6 @@ "bridgeTo": { "message": "Cầu nối đến" }, - "browserNotSupported": { "message": "Trình duyệt của bạn không được hỗ trợ..." }, @@ -955,9 +942,6 @@ "message": "Nhấp vào đây để kết nối với ví Ledger của bạn qua WebHID", "description": "Text that can be clicked to open a browser popup for connecting the ledger device via webhid" }, - "clickToManuallyAdd": { - "message": "Bạn luôn có thể thêm token theo cách thủ công." - }, "close": { "message": "Đóng" }, @@ -2139,9 +2123,6 @@ "gasFee": { "message": "Phí gas" }, - "gasIsETH": { - "message": "Gas là $1 " - }, "gasLimit": { "message": "Hạn mức phí gas" }, @@ -2703,9 +2684,6 @@ "learnScamRisk": { "message": "lừa đảo và nguy cơ bảo mật." }, - "learnToBridge": { - "message": "Tìm hiểu cách sử dụng cầu nối" - }, "leaveMetaMask": { "message": "Rời khỏi MetaMask?" }, @@ -2901,9 +2879,6 @@ "metamaskNotificationsAreOff": { "message": "Thông báo ví hiện không hoạt động." }, - "metamaskPortfolio": { - "message": "MetaMask Portfolio." - }, "metamaskSwapsOfflineDescription": { "message": "Tính năng Hoán đổi trên MetaMask đang được bảo trì. Vui lòng kiểm tra lại sau." }, @@ -3048,10 +3023,6 @@ "message": "$1 đang yêu cầu sự chấp thuận của bạn cho:", "description": "$1 represents dapp name" }, - "nativeToken": { - "message": "Token gốc của mạng này là $1. Token này được dùng làm phí gas.", - "description": "$1 represents the name of the native token on the current network" - }, "nativeTokenScamWarningConversion": { "message": "Chỉnh sửa thông tin mạng" }, @@ -5941,9 +5912,6 @@ "message": "$1 hiện đang hoạt động trên $2", "description": "$1 represents the account name, $2 represents the network name" }, - "switchedTo": { - "message": "Bạn hiện đang sử dụng" - }, "switchingNetworksCancelsPendingConfirmations": { "message": "Khi bạn chuyển mạng, mọi xác nhận đang chờ xử lý sẽ bị hủy" }, @@ -5980,9 +5948,6 @@ "themeDescription": { "message": "Chọn chủ đề MetaMask yêu thích của bạn." }, - "thingsToKeep": { - "message": "Những điều cần lưu ý:" - }, "thirdPartySoftware": { "message": "Thông báo phần mềm của bên thứ ba", "description": "Title of a popup modal displayed when installing a snap for the first time." @@ -6064,9 +6029,6 @@ "tokenScamSecurityRisk": { "message": "rủi ro về bảo mật và lừa đảo token" }, - "tokenShowUp": { - "message": "Các token có thể không tự động hiển thị trong ví của bạn. " - }, "tokenStandard": { "message": "Tiêu chuẩn token" }, diff --git a/app/_locales/zh_CN/messages.json b/app/_locales/zh_CN/messages.json index 52941dcc24f9..944ad47289f0 100644 --- a/app/_locales/zh_CN/messages.json +++ b/app/_locales/zh_CN/messages.json @@ -330,9 +330,6 @@ "addingCustomNetwork": { "message": "正在添加网络" }, - "addingTokens": { - "message": "正在添加代币" - }, "additionalNetworks": { "message": "其他网络" }, @@ -627,12 +624,6 @@ "assetsDescription": { "message": "自动检测钱包中的代币,显示 NFT,并获取批量账户余额更新" }, - "attemptSendingAssets": { - "message": "如果您试图将资产从一个网络直接发送到另一个网络,这可能会导致永久的资产损失。请务必使用跨链桥进行操作。" - }, - "attemptSendingAssetsWithPortfolio": { - "message": "如果您尝试将资产从一个网络发送到另一个网络,这可能会导致资产损失。请务必使用跨链桥在不同网络间安全转移资金,例如 $1" - }, "attemptToCancelSwapForFree": { "message": "尝试免费取消兑换" }, @@ -840,9 +831,6 @@ "bridgeCalculatingAmount": { "message": "正在计算......" }, - "bridgeDontSend": { - "message": "跨链桥,不要发送" - }, "bridgeEnterAmount": { "message": "输入金额" }, @@ -858,7 +846,6 @@ "bridgeTo": { "message": "桥接至" }, - "browserNotSupported": { "message": "您的浏览器不受支持……" }, @@ -955,9 +942,6 @@ "message": "点击这里以通过 WebHID 连接您的 Ledger", "description": "Text that can be clicked to open a browser popup for connecting the ledger device via webhid" }, - "clickToManuallyAdd": { - "message": "请点击这里,以手动添加代币。" - }, "close": { "message": "关闭" }, @@ -2139,9 +2123,6 @@ "gasFee": { "message": "燃料费" }, - "gasIsETH": { - "message": "燃料是 $1 " - }, "gasLimit": { "message": "燃料限制" }, @@ -2703,9 +2684,6 @@ "learnScamRisk": { "message": "欺诈和安全风险信息。" }, - "learnToBridge": { - "message": "了解跨链桥" - }, "leaveMetaMask": { "message": "要离开 MetaMask?" }, @@ -2901,9 +2879,6 @@ "metamaskNotificationsAreOff": { "message": "钱包通知目前未开启。" }, - "metamaskPortfolio": { - "message": "MetaMask Portfolio。" - }, "metamaskSwapsOfflineDescription": { "message": "MetaMask Swaps 正在进行维护。请稍后再查看。" }, @@ -3048,10 +3023,6 @@ "message": "$1 请求您的批准,以便:", "description": "$1 represents dapp name" }, - "nativeToken": { - "message": "此网络上的原生代币为 $1。它是用于燃料费的代币。 ", - "description": "$1 represents the name of the native token on the current network" - }, "nativeTokenScamWarningConversion": { "message": "编辑网络详情" }, @@ -5941,9 +5912,6 @@ "message": "$1 现已在 $2 上激活", "description": "$1 represents the account name, $2 represents the network name" }, - "switchedTo": { - "message": "您现在使用的是" - }, "switchingNetworksCancelsPendingConfirmations": { "message": "切换网络将取消所有待处理的确认" }, @@ -5980,9 +5948,6 @@ "themeDescription": { "message": "选择您喜欢的MetaMask主题。" }, - "thingsToKeep": { - "message": "注意事项:" - }, "thirdPartySoftware": { "message": "第三方软件通告", "description": "Title of a popup modal displayed when installing a snap for the first time." @@ -6064,9 +6029,6 @@ "tokenScamSecurityRisk": { "message": "代币欺诈和安全风险。" }, - "tokenShowUp": { - "message": "您的代币可能不会自动显示在您的钱包中。" - }, "tokenStandard": { "message": "代币标准" }, diff --git a/lavamoat/browserify/beta/policy.json b/lavamoat/browserify/beta/policy.json index 82075f709022..05a22743204c 100644 --- a/lavamoat/browserify/beta/policy.json +++ b/lavamoat/browserify/beta/policy.json @@ -2155,7 +2155,7 @@ "TextEncoder": true }, "packages": { - "@noble/hashes": true + "@ethereumjs/tx>ethereum-cryptography>@noble/curves>@noble/hashes": true } }, "@noble/hashes": { @@ -2170,6 +2170,24 @@ "crypto": true } }, + "@ethereumjs/tx>ethereum-cryptography>@noble/curves>@noble/hashes": { + "globals": { + "TextEncoder": true, + "crypto": true + } + }, + "@ethereumjs/tx>ethereum-cryptography>@scure/bip32>@noble/hashes": { + "globals": { + "TextEncoder": true, + "crypto": true + } + }, + "@ethereumjs/tx>ethereum-cryptography>@noble/hashes": { + "globals": { + "TextEncoder": true, + "crypto": true + } + }, "eth-lattice-keyring>@ethereumjs/tx>ethereum-cryptography>@noble/hashes": { "globals": { "TextEncoder": true, @@ -2248,7 +2266,7 @@ "@ethereumjs/tx>ethereum-cryptography>@scure/bip32": { "packages": { "@ethereumjs/tx>ethereum-cryptography>@noble/curves": true, - "@noble/hashes": true, + "@ethereumjs/tx>ethereum-cryptography>@scure/bip32>@noble/hashes": true, "@metamask/utils>@scure/base": true } }, @@ -3448,7 +3466,7 @@ }, "packages": { "@ethereumjs/tx>ethereum-cryptography>@noble/curves": true, - "@noble/hashes": true, + "@ethereumjs/tx>ethereum-cryptography>@noble/hashes": true, "@ethereumjs/tx>ethereum-cryptography>@scure/bip32": true } }, diff --git a/lavamoat/browserify/flask/policy.json b/lavamoat/browserify/flask/policy.json index 82075f709022..05a22743204c 100644 --- a/lavamoat/browserify/flask/policy.json +++ b/lavamoat/browserify/flask/policy.json @@ -2155,7 +2155,7 @@ "TextEncoder": true }, "packages": { - "@noble/hashes": true + "@ethereumjs/tx>ethereum-cryptography>@noble/curves>@noble/hashes": true } }, "@noble/hashes": { @@ -2170,6 +2170,24 @@ "crypto": true } }, + "@ethereumjs/tx>ethereum-cryptography>@noble/curves>@noble/hashes": { + "globals": { + "TextEncoder": true, + "crypto": true + } + }, + "@ethereumjs/tx>ethereum-cryptography>@scure/bip32>@noble/hashes": { + "globals": { + "TextEncoder": true, + "crypto": true + } + }, + "@ethereumjs/tx>ethereum-cryptography>@noble/hashes": { + "globals": { + "TextEncoder": true, + "crypto": true + } + }, "eth-lattice-keyring>@ethereumjs/tx>ethereum-cryptography>@noble/hashes": { "globals": { "TextEncoder": true, @@ -2248,7 +2266,7 @@ "@ethereumjs/tx>ethereum-cryptography>@scure/bip32": { "packages": { "@ethereumjs/tx>ethereum-cryptography>@noble/curves": true, - "@noble/hashes": true, + "@ethereumjs/tx>ethereum-cryptography>@scure/bip32>@noble/hashes": true, "@metamask/utils>@scure/base": true } }, @@ -3448,7 +3466,7 @@ }, "packages": { "@ethereumjs/tx>ethereum-cryptography>@noble/curves": true, - "@noble/hashes": true, + "@ethereumjs/tx>ethereum-cryptography>@noble/hashes": true, "@ethereumjs/tx>ethereum-cryptography>@scure/bip32": true } }, diff --git a/lavamoat/browserify/main/policy.json b/lavamoat/browserify/main/policy.json index 82075f709022..05a22743204c 100644 --- a/lavamoat/browserify/main/policy.json +++ b/lavamoat/browserify/main/policy.json @@ -2155,7 +2155,7 @@ "TextEncoder": true }, "packages": { - "@noble/hashes": true + "@ethereumjs/tx>ethereum-cryptography>@noble/curves>@noble/hashes": true } }, "@noble/hashes": { @@ -2170,6 +2170,24 @@ "crypto": true } }, + "@ethereumjs/tx>ethereum-cryptography>@noble/curves>@noble/hashes": { + "globals": { + "TextEncoder": true, + "crypto": true + } + }, + "@ethereumjs/tx>ethereum-cryptography>@scure/bip32>@noble/hashes": { + "globals": { + "TextEncoder": true, + "crypto": true + } + }, + "@ethereumjs/tx>ethereum-cryptography>@noble/hashes": { + "globals": { + "TextEncoder": true, + "crypto": true + } + }, "eth-lattice-keyring>@ethereumjs/tx>ethereum-cryptography>@noble/hashes": { "globals": { "TextEncoder": true, @@ -2248,7 +2266,7 @@ "@ethereumjs/tx>ethereum-cryptography>@scure/bip32": { "packages": { "@ethereumjs/tx>ethereum-cryptography>@noble/curves": true, - "@noble/hashes": true, + "@ethereumjs/tx>ethereum-cryptography>@scure/bip32>@noble/hashes": true, "@metamask/utils>@scure/base": true } }, @@ -3448,7 +3466,7 @@ }, "packages": { "@ethereumjs/tx>ethereum-cryptography>@noble/curves": true, - "@noble/hashes": true, + "@ethereumjs/tx>ethereum-cryptography>@noble/hashes": true, "@ethereumjs/tx>ethereum-cryptography>@scure/bip32": true } }, diff --git a/lavamoat/browserify/mmi/policy.json b/lavamoat/browserify/mmi/policy.json index a2453b1686dc..27ddfc86ff15 100644 --- a/lavamoat/browserify/mmi/policy.json +++ b/lavamoat/browserify/mmi/policy.json @@ -2247,7 +2247,7 @@ "TextEncoder": true }, "packages": { - "@noble/hashes": true + "@ethereumjs/tx>ethereum-cryptography>@noble/curves>@noble/hashes": true } }, "@noble/hashes": { @@ -2262,6 +2262,24 @@ "crypto": true } }, + "@ethereumjs/tx>ethereum-cryptography>@noble/curves>@noble/hashes": { + "globals": { + "TextEncoder": true, + "crypto": true + } + }, + "@ethereumjs/tx>ethereum-cryptography>@scure/bip32>@noble/hashes": { + "globals": { + "TextEncoder": true, + "crypto": true + } + }, + "@ethereumjs/tx>ethereum-cryptography>@noble/hashes": { + "globals": { + "TextEncoder": true, + "crypto": true + } + }, "eth-lattice-keyring>@ethereumjs/tx>ethereum-cryptography>@noble/hashes": { "globals": { "TextEncoder": true, @@ -2340,7 +2358,7 @@ "@ethereumjs/tx>ethereum-cryptography>@scure/bip32": { "packages": { "@ethereumjs/tx>ethereum-cryptography>@noble/curves": true, - "@noble/hashes": true, + "@ethereumjs/tx>ethereum-cryptography>@scure/bip32>@noble/hashes": true, "@metamask/utils>@scure/base": true } }, @@ -3540,7 +3558,7 @@ }, "packages": { "@ethereumjs/tx>ethereum-cryptography>@noble/curves": true, - "@noble/hashes": true, + "@ethereumjs/tx>ethereum-cryptography>@noble/hashes": true, "@ethereumjs/tx>ethereum-cryptography>@scure/bip32": true } }, diff --git a/package.json b/package.json index c3e6ff9eac24..1553c739936f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "metamask-crx", - "version": "12.9.1", + "version": "12.9.2", "private": true, "repository": { "type": "git", @@ -237,7 +237,7 @@ "@expo/config-plugins/glob": "^10.3.10", "@solana/web3.js/rpc-websockets": "^8.0.1", "@metamask/nonce-tracker@npm:^5.0.0": "patch:@metamask/nonce-tracker@npm%3A5.0.0#~/.yarn/patches/@metamask-nonce-tracker-npm-5.0.0-d81478218e.patch", - "@metamask/network-controller@npm:^22.0.0": "patch:@metamask/network-controller@npm%3A22.1.0#~/.yarn/patches/@metamask-network-controller-npm-22.1.0-621c281f70.patch", + "@metamask/network-controller@npm:^22.0.2": "patch:@metamask/network-controller@npm%3A22.1.1#~/.yarn/patches/@metamask-network-controller-npm-22.1.1-09b6510f1e.patch", "path-to-regexp": "1.9.0", "@ledgerhq/cryptoassets-evm-signatures/axios": "^0.28.0", "@ledgerhq/domain-service/axios": "^0.28.0", @@ -296,7 +296,7 @@ "@metamask/ens-controller": "^15.0.0", "@metamask/ens-resolver-snap": "^0.1.2", "@metamask/eth-json-rpc-filters": "^9.0.0", - "@metamask/eth-json-rpc-middleware": "^15.0.0", + "@metamask/eth-json-rpc-middleware": "^15.0.1", "@metamask/eth-ledger-bridge-keyring": "^5.0.1", "@metamask/eth-query": "^4.0.0", "@metamask/eth-sig-util": "^7.0.1", @@ -321,7 +321,7 @@ "@metamask/message-signing-snap": "^0.6.0", "@metamask/metamask-eth-abis": "^3.1.1", "@metamask/name-controller": "^8.0.0", - "@metamask/network-controller": "patch:@metamask/network-controller@npm%3A22.1.0#~/.yarn/patches/@metamask-network-controller-npm-22.1.0-621c281f70.patch", + "@metamask/network-controller": "patch:@metamask/network-controller@npm%3A22.1.1#~/.yarn/patches/@metamask-network-controller-npm-22.1.1-09b6510f1e.patch", "@metamask/notification-services-controller": "^0.15.0", "@metamask/object-multiplex": "^2.0.0", "@metamask/obs-store": "^9.0.0", @@ -350,7 +350,7 @@ "@metamask/snaps-utils": "^8.6.1", "@metamask/solana-wallet-snap": "^1.0.3", "@metamask/transaction-controller": "^42.0.0", - "@metamask/user-operation-controller": "^19.0.0", + "@metamask/user-operation-controller": "^21.0.0", "@metamask/utils": "^10.0.1", "@ngraveio/bc-ur": "^1.1.12", "@noble/hashes": "^1.3.3", diff --git a/shared/constants/metametrics.ts b/shared/constants/metametrics.ts index a65c349963ec..8b59e95e650c 100644 --- a/shared/constants/metametrics.ts +++ b/shared/constants/metametrics.ts @@ -645,6 +645,9 @@ export enum MetaMetricsEventName { AppUnlockedFailed = 'App Unlocked Failed', AppLocked = 'App Locked', AppWindowExpanded = 'App Window Expanded', + BannerDisplay = 'Banner Display', + BannerCloseAll = 'Banner Close All', + BannerSelect = 'Banner Select', BridgeLinkClicked = 'Bridge Link Clicked', BitcoinSupportToggled = 'Bitcoin Support Toggled', BitcoinTestnetSupportToggled = 'Bitcoin Testnet Support Toggled', @@ -766,6 +769,7 @@ export enum MetaMetricsEventName { TokenAdded = 'Token Added', TokenRemoved = 'Token Removed', TokenSortPreference = 'Token Sort Preference', + TokenListRefreshed = 'Token List Refreshed', NFTRemoved = 'NFT Removed', TokenDetected = 'Token Detected', TokenHidden = 'Token Hidden', @@ -911,6 +915,7 @@ export enum MetaMetricsEventCategory { App = 'App', Auth = 'Auth', Background = 'Background', + Banner = 'Banner', // The TypeScript ESLint rule is incorrectly marking this line. /* eslint-disable-next-line @typescript-eslint/no-shadow */ Error = 'Error', diff --git a/test/data/mock-state.json b/test/data/mock-state.json index 3131b8335287..9ea2e674e7a3 100644 --- a/test/data/mock-state.json +++ b/test/data/mock-state.json @@ -1891,6 +1891,7 @@ "watchEthereumAccountEnabled": false, "bitcoinSupportEnabled": false, "bitcoinTestnetSupportEnabled": false, + "solanaSupportEnabled": false, "pendingApprovals": { "testApprovalId": { "id": "testApprovalId", diff --git a/test/e2e/constants.ts b/test/e2e/constants.ts index 7123d3e4114a..9d4d5bbf3c8d 100644 --- a/test/e2e/constants.ts +++ b/test/e2e/constants.ts @@ -57,6 +57,9 @@ export const DEFAULT_BTC_FEES_RATE = 0.00001; // BTC /* Default BTC conversion rate to USD */ export const DEFAULT_BTC_CONVERSION_RATE = 62000; // USD +/* Default SOL conversion rate to USD */ +export const DEFAULT_SOL_CONVERSION_RATE = 226; // USD + /* Default BTC transaction ID */ export const DEFAULT_BTC_TRANSACTION_ID = 'e4111a707317da67d49a71af4cbcf6c0546f900ca32c3842d2254e315d1fca18'; @@ -70,3 +73,6 @@ export const DEFAULT_SOLANA_ACCOUNT = /* Default (mocked) SOLANA balance used by the Solana RPC provider */ export const DEFAULT_SOLANA_BALANCE = 1; // SOL + +/* Title of the mocked E2E test empty HTML page */ +export const EMPTY_E2E_TEST_PAGE_TITLE = 'E2E Test Page'; diff --git a/test/e2e/flask/btc/common-btc.ts b/test/e2e/flask/btc/common-btc.ts index 05f382281e29..db2db85eb554 100644 --- a/test/e2e/flask/btc/common-btc.ts +++ b/test/e2e/flask/btc/common-btc.ts @@ -14,6 +14,7 @@ import { Driver } from '../../webdriver/driver'; import { loginWithBalanceValidation } from '../../page-objects/flows/login.flow'; import AccountListPage from '../../page-objects/pages/account-list-page'; import HeaderNavbar from '../../page-objects/pages/header-navbar'; +import { ACCOUNT_TYPE } from '../../page-objects/common'; const QUICKNODE_URL_REGEX = /^https:\/\/.*\.btc.*\.quiknode\.pro(\/|$)/u; @@ -217,7 +218,7 @@ export async function withBtcAccountSnap( await new HeaderNavbar(driver).openAccountMenu(); const accountListPage = new AccountListPage(driver); await accountListPage.check_pageIsLoaded(); - await accountListPage.addNewBtcAccount(); + await accountListPage.addAccount(ACCOUNT_TYPE.Bitcoin, ''); await test(driver, mockServer); }, ); diff --git a/test/e2e/flask/btc/create-btc-account.spec.ts b/test/e2e/flask/btc/create-btc-account.spec.ts index 0dfb24a8a931..f563454ad1e6 100644 --- a/test/e2e/flask/btc/create-btc-account.spec.ts +++ b/test/e2e/flask/btc/create-btc-account.spec.ts @@ -7,6 +7,7 @@ import LoginPage from '../../page-objects/pages/login-page'; import PrivacySettings from '../../page-objects/pages/settings/privacy-settings'; import ResetPasswordPage from '../../page-objects/pages/reset-password-page'; import SettingsPage from '../../page-objects/pages/settings/settings-page'; +import { ACCOUNT_TYPE } from '../../page-objects/common'; import { withBtcAccountSnap } from './common-btc'; describe('Create BTC Account', function (this: Suite) { @@ -34,14 +35,12 @@ describe('Create BTC Account', function (this: Suite) { await headerNavbar.openAccountMenu(); const accountListPage = new AccountListPage(driver); await accountListPage.check_pageIsLoaded(); - await accountListPage.addNewBtcAccount({ - btcAccountCreationEnabled: false, - }); - - // check the number of available accounts is 2 - await headerNavbar.openAccountMenu(); - await accountListPage.check_pageIsLoaded(); await accountListPage.check_numberOfAvailableAccounts(2); + await accountListPage.openAddAccountModal(); + assert.equal( + await accountListPage.isBtcAccountCreationButtonEnabled(), + false, + ); }, ); }); @@ -91,8 +90,14 @@ describe('Create BTC Account', function (this: Suite) { // Recreate account and check that the address is the same await headerNavbar.openAccountMenu(); - await accountListPage.check_pageIsLoaded(); - await accountListPage.addNewBtcAccount(); + await accountListPage.openAddAccountModal(); + assert.equal( + await accountListPage.isBtcAccountCreationButtonEnabled(), + true, + ); + await accountListPage.closeAccountModal(); + await headerNavbar.openAccountMenu(); + await accountListPage.addAccount(ACCOUNT_TYPE.Bitcoin, ''); await headerNavbar.check_accountLabel('Bitcoin Account'); await headerNavbar.openAccountMenu(); @@ -146,7 +151,7 @@ describe('Create BTC Account', function (this: Suite) { await headerNavbar.check_pageIsLoaded(); await headerNavbar.openAccountMenu(); await accountListPage.check_pageIsLoaded(); - await accountListPage.addNewBtcAccount(); + await accountListPage.addAccount(ACCOUNT_TYPE.Bitcoin, ''); await headerNavbar.check_accountLabel('Bitcoin Account'); await headerNavbar.openAccountMenu(); diff --git a/test/e2e/flask/solana/common-solana.ts b/test/e2e/flask/solana/common-solana.ts new file mode 100644 index 000000000000..2ac107bb442c --- /dev/null +++ b/test/e2e/flask/solana/common-solana.ts @@ -0,0 +1,71 @@ +import { Mockttp } from 'mockttp'; +import { withFixtures } from '../../helpers'; +import { Driver } from '../../webdriver/driver'; +import HeaderNavbar from '../../page-objects/pages/header-navbar'; +import AccountListPage from '../../page-objects/pages/account-list-page'; +import FixtureBuilder from '../../fixture-builder'; +import { ACCOUNT_TYPE } from '../../page-objects/common'; +import { loginWithBalanceValidation } from '../../page-objects/flows/login.flow'; + +const SOLANA_URL_REGEX = /^https:\/\/.*\.solana.*/u; + +export enum SendFlowPlaceHolders { + AMOUNT = 'Enter amount to send', + RECIPIENT = 'Enter receiving address', + LOADING = 'Preparing transaction', +} + +export async function mockSolanaBalanceQuote(mockServer: Mockttp) { + return await mockServer + .forPost(SOLANA_URL_REGEX) + .withJsonBodyIncluding({ + method: 'getBalance', + }) + .thenCallback(() => { + return { + statusCode: 200, + json: { + result: { + context: { + apiVersion: '2.0.15', + slot: 305352614, + }, + value: 0, + }, + }, + }; + }); +} + +export async function withSolanaAccountSnap( + { + title, + solanaSupportEnabled, + }: { title?: string; solanaSupportEnabled?: boolean }, + test: (driver: Driver, mockServer: Mockttp) => Promise, +) { + console.log('Starting withSolanaAccountSnap'); + await withFixtures( + { + fixtures: new FixtureBuilder() + .withPreferencesControllerAndFeatureFlag({ + solanaSupportEnabled: solanaSupportEnabled ?? true, + }) + .build(), + title, + dapp: true, + testSpecificMock: async (mockServer: Mockttp) => { + console.log('Setting up test-specific mocks'); + return [await mockSolanaBalanceQuote(mockServer)]; + }, + }, + async ({ driver, mockServer }: { driver: Driver; mockServer: Mockttp }) => { + await loginWithBalanceValidation(driver); + const headerComponen = new HeaderNavbar(driver); + await headerComponen.openAccountMenu(); + const accountListPage = new AccountListPage(driver); + await accountListPage.addAccount(ACCOUNT_TYPE.Solana, 'Solana 1'); + await test(driver, mockServer); + }, + ); +} diff --git a/test/e2e/flask/solana/create-solana-account.spec.ts b/test/e2e/flask/solana/create-solana-account.spec.ts new file mode 100644 index 000000000000..cca4f5222993 --- /dev/null +++ b/test/e2e/flask/solana/create-solana-account.spec.ts @@ -0,0 +1,62 @@ +import { Suite } from 'mocha'; +import HeaderNavbar from '../../page-objects/pages/header-navbar'; +import AccountListPage from '../../page-objects/pages/account-list-page'; +import { ACCOUNT_TYPE } from '../../page-objects/common'; +import { withSolanaAccountSnap } from './common-solana'; + +// Scenarios skipped due to https://consensyssoftware.atlassian.net/browse/SOL-87 +describe('Create Solana Account', function (this: Suite) { + it.skip('Creates 2 Solana accounts', async function () { + await withSolanaAccountSnap( + { title: this.test?.fullTitle() }, + async (driver) => { + // check that we have one Solana account + const headerNavbar = new HeaderNavbar(driver); + await headerNavbar.check_pageIsLoaded(); + await headerNavbar.check_accountLabel('Solana 1'); + await headerNavbar.openAccountMenu(); + const accountListPage = new AccountListPage(driver); + await accountListPage.check_accountDisplayedInAccountList('Account 1'); + await accountListPage.addAccount(ACCOUNT_TYPE.Solana, 'Solana 2'); + await headerNavbar.check_accountLabel('Solana 2'); + await headerNavbar.openAccountMenu(); + await accountListPage.check_numberOfAvailableAccounts(3); + }, + ); + }); + it('Creates a Solana account from the menu', async function () { + await withSolanaAccountSnap( + { title: this.test?.fullTitle() }, + async (driver) => { + const headerNavbar = new HeaderNavbar(driver); + await headerNavbar.check_pageIsLoaded(); + await headerNavbar.check_accountLabel('Solana 1'); + await headerNavbar.openAccountMenu(); + const accountListPage = new AccountListPage(driver); + await accountListPage.check_accountDisplayedInAccountList('Account 1'); + await accountListPage.check_accountDisplayedInAccountList('Solana 1'); + }, + ); + }); +}); +it.skip('Removes Solana account after creating it', async function () { + await withSolanaAccountSnap( + { title: this.test?.fullTitle() }, + async (driver) => { + // check that we have one Solana account + const headerNavbar = new HeaderNavbar(driver); + await headerNavbar.check_accountLabel('Solana 1'); + // check user can cancel the removal of the Solana account + await headerNavbar.openAccountMenu(); + const accountListPage = new AccountListPage(driver); + await accountListPage.check_accountDisplayedInAccountList('Account 1'); + await accountListPage.removeAccount('Solana 1', true); + await headerNavbar.check_accountLabel('Account 1'); + await headerNavbar.openAccountMenu(); + await accountListPage.check_accountDisplayedInAccountList('Account 1'); + await accountListPage.check_accountIsNotDisplayedInAccountList( + 'Solana 1', + ); + }, + ); +}); diff --git a/test/e2e/flask/solana/solana-eth-networks.spec.ts b/test/e2e/flask/solana/solana-eth-networks.spec.ts new file mode 100644 index 000000000000..56a68135fad8 --- /dev/null +++ b/test/e2e/flask/solana/solana-eth-networks.spec.ts @@ -0,0 +1,24 @@ +import { Suite } from 'mocha'; +import HeaderNavbar from '../../page-objects/pages/header-navbar'; +import AccountListPage from '../../page-objects/pages/account-list-page'; +import { withSolanaAccountSnap } from './common-solana'; + +describe('Solana/Evm accounts', function (this: Suite) { + it('Network picker is disabled when Solana account is selected', async function () { + await withSolanaAccountSnap( + { title: this.test?.fullTitle() }, + async (driver) => { + const headerNavbar = new HeaderNavbar(driver); + await headerNavbar.check_pageIsLoaded(); + await headerNavbar.check_accountLabel('Solana 1'); + await headerNavbar.check_currentSelectedNetwork('Solana'); + await headerNavbar.check_ifNetworkPickerClickable(false); + await headerNavbar.openAccountMenu(); + const accountMenu = new AccountListPage(driver); + await accountMenu.switchToAccount('Account 1'); + await headerNavbar.check_currentSelectedNetwork('Localhost 8545'); + await headerNavbar.check_ifNetworkPickerClickable(true); + }, + ); + }); +}); diff --git a/test/e2e/json-rpc/wallet_requestPermissions.spec.js b/test/e2e/json-rpc/wallet_requestPermissions.spec.ts similarity index 51% rename from test/e2e/json-rpc/wallet_requestPermissions.spec.js rename to test/e2e/json-rpc/wallet_requestPermissions.spec.ts index 5484fdf73d80..d2a033dcf3c5 100644 --- a/test/e2e/json-rpc/wallet_requestPermissions.spec.js +++ b/test/e2e/json-rpc/wallet_requestPermissions.spec.ts @@ -1,58 +1,42 @@ -const { strict: assert } = require('assert'); -const { - defaultGanacheOptions, - withFixtures, - switchToNotificationWindow, - switchToOrOpenDapp, - unlockWallet, -} = require('../helpers'); -const FixtureBuilder = require('../fixture-builder'); +import { strict as assert } from 'assert'; +import { withFixtures } from '../helpers'; +import FixtureBuilder from '../fixture-builder'; +import { loginWithBalanceValidation } from '../page-objects/flows/login.flow'; +import TestDapp from '../page-objects/pages/test-dapp'; describe('wallet_requestPermissions', function () { it('executes a request permissions on eth_accounts event', async function () { await withFixtures( { dapp: true, - fixtures: new FixtureBuilder() - .withPermissionControllerConnectedToTestDapp() - .build(), - ganacheOptions: defaultGanacheOptions, - title: this.test.title, + fixtures: new FixtureBuilder().build(), + title: this.test?.title, }, async ({ driver }) => { - await unlockWallet(driver); + await loginWithBalanceValidation(driver); + const testDapp = new TestDapp(driver); + await testDapp.openTestDappPage(); // wallet_requestPermissions - await driver.openNewPage(`http://127.0.0.1:8080`); - const requestPermissionsRequest = JSON.stringify({ jsonrpc: '2.0', method: 'wallet_requestPermissions', params: [{ eth_accounts: {} }], }); - await driver.executeScript( `window.ethereum.request(${requestPermissionsRequest})`, ); - await switchToNotificationWindow(driver); - - await driver.clickElement({ - text: 'Connect', - tag: 'button', - }); - - await switchToOrOpenDapp(driver); + // confirm connect account + await testDapp.confirmConnectAccountModal(); const getPermissionsRequest = JSON.stringify({ method: 'wallet_getPermissions', }); - const getPermissions = await driver.executeScript( `return window.ethereum.request(${getPermissionsRequest})`, ); - - assert.strictEqual(getPermissions[0].parentCapability, 'eth_accounts'); + assert.strictEqual(getPermissions[1].parentCapability, 'eth_accounts'); }, ); }); diff --git a/test/e2e/json-rpc/wallet_revokePermissions.spec.js b/test/e2e/json-rpc/wallet_revokePermissions.spec.ts similarity index 52% rename from test/e2e/json-rpc/wallet_revokePermissions.spec.js rename to test/e2e/json-rpc/wallet_revokePermissions.spec.ts index 70d2fd3ba572..5c444b5ecf01 100644 --- a/test/e2e/json-rpc/wallet_revokePermissions.spec.js +++ b/test/e2e/json-rpc/wallet_revokePermissions.spec.ts @@ -1,12 +1,8 @@ -const { strict: assert } = require('assert'); - -const { - withFixtures, - defaultGanacheOptions, - unlockWallet, - openDapp, -} = require('../helpers'); -const FixtureBuilder = require('../fixture-builder'); +import { strict as assert } from 'assert'; +import { ACCOUNT_1, withFixtures } from '../helpers'; +import FixtureBuilder from '../fixture-builder'; +import TestDapp from '../page-objects/pages/test-dapp'; +import { loginWithBalanceValidation } from '../page-objects/flows/login.flow'; describe('Revoke Dapp Permissions', function () { it('should revoke dapp permissions ', async function () { @@ -16,18 +12,13 @@ describe('Revoke Dapp Permissions', function () { fixtures: new FixtureBuilder() .withPermissionControllerConnectedToTestDapp() .build(), - ganacheOptions: defaultGanacheOptions, title: this.test?.fullTitle(), }, async ({ driver }) => { - await unlockWallet(driver); - - await openDapp(driver); - - await driver.findElement({ - text: '0x5cfe73b6021e818b776b421b1c4db2474086a7e1', - css: '#accounts', - }); + await loginWithBalanceValidation(driver); + const testDapp = new TestDapp(driver); + await testDapp.openTestDappPage(); + await testDapp.check_connectedAccounts(ACCOUNT_1); // wallet_revokePermissions request const revokePermissionsRequest = JSON.stringify({ @@ -43,15 +34,10 @@ describe('Revoke Dapp Permissions', function () { const result = await driver.executeScript( `return window.ethereum.request(${revokePermissionsRequest})`, ); - // Response of method call assert.deepEqual(result, null); - // TODO: Fix having to reload dapp as it is not the proper behavior in production, issue with test setup. - await driver.executeScript(`window.location.reload()`); - - // You cannot use driver.findElement() with an empty string, so use xpath - await driver.findElement({ xpath: '//span[@id="accounts"][.=""]' }); + await testDapp.check_connectedAccounts(ACCOUNT_1, false); }, ); }); diff --git a/test/e2e/page-objects/common.ts b/test/e2e/page-objects/common.ts index 5bf1a91e1859..40eb625d94ac 100644 --- a/test/e2e/page-objects/common.ts +++ b/test/e2e/page-objects/common.ts @@ -2,3 +2,9 @@ export type RawLocator = | string | { css?: string; text?: string } | { tag: string; text: string }; + +export enum ACCOUNT_TYPE { + Ethereum, + Bitcoin, + Solana, +} diff --git a/test/e2e/page-objects/pages/account-list-page.ts b/test/e2e/page-objects/pages/account-list-page.ts index 50eb70ce553c..628d758e7e71 100644 --- a/test/e2e/page-objects/pages/account-list-page.ts +++ b/test/e2e/page-objects/pages/account-list-page.ts @@ -1,7 +1,7 @@ -import { strict as assert } from 'assert'; import { Driver } from '../../webdriver/driver'; import { largeDelayMs, regularDelayMs } from '../../helpers'; import messages from '../../../../app/_locales/en/messages.json'; +import { ACCOUNT_TYPE } from '../common'; class AccountListPage { private readonly driver: Driver; @@ -40,6 +40,11 @@ class AccountListPage { tag: 'button', }; + private readonly addSolanaAccountButton = { + text: messages.addNewSolanaAccount.message, + tag: 'button', + }; + private readonly addEthereumAccountButton = '[data-testid="multichain-account-menu-popover-add-account"]'; @@ -163,48 +168,11 @@ class AccountListPage { ); } - /** - * Adds a new BTC account with an optional custom name. - * - * @param options - Options for adding a new BTC account. - * @param [options.btcAccountCreationEnabled] - Indicates if the BTC account creation is expected to be enabled or disabled. Defaults to true. - * @param [options.accountName] - The custom name for the BTC account. Defaults to an empty string, which means the default name will be used. - */ - async addNewBtcAccount({ - btcAccountCreationEnabled = true, - accountName = '', - }: { - btcAccountCreationEnabled?: boolean; - accountName?: string; - } = {}): Promise { - console.log( - `Adding new BTC account${ - accountName ? ` with custom name: ${accountName}` : ' with default name' - }`, + async isBtcAccountCreationButtonEnabled() { + const createButton = await this.driver.findElement( + this.addBtcAccountButton, ); - await this.driver.clickElement(this.createAccountButton); - if (btcAccountCreationEnabled) { - await this.driver.clickElement(this.addBtcAccountButton); - // needed to mitigate a race condition with the state update - // there is no condition we can wait for in the UI - await this.driver.delay(largeDelayMs); - if (accountName) { - await this.driver.fill(this.accountNameInput, accountName); - } - await this.driver.clickElementAndWaitToDisappear( - this.addAccountConfirmButton, - // Longer timeout than usual, this reduces the flakiness - // around Bitcoin account creation (mainly required for - // Firefox) - 5000, - ); - } else { - const createButton = await this.driver.findElement( - this.addBtcAccountButton, - ); - assert.equal(await createButton.isEnabled(), false); - await this.driver.clickElement(this.closeAccountModalButton); - } + return await createButton.isEnabled(); } /** @@ -234,6 +202,48 @@ class AccountListPage { } } + /** + * Adds a new account of the specified type with an optional custom name. + * + * @param accountType - The type of account to add (Ethereum, Bitcoin, or Solana) + * @param accountName - Optional custom name for the new account + * @throws {Error} If the specified account type is not supported + * @example + * // Add a new Ethereum account with default name + * await accountListPage.addAccount(ACCOUNT_TYPE.Ethereum); + * + * // Add a new Bitcoin account with custom name + * await accountListPage.addAccount(ACCOUNT_TYPE.Bitcoin, 'My BTC Wallet'); + */ + async addAccount(accountType: ACCOUNT_TYPE, accountName?: string) { + await this.driver.clickElement(this.createAccountButton); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let addAccountButton: any; + switch (accountType) { + case ACCOUNT_TYPE.Ethereum: + addAccountButton = this.addEthereumAccountButton; + break; + case ACCOUNT_TYPE.Bitcoin: + addAccountButton = this.addBtcAccountButton; + break; + case ACCOUNT_TYPE.Solana: + addAccountButton = this.addSolanaAccountButton; + break; + default: + throw new Error('Account type not supported'); + } + + await this.driver.clickElement(addAccountButton); + if (accountName) { + await this.driver.fill(this.accountNameInput, accountName); + } + + await this.driver.clickElementAndWaitToDisappear( + this.addAccountConfirmButton, + 5000, + ); + } + /** * Changes the label of the current account. * @@ -600,11 +610,17 @@ class AccountListPage { console.log( `Verify the number of accounts in the account menu is: ${expectedNumberOfAccounts}`, ); + + await this.driver.waitForSelector(this.accountListItem); await this.driver.wait(async () => { const internalAccounts = await this.driver.findElements( this.accountListItem, ); - return internalAccounts.length === expectedNumberOfAccounts; + const isValid = internalAccounts.length === expectedNumberOfAccounts; + console.log( + `Number of accounts: ${internalAccounts.length} is equal to ${expectedNumberOfAccounts}? ${isValid}`, + ); + return isValid; }, 20000); } diff --git a/test/e2e/page-objects/pages/dialog/add-tokens.ts b/test/e2e/page-objects/pages/dialog/add-tokens.ts new file mode 100644 index 000000000000..e587113ab7f5 --- /dev/null +++ b/test/e2e/page-objects/pages/dialog/add-tokens.ts @@ -0,0 +1,50 @@ +import { strict as assert } from 'assert'; +import { Driver } from '../../../webdriver/driver'; + +class AddTokensModal { + protected driver: Driver; + + private addTokenButton = { text: 'Add token', tag: 'button' }; + + private tokenListItem = '.confirm-add-suggested-token__token-list-item'; + + constructor(driver: Driver) { + this.driver = driver; + } + + async check_pageIsLoaded(): Promise { + try { + await this.driver.waitForMultipleSelectors([ + this.tokenListItem, + this.addTokenButton, + ]); + } catch (e) { + console.log( + 'Timeout while waiting for Add tokens dialog to be loaded', + e, + ); + throw e; + } + console.log('Add tokens dialog was loaded'); + } + + /** + * Checks the count of suggested tokens. + * + * @param expectedTokenCount - The expected count of suggested tokens. + */ + async check_SuggestedTokensCount(expectedTokenCount: number) { + const multipleSuggestedTokens = await this.driver.findElements( + this.tokenListItem, + ); + + // Confirm the expected number of tokens are present as suggested token list + assert.equal(multipleSuggestedTokens.length, expectedTokenCount); + } + + async confirmAddTokens() { + await this.driver.clickElementAndWaitForWindowToClose(this.addTokenButton); + } +} + +export default AddTokensModal; diff --git a/test/e2e/page-objects/pages/dialog/create-contract.ts b/test/e2e/page-objects/pages/dialog/create-contract.ts new file mode 100644 index 000000000000..44c101271ffe --- /dev/null +++ b/test/e2e/page-objects/pages/dialog/create-contract.ts @@ -0,0 +1,39 @@ +import { Driver } from '../../../webdriver/driver'; + +class CreateContractModal { + protected driver: Driver; + + private readonly confirmButtton = { text: 'Confirm', tag: 'button' }; + + private readonly cancelButton = { text: 'Cancel', tag: 'button' }; + + constructor(driver: Driver) { + this.driver = driver; + } + + async check_pageIsLoaded(): Promise { + try { + await this.driver.waitForMultipleSelectors([ + this.confirmButtton, + this.cancelButton, + ]); + } catch (e) { + console.log( + 'Timeout while waiting for create contract dialog to be loaded', + e, + ); + throw e; + } + console.log('Create contract dialog was loaded'); + } + + async clickConfirm() { + await this.driver.clickElementAndWaitForWindowToClose(this.confirmButtton); + } + + async clickCancel() { + await this.driver.clickElementAndWaitForWindowToClose(this.cancelButton); + } +} + +export default CreateContractModal; diff --git a/test/e2e/page-objects/pages/header-navbar.ts b/test/e2e/page-objects/pages/header-navbar.ts index df45ecd0277d..57e02e864624 100644 --- a/test/e2e/page-objects/pages/header-navbar.ts +++ b/test/e2e/page-objects/pages/header-navbar.ts @@ -1,3 +1,4 @@ +import { strict as assert } from 'assert'; import { Driver } from '../../webdriver/driver'; class HeaderNavbar { @@ -22,6 +23,8 @@ class HeaderNavbar { private readonly switchNetworkDropDown = '[data-testid="network-display"]'; + private readonly networkPicker = '.mm-picker-network'; + constructor(driver: Driver) { this.driver = driver; } @@ -80,6 +83,21 @@ class HeaderNavbar { await this.driver.clickElement(this.switchNetworkDropDown); } + async check_currentSelectedNetwork(networkName: string): Promise { + console.log(`Validate the Switch network to ${networkName}`); + await this.driver.waitForSelector( + `button[data-testid="network-display"][aria-label="Network Menu ${networkName}"]`, + ); + } + + async check_ifNetworkPickerClickable(clickable: boolean): Promise { + console.log('Check whether the network picker is clickable or not'); + assert.equal( + await (await this.driver.findElement(this.networkPicker)).isEnabled(), + clickable, + ); + } + /** * Verifies that the displayed account label in header matches the expected label. * @@ -94,18 +112,6 @@ class HeaderNavbar { text: expectedLabel, }); } - - /** - * Validates that the currently selected network matches the expected network name. - * - * @param networkName - The expected name of the currently selected network. - */ - async check_currentSelectedNetwork(networkName: string): Promise { - console.log(`Validate the Switch network to ${networkName}`); - await this.driver.waitForSelector( - `button[data-testid="network-display"][aria-label="Network Menu ${networkName}"]`, - ); - } } export default HeaderNavbar; diff --git a/test/e2e/page-objects/pages/home/asset-list.ts b/test/e2e/page-objects/pages/home/asset-list.ts index db9367991bf7..9db896e3a035 100644 --- a/test/e2e/page-objects/pages/home/asset-list.ts +++ b/test/e2e/page-objects/pages/home/asset-list.ts @@ -204,6 +204,21 @@ class AssetListPage { }); } + /** + * This function checks if the specified token is displayed in the token list by its name. + * + * @param tokenName - The name of the token to check for. + * @returns A promise that resolves if the specified token is displayed. + */ + async check_tokenIsDisplayed(tokenName: string): Promise { + console.log(`Waiting for token ${tokenName} to be displayed`); + await this.driver.waitForSelector({ + text: tokenName, + tag: 'p', + }); + console.log(`Token ${tokenName} is displayed.`); + } + /** * This function checks if the specified number of token items is displayed in the token list. * diff --git a/test/e2e/page-objects/pages/home/homepage.ts b/test/e2e/page-objects/pages/home/homepage.ts index 1da2349752cb..b4b79846fb06 100644 --- a/test/e2e/page-objects/pages/home/homepage.ts +++ b/test/e2e/page-objects/pages/home/homepage.ts @@ -39,6 +39,8 @@ class HomePage { testId: 'popover-close', }; + private readonly portfolioLink = '[data-testid="portfolio-link"]'; + private readonly privacyBalanceToggle = { testId: 'sensitive-toggle', }; @@ -99,6 +101,11 @@ class HomePage { await this.driver.clickElement(this.nftTab); } + async openPortfolioPage(): Promise { + console.log(`Open portfolio page on homepage`); + await this.driver.clickElement(this.portfolioLink); + } + async refreshErc20TokenList(): Promise { console.log(`Refresh the ERC20 token list`); await this.driver.clickElement(this.erc20TokenDropdown); diff --git a/test/e2e/page-objects/pages/test-dapp.ts b/test/e2e/page-objects/pages/test-dapp.ts index 4eff00462351..5471afb3556a 100644 --- a/test/e2e/page-objects/pages/test-dapp.ts +++ b/test/e2e/page-objects/pages/test-dapp.ts @@ -8,6 +8,11 @@ const DAPP_URL = `http://${DAPP_HOST_ADDRESS}`; class TestDapp { private driver: Driver; + private readonly addTokensToWalletButton = { + text: 'Add Token(s) to Wallet', + tag: 'button', + }; + private readonly confirmDepositButton = '[data-testid="confirm-footer-button"]'; @@ -38,6 +43,8 @@ class TestDapp { private readonly simpleSendButton = '#sendButton'; + private readonly erc20TokenAddresses = '#erc20TokenAddresses'; + private readonly erc721MintButton = '#mintButton'; private readonly erc721TransferFromButton = '#transferFromButton'; @@ -177,6 +184,11 @@ class TestDapp { private erc20TokenTransferButton = '#transferTokens'; + private createTokenButton = { + text: 'Create Token', + tag: 'button', + }; + constructor(driver: Driver) { this.driver = driver; } @@ -221,6 +233,10 @@ class TestDapp { }); } + public async clickAddTokenToWallet() { + await this.driver.clickElement(this.addTokensToWalletButton); + } + async clickSimpleSendButton() { await this.driver.clickElement(this.simpleSendButton); } @@ -273,6 +289,16 @@ class TestDapp { await this.driver.clickElement(this.erc20TokenTransferButton); } + async confirmConnectAccountModal() { + console.log('Confirm connect account modal in notification window'); + await this.driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); + await this.driver.waitForSelector(this.connectMetaMaskMessage); + await this.driver.clickElementAndWaitForWindowToClose( + this.confirmDialogButton, + ); + await this.driver.switchToWindowWithTitle(WINDOW_TITLES.TestDApp); + } + /** * Connect account to test dapp. * @@ -289,25 +315,18 @@ class TestDapp { }) { console.log('Connect account to test dapp'); await this.driver.clickElement(this.connectAccountButton); - await this.driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - await this.driver.waitForSelector(this.connectMetaMaskMessage); if (connectAccountButtonEnabled) { - await this.driver.clickElementAndWaitForWindowToClose( - this.confirmDialogButton, - ); + await this.confirmConnectAccountModal(); } else { + await this.driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); + await this.driver.waitForSelector(this.connectMetaMaskMessage); const confirmConnectDialogButton = await this.driver.findElement( this.confirmDialogButton, ); assert.equal(await confirmConnectDialogButton.isEnabled(), false); } - if (publicAddress) { - await this.driver.switchToWindowWithTitle(WINDOW_TITLES.TestDApp); - await this.driver.waitForSelector({ - css: this.connectedAccount, - text: publicAddress.toLowerCase(), - }); + await this.check_connectedAccounts(publicAddress); await this.driver.waitForSelector(this.localhostNetworkMessage); } } @@ -332,28 +351,41 @@ class TestDapp { await this.driver.clickElement(this.revokePermissionButton); await this.driver.refresh(); await this.check_pageIsLoaded(); - await this.driver.assertElementNotPresent({ - css: this.connectedAccount, - text: publicAddress.toLowerCase(), - }); + await this.check_connectedAccounts(publicAddress, false); + } + + /** + * Scrolls to the create token button and clicks it. + */ + public async findAndClickCreateToken() { + const createTokenElement = await this.driver.findElement( + this.createTokenButton, + ); + await this.driver.scrollToElement(createTokenElement); + await this.driver.clickElement(this.createTokenButton); } /** * Verifies the accounts connected to the test dapp. * - * @param connectedAccounts - The expected connected accounts separated by a comma. If no accounts are connected we can omit the param. + * @param connectedAccounts - Account addresses to check if connected to test dapp, separated by a comma. + * @param shouldBeConnected - Whether the accounts should be connected to test dapp. Defaults to true. */ - async check_connectedAccounts(connectedAccounts: string = '') { - console.log('Verify connected accounts'); - if (connectedAccounts) { + async check_connectedAccounts( + connectedAccounts: string, + shouldBeConnected: boolean = true, + ) { + if (shouldBeConnected) { + console.log('Verify connected accounts:', connectedAccounts); await this.driver.waitForSelector({ css: this.connectedAccount, - text: connectedAccounts, + text: connectedAccounts.toLowerCase(), }); } else { - await this.driver.waitForSelector({ + console.log('Verify accounts not connected:', connectedAccounts); + await this.driver.assertElementNotPresent({ css: this.connectedAccount, - text: ' ', + text: connectedAccounts.toLowerCase(), }); } } @@ -594,6 +626,24 @@ class TestDapp { }); } + /** + * Checks the count of token addresses. + * + * @param expectedCount - The expected count of token addresses. + */ + async check_TokenAddressesCount(expectedCount: number) { + console.log(`checking token addresses count: ${expectedCount}`); + await this.driver.wait(async () => { + const tokenAddressesElement = await this.driver.findElement( + this.erc20TokenAddresses, + ); + const tokenAddresses = await tokenAddressesElement.getText(); + const addresses = tokenAddresses.split(',').filter(Boolean); + + return addresses.length === expectedCount; + }, 10000); + } + async verify_successSignTypedDataV4Result(result: string) { await this.driver.waitForSelector({ css: this.signTypedDataV4Result, diff --git a/test/e2e/tests/petnames/petnames-signatures.spec.js b/test/e2e/tests/petnames/petnames-signatures.spec.js index 4641424e053f..1ca7f47340d7 100644 --- a/test/e2e/tests/petnames/petnames-signatures.spec.js +++ b/test/e2e/tests/petnames/petnames-signatures.spec.js @@ -169,7 +169,7 @@ describe('Petnames - Signatures', function () { await createSignatureRequest(driver, SIGNATURE_TYPE.TYPED_V4); await switchToNotificationWindow(driver, 3); await expectName(driver, 'test.lens', true); - await expectName(driver, 'Test Token 2', true); + await expectName(driver, 'Test Toke...', true); await showThirdPartyDetails(driver); await expectName(driver, 'Custom Name', true); }, @@ -269,7 +269,7 @@ describe('Petnames - Signatures', function () { await createSignatureRequest(driver, SIGNATURE_TYPE.TYPED_V4); await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); await expectName(driver, 'test.lens', true); - await expectName(driver, 'Test Token 2', true); + await expectName(driver, 'Test Toke...', true); await expectName(driver, 'Custom Name', true); }, ); diff --git a/test/e2e/tests/portfolio/portfolio-site.spec.js b/test/e2e/tests/portfolio/portfolio-site.spec.ts similarity index 56% rename from test/e2e/tests/portfolio/portfolio-site.spec.js rename to test/e2e/tests/portfolio/portfolio-site.spec.ts index cba9c0452522..f630de50f1da 100644 --- a/test/e2e/tests/portfolio/portfolio-site.spec.js +++ b/test/e2e/tests/portfolio/portfolio-site.spec.ts @@ -1,13 +1,13 @@ -const { - withFixtures, - unlockWallet, - defaultGanacheOptions, -} = require('../../helpers'); -const FixtureBuilder = require('../../fixture-builder'); -const { emptyHtmlPage } = require('../../mock-e2e'); +import { MockttpServer } from 'mockttp'; +import { withFixtures } from '../../helpers'; +import { EMPTY_E2E_TEST_PAGE_TITLE } from '../../constants'; +import FixtureBuilder from '../../fixture-builder'; +import { emptyHtmlPage } from '../../mock-e2e'; +import HomePage from '../../page-objects/pages/home/homepage'; +import { loginWithBalanceValidation } from '../../page-objects/flows/login.flow'; describe('Portfolio site', function () { - async function mockPortfolioSite(mockServer) { + async function mockPortfolioSite(mockServer: MockttpServer) { return await mockServer .forGet('https://portfolio.metamask.io/') .withQuery({ @@ -27,18 +27,15 @@ describe('Portfolio site', function () { { dapp: true, fixtures: new FixtureBuilder().build(), - ganacheOptions: defaultGanacheOptions, - title: this.test.fullTitle(), + title: this.test?.fullTitle(), testSpecificMock: mockPortfolioSite, }, async ({ driver }) => { - await unlockWallet(driver); + await loginWithBalanceValidation(driver); + await new HomePage(driver).openPortfolioPage(); // Click Portfolio site - await driver.clickElement('[data-testid="portfolio-link"]'); - await driver.waitUntilXWindowHandles(2); - const windowHandles = await driver.getAllWindowHandles(); - await driver.switchToWindowWithTitle('E2E Test Page', windowHandles); + await driver.switchToWindowWithTitle(EMPTY_E2E_TEST_PAGE_TITLE); // Verify site await driver.waitForUrl({ diff --git a/test/e2e/tests/request-queuing/sendTx-revokePermissions.spec.ts b/test/e2e/tests/request-queuing/sendTx-revokePermissions.spec.ts index c5ff864df99c..9f5454404806 100644 --- a/test/e2e/tests/request-queuing/sendTx-revokePermissions.spec.ts +++ b/test/e2e/tests/request-queuing/sendTx-revokePermissions.spec.ts @@ -41,9 +41,7 @@ describe('Request Queuing', function () { // Open test dapp const testDapp = new TestDapp(driver); await testDapp.openTestDappPage(); - await testDapp.check_connectedAccounts( - DEFAULT_FIXTURE_ACCOUNT.toLowerCase(), - ); + await testDapp.check_connectedAccounts(DEFAULT_FIXTURE_ACCOUNT); // Trigger a tx await testDapp.clickSimpleSendButton(); @@ -71,7 +69,7 @@ describe('Request Queuing', function () { await driver.waitUntilXWindowHandles(2); // Cleared eth_accounts account label - await testDapp.check_connectedAccounts(); + await testDapp.check_connectedAccounts(DEFAULT_FIXTURE_ACCOUNT, false); }, ); }); diff --git a/test/e2e/tests/tokens/add-multiple-tokens.spec.js b/test/e2e/tests/tokens/add-multiple-tokens.spec.js deleted file mode 100644 index bc6ea5e6f2c0..000000000000 --- a/test/e2e/tests/tokens/add-multiple-tokens.spec.js +++ /dev/null @@ -1,114 +0,0 @@ -const { strict: assert } = require('assert'); -const { - withFixtures, - defaultGanacheOptions, - openDapp, - switchToNotificationWindow, - WINDOW_TITLES, - DAPP_URL, - unlockWallet, -} = require('../../helpers'); -const FixtureBuilder = require('../../fixture-builder'); - -describe('Multiple ERC20 Watch Asset', function () { - // TODO: This assertion will change once the method is fixed. - it('should show multiple erc20 watchAsset token list, only confirms one bug', async function () { - await withFixtures( - { - dapp: true, - fixtures: new FixtureBuilder() - .withPermissionControllerConnectedToTestDapp() - .build(), - ganacheOptions: defaultGanacheOptions, - title: this.test.fullTitle(), - }, - async ({ driver }) => { - await unlockWallet(driver); - - await openDapp(driver, undefined, DAPP_URL); - - // Create Token 1 - const createToken = await driver.findElement({ - text: 'Create Token', - tag: 'button', - }); - await driver.scrollToElement(createToken); - await driver.clickElement({ text: 'Create Token', tag: 'button' }); - await switchToNotificationWindow(driver); - await driver.findClickableElement({ text: 'Confirm', tag: 'button' }); - await driver.clickElement({ text: 'Confirm', tag: 'button' }); - - // Wait for token 1 address to populate in dapp - await driver.switchToWindowWithTitle(WINDOW_TITLES.TestDApp); - await driver.wait(async () => { - const tokenAddressesElement = await driver.findElement( - '#erc20TokenAddresses', - ); - const tokenAddresses = await tokenAddressesElement.getText(); - return tokenAddresses !== ''; - }, 10000); - - // Create Token 2 - await driver.clickElement({ text: 'Create Token', tag: 'button' }); - await switchToNotificationWindow(driver); - await driver.findClickableElement({ text: 'Confirm', tag: 'button' }); - await driver.clickElement({ text: 'Confirm', tag: 'button' }); - - // Wait for token 2 address to populate in dapp - await driver.switchToWindowWithTitle(WINDOW_TITLES.TestDApp); - await driver.wait(async () => { - const tokenAddressesElement = await driver.findElement( - '#erc20TokenAddresses', - ); - const tokenAddresses = await tokenAddressesElement.getText(); - return tokenAddresses.split(',').length === 2; - }, 10000); - - // Create Token 3 - await driver.clickElement({ text: 'Create Token', tag: 'button' }); - await switchToNotificationWindow(driver); - await driver.findClickableElement({ text: 'Confirm', tag: 'button' }); - await driver.clickElement({ text: 'Confirm', tag: 'button' }); - - // Wait for token 3 address to populate in dapp - await driver.switchToWindowWithTitle(WINDOW_TITLES.TestDApp); - await driver.wait(async () => { - const tokenAddressesElement = await driver.findElement( - '#erc20TokenAddresses', - ); - const tokenAddresses = await tokenAddressesElement.getText(); - return tokenAddresses.split(',').length === 3; - }, 10000); - - // Watch all 3 tokens - await driver.clickElement({ - text: 'Add Token(s) to Wallet', - tag: 'button', - }); - - // Switch to watchAsset notification - await switchToNotificationWindow(driver); - const multipleSuggestedtokens = await driver.findElements( - '.confirm-add-suggested-token__token-list-item', - ); - - // Confirm all 3 tokens are present as suggested token list - assert.equal(multipleSuggestedtokens.length, 3); - await driver.findClickableElement({ text: 'Add token', tag: 'button' }); - await driver.clickElement({ text: 'Add token', tag: 'button' }); - - // Switch to fullscreen extension - await driver.switchToWindowWithTitle( - WINDOW_TITLES.ExtensionInFullScreenView, - ); - - // Check all three tokens have been added to the token list. - const addedTokens = await driver.findElements({ - tag: 'p', - text: 'TST', - }); - assert.equal(addedTokens.length, 3); - }, - ); - }); -}); diff --git a/test/e2e/tests/tokens/add-multiple-tokens.spec.ts b/test/e2e/tests/tokens/add-multiple-tokens.spec.ts new file mode 100644 index 000000000000..78daa2531da1 --- /dev/null +++ b/test/e2e/tests/tokens/add-multiple-tokens.spec.ts @@ -0,0 +1,70 @@ +import AddTokensModal from '../../page-objects/pages/dialog/add-tokens'; +import AssetListPage from '../../page-objects/pages/home/asset-list'; +import TestDapp from '../../page-objects/pages/test-dapp'; +import { + withFixtures, + defaultGanacheOptions, + openDapp, + WINDOW_TITLES, + DAPP_URL, + unlockWallet, +} from '../../helpers'; +import FixtureBuilder from '../../fixture-builder'; +import CreateContractModal from '../../page-objects/pages/dialog/create-contract'; + +describe('Multiple ERC20 Watch Asset', function () { + it('should show multiple erc20 watchAsset token list, only confirms one bug', async function () { + await withFixtures( + { + dapp: true, + fixtures: new FixtureBuilder() + .withPermissionControllerConnectedToTestDapp() + .build(), + ganacheOptions: defaultGanacheOptions, + title: this.test?.fullTitle(), + }, + async ({ driver }) => { + await unlockWallet(driver); + await openDapp(driver, undefined, DAPP_URL); + const testDapp = new TestDapp(driver); + + // Create multiple tokens + for (let i = 0; i < 3; i++) { + // Create token + await testDapp.findAndClickCreateToken(); + + // Confirm token creation + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); + const createContractModal = new CreateContractModal(driver); + await createContractModal.check_pageIsLoaded(); + await createContractModal.clickConfirm(); + + // Wait for token address to populate in dapp + await driver.switchToWindowWithTitle(WINDOW_TITLES.TestDApp); + await testDapp.check_pageIsLoaded(); + await testDapp.check_TokenAddressesCount(i + 1); + } + + // Watch all 3 tokens + // Switch to watchAsset notification + await testDapp.clickAddTokenToWallet(); + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); + const addTokensPopupModal = new AddTokensModal(driver); + await addTokensPopupModal.check_pageIsLoaded(); + await addTokensPopupModal.check_SuggestedTokensCount(3); + await addTokensPopupModal.confirmAddTokens(); + + // Switch to fullscreen extension + await driver.switchToWindowWithTitle( + WINDOW_TITLES.ExtensionInFullScreenView, + ); + + // Check all three tokens have been added to the token list. + const tokenList = new AssetListPage(driver); + await tokenList.check_tokenItemNumber(4); // 3 tokens plus ETH + await tokenList.check_tokenIsDisplayed('Ethereum'); + await tokenList.check_tokenIsDisplayed('TST'); + }, + ); + }); +}); diff --git a/test/integration/confirmations/signatures/permit-batch.test.tsx b/test/integration/confirmations/signatures/permit-batch.test.tsx index ea311537001c..7a3050dc15c8 100644 --- a/test/integration/confirmations/signatures/permit-batch.test.tsx +++ b/test/integration/confirmations/signatures/permit-batch.test.tsx @@ -1,10 +1,11 @@ import { act, fireEvent, screen } from '@testing-library/react'; import nock from 'nock'; -import mockMetaMaskState from '../../data/integration-init-state.json'; -import { integrationTestRender } from '../../../lib/render-helpers'; +import { useAssetDetails } from '../../../../ui/pages/confirmations/hooks/useAssetDetails'; import * as backgroundConnection from '../../../../ui/store/background-connection'; -import { createMockImplementation } from '../../helpers'; import { tEn } from '../../../lib/i18n-helpers'; +import { integrationTestRender } from '../../../lib/render-helpers'; +import mockMetaMaskState from '../../data/integration-init-state.json'; +import { createMockImplementation } from '../../helpers'; import { getMetaMaskStateWithUnapprovedPermitSign, verifyDetails, @@ -15,10 +16,20 @@ jest.mock('../../../../ui/store/background-connection', () => ({ submitRequestToBackground: jest.fn(), })); +jest.mock('../../../../ui/pages/confirmations/hooks/useAssetDetails', () => ({ + ...jest.requireActual( + '../../../../ui/pages/confirmations/hooks/useAssetDetails', + ), + useAssetDetails: jest.fn().mockResolvedValue({ + decimals: '4', + }), +})); + const mockedBackgroundConnection = jest.mocked(backgroundConnection); const backgroundConnectionMocked = { onNotification: jest.fn(), }; +const mockedAssetDetails = jest.mocked(useAssetDetails); const renderPermitBatchSignature = async () => { const account = @@ -58,6 +69,10 @@ describe('Permit Batch Signature Tests', () => { getTokenStandardAndDetails: { decimals: '2' }, }), ); + mockedAssetDetails.mockImplementation(() => ({ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + decimals: '4' as any, + })); }); afterEach(() => { diff --git a/test/integration/confirmations/signatures/permit-seaport.test.tsx b/test/integration/confirmations/signatures/permit-seaport.test.tsx index 7829a2714b57..dc32671da245 100644 --- a/test/integration/confirmations/signatures/permit-seaport.test.tsx +++ b/test/integration/confirmations/signatures/permit-seaport.test.tsx @@ -1,10 +1,11 @@ import { act, fireEvent, screen } from '@testing-library/react'; import nock from 'nock'; -import mockMetaMaskState from '../../data/integration-init-state.json'; -import { integrationTestRender } from '../../../lib/render-helpers'; +import { useAssetDetails } from '../../../../ui/pages/confirmations/hooks/useAssetDetails'; import * as backgroundConnection from '../../../../ui/store/background-connection'; -import { createMockImplementation } from '../../helpers'; import { tEn } from '../../../lib/i18n-helpers'; +import { integrationTestRender } from '../../../lib/render-helpers'; +import mockMetaMaskState from '../../data/integration-init-state.json'; +import { createMockImplementation } from '../../helpers'; import { getMetaMaskStateWithUnapprovedPermitSign, verifyDetails, @@ -15,10 +16,20 @@ jest.mock('../../../../ui/store/background-connection', () => ({ submitRequestToBackground: jest.fn(), })); +jest.mock('../../../../ui/pages/confirmations/hooks/useAssetDetails', () => ({ + ...jest.requireActual( + '../../../../ui/pages/confirmations/hooks/useAssetDetails', + ), + useAssetDetails: jest.fn().mockResolvedValue({ + decimals: '4', + }), +})); + const mockedBackgroundConnection = jest.mocked(backgroundConnection); const backgroundConnectionMocked = { onNotification: jest.fn(), }; +const mockedAssetDetails = jest.mocked(useAssetDetails); const renderSeaportSignature = async () => { const account = @@ -58,6 +69,10 @@ describe('Permit Seaport Tests', () => { getTokenStandardAndDetails: { decimals: '2' }, }), ); + mockedAssetDetails.mockImplementation(() => ({ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + decimals: '4' as any, + })); }); afterEach(() => { diff --git a/test/integration/confirmations/signatures/permit-single.test.tsx b/test/integration/confirmations/signatures/permit-single.test.tsx index abb5d07c1587..96fe5c26491d 100644 --- a/test/integration/confirmations/signatures/permit-single.test.tsx +++ b/test/integration/confirmations/signatures/permit-single.test.tsx @@ -1,10 +1,11 @@ import { act, fireEvent, screen } from '@testing-library/react'; import nock from 'nock'; -import mockMetaMaskState from '../../data/integration-init-state.json'; -import { integrationTestRender } from '../../../lib/render-helpers'; +import { useAssetDetails } from '../../../../ui/pages/confirmations/hooks/useAssetDetails'; import * as backgroundConnection from '../../../../ui/store/background-connection'; -import { createMockImplementation } from '../../helpers'; import { tEn } from '../../../lib/i18n-helpers'; +import { integrationTestRender } from '../../../lib/render-helpers'; +import mockMetaMaskState from '../../data/integration-init-state.json'; +import { createMockImplementation } from '../../helpers'; import { getMetaMaskStateWithUnapprovedPermitSign, verifyDetails, @@ -15,10 +16,20 @@ jest.mock('../../../../ui/store/background-connection', () => ({ submitRequestToBackground: jest.fn(), })); +jest.mock('../../../../ui/pages/confirmations/hooks/useAssetDetails', () => ({ + ...jest.requireActual( + '../../../../ui/pages/confirmations/hooks/useAssetDetails', + ), + useAssetDetails: jest.fn().mockResolvedValue({ + decimals: '4', + }), +})); + const mockedBackgroundConnection = jest.mocked(backgroundConnection); const backgroundConnectionMocked = { onNotification: jest.fn(), }; +const mockedAssetDetails = jest.mocked(useAssetDetails); const renderSingleBatchSignature = async () => { const account = @@ -58,6 +69,10 @@ describe('Permit Single Signature Tests', () => { getTokenStandardAndDetails: { decimals: '2' }, }), ); + mockedAssetDetails.mockImplementation(() => ({ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + decimals: '4' as any, + })); }); afterEach(() => { diff --git a/test/integration/confirmations/signatures/permit-tradeOrder.test.tsx b/test/integration/confirmations/signatures/permit-tradeOrder.test.tsx index 429e533ff8f8..3f915967dc00 100644 --- a/test/integration/confirmations/signatures/permit-tradeOrder.test.tsx +++ b/test/integration/confirmations/signatures/permit-tradeOrder.test.tsx @@ -1,10 +1,11 @@ import { act, screen } from '@testing-library/react'; import nock from 'nock'; -import mockMetaMaskState from '../../data/integration-init-state.json'; -import { integrationTestRender } from '../../../lib/render-helpers'; +import { useAssetDetails } from '../../../../ui/pages/confirmations/hooks/useAssetDetails'; import * as backgroundConnection from '../../../../ui/store/background-connection'; -import { createMockImplementation } from '../../helpers'; import { tEn } from '../../../lib/i18n-helpers'; +import { integrationTestRender } from '../../../lib/render-helpers'; +import mockMetaMaskState from '../../data/integration-init-state.json'; +import { createMockImplementation } from '../../helpers'; import { getMetaMaskStateWithUnapprovedPermitSign, verifyDetails, @@ -15,10 +16,20 @@ jest.mock('../../../../ui/store/background-connection', () => ({ submitRequestToBackground: jest.fn(), })); +jest.mock('../../../../ui/pages/confirmations/hooks/useAssetDetails', () => ({ + ...jest.requireActual( + '../../../../ui/pages/confirmations/hooks/useAssetDetails', + ), + useAssetDetails: jest.fn().mockResolvedValue({ + decimals: '4', + }), +})); + const mockedBackgroundConnection = jest.mocked(backgroundConnection); const backgroundConnectionMocked = { onNotification: jest.fn(), }; +const mockedAssetDetails = jest.mocked(useAssetDetails); const renderTradeOrderSignature = async () => { const account = @@ -58,6 +69,10 @@ describe('Permit Trade Order Tests', () => { getTokenStandardAndDetails: { decimals: '2' }, }), ); + mockedAssetDetails.mockImplementation(() => ({ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + decimals: '4' as any, + })); }); afterEach(() => { diff --git a/test/integration/confirmations/signatures/permit.test.tsx b/test/integration/confirmations/signatures/permit.test.tsx index 6b736e4add90..332cebc3a6b2 100644 --- a/test/integration/confirmations/signatures/permit.test.tsx +++ b/test/integration/confirmations/signatures/permit.test.tsx @@ -7,6 +7,7 @@ import { MetaMetricsEventName, } from '../../../../shared/constants/metametrics'; import { shortenAddress } from '../../../../ui/helpers/utils/util'; +import { useAssetDetails } from '../../../../ui/pages/confirmations/hooks/useAssetDetails'; import * as backgroundConnection from '../../../../ui/store/background-connection'; import { integrationTestRender } from '../../../lib/render-helpers'; import mockMetaMaskState from '../../data/integration-init-state.json'; @@ -18,10 +19,20 @@ jest.mock('../../../../ui/store/background-connection', () => ({ submitRequestToBackground: jest.fn(), })); +jest.mock('../../../../ui/pages/confirmations/hooks/useAssetDetails', () => ({ + ...jest.requireActual( + '../../../../ui/pages/confirmations/hooks/useAssetDetails', + ), + useAssetDetails: jest.fn().mockResolvedValue({ + decimals: '4', + }), +})); + const mockedBackgroundConnection = jest.mocked(backgroundConnection); const backgroundConnectionMocked = { onNotification: jest.fn(), }; +const mockedAssetDetails = jest.mocked(useAssetDetails); describe('Permit Confirmation', () => { beforeEach(() => { @@ -31,6 +42,10 @@ describe('Permit Confirmation', () => { getTokenStandardAndDetails: { decimals: '2', standard: 'ERC20' }, }), ); + mockedAssetDetails.mockImplementation(() => ({ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + decimals: '4' as any, + })); }); afterEach(() => { diff --git a/test/integration/confirmations/signatures/personalSign.test.tsx b/test/integration/confirmations/signatures/personalSign.test.tsx index 03685f46ab7b..d58e0d441e03 100644 --- a/test/integration/confirmations/signatures/personalSign.test.tsx +++ b/test/integration/confirmations/signatures/personalSign.test.tsx @@ -1,6 +1,6 @@ import { ApprovalType } from '@metamask/controller-utils'; -import { act, fireEvent, screen, waitFor } from '@testing-library/react'; import { CHAIN_IDS } from '@metamask/transaction-controller'; +import { act, fireEvent, screen, waitFor } from '@testing-library/react'; import { MESSAGE_TYPE } from '../../../../shared/constants/app'; import { MetaMetricsEventCategory, @@ -8,6 +8,7 @@ import { MetaMetricsEventName, } from '../../../../shared/constants/metametrics'; import { shortenAddress } from '../../../../ui/helpers/utils/util'; +import { useAssetDetails } from '../../../../ui/pages/confirmations/hooks/useAssetDetails'; import * as backgroundConnection from '../../../../ui/store/background-connection'; import { integrationTestRender } from '../../../lib/render-helpers'; import mockMetaMaskState from '../../data/integration-init-state.json'; @@ -17,7 +18,17 @@ jest.mock('../../../../ui/store/background-connection', () => ({ submitRequestToBackground: jest.fn(), })); +jest.mock('../../../../ui/pages/confirmations/hooks/useAssetDetails', () => ({ + ...jest.requireActual( + '../../../../ui/pages/confirmations/hooks/useAssetDetails', + ), + useAssetDetails: jest.fn().mockResolvedValue({ + decimals: '4', + }), +})); + const mockedBackgroundConnection = jest.mocked(backgroundConnection); +const mockedAssetDetails = jest.mocked(useAssetDetails); const backgroundConnectionMocked = { onNotification: jest.fn(), @@ -68,6 +79,10 @@ const getMetaMaskStateWithUnapprovedPersonalSign = (accountAddress: string) => { describe('PersonalSign Confirmation', () => { beforeEach(() => { jest.resetAllMocks(); + mockedAssetDetails.mockImplementation(() => ({ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + decimals: '4' as any, + })); }); it('displays the header account modal with correct data', async () => { diff --git a/test/integration/confirmations/transactions/alerts.test.tsx b/test/integration/confirmations/transactions/alerts.test.tsx index b21a7dbb679c..ee7e95762ac3 100644 --- a/test/integration/confirmations/transactions/alerts.test.tsx +++ b/test/integration/confirmations/transactions/alerts.test.tsx @@ -1,12 +1,13 @@ import { randomUUID } from 'crypto'; -import { act, fireEvent, screen } from '@testing-library/react'; import { ApprovalType } from '@metamask/controller-utils'; +import { act, fireEvent, screen } from '@testing-library/react'; import nock from 'nock'; -import mockMetaMaskState from '../../data/integration-init-state.json'; -import { integrationTestRender } from '../../../lib/render-helpers'; +import { useAssetDetails } from '../../../../ui/pages/confirmations/hooks/useAssetDetails'; import * as backgroundConnection from '../../../../ui/store/background-connection'; -import { createMockImplementation, mock4byte } from '../../helpers'; +import { integrationTestRender } from '../../../lib/render-helpers'; import { createTestProviderTools } from '../../../stub/provider'; +import mockMetaMaskState from '../../data/integration-init-state.json'; +import { createMockImplementation, mock4byte } from '../../helpers'; import { getUnapprovedApproveTransaction } from './transactionDataHelpers'; jest.mock('../../../../ui/store/background-connection', () => ({ @@ -15,7 +16,17 @@ jest.mock('../../../../ui/store/background-connection', () => ({ callBackgroundMethod: jest.fn(), })); +jest.mock('../../../../ui/pages/confirmations/hooks/useAssetDetails', () => ({ + ...jest.requireActual( + '../../../../ui/pages/confirmations/hooks/useAssetDetails', + ), + useAssetDetails: jest.fn().mockResolvedValue({ + decimals: '4', + }), +})); + const mockedBackgroundConnection = jest.mocked(backgroundConnection); +const mockedAssetDetails = jest.mocked(useAssetDetails); const backgroundConnectionMocked = { onNotification: jest.fn(), @@ -92,6 +103,10 @@ describe('Contract Interaction Confirmation Alerts', () => { setupSubmitRequestToBackgroundMocks(); const APPROVE_NFT_HEX_SIG = '0x095ea7b3'; mock4byte(APPROVE_NFT_HEX_SIG); + mockedAssetDetails.mockImplementation(() => ({ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + decimals: '4' as any, + })); }); afterEach(() => { diff --git a/test/integration/confirmations/transactions/contract-deployment.test.tsx b/test/integration/confirmations/transactions/contract-deployment.test.tsx index 7698ce3ef8b2..45410c0a76d9 100644 --- a/test/integration/confirmations/transactions/contract-deployment.test.tsx +++ b/test/integration/confirmations/transactions/contract-deployment.test.tsx @@ -13,6 +13,7 @@ import { MetaMetricsEventLocation, MetaMetricsEventName, } from '../../../../shared/constants/metametrics'; +import { useAssetDetails } from '../../../../ui/pages/confirmations/hooks/useAssetDetails'; import * as backgroundConnection from '../../../../ui/store/background-connection'; import { tEn } from '../../../lib/i18n-helpers'; import { integrationTestRender } from '../../../lib/render-helpers'; @@ -26,7 +27,17 @@ jest.mock('../../../../ui/store/background-connection', () => ({ callBackgroundMethod: jest.fn(), })); +jest.mock('../../../../ui/pages/confirmations/hooks/useAssetDetails', () => ({ + ...jest.requireActual( + '../../../../ui/pages/confirmations/hooks/useAssetDetails', + ), + useAssetDetails: jest.fn().mockResolvedValue({ + decimals: '4', + }), +})); + const mockedBackgroundConnection = jest.mocked(backgroundConnection); +const mockedAssetDetails = jest.mocked(useAssetDetails); const backgroundConnectionMocked = { onNotification: jest.fn(), @@ -136,6 +147,10 @@ describe('Contract Deployment Confirmation', () => { setupSubmitRequestToBackgroundMocks(); const DEPOSIT_HEX_SIG = '0xd0e30db0'; mock4byte(DEPOSIT_HEX_SIG); + mockedAssetDetails.mockImplementation(() => ({ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + decimals: '4' as any, + })); }); afterEach(() => { diff --git a/test/integration/confirmations/transactions/contract-interaction.test.tsx b/test/integration/confirmations/transactions/contract-interaction.test.tsx index 5db121bc9fda..e3da57aeceb3 100644 --- a/test/integration/confirmations/transactions/contract-interaction.test.tsx +++ b/test/integration/confirmations/transactions/contract-interaction.test.tsx @@ -13,6 +13,7 @@ import { MetaMetricsEventLocation, MetaMetricsEventName, } from '../../../../shared/constants/metametrics'; +import { useAssetDetails } from '../../../../ui/pages/confirmations/hooks/useAssetDetails'; import * as backgroundConnection from '../../../../ui/store/background-connection'; import { tEn } from '../../../lib/i18n-helpers'; import { integrationTestRender } from '../../../lib/render-helpers'; @@ -31,7 +32,17 @@ jest.mock('../../../../ui/store/background-connection', () => ({ callBackgroundMethod: jest.fn(), })); +jest.mock('../../../../ui/pages/confirmations/hooks/useAssetDetails', () => ({ + ...jest.requireActual( + '../../../../ui/pages/confirmations/hooks/useAssetDetails', + ), + useAssetDetails: jest.fn().mockResolvedValue({ + decimals: '4', + }), +})); + const mockedBackgroundConnection = jest.mocked(backgroundConnection); +const mockedAssetDetails = jest.mocked(useAssetDetails); const backgroundConnectionMocked = { onNotification: jest.fn(), @@ -156,6 +167,10 @@ describe('Contract Interaction Confirmation', () => { setupSubmitRequestToBackgroundMocks(); const MINT_NFT_HEX_SIG = '0x3b4b1381'; mock4byte(MINT_NFT_HEX_SIG); + mockedAssetDetails.mockImplementation(() => ({ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + decimals: '4' as any, + })); }); afterEach(() => { diff --git a/test/integration/confirmations/transactions/erc20-approve.test.tsx b/test/integration/confirmations/transactions/erc20-approve.test.tsx index c25b2ee3627d..f77c8162b79d 100644 --- a/test/integration/confirmations/transactions/erc20-approve.test.tsx +++ b/test/integration/confirmations/transactions/erc20-approve.test.tsx @@ -3,6 +3,7 @@ import { act, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import nock from 'nock'; import { TokenStandard } from '../../../../shared/constants/transaction'; +import { useAssetDetails } from '../../../../ui/pages/confirmations/hooks/useAssetDetails'; import * as backgroundConnection from '../../../../ui/store/background-connection'; import { tEn } from '../../../lib/i18n-helpers'; import { integrationTestRender } from '../../../lib/render-helpers'; @@ -17,7 +18,17 @@ jest.mock('../../../../ui/store/background-connection', () => ({ callBackgroundMethod: jest.fn(), })); +jest.mock('../../../../ui/pages/confirmations/hooks/useAssetDetails', () => ({ + ...jest.requireActual( + '../../../../ui/pages/confirmations/hooks/useAssetDetails', + ), + useAssetDetails: jest.fn().mockImplementation(() => ({ + decimals: '4', + })), +})); + const mockedBackgroundConnection = jest.mocked(backgroundConnection); +const mockedAssetDetails = jest.mocked(useAssetDetails); const backgroundConnectionMocked = { onNotification: jest.fn(), @@ -140,6 +151,10 @@ describe('ERC20 Approve Confirmation', () => { const APPROVE_ERC20_HEX_SIG = '0x095ea7b3'; const APPROVE_ERC20_TEXT_SIG = 'approve(address,uint256)'; mock4byte(APPROVE_ERC20_HEX_SIG, APPROVE_ERC20_TEXT_SIG); + mockedAssetDetails.mockImplementation(() => ({ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + decimals: '4' as any, + })); }); afterEach(() => { diff --git a/test/integration/confirmations/transactions/erc721-approve.test.tsx b/test/integration/confirmations/transactions/erc721-approve.test.tsx index 4f211576e6cd..cce5456ae0a2 100644 --- a/test/integration/confirmations/transactions/erc721-approve.test.tsx +++ b/test/integration/confirmations/transactions/erc721-approve.test.tsx @@ -3,6 +3,7 @@ import { act, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import nock from 'nock'; import { TokenStandard } from '../../../../shared/constants/transaction'; +import { useAssetDetails } from '../../../../ui/pages/confirmations/hooks/useAssetDetails'; import * as backgroundConnection from '../../../../ui/store/background-connection'; import { tEn } from '../../../lib/i18n-helpers'; import { integrationTestRender } from '../../../lib/render-helpers'; @@ -17,7 +18,17 @@ jest.mock('../../../../ui/store/background-connection', () => ({ callBackgroundMethod: jest.fn(), })); +jest.mock('../../../../ui/pages/confirmations/hooks/useAssetDetails', () => ({ + ...jest.requireActual( + '../../../../ui/pages/confirmations/hooks/useAssetDetails', + ), + useAssetDetails: jest.fn().mockResolvedValue({ + decimals: '4', + }), +})); + const mockedBackgroundConnection = jest.mocked(backgroundConnection); +const mockedAssetDetails = jest.mocked(useAssetDetails); const backgroundConnectionMocked = { onNotification: jest.fn(), @@ -140,6 +151,10 @@ describe('ERC721 Approve Confirmation', () => { const APPROVE_NFT_HEX_SIG = '0x095ea7b3'; const APPROVE_NFT_TEXT_SIG = 'approve(address,uint256)'; mock4byte(APPROVE_NFT_HEX_SIG, APPROVE_NFT_TEXT_SIG); + mockedAssetDetails.mockImplementation(() => ({ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + decimals: '4' as any, + })); }); afterEach(() => { @@ -220,6 +235,7 @@ describe('ERC721 Approve Confirmation', () => { const approveDetails = await screen.findByTestId( 'confirmation__approve-details', ); + expect(approveDetails).toBeInTheDocument(); const approveDetailsSpender = await screen.findByTestId( 'confirmation__approve-spender', diff --git a/test/integration/confirmations/transactions/increase-allowance.test.tsx b/test/integration/confirmations/transactions/increase-allowance.test.tsx index 810477d3a3a5..49b33bf2d21b 100644 --- a/test/integration/confirmations/transactions/increase-allowance.test.tsx +++ b/test/integration/confirmations/transactions/increase-allowance.test.tsx @@ -3,6 +3,7 @@ import { act, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import nock from 'nock'; import { TokenStandard } from '../../../../shared/constants/transaction'; +import { useAssetDetails } from '../../../../ui/pages/confirmations/hooks/useAssetDetails'; import * as backgroundConnection from '../../../../ui/store/background-connection'; import { tEn } from '../../../lib/i18n-helpers'; import { integrationTestRender } from '../../../lib/render-helpers'; @@ -17,7 +18,17 @@ jest.mock('../../../../ui/store/background-connection', () => ({ callBackgroundMethod: jest.fn(), })); +jest.mock('../../../../ui/pages/confirmations/hooks/useAssetDetails', () => ({ + ...jest.requireActual( + '../../../../ui/pages/confirmations/hooks/useAssetDetails', + ), + useAssetDetails: jest.fn().mockResolvedValue({ + decimals: '4', + }), +})); + const mockedBackgroundConnection = jest.mocked(backgroundConnection); +const mockedAssetDetails = jest.mocked(useAssetDetails); const backgroundConnectionMocked = { onNotification: jest.fn(), @@ -144,6 +155,10 @@ describe('ERC20 increaseAllowance Confirmation', () => { INCREASE_ALLOWANCE_ERC20_HEX_SIG, INCREASE_ALLOWANCE_ERC20_TEXT_SIG, ); + mockedAssetDetails.mockImplementation(() => ({ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + decimals: '4' as any, + })); }); afterEach(() => { diff --git a/test/integration/confirmations/transactions/set-approval-for-all.test.tsx b/test/integration/confirmations/transactions/set-approval-for-all.test.tsx index ebe680983a6c..236162c7b08f 100644 --- a/test/integration/confirmations/transactions/set-approval-for-all.test.tsx +++ b/test/integration/confirmations/transactions/set-approval-for-all.test.tsx @@ -3,6 +3,7 @@ import { act, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import nock from 'nock'; import { TokenStandard } from '../../../../shared/constants/transaction'; +import { useAssetDetails } from '../../../../ui/pages/confirmations/hooks/useAssetDetails'; import * as backgroundConnection from '../../../../ui/store/background-connection'; import { tEn } from '../../../lib/i18n-helpers'; import { integrationTestRender } from '../../../lib/render-helpers'; @@ -17,7 +18,17 @@ jest.mock('../../../../ui/store/background-connection', () => ({ callBackgroundMethod: jest.fn(), })); +jest.mock('../../../../ui/pages/confirmations/hooks/useAssetDetails', () => ({ + ...jest.requireActual( + '../../../../ui/pages/confirmations/hooks/useAssetDetails', + ), + useAssetDetails: jest.fn().mockResolvedValue({ + decimals: '4', + }), +})); + const mockedBackgroundConnection = jest.mocked(backgroundConnection); +const mockedAssetDetails = jest.mocked(useAssetDetails); const backgroundConnectionMocked = { onNotification: jest.fn(), @@ -144,6 +155,10 @@ describe('ERC721 setApprovalForAll Confirmation', () => { INCREASE_SET_APPROVAL_FOR_ALL_HEX_SIG, INCREASE_SET_APPROVAL_FOR_ALL_TEXT_SIG, ); + mockedAssetDetails.mockImplementation(() => ({ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + decimals: '4' as any, + })); }); afterEach(() => { diff --git a/ui/components/app/assets/asset-list/asset-list-control-bar/asset-list-control-bar.test.tsx b/ui/components/app/assets/asset-list/asset-list-control-bar/asset-list-control-bar.test.tsx new file mode 100644 index 000000000000..7c6ba579943c --- /dev/null +++ b/ui/components/app/assets/asset-list/asset-list-control-bar/asset-list-control-bar.test.tsx @@ -0,0 +1,62 @@ +import React from 'react'; +import thunk from 'redux-thunk'; +import configureMockStore from 'redux-mock-store'; +import { renderWithProvider } from '../../../../../../test/jest'; +import { MetaMetricsContext } from '../../../../../contexts/metametrics'; +import { + MetaMetricsEventCategory, + MetaMetricsEventName, +} from '../../../../../../shared/constants/metametrics'; +import AssetListControlBar from './asset-list-control-bar'; + +describe('AssetListControlBar', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + it('should fire metrics event when refresh button is clicked', async () => { + const store = configureMockStore([thunk])({ + metamask: { + selectedNetworkClientId: 'selectedNetworkClientId', + networkConfigurationsByChainId: { + '0x1': { + chainId: '0x1', + defaultRpcEndpointIndex: 0, + rpcEndpoints: [ + { + networkClientId: 'selectedNetworkClientId', + }, + ], + }, + }, + internalAccounts: { + selectedAccount: 'selectedAccount', + accounts: { + selectedAccount: {}, + }, + }, + }, + }); + + const mockTrackEvent = jest.fn(); + + const { findByTestId } = renderWithProvider( + + + , + store, + ); + + const importButton = await findByTestId('import-token-button'); + importButton.click(); + + const refreshListItem = await findByTestId('refreshList__button'); + refreshListItem.click(); + + expect(mockTrackEvent).toHaveBeenCalledTimes(1); + expect(mockTrackEvent).toHaveBeenCalledWith({ + category: MetaMetricsEventCategory.Tokens, + event: MetaMetricsEventName.TokenListRefreshed, + }); + }); +}); diff --git a/ui/components/app/assets/asset-list/asset-list-control-bar/asset-list-control-bar.tsx b/ui/components/app/assets/asset-list/asset-list-control-bar/asset-list-control-bar.tsx index c61815a9ab0a..d335f6f41820 100644 --- a/ui/components/app/assets/asset-list/asset-list-control-bar/asset-list-control-bar.tsx +++ b/ui/components/app/assets/asset-list/asset-list-control-bar/asset-list-control-bar.tsx @@ -160,6 +160,10 @@ const AssetListControlBar = ({ showTokensLinks }: AssetListControlBarProps) => { const handleRefresh = () => { dispatch(detectTokens()); closePopover(); + trackEvent({ + category: MetaMetricsEventCategory.Tokens, + event: MetaMetricsEventName.TokenListRefreshed, + }); }; return ( diff --git a/ui/components/app/assets/asset-list/asset-list.tsx b/ui/components/app/assets/asset-list/asset-list.tsx index 3cd06ec4686b..19d9ecdd4c0d 100644 --- a/ui/components/app/assets/asset-list/asset-list.tsx +++ b/ui/components/app/assets/asset-list/asset-list.tsx @@ -127,6 +127,7 @@ const AssetList = ({ onClickAsset, showTokensLinks }: AssetListProps) => { className="" actionButtonOnClick={() => setShowDetectedTokens(true)} margin={4} + marginBottom={1} /> ) : null} diff --git a/ui/components/app/home-notification/index.scss b/ui/components/app/home-notification/index.scss index ec997e00e3aa..647dc7314d0d 100644 --- a/ui/components/app/home-notification/index.scss +++ b/ui/components/app/home-notification/index.scss @@ -10,10 +10,9 @@ border-radius: 8px; min-height: 116px; padding: 16px; + width: calc(100% - 16px); + max-width: 472px; - @include design-system.screen-sm-min { - min-width: 472px; - } &__content-container { display: flex; diff --git a/ui/components/app/name/__snapshots__/name.test.tsx.snap b/ui/components/app/name/__snapshots__/name.test.tsx.snap index 286429760c1e..883717fca1f4 100644 --- a/ui/components/app/name/__snapshots__/name.test.tsx.snap +++ b/ui/components/app/name/__snapshots__/name.test.tsx.snap @@ -79,7 +79,7 @@ exports[`Name renders address with long saved name 1`] = `

- Very long an... + Very long...

diff --git a/ui/components/app/name/name-details/__snapshots__/name-details.test.tsx.snap b/ui/components/app/name/name-details/__snapshots__/name-details.test.tsx.snap index 3520a1b64a13..8ddce02439c9 100644 --- a/ui/components/app/name/name-details/__snapshots__/name-details.test.tsx.snap +++ b/ui/components/app/name/name-details/__snapshots__/name-details.test.tsx.snap @@ -753,7 +753,7 @@ exports[`NameDetails renders with recognized name 1`] = `

- iZUMi Bond U... + iZUMi Bon...

diff --git a/ui/components/app/name/name.tsx b/ui/components/app/name/name.tsx index 4871cc481f0c..1b5305c2b61f 100644 --- a/ui/components/app/name/name.tsx +++ b/ui/components/app/name/name.tsx @@ -106,7 +106,7 @@ const Name = memo( const MAX_PET_NAME_LENGTH = 12; const formattedName = shortenString(name || undefined, { truncatedCharLimit: MAX_PET_NAME_LENGTH, - truncatedStartChars: MAX_PET_NAME_LENGTH, + truncatedStartChars: MAX_PET_NAME_LENGTH - 3, truncatedEndChars: 0, skipCharacterInEnd: true, }); diff --git a/ui/components/app/permission-cell/permission-cell-options.js b/ui/components/app/permission-cell/permission-cell-options.js index 5cefdf7f7d8f..f94a2b7599d3 100644 --- a/ui/components/app/permission-cell/permission-cell-options.js +++ b/ui/components/app/permission-cell/permission-cell-options.js @@ -49,6 +49,10 @@ export const PermissionCellOptions = ({ dispatch(revokeDynamicSnapPermissions(snapId, [permissionName])); }; + if (!description && !isRevokable) { + return null; + } + return ( {showOptions && ( - - - {t('details')} - - + {description && ( + + + {t('details')} + + + )} {isRevokable && ( { + ///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask) if (id === 'bridge') { openBridgeExperience( 'Carousel', @@ -85,26 +93,57 @@ export const AccountOverviewLayout = ({ location.pathname.includes('asset') ? '&token=native' : '', ); } + ///: END:ONLY_INCLUDE_IF + + trackEvent({ + event: MetaMetricsEventName.BannerSelect, + category: MetaMetricsEventCategory.Banner, + properties: { + banner_name: id, + }, + }); }; - ///: END:ONLY_INCLUDE_IF - const handleRemoveSlide = (id: string) => { + const handleRemoveSlide = (isLastSlide: boolean, id: string) => { if (id === 'fund' && hasZeroBalance) { return; } + if (isLastSlide) { + trackEvent({ + event: MetaMetricsEventName.BannerCloseAll, + category: MetaMetricsEventCategory.Banner, + }); + } dispatch(removeSlide(id)); }; + const handleRenderSlides = useCallback( + (renderedSlides: CarouselSlide[]) => { + if (!hasRendered) { + renderedSlides.forEach((slide) => { + trackEvent({ + event: MetaMetricsEventName.BannerDisplay, + category: MetaMetricsEventCategory.Banner, + properties: { + banner_name: slide.id, + }, + }); + }); + setHasRendered(true); + } + }, + [hasRendered, trackEvent], + ); + return ( <>
{children}
diff --git a/ui/components/multichain/carousel/carousel.test.tsx b/ui/components/multichain/carousel/carousel.test.tsx index 71b9e4f4faa8..25fe8b562600 100644 --- a/ui/components/multichain/carousel/carousel.test.tsx +++ b/ui/components/multichain/carousel/carousel.test.tsx @@ -3,6 +3,40 @@ import { render, fireEvent } from '@testing-library/react'; import { Carousel } from './carousel'; import { MARGIN_VALUES, WIDTH_VALUES } from './constants'; +jest.mock('react-responsive-carousel', () => ({ + Carousel: ({ + children, + onChange, + }: { + children: React.ReactNode; + onChange?: (index: number) => void; + }) => ( +
+ {children} +
+
+
+ ), +})); + +jest.mock('react-redux', () => ({ + useSelector: jest.fn(), + useDispatch: () => jest.fn(), +})); + +jest.mock('reselect', () => ({ + createSelector: jest.fn(), + createDeepEqualSelector: jest.fn(), + createSelectorCreator: jest.fn(() => jest.fn()), + lruMemoize: jest.fn(), +})); + +jest.mock('../../../selectors/approvals', () => ({ + selectPendingApproval: jest.fn(), +})); + jest.mock('../../../hooks/useI18nContext', () => ({ useI18nContext: () => (key: string) => key, })); @@ -46,13 +80,24 @@ describe('Carousel', () => { expect(closeButtons).toHaveLength(2); fireEvent.click(closeButtons[0]); - expect(mockOnClose).toHaveBeenCalledWith('1'); + expect(mockOnClose).toHaveBeenCalledWith(false, '1'); const remainingSlides = mockSlides.filter((slide) => slide.id !== '1'); rerender(); - const updatedSlides = container.querySelectorAll('.mm-carousel-slide'); - expect(updatedSlides).toHaveLength(1); + const updatedCloseButtons = container.querySelectorAll( + '.mm-carousel-slide__close-button', + ); + expect(updatedCloseButtons).toHaveLength(1); + + fireEvent.click(updatedCloseButtons[0]); + expect(mockOnClose).toHaveBeenCalledWith(true, '2'); + + const finalSlides = remainingSlides.filter((slide) => slide.id !== '2'); + rerender(); + + const finalSlideElements = container.querySelectorAll('.mm-carousel-slide'); + expect(finalSlideElements).toHaveLength(0); }); it('should handle slide navigation', () => { @@ -65,7 +110,7 @@ describe('Carousel', () => { fireEvent.click(dots[1]); const slides = container.querySelectorAll('.mm-carousel-slide'); - expect(slides[1].parentElement).toHaveClass('selected'); + expect(slides[1].parentElement).toHaveClass('mock-carousel'); }); it('should return null when no slides are present', () => { diff --git a/ui/components/multichain/carousel/carousel.tsx b/ui/components/multichain/carousel/carousel.tsx index 3fbbe955a8eb..83423948c530 100644 --- a/ui/components/multichain/carousel/carousel.tsx +++ b/ui/components/multichain/carousel/carousel.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useState, useEffect } from 'react'; import { Carousel as ResponsiveCarousel } from 'react-responsive-carousel'; import { useI18nContext } from '../../../hooks/useI18nContext'; import { Box, BoxProps, BannerBase } from '../../component-library'; @@ -24,6 +24,7 @@ export const Carousel = React.forwardRef( isLoading = false, onClose, onClick, + onRenderSlides, ...props }: CarouselProps, ref: React.Ref, @@ -44,6 +45,17 @@ export const Carousel = React.forwardRef( }) .slice(0, MAX_SLIDES); + useEffect(() => { + if ( + visibleSlides && + visibleSlides.length > 0 && + onRenderSlides && + !isLoading + ) { + onRenderSlides(visibleSlides); + } + }, [visibleSlides, onRenderSlides, isLoading]); + const handleClose = (e: React.MouseEvent, slideId: string) => { e.preventDefault(); e.stopPropagation(); @@ -65,7 +77,7 @@ export const Carousel = React.forwardRef( setSelectedIndex(newSelectedIndex); if (onClose) { - onClose(slideId); + onClose(visibleSlides.length === 1, slideId); } }; diff --git a/ui/components/multichain/carousel/carousel.types.ts b/ui/components/multichain/carousel/carousel.types.ts index a8aef8df4839..3a6289e76a4b 100644 --- a/ui/components/multichain/carousel/carousel.types.ts +++ b/ui/components/multichain/carousel/carousel.types.ts @@ -3,6 +3,7 @@ import { CarouselSlide } from '../../../../shared/constants/app-state'; export type CarouselProps = { slides: CarouselSlide[]; isLoading?: boolean; - onClose?: (id: string) => void; + onClose?: (isLastSlide: boolean, id: string) => void; onClick?: (id: string) => void; + onRenderSlides?: (slides: CarouselSlide[]) => void; }; diff --git a/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx index d71bc499717e..cb3c929b5d27 100644 --- a/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx +++ b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx @@ -176,6 +176,11 @@ export const ReviewPermissions = () => { setShowAccountToast(true); }; + const hideAllToasts = () => { + setShowAccountToast(false); + setShowNetworkToast(false); + }; + return ( { onSelectChainIds={handleSelectChainIds} selectedAccountAddresses={connectedAccountAddresses} selectedChainIds={connectedChainIds} + hideAllToasts={hideAllToasts} /> ) : ( diff --git a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.test.tsx b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.test.tsx new file mode 100644 index 000000000000..affd55cfaa66 --- /dev/null +++ b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.test.tsx @@ -0,0 +1,81 @@ +import React from 'react'; +import { Provider } from 'react-redux'; +import { render, fireEvent } from '@testing-library/react'; +import configureStore from '../../../../../store/store'; +import { SiteCell } from './site-cell'; + +describe('SiteCell', () => { + const store = configureStore({ + metamask: { + useBlockie: false, + }, + }); + + describe('toast handling', () => { + it('should call hideAllToasts when edit accounts is clicked', () => { + const hideAllToasts = jest.fn(); + const { getAllByTestId } = render( + + undefined} + onSelectChainIds={() => undefined} + selectedAccountAddresses={[]} + selectedChainIds={[]} + hideAllToasts={hideAllToasts} + /> + , + ); + + const editButtons = getAllByTestId('edit'); + fireEvent.click(editButtons[0]); + expect(hideAllToasts).toHaveBeenCalled(); + }); + + it('should call hideAllToasts when edit networks is clicked', () => { + const hideAllToasts = jest.fn(); + const { getAllByTestId } = render( + + undefined} + onSelectChainIds={() => undefined} + selectedAccountAddresses={[]} + selectedChainIds={[]} + hideAllToasts={hideAllToasts} + /> + , + ); + + const editButtons = getAllByTestId('edit'); + fireEvent.click(editButtons[1]); + expect(hideAllToasts).toHaveBeenCalled(); + }); + + it('should not throw if hideAllToasts is not provided', () => { + const { getAllByTestId } = render( + + undefined} + onSelectChainIds={() => undefined} + selectedAccountAddresses={[]} + selectedChainIds={[]} + /> + , + ); + + expect(() => { + const editButtons = getAllByTestId('edit'); + fireEvent.click(editButtons[0]); + fireEvent.click(editButtons[1]); + }).not.toThrow(); + }); + }); +}); diff --git a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx index fcb104937e28..1c91489d0d1f 100644 --- a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx +++ b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx @@ -38,6 +38,7 @@ type SiteCellProps = { selectedAccountAddresses: string[]; selectedChainIds: string[]; isConnectFlow?: boolean; + hideAllToasts?: () => void; }; export const SiteCell: React.FC = ({ @@ -49,6 +50,7 @@ export const SiteCell: React.FC = ({ selectedAccountAddresses, selectedChainIds, isConnectFlow, + hideAllToasts = () => undefined, }) => { const t = useI18nContext(); const trackEvent = useContext(MetaMetricsContext); @@ -91,6 +93,30 @@ export const SiteCell: React.FC = ({ ? t('requestingForNetwork', [selectedNetworks[0].name]) : t('requestingFor'); + const handleOpenAccountsModal = () => { + hideAllToasts?.(); + setShowEditAccountsModal(true); + trackEvent({ + category: MetaMetricsEventCategory.Navigation, + event: MetaMetricsEventName.ViewPermissionedAccounts, + properties: { + location: 'Connect view, Permissions toast, Permissions (dapp)', + }, + }); + }; + + const handleOpenNetworksModal = () => { + hideAllToasts?.(); + setShowEditNetworksModal(true); + trackEvent({ + category: MetaMetricsEventCategory.Navigation, + event: MetaMetricsEventName.ViewPermissionedNetworks, + properties: { + location: 'Connect view, Permissions toast, Permissions (dapp)', + }, + }); + }; + return ( <> = ({ connectedMessage={accountMessageConnectedState} unconnectedMessage={accountMessageNotConnectedState} isConnectFlow={isConnectFlow} - onClick={() => { - setShowEditAccountsModal(true); - trackEvent({ - category: MetaMetricsEventCategory.Navigation, - event: MetaMetricsEventName.ViewPermissionedAccounts, - properties: { - location: 'Connect view, Permissions toast, Permissions (dapp)', - }, - }); - }} + onClick={handleOpenAccountsModal} paddingBottomValue={2} paddingTopValue={0} content={ @@ -136,16 +153,7 @@ export const SiteCell: React.FC = ({ connectedMessage={networkMessageConnectedState} unconnectedMessage={networkMessageNotConnectedState} isConnectFlow={isConnectFlow} - onClick={() => { - setShowEditNetworksModal(true); - trackEvent({ - category: MetaMetricsEventCategory.Navigation, - event: MetaMetricsEventName.ViewPermissionedNetworks, - properties: { - location: 'Connect view, Permissions toast, Permissions (dapp)', - }, - }); - }} + onClick={handleOpenNetworksModal} paddingTopValue={2} paddingBottomValue={0} content={} diff --git a/ui/components/ui/menu/menu-item.js b/ui/components/ui/menu/menu-item.js index 6b501d6d7709..275515a8f3fd 100644 --- a/ui/components/ui/menu/menu-item.js +++ b/ui/components/ui/menu/menu-item.js @@ -66,7 +66,7 @@ const MenuItem = React.forwardRef( /> )}
-
{children}
+ {children} {subtitle ? {subtitle} : null}
diff --git a/ui/components/ui/menu/menu.scss b/ui/components/ui/menu/menu.scss index d43545e67fd0..2eed45745d9e 100644 --- a/ui/components/ui/menu/menu.scss +++ b/ui/components/ui/menu/menu.scss @@ -33,7 +33,7 @@ text-align: start; align-items: center; width: 100%; - padding: 14px 16px; + padding: 16px; cursor: pointer; color: inherit; diff --git a/ui/components/ui/new-network-info/index.js b/ui/components/ui/new-network-info/index.js deleted file mode 100644 index e4bd2dcdbd3e..000000000000 --- a/ui/components/ui/new-network-info/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './new-network-info'; diff --git a/ui/components/ui/new-network-info/index.scss b/ui/components/ui/new-network-info/index.scss deleted file mode 100644 index 989a927bb251..000000000000 --- a/ui/components/ui/new-network-info/index.scss +++ /dev/null @@ -1,59 +0,0 @@ -.new-network-info { - &__wrapper { - box-shadow: 0 4px 20px rgba(0, 0, 0, 0.214); - border-radius: 8px; - - .popover-footer { - border-top: none; - width: 100%; - gap: 16px; - - .footer__button { - flex: 1; - flex-grow: 1; - white-space: nowrap; - overflow: hidden; - } - - a:hover > span { - color: var(--inherit); - } - } - - .popover-header { - padding-bottom: 1px; - } - - .popover-header__button { - color: var(--color-icon-default); - } - } - - &__token-box { - align-self: center; - margin-top: 8px; - max-width: 245px; - } - - &__button { - display: contents; - padding: 0; - } - - &__bullet-icon-container { - background-color: var(--color-info-muted); - border-radius: 50%; - padding: 6px; - - .mm-icon { - display: flex; - color: var(--color-info-default); - } - } -} - -.chip--with-left-icon { - padding-left: 8px; - padding-top: 8px; - padding-bottom: 8px; -} diff --git a/ui/components/ui/new-network-info/new-network-info.js b/ui/components/ui/new-network-info/new-network-info.js deleted file mode 100644 index 9d4df0ec63ec..000000000000 --- a/ui/components/ui/new-network-info/new-network-info.js +++ /dev/null @@ -1,267 +0,0 @@ -import React, { useCallback, useContext, useEffect, useState } from 'react'; -import { useSelector } from 'react-redux'; -import { TOKEN_API_METASWAP_CODEFI_URL } from '../../../../shared/constants/tokens'; -import fetchWithCache from '../../../../shared/lib/fetch-with-cache'; -import { I18nContext } from '../../../contexts/i18n'; -import { getProviderConfig } from '../../../../shared/modules/selectors/networks'; -import { - AlignItems, - BackgroundColor, - BorderColor, - Color, - Display, - FlexDirection, - TextAlign, - TextColor, - TextVariant, -} from '../../../helpers/constants/design-system'; -import { - getCurrentNetwork, - getIsBridgeChain, - getMetaMetricsId, - getUseTokenDetection, - getUseExternalServices, - getParticipateInMetaMetrics, - getDataCollectionForMarketing, -} from '../../../selectors'; -import { - PickerNetwork, - Text, - Box, - Button, - Icon, - IconName, - ButtonPrimarySize, - IconSize, - AvatarNetworkSize, -} from '../../component-library'; -import Popover from '../popover'; -import { getPortfolioUrl } from '../../../helpers/utils/portfolio'; -import ZENDESK_URLS from '../../../helpers/constants/zendesk-url'; - -export default function NewNetworkInfo() { - const t = useContext(I18nContext); - const [tokenDetectionSupported, setTokenDetectionSupported] = useState(false); - const [showPopup, setShowPopup] = useState(true); - const [isLoading, setIsLoading] = useState(true); - const autoDetectToken = useSelector(getUseTokenDetection); - const areExternalServicesEnabled = useSelector(getUseExternalServices); - const providerConfig = useSelector(getProviderConfig); - const currentNetwork = useSelector(getCurrentNetwork); - const metaMetricsId = useSelector(getMetaMetricsId); - const isBridgeChain = useSelector(getIsBridgeChain); - const isMetaMetricsEnabled = useSelector(getParticipateInMetaMetrics); - const isMarketingEnabled = useSelector(getDataCollectionForMarketing); - - const onCloseClick = () => { - setShowPopup(false); - }; - - const checkTokenDetection = useCallback(async () => { - setIsLoading(true); - const fetchedTokenData = await fetchWithCache({ - url: `${TOKEN_API_METASWAP_CODEFI_URL}${providerConfig.chainId}?occurrenceFloor=100&includeNativeAssets=false`, - functionName: 'getIsTokenDetectionSupported', - }); - const isTokenDetectionSupported = !fetchedTokenData?.error; - setTokenDetectionSupported(isTokenDetectionSupported); - setIsLoading(false); - }, [providerConfig.chainId]); - - useEffect(() => { - if (!areExternalServicesEnabled) { - return; - } - checkTokenDetection(); - // we want to only fetch once - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - return ( - !isLoading && - showPopup && ( - - - - - } - > - - - - {t('thingsToKeep')} - - - {providerConfig.ticker && ( - - - - - - - {t('gasIsETH', [providerConfig.ticker])} - - - {t('nativeToken', [providerConfig.ticker])} - - - - )} - - - - - - - {t('bridgeDontSend')} - - - {isBridgeChain - ? t('attemptSendingAssetsWithPortfolio', [ - - - {t('metamaskPortfolio')} - - , - ]) - : t('attemptSendingAssets')} - - - - - {!autoDetectToken || !tokenDetectionSupported ? ( - - - - - - - {t('addingTokens')} - - - {t('tokenShowUp')} - {t('clickToManuallyAdd')} - - - - ) : null} - - - - ) - ); -} diff --git a/ui/components/ui/new-network-info/new-network-info.stories.js b/ui/components/ui/new-network-info/new-network-info.stories.js deleted file mode 100644 index 9d70ef07b5f0..000000000000 --- a/ui/components/ui/new-network-info/new-network-info.stories.js +++ /dev/null @@ -1,10 +0,0 @@ -import React from 'react'; -import NewNetworkInfo from '.'; - -export default { - title: 'Components/UI/NewNetworkInfo', -}; - -export const DefaultStory = () => ; - -DefaultStory.storyName = 'Default'; diff --git a/ui/components/ui/new-network-info/new-network-info.test.js b/ui/components/ui/new-network-info/new-network-info.test.js deleted file mode 100644 index 59d98ab358fd..000000000000 --- a/ui/components/ui/new-network-info/new-network-info.test.js +++ /dev/null @@ -1,223 +0,0 @@ -import React from 'react'; -import { waitFor } from '@testing-library/react'; -import configureMockStore from 'redux-mock-store'; -import nock from 'nock'; -import { renderWithProvider } from '../../../../test/lib/render-helpers'; -import { mockNetworkState } from '../../../../test/stub/networks'; -import { CHAIN_IDS } from '../../../../shared/constants/network'; -import NewNetworkInfo from './new-network-info'; - -const fetchWithCache = - require('../../../../shared/lib/fetch-with-cache').default; - -const localStorageMock = (function () { - let store = {}; - return { - getItem(key) { - return store[key]; - }, - - setItem(key, value) { - store[key] = value.toString(); - }, - - clear() { - store = {}; - }, - - removeItem(key) { - delete store[key]; - }, - }; -})(); -Object.defineProperty(window, 'localStorage', { value: localStorageMock }); - -const responseOfTokenList = []; -describe('NewNetworkInfo', () => { - afterEach(() => { - nock.cleanAll(); - }); - - describe('fetch token successfully', () => { - const state = { - metamask: { - ...mockNetworkState({ chainId: CHAIN_IDS.MAINNET }), - useExternalServices: true, - useTokenDetection: false, - currencyRates: {}, - }, - }; - - it('should match snapshot and render component', async () => { - nock('https://token.api.cx.metamask.io') - .get('/tokens/0x1?occurrenceFloor=100&includeNativeAssets=false') - .reply(200, responseOfTokenList); - - const store = configureMockStore()(state); - const { getByText, getByTestId } = renderWithProvider( - , - store, - ); - // wait for the fetch to finish - await waitFor(() => { - expect(getByTestId('new-network-info__wrapper')).toBeInTheDocument(); - }); - // render title - expect(getByText("You're now using")).toBeInTheDocument(); - // render the network name - expect(getByText('Ethereum Mainnet')).toBeInTheDocument(); - expect( - getByTestId('new-network-info__bullet-paragraph').textContent, - ).toMatchInlineSnapshot( - `"Gas is ETH The native token on this network is ETH. It is the token used for gas fees. "`, - ); - }); - - it('should render a question mark icon image for non-main network', async () => { - nock('https://token.api.cx.metamask.io') - .get('/tokens/0x1?occurrenceFloor=100&includeNativeAssets=false') - .reply(200, responseOfTokenList); - - const updateTokenDetectionSupportStatus = await fetchWithCache({ - url: 'https://token.api.cx.metamask.io/tokens/0x1?occurrenceFloor=100&includeNativeAssets=false', - functionName: 'getTokenDetectionSupportStatus', - }); - - state.metamask.nativeCurrency = ''; - - const store = configureMockStore()( - state, - updateTokenDetectionSupportStatus, - ); - const { container, getByTestId } = renderWithProvider( - , - store, - ); - // wait for the fetch to finish - await waitFor(() => { - expect(getByTestId('new-network-info__wrapper')).toBeInTheDocument(); - }); - - const questionMark = container.querySelector('.question'); - - expect(questionMark).toBeDefined(); - }); - - it('should not render first bullet when provider ticker is null', async () => { - nock('https://token.api.cx.metamask.io') - .get('/tokens/0x3?occurrenceFloor=100&includeNativeAssets=false') - .reply(200, '{"error":"ChainId 0x3 is not supported"}'); - - const store = configureMockStore()({ - metamask: { - ...state.metamask, - ...mockNetworkState({ chainId: '0x3', ticker: undefined }), - }, - }); - const { container, getByTestId } = renderWithProvider( - , - store, - ); - // wait for the fetch to finish - await new Promise((r) => setTimeout(r, 2000)); - await waitFor(() => { - expect(getByTestId('new-network-info__wrapper')).toBeInTheDocument(); - }); - const firstBox = container.querySelector( - 'new-network-info__content-box-1', - ); - - expect(firstBox).toBeNull(); - }); - - describe('add token link', () => { - const newState = { - metamask: { - ...mockNetworkState({ chainId: CHAIN_IDS.MAINNET }), - - useExternalServices: true, - useTokenDetection: true, - currencyRates: {}, - }, - }; - - it('should not render link when auto token detection is set true and token detection is supported', async () => { - nock('https://token.api.cx.metamask.io') - .get('/tokens/0x1?occurrenceFloor=100&includeNativeAssets=false') - .reply(200, responseOfTokenList); - - const store = configureMockStore()(newState); - const { getByTestId, queryByTestId } = renderWithProvider( - , - store, - ); - // should not render add token link - await waitFor(() => { - expect(getByTestId('new-network-info__wrapper')).toBeInTheDocument(); - }); - expect( - queryByTestId('new-network-info__add-token-manually'), - ).toBeNull(); - }); - - it('should render link when auto token detection is set true and token detection is not supported', async () => { - nock('https://token.api.cx.metamask.io') - .get('/tokens/0x1?occurrenceFloor=100&includeNativeAssets=false') - .replyWithError('something awful happened'); - - const store = configureMockStore()(newState); - const { getByTestId } = renderWithProvider(, store); - // render add token link when token is supported - await waitFor(() => { - expect(getByTestId('new-network-info__wrapper')).toBeInTheDocument(); - }); - }); - - it('should render link when auto token detection is set false but token detection is not supported', async () => { - nock('https://token.api.cx.metamask.io') - .get('/tokens/0x1?occurrenceFloor=100&includeNativeAssets=false') - .reply(403); - - const store = configureMockStore()(state); - const { getByTestId } = renderWithProvider(, store); - // render add token link when token is supported - await waitFor(() => { - expect(getByTestId('new-network-info__wrapper')).toBeInTheDocument(); - }); - expect( - getByTestId('new-network-info__add-token-manually'), - ).toBeInTheDocument(); - }); - - it('should render link when auto token detection is set false and token detection is supported', async () => { - nock('https://token.api.cx.metamask.io') - .get('/tokens/0x1?occurrenceFloor=100&includeNativeAssets=false') - .reply(200, responseOfTokenList); - - const updateTokenDetectionSupportStatus = await fetchWithCache({ - url: 'https://token.api.cx.metamask.io/tokens/0x1?occurrenceFloor=100&includeNativeAssets=false', - functionName: 'getTokenDetectionSupportStatus', - }); - - const store = configureMockStore()( - state, - updateTokenDetectionSupportStatus, - ); - const { getByText, getByTestId } = renderWithProvider( - , - store, - ); - // wait for the fetch to finish - await waitFor(() => { - expect(getByTestId('new-network-info__wrapper')).toBeInTheDocument(); - }); - // render add token link when token is supported - expect( - getByText( - 'Your tokens may not automatically show up in your wallet. You can always add tokens manually.', - ), - ).toBeInTheDocument(); - }); - }); - }); -}); diff --git a/ui/components/ui/ui-components.scss b/ui/components/ui/ui-components.scss index a851a6fd72d5..a1a901ac2a4d 100644 --- a/ui/components/ui/ui-components.scss +++ b/ui/components/ui/ui-components.scss @@ -29,7 +29,6 @@ @import 'logo/logo-coinbasepay.scss'; @import 'loading-screen/index'; @import 'menu/menu'; -@import 'new-network-info/index'; @import 'numeric-input/numeric-input'; @import 'nickname-popover/index'; @import 'form-field/index'; diff --git a/ui/ducks/metamask/metamask.js b/ui/ducks/metamask/metamask.js index f3453b77e7d9..c9c57057f2f9 100644 --- a/ui/ducks/metamask/metamask.js +++ b/ui/ducks/metamask/metamask.js @@ -373,7 +373,7 @@ export function isEIP1559Network(state, networkClientId) { return ( state.metamask.networksMetadata?.[ networkClientId ?? selectedNetworkClientId - ].EIPS[1559] === true + ]?.EIPS[1559] === true ); } diff --git a/ui/hooks/metamask-notifications/useSwitchNotifications.test.tsx b/ui/hooks/metamask-notifications/useSwitchNotifications.test.tsx index b67c2ea80cef..b228698f391e 100644 --- a/ui/hooks/metamask-notifications/useSwitchNotifications.test.tsx +++ b/ui/hooks/metamask-notifications/useSwitchNotifications.test.tsx @@ -1,125 +1,183 @@ -import React from 'react'; -import { Provider } from 'react-redux'; -import { renderHook, act } from '@testing-library/react-hooks'; -import configureStore from 'redux-mock-store'; -import thunk from 'redux-thunk'; -import type { Store } from 'redux'; -import * as actions from '../../store/actions'; -import { MetamaskNotificationsProvider } from '../../contexts/metamask-notifications/metamask-notifications'; +import { waitFor } from '@testing-library/react'; +import * as ActionsModule from '../../store/actions'; +import * as NotificationSelectorsModule from '../../selectors/metamask-notifications/metamask-notifications'; +import { renderHookWithProviderTyped } from '../../../test/lib/render-helpers'; import { useSwitchFeatureAnnouncementsChange, - useSwitchAccountNotifications, useSwitchAccountNotificationsChange, + useAccountSettingsProps, } from './useSwitchNotifications'; -const middlewares = [thunk]; -const mockStore = configureStore(middlewares); - -jest.mock('../../store/actions', () => ({ - setFeatureAnnouncementsEnabled: jest.fn(), - checkAccountsPresence: jest.fn(), - updateOnChainTriggersByAccount: jest.fn(), - deleteOnChainTriggersByAccount: jest.fn(), - showLoadingIndication: jest.fn(), - hideLoadingIndication: jest.fn(), - fetchAndUpdateMetamaskNotifications: jest.fn(), -})); - -describe('useSwitchNotifications', () => { - let store: Store; - +describe('useSwitchFeatureAnnouncementsChange() tests', () => { beforeEach(() => { - store = mockStore({ - metamask: { - isFeatureAnnouncementsEnabled: false, - internalAccounts: { - accounts: { - '0x123': { - address: '0x123', - id: 'account1', - metadata: {}, - options: {}, - methods: [], - type: 'eip155:eoa', - }, - }, - }, - }, - }); - - store.dispatch = jest.fn().mockImplementation((action) => { - if (typeof action === 'function') { - return action(store.dispatch, store.getState); - } - return Promise.resolve(); - }); + jest.restoreAllMocks(); + }); - jest.clearAllMocks(); + const arrangeMocks = () => { + const mockSetFeatureAnnouncementsEnabled = jest.spyOn( + ActionsModule, + 'setFeatureAnnouncementsEnabled', + ); + return { + mockSetFeatureAnnouncementsEnabled, + }; + }; + + it('should update feature announcement when callback invoked', async () => { + const mocks = arrangeMocks(); + const hook = renderHookWithProviderTyped( + () => useSwitchFeatureAnnouncementsChange(), + {}, + ); + + await hook.result.current.onChange(true); + expect(mocks.mockSetFeatureAnnouncementsEnabled).toHaveBeenCalled(); }); - it('should toggle feature announcements', async () => { - const { result } = renderHook(() => useSwitchFeatureAnnouncementsChange(), { - wrapper: ({ children }) => ( - - - {children} - - - ), + it('should update error state when callback fails', async () => { + const mocks = arrangeMocks(); + mocks.mockSetFeatureAnnouncementsEnabled.mockImplementation(() => { + throw new Error('Mock Fail'); }); + const hook = renderHookWithProviderTyped( + () => useSwitchFeatureAnnouncementsChange(), + {}, + ); - await act(async () => { - await result.current.onChange(true); - }); + await hook.result.current.onChange(true); + expect(hook.result.current.error).toBeDefined(); + }); +}); - expect(actions.setFeatureAnnouncementsEnabled).toHaveBeenCalledWith(true); +describe('useSwitchAccountNotificationsChange() tests', () => { + const arrangeMocks = () => { + const mockUpdateOnChainTriggersByAccount = jest.spyOn( + ActionsModule, + 'updateOnChainTriggersByAccount', + ); + const mockDeleteOnChainTriggersByAccount = jest.spyOn( + ActionsModule, + 'deleteOnChainTriggersByAccount', + ); + + return { + mockUpdateOnChainTriggersByAccount, + mockDeleteOnChainTriggersByAccount, + }; + }; + + it('should invoke update notification triggers when an address is enabled', async () => { + const mocks = arrangeMocks(); + const hook = renderHookWithProviderTyped( + () => useSwitchAccountNotificationsChange(), + {}, + ); + + await hook.result.current.onChange(['0x1'], true); + expect(mocks.mockUpdateOnChainTriggersByAccount).toHaveBeenCalledWith([ + '0x1', + ]); + expect(mocks.mockDeleteOnChainTriggersByAccount).not.toHaveBeenCalled(); }); - it('should check account presence', async () => { - const { result } = renderHook(() => useSwitchAccountNotifications(), { - wrapper: ({ children }) => ( - - - {children} - - - ), - }); + it('should invoke delete notification triggers when an address is disabled', async () => { + const mocks = arrangeMocks(); + const hook = renderHookWithProviderTyped( + () => useSwitchAccountNotificationsChange(), + {}, + ); + + await hook.result.current.onChange(['0x1'], false); + expect(mocks.mockUpdateOnChainTriggersByAccount).not.toHaveBeenCalled(); + expect(mocks.mockDeleteOnChainTriggersByAccount).toHaveBeenCalledWith([ + '0x1', + ]); + }); - await act(async () => { - await result.current.switchAccountNotifications(['0x123']); + it('should return an error value if it fails to update or delete triggers', async () => { + const mocks = arrangeMocks(); + mocks.mockUpdateOnChainTriggersByAccount.mockImplementation(() => { + throw new Error('Mock Error'); + }); + mocks.mockDeleteOnChainTriggersByAccount.mockImplementation(() => { + throw new Error('Mock Error'); }); - expect(actions.checkAccountsPresence).toHaveBeenCalledWith(['0x123']); - }); + const act = async (testEnableOrDisable: boolean) => { + const hook = renderHookWithProviderTyped( + () => useSwitchAccountNotificationsChange(), + {}, + ); + await hook.result.current.onChange(['0x1'], testEnableOrDisable); + return hook.result.current.error; + }; - it('should handle account notification changes', async () => { - const { result } = renderHook(() => useSwitchAccountNotificationsChange(), { - wrapper: ({ children }) => ( - - - {children} - - - ), - }); + const enableError = await act(true); + expect(enableError).toBeDefined(); + + const disableError = await act(false); + expect(disableError).toBeDefined(); + }); +}); - // Test enabling notifications - await act(async () => { - await result.current.onChange(['0x123'], true); +describe('useAccountSettingsProps() tests', () => { + const arrangeMocks = () => { + const mockCheckAccountsPresence = jest.spyOn( + ActionsModule, + 'checkAccountsPresence', + ); + const mockGetIsUpdatingMetamaskNotificationsAccount = jest + .spyOn( + NotificationSelectorsModule, + 'getIsUpdatingMetamaskNotificationsAccount', + ) + .mockReturnValue([]); + const mockSelectIsMetamaskNotificationsEnabled = jest + .spyOn( + NotificationSelectorsModule, + 'selectIsMetamaskNotificationsEnabled', + ) + .mockReturnValue(true); + + return { + mockCheckAccountsPresence, + mockGetIsUpdatingMetamaskNotificationsAccount, + mockSelectIsMetamaskNotificationsEnabled, + }; + }; + + it('Should invoke effect when notifications are enabled', async () => { + const mocks = arrangeMocks(); + renderHookWithProviderTyped(() => useAccountSettingsProps(['0x1']), {}); + + await waitFor(() => { + expect(mocks.mockCheckAccountsPresence).toHaveBeenCalled(); }); + }); - expect(actions.updateOnChainTriggersByAccount).toHaveBeenCalledWith([ - '0x123', - ]); + it('Should not invoke effect when notifications are disabled', async () => { + const mocks = arrangeMocks(); + mocks.mockSelectIsMetamaskNotificationsEnabled.mockReturnValue(false); + renderHookWithProviderTyped(() => useAccountSettingsProps(['0x1']), {}); - // Test disabling notifications - await act(async () => { - await result.current.onChange(['0x123'], false); + await waitFor(() => { + expect(mocks.mockCheckAccountsPresence).not.toHaveBeenCalled(); }); + }); - expect(actions.deleteOnChainTriggersByAccount).toHaveBeenCalledWith([ - '0x123', - ]); + it('Should be able to invoke refetch accounts function', async () => { + const mocks = arrangeMocks(); + const hook = renderHookWithProviderTyped( + () => useAccountSettingsProps(['0x1']), + {}, + ); + + await hook.result.current.update(['0x1', '0x2']); + await waitFor(() => { + expect(mocks.mockCheckAccountsPresence).toHaveBeenCalledWith([ + '0x1', + '0x2', + ]); + }); }); }); diff --git a/ui/hooks/metamask-notifications/useSwitchNotifications.ts b/ui/hooks/metamask-notifications/useSwitchNotifications.ts index 53e121473bac..08a03c33e4f1 100644 --- a/ui/hooks/metamask-notifications/useSwitchNotifications.ts +++ b/ui/hooks/metamask-notifications/useSwitchNotifications.ts @@ -8,7 +8,10 @@ import { updateOnChainTriggersByAccount, hideLoadingIndication, } from '../../store/actions'; -import { getIsUpdatingMetamaskNotificationsAccount } from '../../selectors/metamask-notifications/metamask-notifications'; +import { + getIsUpdatingMetamaskNotificationsAccount, + selectIsMetamaskNotificationsEnabled, +} from '../../selectors/metamask-notifications/metamask-notifications'; export function useSwitchFeatureAnnouncementsChange(): { onChange: (state: boolean) => Promise; @@ -28,7 +31,6 @@ export function useSwitchFeatureAnnouncementsChange(): { const errorMessage = e instanceof Error ? e.message : JSON.stringify(e ?? ''); setError(errorMessage); - throw e; } }, [dispatch], @@ -42,44 +44,6 @@ export function useSwitchFeatureAnnouncementsChange(): { export type UseSwitchAccountNotificationsData = { [address: string]: boolean }; -export function useSwitchAccountNotifications(): { - switchAccountNotifications: ( - accounts: string[], - ) => Promise; - isLoading: boolean; - error: string | null; -} { - const dispatch = useDispatch(); - - const [isLoading, setIsLoading] = useState(false); - const [error, setError] = useState(null); - - const switchAccountNotifications = useCallback( - async ( - accounts: string[], - ): Promise => { - setIsLoading(true); - setError(null); - - try { - const data = await dispatch(checkAccountsPresence(accounts)); - return data as unknown as UseSwitchAccountNotificationsData; - } catch (e) { - const errorMessage = - e instanceof Error ? e.message : JSON.stringify(e ?? ''); - setError(errorMessage); - log.error(errorMessage); - throw e; - } finally { - setIsLoading(false); - } - }, - [dispatch], - ); - - return { switchAccountNotifications, isLoading, error }; -} - export function useSwitchAccountNotificationsChange(): { onChange: (addresses: string[], state: boolean) => Promise; error: string | null; @@ -103,7 +67,6 @@ export function useSwitchAccountNotificationsChange(): { e instanceof Error ? e.message : JSON.stringify(e ?? ''); log.error(errorMessage); setError(errorMessage); - throw e; } dispatch(hideLoadingIndication()); }, @@ -146,6 +109,7 @@ export function useAccountSettingsProps(accounts: string[]) { const accountsBeingUpdated = useSelector( getIsUpdatingMetamaskNotificationsAccount, ); + const isEnabled = useSelector(selectIsMetamaskNotificationsEnabled); const fetchAccountSettings = useRefetchAccountSettings(); const [data, setData] = useState({}); const [loading, setLoading] = useState(false); @@ -169,15 +133,12 @@ export function useAccountSettingsProps(accounts: string[]) { // Effect - async get if accounts are enabled/disabled useEffect(() => { - try { - const memoAccounts: string[] = JSON.parse(jsonAccounts); - update(memoAccounts); - } catch { - setError('Failed to get account settings'); - } finally { - setLoading(false); + if (!isEnabled) { + return; } - }, [jsonAccounts, fetchAccountSettings]); + const memoAccounts: string[] = JSON.parse(jsonAccounts); + update(memoAccounts); + }, [jsonAccounts, fetchAccountSettings, isEnabled]); return { data, diff --git a/ui/pages/asset/components/__snapshots__/asset-page.test.tsx.snap b/ui/pages/asset/components/__snapshots__/asset-page.test.tsx.snap index dc3cf4c408de..e4bc078a8c2b 100644 --- a/ui/pages/asset/components/__snapshots__/asset-page.test.tsx.snap +++ b/ui/pages/asset/components/__snapshots__/asset-page.test.tsx.snap @@ -223,9 +223,7 @@ exports[`AssetPage should render a native asset 1`] = `

- $0.00 -

+ />
+
+

+ Network: +

+

+

+ static-logo +
+ Ethereum Mainnet +

+
@@ -583,6 +605,30 @@ exports[`AssetPage should render an ERC20 asset without prices 1`] = `
+
+

+ Network: +

+

+

+ static-logo +
+ Ethereum Mainnet +

+
@@ -1088,6 +1134,30 @@ exports[`AssetPage should render an ERC20 token with prices 1`] = `
+
+

+ Network: +

+

+

+ static-logo +
+ Ethereum Mainnet +

+
diff --git a/ui/pages/asset/components/asset-page.test.tsx b/ui/pages/asset/components/asset-page.test.tsx index 71ca5483b50c..3918003f64b5 100644 --- a/ui/pages/asset/components/asset-page.test.tsx +++ b/ui/pages/asset/components/asset-page.test.tsx @@ -4,7 +4,10 @@ import thunk from 'redux-thunk'; import { fireEvent, waitFor } from '@testing-library/react'; import { EthAccountType } from '@metamask/keyring-api'; import nock from 'nock'; -import { CHAIN_IDS } from '../../../../shared/constants/network'; +import { + CHAIN_IDS, + MAINNET_DISPLAY_NAME, +} from '../../../../shared/constants/network'; import { renderWithProvider } from '../../../../test/jest/rendering'; import { KeyringType } from '../../../../shared/constants/keyring'; import { AssetType } from '../../../../shared/constants/transaction'; @@ -327,6 +330,18 @@ describe('AssetPage', () => { expect(mmiPortfolioButton).toBeInTheDocument(); }); + it('should render the network name', async () => { + const mockedStore = configureMockStore([thunk])(mockStore); + + const { queryByTestId } = renderWithProvider( + , + mockedStore, + ); + const networkNode = queryByTestId('asset-network'); + expect(networkNode).toBeInTheDocument(); + expect(networkNode?.textContent).toBe(MAINNET_DISPLAY_NAME); + }); + it('should render a native asset', () => { const { container } = renderWithProvider( , diff --git a/ui/pages/asset/components/asset-page.tsx b/ui/pages/asset/components/asset-page.tsx index aef098bfd9d2..ad3f54761798 100644 --- a/ui/pages/asset/components/asset-page.tsx +++ b/ui/pages/asset/components/asset-page.tsx @@ -21,6 +21,7 @@ import { getShowFiatInTestnets, } from '../../../selectors'; import { + AlignItems, Display, FlexDirection, IconColor, @@ -29,6 +30,8 @@ import { TextVariant, } from '../../../helpers/constants/design-system'; import { + AvatarNetwork, + AvatarNetworkSize, Box, ButtonIcon, ButtonIconSize, @@ -54,7 +57,11 @@ import { getIsNativeTokenBuyable } from '../../../ducks/ramps'; import { calculateTokenBalance } from '../../../components/app/assets/util/calculateTokenBalance'; import { useTokenBalances } from '../../../hooks/useTokenBalances'; import { useMultichainSelector } from '../../../hooks/useMultichainSelector'; -import { getMultichainShouldShowFiat } from '../../../selectors/multichain'; +import { + getImageForChainId, + getMultichainShouldShowFiat, +} from '../../../selectors/multichain'; +import { getNetworkConfigurationsByChainId } from '../../../../shared/modules/selectors/networks'; import { getPortfolioUrl } from '../../../helpers/utils/portfolio'; import { hexToDecimal } from '../../../../shared/modules/conversion.utils'; import AssetChart from './chart/asset-chart'; @@ -168,7 +175,7 @@ const AssetPage = ({ // Market and conversion rate data const baseCurrency = marketData[chainId]?.[address]?.currency; - const tokenMarketPrice = marketData[chainId]?.[address]?.price || 0; + const tokenMarketPrice = marketData[chainId]?.[address]?.price || undefined; const tokenExchangeRate = type === AssetType.native ? currencyRates[symbol]?.conversionRate @@ -214,6 +221,12 @@ const AssetPage = ({ [account.address, isMarketingEnabled, isMetaMetricsEnabled, metaMetricsId], ); + const networkConfigurationsByChainId = useSelector( + getNetworkConfigurationsByChainId, + ); + const networkName = networkConfigurationsByChainId[chainId]?.name; + const tokenChainImage = getImageForChainId(chainId); + return ( + {renderRow( + t('network'), + + + {networkName} + , + )} {type === AssetType.token && ( {renderRow( diff --git a/ui/pages/confirmations/components/confirm/info/approve/approve-static-simulation/approve-static-simulation.tsx b/ui/pages/confirmations/components/confirm/info/approve/approve-static-simulation/approve-static-simulation.tsx index 8d66b17a1f46..67ff183784c9 100644 --- a/ui/pages/confirmations/components/confirm/info/approve/approve-static-simulation/approve-static-simulation.tsx +++ b/ui/pages/confirmations/components/confirm/info/approve/approve-static-simulation/approve-static-simulation.tsx @@ -27,15 +27,13 @@ export const ApproveStaticSimulation = () => { const { currentConfirmation: transactionMeta } = useConfirmContext(); - const { decimals: initialDecimals } = useAssetDetails( + const { decimals } = useAssetDetails( transactionMeta?.txParams?.to, transactionMeta?.txParams?.from, transactionMeta?.txParams?.data, transactionMeta?.chainId, ); - const decimals = initialDecimals || '0'; - const { spendingCap, isUnlimitedSpendingCap, diff --git a/ui/pages/confirmations/components/confirm/info/approve/approve.test.tsx b/ui/pages/confirmations/components/confirm/info/approve/approve.test.tsx index 914e276369f5..f69671dccf9f 100644 --- a/ui/pages/confirmations/components/confirm/info/approve/approve.test.tsx +++ b/ui/pages/confirmations/components/confirm/info/approve/approve.test.tsx @@ -1,8 +1,10 @@ +import { screen, waitFor } from '@testing-library/dom'; import React from 'react'; import configureMockStore from 'redux-mock-store'; import thunk from 'redux-thunk'; import { getMockApproveConfirmState } from '../../../../../../../test/data/confirmations/helper'; import { renderWithConfirmContextProvider } from '../../../../../../../test/lib/confirmations/render-helpers'; +import { useAssetDetails } from '../../../../hooks/useAssetDetails'; import ApproveInfo from './approve'; jest.mock('../../../../../../store/actions', () => ({ @@ -32,7 +34,7 @@ jest.mock('./hooks/use-approve-token-simulation', () => ({ jest.mock('../../../../hooks/useAssetDetails', () => ({ useAssetDetails: jest.fn(() => ({ - decimals: 18, + decimals: '18', })), })); @@ -70,6 +72,14 @@ jest.mock('../hooks/useDecodedTransactionData', () => ({ describe('', () => { const middleware = [thunk]; + const mockedAssetDetails = jest.mocked(useAssetDetails); + + beforeEach(() => { + mockedAssetDetails.mockImplementation(() => ({ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + decimals: '4' as any, + })); + }); it('renders component for approve request', async () => { const state = getMockApproveConfirmState(); @@ -81,6 +91,10 @@ describe('', () => { mockStore, ); + await waitFor(() => { + expect(screen.getByText('Speed')).toBeInTheDocument(); + }); + expect(container).toMatchSnapshot(); }); }); diff --git a/ui/pages/confirmations/components/confirm/info/approve/approve.tsx b/ui/pages/confirmations/components/confirm/info/approve/approve.tsx index d1d9fbc08364..e5015af533fd 100644 --- a/ui/pages/confirmations/components/confirm/info/approve/approve.tsx +++ b/ui/pages/confirmations/components/confirm/info/approve/approve.tsx @@ -35,7 +35,7 @@ const ApproveInfo = () => { const { spendingCap, pending } = useApproveTokenSimulation( transactionMeta, - decimals || '0', + decimals, ); const showRevokeVariant = @@ -46,7 +46,7 @@ const ApproveInfo = () => { return null; } - if (pending) { + if (pending || (!isNFT && !decimals)) { return ; } diff --git a/ui/pages/confirmations/components/confirm/info/approve/edit-spending-cap-modal/edit-spending-cap-modal.tsx b/ui/pages/confirmations/components/confirm/info/approve/edit-spending-cap-modal/edit-spending-cap-modal.tsx index 1cd080de96d4..f53c7bae42b2 100644 --- a/ui/pages/confirmations/components/confirm/info/approve/edit-spending-cap-modal/edit-spending-cap-modal.tsx +++ b/ui/pages/confirmations/components/confirm/info/approve/edit-spending-cap-modal/edit-spending-cap-modal.tsx @@ -64,7 +64,7 @@ export const EditSpendingCapModal = ({ const { formattedSpendingCap, spendingCap } = useApproveTokenSimulation( transactionMeta, - decimals || '0', + decimals, ); const [customSpendingCapInputValue, setCustomSpendingCapInputValue] = diff --git a/ui/pages/confirmations/components/confirm/info/approve/hooks/use-approve-token-simulation.ts b/ui/pages/confirmations/components/confirm/info/approve/hooks/use-approve-token-simulation.ts index acd470ba8822..74c857f7078b 100644 --- a/ui/pages/confirmations/components/confirm/info/approve/hooks/use-approve-token-simulation.ts +++ b/ui/pages/confirmations/components/confirm/info/approve/hooks/use-approve-token-simulation.ts @@ -18,7 +18,7 @@ function isSpendingCapUnlimited(decodedSpendingCap: number) { export const useApproveTokenSimulation = ( transactionMeta: TransactionMeta, - decimals: string, + decimals: string | undefined, ) => { const locale = useSelector(getIntlLocale); const { isNFT, pending: isNFTPending } = useIsNFT(transactionMeta); @@ -43,7 +43,7 @@ export const useApproveTokenSimulation = ( return calcTokenAmount( value.data[0].params[paramIndex].value, - Number(decimals), + Number(decimals || '0'), ).toFixed(); }, [value, decimals]); diff --git a/ui/pages/confirmations/components/confirm/info/approve/spending-cap/spending-cap.tsx b/ui/pages/confirmations/components/confirm/info/approve/spending-cap/spending-cap.tsx index 29d009c5b810..3a8c6a5a26ec 100644 --- a/ui/pages/confirmations/components/confirm/info/approve/spending-cap/spending-cap.tsx +++ b/ui/pages/confirmations/components/confirm/info/approve/spending-cap/spending-cap.tsx @@ -90,10 +90,7 @@ export const SpendingCap = ({ Number(decimals ?? '0'), ).toFixed(); - const { pending } = useApproveTokenSimulation( - transactionMeta, - decimals || '0', - ); + const { pending } = useApproveTokenSimulation(transactionMeta, decimals); if (pending) { return ; diff --git a/ui/pages/confirmations/components/confirm/info/info.test.tsx b/ui/pages/confirmations/components/confirm/info/info.test.tsx index 75d98d91a1bd..50046ec96764 100644 --- a/ui/pages/confirmations/components/confirm/info/info.test.tsx +++ b/ui/pages/confirmations/components/confirm/info/info.test.tsx @@ -9,6 +9,7 @@ import { getMockTypedSignConfirmState, } from '../../../../../../test/data/confirmations/helper'; import { renderWithConfirmContextProvider } from '../../../../../../test/lib/confirmations/render-helpers'; +import { useAssetDetails } from '../../../hooks/useAssetDetails'; import Info from './info'; jest.mock( @@ -28,7 +29,23 @@ jest.mock('../../../../../store/actions', () => ({ }), })); +jest.mock('../../../hooks/useAssetDetails', () => ({ + ...jest.requireActual('../../../hooks/useAssetDetails'), + useAssetDetails: jest.fn().mockResolvedValue({ + decimals: '4', + }), +})); + describe('Info', () => { + const mockedAssetDetails = jest.mocked(useAssetDetails); + + beforeEach(() => { + mockedAssetDetails.mockImplementation(() => ({ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + decimals: '4' as any, + })); + }); + it('renders info section for personal sign request', () => { const state = getMockPersonalSignConfirmState(); const mockStore = configureMockStore([])(state); diff --git a/ui/pages/confirmations/components/confirm/info/personal-sign/personal-sign.test.tsx b/ui/pages/confirmations/components/confirm/info/personal-sign/personal-sign.test.tsx index e105493485ec..3b5d1dfc3e62 100644 --- a/ui/pages/confirmations/components/confirm/info/personal-sign/personal-sign.test.tsx +++ b/ui/pages/confirmations/components/confirm/info/personal-sign/personal-sign.test.tsx @@ -9,9 +9,13 @@ import { getMockTypedSignConfirmStateForRequest, } from '../../../../../../../test/data/confirmations/helper'; import { renderWithConfirmContextProvider } from '../../../../../../../test/lib/confirmations/render-helpers'; -import { signatureRequestSIWE } from '../../../../../../../test/data/confirmations/personal_sign'; -import * as utils from '../../../../utils'; +import { + signatureRequestSIWE, + unapprovedPersonalSignMsg, +} from '../../../../../../../test/data/confirmations/personal_sign'; import * as snapUtils from '../../../../../../helpers/utils/snaps'; +import { SignatureRequestType } from '../../../../types/confirm'; +import * as utils from '../../../../utils'; import PersonalSignInfo from './personal-sign'; jest.mock( @@ -184,4 +188,22 @@ describe('PersonalSignInfo', () => { queryByText('This is the site asking for your signature.'), ).toBeDefined(); }); + + it('display hex message value if it can not be converted to valid UTF-8 string', () => { + const message = + '0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470'; + const state = getMockPersonalSignConfirmStateForRequest({ + ...unapprovedPersonalSignMsg, + msgParams: { + ...unapprovedPersonalSignMsg.msgParams, + data: message, + }, + } as SignatureRequestType); + const mockStore = configureMockStore([])(state); + const { getByText } = renderWithConfirmContextProvider( + , + mockStore, + ); + expect(getByText(message)).toBeDefined(); + }); }); diff --git a/ui/pages/confirmations/components/confirm/info/personal-sign/personal-sign.tsx b/ui/pages/confirmations/components/confirm/info/personal-sign/personal-sign.tsx index bd6c41866917..38238a2fa49e 100644 --- a/ui/pages/confirmations/components/confirm/info/personal-sign/personal-sign.tsx +++ b/ui/pages/confirmations/components/confirm/info/personal-sign/personal-sign.tsx @@ -38,8 +38,17 @@ import { selectUseTransactionSimulations } from '../../../../selectors/preferenc import { SignatureRequestType } from '../../../../types/confirm'; import { isSIWESignatureRequest } from '../../../../utils'; import { SigningInWithRow } from '../shared/sign-in-with-row/sign-in-with-row'; +import { isValidUTF8 } from '../utils'; import { SIWESignInfo } from './siwe-sign'; +const getMessageText = (hexString?: string) => { + if (!hexString) { + return hexString; + } + const messageText = sanitizeString(hexToText(hexString)); + return isValidUTF8(messageText) ? messageText : hexString; +}; + const PersonalSignInfo: React.FC = () => { const t = useI18nContext(); const { currentConfirmation } = useConfirmContext(); @@ -52,8 +61,8 @@ const PersonalSignInfo: React.FC = () => { } const isSIWE = isSIWESignatureRequest(currentConfirmation); - const messageText = sanitizeString( - hexToText(currentConfirmation.msgParams?.data), + const messageText = getMessageText( + currentConfirmation.msgParams?.data as string, ); let toolTipMessage; diff --git a/ui/pages/confirmations/components/confirm/info/utils.test.ts b/ui/pages/confirmations/components/confirm/info/utils.test.ts index 9c12b9127811..8723cfc3f05d 100644 --- a/ui/pages/confirmations/components/confirm/info/utils.test.ts +++ b/ui/pages/confirmations/components/confirm/info/utils.test.ts @@ -4,6 +4,7 @@ import { DecodedTransactionDataSource } from '../../../../../../shared/types/tra import { getIsRevokeSetApprovalForAll, hasValueAndNativeBalanceMismatch, + isValidUTF8, } from './utils'; describe('getIsRevokeSetApprovalForAll', () => { @@ -141,3 +142,11 @@ describe('hasValueAndNativeBalanceMismatch', () => { expect(hasValueAndNativeBalanceMismatch(transaction)).toBe(true); }); }); + +describe('isValidUTF8', () => { + it('returns true for valid UTF-8 string', () => { + expect(isValidUTF8('Hello')).toEqual(true); + expect(isValidUTF8('\xC3\x28')).toEqual(true); + expect(isValidUTF8('😀')).toEqual(true); + }); +}); diff --git a/ui/pages/confirmations/components/confirm/info/utils.ts b/ui/pages/confirmations/components/confirm/info/utils.ts index 1af918aea74e..17b10e555952 100644 --- a/ui/pages/confirmations/components/confirm/info/utils.ts +++ b/ui/pages/confirmations/components/confirm/info/utils.ts @@ -110,3 +110,17 @@ export function hasValueAndNativeBalanceMismatch( nativeBalanceChange?.isDecrease === false, ); } + +export function isValidUTF8(inputString: string) { + try { + const encoder = new TextEncoder(); + const encoded = encoder.encode(inputString); + + const decoder = new TextDecoder('utf-8', { fatal: true }); + decoder.decode(encoded); + + return true; + } catch (e) { + return false; + } +} diff --git a/ui/pages/confirmations/components/confirm/title/hooks/useCurrentSpendingCap.ts b/ui/pages/confirmations/components/confirm/title/hooks/useCurrentSpendingCap.ts index b50948259734..5fa3918804d7 100644 --- a/ui/pages/confirmations/components/confirm/title/hooks/useCurrentSpendingCap.ts +++ b/ui/pages/confirmations/components/confirm/title/hooks/useCurrentSpendingCap.ts @@ -43,7 +43,7 @@ export function useCurrentSpendingCap(currentConfirmation: Confirmation) { const { spendingCap, pending } = useApproveTokenSimulation( currentConfirmation as TransactionMeta, - decimals || '0', + decimals, ); let customSpendingCap = ''; diff --git a/ui/pages/confirmations/confirm/confirm.test.tsx b/ui/pages/confirmations/confirm/confirm.test.tsx index 939ca8768afe..158884fc36a0 100644 --- a/ui/pages/confirmations/confirm/confirm.test.tsx +++ b/ui/pages/confirmations/confirm/confirm.test.tsx @@ -16,6 +16,7 @@ import { import mockState from '../../../../test/data/mock-state.json'; import { renderWithConfirmContextProvider } from '../../../../test/lib/confirmations/render-helpers'; import * as actions from '../../../store/actions'; +import { useAssetDetails } from '../hooks/useAssetDetails'; import { SignatureRequestType } from '../types/confirm'; import { memoizedGetTokenStandardAndDetails } from '../utils/token'; import Confirm from './confirm'; @@ -27,7 +28,15 @@ jest.mock('react-router-dom', () => ({ }), })); +jest.mock('../hooks/useAssetDetails', () => ({ + ...jest.requireActual('../hooks/useAssetDetails'), + useAssetDetails: jest.fn().mockResolvedValue({ + decimals: '4', + }), +})); + const middleware = [thunk]; +const mockedAssetDetails = jest.mocked(useAssetDetails); describe('Confirm', () => { afterEach(() => { @@ -37,6 +46,13 @@ describe('Confirm', () => { memoizedGetTokenStandardAndDetails?.cache?.clear?.(); }); + beforeEach(() => { + mockedAssetDetails.mockImplementation(() => ({ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + decimals: '4' as any, + })); + }); + it('should render', () => { const mockStore = configureMockStore(middleware)(mockState); diff --git a/ui/pages/notifications-settings/notifications-settings-allow-notifications.tsx b/ui/pages/notifications-settings/notifications-settings-allow-notifications.tsx index b442a4092185..fdfaeb1109cb 100644 --- a/ui/pages/notifications-settings/notifications-settings-allow-notifications.tsx +++ b/ui/pages/notifications-settings/notifications-settings-allow-notifications.tsx @@ -74,7 +74,7 @@ export function NotificationsSettingsAllowNotifications({ }, [isMetamaskNotificationsEnabled]); useEffect(() => { - if (!error) { + if (!error && isMetamaskNotificationsEnabled) { listNotifications(); } }, [isMetamaskNotificationsEnabled, error, listNotifications]); diff --git a/ui/pages/routes/routes.component.test.js b/ui/pages/routes/routes.component.test.js index e661099e1aa2..b9bd116195ea 100644 --- a/ui/pages/routes/routes.component.test.js +++ b/ui/pages/routes/routes.component.test.js @@ -1,6 +1,6 @@ import React from 'react'; import configureMockStore from 'redux-mock-store'; -import { act, waitFor } from '@testing-library/react'; +import { act } from '@testing-library/react'; import thunk from 'redux-thunk'; import { BtcAccountType } from '@metamask/keyring-api'; import { SEND_STAGES } from '../../ducks/send'; @@ -15,10 +15,6 @@ import { useIsOriginalNativeTokenSymbol } from '../../hooks/useIsOriginalNativeT import { createMockInternalAccount } from '../../../test/jest/mocks'; import { CHAIN_IDS } from '../../../shared/constants/network'; import { mockNetworkState } from '../../../test/stub/networks'; -import { - MOCK_ACCOUNT_BIP122_P2WPKH, - MOCK_ACCOUNT_EOA, -} from '../../../test/data/mock-accounts'; import useMultiPolling from '../../hooks/useMultiPolling'; import Routes from '.'; @@ -191,96 +187,6 @@ describe('Routes Component', () => { expect(getByTestId('account-menu-icon')).not.toBeDisabled(); }); }); - - describe('new network popup', () => { - const mockBtcAccount = MOCK_ACCOUNT_BIP122_P2WPKH; - const mockEvmAccount = MOCK_ACCOUNT_EOA; - - const mockNewlyAddedNetwork = { - chainId: CHAIN_IDS.BASE, - name: 'Base', - nativeCurrency: 'ETH', - defaultRpcEndpointIndex: 0, - rpcEndpoints: [ - { - type: 'custom', - url: 'https://base.com', - networkClientId: CHAIN_IDS.BASE, - }, - ], - }; - - const renderPopup = async (account) => { - // This popup does not show up for tests, so we have to disable this: - process.env.IN_TEST = ''; - const state = { - ...mockSendState, - metamask: { - ...mockState.metamask, - completedOnboarding: true, - selectedNetworkClientId: mockNewlyAddedNetwork.chainId, - internalAccounts: { - accounts: { - [account.id]: account, - }, - selectedAccount: account.id, - }, - networkConfigurationsByChainId: { - ...mockState.metamask.networkConfigurationsByChainId, - [mockNewlyAddedNetwork.chainId]: mockNewlyAddedNetwork, - }, - networksMetadata: { - ...mockState.metamask.networksMetadata, - [mockNewlyAddedNetwork.chainId]: { - EIPS: { - 1559: true, - }, - status: 'available', - }, - }, - tokens: [], - swapsState: { swapsFeatureIsLive: false }, - announcements: {}, - pendingApprovals: {}, - termsOfUseLastAgreed: new Date('2999-03-25'), - shouldShowSeedPhraseReminder: false, - useExternalServices: true, - }, - send: { - ...mockSendState.send, - stage: SEND_STAGES.INACTIVE, - currentTransactionUUID: null, - draftTransactions: {}, - }, - appState: { - ...mockSendState.appState, - showWhatsNewPopup: false, - onboardedInThisUISession: false, - }, - }; - return await render(['/'], state); - }; - - it('displays new EVM network popup for EVM accounts', async () => { - const { getAllByText, queryByTestId } = await renderPopup(mockEvmAccount); - - await waitFor(() => { - expect(getAllByText(mockNewlyAddedNetwork.name).length).toBeGreaterThan( - 0, - ); - expect( - queryByTestId('new-network-info__bullet-paragraph'), - ).not.toBeInTheDocument(); - }); - }); - - it('does not display new EVM network popup for non-EVM accounts', async () => { - const { queryByTestId } = await renderPopup(mockBtcAccount); - - const networkInfo = queryByTestId('new-network-info__bullet-paragraph'); - expect(networkInfo).not.toBeInTheDocument(); - }); - }); }); describe('toast display', () => { diff --git a/ui/pages/settings/security-tab/__snapshots__/security-tab.test.js.snap b/ui/pages/settings/security-tab/__snapshots__/security-tab.test.js.snap index 1d5b622b6b46..3b1837d55ef2 100644 --- a/ui/pages/settings/security-tab/__snapshots__/security-tab.test.js.snap +++ b/ui/pages/settings/security-tab/__snapshots__/security-tab.test.js.snap @@ -12,11 +12,62 @@ exports[`Security Tab should match snapshot 1`] = `
- - Basic functionality -
+

+ Basic functionality +

+