From 0145041d0b79feecfce5524270b0fa985b00fca7 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Mon, 21 Feb 2022 19:33:48 -0600 Subject: [PATCH 001/105] [GridPlus] Updates SDK to v0.9.10 (hotfix) (#13691) See: https://github.com/GridPlus/gridplus-sdk/releases/tag/v0.9.10-hotfix --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index e2c668b34..eee361216 100644 --- a/yarn.lock +++ b/yarn.lock @@ -13434,9 +13434,9 @@ graphql-subscriptions@^1.1.0: integrity sha512-5gghUc24tP9HRznNpV2+FIoq3xKkj5dTQqf4v0CpdPbFVwFkWoxOM+o+2OC9ZSvjEMTjfmG9QT+gcvggTwW1zw== gridplus-sdk@^0.9.7: - version "0.9.7" - resolved "https://registry.yarnpkg.com/gridplus-sdk/-/gridplus-sdk-0.9.7.tgz#fefa45c4373d02e351ea18059b49429b903d6cf5" - integrity sha512-n6dG93XYGplDctUaBMyDsBQzi9kWMB+/2XBzNr2uaHVBpXv8SFPSbYD+uPRUwbjIb8xcRsN0RzHL9QqMO+2vOQ== + version "0.9.10" + resolved "https://registry.yarnpkg.com/gridplus-sdk/-/gridplus-sdk-0.9.10.tgz#8835e8bc9a2ca7ae163520ddcc0b313350e67dd2" + integrity sha512-CNvhyaz3F8UvlHqihFJlOt+FenmFkb2dFrbBTyRth/nlzD8Tm0HjW+uyY1W0ekDp45Exz9l0VY0EmdvjphBe1w== dependencies: aes-js "^3.1.1" bech32 "^2.0.0" From 60aa69a624d09b650f377a87e826ee068e59ebf9 Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Tue, 22 Feb 2022 11:28:04 +0530 Subject: [PATCH 002/105] Confirm transaction page: use method name only for contract transactions (#13643) --- .../confirm-transaction-base.component.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js b/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js index ef35c4c72..f7f9774f3 100644 --- a/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js +++ b/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js @@ -1035,7 +1035,14 @@ export default class ConfirmTransactionBase extends Component { return userAcknowledgedGasMissing ? false : !valid; }; - let functionType = getMethodName(name); + let functionType; + if ( + txData.type === TRANSACTION_TYPES.DEPLOY_CONTRACT || + txData.type === TRANSACTION_TYPES.CONTRACT_INTERACTION + ) { + functionType = getMethodName(name); + } + if (!functionType) { if (type) { functionType = getTransactionTypeTitle(t, type, nativeCurrency); From bf8f331297539ca20e68971ba9525f708b288e06 Mon Sep 17 00:00:00 2001 From: Niranjana Binoy <43930900+NiranjanaBinoy@users.noreply.github.com> Date: Tue, 22 Feb 2022 10:45:19 -0500 Subject: [PATCH 003/105] Update "Forgot Password?" copy (#13493) --- app/_locales/am/messages.json | 3 - app/_locales/ar/messages.json | 3 - app/_locales/bg/messages.json | 3 - app/_locales/bn/messages.json | 3 - app/_locales/ca/messages.json | 3 - app/_locales/da/messages.json | 3 - app/_locales/de/messages.json | 13 ---- app/_locales/el/messages.json | 13 ---- app/_locales/en/messages.json | 34 +++++---- app/_locales/es/messages.json | 10 --- app/_locales/es_419/messages.json | 15 +--- app/_locales/et/messages.json | 3 - app/_locales/fa/messages.json | 3 - app/_locales/fi/messages.json | 3 - app/_locales/fil/messages.json | 3 - app/_locales/fr/messages.json | 13 ---- app/_locales/he/messages.json | 3 - app/_locales/hi/messages.json | 13 ---- app/_locales/hr/messages.json | 3 - app/_locales/ht/messages.json | 3 - app/_locales/hu/messages.json | 3 - app/_locales/id/messages.json | 13 ---- app/_locales/it/messages.json | 3 - app/_locales/ja/messages.json | 13 ---- app/_locales/kn/messages.json | 3 - app/_locales/ko/messages.json | 13 ---- app/_locales/lt/messages.json | 3 - app/_locales/lv/messages.json | 3 - app/_locales/ms/messages.json | 3 - app/_locales/no/messages.json | 3 - app/_locales/ph/messages.json | 10 --- app/_locales/pl/messages.json | 3 - app/_locales/pt_BR/messages.json | 13 ---- app/_locales/ro/messages.json | 3 - app/_locales/ru/messages.json | 13 ---- app/_locales/sk/messages.json | 3 - app/_locales/sl/messages.json | 3 - app/_locales/sr/messages.json | 3 - app/_locales/sv/messages.json | 3 - app/_locales/sw/messages.json | 3 - app/_locales/tl/messages.json | 13 ---- app/_locales/tr/messages.json | 13 ---- app/_locales/uk/messages.json | 3 - app/_locales/vi/messages.json | 13 ---- app/_locales/zh_CN/messages.json | 13 ---- app/_locales/zh_TW/messages.json | 3 - test/e2e/metamask-ui.spec.js | 7 +- test/e2e/tests/metamask-responsive-ui.spec.js | 7 +- ui/helpers/constants/zendesk-url.js | 6 ++ ui/pages/keychains/index.scss | 26 ++----- ui/pages/keychains/restore-vault.js | 69 +++++++++++++++---- ui/pages/keychains/restore-vault.test.js | 44 ++++++++++++ ui/pages/unlock-page/README.mdx | 2 +- ui/pages/unlock-page/index.scss | 9 +-- ui/pages/unlock-page/unlock-page.component.js | 19 +++-- .../unlock-page/unlock-page.component.test.js | 2 +- 56 files changed, 147 insertions(+), 369 deletions(-) create mode 100644 ui/pages/keychains/restore-vault.test.js diff --git a/app/_locales/am/messages.json b/app/_locales/am/messages.json index 51ea3e653..8d9e27e13 100644 --- a/app/_locales/am/messages.json +++ b/app/_locales/am/messages.json @@ -750,9 +750,6 @@ "restore": { "message": "እነበረበት መልስ" }, - "restoreAccountWithSeed": { - "message": "መለያዎን በዘር ሐረግ ወደነበረበት ይመልሱ" - }, "revealSeedWords": { "message": "የዘር ቃላትን ይግለጹ" }, diff --git a/app/_locales/ar/messages.json b/app/_locales/ar/messages.json index 0a93bbd3e..d3e78ae7b 100644 --- a/app/_locales/ar/messages.json +++ b/app/_locales/ar/messages.json @@ -766,9 +766,6 @@ "restore": { "message": "استعادة" }, - "restoreAccountWithSeed": { - "message": "قم باستعادة حسابك بواسطة عبارة الأمان" - }, "revealSeedWords": { "message": "كشف كلمات عبارات الأمان" }, diff --git a/app/_locales/bg/messages.json b/app/_locales/bg/messages.json index 35f765175..df8c830ae 100644 --- a/app/_locales/bg/messages.json +++ b/app/_locales/bg/messages.json @@ -761,9 +761,6 @@ "restore": { "message": "Възстановяване" }, - "restoreAccountWithSeed": { - "message": "Възстановете акаунта си с фраза зародиш" - }, "revealSeedWords": { "message": "Разкрий думите зародиш" }, diff --git a/app/_locales/bn/messages.json b/app/_locales/bn/messages.json index 69577c0e7..7290dab16 100644 --- a/app/_locales/bn/messages.json +++ b/app/_locales/bn/messages.json @@ -765,9 +765,6 @@ "restore": { "message": "পুনরুদ্ধার করুন" }, - "restoreAccountWithSeed": { - "message": "সীড ফ্রেজ দিয়ে আপনার অ্যাকাউন্ট রিস্টোর করুন" - }, "revealSeedWords": { "message": "সীড শব্দগুলি প্রকাশ করুন" }, diff --git a/app/_locales/ca/messages.json b/app/_locales/ca/messages.json index 03ff652ce..ce8f14237 100644 --- a/app/_locales/ca/messages.json +++ b/app/_locales/ca/messages.json @@ -743,9 +743,6 @@ "restore": { "message": "Restaura" }, - "restoreAccountWithSeed": { - "message": "Restaura el teu compte amb Frase de Recuperació" - }, "revealSeedWords": { "message": "Revelar Paraules de Recuperació" }, diff --git a/app/_locales/da/messages.json b/app/_locales/da/messages.json index 709e63bcc..f6f1e92d6 100644 --- a/app/_locales/da/messages.json +++ b/app/_locales/da/messages.json @@ -746,9 +746,6 @@ "restore": { "message": "Gendan" }, - "restoreAccountWithSeed": { - "message": "Gendan din konto med Seed-sætning" - }, "revealSeedWords": { "message": "Vis Seedord" }, diff --git a/app/_locales/de/messages.json b/app/_locales/de/messages.json index 5549b95d7..67231be4b 100644 --- a/app/_locales/de/messages.json +++ b/app/_locales/de/messages.json @@ -1279,19 +1279,12 @@ "importAccountError": { "message": "Fehler beim Importieren des Kontos." }, - "importAccountLinkText": { - "message": "mit einer Geheime Wiederherstellungsphrase importieren" - }, "importAccountMsg": { "message": " Importierte Accounts werden nicht mit der Seed-Wörterfolge deines ursprünglichen MetaMask Accounts verknüpft. Erfahre mehr über importierte Accounts." }, "importAccountSeedPhrase": { "message": "Ein Konto mit einem Seed-Schlüssel importieren" }, - "importAccountText": { - "message": "oder $1", - "description": "$1 represents the text from `importAccountLinkText` as a link" - }, "importExistingWalletDescription": { "message": "Geben Sie die Geheime Wiederherstellungsphrase (alias Seed Phrase) ein, die Sie beim Erstellen Ihrer Wallet erhalten haben. $1", "description": "$1 is the words 'Learn More' from key 'learnMore', separated here so that it can be added as a link" @@ -2229,9 +2222,6 @@ "restore": { "message": "Wiederherstellen" }, - "restoreAccountWithSeed": { - "message": "Ihr Konto mit mnemonischer Phrase wiederherstellen" - }, "restoreWalletPreferences": { "message": "$1 hat ein Backup Ihrer Daten gefunden. Möchten Sie die Präferenzen Ihrer Wallet wiederherstellen?", "description": "$1 is the date at which the data was backed up" @@ -2299,9 +2289,6 @@ "secretPhrase": { "message": "Nur das erste Konto auf dieser Wallet wird automatisch geladen. Wenn Sie nach Abschluss dieses Vorgangs weitere Konten hinzufügen möchten, klicken Sie auf das Dropdown-Menü und wählen Sie dann Konto erstellen." }, - "secretPhraseWarning": { - "message": "Wenn Sie eine andere geheime Wiederherstellungsphrase verwenden, werden Ihre aktuelle Wallet, Ihre Konten und Vermögenswerte dauerhaft aus dieser App entfernt. Diese Aktion kann nicht rückgängig gemacht werden." - }, "secretRecoveryPhrase": { "message": "Geheime Wiederherstellungsphrase" }, diff --git a/app/_locales/el/messages.json b/app/_locales/el/messages.json index 387bf6031..9408aeab9 100644 --- a/app/_locales/el/messages.json +++ b/app/_locales/el/messages.json @@ -1273,19 +1273,12 @@ "importAccountError": { "message": "Σφάλμα εισαγωγής λογαριασμού." }, - "importAccountLinkText": { - "message": "εισαγωγή χρησιμοποιώντας τη Μυστική Φράση Ανάκτησης" - }, "importAccountMsg": { "message": "Οι λογαριασμοί που εισάγονται δεν θα συσχετιστούν με τη Μυστική Φράση Ανάκτησης του λογαριασμού σας MetaTask που δημιουργήθηκε αρχικά. Μάθετε περισσότερα για τους εισηγμένους λογαριασμούς" }, "importAccountSeedPhrase": { "message": "Εισαγωγή λογαριασμού με Μυστική Φράση Ανάκτησης" }, - "importAccountText": { - "message": "ή $1", - "description": "$1 represents the text from `importAccountLinkText` as a link" - }, "importExistingWalletDescription": { "message": "Εισάγετε τη Μυστική Φράση Ανάκτησης (δλδ Seed Phrase) που σας δόθηκε όταν δημιουργήσατε το πορτοφόλι σας. $1", "description": "$1 is the words 'Learn More' from key 'learnMore', separated here so that it can be added as a link" @@ -2229,9 +2222,6 @@ "restore": { "message": "Επαναφορά" }, - "restoreAccountWithSeed": { - "message": "Επαναφέρετε τον Λογαριασμό σας με Φράση Επαναφοράς" - }, "restoreWalletPreferences": { "message": "Βρέθηκε ένα αντίγραφο ασφαλείας των δεδομένων σας από το $1. Θα θέλατε να επαναφέρετε τις προτιμήσεις του πορτοφολιού σας;", "description": "$1 is the date at which the data was backed up" @@ -2299,9 +2289,6 @@ "secretPhrase": { "message": "Μόνο ο πρώτος λογαριασμός σε αυτό το πορτοφόλι θα φορτώσει αυτόματα. Μετά την ολοκλήρωση αυτής της διαδικασίας, για να προσθέσετε επιπλέον λογαριασμούς, κάντε κλικ στο αναπτυσσόμενο μενού και, στη συνέχεια, επιλέξτε Δημιουργία Λογαριασμού." }, - "secretPhraseWarning": { - "message": "Αν κάνετε επαναφορά χρησιμοποιώντας μια άλλη Μυστική Φράση Ανάκτησης, το τρέχον πορτοφόλι, οι λογαριασμοί και τα περιουσιακά στοιχεία σας θα αφαιρεθούν από αυτή την εφαρμογή μόνιμα. Αυτή η ενέργεια δεν μπορεί να αναιρεθεί." - }, "secretRecoveryPhrase": { "message": "Μυστική Φράση Ανάκτησης" }, diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index ba8a7c370..237a83d92 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -1206,6 +1206,9 @@ "forgetDevice": { "message": "Forget this device" }, + "forgotPassword": { + "message": "Forgot password?" + }, "from": { "message": "From" }, @@ -1408,19 +1411,12 @@ "importAccountError": { "message": "Error importing account." }, - "importAccountLinkText": { - "message": "import using Secret Recovery Phrase" - }, "importAccountMsg": { "message": "Imported accounts will not be associated with your originally created MetaMask account Secret Recovery Phrase. Learn more about imported accounts" }, "importAccountSeedPhrase": { "message": "Import a wallet with Secret Recovery Phrase" }, - "importAccountText": { - "message": "or $1", - "description": "$1 represents the text from `importAccountLinkText` as a link" - }, "importExistingWalletDescription": { "message": "Enter your Secret Recovery Phrase (aka Seed Phrase) that you were given when you created your wallet. $1", "description": "$1 is the words 'Learn More' from key 'learnMore', separated here so that it can be added as a link" @@ -2356,6 +2352,12 @@ "queued": { "message": "Queued" }, + "reAddAccounts": { + "message": "re-add any other accounts" + }, + "reAdded": { + "message": "re-added" + }, "readdToken": { "message": "You can add this token back in the future by going to “Import token” in your accounts options menu." }, @@ -2455,12 +2457,21 @@ "resetAccountDescription": { "message": "Resetting your account will clear your transaction history. This will not change the balances in your accounts or require you to re-enter your Secret Recovery Phrase." }, + "resetWallet": { + "message": "Reset Wallet" + }, + "resetWalletSubHeader": { + "message": "MetaMask does not keep a copy of your password. If you’re having trouble unlocking your account, you will need to reset your wallet. You can do this by providing the Secret Recovery Phrase you used when you set up your wallet." + }, + "resetWalletUsingSRP": { + "message": "This action will delete your current wallet and Secret Recovery Phrase from this device, along with the list of accounts you’ve curated. After resetting with a Secret Recovery Phrase, you’ll see a list of accounts based on the Secret Recovery Phrase you use to reset. This new list will automatically include accounts that have a balance. You’ll also be able to $1 created previously. Custom accounts that you’ve imported will need to be $2, and any custom tokens you’ve added to an account will need to be $3 as well." + }, + "resetWalletWarning": { + "message": "Make sure you’re using the correct Secret Recovery Phrase before proceeding. You will not be able to undo this." + }, "restore": { "message": "Restore" }, - "restoreAccountWithSeed": { - "message": "Restore your Account with Secret Recovery Phrase" - }, "restoreWalletPreferences": { "message": "A backup of your data from $1 has been found. Would you like to restore your wallet preferences?", "description": "$1 is the date at which the data was backed up" @@ -2528,9 +2539,6 @@ "secretPhrase": { "message": "Only the first account on this wallet will auto load. After completing this process, to add additional accounts, click the drop down menu, then select Create Account." }, - "secretPhraseWarning": { - "message": "If you restore using another Secret Recovery Phrase, your current wallet, accounts and assets will be removed from this app permanently. This action cannot be undone." - }, "secretRecoveryPhrase": { "message": "Secret Recovery Phrase" }, diff --git a/app/_locales/es/messages.json b/app/_locales/es/messages.json index cefb30eaa..a08ff6118 100644 --- a/app/_locales/es/messages.json +++ b/app/_locales/es/messages.json @@ -826,19 +826,12 @@ "importAccount": { "message": "Importar cuenta" }, - "importAccountLinkText": { - "message": "importar con la frase secreta de recuperación" - }, "importAccountMsg": { "message": " Las cuentas importadas no se asociarán con la frase secreta de recuperación de la cuenta original de MetaMask. Más información sobre las cuentas importadas " }, "importAccountSeedPhrase": { "message": "Importar una cuenta con la frase secreta de recuperación" }, - "importAccountText": { - "message": "o $1", - "description": "$1 represents the text from `importAccountLinkText` as a link" - }, "importTokenQuestion": { "message": "¿Desea importar el token?" }, @@ -1421,9 +1414,6 @@ "restore": { "message": "Restaurar" }, - "restoreAccountWithSeed": { - "message": "Restaurar la cuenta con la frase secreta de recuperación" - }, "restoreWalletPreferences": { "message": "Se encontró una copia de seguridad de los datos de $1. ¿Desea restaurar las preferencias de cartera?", "description": "$1 is the date at which the data was backed up" diff --git a/app/_locales/es_419/messages.json b/app/_locales/es_419/messages.json index b1d29d4a9..dc2ebecb5 100644 --- a/app/_locales/es_419/messages.json +++ b/app/_locales/es_419/messages.json @@ -1322,19 +1322,12 @@ "importAccountError": { "message": "Error al importar la cuenta." }, - "importAccountLinkText": { - "message": "importar con la frase secreta de recuperación" - }, "importAccountMsg": { "message": "Las cuentas importadas no se asociarán con la frase secreta de recuperación de la cuenta original de MetaMask. Aprenda más sobre las cuentas importadas" }, "importAccountSeedPhrase": { "message": "Importar una cartera con la frase secreta de recuperación" }, - "importAccountText": { - "message": "o $1", - "description": "$1 represents the text from `importAccountLinkText` as a link" - }, "importExistingWalletDescription": { "message": "Ingrese su frase secreta de recuperación (también conocida como Frase Semilla) que recibió al crear su cartera. $1", "description": "$1 is the words 'Learn More' from key 'learnMore', separated here so that it can be added as a link" @@ -2278,9 +2271,6 @@ "restore": { "message": "Restaurar" }, - "restoreAccountWithSeed": { - "message": "Restaurar la cuenta con la frase secreta de recuperación" - }, "restoreWalletPreferences": { "message": "Se encontró una copia de seguridad de los datos de $1. ¿Desea restaurar las preferencias de cartera?", "description": "$1 is the date at which the data was backed up" @@ -2346,10 +2336,7 @@ "message": "ADVERTENCIA: No revele su frase de respaldo. Cualquier persona que tenga esta frase puede robarle los ethers." }, "secretPhrase": { - "message": "Solo la primera cuenta de esta cartera se cargará automáticamente. Después de llevar a cabo este proceso, para agregar cuentas adicionales haga clic en el menú desplegable y luego seleccione Crear cuenta." - }, - "secretPhraseWarning": { - "message": "Si restablece utilizando otra frase secreta de recuperación, su cartera actual, sus cuentas y sus activos se eliminarán de esta aplicación de forma permanente. Esta acción es irreversible." + "message": "Ingrese su frase secreta aquí para restaurar su bóveda." }, "secretRecoveryPhrase": { "message": "Frase secreta de recuperación" diff --git a/app/_locales/et/messages.json b/app/_locales/et/messages.json index b349b34b2..8bf5aa8ac 100644 --- a/app/_locales/et/messages.json +++ b/app/_locales/et/messages.json @@ -755,9 +755,6 @@ "restore": { "message": "Taasta" }, - "restoreAccountWithSeed": { - "message": "Taastage konto seemnefraasi abil" - }, "revealSeedWords": { "message": "Kuva seemnesõnu" }, diff --git a/app/_locales/fa/messages.json b/app/_locales/fa/messages.json index eb43442d2..32b7c8e48 100644 --- a/app/_locales/fa/messages.json +++ b/app/_locales/fa/messages.json @@ -765,9 +765,6 @@ "restore": { "message": "بازیابی" }, - "restoreAccountWithSeed": { - "message": "حساب تان را با عبارت بازیاب، بازیابی کنید" - }, "revealSeedWords": { "message": "کلمات بازیاب را آشکار کنید" }, diff --git a/app/_locales/fi/messages.json b/app/_locales/fi/messages.json index d00a8ff3e..108165b57 100644 --- a/app/_locales/fi/messages.json +++ b/app/_locales/fi/messages.json @@ -762,9 +762,6 @@ "restore": { "message": "Palauta" }, - "restoreAccountWithSeed": { - "message": "Palauta tilisi käyttäen salaustekstiä (seed phrase)" - }, "revealSeedWords": { "message": "Paljasta salaussanat" }, diff --git a/app/_locales/fil/messages.json b/app/_locales/fil/messages.json index 06999930d..64695058e 100644 --- a/app/_locales/fil/messages.json +++ b/app/_locales/fil/messages.json @@ -689,9 +689,6 @@ "restore": { "message": "Ipanumbalik" }, - "restoreAccountWithSeed": { - "message": "I-restore ang iyong Account gamit ang Seed Phrase" - }, "revealSeedWords": { "message": "Ipakita ang Seed Words" }, diff --git a/app/_locales/fr/messages.json b/app/_locales/fr/messages.json index b1bc21ee4..e00fa4478 100644 --- a/app/_locales/fr/messages.json +++ b/app/_locales/fr/messages.json @@ -1273,19 +1273,12 @@ "importAccountError": { "message": "Erreur d’importation de compte." }, - "importAccountLinkText": { - "message": "importer en utilisant la phrase secrète de récupération" - }, "importAccountMsg": { "message": "Les comptes importés ne seront pas associés à la phrase secrète de récupération que vous avez créée au départ dans MetaMask. En savoir plus sur les comptes importés" }, "importAccountSeedPhrase": { "message": "Importez un compte avec une phrase mnémotechnique" }, - "importAccountText": { - "message": "ou $1", - "description": "$1 represents the text from `importAccountLinkText` as a link" - }, "importExistingWalletDescription": { "message": "Saisissez la phrase secrète de récupération (aussi appelée « phrase mnémonique » ou « seed ») qui vous a été attribuée lors de la création de votre portefeuille. $1", "description": "$1 is the words 'Learn More' from key 'learnMore', separated here so that it can be added as a link" @@ -2229,9 +2222,6 @@ "restore": { "message": "Restaurer" }, - "restoreAccountWithSeed": { - "message": "Restaurer votre compte avec une phrase Seed." - }, "restoreWalletPreferences": { "message": "Une sauvegarde de vos données de $1 a été trouvée. Voulez-vous restaurer vos préférences de portefeuille ?", "description": "$1 is the date at which the data was backed up" @@ -2299,9 +2289,6 @@ "secretPhrase": { "message": "Seul le premier compte de ce portefeuille sera chargé automatiquement. Après avoir terminé ce processus, pour ajouter des comptes supplémentaires, cliquez sur le menu déroulant, puis sélectionnez Créer un compte." }, - "secretPhraseWarning": { - "message": "Si vous effectuez une restauration à l’aide d’une autre phrase secrète de récupération, votre portefeuille, vos comptes et vos actifs actuels seront définitivement supprimés de cette application. Cette action est irréversible." - }, "secretRecoveryPhrase": { "message": "Phrase secrète de récupération" }, diff --git a/app/_locales/he/messages.json b/app/_locales/he/messages.json index 70a04be83..8cfe2edbd 100644 --- a/app/_locales/he/messages.json +++ b/app/_locales/he/messages.json @@ -762,9 +762,6 @@ "restore": { "message": "שחזר" }, - "restoreAccountWithSeed": { - "message": "שחזר את חשבונך באמצעות צירוף הגרעין" - }, "revealSeedWords": { "message": "גלה מילות Seed" }, diff --git a/app/_locales/hi/messages.json b/app/_locales/hi/messages.json index 0853cd02b..9de7692c3 100644 --- a/app/_locales/hi/messages.json +++ b/app/_locales/hi/messages.json @@ -1273,19 +1273,12 @@ "importAccountError": { "message": "खाता आयात करने में त्रुटि।" }, - "importAccountLinkText": { - "message": "गुप्त रिकवरी फ्रेज का उपयोग करके आयात करें" - }, "importAccountMsg": { "message": "आयातित खाते आपके मूल रूप से बनाए गए MetaMask खाते के गुप्त रिकवरी फ्रेज से संबद्ध नहीं होंगे। आयातित खातों के बारे में अधिक जानें" }, "importAccountSeedPhrase": { "message": "गुप्त रिकवरी फ्रेज के साथ एक खाता आयात करें" }, - "importAccountText": { - "message": "या $1", - "description": "$1 represents the text from `importAccountLinkText` as a link" - }, "importExistingWalletDescription": { "message": "अपना सीक्रेट रिकवरी फ्रेज (उर्फ सीड फ्रेज) दर्ज करें जो आपको अपना वॉलेट बनाने पर दिया गया था। $1", "description": "$1 is the words 'Learn More' from key 'learnMore', separated here so that it can be added as a link" @@ -2229,9 +2222,6 @@ "restore": { "message": "पुनर्स्थापित करें" }, - "restoreAccountWithSeed": { - "message": "गुप्त रिकवरी फ्रेज के साथ अपने खाते को पुनर्स्थापित करें" - }, "restoreWalletPreferences": { "message": "$1 से आपके डेटा का बैकअप मिला है। क्या आप अपनी वॉलेट वरीयताओं को पुनर्स्थापित करना चाहते हैं?", "description": "$1 is the date at which the data was backed up" @@ -2299,9 +2289,6 @@ "secretPhrase": { "message": "इस वॉलेट पर केवल पहला खाता स्वतः लोड होगा। इस प्रक्रिया को पूरा करने के बाद, अतिरिक्त खाते जोड़ने के लिए, ड्रॉप डाउन मेन्यू पर क्लिक करें, फिर खाता बनाएं चुनें।" }, - "secretPhraseWarning": { - "message": "यदि आप किसी दूसरे सीक्रेट रिकवरी फ्रेज का उपयोग कर पुनर्स्थापित करते हैं, तो इस ऐप से आपके वर्तमान वॉलेट, अकाउंट, और संपति स्थायी रूप से हटा दिये जाएंगे। यह क्रिया पूर्ववत नहीं की जा सकती।" - }, "secretRecoveryPhrase": { "message": "सीक्रेट रिकवरी फ्रेज" }, diff --git a/app/_locales/hr/messages.json b/app/_locales/hr/messages.json index 1de58c64d..eb3ffefb1 100644 --- a/app/_locales/hr/messages.json +++ b/app/_locales/hr/messages.json @@ -758,9 +758,6 @@ "restore": { "message": "Vrati" }, - "restoreAccountWithSeed": { - "message": "Obnovite svoj račun početnom rečenicom" - }, "revealSeedWords": { "message": "Otkrij početne riječi" }, diff --git a/app/_locales/ht/messages.json b/app/_locales/ht/messages.json index dae10bef3..44409dd45 100644 --- a/app/_locales/ht/messages.json +++ b/app/_locales/ht/messages.json @@ -479,9 +479,6 @@ "restore": { "message": "Retabli" }, - "restoreAccountWithSeed": { - "message": "Retabli kont ou avèk yo Seed Fraz" - }, "revealSeedWords": { "message": "Revele Seed Mo Yo" }, diff --git a/app/_locales/hu/messages.json b/app/_locales/hu/messages.json index 764e55689..ddbde0797 100644 --- a/app/_locales/hu/messages.json +++ b/app/_locales/hu/messages.json @@ -758,9 +758,6 @@ "restore": { "message": "Visszaállítás" }, - "restoreAccountWithSeed": { - "message": "Fiók helyreállítása a seed mondat segítségével" - }, "revealSeedWords": { "message": "Seed szavak megjelenítése" }, diff --git a/app/_locales/id/messages.json b/app/_locales/id/messages.json index 84418a062..c3751ee0f 100644 --- a/app/_locales/id/messages.json +++ b/app/_locales/id/messages.json @@ -1273,19 +1273,12 @@ "importAccountError": { "message": "Galat saat mengimpor akun." }, - "importAccountLinkText": { - "message": "impor menggunakan Frasa Pemulihan Rahasia" - }, "importAccountMsg": { "message": "Akun yang diimpor tidak akan dikaitkan dengan Frasa Pemulihan Rahasia akun MetaMask yang asli dibuat. Pelajari selengkapnya tentang akun yang diimpor" }, "importAccountSeedPhrase": { "message": "Impor dompet dengan Frasa Pemulihan Rahasia" }, - "importAccountText": { - "message": "atau $1", - "description": "$1 represents the text from `importAccountLinkText` as a link" - }, "importExistingWalletDescription": { "message": "Masukkan Frasa Pemulihan Rahasia Anda (alias Frasa Benih) yang diberikan saat Anda membuat dompet. $1", "description": "$1 is the words 'Learn More' from key 'learnMore', separated here so that it can be added as a link" @@ -2229,9 +2222,6 @@ "restore": { "message": "Pulihkan" }, - "restoreAccountWithSeed": { - "message": "Pulihkan Akun dengan Frasa Pemulihan Rahasia" - }, "restoreWalletPreferences": { "message": "Cadangan data Anda dari $1 telah ditemukan. Pulihkan preferensi dompet Anda?", "description": "$1 is the date at which the data was backed up" @@ -2299,9 +2289,6 @@ "secretPhrase": { "message": "Hanya akun pertama di dompet ini yang akan dimuat secara otomatis. Setelah proses ini selesai, untuk menambahkan akun tambahan, klik menu drop down, lalu pilih Buat Akun." }, - "secretPhraseWarning": { - "message": "Jika Anda memulihkan menggunakan Frasa Pemulihan Rahasia lainnya, dompet, akun, dan aset Anda saat ini akan dihapus dari aplikasi ini secara permanen. Tindakan ini tidak dapat dibatalkan." - }, "secretRecoveryPhrase": { "message": "Frasa Pemulihan Rahasia" }, diff --git a/app/_locales/it/messages.json b/app/_locales/it/messages.json index 3ebfc6e95..b686b183b 100644 --- a/app/_locales/it/messages.json +++ b/app/_locales/it/messages.json @@ -1152,9 +1152,6 @@ "restore": { "message": "Ripristina" }, - "restoreAccountWithSeed": { - "message": "Ripristina Account con la Frase Seed" - }, "restoreWalletPreferences": { "message": "È stato trovato un backup dei tuoi dati da $1. Vuoi ripristinare le preferenze del portafoglio?", "description": "$1 is the date at which the data was backed up" diff --git a/app/_locales/ja/messages.json b/app/_locales/ja/messages.json index 88ff32476..c6d3bae26 100644 --- a/app/_locales/ja/messages.json +++ b/app/_locales/ja/messages.json @@ -1273,19 +1273,12 @@ "importAccountError": { "message": "アカウントのインポート中にエラーが発生しました。" }, - "importAccountLinkText": { - "message": "シークレットリカバリーフレーズを使用してインポート" - }, "importAccountMsg": { "message": " インポートされたアカウントは、最初に作成したMetaMaskアカウントのシークレットリカバリーフレーズと関連付けられません。インポートされたアカウントの詳細を表示" }, "importAccountSeedPhrase": { "message": "シークレットリカバリーフレーズを使用してウォレットをインポート" }, - "importAccountText": { - "message": "または$1", - "description": "$1 represents the text from `importAccountLinkText` as a link" - }, "importExistingWalletDescription": { "message": "ウォレットの作成時に提供されたシークレットリカバリーフレーズ (シードフレーズ) を入力してください。$1", "description": "$1 is the words 'Learn More' from key 'learnMore', separated here so that it can be added as a link" @@ -2229,9 +2222,6 @@ "restore": { "message": "復元" }, - "restoreAccountWithSeed": { - "message": "シークレットリカバリーフレーズでアカウントを復元" - }, "restoreWalletPreferences": { "message": "$1のデータのバックアップが見つかりました。ウォレットの基本設定を復元しますか?", "description": "$1 is the date at which the data was backed up" @@ -2299,9 +2289,6 @@ "secretPhrase": { "message": "このウォレットの最初のアカウントのみが自動的に読み込まれます。 このプロセスの完了後、他のアカウントを追加するには、ドロップダウンメニューをクリックし、[アカウントを作成] を選択します。" }, - "secretPhraseWarning": { - "message": "別のシークレットリカバリーフレーズを使用して復元すると、現在のウォレット、アカウント、アセットは永久にこのアプリから削除されます。この操作は元に戻せません。" - }, "secretRecoveryPhrase": { "message": "シークレットリカバリーフレーズ" }, diff --git a/app/_locales/kn/messages.json b/app/_locales/kn/messages.json index 5e74dd375..e6c557c5c 100644 --- a/app/_locales/kn/messages.json +++ b/app/_locales/kn/messages.json @@ -765,9 +765,6 @@ "restore": { "message": "ಮರುಸ್ಥಾಪನೆ" }, - "restoreAccountWithSeed": { - "message": "ಸೀಡ್ ಫ್ರೇಸ್‌ನೊಂದಿಗೆ ನಿಮ್ಮ ಖಾತೆಯನ್ನು ಮರುಸ್ಥಾಪಿಸಿ" - }, "revealSeedWords": { "message": "ಸೀಡ್ ವರ್ಡ್ಸ್ ಬಹಿರಂಗಪಡಿಸಿ" }, diff --git a/app/_locales/ko/messages.json b/app/_locales/ko/messages.json index 413bb0212..d6f619043 100644 --- a/app/_locales/ko/messages.json +++ b/app/_locales/ko/messages.json @@ -1273,19 +1273,12 @@ "importAccountError": { "message": "계정 가져오기 오류" }, - "importAccountLinkText": { - "message": "비밀 복구 구문을 사용해 가져오기" - }, "importAccountMsg": { "message": "가져온 계정은 본래 생성한 MetaMask 계정 비밀 복구 구문과 연결하지 못합니다. 가져온 계정에 대해 자세히 알아보기" }, "importAccountSeedPhrase": { "message": "비밀 복구 구문으로 계정 가져오기" }, - "importAccountText": { - "message": "또는 $1", - "description": "$1 represents the text from `importAccountLinkText` as a link" - }, "importExistingWalletDescription": { "message": "지갑을 만들 때 받은 비밀 복구 구문(시드 구문)을 입력하세요. $1", "description": "$1 is the words 'Learn More' from key 'learnMore', separated here so that it can be added as a link" @@ -2229,9 +2222,6 @@ "restore": { "message": "복구" }, - "restoreAccountWithSeed": { - "message": "비밀 복구 구문으로 계정 복구" - }, "restoreWalletPreferences": { "message": "$1의 데이터 백업이 발견되었습니다. 지갑 환경설정을 복원할까요?", "description": "$1 is the date at which the data was backed up" @@ -2299,9 +2289,6 @@ "secretPhrase": { "message": "금고를 복구하려면 비밀 구문을 여기에 입력하세요." }, - "secretPhraseWarning": { - "message": "다른 비밀 복구 구문을 사용하여 복구하면 현재 지갑, 계정 및 자산이 이 앱에서 영구적으로 제거됩니다. 이 작업은 취소할 수 없습니다." - }, "secretRecoveryPhrase": { "message": "비밀 복구 구문" }, diff --git a/app/_locales/lt/messages.json b/app/_locales/lt/messages.json index efb5c8ab8..1a7dd9a51 100644 --- a/app/_locales/lt/messages.json +++ b/app/_locales/lt/messages.json @@ -765,9 +765,6 @@ "restore": { "message": "Atkurti" }, - "restoreAccountWithSeed": { - "message": "Atkurti paskyrą naudojant atkūrimo frazę" - }, "revealSeedWords": { "message": "Atskleisti atkūrimo žodžius" }, diff --git a/app/_locales/lv/messages.json b/app/_locales/lv/messages.json index 273904682..adf1c19d4 100644 --- a/app/_locales/lv/messages.json +++ b/app/_locales/lv/messages.json @@ -761,9 +761,6 @@ "restore": { "message": "Atjaunot" }, - "restoreAccountWithSeed": { - "message": "Atjaunojiet savu kontu ar atkopšanas frāzi" - }, "revealSeedWords": { "message": "Parādīt atkopšanas vārdus" }, diff --git a/app/_locales/ms/messages.json b/app/_locales/ms/messages.json index 60ca3829c..0675bc2a5 100644 --- a/app/_locales/ms/messages.json +++ b/app/_locales/ms/messages.json @@ -745,9 +745,6 @@ "restore": { "message": "Pulihkan" }, - "restoreAccountWithSeed": { - "message": "Pulihkan Akaun anda dengan Ungkapan Benih" - }, "revealSeedWords": { "message": "Dedahkan Ungkapan Benih" }, diff --git a/app/_locales/no/messages.json b/app/_locales/no/messages.json index 444a81229..a8bb4af03 100644 --- a/app/_locales/no/messages.json +++ b/app/_locales/no/messages.json @@ -752,9 +752,6 @@ "restore": { "message": "Gjenopprett" }, - "restoreAccountWithSeed": { - "message": "Gjenopprett konto med frøfrase" - }, "revealSeedWords": { "message": "Vis frøord" }, diff --git a/app/_locales/ph/messages.json b/app/_locales/ph/messages.json index 6d82062e4..c738b5788 100644 --- a/app/_locales/ph/messages.json +++ b/app/_locales/ph/messages.json @@ -842,19 +842,12 @@ "importAccount": { "message": "Mag-import ng Account" }, - "importAccountLinkText": { - "message": "i-import gamit ang Secret Recovery Phrase" - }, "importAccountMsg": { "message": " Ang mga na-import na account ay hindi mauugnay sa orihinal mong nagawang Secret Recovery Phrase ng MetaMask account. Matuto pa tungkol sa mga na-import account " }, "importAccountSeedPhrase": { "message": "Mag-import ng account gamit ang Secret Recovery Phrase" }, - "importAccountText": { - "message": "o $1", - "description": "$1 represents the text from `importAccountLinkText` as a link" - }, "importTokenQuestion": { "message": "Mag-import ng token?" }, @@ -1446,9 +1439,6 @@ "restore": { "message": "I-restore" }, - "restoreAccountWithSeed": { - "message": "I-restore ang iyong Account gamit ang Secret Recovery Phrase" - }, "restoreWalletPreferences": { "message": "Nakita ang backup ng iyong data mula sa $1. Gusto mo bang i-restore ang mga kagustuhan mo sa wallet?", "description": "$1 is the date at which the data was backed up" diff --git a/app/_locales/pl/messages.json b/app/_locales/pl/messages.json index e37cdeca0..43f9ca385 100644 --- a/app/_locales/pl/messages.json +++ b/app/_locales/pl/messages.json @@ -759,9 +759,6 @@ "restore": { "message": "Przywróć" }, - "restoreAccountWithSeed": { - "message": "Przywróć konto frazą seed" - }, "revealSeedWords": { "message": "Pokaż słowa seed" }, diff --git a/app/_locales/pt_BR/messages.json b/app/_locales/pt_BR/messages.json index d6c8a3923..cbe2a5b1f 100644 --- a/app/_locales/pt_BR/messages.json +++ b/app/_locales/pt_BR/messages.json @@ -1306,19 +1306,12 @@ "importAccountError": { "message": "Erro de importação de conta." }, - "importAccountLinkText": { - "message": "importe usando a Frase de Recuperação Secreta" - }, "importAccountMsg": { "message": "As contas importadas não estarão associadas à Frase de Recuperação Secreta da conta da MetaMask criada originalmente. Saiba mais sobre as contas importadas" }, "importAccountSeedPhrase": { "message": "Importe uma carteira com a Frase de Recuperação Secreta" }, - "importAccountText": { - "message": "ou $1", - "description": "$1 represents the text from `importAccountLinkText` as a link" - }, "importExistingWalletDescription": { "message": "Digite sua Frase de Recuperação Secreta (ou seja, a frase seed) que lhe foi dada quando você criou a sua carteira. $1", "description": "$1 is the words 'Learn More' from key 'learnMore', separated here so that it can be added as a link" @@ -2262,9 +2255,6 @@ "restore": { "message": "Restaurar" }, - "restoreAccountWithSeed": { - "message": "Restaure sua conta com a Frase de Recuperação Secreta" - }, "restoreWalletPreferences": { "message": "Encontramos um backup dos seus dados de $1. Gostaria de restaurar as preferências da sua carteira?", "description": "$1 is the date at which the data was backed up" @@ -2332,9 +2322,6 @@ "secretPhrase": { "message": "Somente a primeira conta nessa carteira será carregada automaticamente. Após concluir esse processo, para adicionar mais contas, clique no menu suspenso e selecione Criar Conta." }, - "secretPhraseWarning": { - "message": "Se você restaurar usando outra Frase de Recuperação Secreta, sua carteira, conta e ativos atuais serão removidos permanentemente deste aplicativo. Essa ação será irreversível." - }, "secretRecoveryPhrase": { "message": "Frase de Recuperação Secreta" }, diff --git a/app/_locales/ro/messages.json b/app/_locales/ro/messages.json index 37135974e..387fac7bb 100644 --- a/app/_locales/ro/messages.json +++ b/app/_locales/ro/messages.json @@ -752,9 +752,6 @@ "restore": { "message": "Restabilește" }, - "restoreAccountWithSeed": { - "message": "Restaurați-vă contul folosind fraza inițială" - }, "revealSeedWords": { "message": "Arată cuvintele din seed" }, diff --git a/app/_locales/ru/messages.json b/app/_locales/ru/messages.json index 23c46ab20..fe4e7336f 100644 --- a/app/_locales/ru/messages.json +++ b/app/_locales/ru/messages.json @@ -1273,19 +1273,12 @@ "importAccountError": { "message": "Ошибка импорта счета." }, - "importAccountLinkText": { - "message": "импортировать с использованием секретной фразы для восстановления" - }, "importAccountMsg": { "message": "Импортированные счета не будут связаны с секретной фразой для восстановления вашего изначально созданного счета MetaMask. Узнайте больше об импортированных счетах" }, "importAccountSeedPhrase": { "message": "Импорт кошелька с помощью секретной фразы для восстановления" }, - "importAccountText": { - "message": "или $1", - "description": "$1 represents the text from `importAccountLinkText` as a link" - }, "importExistingWalletDescription": { "message": "Введите секретную фразу для восстановления (также известную как «сид-фраза»), которую вы получили при создании кошелька. $1", "description": "$1 is the words 'Learn More' from key 'learnMore', separated here so that it can be added as a link" @@ -2229,9 +2222,6 @@ "restore": { "message": "Восстановить" }, - "restoreAccountWithSeed": { - "message": "Восстановите свой счет с помощью секретной фразы для восстановления" - }, "restoreWalletPreferences": { "message": "Найдена резервная копия ваших данных из $1. Хотите восстановить настройки кошелька?", "description": "$1 is the date at which the data was backed up" @@ -2299,9 +2289,6 @@ "secretPhrase": { "message": "Автоматически загружается только первый счет в этом кошельке. Для добавления дополнительных счетов, после завершения этого процесса нажмите на выпадающее меню, а затем выберите «Создать счет»." }, - "secretPhraseWarning": { - "message": "Если вы выполняете восстановление с использованием другой секретной фразы для восстановления, ваш текущий кошелек, счета и активы будут удалены из этого приложения без возможности восстановления. Это действие нельзя отменить." - }, "secretRecoveryPhrase": { "message": "Секретная фраза для восстановления" }, diff --git a/app/_locales/sk/messages.json b/app/_locales/sk/messages.json index 5e818d979..396bdf170 100644 --- a/app/_locales/sk/messages.json +++ b/app/_locales/sk/messages.json @@ -734,9 +734,6 @@ "restore": { "message": "Obnoviť" }, - "restoreAccountWithSeed": { - "message": "Obnoviť účet pomocou seed frázy" - }, "revealSeedWords": { "message": "Zobrazit slova klíčové fráze" }, diff --git a/app/_locales/sl/messages.json b/app/_locales/sl/messages.json index d2cfea50b..490e9ed0c 100644 --- a/app/_locales/sl/messages.json +++ b/app/_locales/sl/messages.json @@ -753,9 +753,6 @@ "restore": { "message": "Obnovi" }, - "restoreAccountWithSeed": { - "message": "Obnovi račun z seed phrase" - }, "revealSeedWords": { "message": "Razkrij seed words" }, diff --git a/app/_locales/sr/messages.json b/app/_locales/sr/messages.json index c6c35376c..63a52f898 100644 --- a/app/_locales/sr/messages.json +++ b/app/_locales/sr/messages.json @@ -756,9 +756,6 @@ "restore": { "message": "Поново отвори" }, - "restoreAccountWithSeed": { - "message": "Povratite svoj nalog uz pomoć seed fraze" - }, "revealSeedWords": { "message": "Otkrivanje početnih reči" }, diff --git a/app/_locales/sv/messages.json b/app/_locales/sv/messages.json index 0000c4949..c3fa28d56 100644 --- a/app/_locales/sv/messages.json +++ b/app/_locales/sv/messages.json @@ -749,9 +749,6 @@ "restore": { "message": "Återställ" }, - "restoreAccountWithSeed": { - "message": "Återställ ditt konto med seedphrase" - }, "revealSeedWords": { "message": "Visa seed-ord" }, diff --git a/app/_locales/sw/messages.json b/app/_locales/sw/messages.json index 247318676..8c4eb4f27 100644 --- a/app/_locales/sw/messages.json +++ b/app/_locales/sw/messages.json @@ -743,9 +743,6 @@ "restore": { "message": "Rejesha" }, - "restoreAccountWithSeed": { - "message": "Rejesha Akaunti yako kwa kutumia Kirai Kianzio." - }, "revealSeedWords": { "message": "Onyesha Maneno ya Kianzio" }, diff --git a/app/_locales/tl/messages.json b/app/_locales/tl/messages.json index 20d2c1182..ac0209ba3 100644 --- a/app/_locales/tl/messages.json +++ b/app/_locales/tl/messages.json @@ -1273,19 +1273,12 @@ "importAccountError": { "message": "Error sa pag-import ng account." }, - "importAccountLinkText": { - "message": "i-import gamit ang Secret Recovery Phrase" - }, "importAccountMsg": { "message": "Ang mga na-import na account ay hindi mauugnay sa orihinal mong nagawang Secret Recovery Phrase ng MetaMask account. Matuto pa tungkol sa mga na-import account" }, "importAccountSeedPhrase": { "message": "Mag-import ng account gamit ang Secret Recovery Phrase" }, - "importAccountText": { - "message": "o $1", - "description": "$1 represents the text from `importAccountLinkText` as a link" - }, "importExistingWalletDescription": { "message": "Ilagay ang iyong Secret Recovery Phrase (kilala rin bilang Seed Phrase) na ibinigay sa iyo noong gumawa ka ng iyong wallet. $1", "description": "$1 is the words 'Learn More' from key 'learnMore', separated here so that it can be added as a link" @@ -2229,9 +2222,6 @@ "restore": { "message": "I-restore" }, - "restoreAccountWithSeed": { - "message": "I-restore ang iyong Account gamit ang Secret Recovery Phrase" - }, "restoreWalletPreferences": { "message": "Nakita ang backup ng iyong data mula sa $1. Gusto mo bang i-restore ang mga kagustuhan mo sa wallet?", "description": "$1 is the date at which the data was backed up" @@ -2299,9 +2289,6 @@ "secretPhrase": { "message": "Ang unang account lang sa wallet na ito ang awtomatikong maglo-load. Pagkatapos makumpleto ang prosesong ito, upang magdagdag ng mga karagdagang account, i-click ang drop down na menu, pagkatapos ay piliin ang Gumawa ng Account." }, - "secretPhraseWarning": { - "message": "Kapag nagre-restore ka gamit ang isa pang Secret Recovery Phrase, permanenteng aalisin sa app na ito ang iyong kasalukuyang wallet, mga account, at asset. Ang gawaing ito ay hindi pwedeng baguhin." - }, "secretRecoveryPhrase": { "message": "Secret Recovery Phrase" }, diff --git a/app/_locales/tr/messages.json b/app/_locales/tr/messages.json index a6d03b419..46afbf969 100644 --- a/app/_locales/tr/messages.json +++ b/app/_locales/tr/messages.json @@ -1273,19 +1273,12 @@ "importAccountError": { "message": "Hesap içe aktarılırken hata oluştu." }, - "importAccountLinkText": { - "message": "Gizli Kurtarma İfadesi kullanarak içe aktar" - }, "importAccountMsg": { "message": "İçe aktarılan hesaplar ilk olarak oluşturduğunuz MetaMask hesabı Gizli Kurtarma ifadenizle ilişkilendirilmez. İçe aktarılan hesaplar hakkında daha fazla bilgi edinin" }, "importAccountSeedPhrase": { "message": "Gizli Kurtarma İfadesi ile bir cüzdanı içe aktarın" }, - "importAccountText": { - "message": "veya $1", - "description": "$1 represents the text from `importAccountLinkText` as a link" - }, "importExistingWalletDescription": { "message": "Cüzdanınızı oluşturduğunuzda size verilen Gizli Kurtarma İfadenizi (başka bir deyişle Tohum İfadesi) girin. $1", "description": "$1 is the words 'Learn More' from key 'learnMore', separated here so that it can be added as a link" @@ -2229,9 +2222,6 @@ "restore": { "message": "Geri Yükle" }, - "restoreAccountWithSeed": { - "message": "Gizli Kurtarma İfadesi ile Hesabınızı geri yükleyin" - }, "restoreWalletPreferences": { "message": "Verilerinizin $1 tarihinden bir yedeği bulundu. Cüzdan tercihlerinizi geri yüklemek ister misiniz?", "description": "$1 is the date at which the data was backed up" @@ -2299,9 +2289,6 @@ "secretPhrase": { "message": "Sadece bu cüzdandaki ilk hesap otomatik olarak yüklenecektir. Bu işlem tamamlandıktan sonra ilave hesaplar eklemek için açılır menüye tıklayın ardından Hesap Oluştur seçeneğini seçin." }, - "secretPhraseWarning": { - "message": "Başka bir Gizli Kurtarma İfadesini kullanarak geri yükleme işlemi yaparsanız mevcut cüzdan, hesap ve varlıklarınız bu uygulamadan kalıcı olarak silinir. Bu işlem geri alınamaz." - }, "secretRecoveryPhrase": { "message": "Gizli Kurtarma İfadesi" }, diff --git a/app/_locales/uk/messages.json b/app/_locales/uk/messages.json index ec6b77371..ed521399c 100644 --- a/app/_locales/uk/messages.json +++ b/app/_locales/uk/messages.json @@ -765,9 +765,6 @@ "restore": { "message": "Відновити" }, - "restoreAccountWithSeed": { - "message": "Відновіть ваш обліковий запис за допомогою seed-фрази" - }, "revealSeedWords": { "message": "Показати мнемонічні слова" }, diff --git a/app/_locales/vi/messages.json b/app/_locales/vi/messages.json index 503ce3495..430004a1f 100644 --- a/app/_locales/vi/messages.json +++ b/app/_locales/vi/messages.json @@ -1273,19 +1273,12 @@ "importAccountError": { "message": "Lỗi khi nhập tài khoản." }, - "importAccountLinkText": { - "message": "nhập bằng Cụm mật khẩu khôi phục bí mật" - }, "importAccountMsg": { "message": "Tài khoản đã nhập sẽ không được liên kết với Cụm mật khẩu khôi phục bí mật cho tài khoản MetaMask đã tạo ban đầu của bạn. Tìm hiểu thêm về các tài khoản đã nhập" }, "importAccountSeedPhrase": { "message": "Nhập một ví bằng Cụm mật khẩu khôi phục bí mật" }, - "importAccountText": { - "message": "hoặc $1", - "description": "$1 represents the text from `importAccountLinkText` as a link" - }, "importExistingWalletDescription": { "message": "Nhập Cụm Mật Khẩu Khôi Phục Bí Mật (còn được gọi là Cụm Mật Khẩu Gốc) mà bạn được cấp khi tạo ví. $1", "description": "$1 is the words 'Learn More' from key 'learnMore', separated here so that it can be added as a link" @@ -2229,9 +2222,6 @@ "restore": { "message": "Khôi phục" }, - "restoreAccountWithSeed": { - "message": "Khôi phục tài khoản của bạn bằng cụm mật khẩu khôi phục bí mật" - }, "restoreWalletPreferences": { "message": "Đã tìm thấy bản sao lưu dữ liệu của bạn từ $1. Bạn có muốn khôi phục các tùy chọn ưu tiên trong ví của mình không?", "description": "$1 is the date at which the data was backed up" @@ -2299,9 +2289,6 @@ "secretPhrase": { "message": "Chỉ tự động tải tài khoản đầu tên trên ví. Sau khi hoàn tất quá trình này, để thêm tài khoản bổ sung, hãy nhấn vào trình đơn thả xuống và chọn Tạo tài khoản." }, - "secretPhraseWarning": { - "message": "Nếu bạn khôi phục bằng cách sử dụng một Cụm Mật Khẩu Khôi Phục Bí Mật khác, thì ví, tài khoản và tài sản hiện tại của bạn sẽ bị xóa khỏi ứng dụng này vĩnh viễn. Không thể hoàn tác hành động này." - }, "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 1e662c7b1..95099693c 100644 --- a/app/_locales/zh_CN/messages.json +++ b/app/_locales/zh_CN/messages.json @@ -1273,19 +1273,12 @@ "importAccountError": { "message": "导入帐户时出错。" }, - "importAccountLinkText": { - "message": "使用账户助记词导入" - }, "importAccountMsg": { "message": "导入的账户将不会与最初创建的 MetaMask 账户助记词相关联。了解更多有关导入账户的信息 。" }, "importAccountSeedPhrase": { "message": "使用账户助记词导入账户" }, - "importAccountText": { - "message": "或 $1", - "description": "$1 represents the text from `importAccountLinkText` as a link" - }, "importExistingWalletDescription": { "message": "输入您创建$1钱包时提供的保密恢复短语(或Seed Phrase)。", "description": "$1 is the words 'Learn More' from key 'learnMore', separated here so that it can be added as a link" @@ -2229,9 +2222,6 @@ "restore": { "message": "恢复" }, - "restoreAccountWithSeed": { - "message": "使用账户助记词恢复您的账户" - }, "restoreWalletPreferences": { "message": "已找到于 $1 的数据备份。您想恢复您的钱包设置吗?", "description": "$1 is the date at which the data was backed up" @@ -2299,9 +2289,6 @@ "secretPhrase": { "message": "只有这个钱包上的第一个帐户将自动加载。 完成此流程后,点击下拉菜单,然后选择创建账户。" }, - "secretPhraseWarning": { - "message": "如果您使用另一个账户助记词来还原,您当前的钱包、帐户和资产将永久从这个应用中移除。 此操作不能撤消。" - }, "secretRecoveryPhrase": { "message": "账户助记词" }, diff --git a/app/_locales/zh_TW/messages.json b/app/_locales/zh_TW/messages.json index 92d7e10b9..ba978532e 100644 --- a/app/_locales/zh_TW/messages.json +++ b/app/_locales/zh_TW/messages.json @@ -747,9 +747,6 @@ "resetAccountDescription": { "message": "重置帳戶將清除您的交易紀錄" }, - "restoreAccountWithSeed": { - "message": "透過助憶詞還原您的帳戶" - }, "revealSeedWords": { "message": "顯示助憶詞" }, diff --git a/test/e2e/metamask-ui.spec.js b/test/e2e/metamask-ui.spec.js index 7dbc96271..3600ec109 100644 --- a/test/e2e/metamask-ui.spec.js +++ b/test/e2e/metamask-ui.spec.js @@ -191,12 +191,9 @@ describe('MetaMask', function () { it('imports Secret Recovery Phrase', async function () { const restoreSeedLink = await driver.findClickableElement( - '.unlock-page__link--import', - ); - assert.equal( - await restoreSeedLink.getText(), - 'import using Secret Recovery Phrase', + '.unlock-page__link', ); + assert.equal(await restoreSeedLink.getText(), 'Forgot password?'); await restoreSeedLink.click(); await driver.delay(regularDelayMs); diff --git a/test/e2e/tests/metamask-responsive-ui.spec.js b/test/e2e/tests/metamask-responsive-ui.spec.js index 3bf8684fb..e01e5b307 100644 --- a/test/e2e/tests/metamask-responsive-ui.spec.js +++ b/test/e2e/tests/metamask-responsive-ui.spec.js @@ -164,12 +164,9 @@ describe('Metamask Responsive UI', function () { // Import Secret Recovery Phrase const restoreSeedLink = await driver.findClickableElement( - '.unlock-page__link--import', - ); - assert.equal( - await restoreSeedLink.getText(), - 'import using Secret Recovery Phrase', + '.unlock-page__link', ); + assert.equal(await restoreSeedLink.getText(), 'Forgot password?'); await restoreSeedLink.click(); await driver.fill( diff --git a/ui/helpers/constants/zendesk-url.js b/ui/helpers/constants/zendesk-url.js index d9d2e3d2b..7f84f8006 100644 --- a/ui/helpers/constants/zendesk-url.js +++ b/ui/helpers/constants/zendesk-url.js @@ -1,4 +1,10 @@ const ZENDESK_URLS = { + ADD_CUSTOM_TOKENS: + 'https://metamask.zendesk.com/hc/en-us/articles/360015489031', + ADD_MISSING_ACCOUNTS: + 'https://metamask.zendesk.com/hc/en-us/articles/360015489271', + IMPORT_ACCOUNTS: + 'https://metamask.zendesk.com/hc/en-us/articles/360015489331', TOKEN_SAFETY_PRACTICES: 'https://metamask.zendesk.com/hc/en-us/articles/4403988839451', }; diff --git a/ui/pages/keychains/index.scss b/ui/pages/keychains/index.scss index 914a40d69..219604084 100644 --- a/ui/pages/keychains/index.scss +++ b/ui/pages/keychains/index.scss @@ -57,35 +57,19 @@ } .import-account { - &__title { - @include H1; - - color: #1b344d; - margin-bottom: 10px; - } - &__back-button { @include Paragraph; margin-bottom: 18px; - color: #22232c; + color: var(--black); position: absolute; top: -25px; } - &__selector-label { - @include Paragraph; - - color: #1b344d; - } - - &__selector-typography { - line-height: 22px; - display: flex; - align-items: center; - color: #000; - margin-top: 18px; - margin-bottom: 16px; + &__link { + display: inline; + padding: 0; + font-size: inherit; } } diff --git a/ui/pages/keychains/restore-vault.js b/ui/pages/keychains/restore-vault.js index 5082a28de..a931a5ac8 100644 --- a/ui/pages/keychains/restore-vault.js +++ b/ui/pages/keychains/restore-vault.js @@ -8,6 +8,11 @@ import { } from '../../store/actions'; import { DEFAULT_ROUTE } from '../../helpers/constants/routes'; import CreateNewVault from '../../components/app/create-new-vault'; +import Button from '../../components/ui/button'; +import Box from '../../components/ui/box'; +import Typography from '../../components/ui/typography'; +import ZENDESK_URLS from '../../helpers/constants/zendesk-url'; +import { TYPOGRAPHY, COLORS } from '../../helpers/constants/design-system'; class RestoreVaultPage extends Component { static contextTypes = { @@ -51,9 +56,9 @@ class RestoreVaultPage extends Component { const { isLoading } = this.props; return ( -
-
-
+ + + { @@ -65,23 +70,57 @@ class RestoreVaultPage extends Component { > {`< ${t('back')}`} -
- {this.context.t('restoreAccountWithSeed')} -
-
- {this.context.t('secretPhrase')} -
-
- {this.context.t('secretPhraseWarning')} -
+ + {t('resetWallet')} + + + {t('resetWalletSubHeader')} + + + {t('resetWalletUsingSRP', [ + , + , + , + ])} + + + {t('resetWalletWarning')} + -
-
-
+ + + ); } } diff --git a/ui/pages/keychains/restore-vault.test.js b/ui/pages/keychains/restore-vault.test.js new file mode 100644 index 000000000..7fde0a8c9 --- /dev/null +++ b/ui/pages/keychains/restore-vault.test.js @@ -0,0 +1,44 @@ +import React from 'react'; +import sinon from 'sinon'; +import configureMockStore from 'redux-mock-store'; +import { renderWithProvider } from '../../../test/lib/render-helpers'; +import RestoreVaultPage from './restore-vault'; + +describe('Restore vault Component', () => { + it('clicks imports seed button', () => { + const props = { + history: { + push: sinon.spy(), + }, + }; + + const { getByText, getByRole, getAllByRole } = renderWithProvider( + , + configureMockStore()({ + metamask: { currentLocale: 'en' }, + appState: { isLoading: false }, + }), + ); + + expect(getByText('Reset Wallet')).toBeInTheDocument(); + expect( + getByText( + 'MetaMask does not keep a copy of your password. If you’re having trouble unlocking your account, you will need to reset your wallet. You can do this by providing the Secret Recovery Phrase you used when you set up your wallet.', + ), + ).toBeInTheDocument(); + expect( + getByText( + 'This action will delete your current wallet and Secret Recovery Phrase from this device, along with the list of accounts you’ve curated. After resetting with a Secret Recovery Phrase, you’ll see a list of accounts based on the Secret Recovery Phrase you use to reset. This new list will automatically include accounts that have a balance. You’ll also be able to created previously. Custom accounts that you’ve imported will need to be , and any custom tokens you’ve added to an account will need to be as well.', + ), + ).toBeInTheDocument(); + expect( + getByRole('link', { name: 're-add any other accounts' }), + ).toBeInTheDocument(); + expect(getAllByRole('link', { name: 're-added' })).toHaveLength(2); + expect( + getByText( + 'Make sure you’re using the correct Secret Recovery Phrase before proceeding. You will not be able to undo this.', + ), + ).toBeInTheDocument(); + }); +}); diff --git a/ui/pages/unlock-page/README.mdx b/ui/pages/unlock-page/README.mdx index 292d21265..bcd2e1360 100644 --- a/ui/pages/unlock-page/README.mdx +++ b/ui/pages/unlock-page/README.mdx @@ -21,7 +21,7 @@ ArgsTable doesn't work with SectionShape | -------------------------- | -------------------------------------------------------------------------- | | `history` | History router for redirect after action `object` | | `isUnlocked` | If isUnlocked is true will redirect to most recent route in history `bool` | -| `onRestore` | onClick handler for "import using Secret Recovery Phrase" link `func` | +| `onRestore` | onClick handler for "Forgot password?" link `func` | | `onSubmit` | onSumbit handler when form is submitted `func` | | `forceUpdateMetamaskState` | Force update metamask data state `func` | | `showOptInModal` | Event handler to show metametrics modal `func` | diff --git a/ui/pages/unlock-page/index.scss b/ui/pages/unlock-page/index.scss index bf83eadc8..6d816a21f 100644 --- a/ui/pages/unlock-page/index.scss +++ b/ui/pages/unlock-page/index.scss @@ -38,18 +38,11 @@ width: 100%; text-align: center; font-size: 0.75rem; - - button { - font-weight: bold; - border-radius: 100px; - } } &__link { - cursor: pointer; - background-color: unset; - color: var(--Blue-500); font-size: unset; + font-weight: bold; } &__support { diff --git a/ui/pages/unlock-page/unlock-page.component.js b/ui/pages/unlock-page/unlock-page.component.js index 50dda7c8b..b64a024ed 100644 --- a/ui/pages/unlock-page/unlock-page.component.js +++ b/ui/pages/unlock-page/unlock-page.component.js @@ -24,7 +24,7 @@ export default class UnlockPage extends Component { */ isUnlocked: PropTypes.bool, /** - * onClick handler for "import using Secret Recovery Phrase" link + * onClick handler for "Forgot password?" link */ onRestore: PropTypes.func, /** @@ -184,15 +184,14 @@ export default class UnlockPage extends Component { {this.renderSubmitButton()}
- {t('importAccountText', [ - , - ])} +
{t('needHelp', [ diff --git a/ui/pages/unlock-page/unlock-page.component.test.js b/ui/pages/unlock-page/unlock-page.component.test.js index 5029f6a0e..40a3d265b 100644 --- a/ui/pages/unlock-page/unlock-page.component.test.js +++ b/ui/pages/unlock-page/unlock-page.component.test.js @@ -23,7 +23,7 @@ describe('Unlock Page Component', () => { configureMockStore()({ metamask: { currentLocale: 'en' } }), ); - fireEvent.click(getByText('import using Secret Recovery Phrase')); + fireEvent.click(getByText('Forgot password?')); expect(props.onRestore.calledOnce).toStrictEqual(true); }); }); From 8eb21304877acb0c13c606a13602a332ca239abe Mon Sep 17 00:00:00 2001 From: ryanml Date: Tue, 22 Feb 2022 08:59:31 -0700 Subject: [PATCH 004/105] Harden keyring type check in EthOverview (#13711) --- ui/components/app/wallet-overview/eth-overview.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/components/app/wallet-overview/eth-overview.js b/ui/components/app/wallet-overview/eth-overview.js index 1ecfe4719..001f84694 100644 --- a/ui/components/app/wallet-overview/eth-overview.js +++ b/ui/components/app/wallet-overview/eth-overview.js @@ -55,7 +55,7 @@ const EthOverview = ({ className }) => { }); const history = useHistory(); const keyring = useSelector(getCurrentKeyring); - const usingHardwareWallet = isHardwareKeyring(keyring.type); + const usingHardwareWallet = isHardwareKeyring(keyring?.type); const balanceIsCached = useSelector(isBalanceCached); const showFiat = useSelector(getShouldShowFiat); const selectedAccount = useSelector(getSelectedAccount); From 575cedf2aa776958a08ee21d46b474080e82a19e Mon Sep 17 00:00:00 2001 From: Archana Sankaranarayanan Date: Tue, 22 Feb 2022 08:07:01 -0800 Subject: [PATCH 005/105] Shows the sign and cancel button fully in signature page (#13686) --- .../app/signature-request/signature-request-message/index.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/components/app/signature-request/signature-request-message/index.scss b/ui/components/app/signature-request/signature-request-message/index.scss index 14f8d4f39..6673d38f7 100644 --- a/ui/components/app/signature-request/signature-request-message/index.scss +++ b/ui/components/app/signature-request/signature-request-message/index.scss @@ -1,7 +1,7 @@ .signature-request-message { flex: 1 60%; display: flex; - max-height: 250px; + max-height: 230px; flex-direction: column; position: relative; From 30b2afe7bc1819cf6e7d8acf1fbeafcc188f1441 Mon Sep 17 00:00:00 2001 From: PeterYinusa <53189696+PeterYinusa@users.noreply.github.com> Date: Tue, 22 Feb 2022 16:48:12 +0000 Subject: [PATCH 006/105] E2e phishing detection (#13704) * phishing detection test * remove unused arg --- development/mock-e2e.js | 4 +- test/e2e/helpers.js | 5 ++- test/e2e/tests/phishing-detection.spec.js | 53 +++++++++++++++++++++++ 3 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 test/e2e/tests/phishing-detection.spec.js diff --git a/development/mock-e2e.js b/development/mock-e2e.js index 1e466d36e..2eb05e75e 100644 --- a/development/mock-e2e.js +++ b/development/mock-e2e.js @@ -1,4 +1,4 @@ -function setupMocking(server) { +function setupMocking(server, testSpecificMock) { server.forAnyRequest().thenPassThrough(); server @@ -27,6 +27,8 @@ function setupMocking(server) { }, }; }); + + testSpecificMock(server); } module.exports = { setupMocking }; diff --git a/test/e2e/helpers.js b/test/e2e/helpers.js index 148964b03..a3db2d3e7 100644 --- a/test/e2e/helpers.js +++ b/test/e2e/helpers.js @@ -29,6 +29,9 @@ async function withFixtures(options, testSuite) { title, failOnConsoleError = true, dappPath = undefined, + testSpecificMock = function () { + // do nothing. + }, } = options; const fixtureServer = new FixtureServer(); const ganacheServer = new Ganache(); @@ -89,8 +92,8 @@ async function withFixtures(options, testSuite) { } const https = await mockttp.generateCACertificate(); mockServer = mockttp.getLocal({ https }); + setupMocking(mockServer, testSpecificMock); await mockServer.start(8000); - setupMocking(mockServer); if ( process.env.SELENIUM_BROWSER === 'chrome' && process.env.CI === 'true' diff --git a/test/e2e/tests/phishing-detection.spec.js b/test/e2e/tests/phishing-detection.spec.js new file mode 100644 index 000000000..0cacf1e23 --- /dev/null +++ b/test/e2e/tests/phishing-detection.spec.js @@ -0,0 +1,53 @@ +const { strict: assert } = require('assert'); +const { convertToHexValue, withFixtures } = require('../helpers'); + +describe('Phishing Detection', function () { + function mockPhishingDetection(mockServer) { + mockServer + .forGet( + 'https://cdn.jsdelivr.net/gh/MetaMask/eth-phishing-detect@master/src/config.json', + ) + .thenCallback(() => { + return { + headers: { 'Access-Control-Allow-Origin': '*' }, + statusCode: 200, + json: { + version: 2, + tolerance: 2, + fuzzylist: [], + whitelist: [], + blacklist: ['example.com'], + }, + }; + }); + } + const ganacheOptions = { + accounts: [ + { + secretKey: + '0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC', + balance: convertToHexValue(25000000000000000000), + }, + ], + }; + it('should display the MetaMask Phishing Detection page', async function () { + await withFixtures( + { + fixtures: 'imported-account', + ganacheOptions, + title: this.test.title, + testSpecificMock: mockPhishingDetection, + }, + async ({ driver }) => { + await driver.navigate(); + await driver.fill('#password', 'correct horse battery staple'); + await driver.press('#password', driver.Key.ENTER); + await driver.navigate(); + await driver.openNewPage('http://example.com'); + await driver.waitForSelector({ text: 'continuing at your own risk' }); + const header = await driver.findElement('h1'); + assert.equal(await header.getText(), 'MetaMask Phishing Detection'); + }, + ); + }); +}); From 2bab7ada8f4062c95facf50abb4e5c6e27e9afa9 Mon Sep 17 00:00:00 2001 From: Niranjana Binoy <43930900+NiranjanaBinoy@users.noreply.github.com> Date: Tue, 22 Feb 2022 12:59:13 -0500 Subject: [PATCH 007/105] Deleting transactions from currentNetworkTxnList based on unique address along with nonce and chainId (#13669) --- app/scripts/controllers/transactions/tx-state-manager.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/scripts/controllers/transactions/tx-state-manager.js b/app/scripts/controllers/transactions/tx-state-manager.js index 879aac56c..d59b5a058 100644 --- a/app/scripts/controllers/transactions/tx-state-manager.js +++ b/app/scripts/controllers/transactions/tx-state-manager.js @@ -249,9 +249,9 @@ export default class TransactionStateManager extends EventEmitter { const txsToDelete = transactions .reverse() .filter((tx) => { - const { nonce } = tx.txParams; + const { nonce, from } = tx.txParams; const { chainId, metamaskNetworkId, status } = tx; - const key = `${nonce}-${chainId ?? metamaskNetworkId}`; + const key = `${nonce}-${chainId ?? metamaskNetworkId}-${from}`; if (nonceNetworkSet.has(key)) { return false; } else if ( From 355e5ab399c42a718a2154e25b6085c87f5dcf8c Mon Sep 17 00:00:00 2001 From: AndreasGassmann Date: Tue, 22 Feb 2022 16:00:51 -0300 Subject: [PATCH 008/105] Add AirGap Vault detail links (#13650) --- .github/ISSUE_TEMPLATE/bug-report.yml | 1 + app/_locales/en/messages.json | 8 ++++++- .../connect-hardware/select-hardware.js | 24 +++++++++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml index cb38aaf16..889bd9d1b 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yml +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -89,6 +89,7 @@ body: - Trezor - Keystone - GridPlus Lattice1 + - AirGap Vault - Other (please elaborate in the "Additional Context" section) - type: textarea id: additional diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 237a83d92..a4350f384 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -42,7 +42,7 @@ "message": "QR-based HW Wallet" }, "QRHardwareWalletSteps2Description": { - "message": "AirGap Vault & Ngrave (Coming Soon)" + "message": "Ngrave (Coming Soon)" }, "about": { "message": "About" @@ -191,6 +191,12 @@ "aggregatorFeeCost": { "message": "Aggregator network fee" }, + "airgapVault": { + "message": "AirGap Vault" + }, + "airgapVaultTutorial": { + "message": " (Tutorials)" + }, "alertDisableTooltip": { "message": "This can be changed in \"Settings > Alerts\"" }, diff --git a/ui/pages/create-account/connect-hardware/select-hardware.js b/ui/pages/create-account/connect-hardware/select-hardware.js index 375b33f23..be4d1326b 100644 --- a/ui/pages/create-account/connect-hardware/select-hardware.js +++ b/ui/pages/create-account/connect-hardware/select-hardware.js @@ -350,6 +350,30 @@ export default class SelectHardware extends Component { ), }, + { + message: ( + <> + + {this.context.t('airgapVault')} + + + {this.context.t('airgapVaultTutorial')} + + + ), + }, { message: this.context.t('QRHardwareWalletSteps2Description'), }, From b92aab1e87b2cec5628b28556160cc4f8e99f6c5 Mon Sep 17 00:00:00 2001 From: Erik Marks <25517051+rekmarks@users.noreply.github.com> Date: Tue, 22 Feb 2022 11:05:22 -0800 Subject: [PATCH 009/105] Update manual build instructions in documentation (#13508) Updates the "add custom build" instructions for Chrome and Firefox, which were outdated and/or poorly written. --- docs/add-to-chrome.md | 11 +++++++---- docs/add-to-firefox.md | 15 ++++++--------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/add-to-chrome.md b/docs/add-to-chrome.md index 82c1afed3..9e4d8c4a4 100644 --- a/docs/add-to-chrome.md +++ b/docs/add-to-chrome.md @@ -2,13 +2,16 @@ ![Load dev build](./load-dev-build-chrome.gif) +* Create a local build of MetaMask using your preferred method. + * You can find build instructions in the [readme](https://github.com/MetaMask/metamask-extension#readme). * Open `Settings` > `Extensions`. + * Or go straight to [chrome://extensions](chrome://extensions). * Check "Developer mode". -* Alternatively, use the URL `chrome://extensions/` in your address bar * At the top, click `Load Unpacked Extension`. -* Navigate to your `metamask-plugin/dist/chrome` folder. +* Navigate to your `metamask-extension/dist/chrome` folder. * Click `Select`. * Change to your locale via `chrome://settings/languages` -* Restart the browser and test the plugin in your locale +* Restart the browser and test the extension in your locale -You now have the plugin, and can click 'inspect views: background plugin' to view its dev console. +Your dev build is now added to Chrome, and you can click `Inspect views +background.html` in its card on the extension settings page to view its dev console. diff --git a/docs/add-to-firefox.md b/docs/add-to-firefox.md index 20810f9a6..424af6317 100644 --- a/docs/add-to-firefox.md +++ b/docs/add-to-firefox.md @@ -1,14 +1,11 @@ # Add Custom Build to Firefox -Go to the url `about:debugging#addons`. - -Click the button `Load Temporary Add-On`. - -Select the file `dist/firefox/manifest.json`. - -You can optionally enable debugging, and click `Debug`, for a console window that logs all of Metamask's processes to a single console. +* Create a local build of MetaMask using your preferred method. + * You can find build instructions in the [readme](https://github.com/MetaMask/metamask-extension#readme). +* Go to the url `about:debugging#addons`. +* Click the button `Load Temporary Add-On`. +* Select the file `metamask-extension/dist/firefox/manifest.json`. +* You can optionally enable debugging, and click `Debug`, for a console window that logs all of Metamask's processes to a single console. If you have problems debugging, try connecting to the IRC channel `#webextensions` on `irc.mozilla.org`. - For longer questions, use the StackOverflow tag `firefox-addons`. - From 6ec07b8121fe69b33fb1cf0e67b4d19582877bf4 Mon Sep 17 00:00:00 2001 From: Guillaume Roux Date: Tue, 22 Feb 2022 20:50:28 +0100 Subject: [PATCH 010/105] Fix permission screen overflow (#13592) --- ui/components/ui/account-list/index.scss | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui/components/ui/account-list/index.scss b/ui/components/ui/account-list/index.scss index a523c5341..712425f8e 100644 --- a/ui/components/ui/account-list/index.scss +++ b/ui/components/ui/account-list/index.scss @@ -35,11 +35,12 @@ &__wrapper { width: 92%; display: flex; + overflow: hidden; } &__list { flex: 2 1 0; - width: 92%; + width: 100%; max-height: max-content; border: 1px solid #d0d5da; box-sizing: border-box; From 96b82349a003ca70e179cf5f88334e18679a649f Mon Sep 17 00:00:00 2001 From: Dan J Miller Date: Tue, 22 Feb 2022 16:22:58 -0330 Subject: [PATCH 011/105] Add EIP-712 support for Trezor (#13693) * Add EIP-712 support for Trezor Co-authored-by: alisinabh Co-authored-by: matejcik Co-authored-by: Brandon Noad * Update eth-trezor-keyring version Co-authored-by: Alois Klink Co-authored-by: alisinabh Co-authored-by: matejcik Co-authored-by: Brandon Noad --- lavamoat/browserify/beta/policy.json | 16 +- lavamoat/browserify/flask/policy.json | 16 +- lavamoat/browserify/main/policy.json | 16 +- lavamoat/build-system/policy.json | 201 -------------------------- package.json | 5 +- yarn.lock | 179 +++++++++++++---------- 6 files changed, 142 insertions(+), 291 deletions(-) diff --git a/lavamoat/browserify/beta/policy.json b/lavamoat/browserify/beta/policy.json index a3af16261..41925f129 100644 --- a/lavamoat/browserify/beta/policy.json +++ b/lavamoat/browserify/beta/policy.json @@ -597,6 +597,16 @@ "hdkey": true } }, + "@metamask/eth-sig-util": { + "packages": { + "buffer": true, + "ethereumjs-abi": true, + "ethereumjs-util": true, + "ethjs-util": true, + "tweetnacl": true, + "tweetnacl-util": true + } + }, "@metamask/eth-token-tracker": { "globals": { "console.warn": true @@ -4970,7 +4980,6 @@ "document.createTextNode": true, "document.getElementById": true, "document.querySelectorAll": true, - "fetch": true, "location": true, "navigator": true, "open": true, @@ -4980,8 +4989,9 @@ }, "packages": { "@babel/runtime": true, - "events": true, - "whatwg-fetch": true + "@metamask/eth-sig-util": true, + "cross-fetch": true, + "events": true } }, "truncate-utf8-bytes": { diff --git a/lavamoat/browserify/flask/policy.json b/lavamoat/browserify/flask/policy.json index c8c9633ff..f540236ba 100644 --- a/lavamoat/browserify/flask/policy.json +++ b/lavamoat/browserify/flask/policy.json @@ -597,6 +597,16 @@ "hdkey": true } }, + "@metamask/eth-sig-util": { + "packages": { + "buffer": true, + "ethereumjs-abi": true, + "ethereumjs-util": true, + "ethjs-util": true, + "tweetnacl": true, + "tweetnacl-util": true + } + }, "@metamask/eth-token-tracker": { "globals": { "console.warn": true @@ -4989,7 +4999,6 @@ "document.createTextNode": true, "document.getElementById": true, "document.querySelectorAll": true, - "fetch": true, "location": true, "navigator": true, "open": true, @@ -4999,8 +5008,9 @@ }, "packages": { "@babel/runtime": true, - "events": true, - "whatwg-fetch": true + "@metamask/eth-sig-util": true, + "cross-fetch": true, + "events": true } }, "truncate-utf8-bytes": { diff --git a/lavamoat/browserify/main/policy.json b/lavamoat/browserify/main/policy.json index a3af16261..41925f129 100644 --- a/lavamoat/browserify/main/policy.json +++ b/lavamoat/browserify/main/policy.json @@ -597,6 +597,16 @@ "hdkey": true } }, + "@metamask/eth-sig-util": { + "packages": { + "buffer": true, + "ethereumjs-abi": true, + "ethereumjs-util": true, + "ethjs-util": true, + "tweetnacl": true, + "tweetnacl-util": true + } + }, "@metamask/eth-token-tracker": { "globals": { "console.warn": true @@ -4970,7 +4980,6 @@ "document.createTextNode": true, "document.getElementById": true, "document.querySelectorAll": true, - "fetch": true, "location": true, "navigator": true, "open": true, @@ -4980,8 +4989,9 @@ }, "packages": { "@babel/runtime": true, - "events": true, - "whatwg-fetch": true + "@metamask/eth-sig-util": true, + "cross-fetch": true, + "events": true } }, "truncate-utf8-bytes": { diff --git a/lavamoat/build-system/policy.json b/lavamoat/build-system/policy.json index 7d6167325..e95acc4d6 100644 --- a/lavamoat/build-system/policy.json +++ b/lavamoat/build-system/policy.json @@ -1052,16 +1052,6 @@ "buffer-equal": true } }, - "are-we-there-yet": { - "builtin": { - "events.EventEmitter": true, - "util.inherits": true - }, - "packages": { - "delegates": true, - "readable-stream": true - } - }, "arr-diff": { "packages": { "arr-flatten": true, @@ -1470,7 +1460,6 @@ "anymatch": true, "async-each": true, "braces": true, - "fsevents": true, "glob-parent": true, "inherits": true, "is-binary-path": true, @@ -1737,16 +1726,6 @@ "through2": true } }, - "detect-libc": { - "builtin": { - "child_process.spawnSync": true, - "fs.readdirSync": true, - "os.platform": true - }, - "globals": { - "process.env": true - } - }, "detective": { "packages": { "acorn-node": true, @@ -2450,45 +2429,6 @@ "process.version": true } }, - "fsevents": { - "builtin": { - "events.EventEmitter": true, - "fs.stat": true, - "path.join": true, - "util.inherits": true - }, - "globals": { - "__dirname": true, - "process.nextTick": true, - "process.platform": true, - "setImmediate": true - }, - "native": true, - "packages": { - "node-pre-gyp": true - } - }, - "gauge": { - "builtin": { - "util.format": true - }, - "globals": { - "clearInterval": true, - "process": true, - "setImmediate": true, - "setInterval": true - }, - "packages": { - "aproba": true, - "console-control-strings": true, - "has-unicode": true, - "object-assign": true, - "signal-exit": true, - "string-width": true, - "strip-ansi": true, - "wide-align": true - } - }, "get-assigned-identifiers": { "builtin": { "assert.equal": true @@ -2867,16 +2807,6 @@ "process.argv": true } }, - "has-unicode": { - "builtin": { - "os.type": true - }, - "globals": { - "process.env.LANG": true, - "process.env.LC_ALL": true, - "process.env.LC_CTYPE": true - } - }, "has-value": { "packages": { "get-value": true, @@ -3048,11 +2978,6 @@ "is-plain-object": true } }, - "is-fullwidth-code-point": { - "packages": { - "number-is-nan": true - } - }, "is-glob": { "packages": { "is-extglob": true @@ -3583,56 +3508,6 @@ "setTimeout": true } }, - "node-pre-gyp": { - "builtin": { - "events.EventEmitter": true, - "fs.existsSync": true, - "fs.readFileSync": true, - "fs.renameSync": true, - "path.dirname": true, - "path.existsSync": true, - "path.join": true, - "path.resolve": true, - "url.parse": true, - "url.resolve": true, - "util.inherits": true - }, - "globals": { - "__dirname": true, - "console.log": true, - "process.arch": true, - "process.cwd": true, - "process.env": true, - "process.platform": true, - "process.version.substr": true, - "process.versions": true - }, - "packages": { - "detect-libc": true, - "nopt": true, - "npmlog": true, - "rimraf": true, - "semver": true - } - }, - "nopt": { - "builtin": { - "path": true, - "stream.Stream": true, - "url": true - }, - "globals": { - "console": true, - "process.argv": true, - "process.env.DEBUG_NOPT": true, - "process.env.NOPT_DEBUG": true, - "process.platform": true - }, - "packages": { - "abbrev": true, - "osenv": true - } - }, "normalize-package-data": { "builtin": { "url.parse": true, @@ -3660,22 +3535,6 @@ "once": true } }, - "npmlog": { - "builtin": { - "events.EventEmitter": true, - "util": true - }, - "globals": { - "process.nextTick": true, - "process.stderr": true - }, - "packages": { - "are-we-there-yet": true, - "console-control-strings": true, - "gauge": true, - "set-blocking": true - } - }, "object-copy": { "packages": { "copy-descriptor": true, @@ -3757,54 +3616,6 @@ "readable-stream": true } }, - "os-homedir": { - "builtin": { - "os.homedir": true - }, - "globals": { - "process.env": true, - "process.getuid": true, - "process.platform": true - } - }, - "os-tmpdir": { - "globals": { - "process.env.SystemRoot": true, - "process.env.TEMP": true, - "process.env.TMP": true, - "process.env.TMPDIR": true, - "process.env.windir": true, - "process.platform": true - } - }, - "osenv": { - "builtin": { - "child_process.exec": true, - "path": true - }, - "globals": { - "process.env.COMPUTERNAME": true, - "process.env.ComSpec": true, - "process.env.EDITOR": true, - "process.env.HOSTNAME": true, - "process.env.PATH": true, - "process.env.PROMPT": true, - "process.env.PS1": true, - "process.env.Path": true, - "process.env.SHELL": true, - "process.env.USER": true, - "process.env.USERDOMAIN": true, - "process.env.USERNAME": true, - "process.env.VISUAL": true, - "process.env.path": true, - "process.nextTick": true, - "process.platform": true - }, - "packages": { - "os-homedir": true, - "os-tmpdir": true - } - }, "p-limit": { "packages": { "p-try": true @@ -4514,12 +4325,6 @@ "lru-cache": true } }, - "set-blocking": { - "globals": { - "process.stderr": true, - "process.stdout": true - } - }, "set-value": { "packages": { "extend-shallow": true, @@ -4783,7 +4588,6 @@ }, "string-width": { "packages": { - "code-point-at": true, "emoji-regex": true, "is-fullwidth-code-point": true, "strip-ansi": true @@ -5436,11 +5240,6 @@ "isexe": true } }, - "wide-align": { - "packages": { - "string-width": true - } - }, "write": { "builtin": { "fs.createWriteStream": true, diff --git a/package.json b/package.json index ee4d7e2cf..e131317e8 100644 --- a/package.json +++ b/package.json @@ -155,7 +155,7 @@ "eth-query": "^2.1.2", "eth-rpc-errors": "^4.0.2", "eth-sig-util": "^3.0.0", - "eth-trezor-keyring": "^0.9.1", + "eth-trezor-keyring": "^0.10.0", "ethereum-ens-network-map": "^1.0.2", "ethereumjs-abi": "^0.6.4", "ethereumjs-util": "^7.0.10", @@ -415,7 +415,8 @@ "ganache>keccak": false, "ganache>leveldown": false, "geckodriver": true, - "react-devtools>electron": true + "react-devtools>electron": true, + "eth-trezor-keyring>trezor-connect>@trezor/transport>protobufjs": false } } } diff --git a/yarn.lock b/yarn.lock index eee361216..5cd92a853 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2737,6 +2737,17 @@ ethereumjs-util "^7.0.9" hdkey "0.8.0" +"@metamask/eth-sig-util@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@metamask/eth-sig-util/-/eth-sig-util-4.0.0.tgz#11553ba06de0d1352332c1bde28c8edd00e0dcf6" + integrity sha512-LczOjjxY4A7XYloxzyxJIHONELmUxVZncpOLoClpEcTiebiVdM46KRPYXGuULro9oNNR2xdVx3yoKiQjdfWmoA== + dependencies: + ethereumjs-abi "^0.6.8" + ethereumjs-util "^6.2.1" + ethjs-util "^0.1.6" + tweetnacl "^1.0.3" + tweetnacl-util "^0.15.1" + "@metamask/eth-token-tracker@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@metamask/eth-token-tracker/-/eth-token-tracker-4.0.0.tgz#f3129855873a857ef675d4c28f532be684241b61" @@ -4080,31 +4091,41 @@ resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== -"@trezor/blockchain-link@^1.0.17": - version "1.0.17" - resolved "https://registry.yarnpkg.com/@trezor/blockchain-link/-/blockchain-link-1.0.17.tgz#3155b44ee9beb71326986d404385ede673519b3c" - integrity sha512-o1ZmZVdhXM8K4OY61A6l/pvmasC6OTGDeDliVlVnNdy3eVDVlnEivQVnMD7CvQr0Crxl1Flox0mp90Ljp/DIkA== +"@trezor/blockchain-link@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@trezor/blockchain-link/-/blockchain-link-1.1.0.tgz#065d18d7c948c4de45437fc2178d9e26a7c2304b" + integrity sha512-tH3hLG54VZ52Y6Fxdw51kqORbuzUSt1VReISj/oZROMexmDDCRgFR14/wxasjHp94XRYrx8777Boa1qrpAos4w== dependencies: bignumber.js "^9.0.1" es6-promise "^4.2.8" events "^3.2.0" - ripple-lib "1.8.2" + ripple-lib "1.10.0" tiny-worker "^2.3.0" ws "^7.4.0" -"@trezor/connect-common@^0.0.3": - version "0.0.3" - resolved "https://registry.yarnpkg.com/@trezor/connect-common/-/connect-common-0.0.3.tgz#d136453d259d24f9200daf72d0184594d56e92e2" - integrity sha512-oc4K/Ve2e1o3R1M0QAexLYN7aOepeSlcO5NVP/cfxBA+36cApZrRSy6kbs3OB2uc15tua5qkmYEDpJ3QSnZarg== +"@trezor/connect-common@0.0.4": + version "0.0.4" + resolved "https://registry.yarnpkg.com/@trezor/connect-common/-/connect-common-0.0.4.tgz#0df221685272544770399aca1c624b037870e310" + integrity sha512-uE8t4JCwHVn224yj3cV4jLu8i5JAGDxzkBu8Als1UFs/Zt8Sfl5xwIqeTSvhXx5x+VXe+uxt2CXX3AcrKTxN3g== -"@trezor/rollout@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@trezor/rollout/-/rollout-1.2.0.tgz#827316debc5cf2e8af2210900eb49540ea2753d1" - integrity sha512-R8/M8PsGQUndHlyETZ5+mPOveaJSTlEgmAovY7Ifm3quvk8nV5ByWL1LUJ8IYUgir209EWDWSKWfa5RWoon8IA== +"@trezor/rollout@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@trezor/rollout/-/rollout-1.2.1.tgz#47c81186768bbb0514d27b386d92a719c290ab5e" + integrity sha512-BDYtPE+rl4QKilGzb3lGxv17+lA5rou04zr1DdWaDqazyvfPW0fj46us4A4WLWptGVL+gfXtn0ZGqgsxHLBm7A== dependencies: - cross-fetch "^3.0.6" + cross-fetch "^3.1.4" runtypes "^5.0.1" +"@trezor/transport@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@trezor/transport/-/transport-1.0.1.tgz#bf0d11a44d1220fe1486e7679370920f9f60afa4" + integrity sha512-JWjA3QlVhcn6zPJFDt80Ayqj5+zrY9m9jzWVd9sceFKYTUuCrsuhze1ho0CBlXLm8GF2/CzFijGbo6O93aL23Q== + dependencies: + bytebuffer "^5.0.1" + json-stable-stringify "^1.0.1" + long "^4.0.0" + protobufjs "^6.11.2" + "@trezor/utxo-lib@1.0.0-beta.10": version "1.0.0-beta.10" resolved "https://registry.yarnpkg.com/@trezor/utxo-lib/-/utxo-lib-1.0.0-beta.10.tgz#93f16ce607d94e50f8338a75ca8a3710f106c20e" @@ -6384,10 +6405,10 @@ base-x@3.0.4: dependencies: safe-buffer "^5.0.1" -base-x@3.0.8, base-x@^3.0.2, base-x@^3.0.8: - version "3.0.8" - resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.8.tgz#1e1106c2537f0162e8b52474a557ebb09000018d" - integrity sha512-Rl/1AWP4J/zRrk54hhlxH4drNxPJXYUaKffODVI53/dAsV4t9fBxyxYKAVPU1XBHxYwOWP9h9H0hM2MVw4YfJA== +base-x@3.0.9, base-x@^3.0.2, base-x@^3.0.8: + version "3.0.9" + resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.9.tgz#6349aaabb58526332de9f60995e548a53fe21320" + integrity sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ== dependencies: safe-buffer "^5.0.1" @@ -6515,6 +6536,11 @@ big-integer@1.6.36: resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.36.tgz#78631076265d4ae3555c04f85e7d9d2f3a071a36" integrity sha512-t70bfa7HYEA1D9idDbmuv7YbsbVkQ+Hp+8KFSul4aE5e/i1bjCNIRYJZlA8Q8p0r9T8cF/RVvwUgRA//FydEyg== +big-integer@^1.6.48: + version "1.6.51" + resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.51.tgz#0df92a5d9880560d3ff2d5fd20245c889d130686" + integrity sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg== + big.js@^5.1.2, big.js@^5.2.2: version "5.2.2" resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" @@ -7272,6 +7298,14 @@ buffer-xor@^1.0.3: resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk= +buffer@5.6.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.6.0.tgz#a31749dc7d81d84db08abf937b6b8c4033f62786" + integrity sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw== + dependencies: + base64-js "^1.0.2" + ieee754 "^1.1.4" + buffer@^4.3.0: version "4.9.1" resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.1.tgz#6d1bb601b07a4efced97094132093027c95bc298" @@ -8751,10 +8785,10 @@ cross-fetch@^2.1.0: node-fetch "2.1.2" whatwg-fetch "2.0.4" -cross-fetch@^3.0.6: - version "3.0.6" - resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.0.6.tgz#3a4040bc8941e653e0e9cf17f29ebcd177d3365c" - integrity sha512-KBPUbqgFjzWlVcURG+Svp9TlhA5uliYtiNx/0r8nv0pdypeQCRJ9IaSIc3q/x3q8t3F75cHuwxVql1HFGHCNJQ== +cross-fetch@^3.1.4: + version "3.1.4" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.4.tgz#9723f3a3a247bf8b89039f3a380a9244e8fa2f39" + integrity sha512-1eAtFWdIubi6T4XPy6ei9iUFoKpUkIF971QLN8lIvvvwueI65+Nw5haMNKUwfJxabqlIIDODJKGrQ66gxC0PbQ== dependencies: node-fetch "2.6.1" @@ -11151,15 +11185,16 @@ eth-simple-keyring@^4.2.0: ethereumjs-wallet "^1.0.1" events "^1.1.1" -eth-trezor-keyring@^0.9.1: - version "0.9.1" - resolved "https://registry.yarnpkg.com/eth-trezor-keyring/-/eth-trezor-keyring-0.9.1.tgz#10d78308c71966eadf94acc8acf7f7136311f31f" - integrity sha512-27k4pjHFG6z5F1dhpTfORONgtAPrF0BR+Qr+ygjYrjchmERJKkrgyqnJqkSGtHF6+XrgI8pKYiDTjo6CBPHVKw== +eth-trezor-keyring@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/eth-trezor-keyring/-/eth-trezor-keyring-0.10.0.tgz#a7dcf3343730897359e9ec7517cfd90f130cfbb9" + integrity sha512-QGwSMM+Bv0MLtsJLrVfo7oqWzzvW1L6TS34EhrP56R0Io88rAJoEyE71Qpim1akKlF/VmJ6v04yXWgUPrTX2vg== dependencies: "@ethereumjs/tx" "^3.2.1" + "@metamask/eth-sig-util" "^4.0.0" ethereumjs-util "^7.0.9" hdkey "0.8.0" - trezor-connect "8.2.3-extended" + trezor-connect "8.2.6-extended" ethereum-bloom-filters@^1.0.6: version "1.0.7" @@ -11284,7 +11319,7 @@ ethereumjs-util@^5.0.0, ethereumjs-util@^5.1.1, ethereumjs-util@^5.1.2, ethereum safe-buffer "^5.1.1" secp256k1 "^3.0.1" -ethereumjs-util@^6.0.0: +ethereumjs-util@^6.0.0, ethereumjs-util@^6.2.1: version "6.2.1" resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz#fcb4e4dd5ceacb9d2305426ab1a5cd93e3163b69" integrity sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw== @@ -11571,7 +11606,7 @@ ethjs-util@0.1.3: is-hex-prefixed "1.0.0" strip-hex-prefix "1.0.0" -ethjs-util@0.1.6, ethjs-util@^0.1.3: +ethjs-util@0.1.6, ethjs-util@^0.1.3, ethjs-util@^0.1.6: version "0.1.6" resolved "https://registry.yarnpkg.com/ethjs-util/-/ethjs-util-0.1.6.tgz#f308b62f185f9fe6237132fb2a9818866a5cd536" integrity sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w== @@ -23911,37 +23946,36 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^3.0.0" inherits "^2.0.1" -ripple-address-codec@^4.0.0, ripple-address-codec@^4.1.0, ripple-address-codec@^4.1.1: - version "4.1.2" - resolved "https://registry.yarnpkg.com/ripple-address-codec/-/ripple-address-codec-4.1.2.tgz#c573309dbd0fdd4ef8c803bf36959b8a716c2aa1" - integrity sha512-bIhmaxOg6rwVYkPQha9cuHdIdwmD8XTnaklBmyRjFvNZwYJ6Cf0cdCt+SpJd+RRJhRU65+U1Eup6YkoCBrqebg== +ripple-address-codec@^4.1.1, ripple-address-codec@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/ripple-address-codec/-/ripple-address-codec-4.2.3.tgz#516675715cd43b71d2fd76c59bd92d0f623c152d" + integrity sha512-9Nd0hQmKoJEhSTzYR9kYjKmSWlH6HaVosNVAM7mIIVlzcNlQCPfKXj7CfvXcRiHl3C6XUZj7RFLqzVaPjq2ufA== dependencies: - base-x "3.0.8" + base-x "3.0.9" create-hash "^1.1.2" -ripple-binary-codec@^0.2.7: - version "0.2.7" - resolved "https://registry.yarnpkg.com/ripple-binary-codec/-/ripple-binary-codec-0.2.7.tgz#c5390e97e4072747a3ff386ee99558b496c6e5ab" - integrity sha512-VD+sHgZK76q3kmO765klFHPDCEveS5SUeg/bUNVpNrj7w2alyDNkbF17XNbAjFv+kSYhfsUudQanoaSs2Y6uzw== +ripple-binary-codec@^1.1.3: + version "1.3.0" + resolved "https://registry.yarnpkg.com/ripple-binary-codec/-/ripple-binary-codec-1.3.0.tgz#0c6cf503fb0e12948d538abd198a740bd9d2143a" + integrity sha512-hz4nhiekqHbUwIdBOg1PQKsbi+/GwOccHmTTfkIJTTp/p5mlifS+U3Zfz4dVzKhftrXCPympYvLb5QgoIP1AKw== dependencies: - babel-runtime "^6.26.0" - bn.js "^5.1.1" + assert "^2.0.0" + big-integer "^1.6.48" + buffer "5.6.0" create-hash "^1.2.0" decimal.js "^10.2.0" - inherits "^2.0.4" - lodash "^4.17.15" - ripple-address-codec "^4.1.0" + ripple-address-codec "^4.2.3" -ripple-keypairs@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/ripple-keypairs/-/ripple-keypairs-1.0.2.tgz#91c724210734e704e35053925a80bf1cd8104c92" - integrity sha512-3l2cUhUO4VEK42NfHtn7WA1NEO+vGU7p7/36QhCIvYFf8+lNdNrY0PrriJ3Mef/TG8hQvO8coCVEO5fpOKSAag== +ripple-keypairs@^1.0.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/ripple-keypairs/-/ripple-keypairs-1.1.3.tgz#3af825ffe85c1777b0aa78d832e9fc5750d4529d" + integrity sha512-y74Y3c0g652BgpDhWsf0x98GnUyY2D9eO2ay2exienUfbIe00TeIiFhYXQhCGVnliGsxeV9WTpU+YuEWuIxuhw== dependencies: bn.js "^5.1.1" brorand "^1.0.5" - elliptic "^6.5.2" + elliptic "^6.5.4" hash.js "^1.0.3" - ripple-address-codec "^4.0.0" + ripple-address-codec "^4.2.3" ripple-lib-transactionparser@0.8.2: version "0.8.2" @@ -23951,10 +23985,10 @@ ripple-lib-transactionparser@0.8.2: bignumber.js "^9.0.0" lodash "^4.17.15" -ripple-lib@1.8.2: - version "1.8.2" - resolved "https://registry.yarnpkg.com/ripple-lib/-/ripple-lib-1.8.2.tgz#d36dafcb64a913a5dab8a2f66a3b0727baaa9347" - integrity sha512-7fLQtiXb8LfyedkXQtDSNQPy7omNu7rGUO8M8bK2qiE+DuX4FPl8B8tVJbxBwOusAuYzdoVQfZtfdXt4WmBpmw== +ripple-lib@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/ripple-lib/-/ripple-lib-1.10.0.tgz#e41aaf17d5c6e6f8bcc8116736ac108ff3d6b810" + integrity sha512-Cg2u73UybfM1PnzcuLt5flvLKZn35ovdIp+1eLrReVB4swuRuUF/SskJG9hf5wMosbvh+E+jZu8A6IbYJoyFIA== dependencies: "@types/lodash" "^4.14.136" "@types/ws" "^7.2.0" @@ -23962,10 +23996,9 @@ ripple-lib@1.8.2: https-proxy-agent "^5.0.0" jsonschema "1.2.2" lodash "^4.17.4" - lodash.isequal "^4.5.0" ripple-address-codec "^4.1.1" - ripple-binary-codec "^0.2.7" - ripple-keypairs "^1.0.0" + ripple-binary-codec "^1.1.3" + ripple-keypairs "^1.0.3" ripple-lib-transactionparser "0.8.2" ws "^7.2.0" @@ -26397,36 +26430,24 @@ tree-kill@^1.2.2: resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== -trezor-connect@8.2.3-extended: - version "8.2.3-extended" - resolved "https://registry.yarnpkg.com/trezor-connect/-/trezor-connect-8.2.3-extended.tgz#597e985bacd9ebc0ab61b031fffc21c96515546e" - integrity sha512-nGgkvE25rKW7SQaz5hOo5hgsx8US0L1piTWu5WFzC5PIz6R+yCyN+SeSeKlYRJ7t9VdzTC77cYu3fklxJHJzCA== +trezor-connect@8.2.6-extended: + version "8.2.6-extended" + resolved "https://registry.yarnpkg.com/trezor-connect/-/trezor-connect-8.2.6-extended.tgz#1f6af2b7d9a8677adcac7b5961ab48f59648f3cc" + integrity sha512-j6I975BhHM2JBDDeW7uAjkVJjkUVxv/YhXdVIRN0O70ZyfWeXlY1ZcsYmgUAp08bo8nabFA4UpFADslDHtL3lQ== dependencies: "@babel/runtime" "^7.15.4" - "@trezor/blockchain-link" "^1.0.17" - "@trezor/connect-common" "^0.0.3" - "@trezor/rollout" "^1.2.0" + "@trezor/blockchain-link" "1.1.0" + "@trezor/connect-common" "0.0.4" + "@trezor/rollout" "^1.2.1" + "@trezor/transport" "1.0.1" "@trezor/utxo-lib" "1.0.0-beta.10" bignumber.js "^9.0.1" bowser "^2.11.0" cbor-web "^7.0.6" + cross-fetch "^3.1.4" events "^3.3.0" - keccak "^3.0.2" - node-fetch "^2.6.1" parse-uri "^1.0.3" tiny-worker "^2.3.0" - trezor-link "2.0.0-beta.6" - whatwg-fetch "^3.6.2" - -trezor-link@2.0.0-beta.6: - version "2.0.0-beta.6" - resolved "https://registry.yarnpkg.com/trezor-link/-/trezor-link-2.0.0-beta.6.tgz#e727890a539ee1b6ff109a0227c38328d42d95c7" - integrity sha512-DwmbKV434Vx7RmuPxcZzABTLFK5n5mZ+5+UjQ8tA0lLx41NstRJ+/OKO0b8qMJOv1ZnoQ87dfu5LtI7o9EXKuw== - dependencies: - bytebuffer "^5.0.1" - json-stable-stringify "^1.0.1" - long "^4.0.0" - protobufjs "^6.11.2" trim-newlines@^3.0.0: version "3.0.1" @@ -26554,7 +26575,7 @@ tunnel@^0.0.6: resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c" integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg== -tweetnacl-util@^0.15.0: +tweetnacl-util@^0.15.0, tweetnacl-util@^0.15.1: version "0.15.1" resolved "https://registry.yarnpkg.com/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz#b80fcdb5c97bcc508be18c44a4be50f022eea00b" integrity sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw== @@ -27737,7 +27758,7 @@ whatwg-fetch@2.0.4: resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz#dde6a5df315f9d39991aa17621853d720b85566f" integrity sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng== -whatwg-fetch@^3.4.1, whatwg-fetch@^3.6.2: +whatwg-fetch@^3.4.1: version "3.6.2" resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz#dced24f37f2624ed0281725d51d0e2e3fe677f8c" integrity sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA== From f5e86d035103e49cf46648214cc9c42d7318066e Mon Sep 17 00:00:00 2001 From: igorms-cons <89637087+igorms-cons@users.noreply.github.com> Date: Tue, 22 Feb 2022 21:58:21 +0100 Subject: [PATCH 012/105] Feat/settings search (#13214) * fix error with color variable - fix rebase * clean list search & fuse threshold decreased * update search-icon , fix tests * nice to have highlighting text & cleaning * unit test on settings & search input ui up on expanded view * fix color variable in alert scss * setting search input padding right up * fix dom warning * util/search test added & Dom element warning fix * renaming files * fix color text in settings search * settings search highlight text refacto & fix ui * fix settings-search test & renaming * Fix styling on search field for edge cases, update components and e2e E2E tests update for search feature Update components from class to functional component # Fix storybook for search box Fix styling Fix unit tests fix: remove z-index Fix unit tests Co-authored-by: amerkadicE --- app/_locales/en/messages.json | 12 + test/e2e/tests/custom-rpc-history.spec.js | 14 +- .../account-menu/account-menu.component.js | 2 +- ui/components/app/tab-bar/index.scss | 1 - .../ui/search-icon/search-icon.component.js | 17 +- .../ui/text-field/text-field.component.js | 9 + ui/helpers/utils/settings-search.js | 408 ++++++++++++++ ui/helpers/utils/settings-search.test.js | 524 ++++++++++++++++++ .../advanced-tab/advanced-tab.component.js | 38 +- ui/pages/settings/alerts-tab/alerts-tab.js | 63 ++- ui/pages/settings/alerts-tab/alerts-tab.scss | 26 +- .../contact-list-tab.component.js | 28 +- .../experimental-tab.component.js | 34 +- ui/pages/settings/index.scss | 149 ++++- .../settings/info-tab/info-tab.component.js | 43 +- .../networks-list-item/networks-list-item.js | 18 +- .../networks-list/networks-list.js | 3 +- .../security-tab/security-tab.component.js | 33 +- .../settings/settings-search-list/index.js | 3 + .../settings-search-list.js | 101 ++++ ui/pages/settings/settings-search/index.js | 3 + .../settings-search/settings-search.js | 105 ++++ .../settings-search.stories.js | 11 + .../settings-tab/settings-tab.component.js | 41 +- ...test.js => settings-tab.component.test.js} | 0 ui/pages/settings/settings.component.js | 72 ++- ui/pages/settings/settings.component.test.js | 52 ++ 27 files changed, 1690 insertions(+), 120 deletions(-) create mode 100644 ui/helpers/utils/settings-search.js create mode 100644 ui/helpers/utils/settings-search.test.js create mode 100644 ui/pages/settings/settings-search-list/index.js create mode 100644 ui/pages/settings/settings-search-list/settings-search-list.js create mode 100644 ui/pages/settings/settings-search/index.js create mode 100644 ui/pages/settings/settings-search/settings-search.js create mode 100644 ui/pages/settings/settings-search/settings-search.stories.js rename ui/pages/settings/settings-tab/{settings-tab.container.test.js => settings-tab.component.test.js} (100%) create mode 100644 ui/pages/settings/settings.component.test.js diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index a4350f384..cb4496c6d 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -1817,6 +1817,12 @@ "missingNFT": { "message": "Don't see your NFT?" }, + "missingSetting": { + "message": "Can't find a setting?" + }, + "missingSettingRequest": { + "message": "Request here" + }, "missingToken": { "message": "Don't see your token?" }, @@ -2533,6 +2539,9 @@ "searchResults": { "message": "Search Results" }, + "searchSettings": { + "message": "Search in settings" + }, "searchTokens": { "message": "Search Tokens" }, @@ -2682,6 +2691,9 @@ "settings": { "message": "Settings" }, + "settingsSearchMatchingNotFound": { + "message": "No matching results found" + }, "show": { "message": "Show" }, diff --git a/test/e2e/tests/custom-rpc-history.spec.js b/test/e2e/tests/custom-rpc-history.spec.js index 025f7f53a..49dd59059 100644 --- a/test/e2e/tests/custom-rpc-history.spec.js +++ b/test/e2e/tests/custom-rpc-history.spec.js @@ -36,10 +36,10 @@ describe('Stores custom RPC history', function () { await driver.findElement('.networks-tab__sub-header-text'); const customRpcInputs = await driver.findElements('input[type="text"]'); - const networkNameInput = customRpcInputs[0]; - const rpcUrlInput = customRpcInputs[1]; - const chainIdInput = customRpcInputs[2]; - const symbolInput = customRpcInputs[3]; + const networkNameInput = customRpcInputs[1]; + const rpcUrlInput = customRpcInputs[2]; + const chainIdInput = customRpcInputs[3]; + const symbolInput = customRpcInputs[4]; await networkNameInput.clear(); await networkNameInput.sendKeys(networkName); @@ -84,7 +84,7 @@ describe('Stores custom RPC history', function () { await driver.findElement('.networks-tab__sub-header-text'); const customRpcInputs = await driver.findElements('input[type="text"]'); - const rpcUrlInput = customRpcInputs[1]; + const rpcUrlInput = customRpcInputs[2]; await rpcUrlInput.clear(); await rpcUrlInput.sendKeys(duplicateRpcUrl); @@ -120,8 +120,8 @@ describe('Stores custom RPC history', function () { await driver.findElement('.networks-tab__sub-header-text'); const customRpcInputs = await driver.findElements('input[type="text"]'); - const rpcUrlInput = customRpcInputs[1]; - const chainIdInput = customRpcInputs[2]; + const rpcUrlInput = customRpcInputs[2]; + const chainIdInput = customRpcInputs[3]; await chainIdInput.clear(); await chainIdInput.sendKeys(duplicateChainId); diff --git a/ui/components/app/account-menu/account-menu.component.js b/ui/components/app/account-menu/account-menu.component.js index 8105af35b..4c3ff209c 100644 --- a/ui/components/app/account-menu/account-menu.component.js +++ b/ui/components/app/account-menu/account-menu.component.js @@ -125,7 +125,7 @@ export default class AccountMenu extends Component { marginLeft: '8px', }} > - + ); diff --git a/ui/components/app/tab-bar/index.scss b/ui/components/app/tab-bar/index.scss index 87b898c79..17ce585b7 100644 --- a/ui/components/app/tab-bar/index.scss +++ b/ui/components/app/tab-bar/index.scss @@ -41,7 +41,6 @@ flex-flow: row wrap; align-items: center; position: relative; - z-index: 201; &__title { @include H4; diff --git a/ui/components/ui/search-icon/search-icon.component.js b/ui/components/ui/search-icon/search-icon.component.js index 957c499bf..fd3d78081 100644 --- a/ui/components/ui/search-icon/search-icon.component.js +++ b/ui/components/ui/search-icon/search-icon.component.js @@ -1,13 +1,9 @@ import React from 'react'; +import PropTypes from 'prop-types'; -export default function SearchIcon() { +export default function SearchIcon({ color }) { return ( - + @@ -15,3 +11,10 @@ export default function SearchIcon() { ); } + +SearchIcon.propTypes = { + /** + * Color of the icon should be a valid design system color and is required + */ + color: PropTypes.string.isRequired, +}; diff --git a/ui/components/ui/text-field/text-field.component.js b/ui/components/ui/text-field/text-field.component.js index 44a96ee3f..43ef47914 100644 --- a/ui/components/ui/text-field/text-field.component.js +++ b/ui/components/ui/text-field/text-field.component.js @@ -88,6 +88,7 @@ const getMaterialThemeInputProps = ({ dir, classes: { materialLabel, materialFocused, materialError, materialUnderline }, startAdornment, + endAdornment, min, max, autoComplete, @@ -101,6 +102,7 @@ const getMaterialThemeInputProps = ({ }, InputProps: { startAdornment, + endAdornment, classes: { underline: materialUnderline, }, @@ -122,12 +124,14 @@ const getMaterialWhitePaddedThemeInputProps = ({ materialWhitePaddedUnderline, }, startAdornment, + endAdornment, min, max, autoComplete, }) => ({ InputProps: { startAdornment, + endAdornment, classes: { root: materialWhitePaddedRoot, focused: materialWhitePaddedFocused, @@ -157,6 +161,7 @@ const getBorderedThemeInputProps = ({ }, largeLabel, startAdornment, + endAdornment, min, max, autoComplete, @@ -172,6 +177,7 @@ const getBorderedThemeInputProps = ({ }, InputProps: { startAdornment, + endAdornment, disableUnderline: true, classes: { root: inputRoot, @@ -198,6 +204,7 @@ const TextField = ({ classes, theme, startAdornment, + endAdornment, largeLabel, dir, min, @@ -209,6 +216,7 @@ const TextField = ({ const inputProps = themeToInputProps[theme]({ classes, startAdornment, + endAdornment, largeLabel, dir, min, @@ -257,6 +265,7 @@ TextField.propTypes = { */ theme: PropTypes.oneOf(['bordered', 'material', 'material-white-padded']), startAdornment: PropTypes.element, + endAdornment: PropTypes.element, /** * Show large label */ diff --git a/ui/helpers/utils/settings-search.js b/ui/helpers/utils/settings-search.js new file mode 100644 index 000000000..2a41d5261 --- /dev/null +++ b/ui/helpers/utils/settings-search.js @@ -0,0 +1,408 @@ +/* eslint-disable require-unicode-regexp */ +import { + ALERTS_ROUTE, + ADVANCED_ROUTE, + SECURITY_ROUTE, + GENERAL_ROUTE, + ABOUT_US_ROUTE, + NETWORKS_ROUTE, + CONTACT_LIST_ROUTE, + EXPERIMENTAL_ROUTE, +} from '../constants/routes'; + +const MENU_TAB = 'menu-tab'; +const MENU_SECTION = 'menu-section'; + +function showHideSettings(t, settings) { + if (!process.env.COLLECTIBLES_V1) { + return settings.filter( + (e) => + e.section !== t('enableOpenSeaAPI') && + e.section !== t('useCollectibleDetection'), + ); + } + return settings; +} + +export function getSettingsRoutes(t) { + const settingsRoutesList = [ + { + tab: t('general'), + section: t('currencyConversion'), + description: '', + route: `${GENERAL_ROUTE}#currency-conversion`, + image: 'general-icon.svg', + }, + { + tab: t('general'), + section: t('primaryCurrencySetting'), + description: t('primaryCurrencySettingDescription'), + route: `${GENERAL_ROUTE}#primary-currency`, + image: 'general-icon.svg', + }, + { + tab: t('general'), + section: t('currentLanguage'), + description: '', + route: `${GENERAL_ROUTE}#current-language`, + image: 'general-icon.svg', + }, + { + tab: t('general'), + section: t('accountIdenticon'), + description: '', + route: `${GENERAL_ROUTE}#account-identicon`, + image: 'general-icon.svg', + }, + { + tab: t('general'), + section: t('hideZeroBalanceTokens'), + description: '', + route: `${GENERAL_ROUTE}#zero-balancetokens`, + image: 'general-icon.svg', + }, + { + tab: t('advanced'), + section: t('stateLogs'), + description: t('stateLogsDescription'), + route: `${ADVANCED_ROUTE}#state-logs`, + image: 'advanced-icon.svg', + }, + { + tab: t('advanced'), + section: t('syncWithMobile'), + description: '', + route: `${ADVANCED_ROUTE}#sync-withmobile`, + image: 'advanced-icon.svg', + }, + { + tab: t('advanced'), + section: t('resetAccount'), + description: t('resetAccountDescription'), + route: `${ADVANCED_ROUTE}#reset-account`, + image: 'advanced-icon.svg', + }, + { + tab: t('advanced'), + section: t('showAdvancedGasInline'), + description: t('showAdvancedGasInlineDescription'), + route: `${ADVANCED_ROUTE}#advanced-gascontrols`, + image: 'advanced-icon.svg', + }, + { + tab: t('advanced'), + section: t('showHexData'), + description: t('showHexDataDescription'), + route: `${ADVANCED_ROUTE}#show-hexdata`, + image: 'advanced-icon.svg', + }, + { + tab: t('advanced'), + section: t('showFiatConversionInTestnets'), + description: t('showFiatConversionInTestnetsDescription'), + route: `${ADVANCED_ROUTE}#conversion-testnetworks`, + image: 'advanced-icon.svg', + }, + { + tab: t('advanced'), + section: t('showTestnetNetworks'), + description: t('showTestnetNetworksDescription'), + route: `${ADVANCED_ROUTE}#show-testnets`, + image: 'advanced-icon.svg', + }, + { + tab: t('advanced'), + section: t('nonceField'), + description: t('nonceFieldDescription'), + route: `${ADVANCED_ROUTE}#customize-nonce`, + image: 'advanced-icon.svg', + }, + { + tab: t('advanced'), + section: t('autoLockTimeLimit'), + description: t('autoLockTimeLimitDescription'), + route: `${ADVANCED_ROUTE}#autolock-timer`, + image: 'advanced-icon.svg', + }, + { + tab: t('advanced'), + section: t('syncWithThreeBox'), + description: t('syncWithThreeBoxDescription'), + route: `${ADVANCED_ROUTE}#sync-with3box`, + image: 'advanced-icon.svg', + }, + { + tab: t('advanced'), + section: t('ipfsGateway'), + description: t('ipfsGatewayDescription'), + route: `${ADVANCED_ROUTE}#ipfs-gateway`, + image: 'advanced-icon.svg', + }, + { + tab: t('advanced'), + section: t('preferredLedgerConnectionType'), + description: t('preferredLedgerConnectionType'), + route: `${ADVANCED_ROUTE}#ledger-connection`, + image: 'advanced-icon.svg', + }, + { + tab: t('advanced'), + section: t('dismissReminderField'), + description: t('dismissReminderDescriptionField'), + route: `${ADVANCED_ROUTE}#dimiss-secretrecovery`, + image: 'advanced-icon.svg', + }, + { + tab: t('contacts'), + section: t('contacts'), + description: t('contacts'), + route: CONTACT_LIST_ROUTE, + image: 'contacts-icon.svg', + }, + { + tab: t('securityAndPrivacy'), + section: t('revealSeedWords'), + description: t('revealSeedWords'), + route: `${SECURITY_ROUTE}#reveal-secretrecovery`, + image: 'security-icon.svg', + }, + { + tab: t('securityAndPrivacy'), + section: t('showIncomingTransactions'), + description: t('showIncomingTransactionsDescription'), + route: `${SECURITY_ROUTE}#incoming-transaction`, + image: 'security-icon.svg', + }, + { + tab: t('securityAndPrivacy'), + section: t('usePhishingDetection'), + description: t('usePhishingDetectionDescription'), + route: `${SECURITY_ROUTE}#phishing-detection`, + image: 'security-icon.svg', + }, + { + tab: t('securityAndPrivacy'), + section: t('participateInMetaMetrics'), + description: t('participateInMetaMetricsDescription'), + route: `${SECURITY_ROUTE}#metrametrics`, + image: 'security-icon.svg', + }, + { + tab: t('alerts'), + section: t('alertSettingsUnconnectedAccount'), + description: t('alertSettingsUnconnectedAccount'), + route: `${ALERTS_ROUTE}#unconnected-account`, + image: 'alerts-icon.svg', + }, + { + tab: t('alerts'), + section: t('alertSettingsWeb3ShimUsage'), + description: t('alertSettingsWeb3ShimUsage'), + route: `${ALERTS_ROUTE}#web3-shimusage`, + image: 'alerts-icon.svg', + }, + { + tab: t('networks'), + section: t('mainnet'), + description: t('mainnet'), + route: `${NETWORKS_ROUTE}#networks-mainnet`, + image: 'network-icon.svg', + }, + { + tab: t('networks'), + section: t('ropsten'), + description: t('ropsten'), + route: `${NETWORKS_ROUTE}#networks-ropsten`, + image: 'network-icon.svg', + }, + { + tab: t('networks'), + section: t('rinkeby'), + description: t('rinkeby'), + route: `${NETWORKS_ROUTE}#networks-rinkeby`, + image: 'network-icon.svg', + }, + { + tab: t('networks'), + section: t('goerli'), + description: t('goerli'), + route: `${NETWORKS_ROUTE}#networks-goerli`, + image: 'network-icon.svg', + }, + { + tab: t('networks'), + section: t('kovan'), + description: t('kovan'), + route: `${NETWORKS_ROUTE}#networtks-kovan`, + image: 'network-icon.svg', + }, + { + tab: t('networks'), + section: t('localhost'), + description: t('localhost'), + route: `${NETWORKS_ROUTE}#network-localhost`, + image: 'network-icon.svg', + }, + { + tab: t('experimental'), + section: t('useTokenDetection'), + description: t('useTokenDetectionDescription'), + route: `${EXPERIMENTAL_ROUTE}#token-description`, + image: 'experimental-icon.svg', + }, + { + tab: t('experimental'), + section: t('enableOpenSeaAPI'), + description: t('enableOpenSeaAPIDescription'), + route: `${EXPERIMENTAL_ROUTE}#opensea-api`, + image: 'experimental-icon.svg', + }, + { + tab: t('experimental'), + section: t('useCollectibleDetection'), + description: t('useCollectibleDetectionDescription'), + route: `${EXPERIMENTAL_ROUTE}#autodetect-nfts`, + image: 'experimental-icon.svg', + }, + + { + tab: t('about'), + section: t('metamaskVersion'), + description: t('builtAroundTheWorld'), + route: `${ABOUT_US_ROUTE}#version`, + image: 'info-icon.svg', + }, + { + tab: t('about'), + section: t('links'), + description: '', + route: `${ABOUT_US_ROUTE}#links`, + image: 'info-icon.svg', + }, + { + tab: t('about'), + section: t('privacyMsg'), + description: t('privacyMsg'), + route: `${ABOUT_US_ROUTE}#privacy-policy`, + image: 'info-icon.svg', + }, + { + tab: t('about'), + section: t('terms'), + description: t('terms'), + route: `${ABOUT_US_ROUTE}#terms`, + image: 'info-icon.svg', + }, + + { + tab: t('about'), + section: t('attributions'), + description: t('attributions'), + route: `${ABOUT_US_ROUTE}#attributions`, + image: 'info-icon.svg', + }, + + { + tab: t('about'), + section: t('supportCenter'), + description: t('supportCenter'), + route: `${ABOUT_US_ROUTE}#supportcenter`, + image: 'info-icon.svg', + }, + + { + tab: t('about'), + section: t('visitWebSite'), + description: t('visitWebSite'), + route: `${ABOUT_US_ROUTE}#visitwebsite`, + image: 'info-icon.svg', + }, + + { + tab: t('about'), + section: t('contactUs'), + description: t('contactUs'), + route: `${ABOUT_US_ROUTE}#contactus`, + image: 'info-icon.svg', + }, + ]; + + // TODO: write to json file? + return showHideSettings(t, settingsRoutesList); +} + +function getFilteredSettingsRoutes(t, tabName) { + return getSettingsRoutes(t).filter((s) => s.tab === tabName); +} + +export function getSettingsSectionNumber(t, tabName) { + return getSettingsRoutes(t).filter((s) => s.tab === tabName).length; +} + +export function handleSettingsRefs(t, tabName, settingsRefs) { + const settingsSearchJsonFiltered = getFilteredSettingsRoutes(t, tabName); + const settingsRefsIndex = settingsSearchJsonFiltered.findIndex( + (s) => s.route.substring(1) === window.location.hash.substring(1), + ); + + if ( + settingsRefsIndex !== -1 && + settingsRefs[settingsRefsIndex].current !== null + ) { + settingsRefs[settingsRefsIndex].current.scrollIntoView({ + behavior: 'smooth', + }); + settingsRefs[settingsRefsIndex].current.focus(); + const historySettingsUrl = window.location.hash.split('#')[1]; + window.location.hash = historySettingsUrl; + } +} + +export function handleHooksSettingsRefs(t, tabName, settingsRefs, itemIndex) { + const settingsSearchJsonFiltered = getFilteredSettingsRoutes(t, tabName); + const settingsRefsIndex = settingsSearchJsonFiltered.findIndex( + (s) => s.route.substring(1) === window.location.hash.substring(1), + ); + + if ( + settingsRefsIndex !== -1 && + settingsRefs !== null && + itemIndex === settingsRefsIndex + ) { + settingsRefs.current.scrollIntoView({ + behavior: 'smooth', + }); + settingsRefs.current.focus(); + const historySettingsUrl = window.location.hash.split('#')[1]; + window.location.hash = historySettingsUrl; + } +} + +function colorText(menuElement, regex) { + if (menuElement !== null) { + let elemText = menuElement?.innerHTML; + elemText = elemText.replace('&', '&'); + elemText = elemText.replace( + /(|<\/span>)/gim, + '', + ); + menuElement.innerHTML = elemText.replace( + regex, + '$&', + ); + } +} +export function highlightSearchedText(menuIndex) { + const menuTabElement = document.getElementById(`${MENU_TAB}_${menuIndex}`); + const menuSectionElement = document.getElementById( + `${MENU_SECTION}_${menuIndex}`, + ); + + const searchElem = document.getElementById('search-settings'); + searchElem.addEventListener('input', (event) => { + const searchQuery = event.target.value; + const searchRegex = new RegExp(searchQuery, 'gi'); + colorText(menuTabElement, searchRegex); + colorText(menuSectionElement, searchRegex); + }); +} diff --git a/ui/helpers/utils/settings-search.test.js b/ui/helpers/utils/settings-search.test.js new file mode 100644 index 000000000..552fa3074 --- /dev/null +++ b/ui/helpers/utils/settings-search.test.js @@ -0,0 +1,524 @@ +import React from 'react'; +import { + getSettingsRoutes, + getSettingsSectionNumber, + handleSettingsRefs, +} from './settings-search'; + +const t = (key) => { + switch (key) { + case 'general': + return 'General'; + case 'currencyConversion': + return 'Currency Conversion'; + case 'primaryCurrencySetting': + return 'Primary Currenc'; + case 'primaryCurrencySettingDescription': + return 'Select native to prioritize displaying values in the native currency of the chain (e.g. ETH). Select Fiat to prioritize displaying values in your selected fiat currency.'; + case 'currentLanguage': + return 'Current Language'; + case 'accountIdenticon': + return 'Current Language"'; + case 'hideZeroBalanceTokens': + return 'Hide Tokens Without Balance'; + case 'advanced': + return 'Advanced'; + case 'stateLogs': + return 'State Logs'; + case 'stateLogsDescription': + return 'State logs contain your public account addresses and sent transactions.'; + case 'syncWithMobile': + return 'Sync with mobile'; + case 'resetAccount': + return 'Reset Account'; + case 'resetAccountDescription': + return 'Resetting your account will clear your transaction history. This will not change the balances in your accounts or require you to re-enter your Secret Recovery Phrase.'; + case 'showAdvancedGasInline': + return 'Advanced gas controls'; + case 'showAdvancedGasInlineDescription': + return 'Select this to show gas price and limit controls directly on the send and confirm screens.'; + case 'showHexData': + return 'Show Hex Data'; + case 'showHexDataDescription': + return 'Select this to show the hex data field on the send screen'; + case 'showFiatConversionInTestnets': + return 'Show Conversion on test networks'; + case 'showFiatConversionInTestnetsDescription': + return 'Select this to show fiat conversion on test network'; + case 'showTestnetNetworks': + return 'Show test networks'; + case 'showTestnetNetworksDescription': + return 'Select this to show test networks in network list'; + case 'nonceField': + return 'Customize transaction nonce'; + case 'nonceFieldDescription': + return 'Turn this on to change the nonce (transaction number) on confirmation screens. This is an advanced feature, use cautiously.'; + case 'autoLockTimeLimit': + return 'Auto-Lock Timer (minutes)'; + case 'autoLockTimeLimitDescription': + return 'Set the idle time in minutes before MetaMask will become locked.'; + case 'syncWithThreeBox': + return 'Sync data with 3Box (experimental)'; + case 'syncWithThreeBoxDescription': + return 'Turn on to have your settings backed up with 3Box. This feature is currently experimental; use at your own risk.'; + case 'ipfsGateway': + return 'IPFS Gateway'; + case 'ipfsGatewayDescription': + return 'Enter the URL of the IPFS CID gateway to use for ENS content resolution.'; + case 'preferredLedgerConnectionType': + return 'Preferred Ledger Connection Type'; + case 'dismissReminderField': + return 'Dismiss Secret Recovery Phrase backup reminder'; + case 'dismissReminderDescriptionField': + return 'Turn this on to dismiss the Secret Recovery Phrase backup reminder message. We highly recommend that you back up your Secret Recovery Phrase to avoid loss of funds'; + case 'Contacts': + return 'Contacts'; + case 'securityAndPrivacy': + return 'Security & Privacy'; + case 'revealSeedWords': + return 'Reveal Secret Recovery Phrase'; + case 'showIncomingTransactions': + return 'Show Incoming Transactions'; + case 'showIncomingTransactionsDescription': + return 'Select this to use Etherscan to show incoming transactions in the transactions list'; + case 'usePhishingDetection': + return 'Use Phishing Detection'; + case 'usePhishingDetectionDescription': + return 'Display a warning for phishing domains targeting Ethereum users'; + case 'participateInMetaMetrics': + return 'Participate in MetaMetrics'; + case 'participateInMetaMetricsDescription': + return 'Participate in MetaMetrics to help us make MetaMask better'; + case 'alerts': + return 'Alerts'; + case 'alertSettingsUnconnectedAccount': + return 'Browsing a website with an unconnected account selected'; + case 'alertSettingsWeb3ShimUsage': + return 'When a website tries to use the removed window.web3 API'; + case 'networks': + return 'Networks'; + case 'mainnet': + return 'Ethereum Mainnet'; + case 'ropsten': + return 'Ropsten Test Network'; + case 'rinkeby': + return 'Rinkeby Test Network'; + case 'goerli': + return 'Goerli Test Network'; + case 'kovan': + return 'Kovan Test Network'; + case 'localhost': + return 'Localhost 8545'; + case 'experimental': + return 'Experimental'; + case 'useTokenDetection': + return 'Use Token Detection'; + case 'useTokenDetectionDescription': + return 'We use third-party APIs to detect and display new tokens sent to your wallet. Turn off if you don’t want MetaMask to pull data from those services.'; + case 'enableOpenSeaAPI': + return 'Enable OpenSea API'; + case 'enableOpenSeaAPIDescription': + return 'Use OpenSea API to fetch NFT data.NFT auto - detection relies on OpenSea API, and will not be available when this is turned off.'; + case 'useCollectibleDetection': + return 'Autodetect NFTs'; + case 'useCollectibleDetectionDescription': + return 'Displaying NFTs media & data may expose your IP address to centralized servers. Third-party APIs (like OpenSea) are used to detect NFTs in your wallet. This exposes your account address with those services. Leave this disabled if you don’t want the app to pull data from those those services.'; + case 'about': + return 'About'; + case 'metamaskVersion': + return 'MetaMask Version'; + case 'builtAroundTheWorld': + return 'MetaMask is designed and built around the world.'; + case 'links': + return 'Links'; + case 'privacyMsg': + return 'Privacy Policy'; + case 'terms': + return 'Terms of Use'; + case 'attributions': + return 'Attributions'; + case 'supportCenter': + return 'Visit our Support Center'; + case 'visitWebSite': + return 'Visit our web site'; + case 'contactUs': + return 'Contact us'; + + default: + return ''; + } +}; + +describe('Settings Search Utils', () => { + describe('getSettingsRoutes', () => { + it('should get all settings', () => { + const settingsListExcepted = [ + { + description: '', + image: 'general-icon.svg', + route: '/settings/general#currency-conversion', + section: 'Currency Conversion', + tab: 'General', + }, + { + description: + 'Select native to prioritize displaying values in the native currency of the chain (e.g. ETH). Select Fiat to prioritize displaying values in your selected fiat currency.', + image: 'general-icon.svg', + route: '/settings/general#primary-currency', + section: 'Primary Currenc', + tab: 'General', + }, + { + description: '', + image: 'general-icon.svg', + route: '/settings/general#current-language', + section: 'Current Language', + tab: 'General', + }, + { + description: '', + image: 'general-icon.svg', + route: '/settings/general#account-identicon', + section: 'Current Language"', + tab: 'General', + }, + { + description: '', + image: 'general-icon.svg', + route: '/settings/general#zero-balancetokens', + section: 'Hide Tokens Without Balance', + tab: 'General', + }, + { + description: + 'State logs contain your public account addresses and sent transactions.', + image: 'advanced-icon.svg', + route: '/settings/advanced#state-logs', + section: 'State Logs', + tab: 'Advanced', + }, + { + description: '', + image: 'advanced-icon.svg', + route: '/settings/advanced#sync-withmobile', + section: 'Sync with mobile', + tab: 'Advanced', + }, + { + description: + 'Resetting your account will clear your transaction history. This will not change the balances in your accounts or require you to re-enter your Secret Recovery Phrase.', + image: 'advanced-icon.svg', + route: '/settings/advanced#reset-account', + section: 'Reset Account', + tab: 'Advanced', + }, + { + description: + 'Select this to show gas price and limit controls directly on the send and confirm screens.', + image: 'advanced-icon.svg', + route: '/settings/advanced#advanced-gascontrols', + section: 'Advanced gas controls', + tab: 'Advanced', + }, + { + description: + 'Select this to show the hex data field on the send screen', + image: 'advanced-icon.svg', + route: '/settings/advanced#show-hexdata', + section: 'Show Hex Data', + tab: 'Advanced', + }, + { + description: 'Select this to show fiat conversion on test network', + image: 'advanced-icon.svg', + route: '/settings/advanced#conversion-testnetworks', + section: 'Show Conversion on test networks', + tab: 'Advanced', + }, + { + description: 'Select this to show test networks in network list', + image: 'advanced-icon.svg', + route: '/settings/advanced#show-testnets', + section: 'Show test networks', + tab: 'Advanced', + }, + { + description: + 'Turn this on to change the nonce (transaction number) on confirmation screens. This is an advanced feature, use cautiously.', + image: 'advanced-icon.svg', + route: '/settings/advanced#customize-nonce', + section: 'Customize transaction nonce', + tab: 'Advanced', + }, + { + description: + 'Set the idle time in minutes before MetaMask will become locked.', + image: 'advanced-icon.svg', + route: '/settings/advanced#autolock-timer', + section: 'Auto-Lock Timer (minutes)', + tab: 'Advanced', + }, + { + description: + 'Turn on to have your settings backed up with 3Box. This feature is currently experimental; use at your own risk.', + image: 'advanced-icon.svg', + route: '/settings/advanced#sync-with3box', + section: 'Sync data with 3Box (experimental)', + tab: 'Advanced', + }, + { + description: + 'Enter the URL of the IPFS CID gateway to use for ENS content resolution.', + image: 'advanced-icon.svg', + route: '/settings/advanced#ipfs-gateway', + section: 'IPFS Gateway', + tab: 'Advanced', + }, + { + description: 'Preferred Ledger Connection Type', + image: 'advanced-icon.svg', + route: '/settings/advanced#ledger-connection', + section: 'Preferred Ledger Connection Type', + tab: 'Advanced', + }, + { + description: + 'Turn this on to dismiss the Secret Recovery Phrase backup reminder message. We highly recommend that you back up your Secret Recovery Phrase to avoid loss of funds', + image: 'advanced-icon.svg', + route: '/settings/advanced#dimiss-secretrecovery', + section: 'Dismiss Secret Recovery Phrase backup reminder', + tab: 'Advanced', + }, + { + description: '', + image: 'contacts-icon.svg', + route: '/settings/contact-list', + section: '', + tab: '', + }, + { + description: 'Reveal Secret Recovery Phrase', + image: 'security-icon.svg', + route: '/settings/security#reveal-secretrecovery', + section: 'Reveal Secret Recovery Phrase', + tab: 'Security & Privacy', + }, + { + description: + 'Select this to use Etherscan to show incoming transactions in the transactions list', + image: 'security-icon.svg', + route: '/settings/security#incoming-transaction', + section: 'Show Incoming Transactions', + tab: 'Security & Privacy', + }, + { + description: + 'Display a warning for phishing domains targeting Ethereum users', + image: 'security-icon.svg', + route: '/settings/security#phishing-detection', + section: 'Use Phishing Detection', + tab: 'Security & Privacy', + }, + { + description: + 'Participate in MetaMetrics to help us make MetaMask better', + image: 'security-icon.svg', + route: '/settings/security#metrametrics', + section: 'Participate in MetaMetrics', + tab: 'Security & Privacy', + }, + { + description: + 'Browsing a website with an unconnected account selected', + image: 'alerts-icon.svg', + route: '/settings/alerts#unconnected-account', + section: 'Browsing a website with an unconnected account selected', + tab: 'Alerts', + }, + { + description: + 'When a website tries to use the removed window.web3 API', + image: 'alerts-icon.svg', + route: '/settings/alerts#web3-shimusage', + section: 'When a website tries to use the removed window.web3 API', + tab: 'Alerts', + }, + { + description: 'Ethereum Mainnet', + image: 'network-icon.svg', + route: '/settings/networks#networks-mainnet', + section: 'Ethereum Mainnet', + tab: 'Networks', + }, + { + description: 'Ropsten Test Network', + image: 'network-icon.svg', + route: '/settings/networks#networks-ropsten', + section: 'Ropsten Test Network', + tab: 'Networks', + }, + { + description: 'Rinkeby Test Network', + image: 'network-icon.svg', + route: '/settings/networks#networks-rinkeby', + section: 'Rinkeby Test Network', + tab: 'Networks', + }, + { + description: 'Goerli Test Network', + image: 'network-icon.svg', + route: '/settings/networks#networks-goerli', + section: 'Goerli Test Network', + tab: 'Networks', + }, + { + description: 'Kovan Test Network', + image: 'network-icon.svg', + route: '/settings/networks#networtks-kovan', + section: 'Kovan Test Network', + tab: 'Networks', + }, + { + description: 'Localhost 8545', + image: 'network-icon.svg', + route: '/settings/networks#network-localhost', + section: 'Localhost 8545', + tab: 'Networks', + }, + { + description: + 'We use third-party APIs to detect and display new tokens sent to your wallet. Turn off if you don’t want MetaMask to pull data from those services.', + image: 'experimental-icon.svg', + route: '/settings/experimental#token-description', + section: 'Use Token Detection', + tab: 'Experimental', + }, + { + description: 'MetaMask is designed and built around the world.', + image: 'info-icon.svg', + route: '/settings/about-us#version', + section: 'MetaMask Version', + tab: 'About', + }, + { + description: '', + image: 'info-icon.svg', + route: '/settings/about-us#links', + section: 'Links', + tab: 'About', + }, + { + description: 'Privacy Policy', + image: 'info-icon.svg', + route: '/settings/about-us#privacy-policy', + section: 'Privacy Policy', + tab: 'About', + }, + { + description: 'Terms of Use', + image: 'info-icon.svg', + route: '/settings/about-us#terms', + section: 'Terms of Use', + tab: 'About', + }, + { + description: 'Attributions', + image: 'info-icon.svg', + route: '/settings/about-us#attributions', + section: 'Attributions', + tab: 'About', + }, + { + description: 'Visit our Support Center', + image: 'info-icon.svg', + route: '/settings/about-us#supportcenter', + section: 'Visit our Support Center', + tab: 'About', + }, + { + description: 'Visit our web site', + image: 'info-icon.svg', + route: '/settings/about-us#visitwebsite', + section: 'Visit our web site', + tab: 'About', + }, + { + description: 'Contact us', + image: 'info-icon.svg', + route: '/settings/about-us#contactus', + section: 'Contact us', + tab: 'About', + }, + ]; + expect(getSettingsRoutes(t)).toStrictEqual(settingsListExcepted); + }); + + it('should not get all settings', () => { + const settingsListExcepted = [ + { + description: '', + image: 'general-icon.svg', + route: '/settings/general#currency-conversion', + section: 'Currency Conversion', + tab: 'General', + }, + { + description: 'Contact us', + image: 'info-icon.svg', + route: '/settings/about-us#contactus', + section: 'Contact us', + tab: 'About', + }, + ]; + expect(getSettingsRoutes(t)).not.toStrictEqual(settingsListExcepted); + }); + }); + + describe('getSettingsSectionNumber', () => { + it('should get good general section number', () => { + expect(getSettingsSectionNumber(t, t('general'))).toStrictEqual(5); + }); + + it('should get good advanced section number', () => { + expect(getSettingsSectionNumber(t, t('advanced'))).toStrictEqual(13); + }); + + it('should get good contact section number', () => { + expect(getSettingsSectionNumber(t, t('contacts'))).toStrictEqual(1); + }); + + it('should get good security & privacy section number', () => { + expect( + getSettingsSectionNumber(t, t('securityAndPrivacy')), + ).toStrictEqual(4); + }); + + it('should get good alerts section number', () => { + expect(getSettingsSectionNumber(t, t('alerts'))).toStrictEqual(2); + }); + + it('should get good network section number', () => { + expect(getSettingsSectionNumber(t, t('networks'))).toStrictEqual(6); + }); + + it('should get good experimental section number', () => { + expect(getSettingsSectionNumber(t, t('experimental'))).toStrictEqual(1); + }); + + it('should get good about section number', () => { + expect(getSettingsSectionNumber(t, t('about'))).toStrictEqual(8); + }); + }); + + // Can't be tested without DOM element + describe('handleSettingsRefs', () => { + it('should handle general refs', () => { + const settingsRefs = Array(getSettingsSectionNumber(t, t('general'))) + .fill(undefined) + .map(() => { + return React.createRef(); + }); + expect(handleSettingsRefs(t, t('general'), settingsRefs)).toBeUndefined(); + }); + }); +}); diff --git a/ui/pages/settings/advanced-tab/advanced-tab.component.js b/ui/pages/settings/advanced-tab/advanced-tab.component.js index c24ddb868..116effd62 100644 --- a/ui/pages/settings/advanced-tab/advanced-tab.component.js +++ b/ui/pages/settings/advanced-tab/advanced-tab.component.js @@ -12,6 +12,10 @@ import Dialog from '../../../components/ui/dialog'; import { getPlatform } from '../../../../app/scripts/lib/util'; import { PLATFORM_FIREFOX } from '../../../../shared/constants/app'; +import { + getSettingsSectionNumber, + handleSettingsRefs, +} from '../../../helpers/utils/settings-search'; import { LEDGER_TRANSPORT_TYPES, @@ -61,13 +65,22 @@ export default class AdvancedTab extends PureComponent { showLedgerTransportWarning: false, }; - showTestNetworksRef = React.createRef(); + settingsRefs = Array( + getSettingsSectionNumber(this.context.t, this.context.t('advanced')), + ) + .fill(undefined) + .map(() => { + return React.createRef(); + }); + + componentDidUpdate() { + const { t } = this.context; + handleSettingsRefs(t, t('advanced'), this.settingsRefs); + } componentDidMount() { - if (window.location.hash.match(/show-testnets/u)) { - this.showTestNetworksRef.current.scrollIntoView({ behavior: 'smooth' }); - this.showTestNetworksRef.current.focus(); - } + const { t } = this.context; + handleSettingsRefs(t, t('advanced'), this.settingsRefs); } renderMobileSync() { @@ -76,6 +89,7 @@ export default class AdvancedTab extends PureComponent { return (
@@ -106,6 +120,7 @@ export default class AdvancedTab extends PureComponent { return (
@@ -144,6 +159,7 @@ export default class AdvancedTab extends PureComponent { return (
@@ -185,6 +201,7 @@ export default class AdvancedTab extends PureComponent { return (
@@ -214,6 +231,7 @@ export default class AdvancedTab extends PureComponent { return (
@@ -243,7 +261,7 @@ export default class AdvancedTab extends PureComponent { return (
@@ -276,6 +294,7 @@ export default class AdvancedTab extends PureComponent { return (
@@ -307,6 +326,7 @@ export default class AdvancedTab extends PureComponent { return (
@@ -355,6 +375,7 @@ export default class AdvancedTab extends PureComponent { return (
@@ -411,6 +432,7 @@ export default class AdvancedTab extends PureComponent { } return (
@@ -479,7 +501,7 @@ export default class AdvancedTab extends PureComponent { : LEDGER_TRANSPORT_NAMES.U2F; return ( -
+
{t('preferredLedgerConnectionType')}
@@ -578,6 +600,7 @@ export default class AdvancedTab extends PureComponent { return (
@@ -622,6 +645,7 @@ export default class AdvancedTab extends PureComponent { return (
diff --git a/ui/pages/settings/alerts-tab/alerts-tab.js b/ui/pages/settings/alerts-tab/alerts-tab.js index c8d092470..1bbd327e2 100644 --- a/ui/pages/settings/alerts-tab/alerts-tab.js +++ b/ui/pages/settings/alerts-tab/alerts-tab.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect, useRef } from 'react'; import PropTypes from 'prop-types'; import { useSelector } from 'react-redux'; @@ -8,27 +8,38 @@ import ToggleButton from '../../../components/ui/toggle-button'; import { setAlertEnabledness } from '../../../store/actions'; import { getAlertEnabledness } from '../../../ducks/metamask/metamask'; import { useI18nContext } from '../../../hooks/useI18nContext'; +import { handleHooksSettingsRefs } from '../../../helpers/utils/settings-search'; -const AlertSettingsEntry = ({ alertId, description, title }) => { +const AlertSettingsEntry = ({ alertId, description, title, alertIndex }) => { const t = useI18nContext(); + const settingsRefs = useRef(alertIndex); + + useEffect(() => { + handleHooksSettingsRefs(t, t('alerts'), settingsRefs, alertIndex); + }, [settingsRefs, t, alertIndex]); + const isEnabled = useSelector((state) => getAlertEnabledness(state)[alertId]); return ( <> - {title} - - - - setAlertEnabledness(alertId, !isEnabled)} - value={isEnabled} - /> +
+ {title} +
+ + + + setAlertEnabledness(alertId, !isEnabled)} + value={isEnabled} + /> +
+
); }; @@ -37,6 +48,7 @@ AlertSettingsEntry.propTypes = { alertId: PropTypes.string.isRequired, description: PropTypes.string.isRequired, title: PropTypes.string.isRequired, + alertIndex: PropTypes.number.isRequired, }; const AlertsTab = () => { @@ -55,14 +67,17 @@ const AlertsTab = () => { return (
- {Object.entries(alertConfig).map(([alertId, { title, description }]) => ( - - ))} + {Object.entries(alertConfig).map( + ([alertId, { title, description }], index) => ( + + ), + )}
); }; diff --git a/ui/pages/settings/alerts-tab/alerts-tab.scss b/ui/pages/settings/alerts-tab/alerts-tab.scss index de31726bf..f3873b43f 100644 --- a/ui/pages/settings/alerts-tab/alerts-tab.scss +++ b/ui/pages/settings/alerts-tab/alerts-tab.scss @@ -6,28 +6,26 @@ grid-template-columns: 8fr 30px max-content; grid-template-rows: 1fr 1fr; align-items: center; + display: block; } - &__body > * { - border-bottom: 1px solid var(--Grey-100); - padding: 16px 8px; - height: 100%; + &__description-container { + display: flex; } - &__body > :first-child { - padding-left: 32px; - } - - &__body > :nth-child(3n+4) { - padding-left: 32px; - } - - &__body > :nth-child(3n) { - padding-right: 32px; + &__description-container > * { + padding: 0 8px; } &__description { display: flex; align-items: center; } + + &__item { + border-bottom: 1px solid var(--Grey-100); + padding: 16px 32px; + display: flex; + justify-content: space-between; + } } diff --git a/ui/pages/settings/contact-list-tab/contact-list-tab.component.js b/ui/pages/settings/contact-list-tab/contact-list-tab.component.js index 707d4605f..f37f2c962 100644 --- a/ui/pages/settings/contact-list-tab/contact-list-tab.component.js +++ b/ui/pages/settings/contact-list-tab/contact-list-tab.component.js @@ -7,6 +7,10 @@ import { CONTACT_VIEW_ROUTE, } from '../../../helpers/constants/routes'; import Button from '../../../components/ui/button'; +import { + getSettingsSectionNumber, + handleSettingsRefs, +} from '../../../helpers/utils/settings-search'; import EditContact from './edit-contact'; import AddContact from './add-contact'; import ViewContact from './view-contact'; @@ -27,6 +31,24 @@ export default class ContactListTab extends Component { hideAddressBook: PropTypes.bool, }; + settingsRefs = Array( + getSettingsSectionNumber(this.context.t, this.context.t('contacts')), + ) + .fill(undefined) + .map(() => { + return React.createRef(); + }); + + componentDidUpdate() { + const { t } = this.context; + handleSettingsRefs(t, t('contacts'), this.settingsRefs); + } + + componentDidMount() { + const { t } = this.context; + handleSettingsRefs(t, t('contacts'), this.settingsRefs); + } + renderAddresses() { const { addressBook, history, selectedAddress } = this.props; const contacts = addressBook.filter(({ name }) => Boolean(name)); @@ -124,7 +146,11 @@ export default class ContactListTab extends Component { const { hideAddressBook } = this.props; if (!hideAddressBook) { - return
{this.renderAddresses()}
; + return ( +
+ {this.renderAddresses()} +
+ ); } return null; } diff --git a/ui/pages/settings/experimental-tab/experimental-tab.component.js b/ui/pages/settings/experimental-tab/experimental-tab.component.js index 3d2b16fde..6d74ca459 100644 --- a/ui/pages/settings/experimental-tab/experimental-tab.component.js +++ b/ui/pages/settings/experimental-tab/experimental-tab.component.js @@ -1,6 +1,10 @@ import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; import ToggleButton from '../../../components/ui/toggle-button'; +import { + getSettingsSectionNumber, + handleSettingsRefs, +} from '../../../helpers/utils/settings-search'; export default class ExperimentalTab extends PureComponent { static contextTypes = { @@ -19,12 +23,30 @@ export default class ExperimentalTab extends PureComponent { setEIP1559V2Enabled: PropTypes.func, }; + settingsRefs = Array( + getSettingsSectionNumber(this.context.t, this.context.t('experimental')), + ) + .fill(undefined) + .map(() => { + return React.createRef(); + }); + + componentDidUpdate() { + const { t } = this.context; + handleSettingsRefs(t, t('experimental'), this.settingsRefs); + } + + componentDidMount() { + const { t } = this.context; + handleSettingsRefs(t, t('experimental'), this.settingsRefs); + } + renderTokenDetectionToggle() { const { t } = this.context; const { useTokenDetection, setUseTokenDetection } = this.props; return ( -
+
{t('useTokenDetection')}
@@ -68,7 +90,10 @@ export default class ExperimentalTab extends PureComponent { } = this.props; return ( -
+
{t('useCollectibleDetection')}
@@ -114,7 +139,10 @@ export default class ExperimentalTab extends PureComponent { } = this.props; return ( -
+
{t('enableOpenSeaAPI')}
diff --git a/ui/pages/settings/index.scss b/ui/pages/settings/index.scss index 315a70456..958915e02 100644 --- a/ui/pages/settings/index.scss +++ b/ui/pages/settings/index.scss @@ -13,22 +13,136 @@ flex-flow: column nowrap; &__header { - display: flex; - flex-flow: row nowrap; - padding: 12px 24px; - align-items: center; - flex: 0 0 auto; + padding: 8px 24px 8px 24px; + position: relative; - &__title { - @include H3; + @media screen and (max-width: $break-small) { + background: var(--ui-1); + } - flex: 1 0 auto; + &__title-container { + display: flex; + flex-flow: row nowrap; + align-items: center; + flex: 0 0 auto; + &__close-button { + margin-left: auto; + } + + &__close-button::after { + content: '\00D7'; + font-size: 40px; + color: var(--ui-4); + cursor: pointer; + } + + &__title { + @include H3; + + flex: 1 0 auto; + + @media screen and (max-width: $break-small) { + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + max-width: 250px; + } + } + } + + &__search { @media screen and (max-width: $break-small) { - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; - max-width: 250px; + position: relative; + } + + @media screen and (min-width: $break-large) { + position: absolute; + right: 57px; + top: 10px; + width: 300px; + } + + @media screen and (min-width: $break-midpoint) { + width: 400px; + } + + &__list { + background: var(--ui-white); + box-sizing: border-box; + box-shadow: 0 0 14px rgba(0, 0, 0, 0.18); + border-radius: 6px; + position: absolute; + width: 100%; + z-index: 10; + + > div { + &:hover { + background: var(--ui-1); + } + } + + &__item { + transition: 200ms ease-in-out; + display: grid; + align-items: center; + padding: 16px; + border-top: 1px solid var(--ui-2); + cursor: pointer; + grid-template-columns: 16px minmax(20px, max-content) 8px auto; + gap: 8px; + + &__icon { + background: --ui-2; + height: 15px; + width: 15px; + margin-right: 16px; + } + + &__request, + &__tab, + &__section, + &__no-matching { + @include H6; + + color: var(--ui-4); + } + + &__tab-multiple-lines { + @media screen and (max-width: $break-small) { + width: 90px; + } + } + + &__section-multiple-lines { + @media screen and (max-width: $break-small) { + width: 170px; + margin-left: 10px; + } + } + + &__caret { + background-image: url('/images/caret-right.svg'); + width: 8px; + height: 10px; + background-size: contain; + background-repeat: no-repeat; + background-position: center; + margin: 0 4px; + + [dir='rtl'] & { + transform: rotate(180deg); + } + } + + &__link { + @include H6; + + display: inline; + color: var(--primary-blue); + margin-left: 3px; + } + } } } } @@ -110,17 +224,6 @@ } } - &__close-button { - margin-left: auto; - } - - &__close-button::after { - content: '\00D7'; - font-size: 40px; - color: var(--ui-4); - cursor: pointer; - } - &__content { display: flex; flex-flow: row nowrap; diff --git a/ui/pages/settings/info-tab/info-tab.component.js b/ui/pages/settings/info-tab/info-tab.component.js index 3dd69e4fd..c4de92b31 100644 --- a/ui/pages/settings/info-tab/info-tab.component.js +++ b/ui/pages/settings/info-tab/info-tab.component.js @@ -6,6 +6,10 @@ import { SUPPORT_REQUEST_LINK, } from '../../../helpers/constants/common'; import { isBeta } from '../../../helpers/utils/build-types'; +import { + getSettingsSectionNumber, + handleSettingsRefs, +} from '../../../helpers/utils/settings-search'; export default class InfoTab extends PureComponent { state = { @@ -16,13 +20,33 @@ export default class InfoTab extends PureComponent { t: PropTypes.func, }; + settingsRefs = Array( + getSettingsSectionNumber(this.context.t, this.context.t('about')), + ) + .fill(undefined) + .map(() => { + return React.createRef(); + }); + + componentDidUpdate() { + const { t } = this.context; + handleSettingsRefs(t, t('about'), this.settingsRefs); + } + + componentDidMount() { + const { t } = this.context; + handleSettingsRefs(t, t('about'), this.settingsRefs); + } + renderInfoLinks() { const { t } = this.context; return (
-
{t('links')}
-
+
+ {t('links')} +
+
-
+
-
+

-
+
-
+
-
+
+ + {t('orDeposit')} + + } + useIcon + iconFillColor="#d73a49" + type="danger" + /> + ) : ( + + {t('insufficientCurrency', [nativeCurrency, networkName])} + {t('buyOther', [nativeCurrency])} + + } + useIcon + iconFillColor="#d73a49" + type="danger" + /> + )} +
+ )} + { props.hasSimulationError = false; props.disabled = true; props.errorKey = TRANSACTION_ERROR_KEY; + props.currentTransaction = { + type: 'transfer', + }; const { queryByText, getByText } = renderWithProvider( , store, diff --git a/ui/components/app/confirm-page-container/confirm-page-container-content/index.scss b/ui/components/app/confirm-page-container/confirm-page-container-content/index.scss index b932af183..f52888db2 100644 --- a/ui/components/app/confirm-page-container/confirm-page-container-content/index.scss +++ b/ui/components/app/confirm-page-container/confirm-page-container-content/index.scss @@ -100,4 +100,12 @@ &__total-value { position: relative; } + + &__link { + background: transparent; + border: 0 transparent; + display: inline; + padding: 0; + font-size: $font-size-h7; + } } diff --git a/ui/components/app/confirm-page-container/confirm-page-container.component.js b/ui/components/app/confirm-page-container/confirm-page-container.component.js index 10f326594..25c9ff937 100644 --- a/ui/components/app/confirm-page-container/confirm-page-container.component.js +++ b/ui/components/app/confirm-page-container/confirm-page-container.component.js @@ -4,10 +4,15 @@ import PropTypes from 'prop-types'; import { EDIT_GAS_MODES } from '../../../../shared/constants/gas'; import { GasFeeContextProvider } from '../../../contexts/gasFee'; import { TRANSACTION_TYPES } from '../../../../shared/constants/transaction'; +import { + NETWORK_TO_NAME_MAP, + MAINNET_CHAIN_ID, +} from '../../../../shared/constants/network'; import { PageContainerFooter } from '../../ui/page-container'; import Dialog from '../../ui/dialog'; -import ErrorMessage from '../../ui/error-message'; +import Button from '../../ui/button'; +import ActionableMessage from '../../ui/actionable-message/actionable-message'; import SenderToRecipient from '../../ui/sender-to-recipient'; import NicknamePopovers from '../modals/nickname-popovers'; @@ -15,6 +20,10 @@ import NicknamePopovers from '../modals/nickname-popovers'; import AdvancedGasFeePopover from '../advanced-gas-fee-popover'; import EditGasFeePopover from '../edit-gas-fee-popover/edit-gas-fee-popover'; import EditGasPopover from '../edit-gas-popover'; +import ErrorMessage from '../../ui/error-message'; +import { INSUFFICIENT_FUNDS_ERROR_KEY } from '../../../helpers/constants/error-keys'; +import Typography from '../../ui/typography'; +import { TYPOGRAPHY } from '../../../helpers/constants/design-system'; import EnableEIP1559V2Notice from './enableEIP1559V2-notice'; import { @@ -87,6 +96,8 @@ export default class ConfirmPageContainer extends Component { contact: PropTypes.object, isOwnedAccount: PropTypes.bool, supportsEIP1559V2: PropTypes.bool, + nativeCurrency: PropTypes.string, + showBuyModal: PropTypes.func, }; render() { @@ -139,6 +150,8 @@ export default class ConfirmPageContainer extends Component { contact = {}, isOwnedAccount, supportsEIP1559V2, + nativeCurrency, + showBuyModal, } = this.props; const showAddToAddressDialog = @@ -152,6 +165,10 @@ export default class ConfirmPageContainer extends Component { currentTransaction.type === TRANSACTION_TYPES.DEPLOY_CONTRACT) && currentTransaction.txParams?.value === '0x0'; + const networkName = NETWORK_TO_NAME_MAP[currentTransaction.chainId]; + + const { t } = this.context; + return (
@@ -192,7 +209,7 @@ export default class ConfirmPageContainer extends Component { className="send__dialog" onClick={() => this.setState({ showNicknamePopovers: true })} > - {this.context.t('newAccountDetectedDialogMessage')} + {t('newAccountDetectedDialogMessage')} {this.state.showNicknamePopovers ? ( )} - {shouldDisplayWarning && ( + {shouldDisplayWarning && errorKey === INSUFFICIENT_FUNDS_ERROR_KEY && ( +
+ {currentTransaction.chainId === MAINNET_CHAIN_ID ? ( + + {t('insufficientCurrency', [nativeCurrency, networkName])} + + + {t('orDeposit')} + + } + useIcon + iconFillColor="#d73a49" + type="danger" + /> + ) : ( + + {t('insufficientCurrency', [nativeCurrency, networkName])} + {t('buyOther', [nativeCurrency])} + + } + useIcon + iconFillColor="#d73a49" + type="danger" + /> + )} +
+ )} + {shouldDisplayWarning && errorKey !== INSUFFICIENT_FUNDS_ERROR_KEY && (
@@ -245,14 +307,14 @@ export default class ConfirmPageContainer extends Component { {contentComponent && ( {unapprovedTxCount > 1 && ( - {this.context.t('rejectTxsN', [unapprovedTxCount])} + {t('rejectTxsN', [unapprovedTxCount])} )} diff --git a/ui/components/app/confirm-page-container/confirm-page-container.container.js b/ui/components/app/confirm-page-container/confirm-page-container.container.js index ec5ba84c0..d0d287aed 100644 --- a/ui/components/app/confirm-page-container/confirm-page-container.container.js +++ b/ui/components/app/confirm-page-container/confirm-page-container.container.js @@ -1,5 +1,6 @@ import { connect } from 'react-redux'; import { getAccountsWithLabels, getAddressBookEntry } from '../../../selectors'; +import { showModal } from '../../../store/actions'; import ConfirmPageContainer from './confirm-page-container.component'; function mapStateToProps(state, ownProps) { @@ -16,4 +17,13 @@ function mapStateToProps(state, ownProps) { }; } -export default connect(mapStateToProps)(ConfirmPageContainer); +const mapDispatchToProps = (dispatch) => { + return { + showBuyModal: () => dispatch(showModal({ name: 'DEPOSIT_ETHER' })), + }; +}; + +export default connect( + mapStateToProps, + mapDispatchToProps, +)(ConfirmPageContainer); diff --git a/ui/components/app/confirm-page-container/index.scss b/ui/components/app/confirm-page-container/index.scss index 9723c2d23..20aa1d94f 100644 --- a/ui/components/app/confirm-page-container/index.scss +++ b/ui/components/app/confirm-page-container/index.scss @@ -7,4 +7,12 @@ &__content-component-wrapper { height: 100%; } + + &__link { + background: transparent; + border: 0 transparent; + display: inline; + padding: 0; + font-size: $font-size-h7; + } } diff --git a/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js b/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js index f7f9774f3..a7202b6ca 100644 --- a/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js +++ b/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js @@ -60,6 +60,7 @@ import { import Typography from '../../components/ui/typography/typography'; import { MIN_GAS_LIMIT_DEC } from '../send/send.constants'; +import { NETWORK_TO_NAME_MAP } from '../../../shared/constants/network'; import TransactionAlerts from './transaction-alerts'; @@ -144,6 +145,7 @@ export default class ConfirmTransactionBase extends Component { hardwareWalletRequiresConnection: PropTypes.bool, isMultiLayerFeeNetwork: PropTypes.bool, eip1559V2Enabled: PropTypes.bool, + showBuyModal: PropTypes.func, }; state = { @@ -323,6 +325,7 @@ export default class ConfirmTransactionBase extends Component { supportsEIP1559, isMultiLayerFeeNetwork, nativeCurrency, + showBuyModal, } = this.props; const { t } = this.context; const { userAcknowledgedGasMissing } = this.state; @@ -335,6 +338,7 @@ export default class ConfirmTransactionBase extends Component { const hasSimulationError = Boolean(txData.simulationFails); const renderSimulationFailureWarning = hasSimulationError && !userAcknowledgedGasMissing; + const networkName = NETWORK_TO_NAME_MAP[txData.chainId]; const renderTotalMaxAmount = () => { if ( @@ -582,6 +586,11 @@ export default class ConfirmTransactionBase extends Component { this.setUserAcknowledgedGasMissing() } userAcknowledgedGasMissing={userAcknowledgedGasMissing} + chainId={txData.chainId} + nativeCurrency={nativeCurrency} + networkName={networkName} + showBuyModal={showBuyModal} + type={txData.type} /> this.handleCloseEditGas()} currentTransaction={txData} supportsEIP1559V2={this.supportsEIP1559V2} + nativeCurrency={nativeCurrency} /> ); diff --git a/ui/pages/confirm-transaction-base/confirm-transaction-base.container.js b/ui/pages/confirm-transaction-base/confirm-transaction-base.container.js index 8b13a1034..c6a6598ad 100644 --- a/ui/pages/confirm-transaction-base/confirm-transaction-base.container.js +++ b/ui/pages/confirm-transaction-base/confirm-transaction-base.container.js @@ -285,6 +285,7 @@ export const mapDispatchToProps = (dispatch) => { updateTransactionGasFees: (gasFees) => { dispatch(updateTransactionGasFees({ ...gasFees, expectHexWei: true })); }, + showBuyModal: () => dispatch(showModal({ name: 'DEPOSIT_ETHER' })), }; }; diff --git a/ui/pages/confirm-transaction-base/transaction-alerts/transaction-alerts.js b/ui/pages/confirm-transaction-base/transaction-alerts/transaction-alerts.js index ae6b195ad..bbb1883ab 100644 --- a/ui/pages/confirm-transaction-base/transaction-alerts/transaction-alerts.js +++ b/ui/pages/confirm-transaction-base/transaction-alerts/transaction-alerts.js @@ -3,19 +3,25 @@ import PropTypes from 'prop-types'; import { useSelector } from 'react-redux'; import { PRIORITY_LEVELS } from '../../../../shared/constants/gas'; -import { INSUFFICIENT_FUNDS_ERROR_KEY } from '../../../helpers/constants/error-keys'; import { submittedPendingTransactionsSelector } from '../../../selectors/transactions'; import { useGasFeeContext } from '../../../contexts/gasFee'; import { useI18nContext } from '../../../hooks/useI18nContext'; import ActionableMessage from '../../../components/ui/actionable-message/actionable-message'; -import ErrorMessage from '../../../components/ui/error-message'; import I18nValue from '../../../components/ui/i18n-value'; +import Button from '../../../components/ui/button'; import Typography from '../../../components/ui/typography'; import { TYPOGRAPHY } from '../../../helpers/constants/design-system'; +import { TRANSACTION_TYPES } from '../../../../shared/constants/transaction'; +import { MAINNET_CHAIN_ID } from '../../../../shared/constants/network'; const TransactionAlerts = ({ userAcknowledgedGasMissing, setUserAcknowledgedGasMissing, + chainId, + nativeCurrency, + networkName, + showBuyModal, + type, }) => { const { balanceError, @@ -90,7 +96,45 @@ const TransactionAlerts = ({ type="warning" /> )} - {balanceError && } + {balanceError && + chainId === MAINNET_CHAIN_ID && + type === TRANSACTION_TYPES.DEPLOY_CONTRACT ? ( + + {t('insufficientCurrency', [nativeCurrency, networkName])}{' '} + {' '} + {t('orDeposit')} + + } + useIcon + iconFillColor="#d73a49" + type="danger" + /> + ) : null} + {balanceError && + chainId !== MAINNET_CHAIN_ID && + type === TRANSACTION_TYPES.DEPLOY_CONTRACT ? ( + + {t('insufficientCurrency', [nativeCurrency, networkName])} + {t('buyOther', [nativeCurrency])} + + } + useIcon + iconFillColor="#d73a49" + type="danger" + /> + ) : null} {estimateUsed === PRIORITY_LEVELS.LOW && ( { supportsEIP1559V2: true, balanceError: true, }, + componentProps: { + nativeCurrency: 'ETH', + networkName: 'Ropsten', + showBuyModal: jest.fn(), + chainId: '0x1', + type: TRANSACTION_TYPES.DEPLOY_CONTRACT, + }, }); - expect(getByText('Insufficient funds.')).toBeInTheDocument(); + expect( + getByText( + /You do not have enough ETH in your account to pay for transaction fees on Ropsten network./u, + ), + ).toBeInTheDocument(); }); }); From 6e34b70db30ccba663e0a3b7b9ef6baa8f6af0a3 Mon Sep 17 00:00:00 2001 From: Brad Decker Date: Wed, 23 Feb 2022 09:15:41 -0600 Subject: [PATCH 016/105] add account_type and device_model to tx metrics (#13682) --- app/scripts/controllers/transactions/index.js | 4 ++ .../controllers/transactions/index.test.js | 16 ++++++ app/scripts/metamask-controller.js | 50 +++++++++++++++++++ shared/constants/hardware-wallets.js | 1 + .../app/account-menu/keyring-label.js | 2 +- 5 files changed, 72 insertions(+), 1 deletion(-) diff --git a/app/scripts/controllers/transactions/index.js b/app/scripts/controllers/transactions/index.js index 04eae26f8..5807e315b 100644 --- a/app/scripts/controllers/transactions/index.js +++ b/app/scripts/controllers/transactions/index.js @@ -130,6 +130,8 @@ export default class TransactionController extends EventEmitter { this.updateEventFragment = opts.updateEventFragment; this.finalizeEventFragment = opts.finalizeEventFragment; this.getEventFragmentById = opts.getEventFragmentById; + this.getDeviceModel = opts.getDeviceModel; + this.getAccountType = opts.getAccountType; this.memStore = new ObservableStore({}); this.query = new EthQuery(this.provider); @@ -1735,6 +1737,8 @@ export default class TransactionController extends EventEmitter { eip_1559_version: eip1559Version, gas_edit_type: 'none', gas_edit_attempted: 'none', + account_type: await this.getAccountType(this.getSelectedAddress()), + device_model: await this.getDeviceModel(this.getSelectedAddress()), }; const sensitiveProperties = { diff --git a/app/scripts/controllers/transactions/index.test.js b/app/scripts/controllers/transactions/index.test.js index c20fd5459..085dc3c04 100644 --- a/app/scripts/controllers/transactions/index.test.js +++ b/app/scripts/controllers/transactions/index.test.js @@ -82,6 +82,8 @@ describe('Transaction Controller', function () { getEventFragmentById: () => fragmentExists === false ? undefined : { id: 0 }, getEIP1559GasFeeEstimates: () => undefined, + getAccountType: () => 'MetaMask', + getDeviceModel: () => 'N/A', }); txController.nonceTracker.getNonceLock = () => Promise.resolve({ nextNonce: 0, releaseLock: noop }); @@ -1616,6 +1618,8 @@ describe('Transaction Controller', function () { referrer: 'metamask', source: 'user', type: TRANSACTION_TYPES.SIMPLE_SEND, + account_type: 'MetaMask', + device_model: 'N/A', }, sensitiveProperties: { default_gas: '0.000031501', @@ -1691,6 +1695,8 @@ describe('Transaction Controller', function () { referrer: 'metamask', source: 'user', type: TRANSACTION_TYPES.SIMPLE_SEND, + account_type: 'MetaMask', + device_model: 'N/A', }, sensitiveProperties: { default_gas: '0.000031501', @@ -1776,6 +1782,8 @@ describe('Transaction Controller', function () { referrer: 'other', source: 'dapp', type: TRANSACTION_TYPES.SIMPLE_SEND, + account_type: 'MetaMask', + device_model: 'N/A', }, sensitiveProperties: { default_gas: '0.000031501', @@ -1853,6 +1861,8 @@ describe('Transaction Controller', function () { referrer: 'other', source: 'dapp', type: TRANSACTION_TYPES.SIMPLE_SEND, + account_type: 'MetaMask', + device_model: 'N/A', }, sensitiveProperties: { default_gas: '0.000031501', @@ -1930,6 +1940,8 @@ describe('Transaction Controller', function () { referrer: 'other', source: 'dapp', type: TRANSACTION_TYPES.SIMPLE_SEND, + account_type: 'MetaMask', + device_model: 'N/A', }, sensitiveProperties: { gas_price: '2', @@ -1989,6 +2001,8 @@ describe('Transaction Controller', function () { eip_1559_version: '0', gas_edit_attempted: 'none', gas_edit_type: 'none', + account_type: 'MetaMask', + device_model: 'N/A', }, sensitiveProperties: { baz: 3.0, @@ -2058,6 +2072,8 @@ describe('Transaction Controller', function () { referrer: 'other', source: 'dapp', type: TRANSACTION_TYPES.SIMPLE_SEND, + account_type: 'MetaMask', + device_model: 'N/A', }, sensitiveProperties: { baz: 3.0, diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 7c9b935ee..7267aee63 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -700,6 +700,8 @@ export default class MetamaskController extends EventEmitter { getExternalPendingTransactions: this.getExternalPendingTransactions.bind( this, ), + getAccountType: this.getAccountType.bind(this), + getDeviceModel: this.getDeviceModel.bind(this), }); this.txController.on('newUnapprovedTx', () => opts.showUserConfirmation()); @@ -2182,6 +2184,54 @@ export default class MetamaskController extends EventEmitter { return true; } + /** + * Retrieves the keyring for the selected address and using the .type returns + * a subtype for the account. Either 'hardware', 'imported' or 'MetaMask'. + * + * @param {string} address - Address to retrieve keyring for + * @returns {'hardware' | 'imported' | 'MetaMask'} + */ + async getAccountType(address) { + const keyring = await this.keyringController.getKeyringForAccount(address); + switch (keyring.type) { + case KEYRING_TYPES.TREZOR: + case KEYRING_TYPES.LATTICE: + case KEYRING_TYPES.QR: + case KEYRING_TYPES.LEDGER: + return 'hardware'; + case KEYRING_TYPES.IMPORTED: + return 'imported'; + default: + return 'MetaMask'; + } + } + + /** + * Retrieves the keyring for the selected address and using the .type + * determines if a more specific name for the device is available. Returns + * 'N/A' for non hardware wallets. + * + * @param {string} address - Address to retrieve keyring for + * @returns {'ledger' | 'lattice' | 'N/A' | string} + */ + async getDeviceModel(address) { + const keyring = await this.keyringController.getKeyringForAccount(address); + switch (keyring.type) { + case KEYRING_TYPES.TREZOR: + return keyring.getModel(); + case KEYRING_TYPES.QR: + return keyring.getName(); + case KEYRING_TYPES.LEDGER: + // TODO: get model after ledger keyring exposes method + return DEVICE_NAMES.LEDGER; + case KEYRING_TYPES.LATTICE: + // TODO: get model after lattice keyring exposes method + return DEVICE_NAMES.LATTICE; + default: + return 'N/A'; + } + } + /** * get hardware account label * diff --git a/shared/constants/hardware-wallets.js b/shared/constants/hardware-wallets.js index 195a1b488..48bc65d3d 100644 --- a/shared/constants/hardware-wallets.js +++ b/shared/constants/hardware-wallets.js @@ -8,6 +8,7 @@ export const KEYRING_TYPES = { TREZOR: 'Trezor Hardware', LATTICE: 'Lattice Hardware', QR: 'QR Hardware Wallet Device', + IMPORTED: 'Simple Key Pair', }; export const DEVICE_NAMES = { diff --git a/ui/components/app/account-menu/keyring-label.js b/ui/components/app/account-menu/keyring-label.js index 794eed5e4..65007390c 100644 --- a/ui/components/app/account-menu/keyring-label.js +++ b/ui/components/app/account-menu/keyring-label.js @@ -22,7 +22,7 @@ export default function KeyRingLabel({ keyring }) { case KEYRING_TYPES.QR: label = KEYRING_NAMES.QR; break; - case 'Simple Key Pair': + case KEYRING_TYPES.IMPORTED: label = t('imported'); break; case KEYRING_TYPES.TREZOR: From 7ddd8cfede11ac406583c9d524de09e8bfc8d9c3 Mon Sep 17 00:00:00 2001 From: Alex Donesky Date: Wed, 23 Feb 2022 10:20:42 -0600 Subject: [PATCH 017/105] fix issue where token details page unnecessarily relies on send asset state (#13717) --- ui/pages/asset/components/token-asset.js | 12 +--- ui/pages/token-details/token-details-page.js | 10 +--- .../token-details/token-details-page.test.js | 56 +++++++++---------- 3 files changed, 31 insertions(+), 47 deletions(-) diff --git a/ui/pages/asset/components/token-asset.js b/ui/pages/asset/components/token-asset.js index 5fef2a741..893d2606c 100644 --- a/ui/pages/asset/components/token-asset.js +++ b/ui/pages/asset/components/token-asset.js @@ -17,8 +17,6 @@ import { import { getURLHostName } from '../../../helpers/utils/util'; import { showModal } from '../../../store/actions'; import { useNewMetricEvent } from '../../../hooks/useMetricEvent'; -import { ASSET_TYPES, updateSendAsset } from '../../../ducks/send'; - import AssetNavigation from './asset-navigation'; import AssetOptions from './asset-options'; @@ -70,13 +68,9 @@ export default function TokenAsset({ token }) { dispatch(showModal({ name: 'ACCOUNT_DETAILS' })); }} onViewTokenDetails={() => { - dispatch( - updateSendAsset({ - type: ASSET_TYPES.TOKEN, - details: { ...token }, - }), - ).then(() => { - history.push(TOKEN_DETAILS); + history.push({ + pathname: TOKEN_DETAILS, + state: { tokenAddress: token.address }, }); }} tokenSymbol={token.symbol} diff --git a/ui/pages/token-details/token-details-page.js b/ui/pages/token-details/token-details-page.js index 4289f4712..8fb34799a 100644 --- a/ui/pages/token-details/token-details-page.js +++ b/ui/pages/token-details/token-details-page.js @@ -2,7 +2,6 @@ import React, { useContext } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { Redirect, useHistory } from 'react-router-dom'; import { getTokens } from '../../ducks/metamask/metamask'; -import { getSendAssetAddress } from '../../ducks/send'; import { getUseTokenDetection, getTokenList } from '../../selectors'; import { useCopyToClipboard } from '../../hooks/useCopyToClipboard'; import { isEqualCaseInsensitive } from '../../helpers/utils/util'; @@ -31,18 +30,13 @@ export default function TokenDetailsPage() { const dispatch = useDispatch(); const history = useHistory(); const t = useContext(I18nContext); - const tokens = useSelector(getTokens); const tokenList = useSelector(getTokenList); const useTokenDetection = useSelector(getUseTokenDetection); - const assetAddress = useSelector((state) => ({ - asset: getSendAssetAddress(state), - })); + const tokenAddress = history?.location?.state?.tokenAddress; - const { asset: tokenAddress } = assetAddress; - - const tokenMetadata = tokenList[tokenAddress]; + const tokenMetadata = tokenList?.[tokenAddress]; const fileName = tokenMetadata?.iconUrl; const imagePath = useTokenDetection ? fileName diff --git a/ui/pages/token-details/token-details-page.test.js b/ui/pages/token-details/token-details-page.test.js index ae5a6a47e..623430725 100644 --- a/ui/pages/token-details/token-details-page.test.js +++ b/ui/pages/token-details/token-details-page.test.js @@ -3,8 +3,10 @@ import configureMockStore from 'redux-mock-store'; import { fireEvent } from '@testing-library/react'; import { renderWithProvider } from '../../../test/lib/render-helpers'; import Identicon from '../../components/ui/identicon/identicon.component'; +import { isEqualCaseInsensitive } from '../../helpers/utils/util'; import TokenDetailsPage from './token-details-page'; +const testTokenAddress = '0xaD6D458402F60fD3Bd25163575031ACDce07538A'; const state = { metamask: { selectedAddress: '0xAddress', @@ -86,10 +88,10 @@ const state = { }, tokens: [ { - address: '0xaD6D458402F60fD3Bd25163575031ACDce07538A', + address: testTokenAddress, symbol: 'DAA', decimals: 18, - image: null, + image: 'testImage', isERC721: false, }, { @@ -101,28 +103,23 @@ const state = { }, ], }, - send: { - asset: { - balance: '0x0', - type: 'TOKEN', - details: { - address: '0xaD6D458402F60fD3Bd25163575031ACDce07538A', - decimals: 18, - image: null, - isERC721: false, - symbol: 'DAI', - }, - }, - }, - token: { - address: '0x6b175474e89094c44da98b954eedeac495271d0f', - decimals: 18, - image: './images/eth_logo.svg', - isERC721: false, - symbol: 'ETH', - }, }; +jest.mock('react-router-dom', () => { + const original = jest.requireActual('react-router-dom'); + return { + ...original, + useHistory: () => ({ + push: jest.fn(), + location: { + state: { + tokenAddress: testTokenAddress, + }, + }, + }), + }; +}); + describe('TokenDetailsPage', () => { it('should render title "Token details" in token details page', () => { const store = configureMockStore()(state); @@ -139,14 +136,13 @@ describe('TokenDetailsPage', () => { }); it('should render an icon image', () => { - const image = ( - + const token = state.metamask.tokens.find(({ address }) => + isEqualCaseInsensitive(address, testTokenAddress), ); - expect(image).toBeDefined(); + const iconImage = ( + + ); + expect(iconImage).toBeDefined(); }); it('should render token contract address title in token details page', () => { @@ -158,7 +154,7 @@ describe('TokenDetailsPage', () => { it('should render token contract address in token details page', () => { const store = configureMockStore()(state); const { getByText } = renderWithProvider(, store); - expect(getByText(state.send.asset.details.address)).toBeInTheDocument(); + expect(getByText(testTokenAddress)).toBeInTheDocument(); }); it('should call copy button when click is simulated', () => { From 6b996a7979638906403e27eaf3d86546ca2e6cfb Mon Sep 17 00:00:00 2001 From: VSaric <92527393+VSaric@users.noreply.github.com> Date: Wed, 23 Feb 2022 17:36:58 +0100 Subject: [PATCH 018/105] Secure your wallet page (#13571) * Secure you wallet page * Remove seedPhraseIntroSidebarBulletThree from messages.json * Change the second bullet from Figma design * Requested review changes --- app/_locales/en/messages.json | 8 +- .../secure-your-wallet/index.scss | 1 + .../secure-your-wallet/secure-your-wallet.js | 105 +++++++++--------- 3 files changed, 55 insertions(+), 59 deletions(-) diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 7494f39df..d64299449 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -2590,22 +2590,22 @@ "message": "Secure my wallet (recommended)" }, "seedPhraseIntroSidebarBulletFour": { - "message": "Write down and store in multiple secret places." + "message": "Write down and store in multiple secret places" }, "seedPhraseIntroSidebarBulletOne": { "message": "Save in a password manager" }, "seedPhraseIntroSidebarBulletThree": { - "message": "Store in a safe-deposit box." + "message": "Store in a safe deposit box" }, "seedPhraseIntroSidebarBulletTwo": { - "message": "Store in a bank vault." + "message": "Store in a bank vault" }, "seedPhraseIntroSidebarCopyOne": { "message": "Your Secret Recovery Phrase is a 12-word phrase that is the “master key” to your wallet and your funds" }, "seedPhraseIntroSidebarCopyThree": { - "message": "If someone asks for your recovery phrase they are likely trying to scam you and steal your wallet funds" + "message": "If someone asks for your recovery phrase they are likely trying to scam you and steal your wallet funds." }, "seedPhraseIntroSidebarCopyTwo": { "message": "Never, ever share your Secret Recovery Phrase, not even with MetaMask!" diff --git a/ui/pages/onboarding-flow/secure-your-wallet/index.scss b/ui/pages/onboarding-flow/secure-your-wallet/index.scss index 3a1f80041..c76b01313 100644 --- a/ui/pages/onboarding-flow/secure-your-wallet/index.scss +++ b/ui/pages/onboarding-flow/secure-your-wallet/index.scss @@ -8,6 +8,7 @@ &__details { max-width: 550px; + padding-inline-end: 10px; } &__actions { diff --git a/ui/pages/onboarding-flow/secure-your-wallet/secure-your-wallet.js b/ui/pages/onboarding-flow/secure-your-wallet/secure-your-wallet.js index 4201b7986..1990b7a4c 100644 --- a/ui/pages/onboarding-flow/secure-your-wallet/secure-your-wallet.js +++ b/ui/pages/onboarding-flow/secure-your-wallet/secure-your-wallet.js @@ -9,6 +9,7 @@ import { TYPOGRAPHY, JUSTIFY_CONTENT, FONT_WEIGHT, + DISPLAY, } from '../../../helpers/constants/design-system'; import { ThreeStepProgressBar, @@ -59,16 +60,13 @@ export default function SecureYourWallet() { justifyContent={JUSTIFY_CONTENT.CENTER} textAlign={TEXT_ALIGN.CENTER} marginBottom={4} + marginTop={8} > {t('seedPhraseIntroTitle')} - + - - - {t('seedPhraseIntroSidebarTitleOne')} - - - {t('seedPhraseIntroSidebarCopyOne')} - - - - - {t('seedPhraseIntroSidebarTitleTwo')} - -
    -
  • {t('seedPhraseIntroSidebarBulletOne')}
  • -
  • {t('seedPhraseIntroSidebarBulletTwo')}
  • -
  • {t('seedPhraseIntroSidebarBulletThree')}
  • -
  • {t('seedPhraseIntroSidebarBulletFour')}
  • -
-
- - - {t('seedPhraseIntroSidebarTitleThree')} - - - {t('seedPhraseIntroSidebarCopyTwo')} - - - - - {t('seedPhraseIntroSidebarCopyThree')} - + + + + {t('seedPhraseIntroSidebarTitleOne')} + + + {t('seedPhraseIntroSidebarCopyOne')} + + + + + {t('seedPhraseIntroSidebarTitleTwo')} + +
    +
  • {t('seedPhraseIntroSidebarBulletOne')}
  • +
  • {t('seedPhraseIntroSidebarBulletThree')}
  • +
  • {t('seedPhraseIntroSidebarBulletFour')}
  • +
+
+ + + {t('seedPhraseIntroSidebarTitleThree')} + + + {t('seedPhraseIntroSidebarCopyTwo')} + + + + + {t('seedPhraseIntroSidebarCopyThree')} + +
); From a0602f1c0e24238c76bb111eb956e9f701cb3489 Mon Sep 17 00:00:00 2001 From: VSaric <92527393+VSaric@users.noreply.github.com> Date: Wed, 23 Feb 2022 17:41:06 +0100 Subject: [PATCH 019/105] Success page (#13570) --- app/_locales/en/messages.json | 3 +++ .../creation-successful/creation-successful.js | 4 ++-- .../creation-successful/creation-successful.test.js | 6 +++--- ui/pages/onboarding-flow/creation-successful/index.scss | 4 ++-- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index d64299449..796f3f641 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -1350,6 +1350,9 @@ "goerli": { "message": "Goerli Test Network" }, + "gotIt": { + "message": "Got it!" + }, "grantedToWithColon": { "message": "Granted to:" }, diff --git a/ui/pages/onboarding-flow/creation-successful/creation-successful.js b/ui/pages/onboarding-flow/creation-successful/creation-successful.js index 8a5c53ee9..fad8fe801 100644 --- a/ui/pages/onboarding-flow/creation-successful/creation-successful.js +++ b/ui/pages/onboarding-flow/creation-successful/creation-successful.js @@ -85,7 +85,7 @@ export default function CreationSuccessful() { type="link" rel="noopener noreferrer" > - {t('learnMore')} + {t('learnMoreUpperCase')} @@ -103,7 +103,7 @@ export default function CreationSuccessful() { rounded onClick={onComplete} > - {t('done')} + {t('gotIt')}
diff --git a/ui/pages/onboarding-flow/creation-successful/creation-successful.test.js b/ui/pages/onboarding-flow/creation-successful/creation-successful.test.js index f2277f4e6..2792b2a30 100644 --- a/ui/pages/onboarding-flow/creation-successful/creation-successful.test.js +++ b/ui/pages/onboarding-flow/creation-successful/creation-successful.test.js @@ -33,10 +33,10 @@ describe('Creation Successful Onboarding View', () => { .mockReturnValue({ push: pushMock }); }); - it('should call completeOnboarding in the background when "Done" button is clicked', () => { + it('should call completeOnboarding in the background when "Got it!" button is clicked', () => { const { getByText } = renderWithProvider(, store); - const doneButton = getByText('Done'); - fireEvent.click(doneButton); + const gotItButton = getByText('Got it!'); + fireEvent.click(gotItButton); expect(completeOnboardingStub).toHaveBeenCalledTimes(1); }); diff --git a/ui/pages/onboarding-flow/creation-successful/index.scss b/ui/pages/onboarding-flow/creation-successful/index.scss index 98cf6b80e..54273b1e0 100644 --- a/ui/pages/onboarding-flow/creation-successful/index.scss +++ b/ui/pages/onboarding-flow/creation-successful/index.scss @@ -31,8 +31,8 @@ button { margin-top: 14px; - max-width: 60%; - padding: 18px 0; + max-width: 280px; + padding: 16px 0; } } } From 93dd85fb2cd768219b4b223f306b1e47ead77fc8 Mon Sep 17 00:00:00 2001 From: Alex Donesky Date: Wed, 23 Feb 2022 11:27:13 -0600 Subject: [PATCH 020/105] add e2e test for deterministic account address generation (#13668) * add e2e test for deterministic account address generation * extract import srp onboarding flow into helper function --- test/e2e/helpers.js | 68 +++++++++++ test/e2e/tests/add-account.spec.js | 163 +++++++++++++++++++++++++- test/e2e/tests/from-import-ui.spec.js | 76 ++---------- 3 files changed, 236 insertions(+), 71 deletions(-) diff --git a/test/e2e/helpers.js b/test/e2e/helpers.js index a3db2d3e7..aebc6b673 100644 --- a/test/e2e/helpers.js +++ b/test/e2e/helpers.js @@ -7,6 +7,7 @@ const { createSegmentServer, } = require('../../development/lib/create-segment-server'); const { setupMocking } = require('../../development/mock-e2e'); +const enLocaleMessages = require('../../app/_locales/en/messages.json'); const Ganache = require('./ganache'); const FixtureServer = require('./fixture-server'); const { buildWebDriver } = require('./webdriver'); @@ -206,6 +207,72 @@ const connectDappWithExtensionPopup = async (driver) => { await driver.delay(regularDelayMs); }; +const completeImportSRPOnboardingFlow = async ( + driver, + seedPhrase, + password, +) => { + if (process.env.ONBOARDING_V2 === '1') { + // welcome + await driver.clickElement('[data-testid="onboarding-import-wallet"]'); + + // metrics + await driver.clickElement('[data-testid="metametrics-no-thanks"]'); + + // import with recovery phrase + await driver.fill('[data-testid="import-srp-text"]', seedPhrase); + await driver.clickElement('[data-testid="import-srp-confirm"]'); + + // create password + await driver.fill('[data-testid="create-password-new"]', password); + await driver.fill('[data-testid="create-password-confirm"]', password); + await driver.clickElement('[data-testid="create-password-terms"]'); + await driver.clickElement('[data-testid="create-password-import"]'); + + // complete + await driver.clickElement('[data-testid="onboarding-complete-done"]'); + + // pin extension + await driver.clickElement('[data-testid="pin-extension-next"]'); + await driver.clickElement('[data-testid="pin-extension-done"]'); + } else { + // clicks the continue button on the welcome screen + await driver.findElement('.welcome-page__header'); + await driver.clickElement({ + text: enLocaleMessages.getStarted.message, + tag: 'button', + }); + + // clicks the "Import Wallet" option + await driver.clickElement({ text: 'Import wallet', tag: 'button' }); + + // clicks the "No thanks" option on the metametrics opt-in screen + await driver.clickElement('.btn-secondary'); + + // Import Secret Recovery Phrase + await driver.fill( + 'input[placeholder="Enter your Secret Recovery Phrase"]', + seedPhrase, + ); + + await driver.fill('#password', password); + await driver.fill('#confirm-password', password); + + await driver.clickElement( + '[data-testid="create-new-vault__terms-checkbox"]', + ); + + await driver.clickElement({ text: 'Import', tag: 'button' }); + + // clicks through the success screen + await driver.findElement({ text: 'Congratulations', tag: 'div' }); + await driver.clickElement({ + text: enLocaleMessages.endOfFlowMessage10.message, + tag: 'button', + }); + } +}; + module.exports = { getWindowHandles, convertToHexValue, @@ -214,4 +281,5 @@ module.exports = { largeDelayMs, withFixtures, connectDappWithExtensionPopup, + completeImportSRPOnboardingFlow, }; diff --git a/test/e2e/tests/add-account.spec.js b/test/e2e/tests/add-account.spec.js index e358660d0..5e0b21c58 100644 --- a/test/e2e/tests/add-account.spec.js +++ b/test/e2e/tests/add-account.spec.js @@ -1,16 +1,26 @@ const { strict: assert } = require('assert'); -const { convertToHexValue, withFixtures } = require('../helpers'); +const { + convertToHexValue, + withFixtures, + regularDelayMs, + completeImportSRPOnboardingFlow, +} = require('../helpers'); +const enLocaleMessages = require('../../../app/_locales/en/messages.json'); describe('Add account', function () { + const testSeedPhrase = + 'forum vessel pink push lonely enact gentle tail admit parrot grunt dress'; + const testPassword = 'correct horse battery staple'; const ganacheOptions = { accounts: [ { secretKey: - '0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC', + '0x53CB0AB5226EEBF4D872113D98332C1555DC304443BEE1CF759D15798D3C55A9', balance: convertToHexValue(25000000000000000000), }, ], }; + it('should display correct new account name after create', async function () { await withFixtures( { @@ -36,4 +46,153 @@ describe('Add account', function () { }, ); }); + + it('should add the same account addresses when a secret recovery phrase is imported, the account is locked, and the same secret recovery phrase is imported again', async function () { + await withFixtures( + { + fixtures: 'onboarding', + ganacheOptions, + title: this.test.title, + failOnConsoleError: false, + }, + async ({ driver }) => { + await driver.navigate(); + + await completeImportSRPOnboardingFlow( + driver, + testSeedPhrase, + testPassword, + ); + + await driver.clickElement('.account-menu__icon'); + await driver.clickElement({ text: 'Create Account', tag: 'div' }); + await driver.fill('.new-account-create-form input', '2nd account'); + await driver.clickElement({ text: 'Create', tag: 'button' }); + + await driver.clickElement( + '[data-testid="account-options-menu-button"]', + ); + await driver.clickElement( + '[data-testid="account-options-menu__account-details"]', + ); + + const detailsModal = await driver.findVisibleElement('span .modal'); + // get the public address for the "second account" + const secondAccountAddress = await driver.findElement( + '.qr-code__address', + ); + const secondAccountPublicAddress = await secondAccountAddress.getText(); + + await driver.clickElement('.account-modal__close'); + await detailsModal.waitForElementState('hidden'); + + // generate a third accound + await driver.clickElement('.account-menu__icon'); + await driver.clickElement({ text: 'Create Account', tag: 'div' }); + await driver.fill('.new-account-create-form input', '3rd account'); + await driver.clickElement({ text: 'Create', tag: 'button' }); + + await driver.clickElement( + '[data-testid="account-options-menu-button"]', + ); + await driver.clickElement( + '[data-testid="account-options-menu__account-details"]', + ); + + // get the public address for the "third account" + const secondDetailsModal = await driver.findVisibleElement( + 'span .modal', + ); + const thirdAccountAddress = await driver.findElement( + '.qr-code__address', + ); + const thirdAccountPublicAddress = await thirdAccountAddress.getText(); + + await driver.clickElement('.account-modal__close'); + await secondDetailsModal.waitForElementState('hidden'); + + // lock account + await driver.clickElement('.account-menu__icon'); + await driver.delay(regularDelayMs); + + const lockButton = await driver.findClickableElement( + '.account-menu__lock-button', + ); + await lockButton.click(); + await driver.delay(regularDelayMs); + + // restore same seed phrase + const restoreSeedLink = await driver.findClickableElement( + '.unlock-page__link--import', + ); + + await restoreSeedLink.click(); + await driver.delay(regularDelayMs); + + await driver.fill( + 'input[placeholder="Enter your Secret Recovery Phrase"]', + testSeedPhrase, + ); + await driver.delay(regularDelayMs); + + await driver.fill('#password', 'correct horse battery staple'); + await driver.fill('#confirm-password', 'correct horse battery staple'); + await driver.clickElement({ + text: enLocaleMessages.restore.message, + tag: 'button', + }); + await driver.delay(regularDelayMs); + + // recreate a "2nd account" + await driver.clickElement('.account-menu__icon'); + await driver.clickElement({ text: 'Create Account', tag: 'div' }); + await driver.fill('.new-account-create-form input', '2nd account'); + await driver.clickElement({ text: 'Create', tag: 'button' }); + + await driver.clickElement( + '[data-testid="account-options-menu-button"]', + ); + await driver.clickElement( + '[data-testid="account-options-menu__account-details"]', + ); + const thirdDetailsModal = await driver.findVisibleElement( + 'span .modal', + ); + // get the public address for the "second account" + const recreatedSecondAccountAddress = await driver.findElement( + '.qr-code__address', + ); + + assert.equal( + await recreatedSecondAccountAddress.getText(), + secondAccountPublicAddress, + ); + + await driver.clickElement('.account-modal__close'); + await thirdDetailsModal.waitForElementState('hidden'); + + // re-generate a third accound + await driver.clickElement('.account-menu__icon'); + await driver.clickElement({ text: 'Create Account', tag: 'div' }); + await driver.fill('.new-account-create-form input', '3rd account'); + await driver.clickElement({ text: 'Create', tag: 'button' }); + + await driver.clickElement( + '[data-testid="account-options-menu-button"]', + ); + await driver.clickElement( + '[data-testid="account-options-menu__account-details"]', + ); + + // get the public address for the "third account" + const recreatedThirdAccountAddress = await driver.findElement( + '.qr-code__address', + ); + assert.strictEqual( + await recreatedThirdAccountAddress.getText(), + thirdAccountPublicAddress, + ); + }, + ); + }); }); diff --git a/test/e2e/tests/from-import-ui.spec.js b/test/e2e/tests/from-import-ui.spec.js index a1a8e1da4..1b3e12667 100644 --- a/test/e2e/tests/from-import-ui.spec.js +++ b/test/e2e/tests/from-import-ui.spec.js @@ -4,8 +4,8 @@ const { withFixtures, regularDelayMs, largeDelayMs, + completeImportSRPOnboardingFlow, } = require('../helpers'); -const enLocaleMessages = require('../../../app/_locales/en/messages.json'); describe('Metamask Import UI', function () { it('Importing wallet using Secret Recovery Phrase', async function () { @@ -20,6 +20,7 @@ describe('Metamask Import UI', function () { }; const testSeedPhrase = 'forum vessel pink push lonely enact gentle tail admit parrot grunt dress'; + const testPassword = 'correct horse battery staple'; const testAddress = '0x0Cc5261AB8cE458dc977078A3623E2BaDD27afD3'; await withFixtures( @@ -32,74 +33,11 @@ describe('Metamask Import UI', function () { async ({ driver }) => { await driver.navigate(); - if (process.env.ONBOARDING_V2 === '1') { - // welcome - await driver.clickElement('[data-testid="onboarding-import-wallet"]'); - - // metrics - await driver.clickElement('[data-testid="metametrics-no-thanks"]'); - - // import with recovery phrase - await driver.fill('[data-testid="import-srp-text"]', testSeedPhrase); - await driver.clickElement('[data-testid="import-srp-confirm"]'); - - // create password - await driver.fill( - '[data-testid="create-password-new"]', - 'correct horse battery staple', - ); - await driver.fill( - '[data-testid="create-password-confirm"]', - 'correct horse battery staple', - ); - await driver.clickElement('[data-testid="create-password-terms"]'); - await driver.clickElement('[data-testid="create-password-import"]'); - - // complete - await driver.clickElement('[data-testid="onboarding-complete-done"]'); - - // pin extension - await driver.clickElement('[data-testid="pin-extension-next"]'); - await driver.clickElement('[data-testid="pin-extension-done"]'); - } else { - // clicks the continue button on the welcome screen - await driver.findElement('.welcome-page__header'); - await driver.clickElement({ - text: enLocaleMessages.getStarted.message, - tag: 'button', - }); - - // clicks the "Import Wallet" option - await driver.clickElement({ text: 'Import wallet', tag: 'button' }); - - // clicks the "No thanks" option on the metametrics opt-in screen - await driver.clickElement('.btn-secondary'); - - // Import Secret Recovery Phrase - await driver.fill( - 'input[placeholder="Enter your Secret Recovery Phrase"]', - testSeedPhrase, - ); - - await driver.fill('#password', 'correct horse battery staple'); - await driver.fill( - '#confirm-password', - 'correct horse battery staple', - ); - - await driver.clickElement( - '[data-testid="create-new-vault__terms-checkbox"]', - ); - - await driver.clickElement({ text: 'Import', tag: 'button' }); - - // clicks through the success screen - await driver.findElement({ text: 'Congratulations', tag: 'div' }); - await driver.clickElement({ - text: enLocaleMessages.endOfFlowMessage10.message, - tag: 'button', - }); - } + await completeImportSRPOnboardingFlow( + driver, + testSeedPhrase, + testPassword, + ); // Show account information await driver.clickElement( From e600ecedbfab4e8b2734d3ce07ec4c6497bac31b Mon Sep 17 00:00:00 2001 From: ryanml Date: Wed, 23 Feb 2022 11:57:16 -0700 Subject: [PATCH 021/105] Fixing blockies on token detail page (#13728) --- ui/pages/token-details/token-details-page.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/pages/token-details/token-details-page.js b/ui/pages/token-details/token-details-page.js index 8fb34799a..01fdd17f8 100644 --- a/ui/pages/token-details/token-details-page.js +++ b/ui/pages/token-details/token-details-page.js @@ -5,7 +5,7 @@ import { getTokens } from '../../ducks/metamask/metamask'; import { getUseTokenDetection, getTokenList } from '../../selectors'; import { useCopyToClipboard } from '../../hooks/useCopyToClipboard'; import { isEqualCaseInsensitive } from '../../helpers/utils/util'; -import Identicon from '../../components/ui/identicon/identicon.component'; +import Identicon from '../../components/ui/identicon'; import { I18nContext } from '../../contexts/i18n'; import { useTokenTracker } from '../../hooks/useTokenTracker'; import { useTokenFiatAmount } from '../../hooks/useTokenFiatAmount'; From 00b178e43fc6d235ddc3e770127d0400d9a800cd Mon Sep 17 00:00:00 2001 From: Alex Donesky Date: Wed, 23 Feb 2022 13:05:55 -0600 Subject: [PATCH 022/105] fix broken e2e test from out of sync test merged (#13734) --- test/e2e/tests/add-account.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/e2e/tests/add-account.spec.js b/test/e2e/tests/add-account.spec.js index 5e0b21c58..3c38f8e72 100644 --- a/test/e2e/tests/add-account.spec.js +++ b/test/e2e/tests/add-account.spec.js @@ -123,7 +123,7 @@ describe('Add account', function () { // restore same seed phrase const restoreSeedLink = await driver.findClickableElement( - '.unlock-page__link--import', + '.unlock-page__link', ); await restoreSeedLink.click(); From 219f8e6299f81b9927c1d04522e9ae507620467c Mon Sep 17 00:00:00 2001 From: Hassan Malik <41640681+hmalik88@users.noreply.github.com> Date: Wed, 23 Feb 2022 15:16:41 -0500 Subject: [PATCH 023/105] minor fixes per snaps pr comments (#13721) --- ui/pages/settings/flask/view-snap/index.scss | 8 ++++++-- ui/pages/settings/flask/view-snap/view-snap.js | 11 ++++------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/ui/pages/settings/flask/view-snap/index.scss b/ui/pages/settings/flask/view-snap/index.scss index d0efae6f7..4d672ef7a 100644 --- a/ui/pages/settings/flask/view-snap/index.scss +++ b/ui/pages/settings/flask/view-snap/index.scss @@ -6,8 +6,6 @@ } &__subheader { - @include H4; - padding: 16px 4px; border-bottom: 1px solid var(--alto); margin-right: 24px; @@ -51,12 +49,18 @@ } &__toggle-container { + margin-left: auto; + @media screen and (max-width: $break-small) { padding-left: 0; display: inline-block; } } + &__toggle-button { + margin-right: -12px; + } + &__content-container { @media screen and (max-width: $break-small) { width: 100%; diff --git a/ui/pages/settings/flask/view-snap/view-snap.js b/ui/pages/settings/flask/view-snap/view-snap.js index 14c2b050e..5986b7b5b 100644 --- a/ui/pages/settings/flask/view-snap/view-snap.js +++ b/ui/pages/settings/flask/view-snap/view-snap.js @@ -86,15 +86,12 @@ function ViewSnap() { url={authorshipPillUrl} /> - + @@ -155,8 +152,8 @@ function ViewSnap() { css={{ maxWidth: '175px', }} - onClick={async () => { - await dispatch(removeSnap(snap)); + onClick={() => { + dispatch(removeSnap(snap)); }} > {t('removeSnap')} From f49e5076f3c0ba2c372148a5617949adc46f34fb Mon Sep 17 00:00:00 2001 From: Mark Stacey Date: Wed, 23 Feb 2022 17:00:26 -0330 Subject: [PATCH 024/105] Refactor: Extract SRP input from create vault component (#13720) This is a pure refactor that extracts the SRP input from the `CreateNewVault` component. This is intended to make future changes to the SRP input easier, and to reduce duplication between the old and new onboarding flows. Extensive unit tests have been added for the new SRP input component. A new test library was added (`@testing-library/user-event`) for simulating user events with components rendered using the `@testing-library` library. A new helper method has been added (`renderWithLocalization`) for rendering components using `@testing-library` with just our localization contexts added as a wrapper. The localization contexts were already added by the `renderWithProviders` helper function, but there is no need for a Redux provider in these unit tests. --- package.json | 1 + test/helpers/setup-helper.js | 8 + test/lib/render-helpers.js | 14 + ui/components/app/app-components.scss | 1 + .../app/create-new-vault/create-new-vault.js | 89 +-- .../create-new-vault/create-new-vault.scss | 23 - ui/components/app/srp-input/index.js | 1 + .../parse-secret-recovery-phrase'.test.js | 0 .../parse-secret-recovery-phrase.js | 0 ui/components/app/srp-input/srp-input.js | 112 ++++ ui/components/app/srp-input/srp-input.scss | 24 + .../app/srp-input/srp-input.stories.js | 23 + ui/components/app/srp-input/srp-input.test.js | 571 ++++++++++++++++++ yarn.lock | 7 + 14 files changed, 766 insertions(+), 108 deletions(-) create mode 100644 ui/components/app/srp-input/index.js rename ui/components/app/{create-new-vault => srp-input}/parse-secret-recovery-phrase'.test.js (100%) rename ui/components/app/{create-new-vault => srp-input}/parse-secret-recovery-phrase.js (100%) create mode 100644 ui/components/app/srp-input/srp-input.js create mode 100644 ui/components/app/srp-input/srp-input.scss create mode 100644 ui/components/app/srp-input/srp-input.stories.js create mode 100644 ui/components/app/srp-input/srp-input.test.js diff --git a/package.json b/package.json index ec8231202..754757ddb 100644 --- a/package.json +++ b/package.json @@ -262,6 +262,7 @@ "@testing-library/jest-dom": "^5.11.10", "@testing-library/react": "^10.4.8", "@testing-library/react-hooks": "^3.2.1", + "@testing-library/user-event": "^13.5.0", "@types/react": "^16.9.53", "addons-linter": "1.14.0", "babelify": "^10.0.0", diff --git a/test/helpers/setup-helper.js b/test/helpers/setup-helper.js index f9d2112aa..9d4b7d892 100644 --- a/test/helpers/setup-helper.js +++ b/test/helpers/setup-helper.js @@ -79,3 +79,11 @@ if (!window.crypto.getRandomValues) { // eslint-disable-next-line node/global-require window.crypto.getRandomValues = require('polyfill-crypto.getrandomvalues'); } + +// Used to test `clearClipboard` function +if (!window.navigator.clipboard) { + window.navigator.clipboard = {}; +} +if (!window.navigator.clipboard.writeText) { + window.navigator.clipboard.writeText = () => undefined; +} diff --git a/test/lib/render-helpers.js b/test/lib/render-helpers.js index 37c5d6370..9fc7aad16 100644 --- a/test/lib/render-helpers.js +++ b/test/lib/render-helpers.js @@ -95,3 +95,17 @@ export function renderWithProvider(component, store) { return render(component, { wrapper: Wrapper }); } + +export function renderWithLocalization(component) { + const Wrapper = ({ children }) => ( + + {children} + + ); + + Wrapper.propTypes = { + children: PropTypes.node, + }; + + return render(component, { wrapper: Wrapper }); +} diff --git a/ui/components/app/app-components.scss b/ui/components/app/app-components.scss index c11ff6027..a135c0766 100644 --- a/ui/components/app/app-components.scss +++ b/ui/components/app/app-components.scss @@ -54,6 +54,7 @@ @import 'selected-account/index'; @import 'signature-request/index'; @import 'signature-request-original/index'; +@import 'srp-input/srp-input'; @import 'tab-bar/index'; @import 'token-cell/token-cell'; @import 'token-list-display/token-list-display'; diff --git a/ui/components/app/create-new-vault/create-new-vault.js b/ui/components/app/create-new-vault/create-new-vault.js index 3c2b5007b..05ca7a716 100644 --- a/ui/components/app/create-new-vault/create-new-vault.js +++ b/ui/components/app/create-new-vault/create-new-vault.js @@ -1,17 +1,12 @@ -import { ethers } from 'ethers'; import React, { useCallback, useContext, useState } from 'react'; import PropTypes from 'prop-types'; import { useI18nContext } from '../../../hooks/useI18nContext'; import { MetaMetricsContext } from '../../../contexts/metametrics'; import TextField from '../../ui/text-field'; import Button from '../../ui/button'; -import { clearClipboard } from '../../../helpers/utils/util'; import CheckBox from '../../ui/check-box'; import Typography from '../../ui/typography'; -import { COLORS } from '../../../helpers/constants/design-system'; -import { parseSecretRecoveryPhrase } from './parse-secret-recovery-phrase'; - -const { isValidMnemonic } = ethers.utils; +import SrpInput from '../srp-input'; export default function CreateNewVault({ disabled = false, @@ -24,33 +19,11 @@ export default function CreateNewVault({ const [password, setPassword] = useState(''); const [passwordError, setPasswordError] = useState(''); const [seedPhrase, setSeedPhrase] = useState(''); - const [seedPhraseError, setSeedPhraseError] = useState(''); - const [showSeedPhrase, setShowSeedPhrase] = useState(false); const [termsChecked, setTermsChecked] = useState(false); const t = useI18nContext(); const metricsEvent = useContext(MetaMetricsContext); - const onSeedPhraseChange = useCallback( - (rawSeedPhrase) => { - let newSeedPhraseError = ''; - - if (rawSeedPhrase) { - const parsedSeedPhrase = parseSecretRecoveryPhrase(rawSeedPhrase); - const wordCount = parsedSeedPhrase.split(/\s/u).length; - if (wordCount % 3 !== 0 || wordCount > 24 || wordCount < 12) { - newSeedPhraseError = t('seedPhraseReq'); - } else if (!isValidMnemonic(parsedSeedPhrase)) { - newSeedPhraseError = t('invalidSeedPhrase'); - } - } - - setSeedPhrase(rawSeedPhrase); - setSeedPhraseError(newSeedPhraseError); - }, - [setSeedPhrase, setSeedPhraseError, t], - ); - const onPasswordChange = useCallback( (newPassword) => { let newConfirmPasswordError = ''; @@ -93,8 +66,7 @@ export default function CreateNewVault({ seedPhrase && (!includeTerms || termsChecked) && !passwordError && - !confirmPasswordError && - !seedPhraseError; + !confirmPasswordError; const onImport = useCallback( async (event) => { @@ -104,7 +76,7 @@ export default function CreateNewVault({ return; } - await onSubmit(password, parseSecretRecoveryPhrase(seedPhrase)); + await onSubmit(password, seedPhrase); }, [isValid, onSubmit, password, seedPhrase], ); @@ -121,10 +93,6 @@ export default function CreateNewVault({ setTermsChecked((currentTermsChecked) => !currentTermsChecked); }, [metricsEvent]); - const toggleShowSeedPhrase = useCallback(() => { - setShowSeedPhrase((currentShowSeedPhrase) => !currentShowSeedPhrase); - }, []); - const termsOfUse = t('acceptTermsOfUse', [
- - {showSeedPhrase ? ( -