From 6e5c2f03bfbc4a0c8fa334b7beedb19cf35c9d73 Mon Sep 17 00:00:00 2001 From: Niranjana Binoy <43930900+NiranjanaBinoy@users.noreply.github.com> Date: Tue, 9 Aug 2022 21:26:25 -0400 Subject: [PATCH] Token detection V2 Flag Removal and Re-introducing the use of legacy token list when token detection is OFF (#15138) * addding the legacy tokenlist, tuning token detection OFF by default, adding new message while importing tokens updating the controller version and calling detectNewToken on network change fixing rebase error Run yarn lavamoat:auto for updating policies updating lavamoat Deleted node modules and run again lavamoat auto fixing rebase issues updating lavamoat policies updating lavamoat after rebasing policies updating custom token warning and blocking detectedtoken link when tpken detection is off for supported networks to update the token in fetchTosync updating the contract map object Revert build-system lavamoat policy changes Move token list selection logic from components to getTokenList selector updating the tokenList Update lavamoat Fix error updating lavamoat lint fix fix unit test fail fix unit test fail lint fix fixing rebase locale error rebase fix Revert build-system policy changes temp addressing review comments * rebase fix --- app/_locales/de/messages.json | 15 -- app/_locales/el/messages.json | 15 -- app/_locales/en/messages.json | 31 ++- app/_locales/es/messages.json | 15 -- app/_locales/es_419/messages.json | 12 - app/_locales/fr/messages.json | 15 -- app/_locales/hi/messages.json | 15 -- app/_locales/id/messages.json | 15 -- app/_locales/ja/messages.json | 15 -- app/_locales/ko/messages.json | 15 -- app/_locales/pt/messages.json | 15 -- app/_locales/pt_BR/messages.json | 12 - app/_locales/ru/messages.json | 15 -- app/_locales/tl/messages.json | 15 -- app/_locales/tr/messages.json | 15 -- app/_locales/vi/messages.json | 15 -- app/_locales/zh/messages.json | 12 - app/_locales/zh_CN/messages.json | 12 - app/scripts/controllers/detect-tokens.js | 152 +++++------ app/scripts/controllers/detect-tokens.test.js | 246 ++++++++---------- app/scripts/controllers/preferences.js | 10 +- app/scripts/controllers/preferences.test.js | 16 ++ app/scripts/metamask-controller.js | 102 ++++---- lavamoat/browserify/beta/policy.json | 6 + lavamoat/browserify/flask/policy.json | 6 + lavamoat/browserify/main/policy.json | 6 + package.json | 2 +- shared/constants/tokens.js | 15 ++ test/e2e/fixtures/address-entry/state.json | 6 + test/e2e/fixtures/connected-state/state.json | 6 + test/e2e/fixtures/custom-rpc/state.json | 6 + test/e2e/fixtures/custom-token/state.json | 6 + test/e2e/fixtures/eip-1559-v2-dapp/state.json | 6 + test/e2e/fixtures/eip-1559-v2/state.json | 6 + test/e2e/fixtures/import-ui/state.json | 6 + test/e2e/fixtures/imported-account/state.json | 6 + test/e2e/fixtures/localization/state.json | 6 + test/e2e/fixtures/metrics-enabled/state.json | 6 + .../fixtures/navigate-transactions/state.json | 6 + test/e2e/fixtures/onboarding/state.json | 6 + test/e2e/fixtures/send-edit-v2/state.json | 6 + test/e2e/fixtures/send-edit/state.json | 6 + test/e2e/fixtures/special-settings/state.json | 2 +- test/e2e/fixtures/threebox-enabled/state.json | 6 + test/e2e/tests/settings-search.spec.js | 4 +- test/e2e/tests/swap-eth.spec.js | 2 +- ui/components/app/asset-list/asset-list.js | 13 +- .../app/contact-list/contact-list.test.js | 4 +- .../detected-token-ignored-popover.js | 19 +- .../detected-token-ignored-popover/index.scss | 12 +- .../app/detected-token/detected-token.js | 13 +- .../import-token-link.component.js | 19 +- .../confirm-remove-account.test.js | 6 +- ui/components/app/token-cell/token-cell.js | 3 - .../app/token-cell/token-cell.test.js | 2 - .../ui/identicon/identicon.component.js | 37 +-- .../ui/identicon/identicon.container.js | 6 +- .../ui/jazzicon/jazzicon.component.js | 6 +- .../nickname-popover.component.js | 11 +- .../update-nickname-popover.js | 6 +- ui/helpers/constants/settings.js | 12 +- ui/helpers/utils/icon-factory.js | 19 +- ui/helpers/utils/settings-search.test.js | 13 +- ui/helpers/utils/token-util.js | 8 +- ui/hooks/useAddressDetails.js | 23 +- ui/hooks/useAddressDetails.test.js | 4 +- ui/hooks/useTokensToSearch.js | 60 +---- .../confirm-approve-content.component.test.js | 4 +- .../confirm-transaction-base.container.js | 17 +- .../import-token/import-token.component.js | 119 +++++---- .../import-token/import-token.container.js | 20 +- ui/pages/import-token/import-token.test.js | 1 + .../token-list/token-list.component.js | 8 +- .../token-list/token-list.container.js | 5 +- .../advanced-tab/advanced-tab.component.js | 6 +- .../advanced-tab.component.test.js | 23 +- .../experimental-tab.component.js | 42 --- .../experimental-tab.component.test.js | 38 --- .../experimental-tab.container.js | 10 - .../settings-tab/settings-tab.component.js | 11 +- .../settings-tab/settings-tab.container.js | 6 +- ui/pages/swaps/build-quote/build-quote.js | 4 +- ui/pages/swaps/index.js | 6 +- ui/pages/token-details/token-details-page.js | 10 +- .../token-details/token-details-page.test.js | 4 - ui/selectors/selectors.js | 66 ++++- yarn.lock | 8 +- 87 files changed, 680 insertions(+), 957 deletions(-) delete mode 100644 ui/pages/settings/experimental-tab/experimental-tab.component.test.js diff --git a/app/_locales/de/messages.json b/app/_locales/de/messages.json index 9e7fd4b50..00c59c67b 100644 --- a/app/_locales/de/messages.json +++ b/app/_locales/de/messages.json @@ -1203,9 +1203,6 @@ "failureMessage": { "message": "Etwas ist schief gelaufen und wir konnten die Aktion nicht abschließen" }, - "fakeTokenWarning": { - "message": "Jeder kann ein Token erstellen, einschließlich der Erstellung gefälschter Versionen bestehender Token. Erfahren Sie mehr über $1" - }, "fast": { "message": "Schnell" }, @@ -3683,12 +3680,6 @@ "tokenDetectionAlertMessage": { "message": "Die Token-Erkennung ist derzeit für $1 verfügbar. $2" }, - "tokenDetectionAnnouncement": { - "message": "Neu! Verbesserte Token Erkennung ist im Ethereum Mainnet als experimentelle Funktion verfügbar. $1" - }, - "tokenDetectionToggleDescription": { - "message": "Die Token-API von ConsenSys sammelt eine Liste von Token aus verschiedenen Token-Listen von Drittanbietern. Wenn Sie diese Funktion deaktivieren, werden keine neuen Token mehr erkannt, die zu Ihrer Wallet hinzugefügt werden, aber die Option zur Suche nach Token für den Import bleibt erhalten." - }, "tokenId": { "message": "Token-ID" }, @@ -3923,12 +3914,6 @@ "usePhishingDetectionDescription": { "message": "Zeigt eine Warnung für Phishing-Domänen, die Ethereum Benutzer ansprechen" }, - "useTokenDetection": { - "message": "Token-Erkennung verwenden" - }, - "useTokenDetectionDescription": { - "message": "Wir verwenden Drittanbieter-API, um neue an Ihre Wallet gesendete Token zu erkennen und anzuzeigen. Deaktivieren Sie diese, wenn Sie nicht möchten, dass MetaMask Daten von diesen Diensten abruft." - }, "useTokenDetectionPrivacyDesc": { "message": "Die automatische Anzeige der an Ihr Konto gesendeten Token erfordert die Kommunikation mit Servern von Drittanbietern, um die Bilder der Token abzurufen. Diese Server haben Zugriff auf Ihre IP-Adresse." }, diff --git a/app/_locales/el/messages.json b/app/_locales/el/messages.json index 14eb2af9f..128bc9c42 100644 --- a/app/_locales/el/messages.json +++ b/app/_locales/el/messages.json @@ -1211,9 +1211,6 @@ "failureMessage": { "message": "Κάτι πήγε λάθος και δεν μπορέσαμε να ολοκληρώσουμε την ενέργεια" }, - "fakeTokenWarning": { - "message": "Οποιοσδήποτε μπορεί να δημιουργήσει ένα token, συμπεριλαμβανομένης της δημιουργίας ψεύτικων εκδόσεων των υφιστάμενων tokens. Μάθετε περισσότερα γι'αυτό $1" - }, "fast": { "message": "Γρήγορα" }, @@ -3739,12 +3736,6 @@ "tokenDetectionAlertMessage": { "message": "Ο εντοπισμός token είναι επί του παρόντος διαθέσιμος στο $1. $2" }, - "tokenDetectionAnnouncement": { - "message": "Νέο! Η βελτιωμένη ανίχνευση token είναι διαθέσιμη στο Ethereum Mainnet ως πειραματικό χαρακτηριστικό. $1" - }, - "tokenDetectionToggleDescription": { - "message": "Το token API της ConsenSys δημιουργεί μια λίστα με token από διάφορες λίστες token τρίτων. Εάν το απενεργοποιήσετε, θα σταματήσει ο εντοπισμός νέων token που προστίθενται στο πορτοφόλι σας, αλλά θα διατηρηθεί η επιλογή αναζήτησης token για εισαγωγή." - }, "tokenId": { "message": "Αναγνωριστικό token" }, @@ -3979,12 +3970,6 @@ "usePhishingDetectionDescription": { "message": "Εμφάνιση μιας προειδοποίησης για τομείς Απάτης Ηλεκτρονικού Ψαρέματος που στοχεύουν χρήστες του Ethereum" }, - "useTokenDetection": { - "message": "Χρήση Ανίχνευσης Token" - }, - "useTokenDetectionDescription": { - "message": "Χρησιμοποιούμε API τρίτων για να εντοπίσουμε και να εμφανίσουμε νέα tokens που αποστέλλονται στο πορτοφόλι σας. Απενεργοποιήστε αν δεν θέλετε το MetaMask να τραβήξει δεδομένα από αυτές τις υπηρεσίες." - }, "useTokenDetectionPrivacyDesc": { "message": "Η αυτόματη εμφάνιση των token που αποστέλλονται στον λογαριασμό σας συνεπάγεται επικοινωνία με διακομιστές τρίτων για τη λήψη εικόνων των token. Αυτοί οι διακομιστές θα έχουν πρόσβαση στη διεύθυνση IP σας." }, diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 35e6fbd15..9dad2d7b3 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -837,6 +837,9 @@ "customTokenWarningInTokenDetectionNetwork": { "message": "Before manually importing a token, make sure you trust it. Learn about $1." }, + "customTokenWarningInTokenDetectionNetworkWithTDOFF": { + "message": "Make sure you trust a token before you import it. Learn how to avoid $1. You can also enable token detection $2." + }, "customerSupport": { "message": "customer support" }, @@ -1284,9 +1287,6 @@ "failureMessage": { "message": "Something went wrong, and we were unable to complete the action" }, - "fakeTokenWarning": { - "message": "Anyone can create a token, including creating fake versions of existing tokens. Learn more about $1" - }, "fast": { "message": "Fast" }, @@ -1602,6 +1602,12 @@ "importNFTs": { "message": "Import NFTs" }, + "importSelectedTokens": { + "message": "Import selected tokens?" + }, + "importSelectedTokensDescription": { + "message": "Only the tokens you've selected will appear in your wallet. You can always import hidden tokens later by searching for them." + }, "importTokenQuestion": { "message": "Import token?" }, @@ -1628,6 +1634,9 @@ "message": "Imported", "description": "status showing that an account has been fully loaded into the keyring" }, + "inYourSettings": { + "message": "in your Settings" + }, "infuraBlockedNotification": { "message": "MetaMask is unable to connect to the blockchain host. Review possible reasons $1.", "description": "$1 is a clickable link with with text defined by the 'here' key" @@ -3847,11 +3856,8 @@ "tokenDetectionAlertMessage": { "message": "Token detection is currently available on $1. $2" }, - "tokenDetectionAnnouncement": { - "message": "New! Improved token detection is available on Ethereum Mainnet as an experimental feature. $1" - }, - "tokenDetectionToggleDescription": { - "message": "ConsenSys’ token API aggregates a list of tokens from various third party token lists. Turning it off will stop detecting new tokens added to your wallet, but will keep the option to search for tokens to import." + "tokenDetectionDescription": { + "message": "ConsenSys' token API aggregates a list of tokens from various third party token lists. When turned on, tokens will be automatically detected, and searchable, on Ethereum mainnet, Binance, Polygon and Avalanche. When turned off, you will still be able to search for tokens on Ethereum mainnet using MetaMask's legacy token list." }, "tokenId": { "message": "Token ID" @@ -3859,6 +3865,9 @@ "tokenList": { "message": "Token lists:" }, + "tokenScamSecurityRisk": { + "message": "token scams and security risks" + }, "tokenSymbol": { "message": "Token symbol" }, @@ -4091,12 +4100,6 @@ "usePhishingDetectionDescription": { "message": "Display a warning for phishing domains targeting Ethereum users" }, - "useTokenDetection": { - "message": "Use token detection" - }, - "useTokenDetectionDescription": { - "message": "We use third-party APIs to detect and display new tokens sent to your wallet. Turn off if you don’t want MetaMask to pull data from those services." - }, "useTokenDetectionPrivacyDesc": { "message": "Automatically displaying tokens sent to your account involves communication with third party servers to fetch token’s images. Those serves will have access to your IP address." }, diff --git a/app/_locales/es/messages.json b/app/_locales/es/messages.json index 1ca806e3a..7709ef0b7 100644 --- a/app/_locales/es/messages.json +++ b/app/_locales/es/messages.json @@ -1211,9 +1211,6 @@ "failureMessage": { "message": "Se produjo un error y no pudimos completar la acción" }, - "fakeTokenWarning": { - "message": "Cualquiera puede crear un token, incluso crear versiones falsas de tokens existentes. Aprenda más sobre $1" - }, "fast": { "message": "Rápido" }, @@ -3739,12 +3736,6 @@ "tokenDetectionAlertMessage": { "message": "La detección de tókens está actualmente disponible en $1. $2" }, - "tokenDetectionAnnouncement": { - "message": "¡Nuevo! La detección de tokens mejorada está disponible en la Mainnet de Ethereum como funcionalidad experimental. $1" - }, - "tokenDetectionToggleDescription": { - "message": "La API de tokens de ConsenSys agrega una lista de tokens de varias listas de tokens de terceros. Al desactivarla se dejará de detectar nuevos tokens agregados a su billetera, pero se mantendrá la opción de buscar tokens para importar." - }, "tokenId": { "message": "Identificador de Token" }, @@ -3979,12 +3970,6 @@ "usePhishingDetectionDescription": { "message": "Mostrar una advertencia respecto de los dominios de phishing dirigidos a los usuarios de Ethereum" }, - "useTokenDetection": { - "message": "Usar detección de token" - }, - "useTokenDetectionDescription": { - "message": "Utilizamos API de terceros para detectar y mostrar nuevos tokens enviados a su cartera. Desactive si no desea que MetaMask extraiga datos de esos servicios." - }, "useTokenDetectionPrivacyDesc": { "message": "La visualización automática de tokens enviados a su cuenta implica la comunicación con servidores de terceros para obtener imágenes de tokens. Esos servicios tendrán acceso a su dirección IP." }, diff --git a/app/_locales/es_419/messages.json b/app/_locales/es_419/messages.json index 7b85225d6..e3a617fbe 100644 --- a/app/_locales/es_419/messages.json +++ b/app/_locales/es_419/messages.json @@ -1047,9 +1047,6 @@ "failureMessage": { "message": "Se produjo un error y no pudimos finalizar la acción" }, - "fakeTokenWarning": { - "message": "Cualquiera puede crear un token, incluso crear versiones falsas de tokens existentes. Aprenda más sobre $1" - }, "fast": { "message": "Rápido" }, @@ -3021,9 +3018,6 @@ "tokenDecimalFetchFailed": { "message": "Se requieren los decimales del token." }, - "tokenDetectionAnnouncement": { - "message": "¡Nuevo! La detección de tokens mejorada está disponible en la Mainnet de Ethereum como funcionalidad experimental. $1" - }, "tokenId": { "message": "ID del token" }, @@ -3236,12 +3230,6 @@ "usePhishingDetectionDescription": { "message": "Mostrar una advertencia respecto de los dominios de phishing dirigidos a los usuarios de Ethereum" }, - "useTokenDetection": { - "message": "Usar detección de token" - }, - "useTokenDetectionDescription": { - "message": "Utilizamos API de terceros para detectar y mostrar nuevos tokens enviados a su cartera. Desactive si no desea que MetaMask extraiga datos de esos servicios." - }, "usedByClients": { "message": "Usado por una variedad de clientes distintos" }, diff --git a/app/_locales/fr/messages.json b/app/_locales/fr/messages.json index 3eb07802a..af374452d 100644 --- a/app/_locales/fr/messages.json +++ b/app/_locales/fr/messages.json @@ -1211,9 +1211,6 @@ "failureMessage": { "message": "Un problème est survenu et nous n’avons pas pu mener à bien l’action" }, - "fakeTokenWarning": { - "message": "Tout un chacun peut créer un jeton, y compris créer de fausses copies de jetons existants. En savoir plus sur $1" - }, "fast": { "message": "Rapide" }, @@ -3739,12 +3736,6 @@ "tokenDetectionAlertMessage": { "message": "La détection du token est actuellement disponible sur $1. $2" }, - "tokenDetectionAnnouncement": { - "message": "Nouveau ! Une détection améliorée des jetons est disponible sur le Mainnet d’Ethereum en tant que fonctionnalité expérimentale. $1" - }, - "tokenDetectionToggleDescription": { - "message": "L’API des tokens de ConsenSys regroupe une liste de tokens provenant de diverses listes de tokens externes. Sa désactivation arrêtera la détection de nouveaux tokens ajoutés à votre portefeuille, mais conservera l’option de recherche de tokens à importer." - }, "tokenId": { "message": "ID de token" }, @@ -3979,12 +3970,6 @@ "usePhishingDetectionDescription": { "message": "Cela permet d’afficher un avertissement pour les domaines d’hameçonnage ciblant les utilisateurs d’Ethereum" }, - "useTokenDetection": { - "message": "Utiliser la détection des jetons" - }, - "useTokenDetectionDescription": { - "message": "Nous utilisons des API tierces pour détecter et afficher les nouveaux jetons envoyés à votre portefeuille. Désactivez cette option si vous ne souhaitez pas que MetaMask récupère les données de ces services." - }, "useTokenDetectionPrivacyDesc": { "message": "L’affichage automatique des tokens envoyés sur votre compte implique une communication avec des serveurs externes afin de récupérer les images des tokens. Ces serveurs auront accès à votre adresse IP." }, diff --git a/app/_locales/hi/messages.json b/app/_locales/hi/messages.json index ad340f921..4f564482e 100644 --- a/app/_locales/hi/messages.json +++ b/app/_locales/hi/messages.json @@ -1211,9 +1211,6 @@ "failureMessage": { "message": "कुछ गलत हुआ और हम कार्रवाई को पूरा करने में असमर्थ रहे" }, - "fakeTokenWarning": { - "message": "कोई भी टोकन बना सकता है, जिसमें मौजूदा टोकन के नकली संस्करण को बनाना शामिल है। $1 के बारे में और अधिक जानें" - }, "fast": { "message": "तेज" }, @@ -3739,12 +3736,6 @@ "tokenDetectionAlertMessage": { "message": "फिलहाल टोकन डिटेक्शन $1 पर उपलब्ध है। $2" }, - "tokenDetectionAnnouncement": { - "message": "नया! प्रायोगिक फीचर के रूप में Ethereum Mainnet पर बेहतर टोकन डिटेक्शन उपलब्ध है। $1" - }, - "tokenDetectionToggleDescription": { - "message": "ConsenSys के टोकन का एपीआई विभिन्न थर्ड पार्टी टोकन सूचियों में से टोकन की एक सूची एकत्र करता है। इसे बंद करने से आपके वॉलेट में जोड़े गए नए टोकन का पता चलना बंद हो जाएगा, लेकिन इंपोर्ट करने के लिए टोकन खोजने का विकल्प बना रहेगा।" - }, "tokenId": { "message": "टोकन आइडी" }, @@ -3979,12 +3970,6 @@ "usePhishingDetectionDescription": { "message": "Ethereum उपयोगकर्ताओं को लक्षित करने वाले फिशिंग डोमेन के लिए एक चेतावनी प्रदर्शित करें" }, - "useTokenDetection": { - "message": "टोकन डिटेक्शन का उपयोग करें" - }, - "useTokenDetectionDescription": { - "message": "हम आपके वॉलेट में भेजे गए नए टोकन का पता लगाने और प्रदर्शित करने के लिए तीसरे-पक्ष API का उपयोग करते हैं। बंद करें यदि आप नहीं चाहते कि MetaMask उन सेवाओं से डेटा पुल करे।" - }, "useTokenDetectionPrivacyDesc": { "message": "आपके खाते में भेजे गए टोकन को स्वचालित रूप से प्रदर्शित करने में थर्ड पार्टी के सर्वर्स के साथ संचार शामिल रहेगा, जो टोकन के चित्रों को लाने का काम करते हैं। वे सर्वर्स आपके IP एड्रेस को एक्सेस कर पाएंगे।" }, diff --git a/app/_locales/id/messages.json b/app/_locales/id/messages.json index a4dbe516f..420a401ed 100644 --- a/app/_locales/id/messages.json +++ b/app/_locales/id/messages.json @@ -1211,9 +1211,6 @@ "failureMessage": { "message": "Ada yang salah, dan kami tidak dapat menyelesaikan tindakan" }, - "fakeTokenWarning": { - "message": "Siapa pun dapat membuat token, termasuk membuat versi palsu dari token yang ada. Pelajari selengkapnya seputar $1" - }, "fast": { "message": "Cepat" }, @@ -3739,12 +3736,6 @@ "tokenDetectionAlertMessage": { "message": "Saat ini deteksi token tersedia di $1. $2" }, - "tokenDetectionAnnouncement": { - "message": "Baru! Deteksi token yang ditingkatkan tersedia di Ethereum Mainnet sebagai fitur eksperimental. $1" - }, - "tokenDetectionToggleDescription": { - "message": "API token ConsenSys mengumpulkan daftar token dari berbagai daftar token pihak ketiga. Menonaktifkannya akan menghentikan deteksi token baru yang ditambahkan ke dompet Anda, tetapi Anda akan tetap memiliki opsi untuk mencari token yang akan diimpor." - }, "tokenId": { "message": "ID token" }, @@ -3979,12 +3970,6 @@ "usePhishingDetectionDescription": { "message": "Menampilkan peringatan untuk domain pengelabuan yang menargetkan pengguna Ethereum" }, - "useTokenDetection": { - "message": "Gunakan Deteksi Token" - }, - "useTokenDetectionDescription": { - "message": "Kami menggunakan API pihak ketiga untuk mendeteksi dan menampilkan token baru yang dikirim ke dompet Anda. Matikan jika Anda tidak ingin MetaMask memakai data dari layanan tersebut." - }, "useTokenDetectionPrivacyDesc": { "message": "Menampilkan token yang dikirim ke akun Anda secara otomatis yang melibatkan komunikasi dengan server pihak ketiga untuk mengambil gambar token. Server tersebut akan memiliki akses ke alamat IP Anda." }, diff --git a/app/_locales/ja/messages.json b/app/_locales/ja/messages.json index 994e44e2b..096d2bce5 100644 --- a/app/_locales/ja/messages.json +++ b/app/_locales/ja/messages.json @@ -1211,9 +1211,6 @@ "failureMessage": { "message": "問題が発生しました。アクションを完了させることができません" }, - "fakeTokenWarning": { - "message": "既存のトークンの偽のバージョンの作成を含め、誰でもトークンを作成できます。$1に関する詳細をご覧ください" - }, "fast": { "message": "高速" }, @@ -3739,12 +3736,6 @@ "tokenDetectionAlertMessage": { "message": "トークン検出は現在 $1 で利用可能です。$2" }, - "tokenDetectionAnnouncement": { - "message": "新機能! 実験的な機能として、Ethereum Mainnetでのトークン検出が改善されました。$1" - }, - "tokenDetectionToggleDescription": { - "message": "ConsenSys のトークン API は、さまざまなサードパーティのトークンリストからトークンのリストを集積します。これをオフにすると、ウォレットに追加された新しいトークンは検出されなくなりますが、引き続きトークンを検索してインポートすることは可能です。" - }, "tokenId": { "message": "トークン ID" }, @@ -3979,12 +3970,6 @@ "usePhishingDetectionDescription": { "message": "イーサリアムユーザーを対象としたドメインのフィッシングに対して警告を表示します" }, - "useTokenDetection": { - "message": "トークン検出を使用" - }, - "useTokenDetectionDescription": { - "message": "弊社はユーザーのウォレットに送信された新しいトークンを検出して表示するために、サードパーティーAPIを使用します。MetaMaskにこれらのサービスからデータを取得させたくない場合は、この機能をオフにしてください。" - }, "useTokenDetectionPrivacyDesc": { "message": "アカウントに送られたトークンを自動的に表示するには、サードパーティーサーバーと通信し、トークンの画像を取得する必要があります。これらのサーバーはユーザーの IP アドレスにアクセスできます。" }, diff --git a/app/_locales/ko/messages.json b/app/_locales/ko/messages.json index 7fc803b10..184a31c15 100644 --- a/app/_locales/ko/messages.json +++ b/app/_locales/ko/messages.json @@ -1211,9 +1211,6 @@ "failureMessage": { "message": "문제가 발생했습니다. 작업을 완료할 수 없습니다." }, - "fakeTokenWarning": { - "message": "기존 토큰의 가짜 버전 생성을 포함하여 누구나 토큰을 생성할 수 있습니다. $1에 대해 자세히 알아보기" - }, "fast": { "message": "빠름" }, @@ -3739,12 +3736,6 @@ "tokenDetectionAlertMessage": { "message": "토큰 감지 기능은 현재 $1. $2에서 사용할 수 있습니다." }, - "tokenDetectionAnnouncement": { - "message": "신규! 개선된 토큰 감지는 실험적 기능으로 이더리움 메인넷에서 사용할 수 있습니다. $1" - }, - "tokenDetectionToggleDescription": { - "message": "ConsenSys의 토큰 API는 타사의 다양한 목록을 모아 하나의 토큰 목록을 작성합니다. 이 API를 끄면 신규 토큰을 감지해서 지갑에 추가하는 기능은 중단되지만 토큰을 검색해서 가져오는 기능은 계속 사용할 수 있습니다." - }, "tokenId": { "message": "토큰 ID" }, @@ -3979,12 +3970,6 @@ "usePhishingDetectionDescription": { "message": "이더리움 사용자를 노리는 피싱 도메인에 대한 경고를 표시합니다" }, - "useTokenDetection": { - "message": "토큰 감지 사용" - }, - "useTokenDetectionDescription": { - "message": "당사는 타사 API를 사용하여 지갑으로 전송된 새 토큰을 감지하고 표시합니다. MetaMask가 해당 서비스에서 데이터를 가져오는 것을 원하지 않으면 이 기능을 사용하지 마세요." - }, "useTokenDetectionPrivacyDesc": { "message": "계정으로 전송된 토큰이 자동으로 표시되도록 하려면 타사 서버와의 통신을 통해 토큰 이미지를 불러와야 합니다. 이를 위해 타사 서버는 사용자의 IP 주소에 액세스하게 됩니다." }, diff --git a/app/_locales/pt/messages.json b/app/_locales/pt/messages.json index 786bd96c3..6bb5a5db8 100644 --- a/app/_locales/pt/messages.json +++ b/app/_locales/pt/messages.json @@ -1211,9 +1211,6 @@ "failureMessage": { "message": "Ocorreu algum erro e não conseguimos concluir a ação" }, - "fakeTokenWarning": { - "message": "Qualquer um pode criar um token, incluindo versões falsas de tokens existentes. Saiba mais sobre $1" - }, "fast": { "message": "Rápido" }, @@ -3739,12 +3736,6 @@ "tokenDetectionAlertMessage": { "message": "A detecção de tokens está atualmente disponível em $1. $2" }, - "tokenDetectionAnnouncement": { - "message": "Novidade! A detecção aprimorada de token está disponível na Mainnet do Ethereum como uma funcionalidade experimental. $1" - }, - "tokenDetectionToggleDescription": { - "message": "A API de token da ConsenSys agrega uma lista de tokens de várias listas de tokens de terceiros. Se você desativá-la, não haverá detecção de novos tokens adicionados à sua carteira, mas continuará com a opção de procurar tokens para importar." - }, "tokenId": { "message": "ID do token" }, @@ -3979,12 +3970,6 @@ "usePhishingDetectionDescription": { "message": "Exibir uma advertência para os domínios de phishing destinados a usuários do Ethereum" }, - "useTokenDetection": { - "message": "Usar detecção de tokens" - }, - "useTokenDetectionDescription": { - "message": "Utilizamos APIs terceirizadas para detectar e exibir novos tokens enviados à sua carteira. Desative essa opção se não deseja que a MetaMask extraia dados desses serviços." - }, "useTokenDetectionPrivacyDesc": { "message": "A exibição automática de tokens enviados para a sua conta envolve a comunicação com servidores de terceiros para buscar as imagens dos tokens. Esses servidores terão acesso ao seu endereço IP." }, diff --git a/app/_locales/pt_BR/messages.json b/app/_locales/pt_BR/messages.json index d282b4316..5b2c10459 100644 --- a/app/_locales/pt_BR/messages.json +++ b/app/_locales/pt_BR/messages.json @@ -1031,9 +1031,6 @@ "failureMessage": { "message": "Ocorreu algum erro e não conseguimos concluir a ação" }, - "fakeTokenWarning": { - "message": "Qualquer um pode criar um token, incluindo versões falsas de tokens existentes. Saiba mais sobre $1" - }, "fast": { "message": "Rápido" }, @@ -3005,9 +3002,6 @@ "tokenDecimalFetchFailed": { "message": "A casa decimal do token é necessária." }, - "tokenDetectionAnnouncement": { - "message": "Novidade! A detecção aprimorada de token está disponível na Mainnet do Ethereum como uma funcionalidade experimental. $1" - }, "tokenId": { "message": "ID do token" }, @@ -3220,12 +3214,6 @@ "usePhishingDetectionDescription": { "message": "Exibir uma advertência para os domínios de phishing destinados a usuários do Ethereum" }, - "useTokenDetection": { - "message": "Usar detecção de tokens" - }, - "useTokenDetectionDescription": { - "message": "Utilizamos APIs terceirizadas para detectar e exibir novos tokens enviados à sua carteira. Desative essa opção se não deseja que a MetaMask extraia dados desses serviços." - }, "usedByClients": { "message": "Usado por diversos clientes diferentes" }, diff --git a/app/_locales/ru/messages.json b/app/_locales/ru/messages.json index 9d0bc5acf..5fa845bc6 100644 --- a/app/_locales/ru/messages.json +++ b/app/_locales/ru/messages.json @@ -1211,9 +1211,6 @@ "failureMessage": { "message": "Что-то пошло не так, и мы не смогли завершить действие" }, - "fakeTokenWarning": { - "message": "Кто угодно может создать токен, в том числе создать поддельные версии существующих токенов. Узнайте подробнее о $1" - }, "fast": { "message": "Быстрый" }, @@ -3739,12 +3736,6 @@ "tokenDetectionAlertMessage": { "message": "Обнаружение токена в настоящее время доступно на $1. $2" }, - "tokenDetectionAnnouncement": { - "message": "Новинка! Улучшенное обнаружение токенов доступно в сети Ethereum Mainnet в качестве экспериментальной функции. $1" - }, - "tokenDetectionToggleDescription": { - "message": "API токенов ConsenSys объединяет список токенов из различных списков сторонних токенов. Отключение этого параметра прекратит обнаружение новых токенов, добавляемых в ваш кошелек, но сохранит возможность поиска токенов для импорта." - }, "tokenId": { "message": "Ид. токена" }, @@ -3979,12 +3970,6 @@ "usePhishingDetectionDescription": { "message": "Показывать предупреждение для фишинговых доменов, нацеленных на пользователей Ethereum" }, - "useTokenDetection": { - "message": "Использовать обнаружение токенов" - }, - "useTokenDetectionDescription": { - "message": "Мы используем сторонние API для обнаружения и отображения новых токенов, отправленных в ваш кошелек. Отключите, если не хотите, чтобы MetaMask получал данные от этих служб." - }, "useTokenDetectionPrivacyDesc": { "message": "Автоматическое отображение токенов, отправленных на ваш счет, требует обмена данными со сторонними серверами для получения изображений токенов. Эти серверы получат доступ к вашему IP-адресу." }, diff --git a/app/_locales/tl/messages.json b/app/_locales/tl/messages.json index 969f244e4..5794d8b9a 100644 --- a/app/_locales/tl/messages.json +++ b/app/_locales/tl/messages.json @@ -1211,9 +1211,6 @@ "failureMessage": { "message": "Nagkaproblema, at hindi namin makumpleto ang aksyon" }, - "fakeTokenWarning": { - "message": "Sinuman ay maaaring gumawa ng token, kabilang ang paggawa ng mga pekeng bersyon ng mga umiiral na token. Alamin pa ang tungkol sa $1" - }, "fast": { "message": "Mabilis" }, @@ -3739,12 +3736,6 @@ "tokenDetectionAlertMessage": { "message": "Ang pagtukoy ng token ay kasalukuyang magagamit sa $1. $2" }, - "tokenDetectionAnnouncement": { - "message": "Bago! Ang pinahusay na pagtukoy ng token ay magagamit sa Ethereum Mainnet bilang isang pang-eksperimentong feature. $1" - }, - "tokenDetectionToggleDescription": { - "message": "Pinagsasama-sama ng ConsenSys’ token API ang listahan ng mga token mula sa maraming listahan ng token ng third party. Ang pag-off dito ay magpapahinto sa pagtuklas ng mga bagong token na madadagdag sa iyong wallet, ngunit pananatilihin ang opsyon sa paghahanap ng mga token para i-import." - }, "tokenId": { "message": "Token ID" }, @@ -3979,12 +3970,6 @@ "usePhishingDetectionDescription": { "message": "Magpakita ng babala para sa mga phishing domain na nagta-target sa mga user ng Ethereum" }, - "useTokenDetection": { - "message": "Gamitin ang Pag-detect ng Token" - }, - "useTokenDetectionDescription": { - "message": "Gumagamit kami ng mga third-party na API para makita at magpakita ng mga bagong token na ipinadala sa iyong wallet. I-off kung ayaw mong makuha ng MetaMask ang data mula sa mga serbisyong iyon." - }, "useTokenDetectionPrivacyDesc": { "message": "Awtomatikong ipinapakita ang mga token na ipinadala sa iyong account na nakapaloob sa komunikasyon ng mga server ng third party para makuha ang mga larawan ng token. Ang mga server na iyon ay magkakaroon ng access sa iyong IP address." }, diff --git a/app/_locales/tr/messages.json b/app/_locales/tr/messages.json index dc05c9c16..7cde4ebc0 100644 --- a/app/_locales/tr/messages.json +++ b/app/_locales/tr/messages.json @@ -1211,9 +1211,6 @@ "failureMessage": { "message": "Bir şeyler ters gitti ve işlemi tamamlayamadık" }, - "fakeTokenWarning": { - "message": "Mevcut tokenlerin sahteleri de dahil olmak üzere herkes bir token oluşturabilir. $1 hakkında daha fazla bilgi edinin" - }, "fast": { "message": "Hızlı" }, @@ -3739,12 +3736,6 @@ "tokenDetectionAlertMessage": { "message": "Token algılama şu anda $1 üzerinden kullanılabilir. $2" }, - "tokenDetectionAnnouncement": { - "message": "Yeni! Deney aşamasında olan bir özellik olarak Ethereum Mainnet'te gelişmiş token algılama mevcut. $1" - }, - "tokenDetectionToggleDescription": { - "message": "ConsenSys'in token API'si, çeşitli üçüncü taraf token listelerinden token'ların bir listesini toplar. Kapatmak, cüzdanına eklenen yeni token'ları algılamayı durduracak, ancak içe aktarılacak token'ları arama seçeneğini koruyacaktır." - }, "tokenId": { "message": "Token Kimliği" }, @@ -3979,12 +3970,6 @@ "usePhishingDetectionDescription": { "message": "Ethereum kullanıcılarını hedefleyen kimlik avı alanları için bir uyarı görüntüler" }, - "useTokenDetection": { - "message": "Token Algılama Kullan" - }, - "useTokenDetectionDescription": { - "message": "Cüzdanınıza gönderilen yeni tokenleri algılamak ve görüntülemek için üçüncü taraf API'leri kullanıyoruz. MetaMask tarafından bu hizmetlerden veri çekilmesini istemiyorsanız bunu kapatın." - }, "useTokenDetectionPrivacyDesc": { "message": "Hesabına gönderilen token'ların otomatik olarak görüntülenmesi, token'ın görüntülerini almak için üçüncü taraf sunucularla iletişimi içerir. Bu servislerin IP adresine erişimi olacaktır." }, diff --git a/app/_locales/vi/messages.json b/app/_locales/vi/messages.json index a447533e3..37791ad53 100644 --- a/app/_locales/vi/messages.json +++ b/app/_locales/vi/messages.json @@ -1211,9 +1211,6 @@ "failureMessage": { "message": "Đã xảy ra sự cố và chúng tôi không thể hoàn tất hành động" }, - "fakeTokenWarning": { - "message": "Bất kỳ ai cũng có thể tạo token, bao gồm cả phiên bản giả mạo của các token hiện tại. Tìm hiểu thêm về $1" - }, "fast": { "message": "Nhanh" }, @@ -3739,12 +3736,6 @@ "tokenDetectionAlertMessage": { "message": "Tính năng phát hiện token hiện có sẵn trên $1. $2" }, - "tokenDetectionAnnouncement": { - "message": "Mới! Tính năng phát hiện token được cải tiến hiện đã có sẵn trên Mạng chính thức của Ethereum dưới dạng một tính năng thử nghiệm. $1" - }, - "tokenDetectionToggleDescription": { - "message": "API token của ConsenSys sẽ tổng hợp danh sách token từ các danh sách token của nhiều bên thứ ba khác nhau. Tắt tính năng này sẽ ngừng phát hiện token mới được thêm vào ví của bạn, nhưng sẽ giữ lại tùy chọn tìm kiếm token để nhập." - }, "tokenId": { "message": "ID Token" }, @@ -3979,12 +3970,6 @@ "usePhishingDetectionDescription": { "message": "Hiển thị cảnh báo đối với các tên miền lừa đảo nhắm đến người dùng Ethereum" }, - "useTokenDetection": { - "message": "Sử Dụng Phát Hiện Token" - }, - "useTokenDetectionDescription": { - "message": "Chúng tôi sử dụng API của bên thứ ba để phát hiện và hiển thị các token mới được gửi vào ví của bạn. Hãy tắt tính năng này nếu bạn không muốn MetaMask lấy dữ liệu từ các dịch vụ đó." - }, "useTokenDetectionPrivacyDesc": { "message": "Tự động hiển thị các token được gửi vào tài khoản của bạn có liên quan đến hoạt động trao đổi thông tin với các máy chủ bên thứ ba để tìm nạp hình ảnh của token. Các máy chủ đó sẽ có quyền truy cập vào địa chỉ IP của bạn." }, diff --git a/app/_locales/zh/messages.json b/app/_locales/zh/messages.json index 274abcb26..37d0db906 100644 --- a/app/_locales/zh/messages.json +++ b/app/_locales/zh/messages.json @@ -1214,9 +1214,6 @@ "failureMessage": { "message": "出了点问题,我们无法完成此操作" }, - "fakeTokenWarning": { - "message": "任何人都可以创建代币,包括创建现有代币的虚假版本。了解关于 $ 的更多详情" - }, "fast": { "message": "快" }, @@ -3745,9 +3742,6 @@ "tokenDetectionAlertMessage": { "message": "代币检测目前适用于 $1. $2" }, - "tokenDetectionAnnouncement": { - "message": "新功能!以太坊主网上提供了经过改进的代币检测作为实验功能。$1" - }, "tokenDetectionToggleDescription": { "message": "ConsenSys的代币API使用来自各种第三方的代币列表,汇总成一个代币列表。关闭它将会停止检测添加到您钱包中的新代币,但会保留搜索代币以导入的选项。" }, @@ -3985,12 +3979,6 @@ "usePhishingDetectionDescription": { "message": "显示针对 Ethereum 用户的网络钓鱼域名警告" }, - "useTokenDetection": { - "message": "使用代币检测" - }, - "useTokenDetectionDescription": { - "message": "我们使用第三方 API 来检测和显示发送到您钱包的新代币。如果您不希望 MetaMask 从这些服务中提取数据,请关闭。" - }, "useTokenDetectionPrivacyDesc": { "message": "要自动显示发送到您账户的代币,需要与第三方服务器通信以获取代币的图像。这些服务器将拥有您的IP地址的访问权限。" }, diff --git a/app/_locales/zh_CN/messages.json b/app/_locales/zh_CN/messages.json index 09dc5fb76..147485e81 100644 --- a/app/_locales/zh_CN/messages.json +++ b/app/_locales/zh_CN/messages.json @@ -1017,9 +1017,6 @@ "failureMessage": { "message": "出了点问题,我们无法完成这个操作。" }, - "fakeTokenWarning": { - "message": "任何人都可以创建代币,包括创建现有代币的假版本。了解更多关于 $1" - }, "fast": { "message": "快" }, @@ -2966,9 +2963,6 @@ "tokenDecimalFetchFailed": { "message": "需要代币十进制。" }, - "tokenDetectionAnnouncement": { - "message": "新功能!改进的代币检测可以作为实验功能在Ethereum Mainnet上进行。$1" - }, "tokenSymbol": { "message": "代币符号" }, @@ -3178,12 +3172,6 @@ "usePhishingDetectionDescription": { "message": "显示针对 Ethereum 用户钓鱼域名的警告。" }, - "useTokenDetection": { - "message": "使用代币检测" - }, - "useTokenDetectionDescription": { - "message": "我们使用第三方API来检测和显示发送到您钱包的新代币。 如果您不想从这些服务中拉取数据,请关闭" - }, "usedByClients": { "message": "可用于各种不同的客户端" }, diff --git a/app/scripts/controllers/detect-tokens.js b/app/scripts/controllers/detect-tokens.js index 806e87155..daef5c7ac 100644 --- a/app/scripts/controllers/detect-tokens.js +++ b/app/scripts/controllers/detect-tokens.js @@ -1,9 +1,8 @@ import Web3 from 'web3'; import { warn } from 'loglevel'; -import SINGLE_CALL_BALANCES_ABI from 'single-call-balance-checker-abi'; -import { SINGLE_CALL_BALANCES_ADDRESS } from '../constants/contracts'; import { MINUTE } from '../../../shared/constants/time'; import { MAINNET_CHAIN_ID } from '../../../shared/constants/network'; +import { STATIC_MAINNET_TOKEN_LIST } from '../../../shared/constants/tokens'; import { isTokenDetectionEnabledForNetwork } from '../../../shared/modules/network.utils'; import { isEqualCaseInsensitive } from '../../../shared/modules/string-utils'; import { @@ -50,14 +49,15 @@ export default class DetectTokensController { this.network = network; this.keyringMemStore = keyringMemStore; this.tokenList = tokenList; + this.useTokenDetection = + this.preferences?.store.getState().useTokenDetection; this.selectedAddress = this.preferences?.store.getState().selectedAddress; this.tokenAddresses = this.tokensController?.state.tokens.map((token) => { return token.address; }); this.hiddenTokens = this.tokensController?.state.ignoredTokens; - this.detectedTokens = process.env.TOKEN_DETECTION_V2 - ? this.tokensController?.state.detectedTokens - : []; + this.detectedTokens = this.tokensController?.state.detectedTokens; + this.chainId = this.getChainIdFromNetworkStore(network); this._trackMetaMetricsEvent = trackMetaMetricsEvent; preferences?.store.subscribe(({ selectedAddress, useTokenDetection }) => { @@ -76,32 +76,11 @@ export default class DetectTokensController { return token.address; }); this.hiddenTokens = ignoredTokens; - this.detectedTokens = process.env.TOKEN_DETECTION_V2 - ? detectedTokens - : []; + this.detectedTokens = detectedTokens; }, ); } - /** - * TODO: Remove during TOKEN_DETECTION_V2 feature flag clean up - * - * @param tokens - */ - async _getTokenBalances(tokens) { - const ethContract = this.web3.eth - .contract(SINGLE_CALL_BALANCES_ABI) - .at(SINGLE_CALL_BALANCES_ADDRESS); - return new Promise((resolve, reject) => { - ethContract.balances([this.selectedAddress], tokens, (error, result) => { - if (error) { - return reject(error); - } - return resolve(result); - }); - }); - } - /** * For each token in the tokenlist provided by the TokenListController, check selectedAddress balance. */ @@ -110,31 +89,33 @@ export default class DetectTokensController { return; } if ( - process.env.TOKEN_DETECTION_V2 && - (!this.useTokenDetection || - !isTokenDetectionEnabledForNetwork( - this._network.store.getState().provider.chainId, - )) + !isTokenDetectionEnabledForNetwork( + this.getChainIdFromNetworkStore(this._network), + ) ) { return; } - const { tokenList } = this._tokenList.state; - // since the token detection is currently enabled only on Mainnet - // we can use the chainId check to ensure token detection is not triggered for any other network - // but once the balance check contract for other networks are deploayed and ready to use, we need to update this check. if ( - !process.env.TOKEN_DETECTION_V2 && - (this._network.store.getState().provider.chainId !== MAINNET_CHAIN_ID || - Object.keys(tokenList).length === 0) + !this.useTokenDetection && + this.getChainIdFromNetworkStore(this._network) !== MAINNET_CHAIN_ID ) { return; } + const isTokenDetectionInactiveInMainnet = + !this.useTokenDetection && + this.getChainIdFromNetworkStore(this._network) === MAINNET_CHAIN_ID; + const { tokenList } = this._tokenList.state; + + const tokenListUsed = isTokenDetectionInactiveInMainnet + ? STATIC_MAINNET_TOKEN_LIST + : tokenList; + const tokensToDetect = []; this.web3.setProvider(this._network._provider); - for (const tokenAddress in tokenList) { + for (const tokenAddress in tokenListUsed) { if ( - !this.tokenAddresses.find((address) => + !this.tokenAddresses.find(({ address }) => isEqualCaseInsensitive(address, tokenAddress), ) && !this.hiddenTokens.find((address) => @@ -154,12 +135,10 @@ export default class DetectTokensController { for (const tokensSlice of sliceOfTokensToDetect) { let result; try { - result = process.env.TOKEN_DETECTION_V2 - ? await this.assetsContractController.getBalancesInSingleCall( - this.selectedAddress, - tokensSlice, - ) - : await this._getTokenBalances(tokensSlice); + result = await this.assetsContractController.getBalancesInSingleCall( + this.selectedAddress, + tokensSlice, + ); } catch (error) { warn( `MetaMask - DetectTokensController single call balance fetch failed`, @@ -168,53 +147,36 @@ export default class DetectTokensController { return; } - let tokensWithBalance = []; - if (process.env.TOKEN_DETECTION_V2) { - const eventTokensDetails = []; - if (result) { - const nonZeroTokenAddresses = Object.keys(result); - for (const nonZeroTokenAddress of nonZeroTokenAddresses) { - const { address, symbol, decimals, iconUrl, aggregators } = - tokenList[nonZeroTokenAddress]; + const tokensWithBalance = []; + const eventTokensDetails = []; + if (result) { + const nonZeroTokenAddresses = Object.keys(result); + for (const nonZeroTokenAddress of nonZeroTokenAddresses) { + const { address, symbol, decimals, aggregators } = + tokenListUsed[nonZeroTokenAddress]; - eventTokensDetails.push(`${symbol} - ${address}`); + eventTokensDetails.push(`${symbol} - ${address}`); - tokensWithBalance.push({ - address, - symbol, - decimals, - image: iconUrl, - aggregators, - }); - } + tokensWithBalance.push({ + address, + symbol, + decimals, + aggregators, + }); + } - if (tokensWithBalance.length > 0) { - this._trackMetaMetricsEvent({ - event: EVENT_NAMES.TOKEN_DETECTED, - category: EVENT.CATEGORIES.WALLET, - properties: { - tokens: eventTokensDetails, - token_standard: TOKEN_STANDARDS.ERC20, - asset_type: ASSET_TYPES.TOKEN, - }, - }); - await this.tokensController.addDetectedTokens(tokensWithBalance); - } + if (tokensWithBalance.length > 0) { + this._trackMetaMetricsEvent({ + event: EVENT_NAMES.TOKEN_DETECTED, + category: EVENT.CATEGORIES.WALLET, + properties: { + tokens: eventTokensDetails, + token_standard: TOKEN_STANDARDS.ERC20, + asset_type: ASSET_TYPES.TOKEN, + }, + }); + await this.tokensController.addDetectedTokens(tokensWithBalance); } - } else { - tokensWithBalance = tokensSlice.filter((_, index) => { - const balance = result[index]; - return balance && !balance.isZero(); - }); - await Promise.all( - tokensWithBalance.map((tokenAddress) => { - return this.tokensController.addToken( - tokenAddress, - tokenList[tokenAddress].symbol, - tokenList[tokenAddress].decimals, - ); - }), - ); } } } @@ -232,6 +194,10 @@ export default class DetectTokensController { this.interval = DEFAULT_INTERVAL; } + getChainIdFromNetworkStore(network) { + return network?.store.getState().provider.chainId; + } + /* eslint-disable accessor-pairs */ /** * @type {number} @@ -255,6 +221,12 @@ export default class DetectTokensController { } this._network = network; this.web3 = new Web3(network._provider); + this._network.store.subscribe(() => { + if (this.chainId !== this.getChainIdFromNetworkStore(network)) { + this.restartTokenDetection(); + this.chainId = this.getChainIdFromNetworkStore(network); + } + }); } /** diff --git a/app/scripts/controllers/detect-tokens.test.js b/app/scripts/controllers/detect-tokens.test.js index 9c52c2e0a..4f1e0334a 100644 --- a/app/scripts/controllers/detect-tokens.test.js +++ b/app/scripts/controllers/detect-tokens.test.js @@ -7,24 +7,23 @@ import { ControllerMessenger, TokenListController, TokensController, + AssetsContractController, } from '@metamask/controllers'; -import { - MAINNET, - MAINNET_NETWORK_ID, - ROPSTEN, -} from '../../../shared/constants/network'; +import { MAINNET, ROPSTEN } from '../../../shared/constants/network'; import { toChecksumHexAddress } from '../../../shared/modules/hexstring-utils'; import DetectTokensController from './detect-tokens'; import NetworkController from './network'; import PreferencesController from './preferences'; -const tokenIconsApiBaseUrl = - 'https://static.metaswap.codefi.network/api/v1/tokenIcons'; - describe('DetectTokensController', function () { - let tokenListController; const sandbox = sinon.createSandbox(); - let keyringMemStore, network, preferences, provider, tokensController; + let assetsContractController, + keyringMemStore, + network, + preferences, + provider, + tokensController, + tokenListController; const noop = () => undefined; @@ -38,18 +37,44 @@ describe('DetectTokensController', function () { network.setInfuraProjectId('foo'); network.initializeProvider(networkControllerProviderConfig); provider = network.getProviderAndBlockTracker().provider; - preferences = new PreferencesController({ network, provider }); - tokensController = new TokensController({ - onPreferencesStateChange: preferences.store.subscribe.bind( - preferences.store, - ), - onNetworkStateChange: network.store.subscribe.bind(network.store), + + const tokenListMessenger = new ControllerMessenger().getRestricted({ + name: 'TokenListController', + }); + tokenListController = new TokenListController({ + chainId: '1', + preventPollingOnNetworkRestart: false, + onNetworkStateChange: sinon.spy(), + onPreferencesStateChange: sinon.spy(), + messenger: tokenListMessenger, + }); + await tokenListController.start(); + + preferences = new PreferencesController({ + network, + provider, + tokenListController, }); preferences.setAddresses([ '0x7e57e2', '0xbc86727e770de68b1060c91f6bb6945c73e10388', ]); preferences.setUseTokenDetection(true); + + tokensController = new TokensController({ + onPreferencesStateChange: preferences.store.subscribe.bind( + preferences.store, + ), + onNetworkStateChange: network.store.subscribe.bind(network.store), + }); + + assetsContractController = new AssetsContractController({ + onPreferencesStateChange: preferences.store.subscribe.bind( + preferences.store, + ), + onNetworkStateChange: network.store.subscribe.bind(network.store), + }); + sandbox .stub(network, 'getLatestBlock') .callsFake(() => Promise.resolve({})); @@ -129,16 +154,6 @@ describe('DetectTokensController', function () { .get(`/tokens/3`) .reply(200, { error: 'ChainId 3 is not supported' }) .persist(); - const tokenListMessenger = new ControllerMessenger().getRestricted({ - name: 'TokenListController', - }); - tokenListController = new TokenListController({ - chainId: '1', - onNetworkStateChange: sinon.spy(), - onPreferencesStateChange: sinon.spy(), - messenger: tokenListMessenger, - }); - await tokenListController.start(); }); after(function () { @@ -161,6 +176,7 @@ describe('DetectTokensController', function () { keyringMemStore, tokenList: tokenListController, tokensController, + assetsContractController, }); controller.isOpen = true; controller.isUnlocked = true; @@ -177,7 +193,7 @@ describe('DetectTokensController', function () { sandbox.assert.calledThrice(stub); }); - it('should not check tokens while on test network', async function () { + it('should not check and add tokens while on unsupported networks', async function () { sandbox.useFakeTimers(); network.setProviderType(ROPSTEN); const tokenListMessengerRopsten = new ControllerMessenger().getRestricted({ @@ -196,17 +212,21 @@ describe('DetectTokensController', function () { keyringMemStore, tokenList: tokenListController, tokensController, + assetsContractController, }); controller.isOpen = true; controller.isUnlocked = true; - const stub = sandbox.stub(controller, '_getTokenBalances'); + const stub = sandbox.stub( + assetsContractController, + 'getBalancesInSingleCall', + ); await controller.detectNewTokens(); sandbox.assert.notCalled(stub); }); - it('should skip adding tokens listed in hiddenTokens array', async function () { + it('should skip adding tokens listed in ignoredTokens array', async function () { sandbox.useFakeTimers(); network.setProviderType(MAINNET); const controller = new DetectTokensController({ @@ -215,53 +235,49 @@ describe('DetectTokensController', function () { keyringMemStore, tokenList: tokenListController, tokensController, + assetsContractController, + trackMetaMetricsEvent: noop, }); controller.isOpen = true; controller.isUnlocked = true; const { tokenList } = tokenListController.state; - const erc20ContractAddresses = Object.keys(tokenList); + const tokenValues = Object.values(tokenList); - const existingTokenAddress = erc20ContractAddresses[0]; - const existingToken = tokenList[existingTokenAddress]; - await tokensController.addToken( - existingTokenAddress, - existingToken.symbol, - existingToken.decimals, - ); - - const tokenAddressToSkip = erc20ContractAddresses[1]; - const tokenToSkip = tokenList[tokenAddressToSkip]; - await tokensController.addToken( - tokenAddressToSkip, - tokenToSkip.symbol, - tokenToSkip.decimals, - ); + await tokensController.addDetectedTokens([ + { + address: tokenValues[0].address, + symbol: tokenValues[0].symbol, + decimals: tokenValues[0].decimals, + aggregators: tokenValues[0].aggregators, + image: undefined, + isERC721: undefined, + }, + ]); sandbox - .stub(controller, '_getTokenBalances') + .stub(assetsContractController, 'getBalancesInSingleCall') .callsFake((tokensToDetect) => tokensToDetect.map((token) => - token === tokenAddressToSkip ? new BigNumber(10) : 0, + token.address === tokenValues[1].address ? new BigNumber(10) : 0, ), ); + await tokensController.ignoreTokens([tokenValues[1].address]); - await tokensController.ignoreTokens([tokenAddressToSkip]); await controller.detectNewTokens(); - - assert.deepEqual(tokensController.state.tokens, [ + assert.deepEqual(tokensController.state.detectedTokens, [ { - address: toChecksumHexAddress(existingTokenAddress), - decimals: existingToken.decimals, - symbol: existingToken.symbol, - aggregators: [], - image: `${tokenIconsApiBaseUrl}/${MAINNET_NETWORK_ID}/${existingTokenAddress}.png`, - isERC721: false, + address: toChecksumHexAddress(tokenValues[0].address), + decimals: tokenValues[0].decimals, + symbol: tokenValues[0].symbol, + aggregators: tokenValues[0].aggregators, + image: undefined, + isERC721: undefined, }, ]); }); - it('should check and add tokens while on main network', async function () { + it('should check and add tokens while on supported networks', async function () { sandbox.useFakeTimers(); network.setProviderType(MAINNET); const controller = new DetectTokensController({ @@ -270,6 +286,8 @@ describe('DetectTokensController', function () { keyringMemStore, tokenList: tokenListController, tokensController, + assetsContractController, + trackMetaMetricsEvent: noop, }); controller.isOpen = true; controller.isUnlocked = true; @@ -279,107 +297,41 @@ describe('DetectTokensController', function () { const existingTokenAddress = erc20ContractAddresses[0]; const existingToken = tokenList[existingTokenAddress]; - await tokensController.addToken( - existingTokenAddress, - existingToken.symbol, - existingToken.decimals, - ); - - const tokenAddressToAdd = erc20ContractAddresses[1]; - const tokenToAdd = tokenList[tokenAddressToAdd]; - - const contractAddressesToDetect = erc20ContractAddresses.filter( - (address) => address !== existingTokenAddress, - ); - const indexOfTokenToAdd = - contractAddressesToDetect.indexOf(tokenAddressToAdd); - const balances = new Array(contractAddressesToDetect.length); - - balances[indexOfTokenToAdd] = new BigNumber(10); - - sandbox - .stub(controller, '_getTokenBalances') - .returns(Promise.resolve(balances)); - await controller.detectNewTokens(); - assert.deepEqual(tokensController.state.tokens, [ + await tokensController.addDetectedTokens([ { - address: toChecksumHexAddress(existingTokenAddress), - decimals: existingToken.decimals, + address: existingToken.address, symbol: existingToken.symbol, - isERC721: false, - aggregators: [], - image: `${tokenIconsApiBaseUrl}/${MAINNET_NETWORK_ID}/${existingTokenAddress}.png`, - }, - { - address: toChecksumHexAddress(tokenAddressToAdd), - decimals: tokenToAdd.decimals, - symbol: tokenToAdd.symbol, - isERC721: false, - aggregators: [], - image: `${tokenIconsApiBaseUrl}/${MAINNET_NETWORK_ID}/${tokenAddressToAdd}.png`, + decimals: existingToken.decimals, + aggregators: existingToken.aggregators, + image: undefined, + isERC721: undefined, }, ]); - }); - - it('should check and add tokens while on non-default Mainnet', async function () { - sandbox.useFakeTimers(); - network.setRpcTarget('https://some-fake-RPC-endpoint.metamask.io', '0x1'); - const controller = new DetectTokensController({ - preferences, - network, - keyringMemStore, - tokenList: tokenListController, - tokensController, - }); - controller.isOpen = true; - controller.isUnlocked = true; - - const { tokenList } = tokenListController.state; - const erc20ContractAddresses = Object.keys(tokenList); - - const existingTokenAddress = erc20ContractAddresses[0]; - const existingToken = tokenList[existingTokenAddress]; - await tokensController.addToken( - existingTokenAddress, - existingToken.symbol, - existingToken.decimals, - ); - const tokenAddressToAdd = erc20ContractAddresses[1]; const tokenToAdd = tokenList[tokenAddressToAdd]; - - const contractAddressesToDetect = erc20ContractAddresses.filter( - (address) => address !== existingTokenAddress, - ); - const indexOfTokenToAdd = - contractAddressesToDetect.indexOf(tokenAddressToAdd); - - const balances = new Array(contractAddressesToDetect.length); - balances[indexOfTokenToAdd] = new BigNumber(10); - sandbox - .stub(controller, '_getTokenBalances') - .returns(Promise.resolve(balances)); - + .stub(assetsContractController, 'getBalancesInSingleCall') + .callsFake(() => + Promise.resolve({ [tokenAddressToAdd]: new BigNumber(10) }), + ); await controller.detectNewTokens(); - - assert.deepEqual(tokensController.state.tokens, [ + assert.deepEqual(tokensController.state.detectedTokens, [ { address: toChecksumHexAddress(existingTokenAddress), decimals: existingToken.decimals, symbol: existingToken.symbol, - image: `${tokenIconsApiBaseUrl}/${MAINNET_NETWORK_ID}/${existingTokenAddress}.png`, - isERC721: false, - aggregators: [], + aggregators: existingToken.aggregators, + image: undefined, + isERC721: undefined, }, { address: toChecksumHexAddress(tokenAddressToAdd), decimals: tokenToAdd.decimals, symbol: tokenToAdd.symbol, - image: `${tokenIconsApiBaseUrl}/${MAINNET_NETWORK_ID}/${tokenAddressToAdd}.png`, - isERC721: false, - aggregators: [], + aggregators: tokenToAdd.aggregators, + image: undefined, + isERC721: undefined, }, ]); }); @@ -392,6 +344,7 @@ describe('DetectTokensController', function () { keyringMemStore, tokenList: tokenListController, tokensController, + assetsContractController, }); controller.isOpen = true; controller.isUnlocked = true; @@ -410,6 +363,7 @@ describe('DetectTokensController', function () { keyringMemStore, tokenList: tokenListController, tokensController, + assetsContractController, }); controller.isOpen = true; controller.selectedAddress = '0x0'; @@ -427,10 +381,14 @@ describe('DetectTokensController', function () { keyringMemStore, tokenList: tokenListController, tokensController, + assetsContractController, }); controller.isOpen = true; controller.isUnlocked = false; - const stub = sandbox.stub(controller, '_getTokenBalances'); + const stub = sandbox.stub( + assetsContractController, + 'getBalancesInSingleCall', + ); clock.tick(180000); sandbox.assert.notCalled(stub); }); @@ -443,6 +401,7 @@ describe('DetectTokensController', function () { network, keyringMemStore, tokensController, + assetsContractController, }); // trigger state update from preferences controller await preferences.setSelectedAddress( @@ -450,7 +409,10 @@ describe('DetectTokensController', function () { ); controller.isOpen = false; controller.isUnlocked = true; - const stub = sandbox.stub(controller, '_getTokenBalances'); + const stub = sandbox.stub( + assetsContractController, + 'getBalancesInSingleCall', + ); clock.tick(180000); sandbox.assert.notCalled(stub); }); diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js index c1a1d2c7b..18ac909a7 100644 --- a/app/scripts/controllers/preferences.js +++ b/app/scripts/controllers/preferences.js @@ -38,7 +38,7 @@ export default class PreferencesController { // set to true means the dynamic list from the API is being used // set to false will be using the static list from contract-metadata - useTokenDetection: Boolean(process.env.TOKEN_DETECTION_V2), + useTokenDetection: false, useCollectibleDetection: false, openSeaEnabled: false, advancedGasFee: null, @@ -79,6 +79,7 @@ export default class PreferencesController { this.store.setMaxListeners(12); this.openPopup = opts.openPopup; this.migrateAddressBookState = opts.migrateAddressBookState; + this.tokenListController = opts.tokenListController; this._subscribeToInfuraAvailability(); @@ -131,6 +132,13 @@ export default class PreferencesController { */ setUseTokenDetection(val) { this.store.updateState({ useTokenDetection: val }); + this.tokenListController.updatePreventPollingOnNetworkRestart(!val); + if (val) { + this.tokenListController.start(); + } else { + this.tokenListController.clearingTokenListData(); + this.tokenListController.stop(); + } } /** diff --git a/app/scripts/controllers/preferences.test.js b/app/scripts/controllers/preferences.test.js index 407b95b0e..8696f7997 100644 --- a/app/scripts/controllers/preferences.test.js +++ b/app/scripts/controllers/preferences.test.js @@ -1,5 +1,9 @@ import { strict as assert } from 'assert'; import sinon from 'sinon'; +import { + ControllerMessenger, + TokenListController, +} from '@metamask/controllers'; import { MAINNET_CHAIN_ID } from '../../../shared/constants/network'; import PreferencesController from './preferences'; import NetworkController from './network'; @@ -9,6 +13,7 @@ describe('preferences controller', function () { let network; let currentChainId; let provider; + let tokenListController; const migrateAddressBookState = sinon.stub(); beforeEach(function () { @@ -21,6 +26,16 @@ describe('preferences controller', function () { network.setInfuraProjectId('foo'); network.initializeProvider(networkControllerProviderConfig); provider = network.getProviderAndBlockTracker().provider; + const tokenListMessenger = new ControllerMessenger().getRestricted({ + name: 'TokenListController', + }); + tokenListController = new TokenListController({ + chainId: '1', + preventPollingOnNetworkRestart: false, + onNetworkStateChange: sinon.spy(), + onPreferencesStateChange: sinon.spy(), + messenger: tokenListMessenger, + }); sandbox .stub(network, 'getLatestBlock') @@ -35,6 +50,7 @@ describe('preferences controller', function () { migrateAddressBookState, network, provider, + tokenListController, }); }); diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 76bab1039..1f7d425db 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -97,6 +97,7 @@ import { } from '../../ui/helpers/utils/token-util'; import { isEqualCaseInsensitive } from '../../shared/modules/string-utils'; import { parseStandardTokenTransactionData } from '../../shared/modules/transaction.utils'; +import { STATIC_MAINNET_TOKEN_LIST } from '../../shared/constants/tokens'; import { onMessageReceived, checkForMultipleVersionsRunning, @@ -235,11 +236,35 @@ export default class MetamaskController extends EventEmitter { this.blockTracker = this.networkController.getProviderAndBlockTracker().blockTracker; + const tokenListMessenger = this.controllerMessenger.getRestricted({ + name: 'TokenListController', + }); + + this.tokenListController = new TokenListController({ + chainId: hexToDecimal(this.networkController.getCurrentChainId()), + preventPollingOnNetworkRestart: true, + onNetworkStateChange: (cb) => { + this.networkController.store.subscribe((networkState) => { + const modifiedNetworkState = { + ...networkState, + provider: { + ...networkState.provider, + chainId: hexToDecimal(networkState.provider.chainId), + }, + }; + return cb(modifiedNetworkState); + }); + }, + messenger: tokenListMessenger, + state: initState.TokenListController, + }); + this.preferencesController = new PreferencesController({ initState: initState.PreferencesController, initLangCode: opts.initLangCode, openPopup: opts.openPopup, network: this.networkController, + tokenListController: this.tokenListController, provider: this.provider, migrateAddressBookState: this.migrateAddressBookState.bind(this), }); @@ -438,27 +463,6 @@ export default class MetamaskController extends EventEmitter { }, }); - const tokenListMessenger = this.controllerMessenger.getRestricted({ - name: 'TokenListController', - }); - - this.tokenListController = new TokenListController({ - chainId: hexToDecimal(this.networkController.getCurrentChainId()), - onNetworkStateChange: (cb) => - this.networkController.store.subscribe((networkState) => { - const modifiedNetworkState = { - ...networkState, - provider: { - ...networkState.provider, - chainId: hexToDecimal(networkState.provider.chainId), - }, - }; - return cb(modifiedNetworkState); - }), - messenger: tokenListMessenger, - state: initState.TokenListController, - }); - this.phishingController = new PhishingController(); this.announcementController = new AnnouncementController( @@ -527,12 +531,16 @@ export default class MetamaskController extends EventEmitter { this.accountTracker.start(); this.incomingTransactionsController.start(); this.currencyRateController.start(); - this.tokenListController.start(); + if (this.preferencesController.store.getState().useTokenDetection) { + this.tokenListController.start(); + } } else { this.accountTracker.stop(); this.incomingTransactionsController.stop(); this.currencyRateController.stop(); - this.tokenListController.stop(); + if (this.preferencesController.store.getState().useTokenDetection) { + this.tokenListController.stop(); + } } }); @@ -753,26 +761,17 @@ export default class MetamaskController extends EventEmitter { }, }); ///: END:ONLY_INCLUDE_IN - - process.env.TOKEN_DETECTION_V2 - ? (this.detectTokensController = new DetectTokensController({ - preferences: this.preferencesController, - tokensController: this.tokensController, - assetsContractController: this.assetsContractController, - network: this.networkController, - keyringMemStore: this.keyringController.memStore, - tokenList: this.tokenListController, - trackMetaMetricsEvent: this.metaMetricsController.trackEvent.bind( - this.metaMetricsController, - ), - })) - : (this.detectTokensController = new DetectTokensController({ - preferences: this.preferencesController, - tokensController: this.tokensController, - network: this.networkController, - keyringMemStore: this.keyringController.memStore, - tokenList: this.tokenListController, - })); + this.detectTokensController = new DetectTokensController({ + preferences: this.preferencesController, + tokensController: this.tokensController, + assetsContractController: this.assetsContractController, + network: this.networkController, + keyringMemStore: this.keyringController.memStore, + tokenList: this.tokenListController, + trackMetaMetricsEvent: this.metaMetricsController.trackEvent.bind( + this.metaMetricsController, + ), + }); this.addressBookController = new AddressBookController( undefined, @@ -2244,7 +2243,14 @@ export default class MetamaskController extends EventEmitter { useTokenDetection, } = this.preferencesController.store.getState(); + const isTokenDetectionInactiveInMainnet = + !useTokenDetection && + this.networkController.store.getState().provider.chainId === + MAINNET_CHAIN_ID; const { tokenList } = this.tokenListController.state; + const caseInSensitiveTokenList = isTokenDetectionInactiveInMainnet + ? STATIC_MAINNET_TOKEN_LIST + : tokenList; const preferences = { currentLocale, @@ -2267,13 +2273,11 @@ export default class MetamaskController extends EventEmitter { checksummedAccountAddress ].filter((asset) => { if (asset.isERC721 === undefined) { - // since the token.address from allTokens is checksumaddress - // asset.address have to be changed to lowercase when we are using dynamic list - const address = useTokenDetection - ? asset.address.toLowerCase() - : asset.address; // the tokenList will be holding only erc20 tokens - if (tokenList[address] !== undefined) { + if ( + caseInSensitiveTokenList[asset.address?.toLowerCase()] !== + undefined + ) { return true; } } else if (asset.isERC721 === false) { diff --git a/lavamoat/browserify/beta/policy.json b/lavamoat/browserify/beta/policy.json index b5fb5d789..536f855b4 100644 --- a/lavamoat/browserify/beta/policy.json +++ b/lavamoat/browserify/beta/policy.json @@ -681,6 +681,7 @@ "3box>ipfs>ipfs-unixfs": true, "3box>ipfs>ipfs-unixfs-importer>async-iterator-batch": true, "3box>ipfs>ipfs-unixfs-importer>async-iterator-first": true, + "3box>ipfs>ipfs-unixfs-importer>deep-extend": true, "3box>ipfs>ipfs-unixfs-importer>rabin-wasm": true, "3box>ipfs>ipld-dag-pb": true, "3box>ipfs>ipld-raw>multihashing-async": true, @@ -691,6 +692,11 @@ "madge>rc>deep-extend": true } }, + "3box>ipfs>ipfs-unixfs-importer>deep-extend": { + "packages": { + "browserify>buffer": true + } + }, "3box>ipfs>ipfs-unixfs-importer>rabin-wasm": { "globals": { "Blob": true, diff --git a/lavamoat/browserify/flask/policy.json b/lavamoat/browserify/flask/policy.json index 1df105c5c..0bfefd182 100644 --- a/lavamoat/browserify/flask/policy.json +++ b/lavamoat/browserify/flask/policy.json @@ -681,6 +681,7 @@ "3box>ipfs>ipfs-unixfs": true, "3box>ipfs>ipfs-unixfs-importer>async-iterator-batch": true, "3box>ipfs>ipfs-unixfs-importer>async-iterator-first": true, + "3box>ipfs>ipfs-unixfs-importer>deep-extend": true, "3box>ipfs>ipfs-unixfs-importer>rabin-wasm": true, "3box>ipfs>ipld-dag-pb": true, "3box>ipfs>ipld-raw>multihashing-async": true, @@ -691,6 +692,11 @@ "madge>rc>deep-extend": true } }, + "3box>ipfs>ipfs-unixfs-importer>deep-extend": { + "packages": { + "browserify>buffer": true + } + }, "3box>ipfs>ipfs-unixfs-importer>rabin-wasm": { "globals": { "Blob": true, diff --git a/lavamoat/browserify/main/policy.json b/lavamoat/browserify/main/policy.json index b5fb5d789..536f855b4 100644 --- a/lavamoat/browserify/main/policy.json +++ b/lavamoat/browserify/main/policy.json @@ -681,6 +681,7 @@ "3box>ipfs>ipfs-unixfs": true, "3box>ipfs>ipfs-unixfs-importer>async-iterator-batch": true, "3box>ipfs>ipfs-unixfs-importer>async-iterator-first": true, + "3box>ipfs>ipfs-unixfs-importer>deep-extend": true, "3box>ipfs>ipfs-unixfs-importer>rabin-wasm": true, "3box>ipfs>ipld-dag-pb": true, "3box>ipfs>ipld-raw>multihashing-async": true, @@ -691,6 +692,11 @@ "madge>rc>deep-extend": true } }, + "3box>ipfs>ipfs-unixfs-importer>deep-extend": { + "packages": { + "browserify>buffer": true + } + }, "3box>ipfs>ipfs-unixfs-importer>rabin-wasm": { "globals": { "Blob": true, diff --git a/package.json b/package.json index 0e204f756..dc075dd67 100644 --- a/package.json +++ b/package.json @@ -121,7 +121,7 @@ "@keystonehq/metamask-airgapped-keyring": "0.2.1", "@material-ui/core": "^4.11.0", "@metamask/contract-metadata": "^1.31.0", - "@metamask/controllers": "^30.0.2", + "@metamask/controllers": "^30.1.0", "@metamask/design-tokens": "^1.8.0", "@metamask/eth-ledger-bridge-keyring": "^0.13.0", "@metamask/eth-token-tracker": "^4.0.0", diff --git a/shared/constants/tokens.js b/shared/constants/tokens.js index d430c81bc..568d89b07 100644 --- a/shared/constants/tokens.js +++ b/shared/constants/tokens.js @@ -21,3 +21,18 @@ export const LISTED_CONTRACT_ADDRESSES = Object.keys(contractMap).map( * asset. * @property {boolean} [isERC721] - True when the asset is a ERC721 token. */ +export const STATIC_MAINNET_TOKEN_LIST = Object.keys(contractMap).reduce( + (acc, base) => { + const { logo, ...tokenMetadata } = contractMap[base]; + return { + ...acc, + [base.toLowerCase()]: { + ...tokenMetadata, + address: base.toLowerCase(), + iconUrl: `images/contract/${logo}`, + aggregators: [], + }, + }; + }, + {}, +); diff --git a/test/e2e/fixtures/address-entry/state.json b/test/e2e/fixtures/address-entry/state.json index 1ce7f2d95..481be663a 100644 --- a/test/e2e/fixtures/address-entry/state.json +++ b/test/e2e/fixtures/address-entry/state.json @@ -67,6 +67,12 @@ "8": { "isShown": true }, + "10": { + "isShown": true + }, + "11": { + "isShown": true + }, "12": { "isShown": true }, diff --git a/test/e2e/fixtures/connected-state/state.json b/test/e2e/fixtures/connected-state/state.json index 11af1d9b2..3c1aa0039 100644 --- a/test/e2e/fixtures/connected-state/state.json +++ b/test/e2e/fixtures/connected-state/state.json @@ -57,6 +57,12 @@ "8": { "isShown": true }, + "10": { + "isShown": true + }, + "11": { + "isShown": true + }, "12": { "isShown": true }, diff --git a/test/e2e/fixtures/custom-rpc/state.json b/test/e2e/fixtures/custom-rpc/state.json index 02cc6d5f8..28ca1894a 100644 --- a/test/e2e/fixtures/custom-rpc/state.json +++ b/test/e2e/fixtures/custom-rpc/state.json @@ -53,6 +53,12 @@ "8": { "isShown": true }, + "10": { + "isShown": true + }, + "11": { + "isShown": true + }, "12": { "isShown": true }, diff --git a/test/e2e/fixtures/custom-token/state.json b/test/e2e/fixtures/custom-token/state.json index baf7c2984..4546a6ede 100644 --- a/test/e2e/fixtures/custom-token/state.json +++ b/test/e2e/fixtures/custom-token/state.json @@ -71,6 +71,12 @@ "8": { "isShown": true }, + "10": { + "isShown": true + }, + "11": { + "isShown": true + }, "12": { "isShown": true }, diff --git a/test/e2e/fixtures/eip-1559-v2-dapp/state.json b/test/e2e/fixtures/eip-1559-v2-dapp/state.json index dc889b1e5..373645f1f 100644 --- a/test/e2e/fixtures/eip-1559-v2-dapp/state.json +++ b/test/e2e/fixtures/eip-1559-v2-dapp/state.json @@ -54,6 +54,12 @@ "8": { "isShown": true }, + "10": { + "isShown": true + }, + "11": { + "isShown": true + }, "12": { "isShown": true }, diff --git a/test/e2e/fixtures/eip-1559-v2/state.json b/test/e2e/fixtures/eip-1559-v2/state.json index 6ac0aa7ad..1346dea32 100644 --- a/test/e2e/fixtures/eip-1559-v2/state.json +++ b/test/e2e/fixtures/eip-1559-v2/state.json @@ -54,6 +54,12 @@ "8": { "isShown": true }, + "10": { + "isShown": true + }, + "11": { + "isShown": true + }, "12": { "isShown": true }, diff --git a/test/e2e/fixtures/import-ui/state.json b/test/e2e/fixtures/import-ui/state.json index c5f3da004..3d0c40c08 100644 --- a/test/e2e/fixtures/import-ui/state.json +++ b/test/e2e/fixtures/import-ui/state.json @@ -108,6 +108,12 @@ "8": { "isShown": true }, + "10": { + "isShown": true + }, + "11": { + "isShown": true + }, "12": { "isShown": true }, diff --git a/test/e2e/fixtures/imported-account/state.json b/test/e2e/fixtures/imported-account/state.json index 7a79d293b..4506bac2d 100644 --- a/test/e2e/fixtures/imported-account/state.json +++ b/test/e2e/fixtures/imported-account/state.json @@ -53,6 +53,12 @@ "8": { "isShown": true }, + "10": { + "isShown": true + }, + "11": { + "isShown": true + }, "12": { "isShown": true }, diff --git a/test/e2e/fixtures/localization/state.json b/test/e2e/fixtures/localization/state.json index 45d09cf57..80f442a04 100644 --- a/test/e2e/fixtures/localization/state.json +++ b/test/e2e/fixtures/localization/state.json @@ -53,6 +53,12 @@ "8": { "isShown": true }, + "10": { + "isShown": true + }, + "11": { + "isShown": true + }, "12": { "isShown": true }, diff --git a/test/e2e/fixtures/metrics-enabled/state.json b/test/e2e/fixtures/metrics-enabled/state.json index 58c7c5109..a6c7d7be0 100644 --- a/test/e2e/fixtures/metrics-enabled/state.json +++ b/test/e2e/fixtures/metrics-enabled/state.json @@ -57,6 +57,12 @@ "8": { "isShown": true }, + "10": { + "isShown": true + }, + "11": { + "isShown": true + }, "12": { "isShown": true }, diff --git a/test/e2e/fixtures/navigate-transactions/state.json b/test/e2e/fixtures/navigate-transactions/state.json index e5774eed6..1d654f7ac 100644 --- a/test/e2e/fixtures/navigate-transactions/state.json +++ b/test/e2e/fixtures/navigate-transactions/state.json @@ -53,6 +53,12 @@ "8": { "isShown": true }, + "10": { + "isShown": true + }, + "11": { + "isShown": true + }, "12": { "isShown": true }, diff --git a/test/e2e/fixtures/onboarding/state.json b/test/e2e/fixtures/onboarding/state.json index 06582c443..a616fc720 100644 --- a/test/e2e/fixtures/onboarding/state.json +++ b/test/e2e/fixtures/onboarding/state.json @@ -28,6 +28,12 @@ }, "NotificationController": { "notifications": { + "10": { + "isShown": true + }, + "11": { + "isShown": true + }, "12": { "isShown": true }, diff --git a/test/e2e/fixtures/send-edit-v2/state.json b/test/e2e/fixtures/send-edit-v2/state.json index 90a8dc1d9..14bc11d12 100644 --- a/test/e2e/fixtures/send-edit-v2/state.json +++ b/test/e2e/fixtures/send-edit-v2/state.json @@ -54,6 +54,12 @@ "8": { "isShown": true }, + "10": { + "isShown": true + }, + "11": { + "isShown": true + }, "12": { "isShown": true }, diff --git a/test/e2e/fixtures/send-edit/state.json b/test/e2e/fixtures/send-edit/state.json index 19dc61b39..5606126f8 100644 --- a/test/e2e/fixtures/send-edit/state.json +++ b/test/e2e/fixtures/send-edit/state.json @@ -54,6 +54,12 @@ "8": { "isShown": true }, + "10": { + "isShown": true + }, + "11": { + "isShown": true + }, "12": { "isShown": true }, diff --git a/test/e2e/fixtures/special-settings/state.json b/test/e2e/fixtures/special-settings/state.json index 8b66b9155..668c4143f 100644 --- a/test/e2e/fixtures/special-settings/state.json +++ b/test/e2e/fixtures/special-settings/state.json @@ -135,7 +135,7 @@ "useBlockie": false, "useNonceField": false, "usePhishDetect": true, - "useTokenDetection": true + "useTokenDetection": false }, "config": {}, "firstTimeInfo": { diff --git a/test/e2e/fixtures/threebox-enabled/state.json b/test/e2e/fixtures/threebox-enabled/state.json index 549ddb635..44bc352a9 100644 --- a/test/e2e/fixtures/threebox-enabled/state.json +++ b/test/e2e/fixtures/threebox-enabled/state.json @@ -64,6 +64,12 @@ "8": { "isShown": true }, + "10": { + "isShown": true + }, + "11": { + "isShown": true + }, "12": { "isShown": true }, diff --git a/test/e2e/tests/settings-search.spec.js b/test/e2e/tests/settings-search.spec.js index 3f79114be..69ac23ec0 100644 --- a/test/e2e/tests/settings-search.spec.js +++ b/test/e2e/tests/settings-search.spec.js @@ -18,8 +18,8 @@ describe('Settings Search', function () { security: 'Reveal Secret', alerts: 'Browsing a website', networks: 'Ethereum Mainnet', - experimental: 'Token Detection', - about: 'Terms of use', + experimental: 'Enable Enhanced Gas Fee UI', + about: 'Terms of Use', }; it('should find element inside the General tab', async function () { diff --git a/test/e2e/tests/swap-eth.spec.js b/test/e2e/tests/swap-eth.spec.js index d764b393f..e53ba0d6d 100644 --- a/test/e2e/tests/swap-eth.spec.js +++ b/test/e2e/tests/swap-eth.spec.js @@ -14,7 +14,7 @@ describe('Swap Eth for another Token', function () { it('Completes a Swap between Eth and Matic', async function () { await withFixtures( { - fixtures: 'imported-account', + fixtures: 'special-settings', ganacheOptions, title: this.test.title, failOnConsoleError: false, diff --git a/ui/components/app/asset-list/asset-list.js b/ui/components/app/asset-list/asset-list.js index 842572066..66c882616 100644 --- a/ui/components/app/asset-list/asset-list.js +++ b/ui/components/app/asset-list/asset-list.js @@ -11,6 +11,7 @@ import { getShouldShowFiat, getNativeCurrencyImage, getDetectedTokensInCurrentNetwork, + getIstokenDetectionInactiveOnNonMainnetSupportedNetwork, } from '../../../selectors'; import { getNativeCurrency } from '../../../ducks/metamask/metamask'; import { useCurrencyDisplay } from '../../../hooks/useCurrencyDisplay'; @@ -65,6 +66,9 @@ const AssetList = ({ onClickAsset }) => { const primaryTokenImage = useSelector(getNativeCurrencyImage); const detectedTokens = useSelector(getDetectedTokensInCurrentNetwork) || []; + const istokenDetectionInactiveOnNonMainnetSupportedNetwork = useSelector( + getIstokenDetectionInactiveOnNonMainnetSupportedNetwork, + ); return ( <> @@ -92,11 +96,10 @@ const AssetList = ({ onClickAsset }) => { }); }} /> - {process.env.TOKEN_DETECTION_V2 - ? detectedTokens.length > 0 && ( - - ) - : null} + {detectedTokens.length > 0 && + !istokenDetectionInactiveOnNonMainnetSupportedNetwork && ( + + )} 0 ? 0 : 4}> { - const store = configureMockStore([])({ metamask: {} }); + const store = configureMockStore([])({ + metamask: { provider: { chainId: '0x0' } }, + }); describe('given searchForContacts', () => { const selectRecipient = () => null; diff --git a/ui/components/app/detected-token/detected-token-ignored-popover/detected-token-ignored-popover.js b/ui/components/app/detected-token/detected-token-ignored-popover/detected-token-ignored-popover.js index 6eaa9c352..341578415 100644 --- a/ui/components/app/detected-token/detected-token-ignored-popover/detected-token-ignored-popover.js +++ b/ui/components/app/detected-token/detected-token-ignored-popover/detected-token-ignored-popover.js @@ -1,5 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; +import classNames from 'classnames'; import { useI18nContext } from '../../../../hooks/useI18nContext'; import Popover from '../../../ui/popover'; @@ -8,6 +9,7 @@ import Typography from '../../../ui/typography/typography'; import { TYPOGRAPHY } from '../../../../helpers/constants/design-system'; const DetectedTokenIgnoredPopover = ({ + partiallyIgnoreDetectedTokens, onCancelIgnore, handleClearTokensSelection, }) => { @@ -34,8 +36,16 @@ const DetectedTokenIgnoredPopover = ({ return ( - {t('ignoreTokenWarning')} + {partiallyIgnoreDetectedTokens + ? t('importSelectedTokensDescription') + : t('ignoreTokenWarning')} ); }; DetectedTokenIgnoredPopover.propTypes = { + partiallyIgnoreDetectedTokens: PropTypes.bool.isRequired, onCancelIgnore: PropTypes.func.isRequired, handleClearTokensSelection: PropTypes.func.isRequired, }; diff --git a/ui/components/app/detected-token/detected-token-ignored-popover/index.scss b/ui/components/app/detected-token/detected-token-ignored-popover/index.scss index 64e0d90bd..69fa05637 100644 --- a/ui/components/app/detected-token/detected-token-ignored-popover/index.scss +++ b/ui/components/app/detected-token/detected-token-ignored-popover/index.scss @@ -7,7 +7,15 @@ margin-inline-start: 8px; } - .popover-header { - margin-inline-start: 85px; + &--ignore { + .popover-header { + margin-inline-start: 85px; + } + } + + &--import { + .popover-header { + margin-inline-start: 50px; + } } } diff --git a/ui/components/app/detected-token/detected-token.js b/ui/components/app/detected-token/detected-token.js index b08c1cdd2..5031f218e 100644 --- a/ui/components/app/detected-token/detected-token.js +++ b/ui/components/app/detected-token/detected-token.js @@ -27,7 +27,12 @@ const sortingBasedOnTokenSelection = (tokensDetected) => { // create a new object with keys 'selected', 'deselected' and group the tokens .groupBy((token) => (token.selected ? 'selected' : 'deselected')) // ditch the 'selected' property and get just the tokens' - .mapValues((group) => group.map(({ token }) => token)) + .mapValues((group) => + group.map(({ token }) => { + const { address, symbol, decimals, aggregators } = token; + return { address, symbol, decimals, aggregators }; + }), + ) // Exit the chain and get the underlying value, an object. .value() ); @@ -47,6 +52,8 @@ const DetectedToken = ({ setShowDetectedTokens }) => { ); const [showDetectedTokenIgnoredPopover, setShowDetectedTokenIgnoredPopover] = useState(false); + const [partiallyIgnoreDetectedTokens, setPartiallyIgnoreDetectedTokens] = + useState(false); const importSelectedTokens = async (selectedTokens) => { selectedTokens.forEach((importedToken) => { @@ -98,6 +105,7 @@ const DetectedToken = ({ setShowDetectedTokens }) => { }), ); setShowDetectedTokens(false); + setPartiallyIgnoreDetectedTokens(false); }; const handleTokenSelection = (token) => { @@ -116,6 +124,7 @@ const DetectedToken = ({ setShowDetectedTokens }) => { if (selectedTokens.length < detectedTokens.length) { setShowDetectedTokenIgnoredPopover(true); + setPartiallyIgnoreDetectedTokens(true); } else { await importSelectedTokens(selectedTokens); setShowDetectedTokens(false); @@ -134,6 +143,7 @@ const DetectedToken = ({ setShowDetectedTokens }) => { const onCancelIgnore = () => { setShowDetectedTokenIgnoredPopover(false); + setPartiallyIgnoreDetectedTokens(false); }; return ( @@ -142,6 +152,7 @@ const DetectedToken = ({ setShowDetectedTokens }) => { )} - {isTokenDetectionsupported && ( + {isTokenDetectionAvailable && ( <> , + , + ])} + withRightButton + useIcon + iconFillColor="var(--color-warning-default)" + /> + ) : ( - ) : ( - - {this.context.t('learnScamRisk')} - , - ])} - type="warning" - withRightButton - useIcon - iconFillColor="var(--color-warning-default)" - /> )} {!useTokenDetection && ( - history.push(`${ADVANCED_ROUTE}#token-description`) - } - > - {t('enableFromSettings')} - , - ]) - : this.context.t('tokenDetectionAnnouncement', [ - , - ]) - } + message={t('tokenDetectionAlertMessage', [ + networkName, + , + ])} withRightButton useIcon iconFillColor="var(--color-primary-default)" diff --git a/ui/pages/import-token/import-token.container.js b/ui/pages/import-token/import-token.container.js index 5d394c6b9..b223d9e5f 100644 --- a/ui/pages/import-token/import-token.container.js +++ b/ui/pages/import-token/import-token.container.js @@ -10,7 +10,10 @@ import { getRpcPrefsForCurrentProvider, getIsTokenDetectionSupported, getTokenDetectionSupportNetworkByChainId, - getIsMainnet, + getIsTokenDetectionInactiveOnMainnet, + getIsDynamicTokenListAvailable, + getIstokenDetectionInactiveOnNonMainnetSupportedNetwork, + getTokenList, } from '../../selectors/selectors'; import ImportToken from './import-token.component'; @@ -22,16 +25,15 @@ const mapStateToProps = (state) => { pendingTokens, provider: { chainId }, useTokenDetection, - tokenList, selectedAddress, }, } = state; - const tokenDetectionV2Supported = - process.env.TOKEN_DETECTION_V2 && getIsTokenDetectionSupported(state); + const isTokenDetectionInactiveOnMainnet = + getIsTokenDetectionInactiveOnMainnet(state); const showSearchTab = - getIsMainnet(state) || - tokenDetectionV2Supported || + getIsTokenDetectionSupported(state) || + isTokenDetectionInactiveOnMainnet || Boolean(process.env.IN_TEST); return { @@ -42,11 +44,13 @@ const mapStateToProps = (state) => { showSearchTab, chainId, rpcPrefs: getRpcPrefsForCurrentProvider(state), - tokenList, + tokenList: getTokenList(state), useTokenDetection, selectedAddress, - isTokenDetectionSupported: getIsTokenDetectionSupported(state), + isDynamicTokenListAvailable: getIsDynamicTokenListAvailable(state), networkName: getTokenDetectionSupportNetworkByChainId(state), + tokenDetectionInactiveOnNonMainnetSupportedNetwork: + getIstokenDetectionInactiveOnNonMainnetSupportedNetwork(state), }; }; const mapDispatchToProps = (dispatch) => { diff --git a/ui/pages/import-token/import-token.test.js b/ui/pages/import-token/import-token.test.js index 83ade8821..2d4ef2125 100644 --- a/ui/pages/import-token/import-token.test.js +++ b/ui/pages/import-token/import-token.test.js @@ -39,6 +39,7 @@ describe('Import Token', () => { frequentRpcListDetail: [], identities: {}, selectedAddress: '0x1231231', + useTokenDetection: true, }, history: { mostRecentOverviewPage: '/', diff --git a/ui/pages/import-token/token-list/token-list.component.js b/ui/pages/import-token/token-list/token-list.component.js index 670ea6126..fed54e5ad 100644 --- a/ui/pages/import-token/token-list/token-list.component.js +++ b/ui/pages/import-token/token-list/token-list.component.js @@ -35,14 +35,13 @@ export default class TokenList extends Component { {Array(6) .fill(undefined) .map((_, i) => { - const { iconUrl, symbol, name, address } = results[i] || {}; - const iconPath = iconUrl; + const { symbol, name, address } = results[i] || {}; const tokenAlreadyAdded = checkExistingAddresses(address, tokens); const onClick = () => !tokenAlreadyAdded && onToggleToken(results[i]); return ( - Boolean(iconUrl || symbol || name) && ( + Boolean(results[i]?.iconUrl || symbol || name) && (
diff --git a/ui/pages/import-token/token-list/token-list.container.js b/ui/pages/import-token/token-list/token-list.container.js index 565bd4262..7b6b0147b 100644 --- a/ui/pages/import-token/token-list/token-list.container.js +++ b/ui/pages/import-token/token-list/token-list.container.js @@ -1,11 +1,10 @@ import { connect } from 'react-redux'; import TokenList from './token-list.component'; -const mapStateToProps = ({ metamask }) => { - const { tokens, useTokenDetection } = metamask; +const mapStateToProps = (state) => { + const { tokens } = state.metamask; return { tokens, - useTokenDetection, }; }; diff --git a/ui/pages/settings/advanced-tab/advanced-tab.component.js b/ui/pages/settings/advanced-tab/advanced-tab.component.js index a03d6a25a..072d8be80 100644 --- a/ui/pages/settings/advanced-tab/advanced-tab.component.js +++ b/ui/pages/settings/advanced-tab/advanced-tab.component.js @@ -791,10 +791,6 @@ export default class AdvancedTab extends PureComponent { } renderTokenDetectionToggle() { - if (!process.env.TOKEN_DETECTION_V2) { - return null; - } - const { t } = this.context; const { useTokenDetection, setUseTokenDetection } = this.props; @@ -807,7 +803,7 @@ export default class AdvancedTab extends PureComponent {
{t('tokenDetection')}
- {t('tokenDetectionToggleDescription')} + {t('tokenDetectionDescription')}
diff --git a/ui/pages/settings/advanced-tab/advanced-tab.component.test.js b/ui/pages/settings/advanced-tab/advanced-tab.component.test.js index 744ef2f80..0aad2d496 100644 --- a/ui/pages/settings/advanced-tab/advanced-tab.component.test.js +++ b/ui/pages/settings/advanced-tab/advanced-tab.component.test.js @@ -43,23 +43,23 @@ describe('AdvancedTab Component', () => { }); it('should render correctly when threeBoxFeatureFlag', () => { - expect(component.find('.settings-page__content-row')).toHaveLength(15); + expect(component.find('.settings-page__content-row')).toHaveLength(16); }); it('should render backup button', () => { - expect(component.find('.settings-page__content-row')).toHaveLength(15); + expect(component.find('.settings-page__content-row')).toHaveLength(16); expect( component .find('.settings-page__content-row') - .at(9) + .at(10) .find('.settings-page__content-item'), ).toHaveLength(2); expect( component .find('.settings-page__content-row') - .at(9) + .at(10) .find('.settings-page__content-item') .at(0) .find('.settings-page__content-description') @@ -69,7 +69,7 @@ describe('AdvancedTab Component', () => { expect( component .find('.settings-page__content-row') - .at(9) + .at(10) .find('.settings-page__content-item') .at(1) .find('Button') @@ -78,19 +78,19 @@ describe('AdvancedTab Component', () => { }); it('should render restore button', () => { - expect(component.find('.settings-page__content-row')).toHaveLength(15); + expect(component.find('.settings-page__content-row')).toHaveLength(16); expect( component .find('.settings-page__content-row') - .at(10) + .at(11) .find('.settings-page__content-item'), ).toHaveLength(2); expect( component .find('.settings-page__content-row') - .at(10) + .at(11) .find('.settings-page__content-item') .at(0) .find('.settings-page__content-description') @@ -100,7 +100,7 @@ describe('AdvancedTab Component', () => { expect( component .find('.settings-page__content-row') - .at(10) + .at(11) .find('.settings-page__content-item') .at(1) .find('label') @@ -137,7 +137,7 @@ describe('AdvancedTab Component', () => { }, ); - const autoTimeout = component.find('.settings-page__content-row').at(8); + const autoTimeout = component.find('.settings-page__content-row').at(9); const textField = autoTimeout.find(TextField); textField.props().onChange({ target: { value: 1440 } }); @@ -148,14 +148,13 @@ describe('AdvancedTab Component', () => { }); it('should toggle show test networks', () => { - const testNetworks = component.find('.settings-page__content-row').at(6); + const testNetworks = component.find('.settings-page__content-row').at(7); const toggleButton = testNetworks.find(ToggleButton); toggleButton.first().simulate('toggle'); expect(toggleTestnet.calledOnce).toStrictEqual(true); }); it('should toggle token detection', () => { - process.env.TOKEN_DETECTION_V2 = true; component = shallow( -
- {t('useTokenDetection')} -
- {t('useTokenDetectionDescription')} -
-
-
-
- { - this.context.trackEvent({ - category: EVENT.CATEGORIES.SETTINGS, - event: 'Token Detection', - properties: { - action: 'Token Detection', - legacy_event: true, - }, - }); - setUseTokenDetection(!value); - }} - offLabel={t('off')} - onLabel={t('on')} - /> -
-
-
- ); - } - renderCollectibleDetectionToggle() { if (!process.env.COLLECTIBLES_V1) { return null; @@ -326,10 +288,6 @@ export default class ExperimentalTab extends PureComponent { render() { return (
- {/* TODO: Remove during TOKEN_DETECTION_V2 feature flag clean up */} - {process.env.TOKEN_DETECTION_V2 - ? null - : this.renderTokenDetectionToggle()} {this.renderOpenSeaEnabledToggle()} {this.renderCollectibleDetectionToggle()} {this.renderEIP1559V2EnabledToggle()} diff --git a/ui/pages/settings/experimental-tab/experimental-tab.component.test.js b/ui/pages/settings/experimental-tab/experimental-tab.component.test.js deleted file mode 100644 index f7a06e076..000000000 --- a/ui/pages/settings/experimental-tab/experimental-tab.component.test.js +++ /dev/null @@ -1,38 +0,0 @@ -import React from 'react'; -import sinon from 'sinon'; -import { mount } from 'enzyme'; -import ExperimentalTab from './experimental-tab.container'; - -describe('Experimental Tab', () => { - let wrapper; - - const props = { - useTokenDetection: true, - setUseTokenDetection: sinon.spy(), - }; - - it('toggles Use token detection', () => { - wrapper = mount(, { - context: { - t: (str) => str, - trackEvent: () => undefined, - }, - }); - const useTokenDetection = wrapper.find({ type: 'checkbox' }).at(0); - useTokenDetection.simulate('click'); - expect(props.setUseTokenDetection.calledOnce).toStrictEqual(true); - }); - - /** TODO: Remove during TOKEN_DETECTION_V2 feature flag clean up */ - it('should not show use token detection toggle', () => { - process.env.TOKEN_DETECTION_V2 = true; - wrapper = mount(, { - context: { - t: (str) => str, - trackEvent: () => undefined, - }, - }); - const useTokenDetectionText = wrapper.find({ text: 'Use token detection' }); - expect(useTokenDetectionText).toHaveLength(0); - }); -}); diff --git a/ui/pages/settings/experimental-tab/experimental-tab.container.js b/ui/pages/settings/experimental-tab/experimental-tab.container.js index 354bb714b..d27f7e572 100644 --- a/ui/pages/settings/experimental-tab/experimental-tab.container.js +++ b/ui/pages/settings/experimental-tab/experimental-tab.container.js @@ -2,7 +2,6 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; import { withRouter } from 'react-router-dom'; import { - setUseTokenDetection, setUseCollectibleDetection, setOpenSeaEnabled, setEIP1559V2Enabled, @@ -10,7 +9,6 @@ import { setCustomNetworkListEnabled, } from '../../../store/actions'; import { - getUseTokenDetection, getUseCollectibleDetection, getOpenSeaEnabled, getEIP1559V2Enabled, @@ -21,10 +19,6 @@ import ExperimentalTab from './experimental-tab.component'; const mapStateToProps = (state) => { return { - useTokenDetection: - getUseTokenDetection( - state, - ) /** TODO: Remove during TOKEN_DETECTION_V2 feature flag clean up */, useCollectibleDetection: getUseCollectibleDetection(state), openSeaEnabled: getOpenSeaEnabled(state), eip1559V2Enabled: getEIP1559V2Enabled(state), @@ -35,10 +29,6 @@ const mapStateToProps = (state) => { const mapDispatchToProps = (dispatch) => { return { - setUseTokenDetection: (val) => - dispatch( - setUseTokenDetection(val), - ) /** TODO: Remove during TOKEN_DETECTION_V2 feature flag clean up */, setUseCollectibleDetection: (val) => dispatch(setUseCollectibleDetection(val)), setOpenSeaEnabled: (val) => dispatch(setOpenSeaEnabled(val)), diff --git a/ui/pages/settings/settings-tab/settings-tab.component.js b/ui/pages/settings/settings-tab/settings-tab.component.js index ff163d228..53d8d6d63 100644 --- a/ui/pages/settings/settings-tab/settings-tab.component.js +++ b/ui/pages/settings/settings-tab/settings-tab.component.js @@ -54,7 +54,6 @@ export default class SettingsTab extends PureComponent { setHideZeroBalanceTokens: PropTypes.func, lastFetchedConversionDate: PropTypes.number, selectedAddress: PropTypes.string, - useTokenDetection: PropTypes.bool, tokenList: PropTypes.object, }; @@ -168,13 +167,8 @@ export default class SettingsTab extends PureComponent { renderBlockieOptIn() { const { t } = this.context; - const { - useBlockie, - setUseBlockie, - selectedAddress, - useTokenDetection, - tokenList, - } = this.props; + const { useBlockie, setUseBlockie, selectedAddress, tokenList } = + this.props; const getIconStyles = () => ({ display: 'block', @@ -215,7 +209,6 @@ export default class SettingsTab extends PureComponent { id="jazzicon" address={selectedAddress} diameter={32} - useTokenDetection={useTokenDetection} tokenList={tokenList} style={getIconStyles()} /> diff --git a/ui/pages/settings/settings-tab/settings-tab.container.js b/ui/pages/settings/settings-tab/settings-tab.container.js index 15dbc51dd..18bae977e 100644 --- a/ui/pages/settings/settings-tab/settings-tab.container.js +++ b/ui/pages/settings/settings-tab/settings-tab.container.js @@ -7,7 +7,7 @@ import { setHideZeroBalanceTokens, setParticipateInMetaMetrics, } from '../../../store/actions'; -import { getPreferences } from '../../../selectors'; +import { getTokenList, getPreferences } from '../../../selectors'; import SettingsTab from './settings-tab.component'; const mapStateToProps = (state, ownProps) => { @@ -21,13 +21,12 @@ const mapStateToProps = (state, ownProps) => { useBlockie, currentLocale, selectedAddress, - useTokenDetection, - tokenList, } = metamask; const { useNativeCurrencyAsPrimaryCurrency, hideZeroBalanceTokens } = getPreferences(state); const { lastFetchedConversionDate } = ownProps; + const tokenList = getTokenList(state); return { warning, @@ -39,7 +38,6 @@ const mapStateToProps = (state, ownProps) => { hideZeroBalanceTokens, lastFetchedConversionDate, selectedAddress, - useTokenDetection, tokenList, }; }; diff --git a/ui/pages/swaps/build-quote/build-quote.js b/ui/pages/swaps/build-quote/build-quote.js index 010a0f6c8..6bbac7e08 100644 --- a/ui/pages/swaps/build-quote/build-quote.js +++ b/ui/pages/swaps/build-quote/build-quote.js @@ -67,7 +67,6 @@ import { getCurrentChainId, getRpcPrefsForCurrentProvider, getUseTokenDetection, - getTokenList, isHardwareWallet, getHardwareWalletType, } from '../../../selectors'; @@ -151,7 +150,6 @@ export default function BuildQuote({ const defaultSwapsToken = useSelector(getSwapsDefaultToken, isEqual); const chainId = useSelector(getCurrentChainId); const rpcPrefs = useSelector(getRpcPrefsForCurrentProvider, shallowEqual); - const tokenList = useSelector(getTokenList, isEqual); const useTokenDetection = useSelector(getUseTokenDetection); const quotes = useSelector(getQuotes, isEqual); const areQuotesPresent = Object.keys(quotes).length > 0; @@ -214,7 +212,7 @@ export default function BuildQuote({ conversionRate, currentCurrency, chainId, - tokenList, + shuffledTokensList, useTokenDetection, ); diff --git a/ui/pages/swaps/index.js b/ui/pages/swaps/index.js index 128ae7474..ea1ef45ae 100644 --- a/ui/pages/swaps/index.js +++ b/ui/pages/swaps/index.js @@ -136,8 +136,8 @@ export default function Swap() { checkNetworkAndAccountSupports1559, ); const defaultSwapsToken = useSelector(getSwapsDefaultToken, isEqual); - const tokenList = useSelector(getTokenList, isEqual); - const listTokenValues = shuffle(Object.values(tokenList)); + const tokenList = useSelector(getTokenList); + const shuffledTokensList = shuffle(Object.entries(tokenList)); const reviewSwapClickedTimestamp = useSelector(getReviewSwapClickedTimestamp); const pendingSmartTransactions = useSelector(getPendingSmartTransactions); const reviewSwapClicked = Boolean(reviewSwapClickedTimestamp); @@ -474,7 +474,7 @@ export default function Swap() { ); }} diff --git a/ui/pages/token-details/token-details-page.js b/ui/pages/token-details/token-details-page.js index fc7ce15f4..b34f35f59 100644 --- a/ui/pages/token-details/token-details-page.js +++ b/ui/pages/token-details/token-details-page.js @@ -34,12 +34,8 @@ export default function TokenDetailsPage() { const tokenList = useSelector(getTokenList); const { address: tokenAddress } = useParams(); - const tokenMetadata = Object.values(tokenList).find((token) => - isEqualCaseInsensitive(token.address, tokenAddress), - ); + const tokenMetadata = tokenList[tokenAddress.toLowerCase()]; const aggregators = tokenMetadata?.aggregators?.join(', '); - const fileName = tokenMetadata?.iconUrl; - const imagePath = fileName; const token = tokens.find(({ address }) => isEqualCaseInsensitive(address, tokenAddress), @@ -99,7 +95,7 @@ export default function TokenDetailsPage() { @@ -183,7 +179,7 @@ export default function TokenDetailsPage() { ? networkNickname ?? t('privateNetwork') : t(networkType)} - {process.env.TOKEN_DETECTION_V2 && aggregators && ( + {aggregators && ( <> { }); it('should render token list title in token details page', () => { - process.env.TOKEN_DETECTION_V2 = true; const store = configureMockStore()(state); const { getByText } = renderWithProvider(, store); expect(getByText('Token lists:')).toBeInTheDocument(); - process.env.TOKEN_DETECTION_V2 = false; }); it('should render token list for the token in token details page', () => { - process.env.TOKEN_DETECTION_V2 = true; const store = configureMockStore()(state); const { getByText } = renderWithProvider(, store); expect( @@ -334,7 +331,6 @@ describe('TokenDetailsPage', () => { 'Aave, Bancor, CMC, Crypto.com, CoinGecko, 1inch, Paraswap, PMM, Synthetix, Zapper, Zerion, 0x.', ), ).toBeInTheDocument(); - process.env.TOKEN_DETECTION_V2 = false; }); it('should call hide token button when button is clicked in token details page', () => { diff --git a/ui/selectors/selectors.js b/ui/selectors/selectors.js index fd26227a2..e45693fdb 100644 --- a/ui/selectors/selectors.js +++ b/ui/selectors/selectors.js @@ -53,7 +53,7 @@ import { } from '../helpers/utils/conversions.util'; import { TEMPLATED_CONFIRMATION_MESSAGE_TYPES } from '../pages/confirmation/templates'; - +import { STATIC_MAINNET_TOKEN_LIST } from '../../shared/constants/tokens'; import { toChecksumHexAddress } from '../../shared/modules/hexstring-utils'; import { DAY } from '../../shared/constants/time'; import { @@ -828,8 +828,8 @@ function getAllowedAnnouncementIds(state) { 7: false, 8: supportsWebHid && currentKeyringIsLedger && currentlyUsingLedgerLive, 9: false, - 10: Boolean(process.env.TOKEN_DETECTION_V2) && !process.env.IN_TEST, - 11: Boolean(process.env.TOKEN_DETECTION_V2) && !process.env.IN_TEST, + 10: true, + 11: true, 12: false, 13: true, }; @@ -919,13 +919,19 @@ export function getTheme(state) { } /** - * To retrieve the tokenList produced by TokenListcontroller + * To retrieve the token list for use throughout the UI. Will return the remotely fetched list + * from the tokens controller if token detection is enabled, or the static list if not. * * @param {*} state * @returns {object} */ export function getTokenList(state) { - return state.metamask.tokenList; + const isTokenDetectionInactiveOnMainnet = + getIsTokenDetectionInactiveOnMainnet(state); + const caseInSensitiveTokenList = isTokenDetectionInactiveOnMainnet + ? STATIC_MAINNET_TOKEN_LIST + : state.metamask.tokenList; + return caseInSensitiveTokenList; } export function doesAddressRequireLedgerHidConnection(state, address) { @@ -1014,6 +1020,8 @@ export function getIsAdvancedGasFeeDefault(state) { } /** + * To get the name of the network that support token detection based in chainId. + * * @param state * @returns string e.g. ethereum, bsc or polygon */ @@ -1033,13 +1041,13 @@ export const getTokenDetectionSupportNetworkByChainId = (state) => { } }; /** - * To check for the chainId that supports token detection , + * To check if teh chainId supports token detection , * currently it returns true for Ethereum Mainnet, Polygon, BSC and Avalanche * * @param {*} state * @returns Boolean */ -export function getIsTokenDetectionSupported(state) { +export function getIsDynamicTokenListAvailable(state) { const chainId = getCurrentChainId(state); return [ MAINNET_CHAIN_ID, @@ -1069,6 +1077,50 @@ export function getNewTokensImported(state) { return state.appState.newTokensImported; } +/** + * To check if the token detection is OFF and the network is Mainnet + * so that the user can skip third party token api fetch + * and use the static tokenlist from contract-metadata + * + * @param {*} state + * @returns Boolean + */ +export function getIsTokenDetectionInactiveOnMainnet(state) { + const isMainnet = getIsMainnet(state); + const useTokenDetection = getUseTokenDetection(state); + + return !useTokenDetection && isMainnet; +} + +/** + * To check for the chainId that supports token detection , + * currently it returns true for Ethereum Mainnet, Polygon, BSC and Avalanche + * + * @param {*} state + * @returns Boolean + */ +export function getIsTokenDetectionSupported(state) { + const useTokenDetection = getUseTokenDetection(state); + const isDynamicTokenListAvailable = getIsDynamicTokenListAvailable(state); + + return useTokenDetection && isDynamicTokenListAvailable; +} + +/** + * To check if the token detection is OFF for the token detection supported networks + * and the network is not Mainnet + * + * @param {*} state + * @returns Boolean + */ +export function getIstokenDetectionInactiveOnNonMainnetSupportedNetwork(state) { + const useTokenDetection = getUseTokenDetection(state); + const isMainnet = getIsMainnet(state); + const isDynamicTokenListAvailable = getIsDynamicTokenListAvailable(state); + + return isDynamicTokenListAvailable && !useTokenDetection && !isMainnet; +} + /** * To get the `customNetworkListEnabled` value which determines whether we use the custom network list * diff --git a/yarn.lock b/yarn.lock index 91de10061..3460200b3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2867,10 +2867,10 @@ resolved "https://registry.yarnpkg.com/@metamask/contract-metadata/-/contract-metadata-1.35.0.tgz#2bf2b8f2b6fdbd5132f0bcfa594b6c02dc71c42e" integrity sha512-zfZKwLFOVrQS8vTFoeoNCG9JhqmK4oyembGiGVVpUAYD9BHVZnd9WpicGoUC07ROXLEyQuAK9AJZNBtqwwzfEQ== -"@metamask/controllers@^30.0.0", "@metamask/controllers@^30.0.2": - version "30.0.2" - resolved "https://registry.yarnpkg.com/@metamask/controllers/-/controllers-30.0.2.tgz#0a5512598d2997e34d3542889cae7088fe1fa4b8" - integrity sha512-nIUQaaGPzy9whcAvzwHCRsMtw3YWdBv6mUiN9EA2CKdYUesnVj4bpXSSMEso1oEhsNRa2/XTZSb6i+Odo/raJw== +"@metamask/controllers@^30.0.0", "@metamask/controllers@^30.1.0": + version "30.1.0" + resolved "https://registry.yarnpkg.com/@metamask/controllers/-/controllers-30.1.0.tgz#157d0afca156f1f37a89fbb864c4ee5c64d23af0" + integrity sha512-480mQafsYKbl0q7YgV820mrPCUtWgLLVH/s8ozNT6/ZVX3sBU0FBhNKeCalewhn0HRfMRnLe8pvHCKIH30k/0w== dependencies: "@ethereumjs/common" "^2.3.1" "@ethereumjs/tx" "^3.2.1"