mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 01:47:00 +01:00
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
This commit is contained in:
parent
d21a8a1cfb
commit
6e5c2f03bf
15
app/_locales/de/messages.json
generated
15
app/_locales/de/messages.json
generated
@ -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."
|
||||
},
|
||||
|
15
app/_locales/el/messages.json
generated
15
app/_locales/el/messages.json
generated
@ -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 σας."
|
||||
},
|
||||
|
31
app/_locales/en/messages.json
generated
31
app/_locales/en/messages.json
generated
@ -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."
|
||||
},
|
||||
|
15
app/_locales/es/messages.json
generated
15
app/_locales/es/messages.json
generated
@ -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."
|
||||
},
|
||||
|
12
app/_locales/es_419/messages.json
generated
12
app/_locales/es_419/messages.json
generated
@ -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"
|
||||
},
|
||||
|
15
app/_locales/fr/messages.json
generated
15
app/_locales/fr/messages.json
generated
@ -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."
|
||||
},
|
||||
|
15
app/_locales/hi/messages.json
generated
15
app/_locales/hi/messages.json
generated
@ -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 एड्रेस को एक्सेस कर पाएंगे।"
|
||||
},
|
||||
|
15
app/_locales/id/messages.json
generated
15
app/_locales/id/messages.json
generated
@ -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."
|
||||
},
|
||||
|
15
app/_locales/ja/messages.json
generated
15
app/_locales/ja/messages.json
generated
@ -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 アドレスにアクセスできます。"
|
||||
},
|
||||
|
15
app/_locales/ko/messages.json
generated
15
app/_locales/ko/messages.json
generated
@ -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 주소에 액세스하게 됩니다."
|
||||
},
|
||||
|
15
app/_locales/pt/messages.json
generated
15
app/_locales/pt/messages.json
generated
@ -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."
|
||||
},
|
||||
|
12
app/_locales/pt_BR/messages.json
generated
12
app/_locales/pt_BR/messages.json
generated
@ -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"
|
||||
},
|
||||
|
15
app/_locales/ru/messages.json
generated
15
app/_locales/ru/messages.json
generated
@ -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-адресу."
|
||||
},
|
||||
|
15
app/_locales/tl/messages.json
generated
15
app/_locales/tl/messages.json
generated
@ -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."
|
||||
},
|
||||
|
15
app/_locales/tr/messages.json
generated
15
app/_locales/tr/messages.json
generated
@ -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."
|
||||
},
|
||||
|
15
app/_locales/vi/messages.json
generated
15
app/_locales/vi/messages.json
generated
@ -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."
|
||||
},
|
||||
|
12
app/_locales/zh/messages.json
generated
12
app/_locales/zh/messages.json
generated
@ -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地址的访问权限。"
|
||||
},
|
||||
|
12
app/_locales/zh_CN/messages.json
generated
12
app/_locales/zh_CN/messages.json
generated
@ -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": "可用于各种不同的客户端"
|
||||
},
|
||||
|
@ -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,
|
||||
});
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
} 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
});
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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",
|
||||
|
@ -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: [],
|
||||
},
|
||||
};
|
||||
},
|
||||
{},
|
||||
);
|
||||
|
@ -67,6 +67,12 @@
|
||||
"8": {
|
||||
"isShown": true
|
||||
},
|
||||
"10": {
|
||||
"isShown": true
|
||||
},
|
||||
"11": {
|
||||
"isShown": true
|
||||
},
|
||||
"12": {
|
||||
"isShown": true
|
||||
},
|
||||
|
@ -57,6 +57,12 @@
|
||||
"8": {
|
||||
"isShown": true
|
||||
},
|
||||
"10": {
|
||||
"isShown": true
|
||||
},
|
||||
"11": {
|
||||
"isShown": true
|
||||
},
|
||||
"12": {
|
||||
"isShown": true
|
||||
},
|
||||
|
@ -53,6 +53,12 @@
|
||||
"8": {
|
||||
"isShown": true
|
||||
},
|
||||
"10": {
|
||||
"isShown": true
|
||||
},
|
||||
"11": {
|
||||
"isShown": true
|
||||
},
|
||||
"12": {
|
||||
"isShown": true
|
||||
},
|
||||
|
@ -71,6 +71,12 @@
|
||||
"8": {
|
||||
"isShown": true
|
||||
},
|
||||
"10": {
|
||||
"isShown": true
|
||||
},
|
||||
"11": {
|
||||
"isShown": true
|
||||
},
|
||||
"12": {
|
||||
"isShown": true
|
||||
},
|
||||
|
@ -54,6 +54,12 @@
|
||||
"8": {
|
||||
"isShown": true
|
||||
},
|
||||
"10": {
|
||||
"isShown": true
|
||||
},
|
||||
"11": {
|
||||
"isShown": true
|
||||
},
|
||||
"12": {
|
||||
"isShown": true
|
||||
},
|
||||
|
@ -54,6 +54,12 @@
|
||||
"8": {
|
||||
"isShown": true
|
||||
},
|
||||
"10": {
|
||||
"isShown": true
|
||||
},
|
||||
"11": {
|
||||
"isShown": true
|
||||
},
|
||||
"12": {
|
||||
"isShown": true
|
||||
},
|
||||
|
@ -108,6 +108,12 @@
|
||||
"8": {
|
||||
"isShown": true
|
||||
},
|
||||
"10": {
|
||||
"isShown": true
|
||||
},
|
||||
"11": {
|
||||
"isShown": true
|
||||
},
|
||||
"12": {
|
||||
"isShown": true
|
||||
},
|
||||
|
@ -53,6 +53,12 @@
|
||||
"8": {
|
||||
"isShown": true
|
||||
},
|
||||
"10": {
|
||||
"isShown": true
|
||||
},
|
||||
"11": {
|
||||
"isShown": true
|
||||
},
|
||||
"12": {
|
||||
"isShown": true
|
||||
},
|
||||
|
@ -53,6 +53,12 @@
|
||||
"8": {
|
||||
"isShown": true
|
||||
},
|
||||
"10": {
|
||||
"isShown": true
|
||||
},
|
||||
"11": {
|
||||
"isShown": true
|
||||
},
|
||||
"12": {
|
||||
"isShown": true
|
||||
},
|
||||
|
@ -57,6 +57,12 @@
|
||||
"8": {
|
||||
"isShown": true
|
||||
},
|
||||
"10": {
|
||||
"isShown": true
|
||||
},
|
||||
"11": {
|
||||
"isShown": true
|
||||
},
|
||||
"12": {
|
||||
"isShown": true
|
||||
},
|
||||
|
@ -53,6 +53,12 @@
|
||||
"8": {
|
||||
"isShown": true
|
||||
},
|
||||
"10": {
|
||||
"isShown": true
|
||||
},
|
||||
"11": {
|
||||
"isShown": true
|
||||
},
|
||||
"12": {
|
||||
"isShown": true
|
||||
},
|
||||
|
@ -28,6 +28,12 @@
|
||||
},
|
||||
"NotificationController": {
|
||||
"notifications": {
|
||||
"10": {
|
||||
"isShown": true
|
||||
},
|
||||
"11": {
|
||||
"isShown": true
|
||||
},
|
||||
"12": {
|
||||
"isShown": true
|
||||
},
|
||||
|
@ -54,6 +54,12 @@
|
||||
"8": {
|
||||
"isShown": true
|
||||
},
|
||||
"10": {
|
||||
"isShown": true
|
||||
},
|
||||
"11": {
|
||||
"isShown": true
|
||||
},
|
||||
"12": {
|
||||
"isShown": true
|
||||
},
|
||||
|
@ -54,6 +54,12 @@
|
||||
"8": {
|
||||
"isShown": true
|
||||
},
|
||||
"10": {
|
||||
"isShown": true
|
||||
},
|
||||
"11": {
|
||||
"isShown": true
|
||||
},
|
||||
"12": {
|
||||
"isShown": true
|
||||
},
|
||||
|
@ -135,7 +135,7 @@
|
||||
"useBlockie": false,
|
||||
"useNonceField": false,
|
||||
"usePhishDetect": true,
|
||||
"useTokenDetection": true
|
||||
"useTokenDetection": false
|
||||
},
|
||||
"config": {},
|
||||
"firstTimeInfo": {
|
||||
|
@ -64,6 +64,12 @@
|
||||
"8": {
|
||||
"isShown": true
|
||||
},
|
||||
"10": {
|
||||
"isShown": true
|
||||
},
|
||||
"11": {
|
||||
"isShown": true
|
||||
},
|
||||
"12": {
|
||||
"isShown": true
|
||||
},
|
||||
|
@ -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 () {
|
||||
|
@ -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,
|
||||
|
@ -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 && (
|
||||
<DetectedTokensLink setShowDetectedTokens={setShowDetectedTokens} />
|
||||
)
|
||||
: null}
|
||||
{detectedTokens.length > 0 &&
|
||||
!istokenDetectionInactiveOnNonMainnetSupportedNetwork && (
|
||||
<DetectedTokensLink setShowDetectedTokens={setShowDetectedTokens} />
|
||||
)}
|
||||
<Box marginTop={detectedTokens.length > 0 ? 0 : 4}>
|
||||
<Box justifyContent={JUSTIFY_CONTENT.CENTER}>
|
||||
<Typography
|
||||
|
@ -5,7 +5,9 @@ import { renderWithProvider } from '../../../../test/jest/rendering';
|
||||
import ContactList from '.';
|
||||
|
||||
describe('Contact List', () => {
|
||||
const store = configureMockStore([])({ metamask: {} });
|
||||
const store = configureMockStore([])({
|
||||
metamask: { provider: { chainId: '0x0' } },
|
||||
});
|
||||
|
||||
describe('given searchForContacts', () => {
|
||||
const selectRecipient = () => null;
|
||||
|
@ -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 (
|
||||
<Popover
|
||||
title={t('areYouSure')}
|
||||
className="detected-token-ignored-popover"
|
||||
title={
|
||||
partiallyIgnoreDetectedTokens
|
||||
? t('importSelectedTokens')
|
||||
: t('areYouSure')
|
||||
}
|
||||
className={classNames('detected-token-ignored-popover', {
|
||||
'detected-token-ignored-popover--import': partiallyIgnoreDetectedTokens,
|
||||
'detected-token-ignored-popover--ignore':
|
||||
!partiallyIgnoreDetectedTokens,
|
||||
})}
|
||||
footer={footer}
|
||||
>
|
||||
<Typography
|
||||
@ -46,13 +56,16 @@ const DetectedTokenIgnoredPopover = ({
|
||||
marginBottom={7}
|
||||
marginLeft={5}
|
||||
>
|
||||
{t('ignoreTokenWarning')}
|
||||
{partiallyIgnoreDetectedTokens
|
||||
? t('importSelectedTokensDescription')
|
||||
: t('ignoreTokenWarning')}
|
||||
</Typography>
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
|
||||
DetectedTokenIgnoredPopover.propTypes = {
|
||||
partiallyIgnoreDetectedTokens: PropTypes.bool.isRequired,
|
||||
onCancelIgnore: PropTypes.func.isRequired,
|
||||
handleClearTokensSelection: PropTypes.func.isRequired,
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 }) => {
|
||||
<DetectedTokenIgnoredPopover
|
||||
onCancelIgnore={onCancelIgnore}
|
||||
handleClearTokensSelection={handleClearTokensSelection}
|
||||
partiallyIgnoreDetectedTokens={partiallyIgnoreDetectedTokens}
|
||||
/>
|
||||
)}
|
||||
<DetectedTokenSelectionPopover
|
||||
|
@ -9,24 +9,29 @@ import { TEXT_ALIGN } from '../../../helpers/constants/design-system';
|
||||
import { detectNewTokens } from '../../../store/actions';
|
||||
import { MetaMetricsContext } from '../../../contexts/metametrics';
|
||||
import { EVENT } from '../../../../shared/constants/metametrics';
|
||||
import { getIsMainnet, getIsTokenDetectionSupported } from '../../../selectors';
|
||||
import {
|
||||
getIsTokenDetectionSupported,
|
||||
getIsTokenDetectionInactiveOnMainnet,
|
||||
} from '../../../selectors';
|
||||
|
||||
export default function ImportTokenLink() {
|
||||
const trackEvent = useContext(MetaMetricsContext);
|
||||
const t = useI18nContext();
|
||||
const history = useHistory();
|
||||
|
||||
const isMainnet = useSelector(getIsMainnet);
|
||||
const isTokenDetectionSupported = useSelector(getIsTokenDetectionSupported);
|
||||
const isTokenDetectionInactiveOnMainnet = useSelector(
|
||||
getIsTokenDetectionInactiveOnMainnet,
|
||||
);
|
||||
|
||||
const isTokenDetectionsupported =
|
||||
isMainnet ||
|
||||
(process.env.TOKEN_DETECTION_V2 && isTokenDetectionSupported) ||
|
||||
const isTokenDetectionAvailable =
|
||||
isTokenDetectionSupported ||
|
||||
isTokenDetectionInactiveOnMainnet ||
|
||||
Boolean(process.env.IN_TEST);
|
||||
|
||||
return (
|
||||
<Box className="import-token-link" textAlign={TEXT_ALIGN.CENTER}>
|
||||
{isTokenDetectionsupported && (
|
||||
{isTokenDetectionAvailable && (
|
||||
<>
|
||||
<Button
|
||||
className="import-token-link__link"
|
||||
@ -53,7 +58,7 @@ export default function ImportTokenLink() {
|
||||
});
|
||||
}}
|
||||
>
|
||||
{isTokenDetectionsupported
|
||||
{isTokenDetectionAvailable
|
||||
? t('importTokens')
|
||||
: t('importTokens').charAt(0).toUpperCase() +
|
||||
t('importTokens').slice(1)}
|
||||
|
@ -10,7 +10,11 @@ describe('Confirm Remove Account', () => {
|
||||
let wrapper;
|
||||
|
||||
const state = {
|
||||
metamask: {},
|
||||
metamask: {
|
||||
provider: {
|
||||
chainId: '0x0',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const props = {
|
||||
|
@ -13,7 +13,6 @@ export default function TokenCell({
|
||||
balanceError,
|
||||
symbol,
|
||||
string,
|
||||
image,
|
||||
onClick,
|
||||
isERC721,
|
||||
}) {
|
||||
@ -44,7 +43,6 @@ export default function TokenCell({
|
||||
iconClassName="token-cell__icon"
|
||||
onClick={onClick.bind(null, address)}
|
||||
tokenAddress={address}
|
||||
tokenImage={image}
|
||||
tokenSymbol={symbol}
|
||||
tokenDecimals={decimals}
|
||||
warning={warning}
|
||||
@ -61,7 +59,6 @@ TokenCell.propTypes = {
|
||||
symbol: PropTypes.string,
|
||||
decimals: PropTypes.number,
|
||||
string: PropTypes.string,
|
||||
image: PropTypes.string,
|
||||
onClick: PropTypes.func.isRequired,
|
||||
isERC721: PropTypes.bool,
|
||||
};
|
||||
|
@ -45,7 +45,6 @@ describe('Token Cell', () => {
|
||||
symbol="TEST"
|
||||
string="5.000"
|
||||
currentCurrency="usd"
|
||||
image="./test-image"
|
||||
onClick={onClick}
|
||||
/>
|
||||
</MemoryRouter>
|
||||
@ -61,7 +60,6 @@ describe('Token Cell', () => {
|
||||
expect(wrapper.find(Identicon).prop('address')).toStrictEqual(
|
||||
'0xAnotherToken',
|
||||
);
|
||||
expect(wrapper.find(Identicon).prop('image')).toStrictEqual('./test-image');
|
||||
});
|
||||
|
||||
it('renders token balance', () => {
|
||||
|
@ -49,10 +49,6 @@ export default class Identicon extends Component {
|
||||
* Check if show image border
|
||||
*/
|
||||
imageBorder: PropTypes.bool,
|
||||
/**
|
||||
* Check if use token detection
|
||||
*/
|
||||
useTokenDetection: PropTypes.bool,
|
||||
/**
|
||||
* Add list of token in object
|
||||
*/
|
||||
@ -102,8 +98,7 @@ export default class Identicon extends Component {
|
||||
}
|
||||
|
||||
renderJazzicon() {
|
||||
const { address, className, diameter, alt, useTokenDetection, tokenList } =
|
||||
this.props;
|
||||
const { address, className, diameter, alt, tokenList } = this.props;
|
||||
return (
|
||||
<Jazzicon
|
||||
address={address}
|
||||
@ -111,7 +106,6 @@ export default class Identicon extends Component {
|
||||
className={classnames('identicon', className)}
|
||||
style={getStyles(diameter)}
|
||||
alt={alt}
|
||||
useTokenDetection={useTokenDetection}
|
||||
tokenList={tokenList}
|
||||
/>
|
||||
);
|
||||
@ -136,15 +130,8 @@ export default class Identicon extends Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
address,
|
||||
image,
|
||||
useBlockie,
|
||||
addBorder,
|
||||
diameter,
|
||||
useTokenDetection,
|
||||
tokenList,
|
||||
} = this.props;
|
||||
const { address, image, useBlockie, addBorder, diameter, tokenList } =
|
||||
this.props;
|
||||
const size = diameter + 8;
|
||||
|
||||
if (image) {
|
||||
@ -152,22 +139,10 @@ export default class Identicon extends Component {
|
||||
}
|
||||
|
||||
if (address) {
|
||||
if (process.env.TOKEN_DETECTION_V2) {
|
||||
if (tokenList[address.toLowerCase()]?.iconUrl) {
|
||||
return this.renderJazzicon();
|
||||
}
|
||||
} else {
|
||||
/** TODO: Remove during TOKEN_DETECTION_V2 feature flag clean up */
|
||||
// token from dynamic api list is fetched when useTokenDetection is true
|
||||
// And since the token.address from allTokens is checksumaddress
|
||||
// tokenAddress have to be changed to lowercase when we are using dynamic list
|
||||
const tokenAddress = useTokenDetection
|
||||
? address.toLowerCase()
|
||||
: address;
|
||||
if (tokenAddress && tokenList[tokenAddress]?.iconUrl) {
|
||||
return this.renderJazzicon();
|
||||
}
|
||||
if (tokenList[address.toLowerCase()]?.iconUrl) {
|
||||
return this.renderJazzicon();
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classnames({ 'identicon__address-wrapper': addBorder })}
|
||||
|
@ -1,15 +1,15 @@
|
||||
import { connect } from 'react-redux';
|
||||
import { getTokenList } from '../../../selectors';
|
||||
import Identicon from './identicon.component';
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
const {
|
||||
metamask: { useBlockie, useTokenDetection, tokenList, ipfsGateway },
|
||||
metamask: { useBlockie, ipfsGateway },
|
||||
} = state;
|
||||
|
||||
return {
|
||||
useBlockie,
|
||||
useTokenDetection,
|
||||
tokenList,
|
||||
tokenList: getTokenList(state),
|
||||
ipfsGateway,
|
||||
};
|
||||
};
|
||||
|
@ -48,7 +48,11 @@ export default class Jazzicon extends PureComponent {
|
||||
|
||||
appendJazzicon() {
|
||||
const { address, diameter, tokenList } = this.props;
|
||||
const image = iconFactory.iconForAddress(address, diameter, tokenList);
|
||||
const image = iconFactory.iconForAddress(
|
||||
address,
|
||||
diameter,
|
||||
tokenList[address.toLowerCase()],
|
||||
);
|
||||
this.container.current.appendChild(image);
|
||||
}
|
||||
|
||||
|
@ -10,12 +10,7 @@ import Identicon from '../identicon/identicon.component';
|
||||
import { shortenAddress } from '../../../helpers/utils/util';
|
||||
import CopyIcon from '../icon/copy-icon.component';
|
||||
import { useCopyToClipboard } from '../../../hooks/useCopyToClipboard';
|
||||
import {
|
||||
getUseTokenDetection,
|
||||
getTokenList,
|
||||
getBlockExplorerLinkText,
|
||||
} from '../../../selectors';
|
||||
|
||||
import { getTokenList, getBlockExplorerLinkText } from '../../../selectors';
|
||||
import { NETWORKS_ROUTE } from '../../../helpers/constants/routes';
|
||||
|
||||
const NicknamePopover = ({
|
||||
@ -33,7 +28,6 @@ const NicknamePopover = ({
|
||||
}, [onAdd]);
|
||||
|
||||
const [copied, handleCopy] = useCopyToClipboard();
|
||||
const useTokenDetection = useSelector(getUseTokenDetection);
|
||||
const tokenList = useSelector(getTokenList);
|
||||
const blockExplorerLinkText = useSelector(getBlockExplorerLinkText);
|
||||
|
||||
@ -54,8 +48,7 @@ const NicknamePopover = ({
|
||||
address={address}
|
||||
diameter={36}
|
||||
className="nickname-popover__identicon"
|
||||
useTokenDetection={useTokenDetection}
|
||||
tokenList={tokenList}
|
||||
image={tokenList[address.toLowerCase()]?.iconUrl}
|
||||
/>
|
||||
<div className="nickname-popover__address">
|
||||
{nickname || shortenAddress(address)}
|
||||
|
@ -9,7 +9,7 @@ import TextField from '../text-field';
|
||||
import { I18nContext } from '../../../contexts/i18n';
|
||||
|
||||
import Identicon from '../identicon/identicon.component';
|
||||
import { getUseTokenDetection, getTokenList } from '../../../selectors';
|
||||
import { getTokenList } from '../../../selectors';
|
||||
|
||||
export default function UpdateNicknamePopover({
|
||||
address,
|
||||
@ -46,7 +46,6 @@ export default function UpdateNicknamePopover({
|
||||
onClose();
|
||||
};
|
||||
|
||||
const useTokenDetection = useSelector(getUseTokenDetection);
|
||||
const tokenList = useSelector(getTokenList);
|
||||
|
||||
return (
|
||||
@ -79,8 +78,7 @@ export default function UpdateNicknamePopover({
|
||||
className="update-nickname__content__indenticon"
|
||||
address={address}
|
||||
diameter={36}
|
||||
useTokenDetection={useTokenDetection}
|
||||
tokenList={tokenList}
|
||||
image={tokenList[address.toLowerCase()]?.iconUrl}
|
||||
/>
|
||||
<label className="update-nickname__content__label--capitalized">
|
||||
{t('address')}
|
||||
|
@ -314,21 +314,11 @@ export const SETTINGS_CONSTANTS = [
|
||||
icon: 'fa fa-flask',
|
||||
},
|
||||
{
|
||||
// TODO: Remove during TOKEN_DETECTION_V2 feature flag clean up
|
||||
tabMessage: (t) => t('advanced'),
|
||||
sectionMessage: (t) => t('tokenDetection'),
|
||||
descriptionMessage: (t) => t('tokenDetectionToggleDescription'),
|
||||
descriptionMessage: (t) => t('tokenDetectionDescription'),
|
||||
route: `${ADVANCED_ROUTE}#token-description`,
|
||||
icon: 'fas fa-sliders-h',
|
||||
featureFlag: 'TOKEN_DETECTION_V2',
|
||||
},
|
||||
{
|
||||
// TODO: Remove during TOKEN_DETECTION_V2 feature flag clean up
|
||||
tabMessage: (t) => t('experimental'),
|
||||
sectionMessage: (t) => t('useTokenDetection'),
|
||||
descriptionMessage: (t) => t('useTokenDetectionDescription'),
|
||||
route: `${EXPERIMENTAL_ROUTE}#token-description`,
|
||||
icon: 'fa fa-flask',
|
||||
},
|
||||
{
|
||||
tabMessage: (t) => t('experimental'),
|
||||
|
@ -14,9 +14,13 @@ function IconFactory(jazzicon) {
|
||||
this.cache = {};
|
||||
}
|
||||
|
||||
IconFactory.prototype.iconForAddress = function (address, diameter, tokenList) {
|
||||
if (iconExistsFor(address.toLowerCase(), tokenList)) {
|
||||
return imageElFor(address.toLowerCase(), tokenList);
|
||||
IconFactory.prototype.iconForAddress = function (
|
||||
address,
|
||||
diameter,
|
||||
tokenMetadata,
|
||||
) {
|
||||
if (iconExistsFor(address, tokenMetadata)) {
|
||||
return imageElFor(tokenMetadata);
|
||||
}
|
||||
|
||||
return this.generateIdenticonSvg(address, diameter);
|
||||
@ -43,16 +47,15 @@ IconFactory.prototype.generateNewIdenticon = function (address, diameter) {
|
||||
|
||||
// util
|
||||
|
||||
function iconExistsFor(address, tokenList) {
|
||||
function iconExistsFor(address, tokenMetadata) {
|
||||
return (
|
||||
tokenList[address] &&
|
||||
isValidHexAddress(address, { allowNonPrefixed: false }) &&
|
||||
tokenList[address].iconUrl
|
||||
tokenMetadata &&
|
||||
tokenMetadata.iconUrl
|
||||
);
|
||||
}
|
||||
|
||||
function imageElFor(address, tokenList) {
|
||||
const tokenMetadata = tokenList[address];
|
||||
function imageElFor(tokenMetadata = {}) {
|
||||
const img = document.createElement('img');
|
||||
img.src = tokenMetadata?.iconUrl;
|
||||
img.style.width = '100%';
|
||||
|
@ -111,15 +111,10 @@ const t = (key) => {
|
||||
return 'Localhost 8545';
|
||||
case 'experimental':
|
||||
return 'Experimental';
|
||||
/** TODO: Remove during TOKEN_DETECTION_V2 feature flag clean up */
|
||||
case 'useTokenDetection':
|
||||
return 'Use token detection';
|
||||
case 'useTokenDetectionDescription':
|
||||
return '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.';
|
||||
case 'tokenDetection':
|
||||
return 'Token detection';
|
||||
case 'tokenDetectionToggleDescription':
|
||||
return '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.';
|
||||
case 'tokenDetectionDescription':
|
||||
return "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.";
|
||||
case 'enableEIP1559V2':
|
||||
return 'Enable enhanced gas fee UI';
|
||||
case 'enableEIP1559V2Description':
|
||||
@ -172,7 +167,7 @@ describe('Settings Search Utils', () => {
|
||||
});
|
||||
|
||||
it('should get good advanced section number', () => {
|
||||
expect(getNumberOfSettingsInSection(t, t('advanced'))).toStrictEqual(15);
|
||||
expect(getNumberOfSettingsInSection(t, t('advanced'))).toStrictEqual(16);
|
||||
});
|
||||
|
||||
it('should get good contact section number', () => {
|
||||
@ -195,7 +190,7 @@ describe('Settings Search Utils', () => {
|
||||
|
||||
it('should get good experimental section number', () => {
|
||||
expect(getNumberOfSettingsInSection(t, t('experimental'))).toStrictEqual(
|
||||
4,
|
||||
3,
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -44,13 +44,7 @@ async function getDecimalsFromContract(tokenAddress) {
|
||||
}
|
||||
|
||||
export function getTokenMetadata(tokenAddress, tokenList) {
|
||||
const casedTokenList = Object.keys(tokenList).reduce((acc, base) => {
|
||||
return {
|
||||
...acc,
|
||||
[base.toLowerCase()]: tokenList[base],
|
||||
};
|
||||
}, {});
|
||||
return tokenAddress && casedTokenList[tokenAddress.toLowerCase()];
|
||||
return tokenAddress && tokenList[tokenAddress.toLowerCase()];
|
||||
}
|
||||
|
||||
async function getSymbol(tokenAddress, tokenList) {
|
||||
|
@ -5,7 +5,6 @@ import {
|
||||
getAddressBook,
|
||||
getMetaMaskIdentities,
|
||||
getTokenList,
|
||||
getUseTokenDetection,
|
||||
} from '../selectors';
|
||||
import { shortenAddress } from '../helpers/utils/util';
|
||||
|
||||
@ -13,7 +12,6 @@ const useAddressDetails = (toAddress) => {
|
||||
const addressBook = useSelector(getAddressBook);
|
||||
const identities = useSelector(getMetaMaskIdentities);
|
||||
const tokenList = useSelector(getTokenList);
|
||||
const useTokenDetection = useSelector(getUseTokenDetection);
|
||||
const checksummedAddress = toChecksumHexAddress(toAddress);
|
||||
|
||||
if (!toAddress) {
|
||||
@ -28,22 +26,11 @@ const useAddressDetails = (toAddress) => {
|
||||
if (identities[toAddress]?.name) {
|
||||
return { toName: identities[toAddress].name, isTrusted: true };
|
||||
}
|
||||
if (process.env.TOKEN_DETECTION_V2) {
|
||||
if (tokenList[toAddress]?.name) {
|
||||
return { toName: tokenList[toAddress].name, isTrusted: true };
|
||||
}
|
||||
} else {
|
||||
const casedTokenList = useTokenDetection
|
||||
? tokenList
|
||||
: Object.keys(tokenList).reduce((acc, base) => {
|
||||
return {
|
||||
...acc,
|
||||
[base.toLowerCase()]: tokenList[base],
|
||||
};
|
||||
}, {});
|
||||
if (casedTokenList[toAddress]?.name) {
|
||||
return { toName: casedTokenList[toAddress].name, isTrusted: true };
|
||||
}
|
||||
if (tokenList[toAddress?.toLowerCase()]?.name) {
|
||||
return {
|
||||
toName: tokenList[toAddress?.toLowerCase()].name,
|
||||
isTrusted: true,
|
||||
};
|
||||
}
|
||||
return {
|
||||
toName: shortenAddress(checksummedAddress),
|
||||
|
@ -77,8 +77,8 @@ describe('useAddressDetails', () => {
|
||||
{
|
||||
useTokenDetection: true,
|
||||
tokenList: {
|
||||
'0x06195827297c7A80a443b6894d3BDB8824b43896': {
|
||||
address: '0x06195827297c7A80a443b6894d3BDB8824b43896',
|
||||
'0x06195827297c7a80a443b6894d3bdb8824b43896': {
|
||||
address: '0x06195827297c7a80a443b6894d3bdb8824b43896',
|
||||
symbol: 'LINK',
|
||||
decimals: 18,
|
||||
name: 'TOKEN-ABC',
|
||||
|
@ -1,16 +1,13 @@
|
||||
import { useMemo } from 'react';
|
||||
import { shallowEqual, useSelector } from 'react-redux';
|
||||
import contractMap from '@metamask/contract-metadata';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { isEqual, shuffle, uniqBy } from 'lodash';
|
||||
import { isEqual, uniqBy } from 'lodash';
|
||||
import { getTokenFiatAmount } from '../helpers/utils/token-util';
|
||||
import {
|
||||
getTokenExchangeRates,
|
||||
getCurrentCurrency,
|
||||
getSwapsDefaultToken,
|
||||
getCurrentChainId,
|
||||
getUseTokenDetection,
|
||||
getTokenList,
|
||||
} from '../selectors';
|
||||
import { getConversionRate } from '../ducks/metamask/metamask';
|
||||
|
||||
@ -20,24 +17,13 @@ import { toChecksumHexAddress } from '../../shared/modules/hexstring-utils';
|
||||
import { TOKEN_BUCKET_PRIORITY } from '../../shared/constants/swaps';
|
||||
import { useEqualityCheck } from './useEqualityCheck';
|
||||
|
||||
/** TODO: Remove during TOKEN_DETECTION_V2 feature flag clean up */
|
||||
const shuffledContractMap = shuffle(
|
||||
Object.entries(contractMap)
|
||||
.map(([address, tokenData]) => ({
|
||||
...tokenData,
|
||||
address: address.toLowerCase(),
|
||||
}))
|
||||
.filter((tokenData) => Boolean(tokenData.erc20)),
|
||||
);
|
||||
|
||||
export function getRenderableTokenData(
|
||||
token,
|
||||
contractExchangeRates,
|
||||
conversionRate,
|
||||
currentCurrency,
|
||||
chainId,
|
||||
tokenList,
|
||||
useTokenDetection,
|
||||
shuffledTokenList,
|
||||
) {
|
||||
const { symbol, name, address, iconUrl, string, balance, decimals } = token;
|
||||
let contractExchangeRate;
|
||||
@ -67,24 +53,15 @@ export function getRenderableTokenData(
|
||||
)
|
||||
: '';
|
||||
|
||||
// token from dynamic api list is fetched when useTokenDetection is true
|
||||
// And since the token.address from allTokens is checksumaddress
|
||||
// token Address have to be changed to lowercase when we are using dynamic list
|
||||
const tokenAddress =
|
||||
useTokenDetection || process.env.TOKEN_DETECTION_V2
|
||||
? address?.toLowerCase()
|
||||
: address;
|
||||
const tokenMetadata = shuffledTokenList.find(
|
||||
(tokenData) => tokenData.address === address?.toLowerCase(),
|
||||
);
|
||||
|
||||
let tokenIconUrl = tokenList[tokenAddress]?.iconUrl;
|
||||
|
||||
if (!process.env.TOKEN_DETECTION_V2 && !useTokenDetection && tokenIconUrl) {
|
||||
tokenIconUrl = `images/contract/${tokenIconUrl}`;
|
||||
}
|
||||
const usedIconUrl = iconUrl || tokenIconUrl || token?.image;
|
||||
const usedIconUrl = iconUrl || tokenMetadata?.iconUrl || token?.image;
|
||||
return {
|
||||
...token,
|
||||
primaryLabel: symbol,
|
||||
secondaryLabel: name || tokenList[tokenAddress]?.name,
|
||||
secondaryLabel: name || tokenMetadata?.name,
|
||||
rightPrimaryLabel:
|
||||
string && `${new BigNumber(string).round(6).toString()} ${symbol}`,
|
||||
rightSecondaryLabel: formattedFiat,
|
||||
@ -92,7 +69,7 @@ export function getRenderableTokenData(
|
||||
identiconAddress: usedIconUrl ? null : address,
|
||||
balance,
|
||||
decimals,
|
||||
name: name || tokenList[tokenAddress]?.name,
|
||||
name: name || tokenMetadata?.name,
|
||||
rawFiat,
|
||||
};
|
||||
}
|
||||
@ -108,15 +85,7 @@ export function useTokensToSearch({
|
||||
const conversionRate = useSelector(getConversionRate);
|
||||
const currentCurrency = useSelector(getCurrentCurrency);
|
||||
const defaultSwapsToken = useSelector(getSwapsDefaultToken, shallowEqual);
|
||||
const tokenList = useSelector(getTokenList, isEqual);
|
||||
const useTokenDetection = useSelector(getUseTokenDetection);
|
||||
let shuffledTokenList = shuffledTokensList;
|
||||
if (!process.env.TOKEN_DETECTION_V2) {
|
||||
// token from dynamic api list is fetched when useTokenDetection is true
|
||||
shuffledTokenList = useTokenDetection
|
||||
? shuffledTokensList
|
||||
: shuffledContractMap;
|
||||
}
|
||||
|
||||
const memoizedTopTokens = useEqualityCheck(topTokens);
|
||||
const memoizedUsersToken = useEqualityCheck(usersTokens);
|
||||
|
||||
@ -126,8 +95,7 @@ export function useTokensToSearch({
|
||||
conversionRate,
|
||||
currentCurrency,
|
||||
chainId,
|
||||
tokenList,
|
||||
useTokenDetection,
|
||||
shuffledTokensList,
|
||||
);
|
||||
const memoizedDefaultToken = useEqualityCheck(defaultToken);
|
||||
|
||||
@ -137,7 +105,7 @@ export function useTokensToSearch({
|
||||
? swapsTokens
|
||||
: [
|
||||
memoizedDefaultToken,
|
||||
...shuffledTokenList.filter(
|
||||
...shuffledTokensList.filter(
|
||||
(token) => token.symbol !== memoizedDefaultToken.symbol,
|
||||
),
|
||||
];
|
||||
@ -167,8 +135,7 @@ export function useTokensToSearch({
|
||||
conversionRate,
|
||||
currentCurrency,
|
||||
chainId,
|
||||
tokenList,
|
||||
useTokenDetection,
|
||||
shuffledTokensList,
|
||||
);
|
||||
if (tokenBucketPriority === TOKEN_BUCKET_PRIORITY.OWNED) {
|
||||
if (
|
||||
@ -224,8 +191,7 @@ export function useTokensToSearch({
|
||||
currentCurrency,
|
||||
memoizedDefaultToken,
|
||||
chainId,
|
||||
tokenList,
|
||||
useTokenDetection,
|
||||
shuffledTokensList,
|
||||
tokenBucketPriority,
|
||||
]);
|
||||
}
|
||||
|
@ -6,7 +6,9 @@ import { ERC20 } from '../../../../shared/constants/transaction';
|
||||
import ConfirmApproveContent from '.';
|
||||
|
||||
const renderComponent = (props) => {
|
||||
const store = configureMockStore([])({ metamask: {} });
|
||||
const store = configureMockStore([])({
|
||||
metamask: { provider: { chainId: '0x0' } },
|
||||
});
|
||||
return renderWithProvider(<ConfirmApproveContent {...props} />, store);
|
||||
};
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { connect } from 'react-redux';
|
||||
import { compose } from 'redux';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
|
||||
import { clearConfirmTransaction } from '../../ducks/confirm-transaction/confirm-transaction.duck';
|
||||
|
||||
import {
|
||||
@ -29,7 +30,6 @@ import {
|
||||
checkNetworkAndAccountSupports1559,
|
||||
getPreferences,
|
||||
doesAddressRequireLedgerHidConnection,
|
||||
getUseTokenDetection,
|
||||
getTokenList,
|
||||
getIsMultiLayerFeeNetwork,
|
||||
getEIP1559V2Enabled,
|
||||
@ -119,21 +119,10 @@ const mapStateToProps = (state, ownProps) => {
|
||||
}
|
||||
|
||||
const tokenList = getTokenList(state);
|
||||
const useTokenDetection = getUseTokenDetection(state);
|
||||
let casedTokenList = tokenList;
|
||||
if (!process.env.TOKEN_DETECTION_V2) {
|
||||
casedTokenList = useTokenDetection
|
||||
? tokenList
|
||||
: Object.keys(tokenList).reduce((acc, base) => {
|
||||
return {
|
||||
...acc,
|
||||
[base.toLowerCase()]: tokenList[base],
|
||||
};
|
||||
}, {});
|
||||
}
|
||||
|
||||
const toName =
|
||||
identities[toAddress]?.name ||
|
||||
casedTokenList[toAddress]?.name ||
|
||||
tokenList[toAddress?.toLowerCase()]?.name ||
|
||||
shortenAddress(toChecksumHexAddress(toAddress));
|
||||
|
||||
const checksummedAddress = toChecksumHexAddress(toAddress);
|
||||
|
@ -1,7 +1,6 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { getTokenTrackerLink } from '@metamask/etherscan-link';
|
||||
import contractMap from '@metamask/contract-metadata';
|
||||
import ZENDESK_URLS from '../../helpers/constants/zendesk-url';
|
||||
import {
|
||||
checkExistingAddresses,
|
||||
@ -11,7 +10,6 @@ import { tokenInfoGetter } from '../../helpers/utils/token-util';
|
||||
import {
|
||||
ADD_COLLECTIBLE_ROUTE,
|
||||
CONFIRM_IMPORT_TOKEN_ROUTE,
|
||||
EXPERIMENTAL_ROUTE,
|
||||
ADVANCED_ROUTE,
|
||||
} from '../../helpers/constants/routes';
|
||||
import TextField from '../../components/ui/text-field';
|
||||
@ -24,12 +22,10 @@ import Typography from '../../components/ui/typography';
|
||||
import { TYPOGRAPHY, FONT_WEIGHT } from '../../helpers/constants/design-system';
|
||||
import Button from '../../components/ui/button';
|
||||
import { TOKEN_STANDARDS } from '../../../shared/constants/transaction';
|
||||
import { STATIC_MAINNET_TOKEN_LIST } from '../../../shared/constants/tokens';
|
||||
import TokenSearch from './token-search';
|
||||
import TokenList from './token-list';
|
||||
|
||||
/*eslint-disable prefer-destructuring*/
|
||||
const TOKEN_DETECTION_V2 = process.env.TOKEN_DETECTION_V2;
|
||||
|
||||
const emptyAddr = '0x0000000000000000000000000000000000000000';
|
||||
|
||||
const MIN_DECIMAL_VALUE = 0;
|
||||
@ -113,7 +109,9 @@ class ImportToken extends Component {
|
||||
* The currently selected active address.
|
||||
*/
|
||||
selectedAddress: PropTypes.string,
|
||||
isTokenDetectionSupported: PropTypes.bool.isRequired,
|
||||
isDynamicTokenListAvailable: PropTypes.bool.isRequired,
|
||||
tokenDetectionInactiveOnNonMainnetSupportedNetwork:
|
||||
PropTypes.bool.isRequired,
|
||||
networkName: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
@ -225,9 +223,7 @@ class ImportToken extends Component {
|
||||
}
|
||||
|
||||
const { setPendingTokens, history, tokenList } = this.props;
|
||||
const tokenAddressList = Object.keys(tokenList).map((address) =>
|
||||
address.toLowerCase(),
|
||||
);
|
||||
const tokenAddressList = Object.keys(tokenList);
|
||||
const {
|
||||
customAddress: address,
|
||||
customSymbol: symbol,
|
||||
@ -278,7 +274,7 @@ class ImportToken extends Component {
|
||||
});
|
||||
const standardAddress = addHexPrefix(customAddress).toLowerCase();
|
||||
|
||||
const isMainnetToken = Object.keys(contractMap).some(
|
||||
const isMainnetToken = Object.keys(STATIC_MAINNET_TOKEN_LIST).some(
|
||||
(key) => key.toLowerCase() === customAddress.toLowerCase(),
|
||||
);
|
||||
|
||||
@ -410,7 +406,13 @@ class ImportToken extends Component {
|
||||
collectibleAddressError,
|
||||
} = this.state;
|
||||
|
||||
const { chainId, rpcPrefs, isTokenDetectionSupported } = this.props;
|
||||
const {
|
||||
chainId,
|
||||
rpcPrefs,
|
||||
isDynamicTokenListAvailable,
|
||||
tokenDetectionInactiveOnNonMainnetSupportedNetwork,
|
||||
history,
|
||||
} = this.props;
|
||||
const blockExplorerTokenLink = getTokenTrackerLink(
|
||||
customAddress,
|
||||
chainId,
|
||||
@ -424,11 +426,40 @@ class ImportToken extends Component {
|
||||
|
||||
return (
|
||||
<div className="import-token__custom-token-form">
|
||||
{TOKEN_DETECTION_V2 ? (
|
||||
{tokenDetectionInactiveOnNonMainnetSupportedNetwork ? (
|
||||
<ActionableMessage
|
||||
type={isTokenDetectionSupported ? 'warning' : 'default'}
|
||||
type="warning"
|
||||
message={t('customTokenWarningInTokenDetectionNetworkWithTDOFF', [
|
||||
<Button
|
||||
type="link"
|
||||
key="import-token-security-risk"
|
||||
className="import-token__link"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
href={ZENDESK_URLS.TOKEN_SAFETY_PRACTICES}
|
||||
>
|
||||
{t('tokenScamSecurityRisk')}
|
||||
</Button>,
|
||||
<Button
|
||||
type="link"
|
||||
key="import-token-token-detection-announcement"
|
||||
className="import-token__link"
|
||||
onClick={() =>
|
||||
history.push(`${ADVANCED_ROUTE}#token-description`)
|
||||
}
|
||||
>
|
||||
{t('inYourSettings')}
|
||||
</Button>,
|
||||
])}
|
||||
withRightButton
|
||||
useIcon
|
||||
iconFillColor="var(--color-warning-default)"
|
||||
/>
|
||||
) : (
|
||||
<ActionableMessage
|
||||
type={isDynamicTokenListAvailable ? 'warning' : 'default'}
|
||||
message={t(
|
||||
isTokenDetectionSupported
|
||||
isDynamicTokenListAvailable
|
||||
? 'customTokenWarningInTokenDetectionNetwork'
|
||||
: 'customTokenWarningInNonTokenDetectionNetwork',
|
||||
[
|
||||
@ -447,30 +478,11 @@ class ImportToken extends Component {
|
||||
withRightButton
|
||||
useIcon
|
||||
iconFillColor={
|
||||
isTokenDetectionSupported
|
||||
isDynamicTokenListAvailable
|
||||
? 'var(--color-warning-default)'
|
||||
: 'var(--color-info-default)'
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<ActionableMessage
|
||||
message={this.context.t('fakeTokenWarning', [
|
||||
<Button
|
||||
type="link"
|
||||
key="import-token-fake-token-warning"
|
||||
className="import-token__link"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
href={ZENDESK_URLS.TOKEN_SAFETY_PRACTICES}
|
||||
>
|
||||
{this.context.t('learnScamRisk')}
|
||||
</Button>,
|
||||
])}
|
||||
type="warning"
|
||||
withRightButton
|
||||
useIcon
|
||||
iconFillColor="var(--color-warning-default)"
|
||||
/>
|
||||
)}
|
||||
<TextField
|
||||
id="custom-address"
|
||||
@ -569,32 +581,19 @@ class ImportToken extends Component {
|
||||
<div className="import-token__search-token">
|
||||
{!useTokenDetection && (
|
||||
<ActionableMessage
|
||||
message={
|
||||
TOKEN_DETECTION_V2
|
||||
? t('tokenDetectionAlertMessage', [
|
||||
networkName,
|
||||
<Button
|
||||
type="link"
|
||||
key="token-detection-announcement"
|
||||
className="import-token__link"
|
||||
onClick={() =>
|
||||
history.push(`${ADVANCED_ROUTE}#token-description`)
|
||||
}
|
||||
>
|
||||
{t('enableFromSettings')}
|
||||
</Button>,
|
||||
])
|
||||
: this.context.t('tokenDetectionAnnouncement', [
|
||||
<Button
|
||||
type="link"
|
||||
key="token-detection-announcement"
|
||||
className="import-token__link"
|
||||
onClick={() => history.push(`${EXPERIMENTAL_ROUTE}`)}
|
||||
>
|
||||
{t('enableFromSettings')}
|
||||
</Button>,
|
||||
])
|
||||
}
|
||||
message={t('tokenDetectionAlertMessage', [
|
||||
networkName,
|
||||
<Button
|
||||
type="link"
|
||||
key="token-detection-announcement"
|
||||
className="import-token__link"
|
||||
onClick={() =>
|
||||
history.push(`${ADVANCED_ROUTE}#token-description`)
|
||||
}
|
||||
>
|
||||
{t('enableFromSettings')}
|
||||
</Button>,
|
||||
])}
|
||||
withRightButton
|
||||
useIcon
|
||||
iconFillColor="var(--color-primary-default)"
|
||||
|
@ -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) => {
|
||||
|
@ -39,6 +39,7 @@ describe('Import Token', () => {
|
||||
frequentRpcListDetail: [],
|
||||
identities: {},
|
||||
selectedAddress: '0x1231231',
|
||||
useTokenDetection: true,
|
||||
},
|
||||
history: {
|
||||
mostRecentOverviewPage: '/',
|
||||
|
@ -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) && (
|
||||
<div
|
||||
className={classnames('token-list__token', {
|
||||
'token-list__token--selected': selectedTokens[address],
|
||||
@ -56,7 +55,8 @@ export default class TokenList extends Component {
|
||||
<div
|
||||
className="token-list__token-icon"
|
||||
style={{
|
||||
backgroundImage: iconUrl && `url(${iconPath})`,
|
||||
backgroundImage:
|
||||
results[i]?.iconUrl && `url(${results[i]?.iconUrl})`,
|
||||
}}
|
||||
/>
|
||||
<div className="token-list__token-data">
|
||||
|
@ -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,
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -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 {
|
||||
<div className="settings-page__content-item">
|
||||
<span>{t('tokenDetection')}</span>
|
||||
<div className="settings-page__content-description">
|
||||
{t('tokenDetectionToggleDescription')}
|
||||
{t('tokenDetectionDescription')}
|
||||
</div>
|
||||
</div>
|
||||
<div className="settings-page__content-item">
|
||||
|
@ -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(
|
||||
<AdvancedTab
|
||||
ipfsGateway=""
|
||||
|
@ -16,8 +16,6 @@ export default class ExperimentalTab extends PureComponent {
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
useTokenDetection: PropTypes.bool,
|
||||
setUseTokenDetection: PropTypes.func,
|
||||
useCollectibleDetection: PropTypes.bool,
|
||||
setUseCollectibleDetection: PropTypes.func,
|
||||
setOpenSeaEnabled: PropTypes.func,
|
||||
@ -51,42 +49,6 @@ export default class ExperimentalTab extends PureComponent {
|
||||
handleSettingsRefs(t, t('experimental'), this.settingsRefs);
|
||||
}
|
||||
|
||||
renderTokenDetectionToggle() {
|
||||
const { t } = this.context;
|
||||
const { useTokenDetection, setUseTokenDetection } = this.props;
|
||||
|
||||
return (
|
||||
<div ref={this.settingsRefs[0]} className="settings-page__content-row">
|
||||
<div className="settings-page__content-item">
|
||||
<span>{t('useTokenDetection')}</span>
|
||||
<div className="settings-page__content-description">
|
||||
{t('useTokenDetectionDescription')}
|
||||
</div>
|
||||
</div>
|
||||
<div className="settings-page__content-item">
|
||||
<div className="settings-page__content-item-col">
|
||||
<ToggleButton
|
||||
value={useTokenDetection}
|
||||
onToggle={(value) => {
|
||||
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')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderCollectibleDetectionToggle() {
|
||||
if (!process.env.COLLECTIBLES_V1) {
|
||||
return null;
|
||||
@ -326,10 +288,6 @@ export default class ExperimentalTab extends PureComponent {
|
||||
render() {
|
||||
return (
|
||||
<div className="settings-page__body">
|
||||
{/* TODO: Remove during TOKEN_DETECTION_V2 feature flag clean up */}
|
||||
{process.env.TOKEN_DETECTION_V2
|
||||
? null
|
||||
: this.renderTokenDetectionToggle()}
|
||||
{this.renderOpenSeaEnabledToggle()}
|
||||
{this.renderCollectibleDetectionToggle()}
|
||||
{this.renderEIP1559V2EnabledToggle()}
|
||||
|
@ -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(<ExperimentalTab.WrappedComponent {...props} />, {
|
||||
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(<ExperimentalTab.WrappedComponent {...props} />, {
|
||||
context: {
|
||||
t: (str) => str,
|
||||
trackEvent: () => undefined,
|
||||
},
|
||||
});
|
||||
const useTokenDetectionText = wrapper.find({ text: 'Use token detection' });
|
||||
expect(useTokenDetectionText).toHaveLength(0);
|
||||
});
|
||||
});
|
@ -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)),
|
||||
|
@ -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()}
|
||||
/>
|
||||
|
@ -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,
|
||||
};
|
||||
};
|
||||
|
@ -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,
|
||||
);
|
||||
|
||||
|
@ -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() {
|
||||
<BuildQuote
|
||||
ethBalance={ethBalance}
|
||||
selectedAccountAddress={selectedAccountAddress}
|
||||
shuffledTokensList={listTokenValues}
|
||||
shuffledTokensList={shuffledTokensList}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
|
@ -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() {
|
||||
<Identicon
|
||||
diameter={32}
|
||||
address={token.address}
|
||||
image={tokenMetadata ? imagePath : token.image}
|
||||
image={tokenMetadata ? tokenMetadata.iconUrl : token.image}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
@ -183,7 +179,7 @@ export default function TokenDetailsPage() {
|
||||
? networkNickname ?? t('privateNetwork')
|
||||
: t(networkType)}
|
||||
</Typography>
|
||||
{process.env.TOKEN_DETECTION_V2 && aggregators && (
|
||||
{aggregators && (
|
||||
<>
|
||||
<Typography
|
||||
variant={TYPOGRAPHY.H9}
|
||||
|
@ -318,15 +318,12 @@ describe('TokenDetailsPage', () => {
|
||||
});
|
||||
|
||||
it('should render token list title in token details page', () => {
|
||||
process.env.TOKEN_DETECTION_V2 = true;
|
||||
const store = configureMockStore()(state);
|
||||
const { getByText } = renderWithProvider(<TokenDetailsPage />, 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(<TokenDetailsPage />, 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', () => {
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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"
|
||||
|
Loading…
Reference in New Issue
Block a user