diff --git a/.storybook/test-data.js b/.storybook/test-data.js index a8bf65c9b..4b60d17c9 100644 --- a/.storybook/test-data.js +++ b/.storybook/test-data.js @@ -19,6 +19,15 @@ const state = { url: 'https://metamask.github.io/test-dapp/', }, metamask: { + announcements: { + 22: { + id: 22, + date: null, + image: { + src: 'images/global-menu-block-explorer.svg', + }, + } + }, tokenList: { '0x514910771af9ca656af840dff83e8264ecf986ca': { address: '0x514910771af9ca656af840dff83e8264ecf986ca', diff --git a/CHANGELOG.md b/CHANGELOG.md index c3b398cc8..265973d75 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [10.34.4] +### Changed +- Updated snaps execution environment ([#20420](https://github.com/MetaMask/metamask-extension/pull/20420)) + +## [10.34.3] +### Fixed +- Ensure users phishing warning list is properly updated ([#20381](https://github.com/MetaMask/metamask-extension/pull/20381)) +- Fix inaccurate info in swaps flow for zero-balance tokens ([#20388](https://github.com/MetaMask/metamask-extension/pull/20388)) +- Fix 'Global Menu Explorer / Account Details' What's New notification display ([#20371](https://github.com/MetaMask/metamask-extension/pull/20371)) + ## [10.34.2] ### Added - Add Address Details and View on Explorer to Global Menu ([#20013](https://github.com/MetaMask/metamask-extension/pull/20013)) @@ -26,7 +36,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [10.34.1] ### Fixed - Fix bug that could cause a failure in the persistence of network related data ([#20080](https://github.com/MetaMask/metamask-extension/pull/20080)) -- Fix ([#20080](https://github.com/MetaMask/metamask-extension/pull/20080)) +- Fix possible crash when opening the network menu ([#20181](https://github.com/MetaMask/metamask-extension/pull/20181)) ## [10.34.0] ### Added @@ -3875,7 +3885,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Uncategorized - Added the ability to restore accounts from seed words. -[Unreleased]: https://github.com/MetaMask/metamask-extension/compare/v10.34.2...HEAD +[Unreleased]: https://github.com/MetaMask/metamask-extension/compare/v10.34.4...HEAD +[10.34.4]: https://github.com/MetaMask/metamask-extension/compare/v10.34.3...v10.34.4 +[10.34.3]: https://github.com/MetaMask/metamask-extension/compare/v10.34.2...v10.34.3 [10.34.2]: https://github.com/MetaMask/metamask-extension/compare/v10.34.1...v10.34.2 [10.34.1]: https://github.com/MetaMask/metamask-extension/compare/v10.34.0...v10.34.1 [10.34.0]: https://github.com/MetaMask/metamask-extension/compare/v10.33.1...v10.34.0 diff --git a/app/_locales/am/messages.json b/app/_locales/am/messages.json index cea1b3cc3..a815821b9 100644 --- a/app/_locales/am/messages.json +++ b/app/_locales/am/messages.json @@ -609,9 +609,6 @@ "searchResults": { "message": "ውጤቶችን ፈልግ" }, - "searchTokens": { - "message": "ተለዋጭ ስሞችን ፈልግ" - }, "securityAndPrivacy": { "message": "ደህንነት እና ግላዊነት" }, diff --git a/app/_locales/ar/messages.json b/app/_locales/ar/messages.json index 1be520429..db4a5a6fe 100644 --- a/app/_locales/ar/messages.json +++ b/app/_locales/ar/messages.json @@ -621,9 +621,6 @@ "searchResults": { "message": "نتائج البحث" }, - "searchTokens": { - "message": "البحث عن العملات الرمزية" - }, "securityAndPrivacy": { "message": "الأمن والخصوصية" }, diff --git a/app/_locales/bg/messages.json b/app/_locales/bg/messages.json index 3743fcbf0..5e41be222 100644 --- a/app/_locales/bg/messages.json +++ b/app/_locales/bg/messages.json @@ -620,9 +620,6 @@ "searchResults": { "message": "Резултати от търсенето" }, - "searchTokens": { - "message": "Търсене на маркери" - }, "securityAndPrivacy": { "message": "Сигурност и поверителност" }, diff --git a/app/_locales/bn/messages.json b/app/_locales/bn/messages.json index 941fb82da..dfa33cf7c 100644 --- a/app/_locales/bn/messages.json +++ b/app/_locales/bn/messages.json @@ -618,9 +618,6 @@ "searchResults": { "message": "অনুসন্ধানের ফলাফলগুলি" }, - "searchTokens": { - "message": "টোকেনগুলি অনুসন্ধান করুন" - }, "securityAndPrivacy": { "message": "নিরাপত্তা এবং গোপনীয়তা" }, diff --git a/app/_locales/ca/messages.json b/app/_locales/ca/messages.json index d5abf0507..fd33ec6f3 100644 --- a/app/_locales/ca/messages.json +++ b/app/_locales/ca/messages.json @@ -605,9 +605,6 @@ "searchResults": { "message": "Resultats de Cerca" }, - "searchTokens": { - "message": "Tokens per cercar" - }, "securityAndPrivacy": { "message": "Seguretat i privacitat" }, diff --git a/app/_locales/cs/messages.json b/app/_locales/cs/messages.json index 1a617a7b7..b0702badb 100644 --- a/app/_locales/cs/messages.json +++ b/app/_locales/cs/messages.json @@ -289,9 +289,6 @@ "search": { "message": "Hledat" }, - "searchTokens": { - "message": "Hledat tokeny" - }, "seedPhraseReq": { "message": "klíčové fráze mají 12 slov" }, diff --git a/app/_locales/da/messages.json b/app/_locales/da/messages.json index 85f449875..fb5b26f48 100644 --- a/app/_locales/da/messages.json +++ b/app/_locales/da/messages.json @@ -605,9 +605,6 @@ "searchResults": { "message": "Søg Resultater" }, - "searchTokens": { - "message": "Søg efter tokens" - }, "securityAndPrivacy": { "message": "Sikkerhed & Privatliv" }, diff --git a/app/_locales/de/messages.json b/app/_locales/de/messages.json index 9379779b6..01948c981 100644 --- a/app/_locales/de/messages.json +++ b/app/_locales/de/messages.json @@ -186,9 +186,6 @@ "addCustomNetwork": { "message": "Benutzerdefiniertes Netzwerk hinzufügen" }, - "addCustomToken": { - "message": "Kunden-Token hinzufügen" - }, "addEthereumChainConfirmationDescription": { "message": "Dadurch kann dieses Netzwerk innerhalb MetaMask verwendet werden." }, @@ -2980,9 +2977,6 @@ "searchResults": { "message": "Suchergebnisse" }, - "searchTokens": { - "message": "Token suchen" - }, "secretRecoveryPhrase": { "message": "Geheime Wiederherstellungsphrase" }, diff --git a/app/_locales/el/messages.json b/app/_locales/el/messages.json index 5875c30e2..691927f66 100644 --- a/app/_locales/el/messages.json +++ b/app/_locales/el/messages.json @@ -186,9 +186,6 @@ "addCustomNetwork": { "message": "Προσθήκη προσαρμοσμένου δικτύου" }, - "addCustomToken": { - "message": "Προσθήκη Προσαρμοσμένου Token" - }, "addEthereumChainConfirmationDescription": { "message": "Αυτό θα επιτρέψει σε αυτό το δίκτυο να χρησιμοποιηθεί στο MetaMask." }, @@ -2977,9 +2974,6 @@ "searchAccounts": { "message": "Αναζήτηση Λογαριασμών" }, - "searchTokens": { - "message": "Αναζήτηση Tokens" - }, "secretRecoveryPhrase": { "message": "Μυστική Φράση Ανάκτησης" }, diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 18e28b7d9..07d28c054 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -192,9 +192,6 @@ "addCustomNetwork": { "message": "Add custom network" }, - "addCustomToken": { - "message": "Add custom token" - }, "addEthereumChainConfirmationDescription": { "message": "This will allow this network to be used within MetaMask." }, @@ -568,6 +565,9 @@ "blockaidDescriptionBlurFarming": { "message": "If you approve this request, someone can steal your assets listed on Blur." }, + "blockaidDescriptionFailed": { + "message": "Because of an error, this request was not verified by the security provider. Proceed with caution." + }, "blockaidDescriptionMaliciousDomain": { "message": "You're interacting with a malicious domain. If you approve this request, you might lose your assets." }, @@ -583,6 +583,9 @@ "blockaidTitleDeceptive": { "message": "This is a deceptive request" }, + "blockaidTitleMayNotBeSafe": { + "message": "Request may not be safe" + }, "blockaidTitleSuspicious": { "message": "This is a suspicious request" }, @@ -3653,9 +3656,6 @@ "searchResults": { "message": "Search results" }, - "searchTokens": { - "message": "Search tokens" - }, "secretRecoveryPhrase": { "message": "Secret Recovery Phrase" }, diff --git a/app/_locales/es/messages.json b/app/_locales/es/messages.json index be5afad23..2f094077c 100644 --- a/app/_locales/es/messages.json +++ b/app/_locales/es/messages.json @@ -186,9 +186,6 @@ "addCustomNetwork": { "message": "Agregar red personalizada" }, - "addCustomToken": { - "message": "Añadir token personalizado" - }, "addEthereumChainConfirmationDescription": { "message": "Esto permitirá que la red se utilice en MetaMask." }, @@ -2980,9 +2977,6 @@ "searchResults": { "message": "Resultados de la búsqueda" }, - "searchTokens": { - "message": "Buscar tokens" - }, "secretRecoveryPhrase": { "message": "Frase secreta de recuperación" }, diff --git a/app/_locales/es_419/messages.json b/app/_locales/es_419/messages.json index 6e209ec6b..1c3adff3a 100644 --- a/app/_locales/es_419/messages.json +++ b/app/_locales/es_419/messages.json @@ -100,9 +100,6 @@ "addContact": { "message": "Agregar contacto" }, - "addCustomToken": { - "message": "Añadir token personalizado" - }, "addEthereumChainConfirmationDescription": { "message": "Esto permitirá que la red se utilice en MetaMask." }, @@ -1882,9 +1879,6 @@ "searchResults": { "message": "Resultados de la búsqueda" }, - "searchTokens": { - "message": "Buscar tokens" - }, "secretRecoveryPhrase": { "message": "Frase secreta de recuperación" }, diff --git a/app/_locales/et/messages.json b/app/_locales/et/messages.json index 073f73d75..e0b6889e9 100644 --- a/app/_locales/et/messages.json +++ b/app/_locales/et/messages.json @@ -614,9 +614,6 @@ "searchResults": { "message": "Otsingutulemused" }, - "searchTokens": { - "message": "Lubade otsimine" - }, "securityAndPrivacy": { "message": "Turvalisus ja privaatsus" }, diff --git a/app/_locales/fa/messages.json b/app/_locales/fa/messages.json index cf24c0c61..54db00df5 100644 --- a/app/_locales/fa/messages.json +++ b/app/_locales/fa/messages.json @@ -624,9 +624,6 @@ "searchResults": { "message": "نتایج جستجو" }, - "searchTokens": { - "message": "رمزیاب های جستجو" - }, "securityAndPrivacy": { "message": "امنیت و حریم خصوصی" }, diff --git a/app/_locales/fi/messages.json b/app/_locales/fi/messages.json index 478620170..9bf17fcef 100644 --- a/app/_locales/fi/messages.json +++ b/app/_locales/fi/messages.json @@ -621,9 +621,6 @@ "searchResults": { "message": "Hakutulokset" }, - "searchTokens": { - "message": "Hae tietueita" - }, "securityAndPrivacy": { "message": "Turva & yksityisyys" }, diff --git a/app/_locales/fil/messages.json b/app/_locales/fil/messages.json index b99f5a5ea..bb8270894 100644 --- a/app/_locales/fil/messages.json +++ b/app/_locales/fil/messages.json @@ -548,9 +548,6 @@ "searchResults": { "message": "Mga Resulta ng Paghahanap" }, - "searchTokens": { - "message": "Maghanap ng Mga Token" - }, "securityAndPrivacy": { "message": "Seguridad at Privacy" }, diff --git a/app/_locales/fr/messages.json b/app/_locales/fr/messages.json index 340f707c6..1e990f8d7 100644 --- a/app/_locales/fr/messages.json +++ b/app/_locales/fr/messages.json @@ -186,9 +186,6 @@ "addCustomNetwork": { "message": "Ajouter un réseau personnalisé" }, - "addCustomToken": { - "message": "Ajouter un jeton personnalisé" - }, "addEthereumChainConfirmationDescription": { "message": "Cela permettra d’utiliser ce réseau dans MetaMask." }, @@ -2980,9 +2977,6 @@ "searchResults": { "message": "Résultats de la recherche" }, - "searchTokens": { - "message": "Rechercher des jetons" - }, "secretRecoveryPhrase": { "message": "Phrase secrète de récupération" }, diff --git a/app/_locales/he/messages.json b/app/_locales/he/messages.json index 15dd5adac..ab70fe6ee 100644 --- a/app/_locales/he/messages.json +++ b/app/_locales/he/messages.json @@ -621,9 +621,6 @@ "searchResults": { "message": "תוצאות חיפוש" }, - "searchTokens": { - "message": "חיפוש טוקנים" - }, "securityAndPrivacy": { "message": "אבטחה ופרטיות" }, diff --git a/app/_locales/hi/messages.json b/app/_locales/hi/messages.json index 1a69af91b..d80b47528 100644 --- a/app/_locales/hi/messages.json +++ b/app/_locales/hi/messages.json @@ -186,9 +186,6 @@ "addCustomNetwork": { "message": "कस्टम नेटवर्क जोड़ें" }, - "addCustomToken": { - "message": "कस्टम टोकन जोड़ें" - }, "addEthereumChainConfirmationDescription": { "message": "इससे इस नेटवर्क को MetaMask के अंदर उपयोग करने की अनुमति मिलेगी।" }, @@ -2980,9 +2977,6 @@ "searchResults": { "message": "खोज परिणाम" }, - "searchTokens": { - "message": "टोकन खोजें" - }, "secretRecoveryPhrase": { "message": "सीक्रेट रिकवरी फ्रेज" }, diff --git a/app/_locales/hr/messages.json b/app/_locales/hr/messages.json index de36eb424..aa20baa31 100644 --- a/app/_locales/hr/messages.json +++ b/app/_locales/hr/messages.json @@ -617,9 +617,6 @@ "searchResults": { "message": "Rezultati pretraživanja" }, - "searchTokens": { - "message": "Pretraži tokene" - }, "securityAndPrivacy": { "message": "Sigurnost i privatnost" }, diff --git a/app/_locales/ht/messages.json b/app/_locales/ht/messages.json index 601e049be..d14e5f7a7 100644 --- a/app/_locales/ht/messages.json +++ b/app/_locales/ht/messages.json @@ -452,9 +452,6 @@ "searchResults": { "message": "Rezilta rechèch" }, - "searchTokens": { - "message": "Rechèch Tokens" - }, "seedPhraseReq": { "message": "Seed fraz yo se 12 long mo" }, diff --git a/app/_locales/hu/messages.json b/app/_locales/hu/messages.json index a1373e469..ca77ac427 100644 --- a/app/_locales/hu/messages.json +++ b/app/_locales/hu/messages.json @@ -617,9 +617,6 @@ "searchResults": { "message": "Keresési eredmények" }, - "searchTokens": { - "message": "Keresés a tokenek között" - }, "securityAndPrivacy": { "message": "Biztonság és adatvédelem" }, diff --git a/app/_locales/id/messages.json b/app/_locales/id/messages.json index 7a7fc5eb2..62e53b537 100644 --- a/app/_locales/id/messages.json +++ b/app/_locales/id/messages.json @@ -186,9 +186,6 @@ "addCustomNetwork": { "message": "Tambahkan jaringan khusus" }, - "addCustomToken": { - "message": "Tambahkan token kustom" - }, "addEthereumChainConfirmationDescription": { "message": "Tindakan ini akan membantu jaringan ini agar dapat digunakan dengan MetaMask." }, @@ -2980,9 +2977,6 @@ "searchResults": { "message": "Cari hasil" }, - "searchTokens": { - "message": "Cari token" - }, "secretRecoveryPhrase": { "message": "Frasa Pemulihan Rahasia" }, diff --git a/app/_locales/it/messages.json b/app/_locales/it/messages.json index a301f751f..4545daa82 100644 --- a/app/_locales/it/messages.json +++ b/app/_locales/it/messages.json @@ -155,9 +155,6 @@ "addContact": { "message": "Aggiungi contatto" }, - "addCustomToken": { - "message": "Aggiungi token personalizzato" - }, "addEthereumChainConfirmationDescription": { "message": "Ciò consentirà a questa rete di essere utilizzata all'interno di MetaMask." }, @@ -1380,9 +1377,6 @@ "searchResults": { "message": "Risultati Ricerca" }, - "searchTokens": { - "message": "Cerca Tokens" - }, "securityAndPrivacy": { "message": "Sicurezza & Privacy" }, diff --git a/app/_locales/ja/messages.json b/app/_locales/ja/messages.json index 58225cb1e..1c4c4827e 100644 --- a/app/_locales/ja/messages.json +++ b/app/_locales/ja/messages.json @@ -186,9 +186,6 @@ "addCustomNetwork": { "message": "カスタムネットワークを追加" }, - "addCustomToken": { - "message": "カスタムトークンを追加" - }, "addEthereumChainConfirmationDescription": { "message": "これにより、このネットワークはMetaMask内で使用できるようになります。" }, @@ -2980,9 +2977,6 @@ "searchResults": { "message": "検索結果" }, - "searchTokens": { - "message": "トークンを検索" - }, "secretRecoveryPhrase": { "message": "シークレットリカバリーフレーズ" }, diff --git a/app/_locales/kn/messages.json b/app/_locales/kn/messages.json index c4138b3a8..62a51a898 100644 --- a/app/_locales/kn/messages.json +++ b/app/_locales/kn/messages.json @@ -624,9 +624,6 @@ "searchResults": { "message": "ಹುಡುಕಾಟ ಫಲಿತಾಂಶಗಳು" }, - "searchTokens": { - "message": "ಟೋಕನ್‌ಗಳನ್ನು ಹುಡುಕಿ" - }, "securityAndPrivacy": { "message": "ಭದ್ರತೆ ಮತ್ತು ಗೌಪ್ಯತೆ" }, diff --git a/app/_locales/ko/messages.json b/app/_locales/ko/messages.json index fccf6041d..741f9de5a 100644 --- a/app/_locales/ko/messages.json +++ b/app/_locales/ko/messages.json @@ -186,9 +186,6 @@ "addCustomNetwork": { "message": "커스텀 네트워크 추가" }, - "addCustomToken": { - "message": "커스텀 토큰 추가" - }, "addEthereumChainConfirmationDescription": { "message": "이렇게 하면 MetaMask 내에서 이 네트워크를 사용할 수 있습니다." }, @@ -2980,9 +2977,6 @@ "searchResults": { "message": "검색 결과" }, - "searchTokens": { - "message": "토큰 검색" - }, "secretRecoveryPhrase": { "message": "비밀 복구 구문" }, diff --git a/app/_locales/lt/messages.json b/app/_locales/lt/messages.json index 36d58197b..f1294dbde 100644 --- a/app/_locales/lt/messages.json +++ b/app/_locales/lt/messages.json @@ -624,9 +624,6 @@ "searchResults": { "message": "Paieškos rezultatai" }, - "searchTokens": { - "message": "Ieškoti žetonų" - }, "securityAndPrivacy": { "message": "Sauga ir privatumas" }, diff --git a/app/_locales/lv/messages.json b/app/_locales/lv/messages.json index 70e2a3333..a2c9cc425 100644 --- a/app/_locales/lv/messages.json +++ b/app/_locales/lv/messages.json @@ -620,9 +620,6 @@ "searchResults": { "message": "Meklēšanas rezultāti" }, - "searchTokens": { - "message": "Meklēt marķierus" - }, "securityAndPrivacy": { "message": "Drošība un konfidencialitāte" }, diff --git a/app/_locales/ms/messages.json b/app/_locales/ms/messages.json index e1ff1b6e3..e94e1ccae 100644 --- a/app/_locales/ms/messages.json +++ b/app/_locales/ms/messages.json @@ -604,9 +604,6 @@ "searchResults": { "message": "Hasil Carian" }, - "searchTokens": { - "message": "Cari Token" - }, "securityAndPrivacy": { "message": "Keselamatan & Privasi" }, diff --git a/app/_locales/no/messages.json b/app/_locales/no/messages.json index e53ecfc02..820dbcf35 100644 --- a/app/_locales/no/messages.json +++ b/app/_locales/no/messages.json @@ -608,9 +608,6 @@ "searchResults": { "message": "Søkeresultater" }, - "searchTokens": { - "message": "Søk i sjetonger" - }, "securityAndPrivacy": { "message": "Sikkerhet og personvern" }, diff --git a/app/_locales/ph/messages.json b/app/_locales/ph/messages.json index 33e995e54..755965d2a 100644 --- a/app/_locales/ph/messages.json +++ b/app/_locales/ph/messages.json @@ -1211,9 +1211,6 @@ "searchResults": { "message": "Mga Resulta ng Paghahanap" }, - "searchTokens": { - "message": "Maghanap ng Mga Token" - }, "securityAndPrivacy": { "message": "Seguridad at Privacy" }, diff --git a/app/_locales/pl/messages.json b/app/_locales/pl/messages.json index 32664ebda..9ec3a0677 100644 --- a/app/_locales/pl/messages.json +++ b/app/_locales/pl/messages.json @@ -618,9 +618,6 @@ "searchResults": { "message": "Wyniki wyszukiwania" }, - "searchTokens": { - "message": "Szukaj tokenów" - }, "securityAndPrivacy": { "message": "Bezpieczeństwo i prywatność" }, diff --git a/app/_locales/pt/messages.json b/app/_locales/pt/messages.json index c3d06b188..540d8d39a 100644 --- a/app/_locales/pt/messages.json +++ b/app/_locales/pt/messages.json @@ -186,9 +186,6 @@ "addCustomNetwork": { "message": "Adicionar rede personalizada" }, - "addCustomToken": { - "message": "Adicionar token personalizado" - }, "addEthereumChainConfirmationDescription": { "message": "Isso permitirá que essa rede seja usada dentro da MetaMask." }, @@ -2980,9 +2977,6 @@ "searchResults": { "message": "Resultados da busca" }, - "searchTokens": { - "message": "Buscar tokens" - }, "secretRecoveryPhrase": { "message": "Frase Secreta de Recuperação" }, diff --git a/app/_locales/pt_BR/messages.json b/app/_locales/pt_BR/messages.json index b023dbb67..bde907241 100644 --- a/app/_locales/pt_BR/messages.json +++ b/app/_locales/pt_BR/messages.json @@ -100,9 +100,6 @@ "addContact": { "message": "Adicionar contato" }, - "addCustomToken": { - "message": "Adicionar token personalizado" - }, "addEthereumChainConfirmationDescription": { "message": "Isso permitirá que essa rede seja usada dentro da MetaMask." }, @@ -1882,9 +1879,6 @@ "searchResults": { "message": "Resultados da busca" }, - "searchTokens": { - "message": "Buscar tokens" - }, "secretRecoveryPhrase": { "message": "Frase de Recuperação Secreta" }, diff --git a/app/_locales/ro/messages.json b/app/_locales/ro/messages.json index d39e5b4e6..7b0ae2062 100644 --- a/app/_locales/ro/messages.json +++ b/app/_locales/ro/messages.json @@ -611,9 +611,6 @@ "searchResults": { "message": "Rezultate căutare" }, - "searchTokens": { - "message": "Căutați token-uri" - }, "securityAndPrivacy": { "message": "Securitate și confidențialitate" }, diff --git a/app/_locales/ru/messages.json b/app/_locales/ru/messages.json index 2e0f11573..22c7cff59 100644 --- a/app/_locales/ru/messages.json +++ b/app/_locales/ru/messages.json @@ -186,9 +186,6 @@ "addCustomNetwork": { "message": "Добавить пользовательскую сеть" }, - "addCustomToken": { - "message": "Добавить пользовательский токен" - }, "addEthereumChainConfirmationDescription": { "message": "Это позволит использовать эту сеть в MetaMask." }, @@ -2980,9 +2977,6 @@ "searchResults": { "message": "Результаты поиска" }, - "searchTokens": { - "message": "Поиск токенов" - }, "secretRecoveryPhrase": { "message": "Секретная фраза для восстановления" }, diff --git a/app/_locales/sk/messages.json b/app/_locales/sk/messages.json index 3dcf16933..277713b1c 100644 --- a/app/_locales/sk/messages.json +++ b/app/_locales/sk/messages.json @@ -596,9 +596,6 @@ "searchResults": { "message": "Výsledky vyhľadávania" }, - "searchTokens": { - "message": "Hledat tokeny" - }, "securityAndPrivacy": { "message": "Bezpečnosť a súkromie" }, diff --git a/app/_locales/sl/messages.json b/app/_locales/sl/messages.json index 92c40f8d4..b81cc3630 100644 --- a/app/_locales/sl/messages.json +++ b/app/_locales/sl/messages.json @@ -612,9 +612,6 @@ "searchResults": { "message": "Rezultati iskanja" }, - "searchTokens": { - "message": "Iskanje žetonov" - }, "securityAndPrivacy": { "message": "Varnost in zasebnost" }, diff --git a/app/_locales/sr/messages.json b/app/_locales/sr/messages.json index e061fa55a..54a833755 100644 --- a/app/_locales/sr/messages.json +++ b/app/_locales/sr/messages.json @@ -615,9 +615,6 @@ "searchResults": { "message": "Rezultati pretrage" }, - "searchTokens": { - "message": "Pretražite tokene" - }, "securityAndPrivacy": { "message": "Bezbednost i privatnost" }, diff --git a/app/_locales/sv/messages.json b/app/_locales/sv/messages.json index 9f02c1ce2..637c22227 100644 --- a/app/_locales/sv/messages.json +++ b/app/_locales/sv/messages.json @@ -608,9 +608,6 @@ "searchResults": { "message": "Sökresultat" }, - "searchTokens": { - "message": "Sök tokens" - }, "securityAndPrivacy": { "message": "Säkerhet och integritet" }, diff --git a/app/_locales/sw/messages.json b/app/_locales/sw/messages.json index 0ddfe8ac6..1b8fc27d0 100644 --- a/app/_locales/sw/messages.json +++ b/app/_locales/sw/messages.json @@ -602,9 +602,6 @@ "searchResults": { "message": "Matokeo ya Utafutaji" }, - "searchTokens": { - "message": "Tafuta Vianzio" - }, "securityAndPrivacy": { "message": "Ulinzi na Faragha" }, diff --git a/app/_locales/ta/messages.json b/app/_locales/ta/messages.json index eb6f2c499..909ff25ec 100644 --- a/app/_locales/ta/messages.json +++ b/app/_locales/ta/messages.json @@ -359,9 +359,6 @@ "search": { "message": "தேடல்" }, - "searchTokens": { - "message": "தேடல் டோக்கன்ஸ்" - }, "seedPhraseReq": { "message": "விதை வாக்கியங்கள் 12 வார்த்தைகள் நீண்டவை" }, diff --git a/app/_locales/tl/messages.json b/app/_locales/tl/messages.json index cff8ca4b2..82311d906 100644 --- a/app/_locales/tl/messages.json +++ b/app/_locales/tl/messages.json @@ -186,9 +186,6 @@ "addCustomNetwork": { "message": "Magdagdag ng custom na network" }, - "addCustomToken": { - "message": "Magdagdag ng Custom na Token" - }, "addEthereumChainConfirmationDescription": { "message": "Magpapahintulot ito sa network na ito na gamitin sa loob ng MetaMask." }, @@ -2980,9 +2977,6 @@ "searchResults": { "message": "Mga Resulta ng Paghahanap" }, - "searchTokens": { - "message": "Maghanap ng Mga Token" - }, "secretRecoveryPhrase": { "message": "Lihim na recovery phrase" }, diff --git a/app/_locales/tr/messages.json b/app/_locales/tr/messages.json index b181d5d95..b6ad12c28 100644 --- a/app/_locales/tr/messages.json +++ b/app/_locales/tr/messages.json @@ -186,9 +186,6 @@ "addCustomNetwork": { "message": "Özel ağ ekle" }, - "addCustomToken": { - "message": "Özel token ekle" - }, "addEthereumChainConfirmationDescription": { "message": "Bu, bu ağın MetaMas dahilinde kullanılmasına olanak tanıyacaktır." }, @@ -2980,9 +2977,6 @@ "searchResults": { "message": "Arama sonuçları" }, - "searchTokens": { - "message": "Token ara" - }, "secretRecoveryPhrase": { "message": "Gizli Kurtarma İfadesi" }, diff --git a/app/_locales/uk/messages.json b/app/_locales/uk/messages.json index e9505d2b2..bba5b27f1 100644 --- a/app/_locales/uk/messages.json +++ b/app/_locales/uk/messages.json @@ -624,9 +624,6 @@ "searchResults": { "message": "Результати пошуку" }, - "searchTokens": { - "message": "Шукати токени" - }, "securityAndPrivacy": { "message": "Безпека й конфіденційність" }, diff --git a/app/_locales/vi/messages.json b/app/_locales/vi/messages.json index 982edf876..ea52fc82b 100644 --- a/app/_locales/vi/messages.json +++ b/app/_locales/vi/messages.json @@ -186,9 +186,6 @@ "addCustomNetwork": { "message": "Thêm mạng tùy chỉnh" }, - "addCustomToken": { - "message": "Thêm token tùy chỉnh" - }, "addEthereumChainConfirmationDescription": { "message": "Thao tác này sẽ cho phép sử dụng mạng này trong MetaMask." }, @@ -2980,9 +2977,6 @@ "searchResults": { "message": "Kết quả tìm kiếm" }, - "searchTokens": { - "message": "Tìm kiếm token" - }, "secretRecoveryPhrase": { "message": "Cụm Mật Khẩu Khôi Phục Bí Mật" }, diff --git a/app/_locales/zh_CN/messages.json b/app/_locales/zh_CN/messages.json index 303e62866..2aade1fdf 100644 --- a/app/_locales/zh_CN/messages.json +++ b/app/_locales/zh_CN/messages.json @@ -186,9 +186,6 @@ "addCustomNetwork": { "message": "添加自定义网络" }, - "addCustomToken": { - "message": "添加自定义代币" - }, "addEthereumChainConfirmationDescription": { "message": "这将允许在 MetaMask 中使用此网络。" }, @@ -2980,9 +2977,6 @@ "searchResults": { "message": "搜索结果" }, - "searchTokens": { - "message": "搜索代币" - }, "secretRecoveryPhrase": { "message": "助记词" }, diff --git a/app/_locales/zh_TW/messages.json b/app/_locales/zh_TW/messages.json index 276d101e5..8a95e870f 100644 --- a/app/_locales/zh_TW/messages.json +++ b/app/_locales/zh_TW/messages.json @@ -42,9 +42,6 @@ "addContact": { "message": "新增合約" }, - "addCustomToken": { - "message": "Add Custom Token" - }, "addEthereumChainConfirmationDescription": { "message": "這會允許在 MetaMask 內使用這個網路。" }, @@ -1133,9 +1130,6 @@ "searchResults": { "message": "搜尋結果" }, - "searchTokens": { - "message": "搜尋代幣" - }, "secureWallet": { "message": "Secure Wallet" }, diff --git a/app/scripts/controllers/decrypt-message.ts b/app/scripts/controllers/decrypt-message.ts index fee01257f..a0d180806 100644 --- a/app/scripts/controllers/decrypt-message.ts +++ b/app/scripts/controllers/decrypt-message.ts @@ -45,7 +45,10 @@ export type CoreMessage = AbstractMessage & { }; export type StateMessage = Required< - Omit + Omit< + AbstractMessage, + 'securityAlertResponse' | 'securityProviderResponse' | 'metadata' | 'error' + > >; export type DecryptMessageControllerState = { diff --git a/app/scripts/controllers/encryption-public-key.ts b/app/scripts/controllers/encryption-public-key.ts index 8448fba8a..4c3bcc622 100644 --- a/app/scripts/controllers/encryption-public-key.ts +++ b/app/scripts/controllers/encryption-public-key.ts @@ -44,7 +44,10 @@ export type CoreMessage = AbstractMessage & { }; export type StateMessage = Required< - Omit + Omit< + AbstractMessage, + 'securityAlertResponse' | 'securityProviderResponse' | 'metadata' | 'error' + > > & { msgParams: string; }; diff --git a/app/scripts/lockdown-more.js b/app/scripts/lockdown-more.js index e6637602a..317052312 100644 --- a/app/scripts/lockdown-more.js +++ b/app/scripts/lockdown-more.js @@ -28,7 +28,7 @@ try { const namedIntrinsics = Reflect.ownKeys(new Compartment().globalThis); // These named intrinsics are not automatically hardened by `lockdown` - const shouldHardenManually = new Set(['eval', 'Function']); + const shouldHardenManually = new Set(['eval', 'Function', 'Symbol']); const globalProperties = new Set([ // universalPropertyNames is a constant added by lockdown to global scope diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 93777335a..fa120200f 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -89,6 +89,7 @@ import { ERC20, ERC721, } from '@metamask/controller-utils'; +import { wordlist } from '@metamask/scure-bip39/dist/wordlists/english'; ///: BEGIN:ONLY_INCLUDE_IN(build-mmi) import { toChecksumHexAddress } from '../../shared/modules/hexstring-utils'; @@ -2905,8 +2906,6 @@ export default class MetamaskController extends EventEmitter { const seedPhraseAsBuffer = Buffer.from(encodedSeedPhrase); - const { keyringController } = this; - // clear known identities this.preferencesController.setAddresses([]); @@ -2930,29 +2929,22 @@ export default class MetamaskController extends EventEmitter { this.txController.txStateManager.clearUnapprovedTxs(); // create new vault - const vault = await keyringController.createNewVaultAndRestore( + const vault = await this.coreKeyringController.createNewVaultAndRestore( password, - seedPhraseAsBuffer, + this._convertMnemonicToWordlistIndices(seedPhraseAsBuffer), ); const ethQuery = new EthQuery(this.provider); - accounts = await keyringController.getAccounts(); + accounts = await this.coreKeyringController.getAccounts(); lastBalance = await this.getBalance( accounts[accounts.length - 1], ethQuery, ); - const [primaryKeyring] = this.coreKeyringController.getKeyringsByType( - KeyringType.hdKeyTree, - ); - if (!primaryKeyring) { - throw new Error('MetamaskController - No HD Key Tree found'); - } - // seek out the first zero balance while (lastBalance !== '0x0') { - await keyringController.addNewAccount(primaryKeyring); - accounts = await keyringController.getAccounts(); + await this.coreKeyringController.addNewAccount(accounts.length); + accounts = await this.coreKeyringController.getAccounts(); lastBalance = await this.getBalance( accounts[accounts.length - 1], ethQuery, @@ -2962,7 +2954,7 @@ export default class MetamaskController extends EventEmitter { // remove extra zero balance account potentially created from seeking ahead if (accounts.length > 1 && lastBalance === '0x0') { await this.removeAccount(accounts[accounts.length - 1]); - accounts = await keyringController.getAccounts(); + accounts = await this.coreKeyringController.getAccounts(); } // This must be set as soon as possible to communicate to the @@ -2973,8 +2965,6 @@ export default class MetamaskController extends EventEmitter { this.preferencesController.getLedgerTransportPreference(); this.setLedgerTransportPreference(transportPreference); - // set new identities - this.preferencesController.setAddresses(accounts); this.selectFirstIdentity(); return vault; @@ -2983,6 +2973,20 @@ export default class MetamaskController extends EventEmitter { } } + /** + * Encodes a BIP-39 mnemonic as the indices of words in the English BIP-39 wordlist. + * + * @param {Buffer} mnemonic - The BIP-39 mnemonic. + * @returns {Buffer} The Unicode code points for the seed phrase formed from the words in the wordlist. + */ + _convertMnemonicToWordlistIndices(mnemonic) { + const indices = mnemonic + .toString() + .split(' ') + .map((word) => wordlist.indexOf(word)); + return new Uint8Array(new Uint16Array(indices).buffer); + } + /** * Get an account balance from the AccountTracker or request it directly from the network. * diff --git a/app/scripts/metamask-controller.test.js b/app/scripts/metamask-controller.test.js index b4fadf9d4..b11e34372 100644 --- a/app/scripts/metamask-controller.test.js +++ b/app/scripts/metamask-controller.test.js @@ -307,7 +307,7 @@ describe('MetaMaskController', function () { 'createNewVaultAndKeychain', ); sandbox.spy( - metamaskController.keyringController, + metamaskController.coreKeyringController, 'createNewVaultAndRestore', ); }); @@ -423,7 +423,7 @@ describe('MetaMaskController', function () { await metamaskController.createNewVaultAndRestore(password, TEST_SEED); assert( - metamaskController.keyringController.createNewVaultAndRestore + metamaskController.coreKeyringController.createNewVaultAndRestore .calledTwice, ); }); diff --git a/builds.yml b/builds.yml index 13b51de6c..4283eb26e 100644 --- a/builds.yml +++ b/builds.yml @@ -47,7 +47,7 @@ buildTypes: - desktop - build-flask - keyring-snaps - # - blockaid + - blockaid env: - INFURA_FLASK_PROJECT_ID - SEGMENT_FLASK_WRITE_KEY diff --git a/lavamoat/browserify/beta/policy.json b/lavamoat/browserify/beta/policy.json index a02644c04..3207a448f 100644 --- a/lavamoat/browserify/beta/policy.json +++ b/lavamoat/browserify/beta/policy.json @@ -380,7 +380,7 @@ "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, - "ganache>secp256k1>elliptic": true + "@metamask/ppom-validator>elliptic": true } }, "@ethersproject/hdnode>@ethersproject/transactions": { @@ -1088,6 +1088,7 @@ }, "@metamask/eth-ledger-bridge-keyring>eth-sig-util>ethereumjs-util": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>assert": true, "browserify>buffer": true, @@ -1095,8 +1096,7 @@ "ethereumjs-util>create-hash": true, "ethereumjs-util>ethereum-cryptography": true, "ethereumjs-util>rlp": true, - "ethereumjs-wallet>safe-buffer": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-wallet>safe-buffer": true } }, "@metamask/eth-ledger-bridge-keyring>hdkey": { @@ -1111,11 +1111,11 @@ "@metamask/eth-ledger-bridge-keyring>hdkey>secp256k1": { "packages": { "@metamask/eth-trezor-keyring>hdkey>secp256k1>bip66": true, + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>insert-module-globals>is-buffer": true, "ethereumjs-util>create-hash": true, - "ethereumjs-wallet>safe-buffer": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-wallet>safe-buffer": true } }, "@metamask/eth-snap-keyring>@metamask/eth-sig-util": { @@ -1422,11 +1422,11 @@ "@metamask/eth-trezor-keyring>hdkey>secp256k1": { "packages": { "@metamask/eth-trezor-keyring>hdkey>secp256k1>bip66": true, + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>insert-module-globals>is-buffer": true, "ethereumjs-util>create-hash": true, - "ethereumjs-wallet>safe-buffer": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-wallet>safe-buffer": true } }, "@metamask/eth-trezor-keyring>hdkey>secp256k1>bip66": { @@ -1802,6 +1802,33 @@ "eslint>optionator>fast-levenshtein": true } }, + "@metamask/ppom-validator>elliptic": { + "packages": { + "@metamask/ppom-validator>elliptic>brorand": true, + "@metamask/ppom-validator>elliptic>hmac-drbg": true, + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, + "@metamask/ppom-validator>elliptic>minimalistic-crypto-utils": true, + "bn.js": true, + "ethereumjs-util>ethereum-cryptography>hash.js": true, + "pumpify>inherits": true + } + }, + "@metamask/ppom-validator>elliptic>brorand": { + "globals": { + "crypto": true, + "msCrypto": true + }, + "packages": { + "browserify>browser-resolve": true + } + }, + "@metamask/ppom-validator>elliptic>hmac-drbg": { + "packages": { + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, + "@metamask/ppom-validator>elliptic>minimalistic-crypto-utils": true, + "ethereumjs-util>ethereum-cryptography>hash.js": true + } + }, "@metamask/rpc-methods": { "packages": { "@metamask/browser-passworder": true, @@ -2853,7 +2880,7 @@ }, "browserify>crypto-browserify>browserify-cipher>browserify-des>des.js": { "packages": { - "ganache>secp256k1>elliptic>minimalistic-assert": true, + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, "pumpify>inherits": true } }, @@ -2865,6 +2892,7 @@ }, "browserify>crypto-browserify>browserify-sign": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>buffer": true, "browserify>crypto-browserify>create-hmac": true, @@ -2872,15 +2900,14 @@ "browserify>crypto-browserify>public-encrypt>parse-asn1": true, "browserify>stream-browserify": true, "ethereumjs-util>create-hash": true, - "ganache>secp256k1>elliptic": true, "pumpify>inherits": true } }, "browserify>crypto-browserify>create-ecdh": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, - "browserify>buffer": true, - "ganache>secp256k1>elliptic": true + "browserify>buffer": true } }, "browserify>crypto-browserify>create-hmac": { @@ -2903,8 +2930,8 @@ }, "browserify>crypto-browserify>diffie-hellman>miller-rabin": { "packages": { - "bn.js": true, - "ganache>secp256k1>elliptic>brorand": true + "@metamask/ppom-validator>elliptic>brorand": true, + "bn.js": true } }, "browserify>crypto-browserify>pbkdf2": { @@ -2951,10 +2978,10 @@ }, "browserify>crypto-browserify>public-encrypt>parse-asn1>asn1.js": { "packages": { + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, "bn.js": true, "browserify>buffer": true, "browserify>vm-browserify": true, - "ganache>secp256k1>elliptic>minimalistic-assert": true, "pumpify>inherits": true } }, @@ -3315,6 +3342,7 @@ "packages": { "@ethereumjs/common>crc-32": true, "@ethersproject/abi": true, + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>buffer": true, "eth-lattice-keyring>gridplus-sdk>@ethereumjs/common": true, @@ -3331,7 +3359,6 @@ "ethereumjs-util>ethereum-cryptography>hash.js": true, "ethereumjs-wallet>aes-js": true, "ethereumjs-wallet>bs58check": true, - "ganache>secp256k1>elliptic": true, "lodash": true } }, @@ -3455,7 +3482,7 @@ }, "eth-lattice-keyring>gridplus-sdk>secp256k1": { "packages": { - "ganache>secp256k1>elliptic": true + "@metamask/ppom-validator>elliptic": true } }, "eth-lattice-keyring>gridplus-sdk>uuid": { @@ -3496,6 +3523,7 @@ }, "eth-sig-util>ethereumjs-util": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>assert": true, "browserify>buffer": true, @@ -3503,8 +3531,7 @@ "ethereumjs-util>create-hash": true, "ethereumjs-util>ethereum-cryptography": true, "ethereumjs-util>rlp": true, - "ethereumjs-wallet>safe-buffer": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-wallet>safe-buffer": true } }, "eth-sig-util>ethereumjs-util>ethjs-util": { @@ -3542,14 +3569,14 @@ }, "ethereumjs-abi>ethereumjs-util": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>assert": true, "browserify>buffer": true, "eth-sig-util>ethereumjs-util>ethjs-util": true, "ethereumjs-util>create-hash": true, "ethereumjs-util>ethereum-cryptography": true, - "ethereumjs-util>rlp": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-util>rlp": true } }, "ethereumjs-util": { @@ -3642,7 +3669,7 @@ }, "ethereumjs-util>ethereum-cryptography>hash.js": { "packages": { - "ganache>secp256k1>elliptic>minimalistic-assert": true, + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, "pumpify>inherits": true } }, @@ -3674,7 +3701,7 @@ }, "ethereumjs-util>ethereum-cryptography>secp256k1": { "packages": { - "ganache>secp256k1>elliptic": true + "@metamask/ppom-validator>elliptic": true } }, "ethereumjs-util>rlp": { @@ -3720,14 +3747,14 @@ }, "ethereumjs-wallet>ethereumjs-util": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>assert": true, "browserify>buffer": true, "eth-sig-util>ethereumjs-util>ethjs-util": true, "ethereumjs-util>create-hash": true, "ethereumjs-util>ethereum-cryptography": true, - "ethereumjs-util>rlp": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-util>rlp": true } }, "ethereumjs-wallet>randombytes": { @@ -3926,33 +3953,6 @@ "define": true } }, - "ganache>secp256k1>elliptic": { - "packages": { - "bn.js": true, - "ethereumjs-util>ethereum-cryptography>hash.js": true, - "ganache>secp256k1>elliptic>brorand": true, - "ganache>secp256k1>elliptic>hmac-drbg": true, - "ganache>secp256k1>elliptic>minimalistic-assert": true, - "ganache>secp256k1>elliptic>minimalistic-crypto-utils": true, - "pumpify>inherits": true - } - }, - "ganache>secp256k1>elliptic>brorand": { - "globals": { - "crypto": true, - "msCrypto": true - }, - "packages": { - "browserify>browser-resolve": true - } - }, - "ganache>secp256k1>elliptic>hmac-drbg": { - "packages": { - "ethereumjs-util>ethereum-cryptography>hash.js": true, - "ganache>secp256k1>elliptic>minimalistic-assert": true, - "ganache>secp256k1>elliptic>minimalistic-crypto-utils": true - } - }, "globalthis>define-properties": { "packages": { "globalthis>define-properties>has-property-descriptors": true, diff --git a/lavamoat/browserify/desktop/policy.json b/lavamoat/browserify/desktop/policy.json index 01e56f8b9..cc3765332 100644 --- a/lavamoat/browserify/desktop/policy.json +++ b/lavamoat/browserify/desktop/policy.json @@ -380,7 +380,7 @@ "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, - "ganache>secp256k1>elliptic": true + "@metamask/ppom-validator>elliptic": true } }, "@ethersproject/hdnode>@ethersproject/transactions": { @@ -1159,6 +1159,7 @@ }, "@metamask/eth-ledger-bridge-keyring>eth-sig-util>ethereumjs-util": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>assert": true, "browserify>buffer": true, @@ -1166,8 +1167,7 @@ "ethereumjs-util>create-hash": true, "ethereumjs-util>ethereum-cryptography": true, "ethereumjs-util>rlp": true, - "ethereumjs-wallet>safe-buffer": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-wallet>safe-buffer": true } }, "@metamask/eth-ledger-bridge-keyring>hdkey": { @@ -1182,11 +1182,11 @@ "@metamask/eth-ledger-bridge-keyring>hdkey>secp256k1": { "packages": { "@metamask/eth-trezor-keyring>hdkey>secp256k1>bip66": true, + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>insert-module-globals>is-buffer": true, "ethereumjs-util>create-hash": true, - "ethereumjs-wallet>safe-buffer": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-wallet>safe-buffer": true } }, "@metamask/eth-snap-keyring": { @@ -1550,11 +1550,11 @@ "@metamask/eth-trezor-keyring>hdkey>secp256k1": { "packages": { "@metamask/eth-trezor-keyring>hdkey>secp256k1>bip66": true, + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>insert-module-globals>is-buffer": true, "ethereumjs-util>create-hash": true, - "ethereumjs-wallet>safe-buffer": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-wallet>safe-buffer": true } }, "@metamask/eth-trezor-keyring>hdkey>secp256k1>bip66": { @@ -2006,6 +2006,33 @@ "@metamask/post-message-stream>readable-stream>safe-buffer": true } }, + "@metamask/ppom-validator>elliptic": { + "packages": { + "@metamask/ppom-validator>elliptic>brorand": true, + "@metamask/ppom-validator>elliptic>hmac-drbg": true, + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, + "@metamask/ppom-validator>elliptic>minimalistic-crypto-utils": true, + "bn.js": true, + "ethereumjs-util>ethereum-cryptography>hash.js": true, + "pumpify>inherits": true + } + }, + "@metamask/ppom-validator>elliptic>brorand": { + "globals": { + "crypto": true, + "msCrypto": true + }, + "packages": { + "browserify>browser-resolve": true + } + }, + "@metamask/ppom-validator>elliptic>hmac-drbg": { + "packages": { + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, + "@metamask/ppom-validator>elliptic>minimalistic-crypto-utils": true, + "ethereumjs-util>ethereum-cryptography>hash.js": true + } + }, "@metamask/providers>@metamask/object-multiplex": { "globals": { "console.warn": true @@ -3388,7 +3415,7 @@ }, "browserify>crypto-browserify>browserify-cipher>browserify-des>des.js": { "packages": { - "ganache>secp256k1>elliptic>minimalistic-assert": true, + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, "pumpify>inherits": true } }, @@ -3400,6 +3427,7 @@ }, "browserify>crypto-browserify>browserify-sign": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>buffer": true, "browserify>crypto-browserify>create-hmac": true, @@ -3407,15 +3435,14 @@ "browserify>crypto-browserify>public-encrypt>parse-asn1": true, "browserify>stream-browserify": true, "ethereumjs-util>create-hash": true, - "ganache>secp256k1>elliptic": true, "pumpify>inherits": true } }, "browserify>crypto-browserify>create-ecdh": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, - "browserify>buffer": true, - "ganache>secp256k1>elliptic": true + "browserify>buffer": true } }, "browserify>crypto-browserify>create-hmac": { @@ -3438,8 +3465,8 @@ }, "browserify>crypto-browserify>diffie-hellman>miller-rabin": { "packages": { - "bn.js": true, - "ganache>secp256k1>elliptic>brorand": true + "@metamask/ppom-validator>elliptic>brorand": true, + "bn.js": true } }, "browserify>crypto-browserify>pbkdf2": { @@ -3486,10 +3513,10 @@ }, "browserify>crypto-browserify>public-encrypt>parse-asn1>asn1.js": { "packages": { + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, "bn.js": true, "browserify>buffer": true, "browserify>vm-browserify": true, - "ganache>secp256k1>elliptic>minimalistic-assert": true, "pumpify>inherits": true } }, @@ -3866,6 +3893,7 @@ "packages": { "@ethereumjs/common>crc-32": true, "@ethersproject/abi": true, + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>buffer": true, "eth-lattice-keyring>gridplus-sdk>@ethereumjs/common": true, @@ -3882,7 +3910,6 @@ "ethereumjs-util>ethereum-cryptography>hash.js": true, "ethereumjs-wallet>aes-js": true, "ethereumjs-wallet>bs58check": true, - "ganache>secp256k1>elliptic": true, "lodash": true } }, @@ -4006,7 +4033,7 @@ }, "eth-lattice-keyring>gridplus-sdk>secp256k1": { "packages": { - "ganache>secp256k1>elliptic": true + "@metamask/ppom-validator>elliptic": true } }, "eth-lattice-keyring>gridplus-sdk>uuid": { @@ -4047,6 +4074,7 @@ }, "eth-sig-util>ethereumjs-util": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>assert": true, "browserify>buffer": true, @@ -4054,8 +4082,7 @@ "ethereumjs-util>create-hash": true, "ethereumjs-util>ethereum-cryptography": true, "ethereumjs-util>rlp": true, - "ethereumjs-wallet>safe-buffer": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-wallet>safe-buffer": true } }, "eth-sig-util>ethereumjs-util>ethjs-util": { @@ -4093,14 +4120,14 @@ }, "ethereumjs-abi>ethereumjs-util": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>assert": true, "browserify>buffer": true, "eth-sig-util>ethereumjs-util>ethjs-util": true, "ethereumjs-util>create-hash": true, "ethereumjs-util>ethereum-cryptography": true, - "ethereumjs-util>rlp": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-util>rlp": true } }, "ethereumjs-util": { @@ -4193,7 +4220,7 @@ }, "ethereumjs-util>ethereum-cryptography>hash.js": { "packages": { - "ganache>secp256k1>elliptic>minimalistic-assert": true, + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, "pumpify>inherits": true } }, @@ -4225,7 +4252,7 @@ }, "ethereumjs-util>ethereum-cryptography>secp256k1": { "packages": { - "ganache>secp256k1>elliptic": true + "@metamask/ppom-validator>elliptic": true } }, "ethereumjs-util>rlp": { @@ -4271,14 +4298,14 @@ }, "ethereumjs-wallet>ethereumjs-util": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>assert": true, "browserify>buffer": true, "eth-sig-util>ethereumjs-util>ethjs-util": true, "ethereumjs-util>create-hash": true, "ethereumjs-util>ethereum-cryptography": true, - "ethereumjs-util>rlp": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-util>rlp": true } }, "ethereumjs-wallet>randombytes": { @@ -4477,33 +4504,6 @@ "define": true } }, - "ganache>secp256k1>elliptic": { - "packages": { - "bn.js": true, - "ethereumjs-util>ethereum-cryptography>hash.js": true, - "ganache>secp256k1>elliptic>brorand": true, - "ganache>secp256k1>elliptic>hmac-drbg": true, - "ganache>secp256k1>elliptic>minimalistic-assert": true, - "ganache>secp256k1>elliptic>minimalistic-crypto-utils": true, - "pumpify>inherits": true - } - }, - "ganache>secp256k1>elliptic>brorand": { - "globals": { - "crypto": true, - "msCrypto": true - }, - "packages": { - "browserify>browser-resolve": true - } - }, - "ganache>secp256k1>elliptic>hmac-drbg": { - "packages": { - "ethereumjs-util>ethereum-cryptography>hash.js": true, - "ganache>secp256k1>elliptic>minimalistic-assert": true, - "ganache>secp256k1>elliptic>minimalistic-crypto-utils": true - } - }, "globalthis>define-properties": { "packages": { "globalthis>define-properties>has-property-descriptors": true, diff --git a/lavamoat/browserify/flask/policy.json b/lavamoat/browserify/flask/policy.json index 01e56f8b9..08100deb2 100644 --- a/lavamoat/browserify/flask/policy.json +++ b/lavamoat/browserify/flask/policy.json @@ -380,7 +380,7 @@ "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, - "ganache>secp256k1>elliptic": true + "@metamask/ppom-validator>elliptic": true } }, "@ethersproject/hdnode>@ethersproject/transactions": { @@ -1159,6 +1159,7 @@ }, "@metamask/eth-ledger-bridge-keyring>eth-sig-util>ethereumjs-util": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>assert": true, "browserify>buffer": true, @@ -1166,8 +1167,7 @@ "ethereumjs-util>create-hash": true, "ethereumjs-util>ethereum-cryptography": true, "ethereumjs-util>rlp": true, - "ethereumjs-wallet>safe-buffer": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-wallet>safe-buffer": true } }, "@metamask/eth-ledger-bridge-keyring>hdkey": { @@ -1182,11 +1182,11 @@ "@metamask/eth-ledger-bridge-keyring>hdkey>secp256k1": { "packages": { "@metamask/eth-trezor-keyring>hdkey>secp256k1>bip66": true, + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>insert-module-globals>is-buffer": true, "ethereumjs-util>create-hash": true, - "ethereumjs-wallet>safe-buffer": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-wallet>safe-buffer": true } }, "@metamask/eth-snap-keyring": { @@ -1550,11 +1550,11 @@ "@metamask/eth-trezor-keyring>hdkey>secp256k1": { "packages": { "@metamask/eth-trezor-keyring>hdkey>secp256k1>bip66": true, + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>insert-module-globals>is-buffer": true, "ethereumjs-util>create-hash": true, - "ethereumjs-wallet>safe-buffer": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-wallet>safe-buffer": true } }, "@metamask/eth-trezor-keyring>hdkey>secp256k1>bip66": { @@ -2006,6 +2006,48 @@ "@metamask/post-message-stream>readable-stream>safe-buffer": true } }, + "@metamask/ppom-validator": { + "globals": { + "clearInterval": true, + "console.error": true, + "setInterval": true + }, + "packages": { + "@metamask/base-controller": true, + "@metamask/controller-utils": true, + "@metamask/ppom-validator>elliptic": true, + "await-semaphore": true, + "browserify>buffer": true, + "eth-query>json-rpc-random-id": true + } + }, + "@metamask/ppom-validator>elliptic": { + "packages": { + "@metamask/ppom-validator>elliptic>brorand": true, + "@metamask/ppom-validator>elliptic>hmac-drbg": true, + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, + "@metamask/ppom-validator>elliptic>minimalistic-crypto-utils": true, + "bn.js": true, + "ethereumjs-util>ethereum-cryptography>hash.js": true, + "pumpify>inherits": true + } + }, + "@metamask/ppom-validator>elliptic>brorand": { + "globals": { + "crypto": true, + "msCrypto": true + }, + "packages": { + "browserify>browser-resolve": true + } + }, + "@metamask/ppom-validator>elliptic>hmac-drbg": { + "packages": { + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, + "@metamask/ppom-validator>elliptic>minimalistic-crypto-utils": true, + "ethereumjs-util>ethereum-cryptography>hash.js": true + } + }, "@metamask/providers>@metamask/object-multiplex": { "globals": { "console.warn": true @@ -3388,7 +3430,7 @@ }, "browserify>crypto-browserify>browserify-cipher>browserify-des>des.js": { "packages": { - "ganache>secp256k1>elliptic>minimalistic-assert": true, + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, "pumpify>inherits": true } }, @@ -3400,6 +3442,7 @@ }, "browserify>crypto-browserify>browserify-sign": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>buffer": true, "browserify>crypto-browserify>create-hmac": true, @@ -3407,15 +3450,14 @@ "browserify>crypto-browserify>public-encrypt>parse-asn1": true, "browserify>stream-browserify": true, "ethereumjs-util>create-hash": true, - "ganache>secp256k1>elliptic": true, "pumpify>inherits": true } }, "browserify>crypto-browserify>create-ecdh": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, - "browserify>buffer": true, - "ganache>secp256k1>elliptic": true + "browserify>buffer": true } }, "browserify>crypto-browserify>create-hmac": { @@ -3438,8 +3480,8 @@ }, "browserify>crypto-browserify>diffie-hellman>miller-rabin": { "packages": { - "bn.js": true, - "ganache>secp256k1>elliptic>brorand": true + "@metamask/ppom-validator>elliptic>brorand": true, + "bn.js": true } }, "browserify>crypto-browserify>pbkdf2": { @@ -3486,10 +3528,10 @@ }, "browserify>crypto-browserify>public-encrypt>parse-asn1>asn1.js": { "packages": { + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, "bn.js": true, "browserify>buffer": true, "browserify>vm-browserify": true, - "ganache>secp256k1>elliptic>minimalistic-assert": true, "pumpify>inherits": true } }, @@ -3866,6 +3908,7 @@ "packages": { "@ethereumjs/common>crc-32": true, "@ethersproject/abi": true, + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>buffer": true, "eth-lattice-keyring>gridplus-sdk>@ethereumjs/common": true, @@ -3882,7 +3925,6 @@ "ethereumjs-util>ethereum-cryptography>hash.js": true, "ethereumjs-wallet>aes-js": true, "ethereumjs-wallet>bs58check": true, - "ganache>secp256k1>elliptic": true, "lodash": true } }, @@ -4006,7 +4048,7 @@ }, "eth-lattice-keyring>gridplus-sdk>secp256k1": { "packages": { - "ganache>secp256k1>elliptic": true + "@metamask/ppom-validator>elliptic": true } }, "eth-lattice-keyring>gridplus-sdk>uuid": { @@ -4047,6 +4089,7 @@ }, "eth-sig-util>ethereumjs-util": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>assert": true, "browserify>buffer": true, @@ -4054,8 +4097,7 @@ "ethereumjs-util>create-hash": true, "ethereumjs-util>ethereum-cryptography": true, "ethereumjs-util>rlp": true, - "ethereumjs-wallet>safe-buffer": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-wallet>safe-buffer": true } }, "eth-sig-util>ethereumjs-util>ethjs-util": { @@ -4093,14 +4135,14 @@ }, "ethereumjs-abi>ethereumjs-util": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>assert": true, "browserify>buffer": true, "eth-sig-util>ethereumjs-util>ethjs-util": true, "ethereumjs-util>create-hash": true, "ethereumjs-util>ethereum-cryptography": true, - "ethereumjs-util>rlp": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-util>rlp": true } }, "ethereumjs-util": { @@ -4193,7 +4235,7 @@ }, "ethereumjs-util>ethereum-cryptography>hash.js": { "packages": { - "ganache>secp256k1>elliptic>minimalistic-assert": true, + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, "pumpify>inherits": true } }, @@ -4225,7 +4267,7 @@ }, "ethereumjs-util>ethereum-cryptography>secp256k1": { "packages": { - "ganache>secp256k1>elliptic": true + "@metamask/ppom-validator>elliptic": true } }, "ethereumjs-util>rlp": { @@ -4271,14 +4313,14 @@ }, "ethereumjs-wallet>ethereumjs-util": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>assert": true, "browserify>buffer": true, "eth-sig-util>ethereumjs-util>ethjs-util": true, "ethereumjs-util>create-hash": true, "ethereumjs-util>ethereum-cryptography": true, - "ethereumjs-util>rlp": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-util>rlp": true } }, "ethereumjs-wallet>randombytes": { @@ -4477,33 +4519,6 @@ "define": true } }, - "ganache>secp256k1>elliptic": { - "packages": { - "bn.js": true, - "ethereumjs-util>ethereum-cryptography>hash.js": true, - "ganache>secp256k1>elliptic>brorand": true, - "ganache>secp256k1>elliptic>hmac-drbg": true, - "ganache>secp256k1>elliptic>minimalistic-assert": true, - "ganache>secp256k1>elliptic>minimalistic-crypto-utils": true, - "pumpify>inherits": true - } - }, - "ganache>secp256k1>elliptic>brorand": { - "globals": { - "crypto": true, - "msCrypto": true - }, - "packages": { - "browserify>browser-resolve": true - } - }, - "ganache>secp256k1>elliptic>hmac-drbg": { - "packages": { - "ethereumjs-util>ethereum-cryptography>hash.js": true, - "ganache>secp256k1>elliptic>minimalistic-assert": true, - "ganache>secp256k1>elliptic>minimalistic-crypto-utils": true - } - }, "globalthis>define-properties": { "packages": { "globalthis>define-properties>has-property-descriptors": true, diff --git a/lavamoat/browserify/main/policy.json b/lavamoat/browserify/main/policy.json index a02644c04..3207a448f 100644 --- a/lavamoat/browserify/main/policy.json +++ b/lavamoat/browserify/main/policy.json @@ -380,7 +380,7 @@ "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, - "ganache>secp256k1>elliptic": true + "@metamask/ppom-validator>elliptic": true } }, "@ethersproject/hdnode>@ethersproject/transactions": { @@ -1088,6 +1088,7 @@ }, "@metamask/eth-ledger-bridge-keyring>eth-sig-util>ethereumjs-util": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>assert": true, "browserify>buffer": true, @@ -1095,8 +1096,7 @@ "ethereumjs-util>create-hash": true, "ethereumjs-util>ethereum-cryptography": true, "ethereumjs-util>rlp": true, - "ethereumjs-wallet>safe-buffer": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-wallet>safe-buffer": true } }, "@metamask/eth-ledger-bridge-keyring>hdkey": { @@ -1111,11 +1111,11 @@ "@metamask/eth-ledger-bridge-keyring>hdkey>secp256k1": { "packages": { "@metamask/eth-trezor-keyring>hdkey>secp256k1>bip66": true, + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>insert-module-globals>is-buffer": true, "ethereumjs-util>create-hash": true, - "ethereumjs-wallet>safe-buffer": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-wallet>safe-buffer": true } }, "@metamask/eth-snap-keyring>@metamask/eth-sig-util": { @@ -1422,11 +1422,11 @@ "@metamask/eth-trezor-keyring>hdkey>secp256k1": { "packages": { "@metamask/eth-trezor-keyring>hdkey>secp256k1>bip66": true, + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>insert-module-globals>is-buffer": true, "ethereumjs-util>create-hash": true, - "ethereumjs-wallet>safe-buffer": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-wallet>safe-buffer": true } }, "@metamask/eth-trezor-keyring>hdkey>secp256k1>bip66": { @@ -1802,6 +1802,33 @@ "eslint>optionator>fast-levenshtein": true } }, + "@metamask/ppom-validator>elliptic": { + "packages": { + "@metamask/ppom-validator>elliptic>brorand": true, + "@metamask/ppom-validator>elliptic>hmac-drbg": true, + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, + "@metamask/ppom-validator>elliptic>minimalistic-crypto-utils": true, + "bn.js": true, + "ethereumjs-util>ethereum-cryptography>hash.js": true, + "pumpify>inherits": true + } + }, + "@metamask/ppom-validator>elliptic>brorand": { + "globals": { + "crypto": true, + "msCrypto": true + }, + "packages": { + "browserify>browser-resolve": true + } + }, + "@metamask/ppom-validator>elliptic>hmac-drbg": { + "packages": { + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, + "@metamask/ppom-validator>elliptic>minimalistic-crypto-utils": true, + "ethereumjs-util>ethereum-cryptography>hash.js": true + } + }, "@metamask/rpc-methods": { "packages": { "@metamask/browser-passworder": true, @@ -2853,7 +2880,7 @@ }, "browserify>crypto-browserify>browserify-cipher>browserify-des>des.js": { "packages": { - "ganache>secp256k1>elliptic>minimalistic-assert": true, + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, "pumpify>inherits": true } }, @@ -2865,6 +2892,7 @@ }, "browserify>crypto-browserify>browserify-sign": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>buffer": true, "browserify>crypto-browserify>create-hmac": true, @@ -2872,15 +2900,14 @@ "browserify>crypto-browserify>public-encrypt>parse-asn1": true, "browserify>stream-browserify": true, "ethereumjs-util>create-hash": true, - "ganache>secp256k1>elliptic": true, "pumpify>inherits": true } }, "browserify>crypto-browserify>create-ecdh": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, - "browserify>buffer": true, - "ganache>secp256k1>elliptic": true + "browserify>buffer": true } }, "browserify>crypto-browserify>create-hmac": { @@ -2903,8 +2930,8 @@ }, "browserify>crypto-browserify>diffie-hellman>miller-rabin": { "packages": { - "bn.js": true, - "ganache>secp256k1>elliptic>brorand": true + "@metamask/ppom-validator>elliptic>brorand": true, + "bn.js": true } }, "browserify>crypto-browserify>pbkdf2": { @@ -2951,10 +2978,10 @@ }, "browserify>crypto-browserify>public-encrypt>parse-asn1>asn1.js": { "packages": { + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, "bn.js": true, "browserify>buffer": true, "browserify>vm-browserify": true, - "ganache>secp256k1>elliptic>minimalistic-assert": true, "pumpify>inherits": true } }, @@ -3315,6 +3342,7 @@ "packages": { "@ethereumjs/common>crc-32": true, "@ethersproject/abi": true, + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>buffer": true, "eth-lattice-keyring>gridplus-sdk>@ethereumjs/common": true, @@ -3331,7 +3359,6 @@ "ethereumjs-util>ethereum-cryptography>hash.js": true, "ethereumjs-wallet>aes-js": true, "ethereumjs-wallet>bs58check": true, - "ganache>secp256k1>elliptic": true, "lodash": true } }, @@ -3455,7 +3482,7 @@ }, "eth-lattice-keyring>gridplus-sdk>secp256k1": { "packages": { - "ganache>secp256k1>elliptic": true + "@metamask/ppom-validator>elliptic": true } }, "eth-lattice-keyring>gridplus-sdk>uuid": { @@ -3496,6 +3523,7 @@ }, "eth-sig-util>ethereumjs-util": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>assert": true, "browserify>buffer": true, @@ -3503,8 +3531,7 @@ "ethereumjs-util>create-hash": true, "ethereumjs-util>ethereum-cryptography": true, "ethereumjs-util>rlp": true, - "ethereumjs-wallet>safe-buffer": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-wallet>safe-buffer": true } }, "eth-sig-util>ethereumjs-util>ethjs-util": { @@ -3542,14 +3569,14 @@ }, "ethereumjs-abi>ethereumjs-util": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>assert": true, "browserify>buffer": true, "eth-sig-util>ethereumjs-util>ethjs-util": true, "ethereumjs-util>create-hash": true, "ethereumjs-util>ethereum-cryptography": true, - "ethereumjs-util>rlp": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-util>rlp": true } }, "ethereumjs-util": { @@ -3642,7 +3669,7 @@ }, "ethereumjs-util>ethereum-cryptography>hash.js": { "packages": { - "ganache>secp256k1>elliptic>minimalistic-assert": true, + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, "pumpify>inherits": true } }, @@ -3674,7 +3701,7 @@ }, "ethereumjs-util>ethereum-cryptography>secp256k1": { "packages": { - "ganache>secp256k1>elliptic": true + "@metamask/ppom-validator>elliptic": true } }, "ethereumjs-util>rlp": { @@ -3720,14 +3747,14 @@ }, "ethereumjs-wallet>ethereumjs-util": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>assert": true, "browserify>buffer": true, "eth-sig-util>ethereumjs-util>ethjs-util": true, "ethereumjs-util>create-hash": true, "ethereumjs-util>ethereum-cryptography": true, - "ethereumjs-util>rlp": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-util>rlp": true } }, "ethereumjs-wallet>randombytes": { @@ -3926,33 +3953,6 @@ "define": true } }, - "ganache>secp256k1>elliptic": { - "packages": { - "bn.js": true, - "ethereumjs-util>ethereum-cryptography>hash.js": true, - "ganache>secp256k1>elliptic>brorand": true, - "ganache>secp256k1>elliptic>hmac-drbg": true, - "ganache>secp256k1>elliptic>minimalistic-assert": true, - "ganache>secp256k1>elliptic>minimalistic-crypto-utils": true, - "pumpify>inherits": true - } - }, - "ganache>secp256k1>elliptic>brorand": { - "globals": { - "crypto": true, - "msCrypto": true - }, - "packages": { - "browserify>browser-resolve": true - } - }, - "ganache>secp256k1>elliptic>hmac-drbg": { - "packages": { - "ethereumjs-util>ethereum-cryptography>hash.js": true, - "ganache>secp256k1>elliptic>minimalistic-assert": true, - "ganache>secp256k1>elliptic>minimalistic-crypto-utils": true - } - }, "globalthis>define-properties": { "packages": { "globalthis>define-properties>has-property-descriptors": true, diff --git a/lavamoat/browserify/mmi/policy.json b/lavamoat/browserify/mmi/policy.json index 6b526f97a..d8c191d6d 100644 --- a/lavamoat/browserify/mmi/policy.json +++ b/lavamoat/browserify/mmi/policy.json @@ -380,7 +380,7 @@ "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, - "ganache>secp256k1>elliptic": true + "@metamask/ppom-validator>elliptic": true } }, "@ethersproject/hdnode>@ethersproject/transactions": { @@ -1316,6 +1316,7 @@ }, "@metamask/eth-ledger-bridge-keyring>eth-sig-util>ethereumjs-util": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>assert": true, "browserify>buffer": true, @@ -1323,8 +1324,7 @@ "ethereumjs-util>create-hash": true, "ethereumjs-util>ethereum-cryptography": true, "ethereumjs-util>rlp": true, - "ethereumjs-wallet>safe-buffer": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-wallet>safe-buffer": true } }, "@metamask/eth-ledger-bridge-keyring>hdkey": { @@ -1339,11 +1339,11 @@ "@metamask/eth-ledger-bridge-keyring>hdkey>secp256k1": { "packages": { "@metamask/eth-trezor-keyring>hdkey>secp256k1>bip66": true, + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>insert-module-globals>is-buffer": true, "ethereumjs-util>create-hash": true, - "ethereumjs-wallet>safe-buffer": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-wallet>safe-buffer": true } }, "@metamask/eth-snap-keyring>@metamask/eth-sig-util": { @@ -1650,11 +1650,11 @@ "@metamask/eth-trezor-keyring>hdkey>secp256k1": { "packages": { "@metamask/eth-trezor-keyring>hdkey>secp256k1>bip66": true, + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>insert-module-globals>is-buffer": true, "ethereumjs-util>create-hash": true, - "ethereumjs-wallet>safe-buffer": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-wallet>safe-buffer": true } }, "@metamask/eth-trezor-keyring>hdkey>secp256k1>bip66": { @@ -2030,6 +2030,33 @@ "eslint>optionator>fast-levenshtein": true } }, + "@metamask/ppom-validator>elliptic": { + "packages": { + "@metamask/ppom-validator>elliptic>brorand": true, + "@metamask/ppom-validator>elliptic>hmac-drbg": true, + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, + "@metamask/ppom-validator>elliptic>minimalistic-crypto-utils": true, + "bn.js": true, + "ethereumjs-util>ethereum-cryptography>hash.js": true, + "pumpify>inherits": true + } + }, + "@metamask/ppom-validator>elliptic>brorand": { + "globals": { + "crypto": true, + "msCrypto": true + }, + "packages": { + "browserify>browser-resolve": true + } + }, + "@metamask/ppom-validator>elliptic>hmac-drbg": { + "packages": { + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, + "@metamask/ppom-validator>elliptic>minimalistic-crypto-utils": true, + "ethereumjs-util>ethereum-cryptography>hash.js": true + } + }, "@metamask/rpc-methods": { "packages": { "@metamask/browser-passworder": true, @@ -3081,7 +3108,7 @@ }, "browserify>crypto-browserify>browserify-cipher>browserify-des>des.js": { "packages": { - "ganache>secp256k1>elliptic>minimalistic-assert": true, + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, "pumpify>inherits": true } }, @@ -3093,6 +3120,7 @@ }, "browserify>crypto-browserify>browserify-sign": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>buffer": true, "browserify>crypto-browserify>create-hmac": true, @@ -3100,15 +3128,14 @@ "browserify>crypto-browserify>public-encrypt>parse-asn1": true, "browserify>stream-browserify": true, "ethereumjs-util>create-hash": true, - "ganache>secp256k1>elliptic": true, "pumpify>inherits": true } }, "browserify>crypto-browserify>create-ecdh": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, - "browserify>buffer": true, - "ganache>secp256k1>elliptic": true + "browserify>buffer": true } }, "browserify>crypto-browserify>create-hmac": { @@ -3131,8 +3158,8 @@ }, "browserify>crypto-browserify>diffie-hellman>miller-rabin": { "packages": { - "bn.js": true, - "ganache>secp256k1>elliptic>brorand": true + "@metamask/ppom-validator>elliptic>brorand": true, + "bn.js": true } }, "browserify>crypto-browserify>pbkdf2": { @@ -3179,10 +3206,10 @@ }, "browserify>crypto-browserify>public-encrypt>parse-asn1>asn1.js": { "packages": { + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, "bn.js": true, "browserify>buffer": true, "browserify>vm-browserify": true, - "ganache>secp256k1>elliptic>minimalistic-assert": true, "pumpify>inherits": true } }, @@ -3543,6 +3570,7 @@ "packages": { "@ethereumjs/common>crc-32": true, "@ethersproject/abi": true, + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>buffer": true, "eth-lattice-keyring>gridplus-sdk>@ethereumjs/common": true, @@ -3559,7 +3587,6 @@ "ethereumjs-util>ethereum-cryptography>hash.js": true, "ethereumjs-wallet>aes-js": true, "ethereumjs-wallet>bs58check": true, - "ganache>secp256k1>elliptic": true, "lodash": true } }, @@ -3683,7 +3710,7 @@ }, "eth-lattice-keyring>gridplus-sdk>secp256k1": { "packages": { - "ganache>secp256k1>elliptic": true + "@metamask/ppom-validator>elliptic": true } }, "eth-lattice-keyring>gridplus-sdk>uuid": { @@ -3724,6 +3751,7 @@ }, "eth-sig-util>ethereumjs-util": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>assert": true, "browserify>buffer": true, @@ -3731,8 +3759,7 @@ "ethereumjs-util>create-hash": true, "ethereumjs-util>ethereum-cryptography": true, "ethereumjs-util>rlp": true, - "ethereumjs-wallet>safe-buffer": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-wallet>safe-buffer": true } }, "eth-sig-util>ethereumjs-util>ethjs-util": { @@ -3770,14 +3797,14 @@ }, "ethereumjs-abi>ethereumjs-util": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>assert": true, "browserify>buffer": true, "eth-sig-util>ethereumjs-util>ethjs-util": true, "ethereumjs-util>create-hash": true, "ethereumjs-util>ethereum-cryptography": true, - "ethereumjs-util>rlp": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-util>rlp": true } }, "ethereumjs-util": { @@ -3870,7 +3897,7 @@ }, "ethereumjs-util>ethereum-cryptography>hash.js": { "packages": { - "ganache>secp256k1>elliptic>minimalistic-assert": true, + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, "pumpify>inherits": true } }, @@ -3902,7 +3929,7 @@ }, "ethereumjs-util>ethereum-cryptography>secp256k1": { "packages": { - "ganache>secp256k1>elliptic": true + "@metamask/ppom-validator>elliptic": true } }, "ethereumjs-util>rlp": { @@ -3948,14 +3975,14 @@ }, "ethereumjs-wallet>ethereumjs-util": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>assert": true, "browserify>buffer": true, "eth-sig-util>ethereumjs-util>ethjs-util": true, "ethereumjs-util>create-hash": true, "ethereumjs-util>ethereum-cryptography": true, - "ethereumjs-util>rlp": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-util>rlp": true } }, "ethereumjs-wallet>randombytes": { @@ -4154,33 +4181,6 @@ "define": true } }, - "ganache>secp256k1>elliptic": { - "packages": { - "bn.js": true, - "ethereumjs-util>ethereum-cryptography>hash.js": true, - "ganache>secp256k1>elliptic>brorand": true, - "ganache>secp256k1>elliptic>hmac-drbg": true, - "ganache>secp256k1>elliptic>minimalistic-assert": true, - "ganache>secp256k1>elliptic>minimalistic-crypto-utils": true, - "pumpify>inherits": true - } - }, - "ganache>secp256k1>elliptic>brorand": { - "globals": { - "crypto": true, - "msCrypto": true - }, - "packages": { - "browserify>browser-resolve": true - } - }, - "ganache>secp256k1>elliptic>hmac-drbg": { - "packages": { - "ethereumjs-util>ethereum-cryptography>hash.js": true, - "ganache>secp256k1>elliptic>minimalistic-assert": true, - "ganache>secp256k1>elliptic>minimalistic-crypto-utils": true - } - }, "globalthis>define-properties": { "packages": { "globalthis>define-properties>has-property-descriptors": true, diff --git a/package.json b/package.json index 23f872073..88a03d3bd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "metamask-crx", - "version": "10.34.2", + "version": "10.34.4", "private": true, "repository": { "type": "git", @@ -205,7 +205,7 @@ }, "dependencies": { "@babel/runtime": "^7.18.9", - "@blockaid/ppom": "^0.1.2", + "@blockaid/ppom": "^1.0.2", "@download/blockies": "^1.0.3", "@ensdomains/content-hash": "^2.5.6", "@ethereumjs/common": "^3.1.1", @@ -250,7 +250,7 @@ "@metamask/key-tree": "^9.0.0", "@metamask/keyring-controller": "^7.0.0", "@metamask/logo": "^3.1.1", - "@metamask/message-manager": "^7.0.2", + "@metamask/message-manager": "^7.3.0", "@metamask/metamask-eth-abis": "^3.0.0", "@metamask/network-controller": "^12.0.0", "@metamask/notification-controller": "^3.0.0", @@ -258,21 +258,21 @@ "@metamask/permission-controller": "^4.0.0", "@metamask/phishing-controller": "^6.0.0", "@metamask/post-message-stream": "^6.0.0", - "@metamask/ppom-validator": "^0.1.2", + "@metamask/ppom-validator": "^0.2.0", "@metamask/providers": "^11.1.0", "@metamask/rate-limit-controller": "^3.0.0", - "@metamask/rpc-methods": "^1.0.0-prerelease.1", + "@metamask/rpc-methods": "^1.0.0", "@metamask/rpc-methods-flask": "npm:@metamask/rpc-methods@0.37.2-flask.1", "@metamask/safe-event-emitter": "^2.0.0", "@metamask/scure-bip39": "^2.0.3", "@metamask/signature-controller": "^5.3.0", "@metamask/slip44": "^3.0.0", "@metamask/smart-transactions-controller": "^4.0.0", - "@metamask/snaps-controllers": "^1.0.0-prerelease.1", + "@metamask/snaps-controllers": "^1.0.0", "@metamask/snaps-controllers-flask": "npm:@metamask/snaps-controllers@0.38.0-flask.1", - "@metamask/snaps-ui": "^1.0.0-prerelease.1", + "@metamask/snaps-ui": "^1.0.0", "@metamask/snaps-ui-flask": "npm:@metamask/snaps-ui@0.37.3-flask.1", - "@metamask/snaps-utils": "^1.0.0-prerelease.1", + "@metamask/snaps-utils": "^1.0.0", "@metamask/snaps-utils-flask": "npm:@metamask/snaps-utils@0.38.0-flask.1", "@metamask/subject-metadata-controller": "^2.0.0", "@metamask/utils": "^5.0.0", @@ -358,7 +358,7 @@ "redux-thunk": "^2.3.0", "remove-trailing-slash": "^0.1.1", "reselect": "^3.0.1", - "ses": "^0.18.4", + "ses": "^0.18.7", "single-call-balance-checker-abi": "^1.0.0", "unicode-confusables": "^0.1.1", "uuid": "^8.3.2", diff --git a/shared/constants/network.ts b/shared/constants/network.ts index d9f0fb99e..18910c207 100644 --- a/shared/constants/network.ts +++ b/shared/constants/network.ts @@ -138,6 +138,8 @@ export const CHAIN_IDS = { BSC_TESTNET: '0x61', OPTIMISM: '0xa', OPTIMISM_TESTNET: '0x1a4', + BASE: '0x2105', + BASE_TESTNET: '0x14a33', POLYGON: '0x89', POLYGON_TESTNET: '0x13881', AVALANCHE: '0xa86a', @@ -553,6 +555,8 @@ export const BUYABLE_CHAINS_MAP: { ChainId, | typeof CHAIN_IDS.LOCALHOST | typeof CHAIN_IDS.OPTIMISM_TESTNET + | typeof CHAIN_IDS.BASE_TESTNET + | typeof CHAIN_IDS.BASE | typeof CHAIN_IDS.BSC_TESTNET | typeof CHAIN_IDS.POLYGON_TESTNET | typeof CHAIN_IDS.AVALANCHE_TESTNET diff --git a/shared/constants/security-provider.ts b/shared/constants/security-provider.ts index 8f9168321..cb2773d29 100644 --- a/shared/constants/security-provider.ts +++ b/shared/constants/security-provider.ts @@ -49,8 +49,8 @@ export enum BlockaidReason { other = 'other', // Locally defined - notApplicable = 'NotApplicable', failed = 'Failed', + notApplicable = 'NotApplicable', } export enum BlockaidResultType { diff --git a/test/e2e/metamask-ui.spec.js b/test/e2e/metamask-ui.spec.js index ac5ac1861..86964698f 100644 --- a/test/e2e/metamask-ui.spec.js +++ b/test/e2e/metamask-ui.spec.js @@ -257,13 +257,18 @@ describe('MetaMask', function () { }); await driver.delay(regularDelayMs); - await driver.fill('#custom-address', tokenAddress); + await driver.fill( + '[data-testid="import-tokens-modal-custom-address"]', + tokenAddress, + ); await driver.delay(regularDelayMs); - await driver.clickElement({ text: 'Add custom token', tag: 'button' }); + await driver.clickElement({ text: 'Next', tag: 'button' }); await driver.delay(regularDelayMs); - await driver.clickElement({ text: 'Import tokens', tag: 'button' }); + await driver.clickElement( + '[data-testid="import-tokens-modal-import-button"]', + ); await driver.delay(regularDelayMs); }); diff --git a/test/e2e/tests/add-hide-token.spec.js b/test/e2e/tests/add-hide-token.spec.js index b1ead17a8..533611ff2 100644 --- a/test/e2e/tests/add-hide-token.spec.js +++ b/test/e2e/tests/add-hide-token.spec.js @@ -108,13 +108,15 @@ describe('Add existing token using search', function () { await driver.press('#password', driver.Key.ENTER); await driver.clickElement({ text: 'Import tokens', tag: 'button' }); - await driver.fill('#search-tokens', 'BAT'); + await driver.fill('input[placeholder="Search"]', 'BAT'); await driver.clickElement({ text: 'BAT', tag: 'span', }); await driver.clickElement({ text: 'Next', tag: 'button' }); - await driver.clickElement({ text: 'Import tokens', tag: 'button' }); + await driver.clickElement( + '[data-testid="import-tokens-modal-import-button"]', + ); await driver.waitForSelector({ css: '.token-overview__primary-balance', diff --git a/test/e2e/tests/custom-token-add-approve.spec.js b/test/e2e/tests/custom-token-add-approve.spec.js index 2cae7ff50..f5f58e5d8 100644 --- a/test/e2e/tests/custom-token-add-approve.spec.js +++ b/test/e2e/tests/custom-token-add-approve.spec.js @@ -51,20 +51,24 @@ describe('Create token, approve token and approve token without gas', function ( text: 'Custom token', tag: 'button', }); - await driver.fill('#custom-address', contractAddress); - await driver.waitForSelector('#custom-decimals'); + await driver.fill( + '[data-testid="import-tokens-modal-custom-address"]', + contractAddress, + ); + await driver.waitForSelector( + '[data-testid="import-tokens-modal-custom-decimals"]', + ); await driver.delay(2000); await driver.clickElement({ - text: 'Add custom token', + text: 'Next', tag: 'button', }); await driver.delay(2000); - await driver.clickElement({ - text: 'Import tokens', - tag: 'button', - }); + await driver.clickElement( + '[data-testid="import-tokens-modal-import-button"]', + ); // renders balance for newly created token await driver.clickElement('.app-header__logo-container'); diff --git a/test/e2e/tests/import-tokens.spec.js b/test/e2e/tests/import-tokens.spec.js index d86d5f097..01adbb2ac 100644 --- a/test/e2e/tests/import-tokens.spec.js +++ b/test/e2e/tests/import-tokens.spec.js @@ -37,14 +37,20 @@ describe('Import flow', function () { await driver.delay(regularDelayMs); await driver.clickElement('[data-testid="import-token-button"]'); - await driver.fill('input[placeholder="Search tokens"]', 'cha'); + await driver.fill('input[placeholder="Search"]', 'cha'); await driver.clickElement('.token-list__token'); await driver.clickElement('.token-list__token:nth-of-type(2)'); await driver.clickElement('.token-list__token:nth-of-type(3)'); - await driver.clickElement({ css: 'button', text: 'Next' }); - await driver.clickElement({ css: 'button', text: 'Import' }); + await driver.clickElement({ + css: '.import-tokens-modal button', + text: 'Next', + }); + await driver.clickElement({ + css: '.import-tokens-modal button', + text: 'Import', + }); await driver.clickElement('.asset-breadcrumb'); diff --git a/test/e2e/tests/token-details.spec.js b/test/e2e/tests/token-details.spec.js index 07c4a8570..e365b760d 100644 --- a/test/e2e/tests/token-details.spec.js +++ b/test/e2e/tests/token-details.spec.js @@ -30,11 +30,19 @@ describe('Token Details', function () { const tokenAddress = '0x2EFA2Cb29C2341d8E5Ba7D3262C9e9d6f1Bf3711'; const tokenSymbol = 'AAVE'; - await driver.fill('#custom-address', tokenAddress); - await driver.waitForSelector('#custom-symbol-helper-text'); - await driver.fill('#custom-symbol', tokenSymbol); - await driver.clickElement({ text: 'Add custom token', tag: 'button' }); - await driver.clickElement({ text: 'Import tokens', tag: 'button' }); + await driver.fill( + '[data-testid="import-tokens-modal-custom-address"]', + tokenAddress, + ); + await driver.waitForSelector('p.mm-box--color-error-default'); + await driver.fill( + '[data-testid="import-tokens-modal-custom-symbol"]', + tokenSymbol, + ); + await driver.clickElement({ text: 'Next', tag: 'button' }); + await driver.clickElement( + '[data-testid="import-tokens-modal-import-button"]', + ); await driver.clickElement('[aria-label="Asset options"]'); await driver.clickElement({ text: 'Token details', tag: 'div' }); diff --git a/ui/components/app/app-components.scss b/ui/components/app/app-components.scss index 4ca6b1dd8..76cd545ed 100644 --- a/ui/components/app/app-components.scss +++ b/ui/components/app/app-components.scss @@ -11,6 +11,7 @@ @import 'confirm-data/index'; @import 'confirmation-warning-modal/index'; @import 'custom-nonce/index'; +@import 'import-token/index'; @import 'nfts-items/index'; @import 'nfts-tab/index'; @import 'nft-details/index'; diff --git a/ui/components/app/import-token/index.scss b/ui/components/app/import-token/index.scss new file mode 100644 index 000000000..24b210c93 --- /dev/null +++ b/ui/components/app/import-token/index.scss @@ -0,0 +1 @@ +@import 'token-list/index'; diff --git a/ui/pages/import-token/token-list/index.js b/ui/components/app/import-token/token-list/index.js similarity index 100% rename from ui/pages/import-token/token-list/index.js rename to ui/components/app/import-token/token-list/index.js diff --git a/ui/pages/import-token/token-list/index.scss b/ui/components/app/import-token/token-list/index.scss similarity index 96% rename from ui/pages/import-token/token-list/index.scss rename to ui/components/app/import-token/token-list/index.scss index b776d093c..e00298ada 100644 --- a/ui/pages/import-token/token-list/index.scss +++ b/ui/components/app/import-token/token-list/index.scss @@ -1,5 +1,3 @@ -@import 'token-list-placeholder/index'; - .token-list { &__title { @include H7; diff --git a/ui/pages/import-token/token-list/token-list-placeholder/index.js b/ui/components/app/import-token/token-list/token-list-placeholder/index.js similarity index 100% rename from ui/pages/import-token/token-list/token-list-placeholder/index.js rename to ui/components/app/import-token/token-list/token-list-placeholder/index.js diff --git a/ui/components/app/import-token/token-list/token-list-placeholder/token-list-placeholder.component.js b/ui/components/app/import-token/token-list/token-list-placeholder/token-list-placeholder.component.js new file mode 100644 index 000000000..7cbf662b1 --- /dev/null +++ b/ui/components/app/import-token/token-list/token-list-placeholder/token-list-placeholder.component.js @@ -0,0 +1,35 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import ZENDESK_URLS from '../../../../../helpers/constants/zendesk-url'; +import { ButtonLink, Text, Box } from '../../../../component-library'; +import { + Display, + FlexDirection, + TextAlign, + TextColor, + AlignItems, +} from '../../../../../helpers/constants/design-system'; + +export default class TokenListPlaceholder extends Component { + static contextTypes = { + t: PropTypes.func, + }; + + render() { + return ( + + + {this.context.t('addAcquiredTokens')} + + + {this.context.t('learnMoreUpperCase')} + + + ); + } +} diff --git a/ui/pages/import-token/token-list/token-list-placeholder/token-list-placeholder.stories.js b/ui/components/app/import-token/token-list/token-list-placeholder/token-list-placeholder.stories.js similarity index 79% rename from ui/pages/import-token/token-list/token-list-placeholder/token-list-placeholder.stories.js rename to ui/components/app/import-token/token-list/token-list-placeholder/token-list-placeholder.stories.js index 72386103d..2cbe8f608 100644 --- a/ui/pages/import-token/token-list/token-list-placeholder/token-list-placeholder.stories.js +++ b/ui/components/app/import-token/token-list/token-list-placeholder/token-list-placeholder.stories.js @@ -2,7 +2,7 @@ import React from 'react'; import TokenListPlaceholder from './token-list-placeholder.component'; export default { - title: 'Pages/ImportToken/TokenList/TokenListPlaceholder', + title: 'Components/App/TokenList/TokenListPlaceholder', }; export const DefaultStory = () => { diff --git a/ui/pages/import-token/token-list/token-list.component.js b/ui/components/app/import-token/token-list/token-list.component.js similarity index 96% rename from ui/pages/import-token/token-list/token-list.component.js rename to ui/components/app/import-token/token-list/token-list.component.js index fed54e5ad..e57d06be2 100644 --- a/ui/pages/import-token/token-list/token-list.component.js +++ b/ui/components/app/import-token/token-list/token-list.component.js @@ -1,7 +1,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import classnames from 'classnames'; -import { checkExistingAddresses } from '../../../helpers/utils/util'; +import { checkExistingAddresses } from '../../../../helpers/utils/util'; import TokenListPlaceholder from './token-list-placeholder'; export default class TokenList extends Component { diff --git a/ui/pages/import-token/token-list/token-list.container.js b/ui/components/app/import-token/token-list/token-list.container.js similarity index 100% rename from ui/pages/import-token/token-list/token-list.container.js rename to ui/components/app/import-token/token-list/token-list.container.js diff --git a/ui/pages/import-token/token-search/index.js b/ui/components/app/import-token/token-search/index.js similarity index 100% rename from ui/pages/import-token/token-search/index.js rename to ui/components/app/import-token/token-search/index.js diff --git a/ui/pages/import-token/token-search/token-search.component.js b/ui/components/app/import-token/token-search/token-search.component.js similarity index 69% rename from ui/pages/import-token/token-search/token-search.component.js rename to ui/components/app/import-token/token-search/token-search.component.js index b6546cd78..853d226ae 100644 --- a/ui/pages/import-token/token-search/token-search.component.js +++ b/ui/components/app/import-token/token-search/token-search.component.js @@ -1,10 +1,9 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import Fuse from 'fuse.js'; -import InputAdornment from '@material-ui/core/InputAdornment'; -import TextField from '../../../components/ui/text-field'; -import { isEqualCaseInsensitive } from '../../../../shared/modules/string-utils'; -import SearchIcon from '../../../components/ui/icon/search-icon'; +import { isEqualCaseInsensitive } from '../../../../../shared/modules/string-utils'; +import { TextFieldSearch } from '../../../component-library'; +import { BlockSize } from '../../../../helpers/constants/design-system'; export default class TokenSearch extends Component { static contextTypes = { @@ -57,30 +56,19 @@ export default class TokenSearch extends Component { this.props.onSearch({ searchQuery, results }); } - renderAdornment() { - return ( - - - - ); - } - render() { const { error } = this.props; const { searchQuery } = this.state; return ( - this.handleSearch(e.target.value)} error={error} - fullWidth autoFocus - autoComplete="off" - startAdornment={this.renderAdornment()} + autoComplete={false} + width={BlockSize.Full} /> ); } diff --git a/ui/pages/import-token/token-search/token-search.stories.js b/ui/components/app/import-token/token-search/token-search.stories.js similarity index 76% rename from ui/pages/import-token/token-search/token-search.stories.js rename to ui/components/app/import-token/token-search/token-search.stories.js index 8b0156474..57ec57cea 100644 --- a/ui/pages/import-token/token-search/token-search.stories.js +++ b/ui/components/app/import-token/token-search/token-search.stories.js @@ -1,9 +1,9 @@ import React from 'react'; -import testData from '../../../../.storybook/test-data'; +import testData from '../../../../../.storybook/test-data'; import TokenSearch from './token-search.component'; export default { - title: 'Pages/ImportToken/TokenSearch', + title: 'Components/App/ImportToken/TokenSearch', argTypes: { error: { diff --git a/ui/components/app/modals/eth-sign-modal/eth-sign-modal.js b/ui/components/app/modals/eth-sign-modal/eth-sign-modal.js index 47b056a7b..41944bb58 100644 --- a/ui/components/app/modals/eth-sign-modal/eth-sign-modal.js +++ b/ui/components/app/modals/eth-sign-modal/eth-sign-modal.js @@ -9,6 +9,7 @@ import { ButtonLink, ButtonPrimary, ButtonSecondary, + ButtonSecondarySize, FormTextField, Icon, IconName, @@ -152,7 +153,11 @@ const EthSignModal = ({ hideModal }) => { gap={4} marginTop={6} > - hideModal()} size={Size.LG} block> + hideModal()} + size={ButtonSecondarySize.Lg} + block + > {t('cancel')} {showTextField ? ( diff --git a/ui/components/app/nfts-items/index.scss b/ui/components/app/nfts-items/index.scss index 225cd2149..f8d7839fb 100644 --- a/ui/components/app/nfts-items/index.scss +++ b/ui/components/app/nfts-items/index.scss @@ -2,6 +2,10 @@ &__collection { margin-bottom: 24px; + &:last-child { + margin-bottom: 0; + } + &-accordion-title { cursor: pointer; } diff --git a/ui/components/app/security-provider-banner-alert/__snapshots__/security-provider-banner-alert.test.js.snap b/ui/components/app/security-provider-banner-alert/__snapshots__/security-provider-banner-alert.test.js.snap index 7cf86eeb7..37761b2ba 100644 --- a/ui/components/app/security-provider-banner-alert/__snapshots__/security-provider-banner-alert.test.js.snap +++ b/ui/components/app/security-provider-banner-alert/__snapshots__/security-provider-banner-alert.test.js.snap @@ -59,7 +59,7 @@ exports[`Security Provider Banner Alert should match snapshot 1`] = `

`; +exports[`Blockaid Banner Alert should render 'warning' UI when securityAlertResponse.result_type is 'Failed 1`] = ` +

+ +
+
+ This is a deceptive request +
+

+ If you approve this request, a third party known for scams might take all your assets. +

+
+
+`; + exports[`Blockaid Banner Alert should render 'warning' UI when securityAlertResponse.result_type is 'Warning 1`] = `

); + const isFailedResultType = resultType === BlockaidResultType.Failed; + const severity = resultType === BlockaidResultType.Malicious ? Severity.Danger : Severity.Warning; - const title = - SUSPCIOUS_REASON.indexOf(reason) > -1 - ? t('blockaidTitleSuspicious') - : t('blockaidTitleDeceptive'); + const title = t(REASON_TO_TITLE_TKEY[reason] || 'blockaidTitleDeceptive'); return ( diff --git a/ui/components/app/security-provider-banner-alert/blockaid-banner-alert/blockaid-banner-alert.test.js b/ui/components/app/security-provider-banner-alert/blockaid-banner-alert/blockaid-banner-alert.test.js index 7710be531..9e9e3bd07 100644 --- a/ui/components/app/security-provider-banner-alert/blockaid-banner-alert/blockaid-banner-alert.test.js +++ b/ui/components/app/security-provider-banner-alert/blockaid-banner-alert/blockaid-banner-alert.test.js @@ -40,7 +40,7 @@ describe('Blockaid Banner Alert', () => { expect(container.querySelector('.mm-banner-alert')).toBeNull(); }); - it(`should not render when securityAlertResponse.result_type is '${BlockaidResultType.Failed}'`, () => { + it(`should render '${Severity.Warning}' UI when securityAlertResponse.result_type is '${BlockaidResultType.Failed}`, () => { const { container } = renderWithLocalization( { }} />, ); + const warningBannerAlert = container.querySelector( + '.mm-banner-alert--severity-warning', + ); - expect(container.querySelector('.mm-banner-alert')).toBeNull(); + expect(warningBannerAlert).toBeInTheDocument(); + expect(warningBannerAlert).toMatchSnapshot(); + }); + + it(`should render '${Severity.Warning}' UI when securityAlertResponse.result_type is '${BlockaidResultType.Warning}`, () => { + const { container } = renderWithLocalization( + , + ); + const warningBannerAlert = container.querySelector( + '.mm-banner-alert--severity-warning', + ); + + expect(warningBannerAlert).toBeInTheDocument(); + expect(warningBannerAlert).toMatchSnapshot(); }); it(`should render '${Severity.Danger}' UI when securityAlertResponse.result_type is '${BlockaidResultType.Malicious}`, () => { @@ -70,18 +86,6 @@ describe('Blockaid Banner Alert', () => { expect(dangerBannerAlert).toMatchSnapshot(); }); - it(`should render '${Severity.Warning}' UI when securityAlertResponse.result_type is '${BlockaidResultType.Warning}`, () => { - const { container } = renderWithLocalization( - , - ); - const warningBannerAlert = container.querySelector( - '.mm-banner-alert--severity-warning', - ); - - expect(warningBannerAlert).toBeInTheDocument(); - expect(warningBannerAlert).toMatchSnapshot(); - }); - it('should render title, "This is a deceptive request"', () => { const { getByText } = renderWithLocalization( , @@ -90,7 +94,20 @@ describe('Blockaid Banner Alert', () => { expect(getByText('This is a deceptive request')).toBeInTheDocument(); }); - it('should render title, "This is a suspicious request", when the reason is "raw_signature_farming"', () => { + it(`should render title, "This is a suspicious request", when the reason is "${BlockaidReason.failed}"`, () => { + const { getByText } = renderWithLocalization( + , + ); + + expect(getByText('Request may not be safe')).toBeInTheDocument(); + }); + + it(`should render title, "This is a suspicious request", when the reason is "${BlockaidReason.rawSignatureFarming}"`, () => { const { getByText } = renderWithLocalization( { 'If you approve this request, a third party known for scams might take all your assets.', [BlockaidReason.blurFarming]: 'If you approve this request, someone can steal your assets listed on Blur.', + [BlockaidReason.failed]: + 'Because of an error, this request was not verified by the security provider. Proceed with caution.', [BlockaidReason.maliciousDomain]: "You're interacting with a malicious domain. If you approve this request, you might lose your assets.", [BlockaidReason.other]: diff --git a/ui/components/app/security-provider-banner-alert/security-provider-banner-alert.js b/ui/components/app/security-provider-banner-alert/security-provider-banner-alert.js index e3b777b8b..5e115c193 100644 --- a/ui/components/app/security-provider-banner-alert/security-provider-banner-alert.js +++ b/ui/components/app/security-provider-banner-alert/security-provider-banner-alert.js @@ -15,6 +15,7 @@ import { I18nContext } from '../../../contexts/i18n'; import { AlignItems, Color, + Display, IconColor, Severity, Size, @@ -45,30 +46,33 @@ function SecurityProviderBannerAlert({ )} - - - {t('securityProviderAdviceBy', [ - - {t(SECURITY_PROVIDER_CONFIG[provider].tKeyName)} - , - ])} - + {provider && ( + + + {t('securityProviderAdviceBy', [ + + {t(SECURITY_PROVIDER_CONFIG[provider].tKeyName)} + , + ])} + + )} ); } @@ -78,9 +82,6 @@ SecurityProviderBannerAlert.propTypes = { description: PropTypes.oneOfType([PropTypes.string, PropTypes.element]) .isRequired, - /** Name of the security provider */ - provider: PropTypes.oneOfType(Object.values(SecurityProvider)).isRequired, - /** Severity level */ severity: PropTypes.oneOfType([Severity.Danger, Severity.Warning]).isRequired, @@ -93,6 +94,9 @@ SecurityProviderBannerAlert.propTypes = { /** Additional details to be displayed under the description */ details: PropTypes.oneOfType([PropTypes.string, PropTypes.element]), + + /** Name of the security provider */ + provider: PropTypes.oneOfType(Object.values(SecurityProvider)), }; export default SecurityProviderBannerAlert; diff --git a/ui/components/app/security-provider-banner-alert/security-provider-banner-alert.stories.js b/ui/components/app/security-provider-banner-alert/security-provider-banner-alert.stories.js index 2b81f2035..ee2f2a311 100644 --- a/ui/components/app/security-provider-banner-alert/security-provider-banner-alert.stories.js +++ b/ui/components/app/security-provider-banner-alert/security-provider-banner-alert.stories.js @@ -53,7 +53,10 @@ export default { control: { type: 'select', }, - options: [Object.values(SecurityProvider)], + options: ['none', ...Object.values(SecurityProvider)], + mapping: { + none: null, + }, }, severity: { control: { diff --git a/ui/components/app/snaps/snap-settings-card/README.mdx b/ui/components/app/snaps/snap-settings-card/README.mdx deleted file mode 100644 index 7ff49feb7..000000000 --- a/ui/components/app/snaps/snap-settings-card/README.mdx +++ /dev/null @@ -1,41 +0,0 @@ -import { Story, Canvas, ArgsTable } from '@storybook/addon-docs'; - -import SnapSettingsCard from '.'; - -# SnapSettingsCard - -A card component that displays information and the status of a snap. The `SnapSettingsCard` component is made up of the `Card`, `IconBorder`, `IconWithFallback`, `ToggleButton`, `Chip`, `ColorIndicator` and `Button` components - - - - - -## Props - - - -## Usage - -The following describes the props and example usage for this component. - -### Status - -There are 4 statuses the `SnapSettingsCard` can have: `'installing'`,`'running'`,`'stopped'` and `'crashed'`. - - - - - -### isEnabled / onToggle - -Use the `isEnabled` and `onToggle` to control the `ToggleButton` component inside of the `SnapSettingsCard` - -```jsx -const [isEnabled, setIsEnabled] = React.useState(false); - -const handleOnToggle = () => { - setIsEnabled(!isEnabled); -}; - -return ; -``` diff --git a/ui/components/app/snaps/snap-settings-card/snap-settings-card.js b/ui/components/app/snaps/snap-settings-card/snap-settings-card.js index 6f7fc6822..184b042b4 100644 --- a/ui/components/app/snaps/snap-settings-card/snap-settings-card.js +++ b/ui/components/app/snaps/snap-settings-card/snap-settings-card.js @@ -1,42 +1,46 @@ import React from 'react'; import PropTypes from 'prop-types'; -import Box from '../../../ui/box'; - import { Color, AlignItems, JustifyContent, - DISPLAY, - BLOCK_SIZES, + Display, + BlockSize, IconColor, TextVariant, } from '../../../../helpers/constants/design-system'; -import { Icon, IconName, IconSize, Text } from '../../../component-library'; +import { + Icon, + IconName, + IconSize, + Text, + Box, +} from '../../../component-library'; import SnapAvatar from '../snap-avatar'; const SnapSettingsCard = ({ name, packageName, onClick, snapId }) => { return ( - + { SnapSettingsCard.propTypes = { /** - * Name of the snap used for the title of the card and fallback letter for the snap icon + * Name of the snap */ name: PropTypes.string, /** @@ -74,7 +78,7 @@ SnapSettingsCard.propTypes = { */ packageName: PropTypes.string, /** - * onClick function of the "See Details" Button + * onClick event handler */ onClick: PropTypes.func, /** @@ -82,5 +86,4 @@ SnapSettingsCard.propTypes = { */ snapId: PropTypes.string.isRequired, }; - export default SnapSettingsCard; diff --git a/ui/components/app/snaps/snap-settings-card/snap-settings-card.stories.js b/ui/components/app/snaps/snap-settings-card/snap-settings-card.stories.js index bf9045142..484deb152 100644 --- a/ui/components/app/snaps/snap-settings-card/snap-settings-card.stories.js +++ b/ui/components/app/snaps/snap-settings-card/snap-settings-card.stories.js @@ -1,152 +1,30 @@ import React from 'react'; -import { useArgs } from '@storybook/client-api'; - -import README from './README.mdx'; import SnapSettingsCard from '.'; export default { title: 'Components/App/Snaps/SnapSettingsCard', - component: SnapSettingsCard, - parameters: { - docs: { - page: README, - }, - }, argTypes: { name: { control: 'text', }, - description: { + packageName: { control: 'text', }, - icon: { - control: 'text', - }, - dateAdded: { - control: 'text', - }, - version: { - control: 'text', - }, - url: { - control: 'text', - }, - onToggle: { - action: 'onToggle', - }, - isEnabled: { - control: 'boolean', - }, onClick: { action: 'onClick', }, - status: { - control: { - type: 'select', - }, - options: ['installing', 'stopped', 'running', 'crashed'], - }, - className: { - control: 'string', - }, - cardProps: { - control: 'object', - }, - toggleButtonProps: { - control: 'object', - }, - buttonProps: { - control: 'object', - }, - chipProps: { - control: 'object', + snapId: { + control: 'text', }, }, + args: { + name: 'Snap Name', + packageName: 'Snap Package Name', + snapId: 'npm:@metamask/test-snap-bip44', + }, }; -export const DefaultStory = (args) => { - const [{ isEnabled }, updateArgs] = useArgs(); - - const handleOnToggle = () => { - updateArgs({ - isEnabled: !isEnabled, - status: isEnabled ? 'stopped' : 'running', - }); - }; - return ( - - ); -}; +export const DefaultStory = (args) => ; DefaultStory.storyName = 'Default'; - -let d = new Date(); -d = d.toDateString(); - -DefaultStory.args = { - name: 'Snap name', - description: - 'This snap provides developers everywhere access to an entirely new data storage paradigm, even letting your programs store data autonomously.', - icon: 'AST.png', - dateAdded: d, - version: '10.5.1234', - url: 'https://metamask.io/', - status: 'stopped', -}; - -export const Status = () => ( - <> - - - - - -); diff --git a/ui/components/app/snaps/update-snap-permission-list/update-snap-permission-list.js b/ui/components/app/snaps/update-snap-permission-list/update-snap-permission-list.js index 216591489..a3b33f857 100644 --- a/ui/components/app/snaps/update-snap-permission-list/update-snap-permission-list.js +++ b/ui/components/app/snaps/update-snap-permission-list/update-snap-permission-list.js @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import { getWeightedPermissions } from '../../../../helpers/utils/permission'; import { useI18nContext } from '../../../../hooks/useI18nContext'; import PermissionCell from '../../permission-cell'; -import Box from '../../../ui/box'; +import { Box } from '../../../component-library'; export default function UpdateSnapPermissionList({ approvedPermissions, diff --git a/ui/components/app/snaps/update-snap-permission-list/update-snap-permission-list.stories.js b/ui/components/app/snaps/update-snap-permission-list/update-snap-permission-list.stories.js index 6aab35b5f..2b5b25e6b 100644 --- a/ui/components/app/snaps/update-snap-permission-list/update-snap-permission-list.stories.js +++ b/ui/components/app/snaps/update-snap-permission-list/update-snap-permission-list.stories.js @@ -2,50 +2,65 @@ import React from 'react'; import UpdateSnapPermissionList from './update-snap-permission-list'; export default { - title: 'Components/App/UpdateSnapPermissionList', + title: 'Components/App/Snaps/UpdateSnapPermissionList', component: UpdateSnapPermissionList, - argTypes: { - permissions: { + approvedPermissions: { control: 'object', }, + revokedPermissions: { + control: 'object', + }, + newPermissions: { + control: 'object', + }, + targetSubjectMetadata: { + control: 'object', + }, + }, + args: { + approvedPermissions: { + 'endowment:network-access': { + date: 1620710693178, + }, + snap_getBip32PublicKey: { + date: 1620710693178, + caveats: [ + { + value: [ + { + path: ['m', `44'`, `0'`], + curve: 'secp256k1', + }, + ], + }, + ], + }, + }, + revokedPermissions: { + snap_notify: { + date: 1620710693178, + }, + eth_accounts: { + date: 1620710693178, + }, + }, + newPermissions: { + snap_dialog: { + date: 1620710693178, + }, + }, + targetSubjectMetadata: { + extensionId: null, + iconUrl: null, + name: 'TypeScript Example Snap', + origin: 'local:http://localhost:8080', + subjectType: 'snap', + version: '0.2.2', + }, }, }; export const DefaultStory = (args) => ; DefaultStory.storyName = 'Default'; - -DefaultStory.args = { - approvedPermissions: { - 'endowment:network-access': { - date: 1620710693178, - }, - snap_getBip32PublicKey: { - date: 1620710693178, - caveats: [ - { - value: [ - { - path: ['m', `44'`, `0'`], - curve: 'secp256k1', - }, - ], - }, - ], - }, - }, - revokedPermissions: { - snap_notify: { - date: 1620710693178, - }, - eth_accounts: { - date: 1620710693178, - }, - }, - newPermissions: { - snap_dialog: { - date: 1620710693178, - }, - }, -}; diff --git a/ui/components/app/whats-new-popup/whats-new-popup.js b/ui/components/app/whats-new-popup/whats-new-popup.js index 7b1b0cb4f..ad9786a9c 100644 --- a/ui/components/app/whats-new-popup/whats-new-popup.js +++ b/ui/components/app/whats-new-popup/whats-new-popup.js @@ -10,7 +10,7 @@ import { useEqualityCheck } from '../../../hooks/useEqualityCheck'; import Popover from '../../ui/popover'; import { Text, - Button, + ButtonPrimary, ///: BEGIN:ONLY_INCLUDE_IN(build-mmi) IconName, ///: END:ONLY_INCLUDE_IN @@ -202,8 +202,7 @@ const renderFirstNotification = ({ {placeImageBelowDescription && imageComponent} {actionText && ( - + )} { ///: BEGIN:ONLY_INCLUDE_IN(build-mmi) customButton && customButton.name === 'mmi-portfolio' && ( - + ) ///: END:ONLY_INCLUDE_IN } diff --git a/ui/components/app/whats-new-popup/whats-new-popup.stories.js b/ui/components/app/whats-new-popup/whats-new-popup.stories.js new file mode 100644 index 000000000..01760b891 --- /dev/null +++ b/ui/components/app/whats-new-popup/whats-new-popup.stories.js @@ -0,0 +1,16 @@ +import React from 'react'; +import WhatsNewPopup from '.'; + +export default { + title: 'Components/Multichain/WhatsNewPopup', + component: WhatsNewPopup, + argTypes: { + onClose: { + action: 'onClose', + }, + }, +}; + +export const DefaultStory = (args) => ; + +DefaultStory.storyName = 'Default'; diff --git a/ui/components/component-library/button-secondary/README.mdx b/ui/components/component-library/button-secondary/README.mdx index 177cde8d0..35570acf3 100644 --- a/ui/components/component-library/button-secondary/README.mdx +++ b/ui/components/component-library/button-secondary/README.mdx @@ -12,33 +12,28 @@ The `ButtonSecondary` is an extension of `ButtonBase` to support secondary style ## Props -The `ButtonSecondary` accepts all props below as well as all [Box](/docs/components-ui-box--default-story#props) and [ButtonBase](/docs/components-componentlibrary-buttonbase--default-story#props) component props - ### Size -Use the `size` prop and the `Size` object from `./ui/helpers/constants/design-system.js` to change the size of `ButtonSecondary`. Defaults to `Size.MD` - -Optional: `BUTTON_SIZES` from `./button-base` object can be used instead of `Size`. +Use the `size` prop and the `ButtonSecondarySize` enum from `./ui/components/component-library` to change the size of `ButtonSecondary`. Defaults to `ButtonSecondarySize.Md` Possible sizes include: -- `Size.SM` 32px -- `Size.MD` 40px -- `Size.LG` 48px +- `ButtonSecondarySize.Sm` 32px +- `ButtonSecondarySize.Md` 40px +- `ButtonSecondarySize.Lg` 48px ```jsx -import { Size } from '../../../helpers/constants/design-system'; -import { ButtonSecondary } from '../../component-library'; +import { ButtonSecondary, ButtonSecondarySize } from '../../component-library'; - - - + + + ``` ### Danger diff --git a/ui/components/component-library/button-secondary/__snapshots__/button-secondary.test.js.snap b/ui/components/component-library/button-secondary/__snapshots__/button-secondary.test.tsx.snap similarity index 100% rename from ui/components/component-library/button-secondary/__snapshots__/button-secondary.test.js.snap rename to ui/components/component-library/button-secondary/__snapshots__/button-secondary.test.tsx.snap diff --git a/ui/components/component-library/button-secondary/button-secondary.constants.js b/ui/components/component-library/button-secondary/button-secondary.constants.js deleted file mode 100644 index 28181aa04..000000000 --- a/ui/components/component-library/button-secondary/button-secondary.constants.js +++ /dev/null @@ -1,7 +0,0 @@ -import { Size } from '../../../helpers/constants/design-system'; - -export const BUTTON_SECONDARY_SIZES = { - SM: Size.SM, - MD: Size.MD, - LG: Size.LG, -}; diff --git a/ui/components/component-library/button-secondary/button-secondary.js b/ui/components/component-library/button-secondary/button-secondary.js deleted file mode 100644 index 399ea67fe..000000000 --- a/ui/components/component-library/button-secondary/button-secondary.js +++ /dev/null @@ -1,54 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import classnames from 'classnames'; - -import { ButtonBase } from '../button-base'; -import { Color } from '../../../helpers/constants/design-system'; -import { BUTTON_SECONDARY_SIZES } from './button-secondary.constants'; - -export const ButtonSecondary = ({ - className, - danger, - disabled, - size = BUTTON_SECONDARY_SIZES.MD, - ...props -}) => { - const buttonColor = danger ? Color.errorDefault : Color.primaryDefault; - return ( - - ); -}; - -ButtonSecondary.propTypes = { - /** - * An additional className to apply to the ButtonSecondary. - */ - className: PropTypes.string, - /** - * When true, ButtonSecondary color becomes Danger. - */ - danger: PropTypes.bool, - /** - * Boolean to disable button - */ - disabled: PropTypes.bool, - /** - * Possible size values: 'SIZES.SM'(32px), 'SIZES.MD'(40px), 'SIZES.LG'(48px). - * Default value is 'SIZES.MD'. - */ - size: PropTypes.oneOf(Object.values(BUTTON_SECONDARY_SIZES)), - /** - * ButtonSecondary accepts all the props from ButtonBase - */ - ...ButtonBase.propTypes, -}; diff --git a/ui/components/component-library/button-secondary/button-secondary.scss b/ui/components/component-library/button-secondary/button-secondary.scss index f602270bf..c279ce88a 100644 --- a/ui/components/component-library/button-secondary/button-secondary.scss +++ b/ui/components/component-library/button-secondary/button-secondary.scss @@ -1,5 +1,5 @@ .mm-button-secondary { - &:hover { + &:hover:not(&--disabled) { color: var(--color-primary-inverse); background-color: var(--color-primary-default); box-shadow: var(--component-button-primary-shadow); @@ -12,7 +12,7 @@ } // Danger type - &--type-danger { + &--type-danger:not(&--disabled) { color: var(--color-error-default); border: 1px solid var(--color-error-default); background-color: transparent; diff --git a/ui/components/component-library/button-secondary/button-secondary.stories.js b/ui/components/component-library/button-secondary/button-secondary.stories.tsx similarity index 77% rename from ui/components/component-library/button-secondary/button-secondary.stories.js rename to ui/components/component-library/button-secondary/button-secondary.stories.tsx index 94166d359..a7883213f 100644 --- a/ui/components/component-library/button-secondary/button-secondary.stories.js +++ b/ui/components/component-library/button-secondary/button-secondary.stories.tsx @@ -1,14 +1,9 @@ import React from 'react'; -import { - AlignItems, - DISPLAY, - Size, -} from '../../../helpers/constants/design-system'; -import Box from '../../ui/box/box'; -import { IconName } from '..'; -import { ButtonSecondary } from './button-secondary'; -import { BUTTON_SECONDARY_SIZES } from './button-secondary.constants'; +import { StoryFn, Meta } from '@storybook/react'; +import { AlignItems, Display } from '../../../helpers/constants/design-system'; +import { IconName, Box } from '..'; import README from './README.mdx'; +import { ButtonSecondary, ButtonSecondarySize } from '.'; const marginSizeControlOptions = [ undefined, @@ -84,7 +79,7 @@ export default { }, size: { control: 'select', - options: Object.values(BUTTON_SECONDARY_SIZES), + options: Object.values(ButtonSecondarySize), }, marginTop: { options: marginSizeControlOptions, @@ -110,29 +105,29 @@ export default { args: { children: 'Button Secondary', }, -}; +} as Meta; export const DefaultStory = (args) => ; DefaultStory.storyName = 'Default'; -export const SizeStory = (args) => ( - - +export const SizeStory: StoryFn = (args) => ( + + Small Button - + Medium (Default) Button - + Large Button ); SizeStory.storyName = 'Size'; -export const Danger = (args) => ( - +export const Danger: StoryFn = (args) => ( + Normal {/* Test Anchor tag to match exactly as button */} diff --git a/ui/components/component-library/button-secondary/button-secondary.test.js b/ui/components/component-library/button-secondary/button-secondary.test.tsx similarity index 77% rename from ui/components/component-library/button-secondary/button-secondary.test.js rename to ui/components/component-library/button-secondary/button-secondary.test.tsx index 0529664cd..f8730d34a 100644 --- a/ui/components/component-library/button-secondary/button-secondary.test.js +++ b/ui/components/component-library/button-secondary/button-secondary.test.tsx @@ -2,8 +2,7 @@ import { render } from '@testing-library/react'; import React from 'react'; import { IconName } from '..'; -import { ButtonSecondary } from './button-secondary'; -import { BUTTON_SECONDARY_SIZES } from './button-secondary.constants'; +import { ButtonSecondary, ButtonSecondarySize } from '.'; describe('ButtonSecondary', () => { it('should render button element correctly', () => { @@ -45,28 +44,28 @@ describe('ButtonSecondary', () => { const { getByTestId } = render( <> , ); - expect(getByTestId(BUTTON_SECONDARY_SIZES.SM)).toHaveClass( - `mm-button-base--size-${BUTTON_SECONDARY_SIZES.SM}`, + expect(getByTestId(ButtonSecondarySize.Sm)).toHaveClass( + `mm-button-base--size-${ButtonSecondarySize.Sm}`, ); - expect(getByTestId(BUTTON_SECONDARY_SIZES.MD)).toHaveClass( - `mm-button-base--size-${BUTTON_SECONDARY_SIZES.MD}`, + expect(getByTestId(ButtonSecondarySize.Md)).toHaveClass( + `mm-button-base--size-${ButtonSecondarySize.Md}`, ); - expect(getByTestId(BUTTON_SECONDARY_SIZES.LG)).toHaveClass( - `mm-button-base--size-${BUTTON_SECONDARY_SIZES.LG}`, + expect(getByTestId(ButtonSecondarySize.Lg)).toHaveClass( + `mm-button-base--size-${ButtonSecondarySize.Lg}`, ); }); diff --git a/ui/components/component-library/button-secondary/button-secondary.tsx b/ui/components/component-library/button-secondary/button-secondary.tsx new file mode 100644 index 000000000..404b97301 --- /dev/null +++ b/ui/components/component-library/button-secondary/button-secondary.tsx @@ -0,0 +1,40 @@ +import React from 'react'; +import classnames from 'classnames'; + +import { ButtonBase, ButtonBaseProps } from '../button-base'; +import { Color } from '../../../helpers/constants/design-system'; +import { PolymorphicRef } from '../box'; +import type { ButtonSecondaryProps } from './button-secondary.types'; +import { + ButtonSecondarySize, + ButtonSecondaryComponent, +} from './button-secondary.types'; + +export const ButtonSecondary: ButtonSecondaryComponent = React.forwardRef( + ( + { + className = '', + danger, + disabled, + size = ButtonSecondarySize.Md, + ...props + }: ButtonSecondaryProps, + ref?: PolymorphicRef, + ) => { + const buttonColor = danger ? Color.errorDefault : Color.primaryDefault; + return ( + ) }} + /> + ); + }, +); diff --git a/ui/components/component-library/button-secondary/button-secondary.types.ts b/ui/components/component-library/button-secondary/button-secondary.types.ts new file mode 100644 index 000000000..e3d73d467 --- /dev/null +++ b/ui/components/component-library/button-secondary/button-secondary.types.ts @@ -0,0 +1,38 @@ +import type { ButtonBaseStyleUtilityProps } from '../button-base/button-base.types'; +import type { PolymorphicComponentPropWithRef } from '../box'; + +export enum ButtonSecondarySize { + Sm = 'sm', + Md = 'md', + Lg = 'lg', +} + +export interface ButtonSecondaryStyleUtilityProps + extends Omit { + /** + * An additional className to apply to the ButtonSecondary. + */ + className?: string; + /** + * When true, ButtonSecondary color becomes Danger. + */ + danger?: boolean; + /** + * Boolean to disable button + */ + disabled?: boolean; + /** + * Possible size values: 'ButtonSecondarySize.Sm'(32px), 'ButtonSecondarySize.Md'(40px), 'ButtonSecondarySize.Lg'(48px). + * Default value is 'ButtonSecondarySize.Md'. + */ + size?: ButtonSecondarySize; +} + +export type ButtonSecondaryProps = + PolymorphicComponentPropWithRef; + +export type ButtonSecondaryComponent = < + C extends React.ElementType = 'button' | 'a', +>( + props: ButtonSecondaryProps, +) => React.ReactElement | null; diff --git a/ui/components/component-library/button-secondary/index.js b/ui/components/component-library/button-secondary/index.js deleted file mode 100644 index bccde44c2..000000000 --- a/ui/components/component-library/button-secondary/index.js +++ /dev/null @@ -1,2 +0,0 @@ -export { ButtonSecondary } from './button-secondary'; -export { BUTTON_SECONDARY_SIZES } from './button-secondary.constants'; diff --git a/ui/components/component-library/button-secondary/index.ts b/ui/components/component-library/button-secondary/index.ts new file mode 100644 index 000000000..5f7818004 --- /dev/null +++ b/ui/components/component-library/button-secondary/index.ts @@ -0,0 +1,3 @@ +export { ButtonSecondary } from './button-secondary'; +export { ButtonSecondarySize } from './button-secondary.types'; +export type { ButtonSecondaryProps } from './button-secondary.types'; diff --git a/ui/components/component-library/index.js b/ui/components/component-library/index.js index a993c763a..f7ee14fb1 100644 --- a/ui/components/component-library/index.js +++ b/ui/components/component-library/index.js @@ -20,7 +20,7 @@ export { ButtonBase, ButtonBaseSize } from './button-base'; export { ButtonIcon, ButtonIconSize } from './button-icon'; export { ButtonLink, ButtonLinkSize } from './button-link'; export { ButtonPrimary, ButtonPrimarySize } from './button-primary'; -export { ButtonSecondary, BUTTON_SECONDARY_SIZES } from './button-secondary'; +export { ButtonSecondary, ButtonSecondarySize } from './button-secondary'; export { Checkbox } from './checkbox'; export { FormTextField } from './form-text-field'; export { HeaderBase } from './header-base'; diff --git a/ui/components/multichain/account-details/account-details-display.js b/ui/components/multichain/account-details/account-details-display.js index 7e70bfaae..410515089 100644 --- a/ui/components/multichain/account-details/account-details-display.js +++ b/ui/components/multichain/account-details/account-details-display.js @@ -13,7 +13,7 @@ import { } from '../../../selectors'; import { isAbleToExportAccount } from '../../../helpers/utils/util'; import { - BUTTON_SECONDARY_SIZES, + ButtonSecondarySize, ButtonSecondary, Box, } from '../../component-library'; @@ -74,7 +74,7 @@ export const AccountDetailsDisplay = ({ {exportPrivateKeyFeatureEnabled ? ( { trackEvent({ diff --git a/ui/components/multichain/activity-list-item/__snapshots__/activity-list-item.test.js.snap b/ui/components/multichain/activity-list-item/__snapshots__/activity-list-item.test.js.snap index 81f7135bd..3bce90a09 100644 --- a/ui/components/multichain/activity-list-item/__snapshots__/activity-list-item.test.js.snap +++ b/ui/components/multichain/activity-list-item/__snapshots__/activity-list-item.test.js.snap @@ -86,7 +86,7 @@ exports[`ActivityListItem should match snapshot with props 1`] = `

Content rendered to the right diff --git a/ui/components/multichain/activity-list-item/activity-list-item.js b/ui/components/multichain/activity-list-item/activity-list-item.js index 1cc51fb0e..e2a2f3100 100644 --- a/ui/components/multichain/activity-list-item/activity-list-item.js +++ b/ui/components/multichain/activity-list-item/activity-list-item.js @@ -114,6 +114,7 @@ export const ActivityListItem = ({ {t('cancel')} @@ -49,7 +49,7 @@ export default function BottomButtons({ } }} disabled={isPrimaryDisabled} - size={BUTTON_SECONDARY_SIZES.LG} + size={ButtonSecondarySize.Lg} data-testid="import-account-confirm-button" block > diff --git a/ui/components/multichain/import-nfts-modal/import-nfts-modal.js b/ui/components/multichain/import-nfts-modal/import-nfts-modal.js index b399c0fb9..0d5544660 100644 --- a/ui/components/multichain/import-nfts-modal/import-nfts-modal.js +++ b/ui/components/multichain/import-nfts-modal/import-nfts-modal.js @@ -44,6 +44,7 @@ import { Modal, ButtonPrimary, ButtonSecondary, + ButtonSecondarySize, Box, FormTextField, Label, @@ -258,7 +259,7 @@ export const ImportNftsModal = ({ onClose }) => { paddingBottom={4} > onClose()} block className="import-nfts-modal__cancel-button" diff --git a/ui/components/multichain/import-token-link/import-token-link.js b/ui/components/multichain/import-token-link/import-token-link.js index 699db9136..914e8110f 100644 --- a/ui/components/multichain/import-token-link/import-token-link.js +++ b/ui/components/multichain/import-token-link/import-token-link.js @@ -1,6 +1,5 @@ import React, { useContext } from 'react'; import { useDispatch, useSelector } from 'react-redux'; -import { useHistory } from 'react-router-dom'; import PropTypes from 'prop-types'; import classnames from 'classnames'; import { ButtonLink, IconName, Box } from '../../component-library'; @@ -10,8 +9,7 @@ import { Size, } from '../../../helpers/constants/design-system'; import { useI18nContext } from '../../../hooks/useI18nContext'; -import { IMPORT_TOKEN_ROUTE } from '../../../helpers/constants/routes'; -import { detectNewTokens } from '../../../store/actions'; +import { detectNewTokens, showImportTokensModal } from '../../../store/actions'; import { MetaMetricsContext } from '../../../contexts/metametrics'; import { MetaMetricsEventCategory, @@ -25,7 +23,6 @@ import { export const ImportTokenLink = ({ className, ...props }) => { const trackEvent = useContext(MetaMetricsContext); const t = useI18nContext(); - const history = useHistory(); const dispatch = useDispatch(); const isTokenDetectionSupported = useSelector(getIsTokenDetectionSupported); @@ -48,7 +45,7 @@ export const ImportTokenLink = ({ className, ...props }) => { data-testid="import-token-button" startIconName={IconName.Add} onClick={() => { - history.push(IMPORT_TOKEN_ROUTE); + dispatch(showImportTokensModal()); trackEvent({ event: MetaMetricsEventName.TokenImportButtonClicked, category: MetaMetricsEventCategory.Navigation, diff --git a/ui/components/multichain/import-token-link/import-token-link.test.js b/ui/components/multichain/import-token-link/import-token-link.test.js index a4763f6de..f4008cfc3 100644 --- a/ui/components/multichain/import-token-link/import-token-link.test.js +++ b/ui/components/multichain/import-token-link/import-token-link.test.js @@ -19,7 +19,12 @@ jest.mock('react-router-dom', () => { }); jest.mock('../../../store/actions.ts', () => ({ - detectNewTokens: jest.fn().mockReturnValue({ type: '' }), + detectNewTokens: jest + .fn() + .mockImplementation(() => ({ type: 'DETECT_TOKENS' })), + showImportTokensModal: jest + .fn() + .mockImplementation(() => ({ type: 'UI_IMPORT_TOKENS_POPOVER_OPEN' })), })); describe('Import Token Link', () => { @@ -90,6 +95,6 @@ describe('Import Token Link', () => { const importToken = screen.getByTestId('import-token-button'); fireEvent.click(importToken); - expect(mockPushHistory).toHaveBeenCalledWith('/import-token'); + expect(screen.getByText('Import tokens')).toBeInTheDocument(); }); }); diff --git a/ui/components/multichain/import-tokens-modal/import-tokens-modal-confirm.js b/ui/components/multichain/import-tokens-modal/import-tokens-modal-confirm.js new file mode 100644 index 000000000..43d4808f8 --- /dev/null +++ b/ui/components/multichain/import-tokens-modal/import-tokens-modal-confirm.js @@ -0,0 +1,108 @@ +import React from 'react'; +import { useSelector } from 'react-redux'; +import PropTypes from 'prop-types'; +import { + Box, + ButtonPrimary, + ButtonSecondary, + Text, +} from '../../component-library'; +import { + AlignItems, + Display, + Size, + TextAlign, + TextColor, + TextVariant, +} from '../../../helpers/constants/design-system'; +import TokenBalance from '../../ui/token-balance/token-balance'; +import Identicon from '../../ui/identicon'; +import { useI18nContext } from '../../../hooks/useI18nContext'; +import { getPendingTokens } from '../../../ducks/metamask/metamask'; + +export const ImportTokensModalConfirm = ({ onBackClick, onImportClick }) => { + const t = useI18nContext(); + const pendingTokens = useSelector(getPendingTokens); + + return ( + + {t('likeToImportTokens')} + + + + {t('token')} + + + {t('balance')} + + + + {Object.entries(pendingTokens).map(([address, token]) => { + const { name, symbol } = token; + return ( + + + + + {name} + + {symbol} + + + + + + + + ); + })} + + + + {t('back')} + + + {t('import')} + + + + + ); +}; + +ImportTokensModalConfirm.propTypes = { + onBackClick: PropTypes.func.isRequired, + onImportClick: PropTypes.func.isRequired, +}; diff --git a/ui/components/multichain/import-tokens-modal/import-tokens-modal-confirm.stories.js b/ui/components/multichain/import-tokens-modal/import-tokens-modal-confirm.stories.js new file mode 100644 index 000000000..2df36b65d --- /dev/null +++ b/ui/components/multichain/import-tokens-modal/import-tokens-modal-confirm.stories.js @@ -0,0 +1,100 @@ +import React from 'react'; +import { Provider } from 'react-redux'; +import configureStore from '../../../store/store'; +import testData from '../../../../.storybook/test-data'; +import { CHAIN_IDS } from '../../../../shared/constants/network'; +import { ImportTokensModalConfirm } from './import-tokens-modal-confirm'; + +const createStore = ( + chainId = CHAIN_IDS.MAINNET, + useTokenDetection = true, + tokenRepetition = 1, +) => { + return configureStore({ + ...testData, + metamask: { + ...testData.metamask, + useTokenDetection, + providerConfig: { chainId }, + pendingTokens: { + '0x0000000de40dfa9b17854cbc7869d80f9f98d823': { + address: '0x0000000de40dfa9b17854cbc7869d80f9f98d823', + aggregators: ['CoinGecko', 'Sonarwatch', 'Coinmarketcap'], + decimals: 18, + fees: {}, + iconUrl: + 'https://static.metafi.codefi.network/api/v1/tokenIcons/1/0x0000000de40dfa9b17854cbc7869d80f9f98d823.png', + name: 'delta theta'.repeat(tokenRepetition), + occurrences: 3, + symbol: 'DLTA', + type: 'erc20', + unlisted: false, + }, + '0x00d8318e44780edeefcf3020a5448f636788883c': { + address: '0x00d8318e44780edeefcf3020a5448f636788883c', + aggregators: ['CoinGecko', 'Sonarwatch', 'Coinmarketcap'], + decimals: 18, + fees: {}, + iconUrl: + 'https://static.metafi.codefi.network/api/v1/tokenIcons/1/0x00d8318e44780edeefcf3020a5448f636788883c.png', + name: 'dAppstore'.repeat(tokenRepetition), + occurrences: 3, + symbol: 'DAPPX', + type: 'erc20', + unlisted: false, + }, + '0x00e679ba63b509182c349f5614f0a07cdd0ce0c5': { + address: '0x00e679ba63b509182c349f5614f0a07cdd0ce0c5', + aggregators: ['CoinGecko', 'Sonarwatch', 'Coinmarketcap'], + decimals: 18, + iconUrl: + 'https://static.metafi.codefi.network/api/v1/tokenIcons/1/0x00e679ba63b509182c349f5614f0a07cdd0ce0c5.png', + name: 'Damex Token'.repeat(tokenRepetition), + occurrences: 3, + symbol: 'DAMEX', + type: 'erc20', + unlisted: false, + }, + }, + }, + }); +}; + +export default { + title: 'Components/Multichain/ImportTokensModal/ImportTokensModalConfirm', + component: ImportTokensModalConfirm, + argTypes: { + onBackClick: { + action: 'onClose', + }, + onImportClick: { + action: 'onClose', + }, + }, +}; + +export const DefaultStory = (args) => ; +DefaultStory.decorators = [ + (Story) => ( + + + + ), +]; + +DefaultStory.storyName = 'Default'; + +export const LongValueStory = (args) => ( +

+ +
+); +LongValueStory.decorators = [ + (Story) => ( + + + + ), +]; + +LongValueStory.storyName = 'LongValueStory'; diff --git a/ui/components/multichain/import-tokens-modal/import-tokens-modal.js b/ui/components/multichain/import-tokens-modal/import-tokens-modal.js new file mode 100644 index 000000000..2e8ef6b10 --- /dev/null +++ b/ui/components/multichain/import-tokens-modal/import-tokens-modal.js @@ -0,0 +1,649 @@ +import React, { + useCallback, + useContext, + useEffect, + useRef, + useState, +} from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { useHistory } from 'react-router-dom'; +import PropTypes from 'prop-types'; +import { getTokenTrackerLink } from '@metamask/etherscan-link/dist/token-tracker-link'; +import { Tab, Tabs } from '../../ui/tabs'; +import { useI18nContext } from '../../../hooks/useI18nContext'; +import { + getCurrentChainId, + getIsDynamicTokenListAvailable, + getIsMainnet, + getIsTokenDetectionInactiveOnMainnet, + getIsTokenDetectionSupported, + getIstokenDetectionInactiveOnNonMainnetSupportedNetwork, + getMetaMaskIdentities, + getRpcPrefsForCurrentProvider, + getSelectedAddress, + getTokenDetectionSupportNetworkByChainId, + getTokenList, +} from '../../../selectors'; +import { + addImportedTokens, + clearPendingTokens, + getTokenStandardAndDetails, + setPendingTokens, + showImportNftsModal, +} from '../../../store/actions'; +import { + BannerAlert, + ButtonLink, + ButtonPrimary, + Text, + FormTextField, + Box, + Modal, + ModalOverlay, + ModalContent, + ModalHeader, +} from '../../component-library'; +import TokenSearch from '../../app/import-token/token-search'; +import TokenList from '../../app/import-token/token-list'; + +import { + FontWeight, + Severity, + Size, + TextAlign, + TextColor, +} from '../../../helpers/constants/design-system'; + +import { ASSET_ROUTE, SECURITY_ROUTE } from '../../../helpers/constants/routes'; +import ZENDESK_URLS from '../../../helpers/constants/zendesk-url'; +import { isValidHexAddress } from '../../../../shared/modules/hexstring-utils'; +import { addHexPrefix } from '../../../../app/scripts/lib/util'; +import { STATIC_MAINNET_TOKEN_LIST } from '../../../../shared/constants/tokens'; +import { + AssetType, + TokenStandard, +} from '../../../../shared/constants/transaction'; +import { + checkExistingAddresses, + getURLHostName, +} from '../../../helpers/utils/util'; +import { tokenInfoGetter } from '../../../helpers/utils/token-util'; +import { MetaMetricsContext } from '../../../contexts/metametrics'; +import { getPendingTokens } from '../../../ducks/metamask/metamask'; +import { + MetaMetricsEventCategory, + MetaMetricsEventName, + MetaMetricsTokenEventSource, +} from '../../../../shared/constants/metametrics'; +import { ImportTokensModalConfirm } from './import-tokens-modal-confirm'; + +export const ImportTokensModal = ({ onClose }) => { + const t = useI18nContext(); + const history = useHistory(); + const dispatch = useDispatch(); + + const [mode, setMode] = useState(''); + + const [tokenSelectorError, setTokenSelectorError] = useState(null); + const [selectedTokens, setSelectedTokens] = useState({}); + const [searchResults, setSearchResults] = useState([]); + + // Determine if we should show the search tab + const isTokenDetectionSupported = useSelector(getIsTokenDetectionSupported); + const isTokenDetectionInactiveOnMainnet = useSelector( + getIsTokenDetectionInactiveOnMainnet, + ); + const showSearchTab = + isTokenDetectionSupported || + isTokenDetectionInactiveOnMainnet || + Boolean(process.env.IN_TEST); + + const tokenList = useSelector(getTokenList); + const useTokenDetection = useSelector( + ({ metamask }) => metamask.useTokenDetection, + ); + const networkName = useSelector(getTokenDetectionSupportNetworkByChainId); + + // Custom token stuff + const tokenDetectionInactiveOnNonMainnetSupportedNetwork = useSelector( + getIstokenDetectionInactiveOnNonMainnetSupportedNetwork, + ); + const isDynamicTokenListAvailable = useSelector( + getIsDynamicTokenListAvailable, + ); + const selectedAddress = useSelector(getSelectedAddress); + const isMainnet = useSelector(getIsMainnet); + const identities = useSelector(getMetaMaskIdentities); + const tokens = useSelector((state) => state.metamask.tokens); + const rpcPrefs = useSelector(getRpcPrefsForCurrentProvider); + + const [customAddress, setCustomAddress] = useState(''); + const [customAddressError, setCustomAddressError] = useState(null); + const [nftAddressError, setNftAddressError] = useState(null); + const [symbolAutoFilled, setSymbolAutoFilled] = useState(false); + const [decimalAutoFilled, setDecimalAutoFilled] = useState(false); + const [mainnetTokenWarning, setMainnetTokenWarning] = useState(null); + const [customSymbol, setCustomSymbol] = useState(''); + const [customSymbolError, setCustomSymbolError] = useState(null); + const [customDecimals, setCustomDecimals] = useState(0); + const [customDecimalsError, setCustomDecimalsError] = useState(null); + const [tokenStandard, setTokenStandard] = useState(TokenStandard.none); + const [forceEditSymbol, setForceEditSymbol] = useState(false); + + const chainId = useSelector(getCurrentChainId); + const blockExplorerTokenLink = getTokenTrackerLink( + customAddress, + chainId, + null, + null, + { blockExplorerUrl: rpcPrefs?.blockExplorerUrl ?? null }, + ); + const blockExplorerLabel = rpcPrefs?.blockExplorerUrl + ? getURLHostName(blockExplorerTokenLink) + : t('etherscan'); + + // Min and Max decimal values + const EMPTY_ADDRESS = '0x0000000000000000000000000000000000000000'; + const MIN_DECIMAL_VALUE = 0; + const MAX_DECIMAL_VALUE = 36; + + const infoGetter = useRef(tokenInfoGetter()); + + // CONFIRMATION MODE + const trackEvent = useContext(MetaMetricsContext); + const pendingTokens = useSelector(getPendingTokens); + + const handleAddTokens = useCallback(async () => { + const addedTokenValues = Object.values(pendingTokens); + await dispatch(addImportedTokens(addedTokenValues)); + + const firstTokenAddress = addedTokenValues?.[0].address?.toLowerCase(); + + addedTokenValues.forEach((pendingToken) => { + trackEvent({ + event: MetaMetricsEventName.TokenAdded, + category: MetaMetricsEventCategory.Wallet, + sensitiveProperties: { + token_symbol: pendingToken.symbol, + token_contract_address: pendingToken.address, + token_decimal_precision: pendingToken.decimals, + unlisted: pendingToken.unlisted, + source_connection_method: pendingToken.isCustom + ? MetaMetricsTokenEventSource.Custom + : MetaMetricsTokenEventSource.List, + token_standard: TokenStandard.ERC20, + asset_type: AssetType.token, + }, + }); + }); + + dispatch(clearPendingTokens()); + + if (firstTokenAddress) { + history.push(`${ASSET_ROUTE}/${firstTokenAddress}`); + } + }, [dispatch, history, pendingTokens, trackEvent]); + + useEffect(() => { + const pendingTokenKeys = Object.keys(pendingTokens); + + if (pendingTokenKeys.length === 0) { + return; + } + + let initialSelectedTokens = {}; + let initialCustomToken = {}; + + pendingTokenKeys.forEach((tokenAddress) => { + const token = pendingTokens[tokenAddress]; + const { isCustom } = token; + + if (isCustom) { + initialCustomToken = { ...token }; + } else { + initialSelectedTokens = { + ...selectedTokens, + [tokenAddress]: { ...token }, + }; + } + }); + + setSelectedTokens(initialSelectedTokens); + setCustomAddress(initialCustomToken.address); + setCustomSymbol(initialCustomToken.symbol); + setCustomDecimals(initialCustomToken.decimals); + }, [pendingTokens]); + + const handleCustomSymbolChange = (value) => { + const symbol = value.trim(); + const symbolLength = symbol.length; + let symbolError = null; + + if (symbolLength <= 0 || symbolLength >= 12) { + symbolError = t('symbolBetweenZeroTwelve'); + } + + setCustomSymbol(symbol); + setCustomSymbolError(symbolError); + }; + + const handleCustomDecimalsChange = (value) => { + let decimals; + let decimalsError = null; + + if (value) { + decimals = Number(value.trim()); + decimalsError = + value < MIN_DECIMAL_VALUE || value > MAX_DECIMAL_VALUE + ? t('decimalsMustZerotoTen') + : null; + } else { + decimals = ''; + decimalsError = t('tokenDecimalFetchFailed'); + } + + setCustomDecimals(decimals); + setCustomDecimalsError(decimalsError); + }; + + const attemptToAutoFillTokenParams = async (address) => { + const { symbol = '', decimals } = await infoGetter.current( + address, + tokenList, + ); + + setSymbolAutoFilled(Boolean(symbol)); + setDecimalAutoFilled(Boolean(decimals)); + + handleCustomSymbolChange(symbol || ''); + handleCustomDecimalsChange(decimals); + }; + + const handleToggleToken = (token) => { + const { address } = token; + const selectedTokensCopy = { ...selectedTokens }; + + if (address in selectedTokensCopy) { + delete selectedTokensCopy[address]; + } else { + selectedTokensCopy[address] = token; + } + + setSelectedTokens(selectedTokensCopy); + setTokenSelectorError(null); + }; + + const hasError = () => { + return ( + tokenSelectorError || + customAddressError || + customSymbolError || + customDecimalsError || + nftAddressError + ); + }; + + const hasSelected = () => { + return customAddress || Object.keys(selectedTokens).length > 0; + }; + + const handleNext = () => { + if (hasError()) { + return; + } + + if (!hasSelected()) { + setTokenSelectorError(t('mustSelectOne')); + return; + } + + const tokenAddressList = Object.keys(tokenList); + const customToken = customAddress + ? { + address: customAddress, + symbol: customSymbol, + decimals: customDecimals, + standard: tokenStandard, + } + : null; + + dispatch( + setPendingTokens({ customToken, selectedTokens, tokenAddressList }), + ); + setMode('confirm'); + }; + + const handleCustomAddressChange = async (value) => { + const address = value.trim(); + + setCustomAddress(address); + setCustomAddressError(null); + setNftAddressError(null); + setSymbolAutoFilled(false); + setDecimalAutoFilled(false); + setMainnetTokenWarning(null); + + const addressIsValid = isValidHexAddress(address, { + allowNonPrefixed: false, + }); + const standardAddress = addHexPrefix(address).toLowerCase(); + + const isMainnetToken = Object.keys(STATIC_MAINNET_TOKEN_LIST).some( + (key) => key.toLowerCase() === address.toLowerCase(), + ); + + let standard; + if (addressIsValid) { + try { + ({ standard } = await getTokenStandardAndDetails( + standardAddress, + selectedAddress, + null, + )); + } catch (error) { + // ignore + } + } + + const addressIsEmpty = address.length === 0 || address === EMPTY_ADDRESS; + + switch (true) { + case !addressIsValid && !addressIsEmpty: + setCustomAddressError(t('invalidAddress')); + setCustomSymbol(''); + setCustomDecimals(0); + setCustomSymbolError(null); + setCustomDecimalsError(null); + break; + + case standard === TokenStandard.ERC1155 || + standard === TokenStandard.ERC721: + setNftAddressError( + t('nftAddressError', [ + { + dispatch(showImportNftsModal()); + onClose(); + }} + color={TextColor.primaryDefault} + key="nftAddressError" + > + {t('importNFTPage')} + , + ]), + ); + break; + + case isMainnetToken && !isMainnet: + setMainnetTokenWarning(t('mainnetToken')); + setCustomSymbol(''); + setCustomDecimals(0); + setCustomSymbolError(null); + setCustomDecimalsError(null); + break; + + case Boolean(identities[standardAddress]): + setCustomAddressError(t('personalAddressDetected')); + break; + + case checkExistingAddresses(address, tokens): + setCustomAddressError(t('tokenAlreadyAdded')); + break; + + default: + if (!addressIsEmpty) { + attemptToAutoFillTokenParams(address); + if (standard) { + setTokenStandard(standard); + } + } + } + }; + + // Determines whether to show the Search/Import or Confirm action + const isConfirming = mode === 'confirm'; + + return ( + { + dispatch(clearPendingTokens()); + onClose(); + }} + className="import-tokens-modal" + > + + + setMode('') : null} + onClose={() => { + dispatch(clearPendingTokens()); + onClose(); + }} + > + {t('importTokensCamelCase')} + + + {isConfirming ? ( + { + dispatch(clearPendingTokens()); + setMode(''); + }} + onImportClick={async () => { + await handleAddTokens(); + onClose(); + }} + /> + ) : ( + <> + + {showSearchTab ? ( + + + {useTokenDetection ? null : ( + + + {t('enhancedTokenDetectionAlertMessage', [ + networkName, + { + history.push( + `${SECURITY_ROUTE}#advanced-settings-autodetect-tokens`, + ); + onClose(); + }} + > + {t('enableFromSettings')} + , + ])} + + + )} + + setSearchResults(results) + } + error={tokenSelectorError} + tokenList={tokenList} + /> + + handleToggleToken(token)} + /> + + + + ) : null} + + + {tokenDetectionInactiveOnNonMainnetSupportedNetwork ? ( + + {t( + 'customTokenWarningInTokenDetectionNetworkWithTDOFF', + [ + + {t('tokenScamSecurityRisk')} + , + + history.push( + `${SECURITY_ROUTE}#advanced-settings-autodetect-tokens`, + ) + } + > + {t('inYourSettings')} + , + ], + )} + + ) : ( + + {t( + isDynamicTokenListAvailable + ? 'customTokenWarningInTokenDetectionNetwork' + : 'customTokenWarningInNonTokenDetectionNetwork', + [ + + {t('learnScamRisk')} + , + ], + )} + + )} + + handleCustomAddressChange(e.target.value) + } + helpText={ + customAddressError || + mainnetTokenWarning || + nftAddressError + } + error={ + customAddressError || + mainnetTokenWarning || + nftAddressError + } + autoFocus + marginTop={6} + inputProps={{ + 'data-testid': 'import-tokens-modal-custom-address', + }} + /> + + {t('tokenSymbol')} + {symbolAutoFilled && !forceEditSymbol && ( + setForceEditSymbol(true)} + textAlign={TextAlign.End} + paddingInlineEnd={1} + paddingInlineStart={1} + color={TextColor.primaryDefault} + > + {t('edit')} + + )} + + } + value={customSymbol} + onChange={(e) => handleCustomSymbolChange(e.target.value)} + helpText={customSymbolError} + error={customSymbolError} + disabled={symbolAutoFilled && !forceEditSymbol} + marginTop={6} + inputProps={{ + 'data-testid': 'import-tokens-modal-custom-symbol', + }} + /> + + handleCustomDecimalsChange(e.target.value) + } + helpText={customDecimalsError} + error={customDecimalsError} + disabled={decimalAutoFilled} + min={MIN_DECIMAL_VALUE} + max={MAX_DECIMAL_VALUE} + marginTop={6} + inputProps={{ + 'data-testid': 'import-tokens-modal-custom-decimals', + }} + /> + {customDecimals === '' && ( + + + {t('tokenDecimalFetchFailed')} + + {t('verifyThisTokenDecimalOn', [ + + {blockExplorerLabel} + , + ])} + + )} + + + + + handleNext()} + size={Size.LG} + disabled={Boolean(hasError()) || !hasSelected()} + block + > + {t('next')} + + + + )} + + + + ); +}; + +ImportTokensModal.propTypes = { + onClose: PropTypes.func.isRequired, +}; diff --git a/ui/components/multichain/import-tokens-modal/import-tokens-modal.scss b/ui/components/multichain/import-tokens-modal/import-tokens-modal.scss new file mode 100644 index 000000000..f1240f786 --- /dev/null +++ b/ui/components/multichain/import-tokens-modal/import-tokens-modal.scss @@ -0,0 +1,67 @@ +.import-tokens-modal { + $self: &; + + .tabs { + ul { + border-bottom: 0; + } + + li { + width: 50%; + } + } + + &__autodetect { + vertical-align: unset; + } + + &__search-list { + max-width: 100%; + overflow: auto; + max-height: 200px; + } + + &__custom-token-form { + input[type='number']::-webkit-inner-spin-button { + -webkit-appearance: none; + display: none; + } + + input[type='number']:hover::-webkit-inner-spin-button { + -webkit-appearance: none; + display: none; + } + & #{$self}__decimal-warning { + margin-top: 5px; + } + } + + &__token-name { + flex: 1; + } + + &__token-balance { + flex: 0 0 30%; + } + + &__confirm-token-list { + flex-flow: column nowrap; + + &-item { + flex-flow: row nowrap; + + &-wrapper { + flex-grow: 1; + + &__text { + max-width: 130px; + } + } + } + } + + &__nft-address-error-link { + display: contents; + font-size: inherit; + } +} diff --git a/ui/components/multichain/import-tokens-modal/import-tokens-modal.stories.js b/ui/components/multichain/import-tokens-modal/import-tokens-modal.stories.js new file mode 100644 index 000000000..04505d00b --- /dev/null +++ b/ui/components/multichain/import-tokens-modal/import-tokens-modal.stories.js @@ -0,0 +1,62 @@ +import React from 'react'; +import { Provider } from 'react-redux'; +import configureStore from '../../../store/store'; +import testData from '../../../../.storybook/test-data'; +import { CHAIN_IDS } from '../../../../shared/constants/network'; +import { ImportTokensModal } from './import-tokens-modal'; + +const createStore = (chainId = CHAIN_IDS.MAINNET, useTokenDetection = true) => { + return configureStore({ + ...testData, + metamask: { + ...testData.metamask, + useTokenDetection, + providerConfig: { chainId }, + }, + }); +}; + +export default { + title: 'Components/Multichain/ImportTokensModal', + component: ImportTokensModal, + argTypes: { + onClose: { + action: 'onClose', + }, + }, +}; + +export const DefaultStory = (args) => ; +DefaultStory.decorators = [ + (Story) => ( + + + + ), +]; + +DefaultStory.storyName = 'Default'; + +export const CustomImportOnlyStory = (args) => ; +CustomImportOnlyStory.decorators = [ + (Story) => ( + + + + ), +]; + +CustomImportOnlyStory.storyName = 'Custom Import Only'; + +export const TokenDetectionDisabledStory = (args) => ( + +); +TokenDetectionDisabledStory.decorators = [ + (Story) => ( + + + + ), +]; + +TokenDetectionDisabledStory.storyName = 'Token Detection Disabled'; diff --git a/ui/components/multichain/import-tokens-modal/import-tokens-modal.test.js b/ui/components/multichain/import-tokens-modal/import-tokens-modal.test.js new file mode 100644 index 000000000..e61320662 --- /dev/null +++ b/ui/components/multichain/import-tokens-modal/import-tokens-modal.test.js @@ -0,0 +1,200 @@ +import React from 'react'; +import { fireEvent } from '@testing-library/react'; +import { renderWithProvider } from '../../../../test/lib/render-helpers'; + +import configureStore from '../../../store/store'; +import { + setPendingTokens, + clearPendingTokens, + getTokenStandardAndDetails, +} from '../../../store/actions'; +import mockState from '../../../../test/data/mock-state.json'; +import { TokenStandard } from '../../../../shared/constants/transaction'; +import { ImportTokensModal } from '.'; + +jest.mock('../../../store/actions', () => ({ + getTokenStandardAndDetails: jest + .fn() + .mockImplementation(() => Promise.resolve({ standard: 'ERC20' })), + setPendingTokens: jest + .fn() + .mockImplementation(() => ({ type: 'SET_PENDING_TOKENS' })), + clearPendingTokens: jest + .fn() + .mockImplementation(() => ({ type: 'CLEAR_PENDING_TOKENS' })), +})); + +describe('ImportTokensModal', () => { + const render = (metamaskStateChanges = {}, onClose = jest.fn()) => { + const store = configureStore({ + ...mockState, + metamask: { + ...mockState.metamask, + ...metamaskStateChanges, + }, + }); + return renderWithProvider(, store); + }; + + describe('Search', () => { + it('renders expected elements', () => { + const { getByText, getByPlaceholderText } = render(); + expect( + getByText(`Add the tokens you've acquired using MetaMask`), + ).toBeInTheDocument(); + expect(getByText('Next')).toBeDisabled(); + expect(getByPlaceholderText('Search')).toBeInTheDocument(); + }); + + it('shows the token detection notice when setting is off', () => { + const { getByText } = render({ useTokenDetection: false }); + expect(getByText('Enable it from Settings.')).toBeInTheDocument(); + }); + }); + + describe('Custom Token', () => { + it('add custom token button is disabled when no fields are populated', () => { + const { getByText } = render(); + const customTokenButton = getByText('Custom token'); + fireEvent.click(customTokenButton); + const submit = getByText('Next'); + + expect(submit).toBeDisabled(); + }); + + it('edits token address', () => { + const { getByText, getByTestId } = render(); + const customTokenButton = getByText('Custom token'); + fireEvent.click(customTokenButton); + + const tokenAddress = '0x617b3f8050a0BD94b6b1da02B4384eE5B4DF13F4'; + const event = { target: { value: tokenAddress } }; + fireEvent.change( + getByTestId('import-tokens-modal-custom-address'), + event, + ); + + expect( + getByTestId('import-tokens-modal-custom-address').value, + ).toStrictEqual(tokenAddress); + }); + + it('edits token symbol', () => { + const { getByText, getByTestId } = render(); + const customTokenButton = getByText('Custom token'); + fireEvent.click(customTokenButton); + + const tokenSymbol = 'META'; + const event = { target: { value: tokenSymbol } }; + fireEvent.change(getByTestId('import-tokens-modal-custom-symbol'), event); + + expect( + getByTestId('import-tokens-modal-custom-symbol').value, + ).toStrictEqual(tokenSymbol); + }); + + it('edits token decimal precision', () => { + const { getByText, getByTestId } = render(); + const customTokenButton = getByText('Custom token'); + fireEvent.click(customTokenButton); + + const tokenPrecision = '2'; + const event = { target: { value: tokenPrecision } }; + fireEvent.change( + getByTestId('import-tokens-modal-custom-decimals'), + event, + ); + + expect( + getByTestId('import-tokens-modal-custom-decimals').value, + ).toStrictEqual(tokenPrecision); + }); + + it('adds custom tokens successfully', async () => { + const { getByText, getByTestId } = render({ tokens: [], tokenList: {} }); + const customTokenButton = getByText('Custom token'); + fireEvent.click(customTokenButton); + + expect(getByText('Next')).toBeDisabled(); + + const tokenAddress = '0x617b3f8050a0BD94b6b1da02B4384eE5B4DF13F4'; + await fireEvent.change( + getByTestId('import-tokens-modal-custom-address'), + { + target: { value: tokenAddress }, + }, + ); + expect(getByText('Next')).not.toBeDisabled(); + + const tokenSymbol = 'META'; + + fireEvent.change(getByTestId('import-tokens-modal-custom-symbol'), { + target: { value: tokenSymbol }, + }); + + expect(getByTestId('import-tokens-modal-custom-symbol').value).toBe( + 'META', + ); + + const tokenPrecision = '2'; + + fireEvent.change(getByTestId('import-tokens-modal-custom-decimals'), { + target: { value: tokenPrecision }, + }); + + expect(getByText('Next')).not.toBeDisabled(); + + fireEvent.click(getByText('Next')); + + expect(setPendingTokens).toHaveBeenCalledWith({ + customToken: { + address: tokenAddress, + decimals: Number(tokenPrecision), + standard: TokenStandard.ERC20, + symbol: tokenSymbol, + }, + selectedTokens: {}, + tokenAddressList: [], + }); + + expect(getByText('Import')).toBeInTheDocument(); + }); + + it('cancels out of import token flow', () => { + const onClose = jest.fn(); + render({}, onClose); + + fireEvent.click(document.querySelector('button[aria-label="Close"]')); + + expect(clearPendingTokens).toHaveBeenCalled(); + expect(onClose).toHaveBeenCalled(); + }); + + it('sets and error when a token is an NFT', async () => { + getTokenStandardAndDetails.mockImplementation(() => + Promise.resolve({ standard: TokenStandard.ERC721 }), + ); + + const { getByText, getByTestId } = render(); + const customTokenButton = getByText('Custom token'); + fireEvent.click(customTokenButton); + + const submit = getByText('Next'); + expect(submit).toBeDisabled(); + + const tokenAddress = '0x617b3f8050a0BD94b6b1da02B4384eE5B4DF13F4'; + await fireEvent.change( + getByTestId('import-tokens-modal-custom-address'), + { + target: { value: tokenAddress }, + }, + ); + + expect(submit).toBeDisabled(); + + // The last part of this error message won't be found by getByText because it is wrapped as a link. + const errorMessage = getByText('This token is an NFT. Add on the'); + expect(errorMessage).toBeInTheDocument(); + }); + }); +}); diff --git a/ui/components/multichain/import-tokens-modal/index.js b/ui/components/multichain/import-tokens-modal/index.js new file mode 100644 index 000000000..ecaf2735e --- /dev/null +++ b/ui/components/multichain/import-tokens-modal/index.js @@ -0,0 +1 @@ +export { ImportTokensModal } from './import-tokens-modal'; diff --git a/ui/components/multichain/index.js b/ui/components/multichain/index.js index 95a9b191b..e1e3359fc 100644 --- a/ui/components/multichain/index.js +++ b/ui/components/multichain/index.js @@ -18,3 +18,4 @@ export { CreateAccount } from './create-account'; export { ImportAccount } from './import-account'; export { ImportNftsModal } from './import-nfts-modal'; export { AccountDetailsMenuItem, ViewExplorerMenuItem } from './menu-items'; +export { ImportTokensModal } from './import-tokens-modal'; diff --git a/ui/components/multichain/multichain-components.scss b/ui/components/multichain/multichain-components.scss index c67217a0f..7f6282a35 100644 --- a/ui/components/multichain/multichain-components.scss +++ b/ui/components/multichain/multichain-components.scss @@ -19,3 +19,4 @@ @import 'network-list-menu/'; @import 'product-tour-popover/product-tour-popover'; @import 'nft-item/nft-item'; +@import 'import-tokens-modal/import-tokens-modal' diff --git a/ui/components/multichain/network-list-menu/network-list-menu.js b/ui/components/multichain/network-list-menu/network-list-menu.js index 49557caee..fb1e2c327 100644 --- a/ui/components/multichain/network-list-menu/network-list-menu.js +++ b/ui/components/multichain/network-list-menu/network-list-menu.js @@ -75,7 +75,7 @@ export const NetworkListMenu = ({ onClose }) => { const isUnlocked = useSelector((state) => state.metamask.isUnlocked); - const showSearch = nonTestNetworks.length > 3; + const showSearch = nonTestNetworks.length > 10; useEffect(() => { if (currentlyOnTestNetwork) { diff --git a/ui/components/ui/page-container/page-container.component.js b/ui/components/ui/page-container/page-container.component.js index d360bbc1b..315a557e6 100644 --- a/ui/components/ui/page-container/page-container.component.js +++ b/ui/components/ui/page-container/page-container.component.js @@ -88,19 +88,6 @@ export default class PageContainer extends PureComponent { return null; } - getTabSubmitText() { - const { tabsComponent } = this.props; - const { activeTabIndex } = this.state; - if (tabsComponent) { - let { children } = tabsComponent.props; - children = children.filter(Boolean); - if (children[activeTabIndex]?.key === 'custom-tab') { - return this.context.t('addCustomToken'); - } - } - return null; - } - render() { const { title, @@ -118,7 +105,6 @@ export default class PageContainer extends PureComponent { headerCloseText, hideCancel, } = this.props; - const tabSubmitText = this.getTabSubmitText(); return (
diff --git a/ui/components/ui/popover/index.scss b/ui/components/ui/popover/index.scss index 2b53cf892..409758812 100644 --- a/ui/components/ui/popover/index.scss +++ b/ui/components/ui/popover/index.scss @@ -90,4 +90,8 @@ transform: rotate(45deg); box-shadow: var(--shadow-size-lg) var(--color-shadow-default); } + + &-container .page-container { + width: auto; + } } diff --git a/ui/ducks/app/app.ts b/ui/ducks/app/app.ts index e34edcf2d..0ad619c0a 100644 --- a/ui/ducks/app/app.ts +++ b/ui/ducks/app/app.ts @@ -28,6 +28,7 @@ interface AppState { networkDropdownOpen: boolean; importNftsModalOpen: boolean; showIpfsModalOpen: boolean; + importTokensModalOpen: boolean; accountDetail: { subview?: string; accountExport?: string; @@ -98,6 +99,7 @@ const initialState: AppState = { networkDropdownOpen: false, importNftsModalOpen: false, showIpfsModalOpen: false, + importTokensModalOpen: false, accountDetail: { privateKey: '', }, @@ -191,6 +193,18 @@ export default function reduceApp( showIpfsModalOpen: false, }; + case actionConstants.IMPORT_TOKENS_POPOVER_OPEN: + return { + ...appState, + importTokensModalOpen: true, + }; + + case actionConstants.IMPORT_TOKENS_POPOVER_CLOSE: + return { + ...appState, + importTokensModalOpen: false, + }; + // alert methods case actionConstants.ALERT_OPEN: return { diff --git a/ui/pages/confirm-import-token/confirm-import-token.js b/ui/pages/confirm-import-token/confirm-import-token.js deleted file mode 100644 index e45ade9da..000000000 --- a/ui/pages/confirm-import-token/confirm-import-token.js +++ /dev/null @@ -1,150 +0,0 @@ -import React, { useCallback, useContext, useEffect } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; -import { useHistory } from 'react-router-dom'; -import { - ASSET_ROUTE, - IMPORT_TOKEN_ROUTE, -} from '../../helpers/constants/routes'; -import Button from '../../components/ui/button'; -import Identicon from '../../components/ui/identicon'; -import TokenBalance from '../../components/ui/token-balance'; -import { I18nContext } from '../../contexts/i18n'; -import { MetaMetricsContext } from '../../contexts/metametrics'; -import { getMostRecentOverviewPage } from '../../ducks/history/history'; -import { getPendingTokens } from '../../ducks/metamask/metamask'; -import { addImportedTokens, clearPendingTokens } from '../../store/actions'; -import { - MetaMetricsEventCategory, - MetaMetricsEventName, - MetaMetricsTokenEventSource, -} from '../../../shared/constants/metametrics'; -import { - AssetType, - TokenStandard, -} from '../../../shared/constants/transaction'; - -const getTokenName = (name, symbol) => { - return name === undefined ? symbol : `${name} (${symbol})`; -}; - -const ConfirmImportToken = () => { - const t = useContext(I18nContext); - const dispatch = useDispatch(); - const history = useHistory(); - const trackEvent = useContext(MetaMetricsContext); - - const mostRecentOverviewPage = useSelector(getMostRecentOverviewPage); - const pendingTokens = useSelector(getPendingTokens); - - const handleAddTokens = useCallback(async () => { - const addedTokenValues = Object.values(pendingTokens); - await dispatch(addImportedTokens(addedTokenValues)); - - const firstTokenAddress = addedTokenValues?.[0].address?.toLowerCase(); - - addedTokenValues.forEach((pendingToken) => { - trackEvent({ - event: MetaMetricsEventName.TokenAdded, - category: MetaMetricsEventCategory.Wallet, - sensitiveProperties: { - token_symbol: pendingToken.symbol, - token_contract_address: pendingToken.address, - token_decimal_precision: pendingToken.decimals, - unlisted: pendingToken.unlisted, - source: pendingToken.isCustom - ? MetaMetricsTokenEventSource.Custom - : MetaMetricsTokenEventSource.List, - token_standard: TokenStandard.ERC20, - asset_type: AssetType.token, - }, - }); - }); - - dispatch(clearPendingTokens()); - - if (firstTokenAddress) { - history.push(`${ASSET_ROUTE}/${firstTokenAddress}`); - } else { - history.push(mostRecentOverviewPage); - } - }, [dispatch, history, mostRecentOverviewPage, pendingTokens, trackEvent]); - - useEffect(() => { - if (Object.keys(pendingTokens).length === 0) { - history.push(mostRecentOverviewPage); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - return ( -
-
-
- {t('importTokensCamelCase')} -
-
- {t('likeToImportTokens')} -
-
-
-
-
-
{t('token')}
-
{t('balance')}
-
-
- {Object.entries(pendingTokens).map(([address, token]) => { - const { name, symbol } = token; - - return ( -
-
- -
- {getTokenName(name, symbol)} -
-
-
- -
-
- ); - })} -
-
-
-
-
- - -
-
-
- ); -}; - -export default ConfirmImportToken; diff --git a/ui/pages/confirm-import-token/confirm-import-token.stories.js b/ui/pages/confirm-import-token/confirm-import-token.stories.js deleted file mode 100644 index c9bdb091b..000000000 --- a/ui/pages/confirm-import-token/confirm-import-token.stories.js +++ /dev/null @@ -1,46 +0,0 @@ -/* eslint-disable react/prop-types */ -import React, { useEffect } from 'react'; - -import { store, getNewState } from '../../../.storybook/preview'; -import { tokens } from '../../../.storybook/initial-states/approval-screens/add-token'; -import { updateMetamaskState } from '../../store/actions'; -import ConfirmAddToken from '.'; - -export default { - title: 'Pages/ConfirmImportToken', - - argTypes: { - pendingTokens: { - control: 'object', - table: { category: 'Data' }, - }, - }, -}; - -const PageSet = ({ children, pendingTokens }) => { - const { metamask: state } = store.getState(); - - useEffect(() => { - store.dispatch( - updateMetamaskState( - getNewState(state, { - pendingTokens, - }), - ), - ); - }, [state, pendingTokens]); - - return children; -}; - -export const DefaultStory = ({ pendingTokens }) => { - return ( - - - - ); -}; -DefaultStory.args = { - pendingTokens: { ...tokens }, -}; -DefaultStory.storyName = 'Default'; diff --git a/ui/pages/confirm-import-token/confirm-import-token.test.js b/ui/pages/confirm-import-token/confirm-import-token.test.js deleted file mode 100644 index c756e8983..000000000 --- a/ui/pages/confirm-import-token/confirm-import-token.test.js +++ /dev/null @@ -1,128 +0,0 @@ -import React from 'react'; -import reactRouterDom from 'react-router-dom'; -import { fireEvent, screen } from '@testing-library/react'; -import { - ASSET_ROUTE, - IMPORT_TOKEN_ROUTE, -} from '../../helpers/constants/routes'; -import { addImportedTokens, clearPendingTokens } from '../../store/actions'; -import configureStore from '../../store/store'; -import { renderWithProvider } from '../../../test/jest'; -import ConfirmImportToken from '.'; - -const MOCK_PENDING_TOKENS = { - '0x6b175474e89094c44da98b954eedeac495271d0f': { - address: '0x6b175474e89094c44da98b954eedeac495271d0f', - symbol: 'META', - decimals: 18, - image: 'metamark.svg', - }, - '0xB8c77482e45F1F44dE1745F52C74426C631bDD52': { - address: '0xB8c77482e45F1F44dE1745F52C74426C631bDD52', - symbol: '0X', - decimals: 18, - image: '0x.svg', - }, -}; - -jest.mock('../../store/actions', () => ({ - addImportedTokens: jest.fn().mockReturnValue({ type: 'test' }), - clearPendingTokens: jest - .fn() - .mockReturnValue({ type: 'CLEAR_PENDING_TOKENS' }), -})); - -const renderComponent = (mockPendingTokens = MOCK_PENDING_TOKENS) => { - const store = configureStore({ - metamask: { - pendingTokens: { ...mockPendingTokens }, - providerConfig: { chainId: '0x1' }, - }, - history: { - mostRecentOverviewPage: '/', - }, - }); - - return renderWithProvider(, store); -}; - -describe('ConfirmImportToken Component', () => { - const mockHistoryPush = jest.fn(); - - beforeEach(() => { - jest - .spyOn(reactRouterDom, 'useHistory') - .mockImplementation() - .mockReturnValue({ push: mockHistoryPush }); - }); - - afterEach(() => { - jest.clearAllMocks(); - }); - - it('should render', () => { - renderComponent(); - - const [title, importTokensBtn] = screen.queryAllByText('Import tokens'); - - expect(title).toBeInTheDocument(title); - expect( - screen.getByText('Would you like to import these tokens?'), - ).toBeInTheDocument(); - expect(screen.getByText('Token')).toBeInTheDocument(); - expect(screen.getByText('Balance')).toBeInTheDocument(); - expect(screen.getByRole('button', { name: 'Back' })).toBeInTheDocument(); - expect(importTokensBtn).toBeInTheDocument(); - }); - - it('should render the list of tokens', () => { - renderComponent(); - - Object.values(MOCK_PENDING_TOKENS).forEach((token) => { - expect(screen.getByText(token.symbol)).toBeInTheDocument(); - }); - }); - - it('should go to "IMPORT_TOKEN_ROUTE" route when clicking the "Back" button', async () => { - renderComponent(); - - const backBtn = screen.getByRole('button', { name: 'Back' }); - - await fireEvent.click(backBtn); - expect(mockHistoryPush).toHaveBeenCalledTimes(1); - expect(mockHistoryPush).toHaveBeenCalledWith(IMPORT_TOKEN_ROUTE); - }); - - it('should dispatch clearPendingTokens and redirect to the first token page when clicking the "Import tokens" button', async () => { - const mockFirstPendingTokenAddress = - '0xe83cccfabd4ed148903bf36d4283ee7c8b3494d1'; - const mockPendingTokens = { - [mockFirstPendingTokenAddress]: { - address: mockFirstPendingTokenAddress, - symbol: 'CVL', - decimals: 18, - image: 'CVL_token.svg', - }, - '0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e': { - address: '0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e', - symbol: 'GLA', - decimals: 18, - image: 'gladius.svg', - }, - }; - renderComponent(mockPendingTokens); - - const importTokensBtn = screen.getByRole('button', { - name: 'Import tokens', - }); - - await fireEvent.click(importTokensBtn); - - expect(addImportedTokens).toHaveBeenCalled(); - expect(clearPendingTokens).toHaveBeenCalled(); - expect(mockHistoryPush).toHaveBeenCalledTimes(1); - expect(mockHistoryPush).toHaveBeenCalledWith( - `${ASSET_ROUTE}/${mockFirstPendingTokenAddress}`, - ); - }); -}); diff --git a/ui/pages/confirm-import-token/index.js b/ui/pages/confirm-import-token/index.js deleted file mode 100644 index 4443efa6b..000000000 --- a/ui/pages/confirm-import-token/index.js +++ /dev/null @@ -1,3 +0,0 @@ -import ConfirmImportToken from './confirm-import-token'; - -export default ConfirmImportToken; diff --git a/ui/pages/confirm-import-token/index.scss b/ui/pages/confirm-import-token/index.scss deleted file mode 100644 index 4dbc659ec..000000000 --- a/ui/pages/confirm-import-token/index.scss +++ /dev/null @@ -1,50 +0,0 @@ -.confirm-import-token { - padding: 16px; - - &__header { - @include H7; - - display: flex; - } - - &__token { - flex: 1; - min-width: 0; - } - - &__balance { - flex: 0 0 30%; - min-width: 0; - } - - &__token-list { - display: flex; - flex-flow: column nowrap; - } - - &__token-list-item { - display: flex; - flex-flow: row nowrap; - align-items: center; - margin-top: 8px; - box-sizing: border-box; - } - - &__data { - display: flex; - align-items: center; - padding: 8px; - } - - &__name { - min-width: 0; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - - &__token-icon { - margin-right: 12px; - flex: 0 0 auto; - } -} diff --git a/ui/pages/home/home.component.js b/ui/pages/home/home.component.js index e174fe2ee..09d0e4f62 100644 --- a/ui/pages/home/home.component.js +++ b/ui/pages/home/home.component.js @@ -684,7 +684,6 @@ export default class Home extends PureComponent {
- {this.renderNotifications()} diff --git a/ui/pages/import-token/README.mdx b/ui/pages/import-token/README.mdx deleted file mode 100644 index 040a0e925..000000000 --- a/ui/pages/import-token/README.mdx +++ /dev/null @@ -1,31 +0,0 @@ -import { Story, Canvas, ArgsTable } from '@storybook/addon-docs'; - -import ImportToken from './import-token.component'; - -import testData from '../../../.storybook/test-data'; -import configureStore from '../../store/store'; - -export const PersonalAddress = () => {configureStore(testData).getState().metamask.selectedAddress}; - -# ImportToken - -The `ImportToken` component allows a user to import custom tokens in one of two ways: - -1. By searching for one -2. By importing one by `Token Contract Address` - - - - - -## Example inputs - -An example input that works, to enable the `Add custom token` button is `0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA`. - -### Personal address error - -To show the personal address detected error, input the address in the `Token Contract Address` field. - -## Props - - diff --git a/ui/pages/import-token/import-token.component.js b/ui/pages/import-token/import-token.component.js deleted file mode 100644 index 20a00d1aa..000000000 --- a/ui/pages/import-token/import-token.component.js +++ /dev/null @@ -1,659 +0,0 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { getTokenTrackerLink } from '@metamask/etherscan-link'; -import ZENDESK_URLS from '../../helpers/constants/zendesk-url'; -import { - checkExistingAddresses, - getURLHostName, -} from '../../helpers/utils/util'; -import { tokenInfoGetter } from '../../helpers/utils/token-util'; -import { - CONFIRM_IMPORT_TOKEN_ROUTE, - SECURITY_ROUTE, -} from '../../helpers/constants/routes'; -import TextField from '../../components/ui/text-field'; -import PageContainer from '../../components/ui/page-container'; -import { Tabs, Tab } from '../../components/ui/tabs'; -import { addHexPrefix } from '../../../app/scripts/lib/util'; -import { isValidHexAddress } from '../../../shared/modules/hexstring-utils'; -import ActionableMessage from '../../components/ui/actionable-message/actionable-message'; -import Typography from '../../components/ui/typography'; -import { - TypographyVariant, - FONT_WEIGHT, -} from '../../helpers/constants/design-system'; -import Button from '../../components/ui/button'; -import { TokenStandard } from '../../../shared/constants/transaction'; -import { STATIC_MAINNET_TOKEN_LIST } from '../../../shared/constants/tokens'; -import TokenSearch from './token-search'; -import TokenList from './token-list'; - -const emptyAddr = '0x0000000000000000000000000000000000000000'; - -const MIN_DECIMAL_VALUE = 0; -const MAX_DECIMAL_VALUE = 36; - -class ImportToken extends Component { - static contextTypes = { - t: PropTypes.func, - }; - - static propTypes = { - /** - * History object of the router. - */ - history: PropTypes.object, - - /** - * Set the state of `pendingTokens`, called when adding a token. - */ - setPendingTokens: PropTypes.func, - - /** - * The current list of pending tokens to be added. - */ - pendingTokens: PropTypes.object, - - /** - * Clear the list of pending tokens. Called when closing the modal. - */ - clearPendingTokens: PropTypes.func, - - /** - * Clear the list of pending tokens. Called when closing the modal. - */ - showImportNftsModal: PropTypes.func, - - /** - * The list of already added tokens. - */ - tokens: PropTypes.array, - - /** - * The identities/accounts that are currently added to the wallet. - */ - identities: PropTypes.object, - - /** - * Boolean flag that shows/hides the search tab. - */ - showSearchTab: PropTypes.bool.isRequired, - - /** - * The most recent overview page route, which is 'navigated' to when closing the modal. - */ - mostRecentOverviewPage: PropTypes.string.isRequired, - - /** - * The active chainId in use. - */ - chainId: PropTypes.string, - - /** - * The rpc preferences to use for the current provider. - */ - rpcPrefs: PropTypes.object, - - /** - * The list of tokens available for search. - */ - tokenList: PropTypes.object, - - /** - * Boolean flag indicating whether token detection is enabled or not. - * When disabled, shows an information alert in the search tab informing the - * user of the availability of this feature. - */ - useTokenDetection: PropTypes.bool, - - /** - * Function called to fetch information about the token standard and - * details, see `actions.js`. - */ - getTokenStandardAndDetails: PropTypes.func, - - /** - * The currently selected active address. - */ - selectedAddress: PropTypes.string, - isDynamicTokenListAvailable: PropTypes.bool.isRequired, - tokenDetectionInactiveOnNonMainnetSupportedNetwork: - PropTypes.bool.isRequired, - networkName: PropTypes.string.isRequired, - }; - - static defaultProps = { - tokenList: {}, - }; - - state = { - customAddress: '', - customSymbol: '', - customDecimals: 0, - searchResults: [], - selectedTokens: {}, - standard: TokenStandard.NONE, - tokenSelectorError: null, - customAddressError: null, - customSymbolError: null, - customDecimalsError: null, - nftAddressError: null, - forceEditSymbol: false, - symbolAutoFilled: false, - decimalAutoFilled: false, - mainnetTokenWarning: null, - }; - - componentDidMount() { - this.tokenInfoGetter = tokenInfoGetter(); - const { pendingTokens = {} } = this.props; - const pendingTokenKeys = Object.keys(pendingTokens); - - if (pendingTokenKeys.length > 0) { - let selectedTokens = {}; - let customToken = {}; - - pendingTokenKeys.forEach((tokenAddress) => { - const token = pendingTokens[tokenAddress]; - const { isCustom } = token; - - if (isCustom) { - customToken = { ...token }; - } else { - selectedTokens = { ...selectedTokens, [tokenAddress]: { ...token } }; - } - }); - - const { - address: customAddress = '', - symbol: customSymbol = '', - decimals: customDecimals = 0, - } = customToken; - - this.setState({ - selectedTokens, - customAddress, - customSymbol, - customDecimals, - }); - } - } - - handleToggleToken(token) { - const { address } = token; - const { selectedTokens = {} } = this.state; - const selectedTokensCopy = { ...selectedTokens }; - - if (address in selectedTokensCopy) { - delete selectedTokensCopy[address]; - } else { - selectedTokensCopy[address] = token; - } - - this.setState({ - selectedTokens: selectedTokensCopy, - tokenSelectorError: null, - }); - } - - hasError() { - const { - tokenSelectorError, - customAddressError, - customSymbolError, - customDecimalsError, - nftAddressError, - } = this.state; - - return ( - tokenSelectorError || - customAddressError || - customSymbolError || - customDecimalsError || - nftAddressError - ); - } - - hasSelected() { - const { customAddress = '', selectedTokens = {} } = this.state; - return customAddress || Object.keys(selectedTokens).length > 0; - } - - handleNext() { - if (this.hasError()) { - return; - } - - if (!this.hasSelected()) { - this.setState({ tokenSelectorError: this.context.t('mustSelectOne') }); - return; - } - - const { setPendingTokens, history, tokenList } = this.props; - const tokenAddressList = Object.keys(tokenList); - const { - customAddress: address, - customSymbol: symbol, - customDecimals: decimals, - selectedTokens, - standard, - } = this.state; - - const customToken = { - address, - symbol, - decimals, - standard, - }; - - setPendingTokens({ customToken, selectedTokens, tokenAddressList }); - history.push(CONFIRM_IMPORT_TOKEN_ROUTE); - } - - async attemptToAutoFillTokenParams(address) { - const { tokenList } = this.props; - const { symbol = '', decimals } = await this.tokenInfoGetter( - address, - tokenList, - ); - - const symbolAutoFilled = Boolean(symbol); - const decimalAutoFilled = Boolean(decimals); - this.setState({ symbolAutoFilled, decimalAutoFilled }); - this.handleCustomSymbolChange(symbol || ''); - this.handleCustomDecimalsChange(decimals); - } - - async handleCustomAddressChange(value) { - const customAddress = value.trim(); - this.setState({ - customAddress, - customAddressError: null, - nftAddressError: null, - tokenSelectorError: null, - symbolAutoFilled: false, - decimalAutoFilled: false, - mainnetTokenWarning: null, - }); - - const addressIsValid = isValidHexAddress(customAddress, { - allowNonPrefixed: false, - }); - const standardAddress = addHexPrefix(customAddress).toLowerCase(); - - const isMainnetToken = Object.keys(STATIC_MAINNET_TOKEN_LIST).some( - (key) => key.toLowerCase() === customAddress.toLowerCase(), - ); - - const isMainnetNetwork = this.props.chainId === '0x1'; - - let standard; - if (addressIsValid) { - try { - ({ standard } = await this.props.getTokenStandardAndDetails( - standardAddress, - this.props.selectedAddress, - )); - } catch (error) { - // ignore - } - } - - const addressIsEmpty = - customAddress.length === 0 || customAddress === emptyAddr; - - switch (true) { - case !addressIsValid && !addressIsEmpty: - this.setState({ - customAddressError: this.context.t('invalidAddress'), - customSymbol: '', - customDecimals: 0, - customSymbolError: null, - customDecimalsError: null, - }); - - break; - case standard === 'ERC1155' || standard === 'ERC721': - this.setState({ - nftAddressError: this.context.t('nftAddressError', [ - { - this.props.showImportNftsModal(); - }} - key="nftAddressError" - > - {this.context.t('importNFTPage')} - , - ]), - }); - break; - case isMainnetToken && !isMainnetNetwork: - this.setState({ - mainnetTokenWarning: this.context.t('mainnetToken'), - customSymbol: '', - customDecimals: 0, - customSymbolError: null, - customDecimalsError: null, - }); - - break; - case Boolean(this.props.identities[standardAddress]): - this.setState({ - customAddressError: this.context.t('personalAddressDetected'), - }); - - break; - case checkExistingAddresses(customAddress, this.props.tokens): - this.setState({ - customAddressError: this.context.t('tokenAlreadyAdded'), - }); - - break; - default: - if (!addressIsEmpty) { - this.attemptToAutoFillTokenParams(customAddress); - if (standard) { - this.setState({ standard }); - } - } - } - } - - handleCustomSymbolChange(value) { - const customSymbol = value.trim(); - const symbolLength = customSymbol.length; - let customSymbolError = null; - - if (symbolLength <= 0 || symbolLength >= 12) { - customSymbolError = this.context.t('symbolBetweenZeroTwelve'); - } - - this.setState({ customSymbol, customSymbolError }); - } - - handleCustomDecimalsChange(value) { - let customDecimals; - let customDecimalsError = null; - - if (value) { - customDecimals = Number(value.trim()); - customDecimalsError = - value < MIN_DECIMAL_VALUE || value > MAX_DECIMAL_VALUE - ? this.context.t('decimalsMustZerotoTen') - : null; - } else { - customDecimals = ''; - customDecimalsError = this.context.t('tokenDecimalFetchFailed'); - } - - this.setState({ customDecimals, customDecimalsError }); - } - - renderCustomTokenForm() { - const { t } = this.context; - const { - customAddress, - customSymbol, - customDecimals, - customAddressError, - customSymbolError, - customDecimalsError, - forceEditSymbol, - symbolAutoFilled, - decimalAutoFilled, - mainnetTokenWarning, - nftAddressError, - } = this.state; - - const { - chainId, - rpcPrefs, - isDynamicTokenListAvailable, - tokenDetectionInactiveOnNonMainnetSupportedNetwork, - history, - } = this.props; - const blockExplorerTokenLink = getTokenTrackerLink( - customAddress, - chainId, - null, - null, - { blockExplorerUrl: rpcPrefs?.blockExplorerUrl ?? null }, - ); - const blockExplorerLabel = rpcPrefs?.blockExplorerUrl - ? getURLHostName(blockExplorerTokenLink) - : t('etherscan'); - - return ( -
- {tokenDetectionInactiveOnNonMainnetSupportedNetwork ? ( - - {t('tokenScamSecurityRisk')} - , - , - ])} - withRightButton - useIcon - iconFillColor="var(--color-warning-default)" - /> - ) : ( - - {t('learnScamRisk')} - , - ], - )} - withRightButton - useIcon - iconFillColor={ - isDynamicTokenListAvailable - ? 'var(--color-warning-default)' - : 'var(--color-info-default)' - } - /> - )} - this.handleCustomAddressChange(e.target.value)} - error={customAddressError || mainnetTokenWarning || nftAddressError} - fullWidth - autoFocus - margin="normal" - /> - - - {t('tokenSymbol')} - - {symbolAutoFilled && !forceEditSymbol && ( -
this.setState({ forceEditSymbol: true })} - > - {t('edit')} -
- )} -
- } - type="text" - value={customSymbol} - onChange={(e) => this.handleCustomSymbolChange(e.target.value)} - error={customSymbolError} - fullWidth - margin="normal" - disabled={symbolAutoFilled && !forceEditSymbol} - /> - this.handleCustomDecimalsChange(e.target.value)} - error={customDecimals ? customDecimalsError : null} - fullWidth - margin="normal" - disabled={decimalAutoFilled} - min={MIN_DECIMAL_VALUE} - max={MAX_DECIMAL_VALUE} - /> - {customDecimals === '' && ( - - - {t('tokenDecimalFetchFailed')} - - - {t('verifyThisTokenDecimalOn', [ - , - ])} - - - } - type="warning" - withRightButton - className="import-token__decimal-warning" - /> - )} - - ); - } - - renderSearchToken() { - const { t } = this.context; - const { tokenList, history, useTokenDetection, networkName } = this.props; - const { tokenSelectorError, selectedTokens, searchResults } = this.state; - return ( -
- {!useTokenDetection && ( - - history.push(`${SECURITY_ROUTE}#token-description`) - } - > - {t('enableFromSettings')} - , - ])} - withRightButton - useIcon - iconFillColor="var(--color-primary-default)" - className="import-token__token-detection-announcement" - /> - )} - - this.setState({ searchResults: results }) - } - error={tokenSelectorError} - tokenList={tokenList} - /> -
- this.handleToggleToken(token)} - /> -
-
- ); - } - - renderTabs() { - const { t } = this.context; - const { showSearchTab } = this.props; - const tabs = []; - - if (showSearchTab) { - tabs.push( - - {this.renderSearchToken()} - , - ); - } - tabs.push( - - {this.renderCustomTokenForm()} - , - ); - - return {tabs}; - } - - render() { - const { history, clearPendingTokens, mostRecentOverviewPage } = this.props; - - return ( - this.handleNext()} - hideCancel - disabled={Boolean(this.hasError()) || !this.hasSelected()} - onClose={() => { - clearPendingTokens(); - history.push(mostRecentOverviewPage); - }} - /> - ); - } -} - -export default ImportToken; diff --git a/ui/pages/import-token/import-token.container.js b/ui/pages/import-token/import-token.container.js deleted file mode 100644 index 9d238d646..000000000 --- a/ui/pages/import-token/import-token.container.js +++ /dev/null @@ -1,68 +0,0 @@ -import { connect } from 'react-redux'; - -import { - setPendingTokens, - clearPendingTokens, - getTokenStandardAndDetails, - showImportNftsModal, -} from '../../store/actions'; -import { getMostRecentOverviewPage } from '../../ducks/history/history'; -import { getProviderConfig } from '../../ducks/metamask/metamask'; -import { - getRpcPrefsForCurrentProvider, - getIsTokenDetectionSupported, - getTokenDetectionSupportNetworkByChainId, - getIsTokenDetectionInactiveOnMainnet, - getIsDynamicTokenListAvailable, - getIstokenDetectionInactiveOnNonMainnetSupportedNetwork, - getTokenList, -} from '../../selectors/selectors'; -import ImportToken from './import-token.component'; - -const mapStateToProps = (state) => { - const { - metamask: { - identities, - tokens, - pendingTokens, - useTokenDetection, - selectedAddress, - }, - } = state; - const { chainId } = getProviderConfig(state); - - const isTokenDetectionInactiveOnMainnet = - getIsTokenDetectionInactiveOnMainnet(state); - const showSearchTab = - getIsTokenDetectionSupported(state) || - isTokenDetectionInactiveOnMainnet || - Boolean(process.env.IN_TEST); - - return { - identities, - mostRecentOverviewPage: getMostRecentOverviewPage(state), - tokens, - pendingTokens, - showSearchTab, - chainId, - rpcPrefs: getRpcPrefsForCurrentProvider(state), - tokenList: getTokenList(state), - useTokenDetection, - selectedAddress, - isDynamicTokenListAvailable: getIsDynamicTokenListAvailable(state), - networkName: getTokenDetectionSupportNetworkByChainId(state), - tokenDetectionInactiveOnNonMainnetSupportedNetwork: - getIstokenDetectionInactiveOnNonMainnetSupportedNetwork(state), - }; -}; -const mapDispatchToProps = (dispatch) => { - return { - setPendingTokens: (tokens) => dispatch(setPendingTokens(tokens)), - clearPendingTokens: () => dispatch(clearPendingTokens()), - showImportNftsModal: () => dispatch(showImportNftsModal()), - getTokenStandardAndDetails: (address, selectedAddress) => - getTokenStandardAndDetails(address, selectedAddress, null), - }; -}; - -export default connect(mapStateToProps, mapDispatchToProps)(ImportToken); diff --git a/ui/pages/import-token/import-token.stories.js b/ui/pages/import-token/import-token.stories.js deleted file mode 100644 index b5368a430..000000000 --- a/ui/pages/import-token/import-token.stories.js +++ /dev/null @@ -1,120 +0,0 @@ -import React from 'react'; - -import { Provider } from 'react-redux'; -import { action } from '@storybook/addon-actions'; -import { DEFAULT_ROUTE } from '../../helpers/constants/routes'; -import configureStore from '../../store/store'; -import testData from '../../../.storybook/test-data'; -import ImportToken from './import-token.component'; -import README from './README.mdx'; - -const store = configureStore(testData); -const { metamask } = store.getState(); - -const { - networkConfigurations, - identities, - pendingTokens, - selectedAddress, - tokenList, - tokens, -} = metamask; - -export default { - title: 'Pages/ImportToken', - - decorators: [(story) => {story()}], - component: ImportToken, - parameters: { - docs: { - page: README, - }, - }, - argTypes: { - history: { - control: { - type: 'object', - }, - }, - setPendingTokens: { - action: 'setPendingTokens', - }, - pendingTokens: { - control: { - type: 'object', - }, - }, - clearPendingTokens: { - action: 'clearPendingTokens', - }, - tokens: { - control: { - type: 'object', - }, - }, - identities: { - control: { - type: 'object', - }, - }, - showSearchTab: { - control: { - type: 'boolean', - }, - }, - mostRecentOverviewPage: { - control: { - type: 'text', - }, - }, - chainId: { - control: { - type: 'text', - }, - }, - rpcPrefs: { - control: { - type: 'object', - }, - }, - tokenList: { - control: { - type: 'object', - }, - }, - useTokenDetection: { - control: { - type: 'boolean', - }, - }, - getTokenStandardAndDetails: { - action: 'getTokenStandardAndDetails', - }, - selectedAddress: { - control: { - type: 'text', - }, - }, - }, - args: { - history: { - push: action('history.push()'), - }, - pendingTokens, - tokens, - identities, - showSearchTab: true, - mostRecentOverviewPage: DEFAULT_ROUTE, - chainId: networkConfigurations['test-networkConfigurationId-1'].chainId, - rpcPrefs: networkConfigurations['test-networkConfigurationId-1'].rpcPrefs, - tokenList, - useTokenDetection: false, - selectedAddress, - }, -}; - -export const DefaultStory = (args) => { - return ; -}; - -DefaultStory.storyName = 'Default'; diff --git a/ui/pages/import-token/import-token.test.js b/ui/pages/import-token/import-token.test.js deleted file mode 100644 index b2fb6b5a2..000000000 --- a/ui/pages/import-token/import-token.test.js +++ /dev/null @@ -1,178 +0,0 @@ -import React from 'react'; -import { fireEvent } from '@testing-library/react'; -import { renderWithProvider } from '../../../test/lib/render-helpers'; -import configureStore from '../../store/store'; -import { - setPendingTokens, - clearPendingTokens, - getTokenStandardAndDetails, -} from '../../store/actions'; -import ImportToken from './import-token.container'; - -jest.mock('../../store/actions', () => ({ - getTokenStandardAndDetails: jest - .fn() - .mockImplementation(() => Promise.resolve({ standard: 'ERC20' })), - setPendingTokens: jest - .fn() - .mockImplementation(() => ({ type: 'SET_PENDING_TOKENS' })), - clearPendingTokens: jest - .fn() - .mockImplementation(() => ({ type: 'CLEAR_PENDING_TOKENS' })), -})); - -describe('Import Token', () => { - const historyStub = jest.fn(); - const props = { - history: { - push: historyStub, - }, - showSearchTab: true, - tokenList: {}, - }; - - const render = () => { - const baseStore = { - metamask: { - tokens: [], - providerConfig: { chainId: '0x1' }, - networkConfigurations: {}, - identities: {}, - selectedAddress: '0x1231231', - useTokenDetection: true, - }, - history: { - mostRecentOverviewPage: '/', - }, - }; - - const store = configureStore(baseStore); - - return renderWithProvider(, store); - }; - - describe('Import Token', () => { - it('add custom token button is disabled when no fields are populated', () => { - const { getByText } = render(); - const customTokenButton = getByText('Custom token'); - fireEvent.click(customTokenButton); - const submit = getByText('Add custom token'); - - expect(submit).toBeDisabled(); - }); - - it('edits token address', () => { - const { getByText } = render(); - const customTokenButton = getByText('Custom token'); - fireEvent.click(customTokenButton); - - const tokenAddress = '0x617b3f8050a0BD94b6b1da02B4384eE5B4DF13F4'; - const event = { target: { value: tokenAddress } }; - fireEvent.change(document.getElementById('custom-address'), event); - - expect(document.getElementById('custom-address').value).toStrictEqual( - tokenAddress, - ); - }); - - it('edits token symbol', () => { - const { getByText } = render(); - const customTokenButton = getByText('Custom token'); - fireEvent.click(customTokenButton); - - const tokenSymbol = 'META'; - const event = { target: { value: tokenSymbol } }; - fireEvent.change(document.getElementById('custom-symbol'), event); - - expect(document.getElementById('custom-symbol').value).toStrictEqual( - tokenSymbol, - ); - }); - - it('edits token decimal precision', () => { - const { getByText } = render(); - const customTokenButton = getByText('Custom token'); - fireEvent.click(customTokenButton); - - const tokenPrecision = '2'; - const event = { target: { value: tokenPrecision } }; - fireEvent.change(document.getElementById('custom-decimals'), event); - - expect(document.getElementById('custom-decimals').value).toStrictEqual( - tokenPrecision, - ); - }); - - it('adds custom tokens successfully', async () => { - const { getByText } = render(); - const customTokenButton = getByText('Custom token'); - fireEvent.click(customTokenButton); - - const submit = getByText('Add custom token'); - expect(submit).toBeDisabled(); - - const tokenAddress = '0x617b3f8050a0BD94b6b1da02B4384eE5B4DF13F4'; - fireEvent.change(document.getElementById('custom-address'), { - target: { value: tokenAddress }, - }); - expect(submit).not.toBeDisabled(); - - const tokenSymbol = 'META'; - fireEvent.change(document.getElementById('custom-symbol'), { - target: { value: tokenSymbol }, - }); - - const tokenPrecision = '2'; - await fireEvent.change(document.getElementById('custom-decimals'), { - target: { value: tokenPrecision }, - }); - - expect(submit).not.toBeDisabled(); - fireEvent.click(submit); - expect(setPendingTokens).toHaveBeenCalledWith({ - customToken: { - address: tokenAddress, - decimals: Number(tokenPrecision), - standard: 'ERC20', - symbol: tokenSymbol, - }, - selectedTokens: {}, - tokenAddressList: [], - }); - expect(historyStub).toHaveBeenCalledWith('/confirm-import-token'); - }); - - it('cancels out of import token flow', () => { - const { getByRole } = render(); - const closeButton = getByRole('button', { name: 'close' }); - fireEvent.click(closeButton); - - expect(clearPendingTokens).toHaveBeenCalled(); - expect(historyStub).toHaveBeenCalledWith('/'); - }); - - it('sets and error when a token is an NFT', async () => { - getTokenStandardAndDetails.mockImplementation(() => - Promise.resolve({ standard: 'ERC721' }), - ); - - const { getByText } = render(); - const customTokenButton = getByText('Custom token'); - fireEvent.click(customTokenButton); - - const submit = getByText('Add custom token'); - expect(submit).toBeDisabled(); - - const tokenAddress = '0x617b3f8050a0BD94b6b1da02B4384eE5B4DF13F4'; - await fireEvent.change(document.getElementById('custom-address'), { - target: { value: tokenAddress }, - }); - - expect(submit).toBeDisabled(); - - // The last part of this error message won't be found by getByText because it is wrapped as a link. - const errorMessage = getByText('This token is an NFT. Add on the'); - expect(errorMessage).toBeInTheDocument(); - }); - }); -}); diff --git a/ui/pages/import-token/index.js b/ui/pages/import-token/index.js deleted file mode 100644 index 8fa4ee8c8..000000000 --- a/ui/pages/import-token/index.js +++ /dev/null @@ -1,3 +0,0 @@ -import ImportToken from './import-token.container'; - -export default ImportToken; diff --git a/ui/pages/import-token/index.scss b/ui/pages/import-token/index.scss deleted file mode 100644 index be2cc1cc5..000000000 --- a/ui/pages/import-token/index.scss +++ /dev/null @@ -1,79 +0,0 @@ -@import 'token-list/index'; - -.import-token { - $self: &; - - &__custom-token-form { - padding: 8px 16px 16px; - - input[type='number']::-webkit-inner-spin-button { - -webkit-appearance: none; - display: none; - } - - input[type='number']:hover::-webkit-inner-spin-button { - -webkit-appearance: none; - display: none; - } - & #{$self}__decimal-warning { - margin-top: 5px; - } - } - - &__search-token { - padding: 16px 16px 16px 16px; - } - - &__token-list { - margin-top: 16px; - } - - &__custom-symbol { - &__label-wrapper { - display: flex; - flex-flow: row nowrap; - } - - &__label { - flex: 0 0 auto; - } - - &__edit { - flex: 1 1 auto; - text-align: right; - color: var(--color-primary-default); - padding-right: 4px; - cursor: pointer; - } - } - - &__link { - @include H7; - - display: inline; - color: var(--color-primary-default); - padding-left: 0; - } - - &__token-detection-announcement { - margin-bottom: 16px; - margin-top: 0; - } - - &__close { - color: var(--color-icon-default); - background: none; - flex: 0; - align-self: flex-start; - padding-right: 0; - } - - &__nft-address-error-link { - color: var(--color-primary-default); - cursor: pointer; - - &:hover { - color: var(--color-primary-default); - } - } -} diff --git a/ui/pages/import-token/token-list/token-list-placeholder/index.scss b/ui/pages/import-token/token-list/token-list-placeholder/index.scss deleted file mode 100644 index e97741621..000000000 --- a/ui/pages/import-token/token-list/token-list-placeholder/index.scss +++ /dev/null @@ -1,27 +0,0 @@ -.token-list-placeholder { - display: flex; - align-items: center; - padding-top: 36px; - flex-direction: column; - line-height: 22px; - - img { - opacity: 0.5; - } - - &__text { - color: var(--color-text-alternative); - width: 50%; - text-align: center; - margin-top: 8px; - opacity: 0.5; - - @include screen-sm-max { - width: 60%; - } - } - - &__link { - margin-top: 0.5rem; - } -} diff --git a/ui/pages/import-token/token-list/token-list-placeholder/token-list-placeholder.component.js b/ui/pages/import-token/token-list/token-list-placeholder/token-list-placeholder.component.js deleted file mode 100644 index ffc13bbec..000000000 --- a/ui/pages/import-token/token-list/token-list-placeholder/token-list-placeholder.component.js +++ /dev/null @@ -1,31 +0,0 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import Button from '../../../../components/ui/button'; -import IconTokenSearch from '../../../../components/ui/icon/icon-token-search'; -import ZENDESK_URLS from '../../../../helpers/constants/zendesk-url'; - -export default class TokenListPlaceholder extends Component { - static contextTypes = { - t: PropTypes.func, - }; - - render() { - return ( -
- -
- {this.context.t('addAcquiredTokens')} -
- -
- ); - } -} diff --git a/ui/pages/pages.scss b/ui/pages/pages.scss index 72c4b2b49..96f1f29c8 100644 --- a/ui/pages/pages.scss +++ b/ui/pages/pages.scss @@ -1,6 +1,5 @@ /** Please import your files in alphabetical order **/ @import 'asset/asset'; -@import 'confirm-import-token/index'; @import 'confirm-add-suggested-token/index'; @import 'confirm-add-suggested-nft/index'; @import 'confirm-approve/index'; @@ -15,7 +14,6 @@ @import 'desktop-pairing/index'; @import 'error/index'; @import 'home/index'; -@import 'import-token/index'; ///: BEGIN:ONLY_INCLUDE_IN(build-mmi) @import "institutional/connect-custody/index"; @import "institutional/institutional-entity-done-page/index"; diff --git a/ui/pages/routes/routes.component.js b/ui/pages/routes/routes.component.js index a25eedf49..03b39eefe 100644 --- a/ui/pages/routes/routes.component.js +++ b/ui/pages/routes/routes.component.js @@ -7,6 +7,7 @@ import IdleTimer from 'react-idle-timer'; ///: BEGIN:ONLY_INCLUDE_IN(desktop) import browserAPI from 'webextension-polyfill'; ///: END:ONLY_INCLUDE_IN + import SendTransactionScreen from '../send'; import Swaps from '../swaps'; import ConfirmTransaction from '../confirm-transaction'; @@ -18,8 +19,6 @@ import Lock from '../lock'; import PermissionsConnect from '../permissions-connect'; import RestoreVaultPage from '../keychains/restore-vault'; import RevealSeedConfirmation from '../keychains/reveal-seed'; -import ImportTokenPage from '../import-token'; -import ConfirmImportTokenPage from '../confirm-import-token'; import ConfirmAddSuggestedTokenPage from '../confirm-add-suggested-token'; import CreateAccountPage from '../create-account/create-account.component'; import ConfirmAddSuggestedNftPage from '../confirm-add-suggested-nft'; @@ -33,6 +32,7 @@ import { NetworkListMenu, AccountDetails, ImportNftsModal, + ImportTokensModal, } from '../../components/multichain'; import UnlockPage from '../unlock-page'; import Alerts from '../../components/app/alerts'; @@ -59,7 +59,6 @@ import CustodyPage from '../institutional/custody'; ///: END:ONLY_INCLUDE_IN import { - IMPORT_TOKEN_ROUTE, ASSET_ROUTE, CONFIRM_ADD_SUGGESTED_TOKEN_ROUTE, CONFIRM_ADD_SUGGESTED_NFT_ROUTE, @@ -76,7 +75,6 @@ import { UNLOCK_ROUTE, BUILD_QUOTE_ROUTE, CONFIRMATION_V_NEXT_ROUTE, - CONFIRM_IMPORT_TOKEN_ROUTE, ONBOARDING_ROUTE, ONBOARDING_UNLOCK_ROUTE, TOKEN_DETAILS, @@ -161,6 +159,8 @@ export default class Routes extends Component { hideImportNftsModal: PropTypes.func.isRequired, isIpfsModalOpen: PropTypes.bool.isRequired, hideIpfsModal: PropTypes.func.isRequired, + isImportTokensModalOpen: PropTypes.bool.isRequired, + hideImportTokensModal: PropTypes.func.isRequired, }; static contextTypes = { @@ -278,16 +278,6 @@ export default class Routes extends Component { exact /> - - hideIpfsModal()} /> ) : null} + {isImportTokensModalOpen ? ( + hideImportTokensModal()} /> + ) : null} {isLoading ? : null} {!isLoading && isNetworkLoading ? : null} diff --git a/ui/pages/routes/routes.container.js b/ui/pages/routes/routes.container.js index 918c80a9a..c90de1b93 100644 --- a/ui/pages/routes/routes.container.js +++ b/ui/pages/routes/routes.container.js @@ -13,6 +13,7 @@ import { isCurrentProviderCustom, } from '../../selectors'; import { + hideImportTokensModal, lockMetamask, hideImportNftsModal, hideIpfsModal, @@ -62,6 +63,7 @@ function mapStateToProps(state) { completedOnboarding, isAccountMenuOpen: state.metamask.isAccountMenuOpen, isNetworkMenuOpen: state.metamask.isNetworkMenuOpen, + isImportTokensModalOpen: state.appState.importTokensModalOpen, accountDetailsAddress: state.appState.accountDetailsAddress, isImportNftsModalOpen: state.appState.importNftsModalOpen, isIpfsModalOpen: state.appState.showIpfsModalOpen, @@ -81,6 +83,7 @@ function mapDispatchToProps(dispatch) { toggleNetworkMenu: () => dispatch(toggleNetworkMenu()), hideImportNftsModal: () => dispatch(hideImportNftsModal()), hideIpfsModal: () => dispatch(hideIpfsModal()), + hideImportTokensModal: () => dispatch(hideImportTokensModal()), }; } diff --git a/ui/pages/settings/security-tab/__snapshots__/security-tab.test.js.snap b/ui/pages/settings/security-tab/__snapshots__/security-tab.test.js.snap index f9e256234..1190abd2a 100644 --- a/ui/pages/settings/security-tab/__snapshots__/security-tab.test.js.snap +++ b/ui/pages/settings/security-tab/__snapshots__/security-tab.test.js.snap @@ -627,6 +627,7 @@ exports[`Security Tab should match snapshot 1`] = `
{t('autoDetectTokens')} diff --git a/ui/selectors/selectors.js b/ui/selectors/selectors.js index 973040e1c..439510f21 100644 --- a/ui/selectors/selectors.js +++ b/ui/selectors/selectors.js @@ -1307,11 +1307,22 @@ export function getIsOptimism(state) { ); } +export function getIsBase(state) { + return ( + getCurrentChainId(state) === CHAIN_IDS.BASE || + getCurrentChainId(state) === CHAIN_IDS.BASE_TESTNET + ); +} + +export function getIsOpStack(state) { + return getIsOptimism(state) || getIsBase(state); +} + export function getIsMultiLayerFeeNetwork(state) { - return getIsOptimism(state); + return getIsOpStack(state); } /** - * To retrieve the maxBaseFee and priotitFee teh user has set as default + * To retrieve the maxBaseFee and priorityFee the user has set as default * * @param {*} state * @returns Boolean diff --git a/ui/store/actionConstants.ts b/ui/store/actionConstants.ts index 3cbc24497..0a48192b5 100644 --- a/ui/store/actionConstants.ts +++ b/ui/store/actionConstants.ts @@ -13,6 +13,8 @@ export const IMPORT_NFTS_MODAL_OPEN = 'UI_IMPORT_NFTS_MODAL_OPEN'; export const IMPORT_NFTS_MODAL_CLOSE = 'UI_IMPORT_NFTS_MODAL_CLOSE'; export const SHOW_IPFS_MODAL_OPEN = 'UI_IPFS_MODAL_OPEN'; export const SHOW_IPFS_MODAL_CLOSE = 'UI_IPFS_MODAL_CLOSE'; +export const IMPORT_TOKENS_POPOVER_OPEN = 'UI_IMPORT_TOKENS_POPOVER_OPEN'; +export const IMPORT_TOKENS_POPOVER_CLOSE = 'UI_IMPORT_TOKENS_POPOVER_CLOSE'; // remote state export const UPDATE_METAMASK_STATE = 'UPDATE_METAMASK_STATE'; export const SELECTED_ADDRESS_CHANGED = 'SELECTED_ADDRESS_CHANGED'; diff --git a/ui/store/actions.ts b/ui/store/actions.ts index b7751a7ed..6d92e5b1b 100644 --- a/ui/store/actions.ts +++ b/ui/store/actions.ts @@ -2381,6 +2381,18 @@ export function hideNetworkDropdown() { }; } +export function showImportTokensModal(): Action { + return { + type: actionConstants.IMPORT_TOKENS_POPOVER_OPEN, + }; +} + +export function hideImportTokensModal(): Action { + return { + type: actionConstants.IMPORT_TOKENS_POPOVER_CLOSE, + }; +} + type ModalPayload = { name: string } & Record; export function showModal(payload: ModalPayload): PayloadAction { diff --git a/yarn.lock b/yarn.lock index 23b862842..f9a0a9995 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1619,10 +1619,10 @@ __metadata: languageName: node linkType: hard -"@blockaid/ppom@npm:^0.1.2": - version: 0.1.2 - resolution: "@blockaid/ppom@npm:0.1.2" - checksum: ff5485db12a11a0b60465faf27b22e365c5a6fdc9b08d932e8a50a50effe1d07059c0e9d3e76f8bf051fbe32f473f6fdac57c5993f6633d2668d3760aa53223b +"@blockaid/ppom@npm:^1.0.2": + version: 1.0.2 + resolution: "@blockaid/ppom@npm:1.0.2" + checksum: d288117f19400108b0177df63d6accc3e06a60f203d36b9e3b1d8cabc55e167674cb000d1fe50971a7fe73662ca55659211682a0d20e676c66ccba112043d9fb languageName: node linkType: hard @@ -1864,6 +1864,13 @@ __metadata: languageName: node linkType: hard +"@endo/env-options@npm:^0.1.3": + version: 0.1.3 + resolution: "@endo/env-options@npm:0.1.3" + checksum: dbaa1c6edf2de712b0af76c991f2ffd29e52a27d3adca2292531dbbf42ef0541c03daa4d7f2de3efb223d0cacf303c82fe88c19d44a0f5535dce9ccbe5f66138 + languageName: node + linkType: hard + "@ensdomains/address-encoder@npm:^0.1.7": version: 0.1.9 resolution: "@ensdomains/address-encoder@npm:0.1.9" @@ -4364,9 +4371,9 @@ __metadata: languageName: node linkType: hard -"@metamask/message-manager@npm:^7.0.2, @metamask/message-manager@npm:^7.2.0": - version: 7.2.0 - resolution: "@metamask/message-manager@npm:7.2.0" +"@metamask/message-manager@npm:^7.2.0, @metamask/message-manager@npm:^7.3.0": + version: 7.3.0 + resolution: "@metamask/message-manager@npm:7.3.0" dependencies: "@metamask/base-controller": "npm:^3.2.0" "@metamask/controller-utils": "npm:^4.3.0" @@ -4376,7 +4383,7 @@ __metadata: ethereumjs-util: "npm:^7.0.10" jsonschema: "npm:^1.2.4" uuid: "npm:^8.3.2" - checksum: 2129b2418b8a45234e4e00c1f3aabd386e2b6d5bba7db04b1a19fcd8d4930c8c10de6660a272bab227e9bfed684dc6a37cef26403471ffd5c6fe71921e06e576 + checksum: 2001622731a01a777270d9d4c5f1c7897aa3071bcbc6eb6ccbac819574d0c4786a070ef519fcf11aaa950cedd5a1feba3e1cc55b8641c2a87cb791aab5120903 languageName: node linkType: hard @@ -4566,14 +4573,16 @@ __metadata: languageName: node linkType: hard -"@metamask/ppom-validator@npm:^0.1.2": - version: 0.1.2 - resolution: "@metamask/ppom-validator@npm:0.1.2" +"@metamask/ppom-validator@npm:^0.2.0": + version: 0.2.0 + resolution: "@metamask/ppom-validator@npm:0.2.0" dependencies: "@metamask/base-controller": "npm:^3.0.0" "@metamask/controller-utils": "npm:^4.0.0" await-semaphore: "npm:^0.1.3" - checksum: 1a034882e7040fe66061c956f3736bc6cf164212cc07c9bf520dd64f9df49b6ef51554c1d00ec832e723293d4287e37c1d9ca00112e185bba58aedd6205e27c3 + elliptic: "npm:^6.5.4" + json-rpc-random-id: "npm:^1.0.1" + checksum: c86e3d2aa8321347ffaf45290d9c8b0d4f3a594c88dc2090933b6159a604c2f574360462faba7cbda7fa1f32f730d159f90847bdd47badb0dc0cd027be0db9e8 languageName: node linkType: hard @@ -4682,22 +4691,22 @@ __metadata: languageName: node linkType: hard -"@metamask/rpc-methods@npm:^1.0.0-prerelease.1": - version: 1.0.0-prerelease.1 - resolution: "@metamask/rpc-methods@npm:1.0.0-prerelease.1" +"@metamask/rpc-methods@npm:^1.0.0": + version: 1.0.0 + resolution: "@metamask/rpc-methods@npm:1.0.0" dependencies: "@metamask/browser-passworder": "npm:^4.0.2" "@metamask/key-tree": "npm:^7.1.1" "@metamask/permission-controller": "npm:^4.0.0" - "@metamask/snaps-ui": "npm:^1.0.0-prerelease.1" - "@metamask/snaps-utils": "npm:^1.0.0-prerelease.1" + "@metamask/snaps-ui": "npm:^1.0.0" + "@metamask/snaps-utils": "npm:^1.0.0" "@metamask/types": "npm:^1.1.0" "@metamask/utils": "npm:^6.0.1" "@noble/hashes": "npm:^1.1.3" eth-rpc-errors: "npm:^4.0.2" nanoid: "npm:^3.1.31" superstruct: "npm:^1.0.3" - checksum: ea07ca26eb8b99be108e7d6f790d2ed2e74aa859f228c4e974410663d0a6ddcc4aa1aee87688b0491d70352c96a707931f8364ceddd088065ac04c12577b2b36 + checksum: 7e5f2900f9a54bcc112d9861eeb461de5a7803fdaa4e1bfee1c1c9f68a659dc42f56a7dbbc4f8147f66927c7192d1b5314cc32ca5d8985b969694582127b8fa8 languageName: node linkType: hard @@ -4847,19 +4856,19 @@ __metadata: languageName: node linkType: hard -"@metamask/snaps-controllers@npm:^1.0.0-prerelease.1": - version: 1.0.0-prerelease.1 - resolution: "@metamask/snaps-controllers@npm:1.0.0-prerelease.1" +"@metamask/snaps-controllers@npm:^1.0.0": + version: 1.0.0 + resolution: "@metamask/snaps-controllers@npm:1.0.0" dependencies: "@metamask/approval-controller": "npm:^3.0.0" "@metamask/base-controller": "npm:^3.0.0" "@metamask/object-multiplex": "npm:^1.2.0" "@metamask/permission-controller": "npm:^4.0.0" "@metamask/post-message-stream": "npm:^6.1.2" - "@metamask/rpc-methods": "npm:^1.0.0-prerelease.1" - "@metamask/snaps-execution-environments": "npm:^1.0.0-prerelease.1" + "@metamask/rpc-methods": "npm:^1.0.0" + "@metamask/snaps-execution-environments": "npm:^1.0.0" "@metamask/snaps-registry": "npm:^1.2.1" - "@metamask/snaps-utils": "npm:^1.0.0-prerelease.1" + "@metamask/snaps-utils": "npm:^1.0.0" "@metamask/utils": "npm:^6.0.1" "@xstate/fsm": "npm:^2.0.0" concat-stream: "npm:^2.0.0" @@ -4873,7 +4882,7 @@ __metadata: pump: "npm:^3.0.0" readable-web-to-node-stream: "npm:^3.0.2" tar-stream: "npm:^2.2.0" - checksum: 4977d962019d2f26c1b4487ceeb42690bfaadf8f78bbe6600ae15c5b13da3ddad4ac1eb4632f538c1d912c1de2401005646972ebc371b1b788fdf8faa6fe79aa + checksum: f9ab5a5f593d5d0e971e682d3b32758d30e4bb444ba48f2f66dcf662305ed5c38394fadab32c625d9d171025736637c15765e443d01ef6be247ab75875e0e2e5 languageName: node linkType: hard @@ -4919,15 +4928,15 @@ __metadata: languageName: node linkType: hard -"@metamask/snaps-execution-environments@npm:^1.0.0-prerelease.1": - version: 1.0.0-prerelease.1 - resolution: "@metamask/snaps-execution-environments@npm:1.0.0-prerelease.1" +"@metamask/snaps-execution-environments@npm:^1.0.0": + version: 1.0.0 + resolution: "@metamask/snaps-execution-environments@npm:1.0.0" dependencies: "@metamask/object-multiplex": "npm:^1.2.0" "@metamask/post-message-stream": "npm:^6.1.1" "@metamask/providers": "npm:^10.2.0" - "@metamask/rpc-methods": "npm:^1.0.0-prerelease.1" - "@metamask/snaps-utils": "npm:^1.0.0-prerelease.1" + "@metamask/rpc-methods": "npm:^1.0.0" + "@metamask/snaps-utils": "npm:^1.0.0" "@metamask/utils": "npm:^6.0.1" eth-rpc-errors: "npm:^4.0.3" json-rpc-engine: "npm:^6.1.0" @@ -4935,7 +4944,7 @@ __metadata: ses: "npm:^0.18.1" stream-browserify: "npm:^3.0.0" superstruct: "npm:^1.0.3" - checksum: 209834cf5ffd51013b91cecc287f4a3c73482307481b02e95262653626a23e801bc542ec39557dc84905065ac3fdf5c9f0ab532ca1bb467b2851558548510eca + checksum: 744af06aab2952da69efa6922eb886a6cdbbec0368b35d3d253ecedcc940001e08b2496aa87acfbfe88d7e38955c8e807e942a4c86fc6c01ed86ce44f2106180 languageName: node linkType: hard @@ -4980,13 +4989,13 @@ __metadata: languageName: node linkType: hard -"@metamask/snaps-ui@npm:^1.0.0-prerelease.1": - version: 1.0.0-prerelease.1 - resolution: "@metamask/snaps-ui@npm:1.0.0-prerelease.1" +"@metamask/snaps-ui@npm:^1.0.0": + version: 1.0.0 + resolution: "@metamask/snaps-ui@npm:1.0.0" dependencies: "@metamask/utils": "npm:^6.0.1" superstruct: "npm:^1.0.3" - checksum: 2eca86fdfdfcc5620ec4e6f1ce365e3d694183105c02add8cb390abeae008da38f6626d0172ae0cada6164f9506f03a2cd2cafd625368879a3f97ec89deaf137 + checksum: 805d23c43eb9a5d7ed7d332c9f98187b755142aeb37129d29a5153d2c9bd995beb5508a4d7f26b9d958d403768decded133d8b0c9935d3ac691f6e26fa81c285 languageName: node linkType: hard @@ -5079,9 +5088,9 @@ __metadata: languageName: node linkType: hard -"@metamask/snaps-utils@npm:^1.0.0-prerelease.1": - version: 1.0.0-prerelease.1 - resolution: "@metamask/snaps-utils@npm:1.0.0-prerelease.1" +"@metamask/snaps-utils@npm:^1.0.0": + version: 1.0.0 + resolution: "@metamask/snaps-utils@npm:1.0.0" dependencies: "@babel/core": "npm:^7.18.6" "@babel/types": "npm:^7.18.7" @@ -5090,7 +5099,7 @@ __metadata: "@metamask/permission-controller": "npm:^4.0.0" "@metamask/providers": "npm:^10.2.1" "@metamask/snaps-registry": "npm:^1.2.1" - "@metamask/snaps-ui": "npm:^1.0.0-prerelease.1" + "@metamask/snaps-ui": "npm:^1.0.0" "@metamask/utils": "npm:^6.0.1" "@noble/hashes": "npm:^1.1.3" "@scure/base": "npm:^1.1.1" @@ -5099,11 +5108,11 @@ __metadata: fast-deep-equal: "npm:^3.1.3" fast-json-stable-stringify: "npm:^2.1.0" rfdc: "npm:^1.3.0" - semver: "npm:^7.3.7" - ses: "npm:^0.18.1" + semver: "npm:^7.5.4" + ses: "npm:^0.18.7" superstruct: "npm:^1.0.3" validate-npm-package-name: "npm:^5.0.0" - checksum: 5adda5d8aa059e6115deb29246e3828200c90dad4e212c872521306e2d9f43c4ea66be93e999a5fedb592744473fc89ba90011ef4dec23f1737a101dd5213a03 + checksum: daf2ff95c7fbd3c68ef47b3816aba9fbbe7363adc780500fe03b3b0b0ba23ca382e16feeb6deb909d458e08c035214e5819a48d8f7456499934299224f980b8f languageName: node linkType: hard @@ -24213,7 +24222,7 @@ __metadata: "@babel/preset-typescript": "npm:^7.16.7" "@babel/register": "npm:^7.5.5" "@babel/runtime": "npm:^7.18.9" - "@blockaid/ppom": "npm:^0.1.2" + "@blockaid/ppom": "npm:^1.0.2" "@download/blockies": "npm:^1.0.3" "@ensdomains/content-hash": "npm:^2.5.6" "@ethereumjs/common": "npm:^3.1.1" @@ -24267,7 +24276,7 @@ __metadata: "@metamask/key-tree": "npm:^9.0.0" "@metamask/keyring-controller": "npm:^7.0.0" "@metamask/logo": "npm:^3.1.1" - "@metamask/message-manager": "npm:^7.0.2" + "@metamask/message-manager": "npm:^7.3.0" "@metamask/metamask-eth-abis": "npm:^3.0.0" "@metamask/network-controller": "npm:^12.0.0" "@metamask/notification-controller": "npm:^3.0.0" @@ -24276,21 +24285,21 @@ __metadata: "@metamask/phishing-controller": "npm:^6.0.0" "@metamask/phishing-warning": "npm:^2.1.0" "@metamask/post-message-stream": "npm:^6.0.0" - "@metamask/ppom-validator": "npm:^0.1.2" + "@metamask/ppom-validator": "npm:^0.2.0" "@metamask/providers": "npm:^11.1.0" "@metamask/rate-limit-controller": "npm:^3.0.0" - "@metamask/rpc-methods": "npm:^1.0.0-prerelease.1" + "@metamask/rpc-methods": "npm:^1.0.0" "@metamask/rpc-methods-flask": "npm:@metamask/rpc-methods@0.37.2-flask.1" "@metamask/safe-event-emitter": "npm:^2.0.0" "@metamask/scure-bip39": "npm:^2.0.3" "@metamask/signature-controller": "npm:^5.3.0" "@metamask/slip44": "npm:^3.0.0" "@metamask/smart-transactions-controller": "npm:^4.0.0" - "@metamask/snaps-controllers": "npm:^1.0.0-prerelease.1" + "@metamask/snaps-controllers": "npm:^1.0.0" "@metamask/snaps-controllers-flask": "npm:@metamask/snaps-controllers@0.38.0-flask.1" - "@metamask/snaps-ui": "npm:^1.0.0-prerelease.1" + "@metamask/snaps-ui": "npm:^1.0.0" "@metamask/snaps-ui-flask": "npm:@metamask/snaps-ui@0.37.3-flask.1" - "@metamask/snaps-utils": "npm:^1.0.0-prerelease.1" + "@metamask/snaps-utils": "npm:^1.0.0" "@metamask/snaps-utils-flask": "npm:@metamask/snaps-utils@0.38.0-flask.1" "@metamask/subject-metadata-controller": "npm:^2.0.0" "@metamask/test-dapp": "npm:^7.0.1" @@ -24519,7 +24528,7 @@ __metadata: selenium-webdriver: "npm:^4.9.0" semver: "npm:^7.5.4" serve-handler: "npm:^6.1.2" - ses: "npm:^0.18.4" + ses: "npm:^0.18.7" single-call-balance-checker-abi: "npm:^1.0.0" sinon: "npm:^9.0.0" source-map: "npm:^0.7.2" @@ -30933,10 +30942,12 @@ __metadata: languageName: node linkType: hard -"ses@npm:^0.18.1, ses@npm:^0.18.4": - version: 0.18.4 - resolution: "ses@npm:0.18.4" - checksum: 377d2a898816fe2651fe4cff3832f52efb7f0649a9e439b89d0f39f9d15279fb5bb6aadf85759af6768da43e9dbe096d2e39697d66171412c87f3acf09fcbc30 +"ses@npm:^0.18.1, ses@npm:^0.18.7": + version: 0.18.7 + resolution: "ses@npm:0.18.7" + dependencies: + "@endo/env-options": "npm:^0.1.3" + checksum: 601b442426bb8113bb855548878771aacb62f3c4eb3c45ff32f70338320f03eeb23c0d7eb834f5539793fa8ce497f304db50cbd5da51ef3531a4e94d424bb98c languageName: node linkType: hard