mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 18:00:18 +01:00
Merge pull request #9160 from MetaMask/Version-v8.0.7
Version v8.0.7 RC
This commit is contained in:
commit
1a5218ce8e
@ -38,6 +38,9 @@ workflows:
|
||||
- test-unit-global:
|
||||
requires:
|
||||
- prep-deps
|
||||
- validate-source-maps:
|
||||
requires:
|
||||
- prep-build
|
||||
- test-mozilla-lint:
|
||||
requires:
|
||||
- prep-deps
|
||||
@ -49,6 +52,7 @@ workflows:
|
||||
- test-lint-lockfile
|
||||
- test-unit
|
||||
- test-unit-global
|
||||
- validate-source-maps
|
||||
- test-mozilla-lint
|
||||
- test-e2e-chrome
|
||||
- test-e2e-firefox
|
||||
@ -358,6 +362,18 @@ jobs:
|
||||
- run:
|
||||
name: test:unit:global
|
||||
command: yarn test:unit:global
|
||||
|
||||
validate-source-maps:
|
||||
docker:
|
||||
- image: circleci/node@sha256:e16740707de2ebed45c05d507f33ef204902349c7356d720610b5ec6a35d3d88
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: .
|
||||
- run:
|
||||
name: Validate source maps
|
||||
command: yarn validate-source-maps
|
||||
|
||||
test-mozilla-lint:
|
||||
docker:
|
||||
- image: circleci/node@sha256:e16740707de2ebed45c05d507f33ef204902349c7356d720610b5ec6a35d3d88
|
||||
|
@ -44,7 +44,6 @@ install_github_cli
|
||||
printf '%s\n' "Creating a Pull Request to sync 'master' with 'develop'"
|
||||
|
||||
if ! hub pull-request \
|
||||
--reviewer '@MetaMask/extension-release-team' \
|
||||
--message "Master => develop" --message 'Merge latest release back into develop' \
|
||||
--base "$CIRCLE_PROJECT_USERNAME:$base_branch" \
|
||||
--head "$CIRCLE_PROJECT_USERNAME:$CIRCLE_BRANCH";
|
||||
|
@ -45,7 +45,6 @@ install_github_cli
|
||||
printf '%s\n' "Creating a Pull Request for $version on GitHub"
|
||||
|
||||
if ! hub pull-request \
|
||||
--reviewer '@MetaMask/extension-release-team' \
|
||||
--message "${CIRCLE_BRANCH/-/ } RC" --message ':package: :rocket:' \
|
||||
--base "$CIRCLE_PROJECT_USERNAME:$base_branch" \
|
||||
--head "$CIRCLE_PROJECT_USERNAME:$CIRCLE_BRANCH";
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -34,6 +34,7 @@ builds.zip
|
||||
|
||||
test-artifacts
|
||||
test-builds
|
||||
build-artifacts
|
||||
|
||||
#ignore css output and sourcemaps
|
||||
ui/app/css/output/
|
||||
|
12
CHANGELOG.md
12
CHANGELOG.md
@ -2,6 +2,18 @@
|
||||
|
||||
## Current Develop Branch
|
||||
|
||||
## 8.0.7 Fri Aug 07 2020
|
||||
- [#9065](https://github.com/MetaMask/metamask-extension/pull/9065): Change title of "Reveal Seed Words" page to "Reveal Seed Phrase"
|
||||
- [#8974](https://github.com/MetaMask/metamask-extension/pull/8974): Add tooltip to copy button for contacts and seed phrase
|
||||
- [#9063](https://github.com/MetaMask/metamask-extension/pull/9063): Fix broken UI upon failed password validation
|
||||
- [#9075](https://github.com/MetaMask/metamask-extension/pull/9075): Fix shifted popup notification when browser is in fullscreen on macOS
|
||||
- [#9085](https://github.com/MetaMask/metamask-extension/pull/9085): Support longer text in network dropdown
|
||||
- [#8873](https://github.com/MetaMask/metamask-extension/pull/8873): Fix onboarding bug where user can be asked to verify seed phrase twice
|
||||
- [#9104](https://github.com/MetaMask/metamask-extension/pull/9104): Replace "Email us" button with "Contact us" button
|
||||
- [#9137](https://github.com/MetaMask/metamask-extension/pull/9137): Fix bug where `accountsChanged` events stop after a dapp connection is closed.
|
||||
- [#9152](https://github.com/MetaMask/metamask-extension/pull/9152): Fix network name alignment
|
||||
- [#9144](https://github.com/MetaMask/metamask-extension/pull/9144): Add web3 usage metrics and prepare for web3 removal
|
||||
|
||||
## 8.0.6 Wed Jul 22 2020
|
||||
- [#9030](https://github.com/MetaMask/metamask-extension/pull/9030): Hide "delete" button when editing contact of wallet account
|
||||
- [#9031](https://github.com/MetaMask/metamask-extension/pull/9031): Fix crash upon removing contact
|
||||
|
@ -359,9 +359,6 @@
|
||||
"editContact": {
|
||||
"message": "ዕውቂያን አርትዕ"
|
||||
},
|
||||
"emailUs": {
|
||||
"message": "ኢሜይል ያድርጉልን!"
|
||||
},
|
||||
"endOfFlowMessage1": {
|
||||
"message": "ፈተናውን አልፈዋል - የዘር ሐረግዎን ደህንነቱ በተጠበቀ መንገድ ያስቀምጡ፣ የእርስዎ ሃላፊነት ነው! "
|
||||
},
|
||||
|
@ -359,9 +359,6 @@
|
||||
"editContact": {
|
||||
"message": "تعديل جهة الاتصال"
|
||||
},
|
||||
"emailUs": {
|
||||
"message": "راسلنا عبر البريد الإلكتروني!"
|
||||
},
|
||||
"endOfFlowMessage1": {
|
||||
"message": "لقد نجحت في الاختبار - احتفظ بعبارة الأمان الخاصة بك في مكان آمن، إنها مسؤوليتك!"
|
||||
},
|
||||
|
@ -359,9 +359,6 @@
|
||||
"editContact": {
|
||||
"message": "Редактиране на контакт"
|
||||
},
|
||||
"emailUs": {
|
||||
"message": "Изпратете ни имейл!"
|
||||
},
|
||||
"endOfFlowMessage1": {
|
||||
"message": "Преминахте теста - пазете основната си фраза на сигурно място, това е Ваша отговорност!"
|
||||
},
|
||||
|
@ -359,9 +359,6 @@
|
||||
"editContact": {
|
||||
"message": "পরিচিতি সম্পাদনা করুন"
|
||||
},
|
||||
"emailUs": {
|
||||
"message": "আমাদের ইমেল করুন!"
|
||||
},
|
||||
"endOfFlowMessage1": {
|
||||
"message": "আপনি টেস্টটি পাস করেছেন - আপনার সীডফ্রেজ নিরাপদে রাখুন, এটি আপনার দায়িত্ব!"
|
||||
},
|
||||
|
@ -356,9 +356,6 @@
|
||||
"editContact": {
|
||||
"message": "Editar Contacte"
|
||||
},
|
||||
"emailUs": {
|
||||
"message": "Envia'ns un correu!"
|
||||
},
|
||||
"endOfFlowMessage1": {
|
||||
"message": "Has passat el test - mantingues la teva seedphrase segura, és la teva responsabilitat!"
|
||||
},
|
||||
|
@ -148,9 +148,6 @@
|
||||
"edit": {
|
||||
"message": "Upravit"
|
||||
},
|
||||
"emailUs": {
|
||||
"message": "Napište nám e-mail!"
|
||||
},
|
||||
"enterPassword": {
|
||||
"message": "Zadejte heslo"
|
||||
},
|
||||
|
@ -359,9 +359,6 @@
|
||||
"editContact": {
|
||||
"message": "Redigér Kontakt"
|
||||
},
|
||||
"emailUs": {
|
||||
"message": "Send os en email!"
|
||||
},
|
||||
"endOfFlowMessage1": {
|
||||
"message": "Du bestod testen - pas godt på din seed-sætning, det er dit ansvar!"
|
||||
},
|
||||
|
@ -344,9 +344,6 @@
|
||||
"editContact": {
|
||||
"message": "Kontakt bearbeiten"
|
||||
},
|
||||
"emailUs": {
|
||||
"message": "Schreib uns eine Mail!"
|
||||
},
|
||||
"endOfFlowMessage1": {
|
||||
"message": "Sie haben den Test bestanden - bewahren Sie Ihre mnemonische Phrase sicher auf, Sie sind dafür verantwortlich! "
|
||||
},
|
||||
|
@ -356,9 +356,6 @@
|
||||
"editContact": {
|
||||
"message": "Επεξεργασία Επαφής"
|
||||
},
|
||||
"emailUs": {
|
||||
"message": "Στείλτε μας email!"
|
||||
},
|
||||
"endOfFlowMessage1": {
|
||||
"message": "Περάσατε τη δοκιμή - κρατήστε τη φράση φύτρου σας ασφαλή, είναι δική σας ευθύνη!"
|
||||
},
|
||||
|
@ -409,6 +409,9 @@
|
||||
"contactsSettingsDescription": {
|
||||
"message": "Add, edit, remove, and manage your contacts"
|
||||
},
|
||||
"contactUs": {
|
||||
"message": "Contact us!"
|
||||
},
|
||||
"continueToWyre": {
|
||||
"message": "Continue to Wyre"
|
||||
},
|
||||
@ -535,9 +538,6 @@
|
||||
"editPermission": {
|
||||
"message": "Edit Permission"
|
||||
},
|
||||
"emailUs": {
|
||||
"message": "Email us!"
|
||||
},
|
||||
"endOfFlowMessage1": {
|
||||
"message": "You passed the test - keep your seedphrase safe, it's your responsibility!"
|
||||
},
|
||||
@ -1196,7 +1196,7 @@
|
||||
"message": "Restore"
|
||||
},
|
||||
"revealSeedWords": {
|
||||
"message": "Reveal Seed Words"
|
||||
"message": "Reveal Seed Phrase"
|
||||
},
|
||||
"revealSeedWordsTitle": {
|
||||
"message": "Seed Phrase"
|
||||
|
@ -313,9 +313,6 @@
|
||||
"editContact": {
|
||||
"message": "Editar Contacto"
|
||||
},
|
||||
"emailUs": {
|
||||
"message": "¡Envíanos un correo!"
|
||||
},
|
||||
"endOfFlowMessage8": {
|
||||
"message": "MetaMask no puede recuperar tu seedphrase. Saber más."
|
||||
},
|
||||
|
@ -356,9 +356,6 @@
|
||||
"editContact": {
|
||||
"message": "Editar contacto"
|
||||
},
|
||||
"emailUs": {
|
||||
"message": "¡Envíanos un correo electrónico!"
|
||||
},
|
||||
"endOfFlowMessage1": {
|
||||
"message": "Pasaste la prueba. Es responsabilidad tuya mantener la frase de inicialización segura."
|
||||
},
|
||||
|
@ -359,9 +359,6 @@
|
||||
"editContact": {
|
||||
"message": "Muuda kontakti"
|
||||
},
|
||||
"emailUs": {
|
||||
"message": "Saatke meile e-kiri!"
|
||||
},
|
||||
"endOfFlowMessage1": {
|
||||
"message": "Läbisite kontrolli – hoidke seemnefraasi turvaliselt, see on teie vastutusel!"
|
||||
},
|
||||
|
@ -359,9 +359,6 @@
|
||||
"editContact": {
|
||||
"message": "ویرایش تماس"
|
||||
},
|
||||
"emailUs": {
|
||||
"message": "به ما ایمیل کنید!"
|
||||
},
|
||||
"endOfFlowMessage1": {
|
||||
"message": "شما در امتحان موفق شدید - عبارت بازیابی را مصؤن نگهدارید، این مسؤلیت شماست!"
|
||||
},
|
||||
|
@ -356,9 +356,6 @@
|
||||
"editContact": {
|
||||
"message": "Muokkaa yhteystietoa"
|
||||
},
|
||||
"emailUs": {
|
||||
"message": "Lähetä meille sähköpostia!"
|
||||
},
|
||||
"endOfFlowMessage1": {
|
||||
"message": "Läpäisit kokeen – pidä salaustekstisi turvassa. Se on sinun vastuullasi!"
|
||||
},
|
||||
|
@ -332,9 +332,6 @@
|
||||
"editContact": {
|
||||
"message": "I-edit ang Contact"
|
||||
},
|
||||
"emailUs": {
|
||||
"message": "Mag-email sa amin!"
|
||||
},
|
||||
"endOfFlowMessage1": {
|
||||
"message": "Pumasa ka sa test - panatilihing ligtas ang iyong seedphrase, responsibilidad mo ito!"
|
||||
},
|
||||
|
@ -347,9 +347,6 @@
|
||||
"editContact": {
|
||||
"message": "Modifier le contact"
|
||||
},
|
||||
"emailUs": {
|
||||
"message": "Envoyez-nous un email !"
|
||||
},
|
||||
"endOfFlowMessage1": {
|
||||
"message": "Vous avez réussi l'essai : gardez votre phrase de départ en sécurité, c'est de votre responsabilité !"
|
||||
},
|
||||
|
@ -359,9 +359,6 @@
|
||||
"editContact": {
|
||||
"message": "ערוך איש קשר"
|
||||
},
|
||||
"emailUs": {
|
||||
"message": "שלח/י לנו מייל!"
|
||||
},
|
||||
"endOfFlowMessage1": {
|
||||
"message": "עברת את הבחינה - יש לשמור את ה-seedphrase שלך במקום בטוח, זה באחריותך!"
|
||||
},
|
||||
|
@ -359,9 +359,6 @@
|
||||
"editContact": {
|
||||
"message": "संपर्क संपादित करें"
|
||||
},
|
||||
"emailUs": {
|
||||
"message": "हमें ईमेल करें!"
|
||||
},
|
||||
"endOfFlowMessage1": {
|
||||
"message": "आपने परीक्षा उत्तीर्ण कर ली - अपने बीज वाक्यांश को सुरक्षित रखें, यह आपकी जिम्मेदारी है!"
|
||||
},
|
||||
|
@ -124,9 +124,6 @@
|
||||
"edit": {
|
||||
"message": "संपादित करें"
|
||||
},
|
||||
"emailUs": {
|
||||
"message": "हमें ईमेल करें!"
|
||||
},
|
||||
"enterPassword": {
|
||||
"message": "पासवर्ड दर्ज करें"
|
||||
},
|
||||
|
@ -359,9 +359,6 @@
|
||||
"editContact": {
|
||||
"message": "Uredi kontakt"
|
||||
},
|
||||
"emailUs": {
|
||||
"message": "Pošaljite nam elektroničku poruku!"
|
||||
},
|
||||
"endOfFlowMessage1": {
|
||||
"message": "Prošli ste test – čuvajte svoju početnu rečenicu jer ste vi odgovorni za nju!"
|
||||
},
|
||||
|
@ -205,9 +205,6 @@
|
||||
"edit": {
|
||||
"message": "Korije"
|
||||
},
|
||||
"emailUs": {
|
||||
"message": "Imèl nou!"
|
||||
},
|
||||
"enterPassword": {
|
||||
"message": "Mete modpas"
|
||||
},
|
||||
|
@ -359,9 +359,6 @@
|
||||
"editContact": {
|
||||
"message": "Kapcsolatok szerkesztése"
|
||||
},
|
||||
"emailUs": {
|
||||
"message": "Írjon nekünk!"
|
||||
},
|
||||
"endOfFlowMessage1": {
|
||||
"message": "Átment a teszten - tartsa biztonságban a seed mondatot, ez az ön felelőssége!"
|
||||
},
|
||||
|
@ -350,9 +350,6 @@
|
||||
"editContact": {
|
||||
"message": "Sunting Kontak"
|
||||
},
|
||||
"emailUs": {
|
||||
"message": "Kirimkan kami email!"
|
||||
},
|
||||
"endOfFlowMessage1": {
|
||||
"message": "Anda lulus tes - jagalah agar frasa benih Anda aman, itu tanggung jawab Anda!"
|
||||
},
|
||||
|
@ -531,9 +531,6 @@
|
||||
"editPermission": {
|
||||
"message": "Modifica Permessi"
|
||||
},
|
||||
"emailUs": {
|
||||
"message": "Mandaci una mail!"
|
||||
},
|
||||
"endOfFlowMessage1": {
|
||||
"message": "Hai passato il test - tieni la tua frase seed al sicuro, è tua responsabilità!"
|
||||
},
|
||||
|
@ -359,9 +359,6 @@
|
||||
"editContact": {
|
||||
"message": "ಸಂಪರ್ಕವನ್ನು ಸಂಪಾದಿಸಿ"
|
||||
},
|
||||
"emailUs": {
|
||||
"message": "ನಮಗೆ ಇಮೇಲ್ ಮಾಡಿ!"
|
||||
},
|
||||
"endOfFlowMessage1": {
|
||||
"message": "ನೀವು ಪರೀಕ್ಷೆಯನ್ನು ಪಾಸ್ ಮಾಡಿರುವಿರಿ - ನಿಮ್ಮ ಸೀಡ್ಫ್ರೇಸ್ ಸುರಕ್ಷಿತವಾಗಿರಿಸಿ, ಅದು ನಿಮ್ಮ ಜವಾಬ್ದಾರಿಯಾಗಿದೆ!"
|
||||
},
|
||||
|
@ -356,9 +356,6 @@
|
||||
"editContact": {
|
||||
"message": "연락처 수정"
|
||||
},
|
||||
"emailUs": {
|
||||
"message": "저자에게 메일 보내기!"
|
||||
},
|
||||
"endOfFlowMessage1": {
|
||||
"message": "인증을 통과했습니다 - 시드 구문을 안전하게 보관하세요. 그것은 당신의 의무입니다."
|
||||
},
|
||||
|
@ -359,9 +359,6 @@
|
||||
"editContact": {
|
||||
"message": "Taisyti kontaktą"
|
||||
},
|
||||
"emailUs": {
|
||||
"message": "Rašykite mums el. paštu!"
|
||||
},
|
||||
"endOfFlowMessage1": {
|
||||
"message": "Jūs perėjote testą – laikykite saugiai savo atkūrimo frazę, tai jūsų atsakomybė!"
|
||||
},
|
||||
|
@ -359,9 +359,6 @@
|
||||
"editContact": {
|
||||
"message": "Rediģēt līgumu"
|
||||
},
|
||||
"emailUs": {
|
||||
"message": "Raksiet mums e-pastu!"
|
||||
},
|
||||
"endOfFlowMessage1": {
|
||||
"message": "Jūs izgājāt pārbaudi — glabājiet atkopšanas frāzi drošībā, tā ir jūsu atbildība!"
|
||||
},
|
||||
|
@ -347,9 +347,6 @@
|
||||
"editContact": {
|
||||
"message": "Edit Kenalan"
|
||||
},
|
||||
"emailUs": {
|
||||
"message": "Hantarkan e-mel kepada kami!"
|
||||
},
|
||||
"endOfFlowMessage1": {
|
||||
"message": "Anda telah lulus ujian - simpan frasa benih anda di tempat yang selamat, itu tanggungjawab anda!"
|
||||
},
|
||||
|
@ -118,9 +118,6 @@
|
||||
"edit": {
|
||||
"message": "Bewerk"
|
||||
},
|
||||
"emailUs": {
|
||||
"message": "Email ons!"
|
||||
},
|
||||
"enterPassword": {
|
||||
"message": "Voer wachtwoord in"
|
||||
},
|
||||
|
@ -356,9 +356,6 @@
|
||||
"editContact": {
|
||||
"message": "Rediger kontakt"
|
||||
},
|
||||
"emailUs": {
|
||||
"message": "Send oss en e-post!"
|
||||
},
|
||||
"endOfFlowMessage1": {
|
||||
"message": "Du besto prøven - oppbevar den mnemoniske gjenopprettingsfrasen din på et sikkert sted, det er ditt ansvar! "
|
||||
},
|
||||
|
@ -356,9 +356,6 @@
|
||||
"editContact": {
|
||||
"message": "Edytuj kontakt"
|
||||
},
|
||||
"emailUs": {
|
||||
"message": "Napisz do nas!"
|
||||
},
|
||||
"endOfFlowMessage1": {
|
||||
"message": "Zaliczyłeś test – zabezpiecz swoją frazę seed, to Twoja odpowiedzialność!"
|
||||
},
|
||||
|
@ -124,9 +124,6 @@
|
||||
"edit": {
|
||||
"message": "Editar"
|
||||
},
|
||||
"emailUs": {
|
||||
"message": "Fale connosco!"
|
||||
},
|
||||
"enterPassword": {
|
||||
"message": "Introduza palavra-passe"
|
||||
},
|
||||
|
@ -353,9 +353,6 @@
|
||||
"editContact": {
|
||||
"message": "Editar Contato"
|
||||
},
|
||||
"emailUs": {
|
||||
"message": "Escreva para nós!"
|
||||
},
|
||||
"endOfFlowMessage1": {
|
||||
"message": "Você passou no teste — mantenha sua frase semente segura, a responsabilidade é sua!"
|
||||
},
|
||||
|
@ -359,9 +359,6 @@
|
||||
"editContact": {
|
||||
"message": "Editați contact"
|
||||
},
|
||||
"emailUs": {
|
||||
"message": "Trimiteți-ne un email!"
|
||||
},
|
||||
"endOfFlowMessage1": {
|
||||
"message": "Ați trecut testul – păstrați expresia sursă în siguranță, este răspunderea dvs.!"
|
||||
},
|
||||
|
@ -154,9 +154,6 @@
|
||||
"edit": {
|
||||
"message": "Редактировать"
|
||||
},
|
||||
"emailUs": {
|
||||
"message": "Свяжитесь с нами по электронной почте!"
|
||||
},
|
||||
"enterPassword": {
|
||||
"message": "Введите пароль"
|
||||
},
|
||||
|
@ -350,9 +350,6 @@
|
||||
"editContact": {
|
||||
"message": "Upraviť kontakt"
|
||||
},
|
||||
"emailUs": {
|
||||
"message": "Napište nám e-mail!"
|
||||
},
|
||||
"endOfFlowMessage1": {
|
||||
"message": "Úspešne ste prešli testom – uchovávajte svoju seed frázu v bezpečí. Je to vaša zodpovednosť!"
|
||||
},
|
||||
|
@ -359,9 +359,6 @@
|
||||
"editContact": {
|
||||
"message": "Uredi stik"
|
||||
},
|
||||
"emailUs": {
|
||||
"message": "Pišite nam!"
|
||||
},
|
||||
"endOfFlowMessage1": {
|
||||
"message": "Opravili ste test - vaše geslo seed phrase je vaša odgovornost - skrbno pazite nanj!"
|
||||
},
|
||||
|
@ -356,9 +356,6 @@
|
||||
"editContact": {
|
||||
"message": "Izmeni kontakt"
|
||||
},
|
||||
"emailUs": {
|
||||
"message": "Pošaljite nam e-poštu!"
|
||||
},
|
||||
"endOfFlowMessage1": {
|
||||
"message": "Prošli ste test - čuvajte svoju frazu početnih vrednosti, to je vaša odgovornost!"
|
||||
},
|
||||
|
@ -353,9 +353,6 @@
|
||||
"editContact": {
|
||||
"message": "Redigera kontakt"
|
||||
},
|
||||
"emailUs": {
|
||||
"message": "Mejla oss!"
|
||||
},
|
||||
"endOfFlowMessage1": {
|
||||
"message": "Du klarade testet. Håll din seed phrase hemlig, den är på ditt ansvar!"
|
||||
},
|
||||
|
@ -353,9 +353,6 @@
|
||||
"editContact": {
|
||||
"message": "Hariri Mawasiliano"
|
||||
},
|
||||
"emailUs": {
|
||||
"message": "Tutumie barua pepe!"
|
||||
},
|
||||
"endOfFlowMessage1": {
|
||||
"message": "Umefaulu jaribio - weka kirai chako cha kuanzia mahali salama, ni wajibu wako!"
|
||||
},
|
||||
|
@ -139,9 +139,6 @@
|
||||
"edit": {
|
||||
"message": "திருத்து"
|
||||
},
|
||||
"emailUs": {
|
||||
"message": "எங்களுக்கு மின்னஞ்சல்!"
|
||||
},
|
||||
"enterPassword": {
|
||||
"message": "கடவுச்சொல்லை உள்ளிடவும்"
|
||||
},
|
||||
|
@ -178,9 +178,6 @@
|
||||
"editContact": {
|
||||
"message": "แก้ไขผู้ติดต่อ"
|
||||
},
|
||||
"emailUs": {
|
||||
"message": "อีเมลหาเรา!"
|
||||
},
|
||||
"endOfFlowMessage1": {
|
||||
"message": "คุณผ่านการทดสอบแล้ว กรุณารับผิดชอบในการรักษา Seed Phrase ให้ปลอดภัย"
|
||||
},
|
||||
|
@ -151,9 +151,6 @@
|
||||
"edit": {
|
||||
"message": "Düzenle"
|
||||
},
|
||||
"emailUs": {
|
||||
"message": "Bize e-posta atın!"
|
||||
},
|
||||
"enterPassword": {
|
||||
"message": "Parolanızı girin"
|
||||
},
|
||||
|
@ -359,9 +359,6 @@
|
||||
"editContact": {
|
||||
"message": "Редагувати контракт"
|
||||
},
|
||||
"emailUs": {
|
||||
"message": "Напишіть нам!"
|
||||
},
|
||||
"endOfFlowMessage1": {
|
||||
"message": "Ви пройшли тест, зберігайте вашу початкову фразу в безпеці - це ваша відповідальність!"
|
||||
},
|
||||
|
@ -359,9 +359,6 @@
|
||||
"editContact": {
|
||||
"message": "编辑联系人"
|
||||
},
|
||||
"emailUs": {
|
||||
"message": "联系我们"
|
||||
},
|
||||
"endOfFlowMessage1": {
|
||||
"message": "您已通过测试 - 请妥善保管你的种子密语。这是您的责任!"
|
||||
},
|
||||
|
@ -356,9 +356,6 @@
|
||||
"editContact": {
|
||||
"message": "編輯聯絡資訊"
|
||||
},
|
||||
"emailUs": {
|
||||
"message": "寄 Email 給我們!"
|
||||
},
|
||||
"endOfFlowMessage1": {
|
||||
"message": "你通過測試了—安全存放助記詞,這是你的責任!"
|
||||
},
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "__MSG_appName__",
|
||||
"short_name": "__MSG_appName__",
|
||||
"version": "8.0.6",
|
||||
"version": "8.0.7",
|
||||
"manifest_version": 2,
|
||||
"author": "https://metamask.io",
|
||||
"description": "__MSG_appDescription__",
|
||||
|
@ -7,7 +7,7 @@ import { CapabilitiesController as RpcCap } from 'rpc-cap'
|
||||
import { ethErrors } from 'eth-json-rpc-errors'
|
||||
import { cloneDeep } from 'lodash'
|
||||
|
||||
import createMethodMiddleware from './methodMiddleware'
|
||||
import createPermissionsMethodMiddleware from './permissionsMethodMiddleware'
|
||||
import PermissionsLogController from './permissionsLog'
|
||||
|
||||
// Methods that do not require any permissions to use:
|
||||
@ -90,7 +90,7 @@ export class PermissionsController {
|
||||
|
||||
engine.push(this.permissionsLog.createMiddleware())
|
||||
|
||||
engine.push(createMethodMiddleware({
|
||||
engine.push(createPermissionsMethodMiddleware({
|
||||
addDomainMetadata: this.addDomainMetadata.bind(this),
|
||||
getAccounts: this.getAccounts.bind(this, origin),
|
||||
getUnlockPromise: () => this._getUnlockPromise(true),
|
||||
|
@ -4,7 +4,7 @@ import { ethErrors } from 'eth-json-rpc-errors'
|
||||
/**
|
||||
* Create middleware for handling certain methods and preprocessing permissions requests.
|
||||
*/
|
||||
export default function createMethodMiddleware ({
|
||||
export default function createPermissionsMethodMiddleware ({
|
||||
addDomainMetadata,
|
||||
getAccounts,
|
||||
getUnlockPromise,
|
@ -1,34 +1,30 @@
|
||||
export default function getRestrictedMethods ({ getIdentities, getKeyringAccounts }) {
|
||||
return {
|
||||
'eth_accounts': {
|
||||
method: (_, res, __, end) => {
|
||||
getKeyringAccounts()
|
||||
.then((accounts) => {
|
||||
const identities = getIdentities()
|
||||
res.result = accounts
|
||||
.sort((firstAddress, secondAddress) => {
|
||||
if (!identities[firstAddress]) {
|
||||
throw new Error(`Missing identity for address ${firstAddress}`)
|
||||
} else if (!identities[secondAddress]) {
|
||||
throw new Error(`Missing identity for address ${secondAddress}`)
|
||||
} else if (identities[firstAddress].lastSelected === identities[secondAddress].lastSelected) {
|
||||
return 0
|
||||
} else if (identities[firstAddress].lastSelected === undefined) {
|
||||
return 1
|
||||
} else if (identities[secondAddress].lastSelected === undefined) {
|
||||
return -1
|
||||
}
|
||||
method: async (_, res, __, end) => {
|
||||
try {
|
||||
const accounts = await getKeyringAccounts()
|
||||
const identities = getIdentities()
|
||||
res.result = accounts.sort((firstAddress, secondAddress) => {
|
||||
if (!identities[firstAddress]) {
|
||||
throw new Error(`Missing identity for address ${firstAddress}`)
|
||||
} else if (!identities[secondAddress]) {
|
||||
throw new Error(`Missing identity for address ${secondAddress}`)
|
||||
} else if (identities[firstAddress].lastSelected === identities[secondAddress].lastSelected) {
|
||||
return 0
|
||||
} else if (identities[firstAddress].lastSelected === undefined) {
|
||||
return 1
|
||||
} else if (identities[secondAddress].lastSelected === undefined) {
|
||||
return -1
|
||||
}
|
||||
|
||||
return identities[secondAddress].lastSelected - identities[firstAddress].lastSelected
|
||||
})
|
||||
end()
|
||||
return identities[secondAddress].lastSelected - identities[firstAddress].lastSelected
|
||||
})
|
||||
.catch(
|
||||
(err) => {
|
||||
res.error = err
|
||||
end(err)
|
||||
},
|
||||
)
|
||||
end()
|
||||
} catch (err) {
|
||||
res.error = err
|
||||
end(err)
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -434,7 +434,7 @@ export default class TransactionController extends EventEmitter {
|
||||
// Since this transaction is async,
|
||||
// we need to keep track of what is currently being signed,
|
||||
// So that we do not increment nonce + resubmit something
|
||||
// that is already being incrmented & signed.
|
||||
// that is already being incremented & signed.
|
||||
if (this.inProcessOfSigning.has(txId)) {
|
||||
return
|
||||
}
|
||||
|
@ -1,5 +1,3 @@
|
||||
/*global Web3*/
|
||||
|
||||
// need to make sure we aren't affected by overlapping namespaces
|
||||
// and that we dont affect the app with our namespace
|
||||
// mostly a fix for web3's BigNumber if AMD's "define" is defined...
|
||||
@ -37,9 +35,7 @@ import LocalMessageDuplexStream from 'post-message-stream'
|
||||
import { initProvider } from '@metamask/inpage-provider'
|
||||
|
||||
// TODO:deprecate:2020
|
||||
import 'web3/dist/web3.min.js'
|
||||
|
||||
import setupDappAutoReload from './lib/auto-reload.js'
|
||||
import setupWeb3 from './lib/setupWeb3.js'
|
||||
|
||||
restoreContextAfterImports()
|
||||
|
||||
@ -59,11 +55,9 @@ initProvider({
|
||||
connectionStream: metamaskStream,
|
||||
})
|
||||
|
||||
//
|
||||
// TODO:deprecate:2020
|
||||
//
|
||||
// Setup web3
|
||||
|
||||
// setup web3
|
||||
|
||||
if (typeof window.web3 !== 'undefined') {
|
||||
throw new Error(`MetaMask detected another web3.
|
||||
@ -73,18 +67,5 @@ if (typeof window.web3 !== 'undefined') {
|
||||
and try again.`)
|
||||
}
|
||||
|
||||
const web3 = new Web3(window.ethereum)
|
||||
web3.setProvider = function () {
|
||||
log.debug('MetaMask - overrode web3.setProvider')
|
||||
}
|
||||
log.debug('MetaMask - injected web3')
|
||||
|
||||
Object.defineProperty(window.ethereum, '_web3Ref', {
|
||||
enumerable: false,
|
||||
writable: true,
|
||||
configurable: true,
|
||||
value: web3.eth,
|
||||
})
|
||||
|
||||
// setup dapp auto reload AND proxy web3
|
||||
setupDappAutoReload(web3, window.ethereum._publicConfigStore)
|
||||
// proxy web3, assign to window, and set up site auto reload
|
||||
setupWeb3(log)
|
||||
|
@ -1,20 +1,16 @@
|
||||
import { getBackgroundMetaMetricState } from '../../../ui/app/selectors'
|
||||
import { sendMetaMetricsEvent } from '../../../ui/app/helpers/utils/metametrics.util'
|
||||
|
||||
const inDevelopment = process.env.NODE_ENV === 'development'
|
||||
export default function backgroundMetaMetricsEvent (metaMaskState, eventData) {
|
||||
|
||||
const METAMETRICS_TRACKING_URL = inDevelopment
|
||||
? 'http://www.metamask.io/metametrics'
|
||||
: 'http://www.metamask.io/metametrics-prod'
|
||||
eventData.eventOpts['category'] = 'Background'
|
||||
|
||||
export default function backEndMetaMetricsEvent (metaMaskState, eventData) {
|
||||
const stateEventData = getBackgroundMetaMetricState({ metamask: metaMaskState })
|
||||
|
||||
if (stateEventData.participateInMetaMetrics) {
|
||||
sendMetaMetricsEvent({
|
||||
...stateEventData,
|
||||
...eventData,
|
||||
url: METAMETRICS_TRACKING_URL + '/backend',
|
||||
currentPath: '/background',
|
||||
})
|
||||
}
|
||||
}
|
32
app/scripts/lib/createMethodMiddleware.js
Normal file
32
app/scripts/lib/createMethodMiddleware.js
Normal file
@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Returns a middleware that implements the following RPC methods:
|
||||
* - metamask_logInjectedWeb3Usage
|
||||
*
|
||||
* @param {Object} opts - The middleware options
|
||||
* @param {string} opts.origin - The origin for the middleware stack
|
||||
* @param {Function} opts.sendMetrics - A function for sending a metrics event
|
||||
* @returns {(req: any, res: any, next: Function, end: Function) => void}
|
||||
*/
|
||||
export default function createMethodMiddleware ({ origin, sendMetrics }) {
|
||||
return function methodMiddleware (req, res, next, end) {
|
||||
switch (req.method) {
|
||||
|
||||
case 'metamask_logInjectedWeb3Usage':
|
||||
|
||||
const { action, name } = req.params[0]
|
||||
|
||||
sendMetrics({
|
||||
action,
|
||||
name,
|
||||
customVariables: { origin },
|
||||
})
|
||||
|
||||
res.result = true
|
||||
break
|
||||
|
||||
default:
|
||||
return next()
|
||||
}
|
||||
return end()
|
||||
}
|
||||
}
|
@ -56,7 +56,7 @@ export default class NotificationManager {
|
||||
})
|
||||
|
||||
// Firefox currently ignores left/top for create, but it works for update
|
||||
if (popupWindow.left !== left) {
|
||||
if (popupWindow.left !== left && popupWindow.state !== 'fullscreen') {
|
||||
await this.platform.updateWindowPosition(popupWindow.id, left, top)
|
||||
}
|
||||
this._popupId = popupWindow.id
|
||||
|
@ -16,41 +16,33 @@ const seedPhraseVerifier = {
|
||||
* @returns {Promise<void>} - Promises undefined
|
||||
*
|
||||
*/
|
||||
verifyAccounts (createdAccounts, seedWords) {
|
||||
async verifyAccounts (createdAccounts, seedWords) {
|
||||
if (!createdAccounts || createdAccounts.length < 1) {
|
||||
throw new Error('No created accounts defined.')
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const keyringController = new KeyringController({})
|
||||
const Keyring = keyringController.getKeyringClassForType('HD Key Tree')
|
||||
const opts = {
|
||||
mnemonic: seedWords,
|
||||
numberOfAccounts: createdAccounts.length,
|
||||
}
|
||||
|
||||
if (!createdAccounts || createdAccounts.length < 1) {
|
||||
return reject(new Error('No created accounts defined.'))
|
||||
const keyring = new Keyring(opts)
|
||||
const restoredAccounts = await keyring.getAccounts()
|
||||
log.debug('Created accounts: ' + JSON.stringify(createdAccounts))
|
||||
log.debug('Restored accounts: ' + JSON.stringify(restoredAccounts))
|
||||
|
||||
if (restoredAccounts.length !== createdAccounts.length) {
|
||||
// this should not happen...
|
||||
throw new Error('Wrong number of accounts')
|
||||
}
|
||||
|
||||
for (let i = 0; i < restoredAccounts.length; i++) {
|
||||
if (restoredAccounts[i].toLowerCase() !== createdAccounts[i].toLowerCase()) {
|
||||
throw new Error('Not identical accounts! Original: ' + createdAccounts[i] + ', Restored: ' + restoredAccounts[i])
|
||||
}
|
||||
|
||||
const keyringController = new KeyringController({})
|
||||
const Keyring = keyringController.getKeyringClassForType('HD Key Tree')
|
||||
const opts = {
|
||||
mnemonic: seedWords,
|
||||
numberOfAccounts: createdAccounts.length,
|
||||
}
|
||||
|
||||
const keyring = new Keyring(opts)
|
||||
keyring.getAccounts()
|
||||
.then((restoredAccounts) => {
|
||||
|
||||
log.debug('Created accounts: ' + JSON.stringify(createdAccounts))
|
||||
log.debug('Restored accounts: ' + JSON.stringify(restoredAccounts))
|
||||
|
||||
if (restoredAccounts.length !== createdAccounts.length) {
|
||||
// this should not happen...
|
||||
return reject(new Error('Wrong number of accounts'))
|
||||
}
|
||||
|
||||
for (let i = 0; i < restoredAccounts.length; i++) {
|
||||
if (restoredAccounts[i].toLowerCase() !== createdAccounts[i].toLowerCase()) {
|
||||
return reject(new Error('Not identical accounts! Original: ' + createdAccounts[i] + ', Restored: ' + restoredAccounts[i]))
|
||||
}
|
||||
}
|
||||
return resolve()
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,6 @@ import extractEthjsErrorMessage from './extractEthjsErrorMessage'
|
||||
|
||||
const METAMASK_DEBUG = process.env.METAMASK_DEBUG
|
||||
const METAMASK_ENVIRONMENT = process.env.METAMASK_ENVIRONMENT
|
||||
const SENTRY_DSN_PROD = 'https://3567c198f8a8412082d32655da2961d0@sentry.io/273505'
|
||||
const SENTRY_DSN_DEV = 'https://f59f3dd640d2429d9d0e2445a87ea8e1@sentry.io/273496'
|
||||
|
||||
// This describes the subset of Redux state attached to errors sent to Sentry
|
||||
@ -72,12 +71,17 @@ export const SENTRY_STATE = {
|
||||
export default function setupSentry ({ release, getState }) {
|
||||
let sentryTarget
|
||||
|
||||
if (METAMASK_DEBUG || process.env.IN_TEST) {
|
||||
if (METAMASK_DEBUG) {
|
||||
return
|
||||
} else if (METAMASK_ENVIRONMENT === 'production') {
|
||||
if (!process.env.SENTRY_DSN) {
|
||||
throw new Error(`Missing SENTRY_DSN environment variable in production environment`)
|
||||
}
|
||||
console.log(`Setting up Sentry Remote Error Reporting for '${METAMASK_ENVIRONMENT}': SENTRY_DSN`)
|
||||
sentryTarget = process.env.SENTRY_DSN
|
||||
} else {
|
||||
console.log(`Setting up Sentry Remote Error Reporting for '${METAMASK_ENVIRONMENT}': SENTRY_DSN_DEV`)
|
||||
sentryTarget = SENTRY_DSN_DEV
|
||||
} else {
|
||||
console.log(`Setting up Sentry Remote Error Reporting for '${METAMASK_ENVIRONMENT}': SENTRY_DSN_PROD`)
|
||||
sentryTarget = SENTRY_DSN_PROD
|
||||
}
|
||||
|
||||
Sentry.init({
|
||||
|
@ -1,26 +1,67 @@
|
||||
/*global Web3*/
|
||||
|
||||
// TODO:deprecate:2020
|
||||
// Delete this file
|
||||
|
||||
export default function setupDappAutoReload (web3, observable) {
|
||||
import 'web3/dist/web3.min.js'
|
||||
|
||||
const shouldLogUsage = !([
|
||||
'docs.metamask.io',
|
||||
'metamask.github.io',
|
||||
'metamask.io',
|
||||
].includes(window.location.hostname))
|
||||
|
||||
export default function setupWeb3 (log) {
|
||||
// export web3 as a global, checking for usage
|
||||
let reloadInProgress = false
|
||||
let lastTimeUsed
|
||||
let lastSeenNetwork
|
||||
let hasBeenWarned = false
|
||||
|
||||
const web3 = new Web3(window.ethereum)
|
||||
web3.setProvider = function () {
|
||||
log.debug('MetaMask - overrode web3.setProvider')
|
||||
}
|
||||
log.debug('MetaMask - injected web3')
|
||||
|
||||
Object.defineProperty(window.ethereum, '_web3Ref', {
|
||||
enumerable: false,
|
||||
writable: true,
|
||||
configurable: true,
|
||||
value: web3.eth,
|
||||
})
|
||||
|
||||
const web3Proxy = new Proxy(web3, {
|
||||
get: (_web3, key) => {
|
||||
|
||||
// get the time of use
|
||||
lastTimeUsed = Date.now()
|
||||
|
||||
// show warning once on web3 access
|
||||
if (!hasBeenWarned && key !== 'currentProvider') {
|
||||
if (!hasBeenWarned) {
|
||||
console.warn(`MetaMask: We will stop injecting web3 in Q4 2020.\nPlease see this article for more information: https://medium.com/metamask/no-longer-injecting-web3-js-4a899ad6e59e`)
|
||||
hasBeenWarned = true
|
||||
}
|
||||
|
||||
if (shouldLogUsage) {
|
||||
window.ethereum.request({
|
||||
method: 'metamask_logInjectedWeb3Usage',
|
||||
params: [{ action: 'window.web3 get', name: key }],
|
||||
})
|
||||
}
|
||||
|
||||
// return value normally
|
||||
return _web3[key]
|
||||
},
|
||||
set: (_web3, key, value) => {
|
||||
|
||||
if (shouldLogUsage) {
|
||||
window.ethereum.request({
|
||||
method: 'metamask_logInjectedWeb3Usage',
|
||||
params: [{ action: 'window.web3 set', name: key }],
|
||||
})
|
||||
}
|
||||
|
||||
// set value normally
|
||||
_web3[key] = value
|
||||
},
|
||||
@ -33,7 +74,7 @@ export default function setupDappAutoReload (web3, observable) {
|
||||
value: web3Proxy,
|
||||
})
|
||||
|
||||
observable.subscribe(function (state) {
|
||||
window.ethereum._publicConfigStore.subscribe((state) => {
|
||||
// if the auto refresh on network change is false do not
|
||||
// do anything
|
||||
if (!window.ethereum.autoRefreshOnNetworkChange) {
|
@ -19,6 +19,7 @@ import createEngineStream from 'json-rpc-middleware-stream/engineStream'
|
||||
import createFilterMiddleware from 'eth-json-rpc-filters'
|
||||
import createSubscriptionManager from 'eth-json-rpc-filters/subscriptionManager'
|
||||
import createLoggerMiddleware from './lib/createLoggerMiddleware'
|
||||
import createMethodMiddleware from './lib/createMethodMiddleware'
|
||||
import createOriginMiddleware from './lib/createOriginMiddleware'
|
||||
import createTabIdMiddleware from './lib/createTabIdMiddleware'
|
||||
import createOnboardingMiddleware from './lib/createOnboardingMiddleware'
|
||||
@ -66,7 +67,7 @@ import {
|
||||
PhishingController,
|
||||
} from '@metamask/controllers'
|
||||
|
||||
import backEndMetaMetricsEvent from './lib/backend-metametrics'
|
||||
import backgroundMetaMetricsEvent from './lib/background-metametrics'
|
||||
|
||||
export default class MetamaskController extends EventEmitter {
|
||||
|
||||
@ -249,18 +250,11 @@ export default class MetamaskController extends EventEmitter {
|
||||
this.platform.showTransactionNotification(txMeta)
|
||||
|
||||
const { txReceipt } = txMeta
|
||||
const participateInMetaMetrics = this.preferencesController.getParticipateInMetaMetrics()
|
||||
if (txReceipt && txReceipt.status === '0x0' && participateInMetaMetrics) {
|
||||
const metamaskState = await this.getState()
|
||||
backEndMetaMetricsEvent(metamaskState, {
|
||||
customVariables: {
|
||||
errorMessage: txMeta.simulationFails?.reason,
|
||||
},
|
||||
eventOpts: {
|
||||
category: 'backend',
|
||||
action: 'Transactions',
|
||||
name: 'On Chain Failure',
|
||||
},
|
||||
if (txReceipt && txReceipt.status === '0x0') {
|
||||
this.sendBackgroundMetaMetrics({
|
||||
action: 'Transactions',
|
||||
name: 'On Chain Failure',
|
||||
customVariables: { errorMessage: txMeta.simulationFails?.reason },
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -478,6 +472,7 @@ export default class MetamaskController extends EventEmitter {
|
||||
|
||||
// vault management
|
||||
submitPassword: nodeify(this.submitPassword, this),
|
||||
verifyPassword: nodeify(this.verifyPassword, this),
|
||||
|
||||
// network management
|
||||
setProviderType: nodeify(networkController.setProviderType, networkController),
|
||||
@ -808,6 +803,15 @@ export default class MetamaskController extends EventEmitter {
|
||||
return this.keyringController.fullUpdate()
|
||||
}
|
||||
|
||||
/**
|
||||
* Submits a user's password to check its validity.
|
||||
*
|
||||
* @param {string} password The user's password
|
||||
*/
|
||||
async verifyPassword (password) {
|
||||
await this.keyringController.verifyPassword(password)
|
||||
}
|
||||
|
||||
/**
|
||||
* @type Identity
|
||||
* @property {string} name - The account nickname.
|
||||
@ -1627,6 +1631,10 @@ export default class MetamaskController extends EventEmitter {
|
||||
location,
|
||||
registerOnboarding: this.onboardingController.registerOnboarding,
|
||||
}))
|
||||
engine.push(createMethodMiddleware({
|
||||
origin,
|
||||
sendMetrics: this.sendBackgroundMetaMetrics.bind(this),
|
||||
}))
|
||||
// filter and subscription polyfills
|
||||
engine.push(filterMiddleware)
|
||||
engine.push(subscriptionManager.middleware)
|
||||
@ -1712,7 +1720,7 @@ export default class MetamaskController extends EventEmitter {
|
||||
|
||||
delete connections[id]
|
||||
|
||||
if (Object.keys(connections.length === 0)) {
|
||||
if (Object.keys(connections).length === 0) {
|
||||
delete this.connections[origin]
|
||||
}
|
||||
}
|
||||
@ -1827,6 +1835,22 @@ export default class MetamaskController extends EventEmitter {
|
||||
return nonceLock.nextNonce
|
||||
}
|
||||
|
||||
async sendBackgroundMetaMetrics ({ action, name, customVariables } = {}) {
|
||||
|
||||
if (!action || !name) {
|
||||
throw new Error('Must provide action and name.')
|
||||
}
|
||||
|
||||
const metamaskState = await this.getState()
|
||||
backgroundMetaMetricsEvent(metamaskState, {
|
||||
customVariables,
|
||||
eventOpts: {
|
||||
action,
|
||||
name,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// CONFIG
|
||||
//=============================================================================
|
||||
|
@ -206,6 +206,9 @@ function createScriptTasks ({ browserPlatforms, livereload }) {
|
||||
mangle: {
|
||||
reserved: [ 'MetamaskInpageProvider' ],
|
||||
},
|
||||
sourceMap: {
|
||||
content: true,
|
||||
},
|
||||
}))
|
||||
}
|
||||
|
||||
@ -313,33 +316,23 @@ function createScriptTasks ({ browserPlatforms, livereload }) {
|
||||
bundler = bundler.external(opts.externalDependencies)
|
||||
}
|
||||
|
||||
let environment
|
||||
if (opts.devMode) {
|
||||
environment = 'development'
|
||||
} else if (opts.testing) {
|
||||
environment = 'testing'
|
||||
} else if (process.env.CIRCLE_BRANCH === 'master') {
|
||||
environment = 'production'
|
||||
} else if (/^Version-v(\d+)[.](\d+)[.](\d+)/.test(process.env.CIRCLE_BRANCH)) {
|
||||
environment = 'release-candidate'
|
||||
} else if (process.env.CIRCLE_BRANCH === 'develop') {
|
||||
environment = 'staging'
|
||||
} else if (process.env.CIRCLE_PULL_REQUEST) {
|
||||
environment = 'pull-request'
|
||||
} else {
|
||||
environment = 'other'
|
||||
const environment = getEnvironment({ devMode: opts.devMode, test: opts.testing })
|
||||
if (environment === 'production' && !process.env.SENTRY_DSN) {
|
||||
throw new Error('Missing SENTRY_DSN environment variable')
|
||||
}
|
||||
|
||||
// Inject variables into bundle
|
||||
bundler.transform(envify({
|
||||
METAMASK_DEBUG: opts.devMode,
|
||||
METAMASK_ENVIRONMENT: environment,
|
||||
METAMETRICS_PROJECT_ID: process.env.METAMETRICS_PROJECT_ID,
|
||||
NODE_ENV: opts.devMode ? 'development' : 'production',
|
||||
IN_TEST: opts.testing ? 'true' : false,
|
||||
PUBNUB_SUB_KEY: process.env.PUBNUB_SUB_KEY || '',
|
||||
PUBNUB_PUB_KEY: process.env.PUBNUB_PUB_KEY || '',
|
||||
ETH_GAS_STATION_API_KEY: process.env.ETH_GAS_STATION_API_KEY || '',
|
||||
CONF: opts.devMode ? conf : ({}),
|
||||
SENTRY_DSN: process.env.SENTRY_DSN,
|
||||
}), {
|
||||
global: true,
|
||||
})
|
||||
@ -363,3 +356,22 @@ function createScriptTasks ({ browserPlatforms, livereload }) {
|
||||
function beep () {
|
||||
process.stdout.write('\x07')
|
||||
}
|
||||
|
||||
function getEnvironment ({ devMode, test }) {
|
||||
// get environment slug
|
||||
if (devMode) {
|
||||
return 'development'
|
||||
} else if (test) {
|
||||
return 'testing'
|
||||
} else if (process.env.CIRCLE_BRANCH === 'master') {
|
||||
return 'production'
|
||||
} else if (/^Version-v(\d+)[.](\d+)[.](\d+)/.test(process.env.CIRCLE_BRANCH)) {
|
||||
return 'release-candidate'
|
||||
} else if (process.env.CIRCLE_BRANCH === 'develop') {
|
||||
return 'staging'
|
||||
} else if (process.env.CIRCLE_PULL_REQUEST) {
|
||||
return 'pull-request'
|
||||
} else {
|
||||
return 'other'
|
||||
}
|
||||
}
|
||||
|
@ -13,13 +13,31 @@ const fsAsync = pify(fs)
|
||||
// if not working it may error or print minified garbage
|
||||
//
|
||||
|
||||
start().catch(console.error)
|
||||
start().catch((error) => {
|
||||
console.error(error)
|
||||
process.exit(1)
|
||||
})
|
||||
|
||||
|
||||
async function start () {
|
||||
const targetFiles = [`inpage.js`, `contentscript.js`, `ui.js`, `background.js`]
|
||||
const targetFiles = [
|
||||
`background.js`,
|
||||
// `bg-libs`, skipped because source maps are invalid due to browserify bug: https://github.com/browserify/browserify/issues/1971
|
||||
// `contentscript.js`, skipped because the validator is erroneously sampling the inlined `inpage.js` script
|
||||
`inpage.js`,
|
||||
'phishing-detect.js',
|
||||
`ui.js`,
|
||||
// `ui-libs.js`, skipped because source maps are invalid due to browserify bug: https://github.com/browserify/browserify/issues/1971
|
||||
]
|
||||
let valid = true
|
||||
|
||||
for (const buildName of targetFiles) {
|
||||
await validateSourcemapForFile({ buildName })
|
||||
const fileIsValid = await validateSourcemapForFile({ buildName })
|
||||
valid = valid && fileIsValid
|
||||
}
|
||||
|
||||
if (!valid) {
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
@ -59,6 +77,7 @@ async function validateSourcemapForFile ({ buildName }) {
|
||||
|
||||
console.log(` sampling from ${consumer.sources.length} files`)
|
||||
let sampleCount = 0
|
||||
let valid = true
|
||||
|
||||
const buildLines = rawBuild.split('\n')
|
||||
const targetString = 'new Error'
|
||||
@ -71,6 +90,7 @@ async function validateSourcemapForFile ({ buildName }) {
|
||||
const result = consumer.originalPositionFor(position)
|
||||
// warn if source content is missing
|
||||
if (!result.source) {
|
||||
valid = false
|
||||
console.warn(`!! missing source for position: ${JSON.stringify(position)}`)
|
||||
// const buildLine = buildLines[position.line - 1]
|
||||
console.warn(` origin in build:`)
|
||||
@ -86,14 +106,27 @@ async function validateSourcemapForFile ({ buildName }) {
|
||||
const portion = line.slice(result.column)
|
||||
const isMaybeValid = portion.includes(targetString)
|
||||
if (!isMaybeValid) {
|
||||
console.error('Sourcemap seems invalid:')
|
||||
console.log(`\n========================== ${result.source} ====================================\n`)
|
||||
console.log(line)
|
||||
console.log(`\n==============================================================================\n`)
|
||||
valid = false
|
||||
console.error(`Sourcemap seems invalid:\n${getFencedCode(result.source, line)}`)
|
||||
}
|
||||
})
|
||||
})
|
||||
console.log(` checked ${sampleCount} samples`)
|
||||
return valid
|
||||
}
|
||||
|
||||
const CODE_FENCE_LENGTH = 80
|
||||
const TITLE_PADDING_LENGTH = 1
|
||||
|
||||
function getFencedCode (filename, code) {
|
||||
const title = `${' '.repeat(TITLE_PADDING_LENGTH)}${filename}${' '.repeat(TITLE_PADDING_LENGTH)}`
|
||||
const openingFenceLength = Math.max(CODE_FENCE_LENGTH - (filename.length + (TITLE_PADDING_LENGTH * 2)), 0)
|
||||
const startOpeningFenceLength = Math.floor(openingFenceLength / 2)
|
||||
const endOpeningFenceLength = Math.ceil(openingFenceLength / 2)
|
||||
const openingFence = `${'='.repeat(startOpeningFenceLength)}${title}${'='.repeat(endOpeningFenceLength)}`
|
||||
const closingFence = '='.repeat(CODE_FENCE_LENGTH)
|
||||
|
||||
return `${openingFence}\n${code}\n${closingFence}\n`
|
||||
}
|
||||
|
||||
function indicesOf (substring, string) {
|
||||
|
28
package.json
28
package.json
@ -22,8 +22,6 @@
|
||||
"test:unit:strict": "mocha --exit --require test/env.js --require test/setup.js --recursive \"test/unit/**/permissions/*.js\"",
|
||||
"test:unit:path": "mocha --exit --require test/env.js --require test/setup.js --recursive",
|
||||
"test:e2e:chrome": "SELENIUM_BROWSER=chrome test/e2e/run-all.sh",
|
||||
"test:web3:chrome": "SELENIUM_BROWSER=chrome test/e2e/run-web3.sh",
|
||||
"test:web3:firefox": "SELENIUM_BROWSER=firefox test/e2e/run-web3.sh",
|
||||
"test:e2e:firefox": "SELENIUM_BROWSER=firefox test/e2e/run-all.sh",
|
||||
"test:coverage": "nyc --silent --check-coverage yarn test:unit:strict && nyc --silent --no-clean yarn test:unit:lax && nyc report --reporter=text --reporter=html",
|
||||
"test:coverage:strict": "nyc --check-coverage yarn test:unit:strict",
|
||||
@ -37,6 +35,7 @@
|
||||
"lint:shellcheck": "./development/shellcheck.sh",
|
||||
"lint:styles": "stylelint '*/**/*.scss'",
|
||||
"lint:lockfile": "lockfile-lint --path yarn.lock --allowed-hosts npm yarn github.com codeload.github.com --empty-hostname false --allowed-schemes \"https:\" \"git+https:\"",
|
||||
"validate-source-maps": "node ./development/sourcemap-validator.js",
|
||||
"verify-locales": "node ./development/verify-locale-strings.js",
|
||||
"verify-locales:fix": "node ./development/verify-locale-strings.js --fix",
|
||||
"mozilla-lint": "addons-linter dist/firefox",
|
||||
@ -52,10 +51,13 @@
|
||||
"generate:migration": "./development/generate-migration.sh"
|
||||
},
|
||||
"resolutions": {
|
||||
"**/configstore/dot-prop": "^5.1.1",
|
||||
"**/ethers/elliptic": "^6.5.3",
|
||||
"**/knex/minimist": "^1.2.5",
|
||||
"**/optimist/minimist": "^1.2.5",
|
||||
"**/socketcluster/minimist": "^1.2.5",
|
||||
"3box/ipfs/ipld-zcash/zcash-bitcore-lib/lodash": "^4.17.19",
|
||||
"3box/ipfs/ipld-zcash/zcash-bitcore-lib/elliptic": "^6.5.3",
|
||||
"ganache-core/lodash": "^4.17.19"
|
||||
},
|
||||
"dependencies": {
|
||||
@ -98,7 +100,7 @@
|
||||
"eth-json-rpc-filters": "^4.1.1",
|
||||
"eth-json-rpc-infura": "^4.0.2",
|
||||
"eth-json-rpc-middleware": "^5.0.2",
|
||||
"eth-keyring-controller": "^6.0.1",
|
||||
"eth-keyring-controller": "^6.1.0",
|
||||
"eth-method-registry": "^1.2.0",
|
||||
"eth-phishing-detect": "^1.1.4",
|
||||
"eth-query": "^2.1.2",
|
||||
@ -119,12 +121,12 @@
|
||||
"fuse.js": "^3.2.0",
|
||||
"human-standard-token-abi": "^2.0.0",
|
||||
"jazzicon": "^2.0.0",
|
||||
"json-rpc-engine": "^5.1.8",
|
||||
"json-rpc-engine": "^5.2.0",
|
||||
"json-rpc-middleware-stream": "^2.1.1",
|
||||
"jsonschema": "^1.2.4",
|
||||
"lodash": "^4.17.19",
|
||||
"loglevel": "^1.4.1",
|
||||
"luxon": "^1.23.0",
|
||||
"luxon": "^1.24.1",
|
||||
"metamask-logo": "^2.1.4",
|
||||
"multihashes": "^0.4.12",
|
||||
"nanoid": "^2.1.6",
|
||||
@ -157,7 +159,7 @@
|
||||
"redux": "^4.0.5",
|
||||
"redux-thunk": "^2.3.0",
|
||||
"reselect": "^3.0.1",
|
||||
"rpc-cap": "^3.0.1",
|
||||
"rpc-cap": "^3.1.0",
|
||||
"safe-event-emitter": "^1.0.1",
|
||||
"safe-json-stringify": "^1.2.0",
|
||||
"single-call-balance-checker-abi": "^1.0.0",
|
||||
@ -192,8 +194,8 @@
|
||||
"babel-eslint": "^10.1.0",
|
||||
"babel-loader": "^8.0.6",
|
||||
"babelify": "^10.0.0",
|
||||
"brfs": "^1.6.1",
|
||||
"browserify": "^16.2.3",
|
||||
"brfs": "^2.0.2",
|
||||
"browserify": "^16.5.1",
|
||||
"browserify-derequire": "^1.0.1",
|
||||
"browserify-transform-tools": "^1.7.0",
|
||||
"chai": "^4.1.0",
|
||||
@ -230,13 +232,13 @@
|
||||
"gulp-imagemin": "^6.1.0",
|
||||
"gulp-livereload": "4.0.0",
|
||||
"gulp-multi-process": "^1.3.1",
|
||||
"gulp-rename": "^1.4.0",
|
||||
"gulp-rename": "^2.0.0",
|
||||
"gulp-replace": "^1.0.0",
|
||||
"gulp-rtlcss": "^1.4.0",
|
||||
"gulp-sass": "^4.0.0",
|
||||
"gulp-sourcemaps": "^2.6.0",
|
||||
"gulp-stylelint": "^13.0.0",
|
||||
"gulp-terser-js": "^5.0.0",
|
||||
"gulp-terser-js": "^5.2.2",
|
||||
"gulp-watch": "^5.0.1",
|
||||
"gulp-zip": "^4.0.0",
|
||||
"jsdom": "^11.2.0",
|
||||
@ -251,7 +253,7 @@
|
||||
"proxyquire": "^2.1.3",
|
||||
"randomcolor": "^0.5.4",
|
||||
"rc": "^1.2.8",
|
||||
"react-devtools": "^4.4.0",
|
||||
"react-devtools": "^4.8.0",
|
||||
"react-test-renderer": "^16.12.0",
|
||||
"read-installed": "^4.0.3",
|
||||
"redux-mock-store": "^1.5.4",
|
||||
@ -263,10 +265,10 @@
|
||||
"selenium-webdriver": "^4.0.0-alpha.5",
|
||||
"serve-handler": "^6.1.2",
|
||||
"sesify": "^4.2.1",
|
||||
"sesify-viz": "^3.0.5",
|
||||
"sesify-viz": "^3.0.10",
|
||||
"sinon": "^9.0.0",
|
||||
"source-map": "^0.7.2",
|
||||
"source-map-explorer": "^2.0.1",
|
||||
"source-map-explorer": "^2.4.2",
|
||||
"string.prototype.matchall": "^4.0.2",
|
||||
"style-loader": "^0.21.0",
|
||||
"stylelint": "^13.6.1",
|
||||
|
@ -5,72 +5,91 @@ set -e
|
||||
set -u
|
||||
set -o pipefail
|
||||
|
||||
retry () {
|
||||
retry=0
|
||||
limit="${METAMASK_E2E_RETRY_LIMIT:-3}"
|
||||
while [[ $retry -lt $limit ]]
|
||||
do
|
||||
"$@" && break
|
||||
retry=$(( retry + 1 ))
|
||||
sleep 1
|
||||
done
|
||||
|
||||
if [[ $retry == "$limit" ]]
|
||||
then
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
export PATH="$PATH:./node_modules/.bin"
|
||||
|
||||
mocha --no-timeouts test/e2e/tests/*.spec.js
|
||||
for spec in test/e2e/tests/*.spec.js
|
||||
do
|
||||
retry mocha --no-timeouts "${spec}"
|
||||
done
|
||||
|
||||
concurrently --kill-others \
|
||||
retry concurrently --kill-others \
|
||||
--names 'dapp,e2e' \
|
||||
--prefix '[{time}][{name}]' \
|
||||
--success first \
|
||||
'yarn dapp' \
|
||||
'mocha test/e2e/metamask-ui.spec'
|
||||
|
||||
concurrently --kill-others \
|
||||
retry concurrently --kill-others \
|
||||
--names 'dapp,e2e' \
|
||||
--prefix '[{time}][{name}]' \
|
||||
--success first \
|
||||
'yarn dapp' \
|
||||
'mocha test/e2e/metamask-responsive-ui.spec'
|
||||
|
||||
concurrently --kill-others \
|
||||
retry concurrently --kill-others \
|
||||
--names 'dapp,e2e' \
|
||||
--prefix '[{time}][{name}]' \
|
||||
--success first \
|
||||
'yarn dapp' \
|
||||
'mocha test/e2e/signature-request.spec'
|
||||
|
||||
concurrently --kill-others \
|
||||
retry concurrently --kill-others \
|
||||
--names 'e2e' \
|
||||
--prefix '[{time}][{name}]' \
|
||||
--success first \
|
||||
'mocha test/e2e/from-import-ui.spec'
|
||||
|
||||
concurrently --kill-others \
|
||||
retry concurrently --kill-others \
|
||||
--names 'e2e' \
|
||||
--prefix '[{time}][{name}]' \
|
||||
--success first \
|
||||
'mocha test/e2e/send-edit.spec'
|
||||
|
||||
concurrently --kill-others \
|
||||
retry concurrently --kill-others \
|
||||
--names 'dapp,e2e' \
|
||||
--prefix '[{time}][{name}]' \
|
||||
--success first \
|
||||
'yarn dapp' \
|
||||
'mocha test/e2e/ethereum-on.spec'
|
||||
|
||||
concurrently --kill-others \
|
||||
retry concurrently --kill-others \
|
||||
--names 'dapp,e2e' \
|
||||
--prefix '[{time}][{name}]' \
|
||||
--success first \
|
||||
'yarn dapp' \
|
||||
'mocha test/e2e/permissions.spec'
|
||||
|
||||
concurrently --kill-others \
|
||||
retry concurrently --kill-others \
|
||||
--names 'sendwithprivatedapp,e2e' \
|
||||
--prefix '[{time}][{name}]' \
|
||||
--success first \
|
||||
'yarn sendwithprivatedapp' \
|
||||
'mocha test/e2e/incremental-security.spec'
|
||||
|
||||
concurrently --kill-others \
|
||||
retry concurrently --kill-others \
|
||||
--names 'dapp,e2e' \
|
||||
--prefix '[{time}][{name}]' \
|
||||
--success first \
|
||||
'yarn dapp' \
|
||||
'mocha test/e2e/address-book.spec'
|
||||
|
||||
concurrently --kill-others \
|
||||
retry concurrently --kill-others \
|
||||
--names '3box,dapp,e2e' \
|
||||
--prefix '[{time}][{name}]' \
|
||||
--success first \
|
||||
|
@ -1,13 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
set -u
|
||||
set -o pipefail
|
||||
|
||||
export PATH="$PATH:./node_modules/.bin"
|
||||
|
||||
concurrently --kill-others \
|
||||
--names 'dapp,e2e' \
|
||||
--prefix '[{time}][{name}]' \
|
||||
'node development/static-server.js test/web3 --port 8080' \
|
||||
'sleep 5 && mocha test/e2e/web3.spec'
|
@ -1,288 +0,0 @@
|
||||
const assert = require('assert')
|
||||
const webdriver = require('selenium-webdriver')
|
||||
|
||||
const { By } = webdriver
|
||||
const {
|
||||
regularDelayMs,
|
||||
largeDelayMs,
|
||||
} = require('./helpers')
|
||||
const { buildWebDriver } = require('./webdriver')
|
||||
const enLocaleMessages = require('../../app/_locales/en/messages.json')
|
||||
|
||||
describe('Using MetaMask with an existing account', function () {
|
||||
let driver
|
||||
|
||||
const testSeedPhrase = 'forum vessel pink push lonely enact gentle tail admit parrot grunt dress'
|
||||
|
||||
const button = async (x) => {
|
||||
const buttoncheck = x
|
||||
await buttoncheck.click()
|
||||
await driver.delay(largeDelayMs)
|
||||
const [results] = await driver.findElements(By.css('#results'))
|
||||
const resulttext = await results.getText()
|
||||
const parsedData = JSON.parse(resulttext)
|
||||
|
||||
return (parsedData)
|
||||
|
||||
}
|
||||
|
||||
this.timeout(0)
|
||||
this.bail(true)
|
||||
|
||||
before(async function () {
|
||||
const result = await buildWebDriver()
|
||||
driver = result.driver
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
if (process.env.SELENIUM_BROWSER === 'chrome') {
|
||||
const errors = await driver.checkBrowserForConsoleErrors(driver)
|
||||
if (errors.length) {
|
||||
const errorReports = errors.map((err) => err.message)
|
||||
const errorMessage = `Errors found in browser console:\n${errorReports.join('\n')}`
|
||||
console.error(new Error(errorMessage))
|
||||
}
|
||||
}
|
||||
if (this.currentTest.state === 'failed') {
|
||||
await driver.verboseReportOnFailure(this.currentTest.title)
|
||||
}
|
||||
})
|
||||
|
||||
after(async function () {
|
||||
await driver.quit()
|
||||
})
|
||||
|
||||
describe('First time flow starting from an existing seed phrase', function () {
|
||||
it('clicks the continue button on the welcome screen', async function () {
|
||||
await driver.findElement(By.css('.welcome-page__header'))
|
||||
await driver.clickElement(By.xpath(`//button[contains(text(), '${enLocaleMessages.getStarted.message}')]`))
|
||||
await driver.delay(largeDelayMs)
|
||||
})
|
||||
|
||||
it('clicks the "Import Wallet" option', async function () {
|
||||
await driver.clickElement(By.xpath(`//button[contains(text(), 'Import wallet')]`))
|
||||
await driver.delay(largeDelayMs)
|
||||
})
|
||||
|
||||
it('clicks the "No thanks" option on the metametrics opt-in screen', async function () {
|
||||
await driver.clickElement(By.css('.btn-default'))
|
||||
await driver.delay(largeDelayMs)
|
||||
})
|
||||
|
||||
it('imports a seed phrase', async function () {
|
||||
const [seedTextArea] = await driver.findElements(By.css('input[placeholder="Paste seed phrase from clipboard"]'))
|
||||
await seedTextArea.sendKeys(testSeedPhrase)
|
||||
await driver.delay(regularDelayMs)
|
||||
|
||||
const [password] = await driver.findElements(By.id('password'))
|
||||
await password.sendKeys('correct horse battery staple')
|
||||
const [confirmPassword] = await driver.findElements(By.id('confirm-password'))
|
||||
confirmPassword.sendKeys('correct horse battery staple')
|
||||
|
||||
await driver.clickElement(By.css('.first-time-flow__terms'))
|
||||
|
||||
await driver.clickElement(By.xpath(`//button[contains(text(), 'Import')]`))
|
||||
await driver.delay(regularDelayMs)
|
||||
})
|
||||
|
||||
it('clicks through the success screen', async function () {
|
||||
await driver.findElement(By.xpath(`//div[contains(text(), 'Congratulations')]`))
|
||||
await driver.clickElement(By.xpath(`//button[contains(text(), '${enLocaleMessages.endOfFlowMessage10.message}')]`))
|
||||
await driver.delay(regularDelayMs)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
describe('opens dapp', function () {
|
||||
|
||||
it('switches to mainnet', async function () {
|
||||
await driver.clickElement(By.css('.network-name'))
|
||||
await driver.delay(regularDelayMs)
|
||||
|
||||
await driver.clickElement(By.xpath(`//span[contains(text(), 'Main Ethereum Network')]`))
|
||||
await driver.delay(largeDelayMs * 2)
|
||||
})
|
||||
|
||||
it('connects to dapp', async function () {
|
||||
await driver.openNewPage('http://127.0.0.1:8080/')
|
||||
await driver.delay(regularDelayMs)
|
||||
|
||||
await driver.clickElement(By.xpath(`//button[contains(text(), 'Connect')]`))
|
||||
|
||||
await driver.delay(regularDelayMs)
|
||||
|
||||
await driver.waitUntilXWindowHandles(3)
|
||||
const windowHandles = await driver.getAllWindowHandles()
|
||||
|
||||
const extension = windowHandles[0]
|
||||
const popup = await driver.switchToWindowWithTitle('MetaMask Notification', windowHandles)
|
||||
const dapp = windowHandles.find((handle) => handle !== extension && handle !== popup)
|
||||
|
||||
await driver.delay(regularDelayMs)
|
||||
await driver.clickElement(By.xpath(`//button[contains(text(), 'Connect')]`))
|
||||
|
||||
await driver.switchToWindow(dapp)
|
||||
await driver.delay(regularDelayMs)
|
||||
})
|
||||
})
|
||||
|
||||
describe('testing web3 methods', function () {
|
||||
|
||||
|
||||
it('testing hexa methods', async function () {
|
||||
|
||||
|
||||
const List = await driver.findClickableElements(By.className('hexaNumberMethods'))
|
||||
|
||||
for (let i = 0; i < List.length; i++) {
|
||||
try {
|
||||
|
||||
const parsedData = await button(List[i])
|
||||
console.log(parsedData)
|
||||
const result = parseInt(parsedData.result, 16)
|
||||
|
||||
assert.equal((typeof result === 'number'), true)
|
||||
await driver.delay(regularDelayMs)
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
assert(false)
|
||||
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
it('testing booleanMethods', async function () {
|
||||
|
||||
const List = await driver.findClickableElement(By.className('booleanMethods'))
|
||||
|
||||
for (let i = 0; i < List.length; i++) {
|
||||
try {
|
||||
|
||||
const parsedData = await button(List[i])
|
||||
console.log(parsedData)
|
||||
const result = parsedData.result
|
||||
|
||||
assert.equal(result, false)
|
||||
await driver.delay(regularDelayMs)
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
assert(false)
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
it('testing transactionMethods', async function () {
|
||||
|
||||
const List = await driver.findClickableElement(By.className('transactionMethods'))
|
||||
|
||||
for (let i = 0; i < List.length; i++) {
|
||||
try {
|
||||
|
||||
const parsedData = await button(List[i])
|
||||
|
||||
console.log(parsedData.result.blockHash)
|
||||
|
||||
const result = []
|
||||
result.push(parseInt(parsedData.result.blockHash, 16))
|
||||
result.push(parseInt(parsedData.result.blockNumber, 16))
|
||||
result.push(parseInt(parsedData.result.gas, 16))
|
||||
result.push(parseInt(parsedData.result.gasPrice, 16))
|
||||
result.push(parseInt(parsedData.result.hash, 16))
|
||||
result.push(parseInt(parsedData.result.input, 16))
|
||||
result.push(parseInt(parsedData.result.nonce, 16))
|
||||
result.push(parseInt(parsedData.result.r, 16))
|
||||
result.push(parseInt(parsedData.result.s, 16))
|
||||
result.push(parseInt(parsedData.result.v, 16))
|
||||
result.push(parseInt(parsedData.result.to, 16))
|
||||
result.push(parseInt(parsedData.result.value, 16))
|
||||
|
||||
|
||||
result.forEach((value) => {
|
||||
assert.equal((typeof value === 'number'), true)
|
||||
})
|
||||
|
||||
|
||||
} catch (err) {
|
||||
|
||||
console.log(err)
|
||||
assert(false)
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
it('testing blockMethods', async function () {
|
||||
|
||||
const List = await driver.findClickableElement(By.className('blockMethods'))
|
||||
|
||||
for (let i = 0; i < List.length; i++) {
|
||||
try {
|
||||
|
||||
const parsedData = await button(List[i])
|
||||
console.log(JSON.stringify(parsedData) + i)
|
||||
|
||||
console.log(parsedData.result.parentHash)
|
||||
|
||||
const result = parseInt(parsedData.result.parentHash, 16)
|
||||
|
||||
assert.equal((typeof result === 'number'), true)
|
||||
await driver.delay(regularDelayMs)
|
||||
} catch (err) {
|
||||
|
||||
console.log(err)
|
||||
assert(false)
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
it('testing methods', async function () {
|
||||
|
||||
const List = await driver.findClickableElement(By.className('methods'))
|
||||
let parsedData
|
||||
let result
|
||||
|
||||
for (let i = 0; i < List.length; i++) {
|
||||
try {
|
||||
|
||||
if (i === 2) {
|
||||
|
||||
parsedData = await button(List[i])
|
||||
console.log(parsedData.result.blockHash)
|
||||
|
||||
result = parseInt(parsedData.result.blockHash, 16)
|
||||
|
||||
assert.equal((typeof result === 'number' || (result === 0)), true)
|
||||
await driver.delay(regularDelayMs)
|
||||
} else {
|
||||
parsedData = await button(List[i])
|
||||
console.log(parsedData.result)
|
||||
|
||||
result = parseInt(parsedData.result, 16)
|
||||
|
||||
assert.equal((typeof result === 'number' || (result === 0)), true)
|
||||
await driver.delay(regularDelayMs)
|
||||
}
|
||||
|
||||
|
||||
} catch (err) {
|
||||
|
||||
console.log(err)
|
||||
assert(false)
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
})
|
||||
|
||||
|
||||
})
|
@ -18,7 +18,6 @@ describe('NetworkController', function () {
|
||||
.reply(200)
|
||||
|
||||
networkController = new NetworkController()
|
||||
networkController.initializeProvider(networkControllerProviderConfig)
|
||||
})
|
||||
|
||||
afterEach(function () {
|
||||
@ -34,8 +33,9 @@ describe('NetworkController', function () {
|
||||
assert.equal(providerProxy.test, true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#getNetworkState', function () {
|
||||
it('should return loading when new', function () {
|
||||
it('should return "loading" when new', function () {
|
||||
const networkState = networkController.getNetworkState()
|
||||
assert.equal(networkState, 'loading', 'network is loading')
|
||||
})
|
||||
@ -51,11 +51,13 @@ describe('NetworkController', function () {
|
||||
|
||||
describe('#setProviderType', function () {
|
||||
it('should update provider.type', function () {
|
||||
networkController.initializeProvider(networkControllerProviderConfig)
|
||||
networkController.setProviderType('mainnet')
|
||||
const type = networkController.getProviderConfig().type
|
||||
assert.equal(type, 'mainnet', 'provider type is updated')
|
||||
})
|
||||
it('should set the network to loading', function () {
|
||||
networkController.initializeProvider(networkControllerProviderConfig)
|
||||
networkController.setProviderType('mainnet')
|
||||
const loading = networkController.isNetworkLoading()
|
||||
assert.ok(loading, 'network is loading')
|
||||
|
@ -19,25 +19,44 @@ export function grantPermissions (permController, origin, permissions) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the underlying rpc-cap requestUserApproval function, and returns
|
||||
* a promise that's resolved once it has been set.
|
||||
* Returns a wrapper for the given permissions controller's requestUserApproval
|
||||
* function, so we don't have to worry about its internals.
|
||||
*
|
||||
* This function must be called on the given permissions controller every
|
||||
* time you want such a Promise. As of writing, it's only called once per test.
|
||||
* @param {PermissionsController} permController - The permissions controller.
|
||||
* @return {Function} A convenient wrapper for the requestUserApproval function.
|
||||
*/
|
||||
export function getRequestUserApprovalHelper (permController) {
|
||||
/**
|
||||
* Returns a request object that can be passed to requestUserApproval.
|
||||
*
|
||||
* @param {string} id - The internal permissions request ID (not the RPC request ID).
|
||||
* @param {string} [origin] - The origin of the request, if necessary.
|
||||
* @returns {Object} The corresponding request object.
|
||||
*/
|
||||
return (id, origin = 'defaultOrigin') => {
|
||||
return permController.permissions.requestUserApproval({ metadata: { id, origin } })
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Promise that resolves once a pending user approval has been set.
|
||||
* Calls the underlying requestUserApproval function as normal, and restores it
|
||||
* once the Promise is resolved.
|
||||
*
|
||||
* This function must be called on the permissions controller for each request.
|
||||
*
|
||||
* @param {PermissionsController} - A permissions controller.
|
||||
* @returns {Promise<void>} A Promise that resolves once a pending approval
|
||||
* has been set.
|
||||
*/
|
||||
export function getUserApprovalPromise (permController) {
|
||||
return new Promise((resolveForCaller) => {
|
||||
permController.permissions.requestUserApproval = async (req) => {
|
||||
const { origin, metadata: { id } } = req
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
permController.pendingApprovals.set(id, { origin, resolve, reject })
|
||||
resolveForCaller()
|
||||
})
|
||||
const originalFunction = permController.permissions.requestUserApproval
|
||||
return new Promise((resolveHelperPromise) => {
|
||||
permController.permissions.requestUserApproval = (req) => {
|
||||
const userApprovalPromise = originalFunction(req)
|
||||
permController.permissions.requestUserApproval = originalFunction
|
||||
resolveHelperPromise()
|
||||
return userApprovalPromise
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ import {
|
||||
} from '../../../../../app/scripts/controllers/permissions'
|
||||
|
||||
import {
|
||||
getRequestUserApprovalHelper,
|
||||
grantPermissions,
|
||||
} from './helpers'
|
||||
|
||||
@ -58,12 +59,6 @@ const initPermController = (notifications = initNotifications()) => {
|
||||
})
|
||||
}
|
||||
|
||||
const getMockRequestUserApprovalFunction = (permController) => (id, origin) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
permController.pendingApprovals.set(id, { origin, resolve, reject })
|
||||
})
|
||||
}
|
||||
|
||||
describe('permissions controller', function () {
|
||||
|
||||
describe('getAccounts', function () {
|
||||
@ -951,13 +946,11 @@ describe('permissions controller', function () {
|
||||
|
||||
describe('approvePermissionsRequest', function () {
|
||||
|
||||
let permController, mockRequestUserApproval
|
||||
let permController, requestUserApproval
|
||||
|
||||
beforeEach(function () {
|
||||
permController = initPermController()
|
||||
mockRequestUserApproval = getMockRequestUserApprovalFunction(
|
||||
permController,
|
||||
)
|
||||
requestUserApproval = getRequestUserApprovalHelper(permController)
|
||||
})
|
||||
|
||||
it('does nothing if called on non-existing request', async function () {
|
||||
@ -994,14 +987,14 @@ describe('permissions controller', function () {
|
||||
PERMS.requests.eth_accounts(),
|
||||
)
|
||||
|
||||
const requestRejection = assert.rejects(
|
||||
mockRequestUserApproval(REQUEST_IDS.a),
|
||||
const rejectionPromise = assert.rejects(
|
||||
requestUserApproval(REQUEST_IDS.a),
|
||||
ERRORS.validatePermittedAccounts.invalidParam(),
|
||||
'should reject bad accounts',
|
||||
'should reject with "null" accounts',
|
||||
)
|
||||
|
||||
await permController.approvePermissionsRequest(request, null)
|
||||
await requestRejection
|
||||
await rejectionPromise
|
||||
|
||||
assert.equal(
|
||||
permController.pendingApprovals.size, 0,
|
||||
@ -1014,7 +1007,7 @@ describe('permissions controller', function () {
|
||||
const request = PERMS.approvedRequest(REQUEST_IDS.a, {})
|
||||
|
||||
const requestRejection = assert.rejects(
|
||||
mockRequestUserApproval(REQUEST_IDS.a),
|
||||
requestUserApproval(REQUEST_IDS.a),
|
||||
ERRORS.approvePermissionsRequest.noPermsRequested(),
|
||||
'should reject if no permissions in request',
|
||||
)
|
||||
@ -1036,7 +1029,7 @@ describe('permissions controller', function () {
|
||||
|
||||
const requestApproval = assert.doesNotReject(
|
||||
async () => {
|
||||
perms = await mockRequestUserApproval(REQUEST_IDS.a)
|
||||
perms = await requestUserApproval(REQUEST_IDS.a)
|
||||
},
|
||||
'should not reject single valid request',
|
||||
)
|
||||
@ -1065,14 +1058,14 @@ describe('permissions controller', function () {
|
||||
|
||||
const approval1 = assert.doesNotReject(
|
||||
async () => {
|
||||
perms1 = await mockRequestUserApproval(REQUEST_IDS.a)
|
||||
perms1 = await requestUserApproval(REQUEST_IDS.a, DOMAINS.a.origin)
|
||||
},
|
||||
'should not reject request',
|
||||
)
|
||||
|
||||
const approval2 = assert.doesNotReject(
|
||||
async () => {
|
||||
perms2 = await mockRequestUserApproval(REQUEST_IDS.b)
|
||||
perms2 = await requestUserApproval(REQUEST_IDS.b, DOMAINS.b.origin)
|
||||
},
|
||||
'should not reject request',
|
||||
)
|
||||
@ -1105,13 +1098,11 @@ describe('permissions controller', function () {
|
||||
|
||||
describe('rejectPermissionsRequest', function () {
|
||||
|
||||
let permController, mockRequestUserApproval
|
||||
let permController, requestUserApproval
|
||||
|
||||
beforeEach(async function () {
|
||||
permController = initPermController()
|
||||
mockRequestUserApproval = getMockRequestUserApprovalFunction(
|
||||
permController,
|
||||
)
|
||||
requestUserApproval = getRequestUserApprovalHelper(permController)
|
||||
})
|
||||
|
||||
it('does nothing if called on non-existing request', async function () {
|
||||
@ -1135,7 +1126,7 @@ describe('permissions controller', function () {
|
||||
it('rejects single existing request', async function () {
|
||||
|
||||
const requestRejection = assert.rejects(
|
||||
mockRequestUserApproval(REQUEST_IDS.a),
|
||||
requestUserApproval(REQUEST_IDS.a),
|
||||
ERRORS.rejectPermissionsRequest.rejection(),
|
||||
'should reject with expected error',
|
||||
)
|
||||
@ -1152,13 +1143,13 @@ describe('permissions controller', function () {
|
||||
it('rejects requests regardless of order', async function () {
|
||||
|
||||
const requestRejection1 = assert.rejects(
|
||||
mockRequestUserApproval(REQUEST_IDS.b),
|
||||
requestUserApproval(REQUEST_IDS.b, DOMAINS.b.origin),
|
||||
ERRORS.rejectPermissionsRequest.rejection(),
|
||||
'should reject with expected error',
|
||||
)
|
||||
|
||||
const requestRejection2 = assert.rejects(
|
||||
mockRequestUserApproval(REQUEST_IDS.c),
|
||||
requestUserApproval(REQUEST_IDS.c, DOMAINS.c.origin),
|
||||
ERRORS.rejectPermissionsRequest.rejection(),
|
||||
'should reject with expected error',
|
||||
)
|
||||
|
@ -70,11 +70,15 @@ describe('permissions middleware', function () {
|
||||
)
|
||||
const res = {}
|
||||
|
||||
const userApprovalPromise = getUserApprovalPromise(permController)
|
||||
|
||||
const pendingApproval = assert.doesNotReject(
|
||||
aMiddleware(req, res),
|
||||
'should not reject permissions request',
|
||||
)
|
||||
|
||||
await userApprovalPromise
|
||||
|
||||
assert.equal(
|
||||
permController.pendingApprovals.size, 1,
|
||||
'perm controller should have single pending approval',
|
||||
@ -131,11 +135,15 @@ describe('permissions middleware', function () {
|
||||
// send, approve, and validate first request
|
||||
// note use of ACCOUNTS.a.permitted
|
||||
|
||||
let userApprovalPromise = getUserApprovalPromise(permController)
|
||||
|
||||
const pendingApproval1 = assert.doesNotReject(
|
||||
aMiddleware(req1, res1),
|
||||
'should not reject permissions request',
|
||||
)
|
||||
|
||||
await userApprovalPromise
|
||||
|
||||
const id1 = permController.pendingApprovals.keys().next().value
|
||||
const approvedReq1 = PERMS.approvedRequest(id1, PERMS.requests.eth_accounts())
|
||||
|
||||
@ -187,11 +195,15 @@ describe('permissions middleware', function () {
|
||||
// send, approve, and validate second request
|
||||
// note use of ACCOUNTS.b.permitted
|
||||
|
||||
userApprovalPromise = getUserApprovalPromise(permController)
|
||||
|
||||
const pendingApproval2 = assert.doesNotReject(
|
||||
aMiddleware(req2, res2),
|
||||
'should not reject permissions request',
|
||||
)
|
||||
|
||||
await userApprovalPromise
|
||||
|
||||
const id2 = permController.pendingApprovals.keys().next().value
|
||||
const approvedReq2 = PERMS.approvedRequest(id2, { ...requestedPerms2 })
|
||||
|
||||
@ -251,12 +263,16 @@ describe('permissions middleware', function () {
|
||||
|
||||
const expectedError = ERRORS.rejectPermissionsRequest.rejection()
|
||||
|
||||
const userApprovalPromise = getUserApprovalPromise(permController)
|
||||
|
||||
const requestRejection = assert.rejects(
|
||||
aMiddleware(req, res),
|
||||
expectedError,
|
||||
'request should be rejected with correct error',
|
||||
)
|
||||
|
||||
await userApprovalPromise
|
||||
|
||||
assert.equal(
|
||||
permController.pendingApprovals.size, 1,
|
||||
'perm controller should have single pending approval',
|
||||
@ -343,11 +359,15 @@ describe('permissions middleware', function () {
|
||||
)
|
||||
const resA1 = {}
|
||||
|
||||
let userApprovalPromise = getUserApprovalPromise(permController)
|
||||
|
||||
const requestApproval1 = assert.doesNotReject(
|
||||
aMiddleware(reqA1, resA1),
|
||||
'should not reject permissions request',
|
||||
)
|
||||
|
||||
await userApprovalPromise
|
||||
|
||||
// create and start processing first request for second origin
|
||||
|
||||
const reqB1 = RPC_REQUESTS.requestPermission(
|
||||
@ -355,11 +375,15 @@ describe('permissions middleware', function () {
|
||||
)
|
||||
const resB1 = {}
|
||||
|
||||
userApprovalPromise = getUserApprovalPromise(permController)
|
||||
|
||||
const requestApproval2 = assert.doesNotReject(
|
||||
bMiddleware(reqB1, resB1),
|
||||
'should not reject permissions request',
|
||||
)
|
||||
|
||||
await userApprovalPromise
|
||||
|
||||
assert.equal(
|
||||
permController.pendingApprovals.size, 2,
|
||||
'perm controller should have expected number of pending approvals',
|
||||
@ -373,12 +397,17 @@ describe('permissions middleware', function () {
|
||||
)
|
||||
const resA2 = {}
|
||||
|
||||
await assert.rejects(
|
||||
userApprovalPromise = getUserApprovalPromise(permController)
|
||||
|
||||
const requestApprovalFail = assert.rejects(
|
||||
aMiddleware(reqA2, resA2),
|
||||
expectedError,
|
||||
'request should be rejected with correct error',
|
||||
)
|
||||
|
||||
await userApprovalPromise
|
||||
await requestApprovalFail
|
||||
|
||||
assert.ok(
|
||||
(
|
||||
!resA2.result && resA2.error &&
|
||||
|
@ -1,105 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Web3 Test Dapp</title>
|
||||
</head>
|
||||
<body>
|
||||
<div style="display: flex; flex-flow: column;">
|
||||
<div style="display: flex; font-size: 1.25rem;">hexaNumberMethods</div>
|
||||
<div style="display: flex;">
|
||||
<button id="eth_blockNumber" class="hexaNumberMethods">eth_blockNumber</button>
|
||||
|
||||
<button id="eth_gasPrice" class="hexaNumberMethods">eth_gasPrice</button>
|
||||
<button id="eth_newBlockFilter" class="hexaNumberMethods">eth_newBlockFilter</button>
|
||||
<button id="eth_newPendingTransactionFilter" class="hexaNumberMethods">
|
||||
eth_newPendingTransactionFilter
|
||||
</button>
|
||||
<button id="eth_getUncleCountByBlockHash" class="hexaNumberMethods">
|
||||
eth_getUncleCountByBlockHash
|
||||
</button>
|
||||
<button id="eth_getBlockTransactionCountByHash" class="hexaNumberMethods">
|
||||
getBlockTransactionCountByHash
|
||||
</button>
|
||||
</div>
|
||||
<div style="display: flex ;">
|
||||
<button id="eth_getTransactionCount" class="hexaNumberMethods">eth_getTransactionCount</button>
|
||||
<button id="eth_getBalance" class="hexaNumberMethods">eth_getBalance</button>
|
||||
<button id="eth_estimateGas" class="hexaNumberMethods">eth_estimateGas</button>
|
||||
</div>
|
||||
<div style="display: flex ;">
|
||||
|
||||
<button id="eth_getUncleCountByBlockNumber" class="hexaNumberMethods">
|
||||
eth_getUncleCountByBlockNumber
|
||||
</button>
|
||||
<button id='eth_getBlockTransactionCountByNumber' class="hexaNumberMethods">
|
||||
eth_getBlockTransactionCountByNumber
|
||||
</button>
|
||||
<button id="eth_protocolVersion" class="hexaNumberMethods">eth_protocolVersion</button>
|
||||
|
||||
<button id="eth_getCode" class="hexaNumberMethods">eth_getCode</button>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: flex; flex-flow: column;">
|
||||
<div style="display: flex; font-size: 1.25rem;">booleanMethods</div>
|
||||
<div style="display: flex ;">
|
||||
<button id="eth_uninstallFilter" class = 'booleanMethods'>eth_uninstallFilter</button>
|
||||
<button id="eth_mining" class = 'booleanMethods'>eth_mining</button>
|
||||
<button id="eth_syncing" class = 'booleanMethods'>eth_syncing</button>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: flex; flex-flow: column;">
|
||||
<div style="display: flex; font-size: 1.25rem;" >transactionMethods</div>
|
||||
<div style="display: flex ;">
|
||||
<button id="eth_getTransactionByHash" class='transactionMethods'>eth_getTransactionByHash</button>
|
||||
<button id="eth_getTransactionByBlockHashAndIndex" class = 'transactionMethods'>
|
||||
eth_getTransactionByBlockHashAndIndex
|
||||
</button>
|
||||
<button id="eth_getTransactionByBlockNumberAndIndex" class="transactionMethods">
|
||||
eth_getTransactionByBlockNumberAndIndex
|
||||
</button>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; flex-flow: column;">
|
||||
<div style="display: flex; font-size: 1.25rem;">blockMethods</div>
|
||||
|
||||
<div style="display: flex ;">
|
||||
|
||||
|
||||
<button id="eth_getUncleByBlockHashAndIndex" class="blockMethods">
|
||||
eth_getUncleByBlockHashAndIndex
|
||||
</button>
|
||||
<button id="eth_getBlockByHash" class="blockMethods">eth_getBlockByHash</button>
|
||||
</div>
|
||||
<div style="display: flex ;">
|
||||
<button id="eth_getBlockByNumber" class="blockMethods">eth_getBlockByNumber</button>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; flex-flow: column;">
|
||||
<div style="display: flex; font-size: 1.25rem;">Methods</div>
|
||||
<div style="display: flex ;">
|
||||
<button id="eth_call" class = 'methods'>eth_call</button>
|
||||
<button id="eth_getStorageAt" class="methods">eth_getStorageAt</button>
|
||||
<button id="eth_getTransactionReceipt" class="methods">
|
||||
eth_getTransactionReceipt
|
||||
</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: flex; flex-flow: column;">
|
||||
<div id='results'></div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
<script src="schema.js"></script>
|
||||
<script src="web3.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -1,209 +0,0 @@
|
||||
/* eslint no-unused-vars: 0 */
|
||||
|
||||
const params = {
|
||||
// diffrent params used in the methods
|
||||
param: [],
|
||||
blockHashParams: '0xb3b20624f8f0f86eb50dd04688409e5cea4bd02d700bf6e79e9384d47d6a5a35',
|
||||
filterParams: ['0xfe704947a3cd3ca12541458a4321c869'],
|
||||
transactionHashParams: [
|
||||
'0xbb3a336e3f823ec18197f1e13ee875700f08f03e2cab75f0d0b118dabb44cba0',
|
||||
],
|
||||
blockHashAndIndexParams: [
|
||||
'0xb3b20624f8f0f86eb50dd04688409e5cea4bd02d700bf6e79e9384d47d6a5a35',
|
||||
'0x0',
|
||||
],
|
||||
uncleByBlockNumberAndIndexParams: ['0x29c', '0x0'],
|
||||
blockParameterParams: '0x5bad55',
|
||||
data: '0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675',
|
||||
addressParams: '0xc94770007dda54cF92009BFF0dE90c06F603a09f',
|
||||
getStorageAtParams: [
|
||||
'0x295a70b2de5e3953354a6a8344e616ed314d7251',
|
||||
'0x6661e9d6d8b923d5bbaab1b96e1dd51ff6ea2a93520fdc9eb75d059238b8c5e9',
|
||||
'0x65a8db',
|
||||
],
|
||||
getCodeParams: ['0x06012c8cf97bead5deae237070f9587f8e7a266d', '0x65a8db'],
|
||||
estimateTransaction: {
|
||||
from: '0xb60e8dd61c5d32be8058bb8eb970870f07233155',
|
||||
to: '0xd46e8dd67c5d32be8058bb8eb970870f07244567',
|
||||
gas: '0x76c0',
|
||||
gasPrice: '0x9184e72a000',
|
||||
value: '0x9184e72a',
|
||||
data: '0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675',
|
||||
},
|
||||
filterGetLogs: [{ 'blockHash': '0x7c5a35e9cb3e8ae0e221ab470abae9d446c3a5626ce6689fc777dcffcab52c70', 'topics': ['0x241ea03ca20251805084d27d4440371c34a0b85ff108f6bb5611248f73818b80'] }],
|
||||
block: {
|
||||
__required: [],
|
||||
number: 'Q',
|
||||
hash: 'D32',
|
||||
parentHash: 'D32',
|
||||
nonce: 'D',
|
||||
sha3Uncles: 'D',
|
||||
logsBloom: 'D',
|
||||
transactionsRoot: 'D',
|
||||
stateRoot: 'D',
|
||||
receiptsRoot: 'D',
|
||||
miner: 'D',
|
||||
difficulty: 'Q',
|
||||
totalDifficulty: 'Q',
|
||||
extraData: 'D',
|
||||
size: 'Q',
|
||||
gasLimit: 'Q',
|
||||
gasUsed: 'Q',
|
||||
timestamp: 'Q',
|
||||
transactions: ['DATA|Transaction'],
|
||||
uncles: ['D'],
|
||||
},
|
||||
transaction: {
|
||||
__required: [],
|
||||
hash: 'D32',
|
||||
nonce: 'Q',
|
||||
blockHash: 'D32',
|
||||
blockNumber: 'Q',
|
||||
transactionIndex: 'Q',
|
||||
from: 'D20',
|
||||
to: 'D20',
|
||||
value: 'Q',
|
||||
gasPrice: 'Q',
|
||||
gas: 'Q',
|
||||
input: 'D',
|
||||
},
|
||||
receipt: {
|
||||
__required: [],
|
||||
transactionHash: 'D32',
|
||||
transactionIndex: 'Q',
|
||||
blockHash: 'D32',
|
||||
blockNumber: 'Q',
|
||||
cumulativeGasUsed: 'Q',
|
||||
gasUsed: 'Q',
|
||||
contractAddress: 'D20',
|
||||
logs: ['FilterChange'],
|
||||
},
|
||||
|
||||
filterChange: {
|
||||
__required: [],
|
||||
removed: 'B',
|
||||
logIndex: 'Q',
|
||||
transactionIndex: 'Q',
|
||||
transactionHash: 'D32',
|
||||
blockHash: 'D32',
|
||||
blockNumber: 'Q',
|
||||
address: 'D20',
|
||||
data: 'Array|DATA',
|
||||
topics: ['D'],
|
||||
},
|
||||
}
|
||||
|
||||
const methods = {
|
||||
hexaNumberMethods: {
|
||||
// these are the methods which have output in the form of hexa decimal numbers
|
||||
eth_blockNumber: ['eth_blockNumber', params.param, 'Q'],
|
||||
eth_gasPrice: ['eth_gasPrice', params.param, 'Q'],
|
||||
eth_newBlockFilter: ['eth_newBlockFilter', params.param, 'Q'],
|
||||
eth_newPendingTransactionFilter: [
|
||||
'eth_newPendingTransactionFilter',
|
||||
params.param,
|
||||
'Q',
|
||||
],
|
||||
eth_getUncleCountByBlockHash: [
|
||||
'eth_getUncleCountByBlockHash',
|
||||
[params.blockHashParams],
|
||||
'Q',
|
||||
1,
|
||||
],
|
||||
eth_getBlockTransactionCountByHash: [
|
||||
'eth_getBlockTransactionCountByHash',
|
||||
[params.blockHashParams],
|
||||
'Q',
|
||||
1,
|
||||
],
|
||||
eth_getTransactionCount: [
|
||||
'eth_getTransactionCount',
|
||||
[params.addressParams, params.blockParameterParams],
|
||||
'Q',
|
||||
1,
|
||||
2,
|
||||
],
|
||||
eth_getBalance: ['eth_getBalance', [params.addressParams, 'latest'], 'Q', 1, 2],
|
||||
eth_estimateGas: ['eth_estimateGas', [params.estimateTransaction], 'Q', 1],
|
||||
eth_getUncleCountByBlockNumber: [
|
||||
'eth_getUncleCountByBlockNumber',
|
||||
[params.blockParameterParams],
|
||||
'Q',
|
||||
1,
|
||||
],
|
||||
eth_getBlockTransactionCountByNumber: [
|
||||
'eth_getBlockTransactionCountByNumber',
|
||||
['latest'],
|
||||
'Q',
|
||||
1,
|
||||
],
|
||||
eth_protocolVersion: ['eth_protocolVersion', params.param, 'S'],
|
||||
eth_getCode: ['eth_getCode', params.getCodeParams, 'D', 1, 2],
|
||||
},
|
||||
booleanMethods: {
|
||||
// these are the methods which have output in the form of boolean
|
||||
eth_uninstallFilter: ['eth_uninstallFilter', params.filterParams, 'B', 1],
|
||||
eth_mining: ['eth_mining', params.param, 'B'],
|
||||
eth_syncing: ['eth_syncing', params.param, 'B|EthSyncing'],
|
||||
},
|
||||
transactionMethods: {
|
||||
// these are the methods which have output in the form of transaction object
|
||||
eth_getTransactionByHash: [
|
||||
'eth_getTransactionByHash',
|
||||
params.transactionHashParams,
|
||||
params.transaction,
|
||||
1,
|
||||
],
|
||||
eth_getTransactionByBlockHashAndIndex: [
|
||||
'eth_getTransactionByBlockHashAndIndex',
|
||||
params.blockHashAndIndexParams,
|
||||
params.transaction,
|
||||
2,
|
||||
],
|
||||
eth_getTransactionByBlockNumberAndIndex: [
|
||||
'eth_getTransactionByBlockNumberAndIndex',
|
||||
[params.blockParameterParams, '0x0'],
|
||||
params.transaction,
|
||||
2,
|
||||
],
|
||||
|
||||
},
|
||||
blockMethods: {
|
||||
// these are the methods which have output in the form of a block
|
||||
|
||||
eth_getUncleByBlockNumberAndIndex: [
|
||||
'eth_getUncleByBlockNumberAndIndex',
|
||||
params.uncleByBlockNumberAndIndexParams,
|
||||
params.block,
|
||||
2,
|
||||
],
|
||||
eth_getBlockByHash: [
|
||||
'eth_getBlockByHash',
|
||||
[params.params, false],
|
||||
params.block,
|
||||
2,
|
||||
],
|
||||
eth_getBlockByNumber: [
|
||||
'eth_getBlockByNumber',
|
||||
[params.blockParameterParams, false],
|
||||
params.block,
|
||||
2,
|
||||
],
|
||||
},
|
||||
|
||||
methods: {
|
||||
// these are the methods which have output in the form of bytes data
|
||||
|
||||
eth_call: ['eth_call', [params.estimateTransaction, 'latest'], 'D', 1, 2],
|
||||
eth_getStorageAt: ['eth_getStorageAt', params.getStorageAtParams, 'D', 2, 2],
|
||||
eth_getTransactionReceipt: [
|
||||
'eth_getTransactionReceipt',
|
||||
params.transactionHashParams,
|
||||
params.receipt,
|
||||
1,
|
||||
],
|
||||
|
||||
},
|
||||
|
||||
}
|
||||
|
@ -1,34 +0,0 @@
|
||||
/* eslint no-undef: 0 */
|
||||
|
||||
const json = methods
|
||||
|
||||
web3.currentProvider.enable().then(() => {
|
||||
|
||||
Object.keys(json).forEach((methodGroupKey) => {
|
||||
|
||||
console.log(methodGroupKey)
|
||||
const methodGroup = json[methodGroupKey]
|
||||
console.log(methodGroup)
|
||||
Object.keys(methodGroup).forEach((methodKey) => {
|
||||
|
||||
const methodButton = document.getElementById(methodKey)
|
||||
methodButton.addEventListener('click', () => {
|
||||
|
||||
window.ethereum.sendAsync({
|
||||
method: methodKey,
|
||||
params: methodGroup[methodKey][1],
|
||||
}, (err, result) => {
|
||||
if (err) {
|
||||
console.log(err)
|
||||
console.log(methodKey)
|
||||
} else {
|
||||
document.getElementById('results').innerHTML = JSON.stringify(result)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
})
|
||||
|
@ -1,52 +1,47 @@
|
||||
import React, { Component } from 'react'
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import copyToClipboard from 'copy-to-clipboard'
|
||||
import { exportAsFile } from '../../../helpers/utils/util'
|
||||
import Copy from '../icon/copy-icon.component'
|
||||
import { useI18nContext } from '../../../hooks/useI18nContext'
|
||||
import { useCopyToClipboard } from '../../../hooks/useCopyToClipboard'
|
||||
|
||||
class ExportTextContainer extends Component {
|
||||
render () {
|
||||
const { text = '' } = this.props
|
||||
const { t } = this.context
|
||||
function ExportTextContainer ({ text = '' }) {
|
||||
const t = useI18nContext()
|
||||
const [copied, handleCopy] = useCopyToClipboard()
|
||||
|
||||
return (
|
||||
<div className="export-text-container">
|
||||
<div className="export-text-container__text-container">
|
||||
<div className="export-text-container__text notranslate">
|
||||
{text}
|
||||
return (
|
||||
<div className="export-text-container">
|
||||
<div className="export-text-container__text-container">
|
||||
<div className="export-text-container__text notranslate">{text}</div>
|
||||
</div>
|
||||
<div className="export-text-container__buttons-container">
|
||||
<div
|
||||
className="export-text-container__button export-text-container__button--copy"
|
||||
onClick={() => {
|
||||
handleCopy(text)
|
||||
}}
|
||||
>
|
||||
<Copy size={17} color="#3098DC" />
|
||||
<div className="export-text-container__button-text">
|
||||
{copied ? t('copiedExclamation') : t('copyToClipboard')}
|
||||
</div>
|
||||
</div>
|
||||
<div className="export-text-container__buttons-container">
|
||||
<div
|
||||
className="export-text-container__button export-text-container__button--copy"
|
||||
onClick={() => copyToClipboard(text)}
|
||||
>
|
||||
<Copy size={17} color="#3098DC" />
|
||||
<div className="export-text-container__button-text">
|
||||
{t('copyToClipboard')}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="export-text-container__button"
|
||||
onClick={() => exportAsFile('', text)}
|
||||
>
|
||||
<img src="images/download.svg" alt="" />
|
||||
<div className="export-text-container__button-text">
|
||||
{t('saveAsCsvFile')}
|
||||
</div>
|
||||
<div
|
||||
className="export-text-container__button"
|
||||
onClick={() => exportAsFile('', text)}
|
||||
>
|
||||
<img src="images/download.svg" alt="" />
|
||||
<div className="export-text-container__button-text">
|
||||
{t('saveAsCsvFile')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
ExportTextContainer.propTypes = {
|
||||
text: PropTypes.string,
|
||||
}
|
||||
|
||||
ExportTextContainer.contextTypes = {
|
||||
t: PropTypes.func,
|
||||
}
|
||||
|
||||
export default ExportTextContainer
|
||||
export default React.memo(ExportTextContainer)
|
||||
|
@ -41,7 +41,7 @@ export function MetaMetricsProvider ({ children }) {
|
||||
const numberOfAccounts = useSelector(getNumberOfAccounts)
|
||||
const history = useHistory()
|
||||
const [state, setState] = useState(() => ({
|
||||
currentPath: window.location.href,
|
||||
currentPath: (new URL(window.location.href)).pathname,
|
||||
previousPath: '',
|
||||
}))
|
||||
|
||||
@ -49,7 +49,7 @@ export function MetaMetricsProvider ({ children }) {
|
||||
|
||||
useEffect(() => {
|
||||
const unlisten = history.listen(() => setState((prevState) => ({
|
||||
currentPath: window.location.href,
|
||||
currentPath: (new URL(window.location.href)).pathname,
|
||||
previousPath: prevState.currentPath,
|
||||
})))
|
||||
// remove this listener if the component is no longer mounted
|
||||
@ -59,8 +59,8 @@ export function MetaMetricsProvider ({ children }) {
|
||||
const metricsEvent = useCallback((config = {}, overrides = {}) => {
|
||||
const { eventOpts = {} } = config
|
||||
const { name = '' } = eventOpts
|
||||
const { pathname: overRidePathName = '' } = overrides
|
||||
const isSendFlow = Boolean(name.match(/^send|^confirm/) || overRidePathName.match(/send|confirm/))
|
||||
const { currentPath: overrideCurrentPath = '' } = overrides
|
||||
const isSendFlow = Boolean(name.match(/^send|^confirm/) || overrideCurrentPath.match(/send|confirm/))
|
||||
|
||||
if (participateInMetaMetrics || config.isOptIn) {
|
||||
return sendMetaMetricsEvent({
|
||||
|
@ -75,7 +75,6 @@
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.dropdown-menu-item .fa.delete {
|
||||
@ -175,7 +174,7 @@
|
||||
}
|
||||
|
||||
.network-dropdown-content {
|
||||
height: 36px;
|
||||
min-height: 36px;
|
||||
width: 265px;
|
||||
color: $dusty-gray;
|
||||
font-family: Roboto;
|
||||
|
@ -6,6 +6,7 @@ import * as Sentry from '@sentry/browser'
|
||||
|
||||
const warned = {}
|
||||
const missingMessageErrors = {}
|
||||
const missingSubstitutionErrors = {}
|
||||
|
||||
/**
|
||||
* Returns a localized message for the given key
|
||||
@ -55,7 +56,11 @@ export const getMessage = (localeCode, localeMessages, key, substitutions) => {
|
||||
return part
|
||||
}
|
||||
const substituteIndex = Number(subMatch[1]) - 1
|
||||
if (substitutions[substituteIndex] == null) {
|
||||
if (substitutions[substituteIndex] == null && !missingSubstitutionErrors[localeCode]?.[key]) {
|
||||
if (!missingSubstitutionErrors[localeCode]) {
|
||||
missingSubstitutionErrors[localeCode] = {}
|
||||
}
|
||||
missingSubstitutionErrors[localeCode][key] = true
|
||||
const error = new Error(`Insufficient number of substitutions for message: '${phrase}'`)
|
||||
log.error(error)
|
||||
Sentry.captureException(error)
|
||||
|
@ -2,10 +2,15 @@
|
||||
|
||||
import ethUtil from 'ethereumjs-util'
|
||||
|
||||
const inDevelopment = process.env.NODE_ENV === 'development'
|
||||
const inDevelopment = process.env.METAMASK_DEBUG || process.env.IN_TEST
|
||||
|
||||
let projectId = process.env.METAMETRICS_PROJECT_ID
|
||||
if (!projectId) {
|
||||
projectId = inDevelopment ? 1 : 2
|
||||
}
|
||||
|
||||
const METAMETRICS_BASE_URL = 'https://chromeextensionmm.innocraft.cloud/piwik.php'
|
||||
const METAMETRICS_REQUIRED_PARAMS = `?idsite=${inDevelopment ? 1 : 2}&rec=1&apiv=1`
|
||||
const METAMETRICS_REQUIRED_PARAMS = `?idsite=${projectId}&rec=1&apiv=1`
|
||||
const METAMETRICS_BASE_FULL = METAMETRICS_BASE_URL + METAMETRICS_REQUIRED_PARAMS
|
||||
|
||||
const METAMETRICS_TRACKING_URL = inDevelopment
|
||||
@ -18,7 +23,7 @@ const METAMETRICS_CUSTOM_GAS_LIMIT_CHANGE = 'gasLimitChange'
|
||||
const METAMETRICS_CUSTOM_GAS_PRICE_CHANGE = 'gasPriceChange'
|
||||
const METAMETRICS_CUSTOM_FUNCTION_TYPE = 'functionType'
|
||||
const METAMETRICS_CUSTOM_RECIPIENT_KNOWN = 'recipientKnown'
|
||||
const METAMETRICS_CUSTOM_CONFIRM_SCREEN_ORIGIN = 'origin'
|
||||
const METAMETRICS_REQUEST_ORIGIN = 'origin'
|
||||
const METAMETRICS_CUSTOM_FROM_NETWORK = 'fromNetwork'
|
||||
const METAMETRICS_CUSTOM_TO_NETWORK = 'toNetwork'
|
||||
const METAMETRICS_CUSTOM_ERROR_FIELD = 'errorField'
|
||||
@ -31,7 +36,7 @@ const METAMETRICS_CUSTOM_ASSET_SELECTED = 'assetSelected'
|
||||
const customVariableNameIdMap = {
|
||||
[METAMETRICS_CUSTOM_FUNCTION_TYPE]: 1,
|
||||
[METAMETRICS_CUSTOM_RECIPIENT_KNOWN]: 2,
|
||||
[METAMETRICS_CUSTOM_CONFIRM_SCREEN_ORIGIN]: 3,
|
||||
[METAMETRICS_REQUEST_ORIGIN]: 3,
|
||||
[METAMETRICS_CUSTOM_GAS_LIMIT_CHANGE]: 4,
|
||||
[METAMETRICS_CUSTOM_GAS_PRICE_CHANGE]: 5,
|
||||
|
||||
@ -69,7 +74,7 @@ const customDimensionsNameIdMap = {
|
||||
|
||||
function composeUrlRefParamAddition (previousPath, confirmTransactionOrigin) {
|
||||
const externalOrigin = confirmTransactionOrigin && confirmTransactionOrigin !== 'metamask'
|
||||
return `&urlref=${externalOrigin ? 'EXTERNAL' : encodeURIComponent(previousPath.replace(/chrome-extension:\/\/\w+/, METAMETRICS_TRACKING_URL))}`
|
||||
return `&urlref=${externalOrigin ? 'EXTERNAL' : encodeURIComponent(`${METAMETRICS_TRACKING_URL}${previousPath}`)}`
|
||||
}
|
||||
|
||||
// composes query params of the form &dimension[0-999]=[value]
|
||||
@ -110,11 +115,10 @@ function composeParamAddition (paramValue, paramName) {
|
||||
* @property {string} config.accountType The account type being used at the time of the event: 'hardware', 'imported' or 'default'
|
||||
* @property {number} config.numberOfTokens The number of tokens that the user has added at the time of the event
|
||||
* @property {number} config.numberOfAccounts The number of accounts the user has added at the time of the event
|
||||
* @property {string} config.previousPath The location path the user was on prior to the path they are on at the time of the event
|
||||
* @property {string} config.currentPath The location path the user is on at the time of the event
|
||||
* @property {string} config.previousPath The pathname of the URL the user was on prior to the URL they are on at the time of the event
|
||||
* @property {string} config.currentPath The pathname of the URL the user is on at the time of the event
|
||||
* @property {string} config.metaMetricsId A random id assigned to a user at the time of opting in to metametrics. A hexadecimal number
|
||||
* @property {string} config.confirmTransactionOrigin The origin on a transaction
|
||||
* @property {string} config.url The url to track an event at. Overrides `currentPath`
|
||||
* @property {boolean} config.excludeMetaMetricsId Whether or not the tracked event data should be associated with a metametrics id
|
||||
* @property {boolean} config.isNewVisit Whether or not the event should be tracked as a new visit/user sessions
|
||||
* @returns {string} - Returns a url to be passed to fetch to make the appropriate request to matomo.
|
||||
@ -136,7 +140,6 @@ function composeUrl (config) {
|
||||
currentPath,
|
||||
metaMetricsId,
|
||||
confirmTransactionOrigin,
|
||||
url: configUrl,
|
||||
excludeMetaMetricsId,
|
||||
isNewVisit,
|
||||
} = config
|
||||
@ -162,10 +165,10 @@ function composeUrl (config) {
|
||||
numberOfTokens: (customVariables && customVariables.numberOfTokens) || numberOfTokens,
|
||||
numberOfAccounts: (customVariables && customVariables.numberOfAccounts) || numberOfAccounts,
|
||||
}) : ''
|
||||
const url = configUrl || currentPath ? `&url=${encodeURIComponent(currentPath.replace(/chrome-extension:\/\/\w+/, METAMETRICS_TRACKING_URL))}` : ''
|
||||
const url = currentPath ? `&url=${encodeURIComponent(`${METAMETRICS_TRACKING_URL}${currentPath}`)}` : ''
|
||||
const _id = metaMetricsId && !excludeMetaMetricsId ? `&_id=${metaMetricsId.slice(2, 18)}` : ''
|
||||
const rand = `&rand=${String(Math.random()).slice(2)}`
|
||||
const pv_id = ((url || currentPath) && `&pv_id=${ethUtil.bufferToHex(ethUtil.sha3(url || currentPath.match(/chrome-extension:\/\/\w+\/(.+)/)[0])).slice(2, 8)}`) || ''
|
||||
const pv_id = currentPath ? `&pv_id=${ethUtil.bufferToHex(ethUtil.sha3(currentPath)).slice(2, 8)}` : ''
|
||||
const uid = metaMetricsId && !excludeMetaMetricsId
|
||||
? `&uid=${metaMetricsId.slice(2, 18)}`
|
||||
: excludeMetaMetricsId
|
||||
|
28
ui/app/hooks/useCopyToClipboard.js
Normal file
28
ui/app/hooks/useCopyToClipboard.js
Normal file
@ -0,0 +1,28 @@
|
||||
import { useState, useCallback } from 'react'
|
||||
import copyToClipboard from 'copy-to-clipboard'
|
||||
import { useTimeout } from './useTimeout'
|
||||
|
||||
/**
|
||||
* useCopyToClipboard
|
||||
*
|
||||
* @param {number} [delay=3000] - delay in ms
|
||||
*
|
||||
* @return {[boolean, Function]}
|
||||
*/
|
||||
const DEFAULT_DELAY = 3000
|
||||
|
||||
export function useCopyToClipboard (delay = DEFAULT_DELAY) {
|
||||
const [copied, setCopied] = useState(false)
|
||||
const startTimeout = useTimeout(() => setCopied(false), delay, false)
|
||||
|
||||
const handleCopy = useCallback(
|
||||
(text) => {
|
||||
setCopied(true)
|
||||
startTimeout()
|
||||
copyToClipboard(text)
|
||||
},
|
||||
[startTimeout],
|
||||
)
|
||||
|
||||
return [copied, handleCopy]
|
||||
}
|
46
ui/app/hooks/useTimeout.js
Normal file
46
ui/app/hooks/useTimeout.js
Normal file
@ -0,0 +1,46 @@
|
||||
import { useState, useEffect, useRef, useCallback } from 'react'
|
||||
|
||||
/**
|
||||
* useTimeout
|
||||
*
|
||||
* @param {Function} cb - callback function inside setTimeout
|
||||
* @param {number} delay - delay in ms
|
||||
* @param {boolean} [immediate] - determines whether the timeout is invoked immediately
|
||||
*
|
||||
* @return {Function}
|
||||
*/
|
||||
export function useTimeout (cb, delay, immediate = true) {
|
||||
const saveCb = useRef()
|
||||
const [timeoutId, setTimeoutId] = useState(null)
|
||||
|
||||
useEffect(() => {
|
||||
saveCb.current = cb
|
||||
}, [cb])
|
||||
|
||||
useEffect(() => {
|
||||
if (timeoutId !== 'start') {
|
||||
return
|
||||
}
|
||||
|
||||
const id = setTimeout(() => {
|
||||
saveCb.current()
|
||||
}, delay)
|
||||
|
||||
setTimeoutId(id)
|
||||
|
||||
return () => {
|
||||
clearTimeout(timeoutId)
|
||||
}
|
||||
}, [delay, timeoutId])
|
||||
|
||||
const startTimeout = useCallback(() => {
|
||||
clearTimeout(timeoutId)
|
||||
setTimeoutId('start')
|
||||
}, [timeoutId])
|
||||
|
||||
if (immediate) {
|
||||
startTimeout()
|
||||
}
|
||||
|
||||
return startTimeout
|
||||
}
|
@ -19,6 +19,7 @@ export default class ImportWithSeedPhrase extends PureComponent {
|
||||
onSubmit: PropTypes.func.isRequired,
|
||||
setSeedPhraseBackedUp: PropTypes.func,
|
||||
initializeThreeBox: PropTypes.func,
|
||||
completeOnboarding: PropTypes.func,
|
||||
}
|
||||
|
||||
state = {
|
||||
@ -119,7 +120,7 @@ export default class ImportWithSeedPhrase extends PureComponent {
|
||||
}
|
||||
|
||||
const { password, seedPhrase } = this.state
|
||||
const { history, onSubmit, setSeedPhraseBackedUp, initializeThreeBox } = this.props
|
||||
const { history, onSubmit, setSeedPhraseBackedUp, initializeThreeBox, completeOnboarding } = this.props
|
||||
|
||||
try {
|
||||
await onSubmit(password, this.parseSeedPhrase(seedPhrase))
|
||||
@ -131,7 +132,8 @@ export default class ImportWithSeedPhrase extends PureComponent {
|
||||
},
|
||||
})
|
||||
|
||||
setSeedPhraseBackedUp(true).then(() => {
|
||||
setSeedPhraseBackedUp(true).then(async () => {
|
||||
await completeOnboarding()
|
||||
initializeThreeBox()
|
||||
history.push(INITIALIZE_END_OF_FLOW_ROUTE)
|
||||
})
|
||||
|
@ -3,12 +3,14 @@ import ImportWithSeedPhrase from './import-with-seed-phrase.component'
|
||||
import {
|
||||
setSeedPhraseBackedUp,
|
||||
initializeThreeBox,
|
||||
setCompletedOnboarding,
|
||||
} from '../../../../store/actions'
|
||||
|
||||
const mapDispatchToProps = (dispatch) => {
|
||||
return {
|
||||
setSeedPhraseBackedUp: (seedPhraseBackupState) => dispatch(setSeedPhraseBackedUp(seedPhraseBackupState)),
|
||||
initializeThreeBox: () => dispatch(initializeThreeBox()),
|
||||
completeOnboarding: () => dispatch(setCompletedOnboarding()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,6 @@ export default class EndOfFlowScreen extends PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
history: PropTypes.object,
|
||||
completeOnboarding: PropTypes.func,
|
||||
completionMetaMetricsName: PropTypes.string,
|
||||
onboardingInitiator: PropTypes.exact({
|
||||
location: PropTypes.string,
|
||||
@ -23,9 +22,8 @@ export default class EndOfFlowScreen extends PureComponent {
|
||||
}
|
||||
|
||||
onComplete = async () => {
|
||||
const { history, completeOnboarding, completionMetaMetricsName, onboardingInitiator } = this.props
|
||||
const { history, completionMetaMetricsName, onboardingInitiator } = this.props
|
||||
|
||||
await completeOnboarding()
|
||||
this.context.metricsEvent({
|
||||
eventOpts: {
|
||||
category: 'Onboarding',
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { connect } from 'react-redux'
|
||||
import EndOfFlow from './end-of-flow.component'
|
||||
import { setCompletedOnboarding } from '../../../store/actions'
|
||||
import { getOnboardingInitiator } from '../../../selectors'
|
||||
|
||||
const firstTimeFlowTypeNameMap = {
|
||||
@ -17,10 +16,4 @@ const mapStateToProps = (state) => {
|
||||
}
|
||||
}
|
||||
|
||||
const mapDispatchToProps = (dispatch) => {
|
||||
return {
|
||||
completeOnboarding: () => dispatch(setCompletedOnboarding()),
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(EndOfFlow)
|
||||
export default connect(mapStateToProps)(EndOfFlow)
|
||||
|
@ -12,7 +12,6 @@ describe('End of Flow Screen', function () {
|
||||
history: {
|
||||
push: sinon.spy(),
|
||||
},
|
||||
completeOnboarding: sinon.spy(),
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
@ -30,7 +29,6 @@ describe('End of Flow Screen', function () {
|
||||
endOfFlowButton.simulate('click')
|
||||
|
||||
setImmediate(() => {
|
||||
assert(props.completeOnboarding.calledOnce)
|
||||
assert(props.history.push.calledOnceWithExactly(DEFAULT_ROUTE))
|
||||
done()
|
||||
})
|
||||
|
@ -26,6 +26,7 @@ export default class ConfirmSeedPhrase extends PureComponent {
|
||||
seedPhrase: PropTypes.string,
|
||||
initializeThreeBox: PropTypes.func,
|
||||
setSeedPhraseBackedUp: PropTypes.func,
|
||||
completeOnboarding: PropTypes.func,
|
||||
}
|
||||
|
||||
state = {
|
||||
@ -66,6 +67,10 @@ export default class ConfirmSeedPhrase extends PureComponent {
|
||||
exportAsFile('', this.props.seedPhrase, 'text/plain')
|
||||
}
|
||||
|
||||
setOnboardingCompleted = async () => {
|
||||
await this.props.completeOnboarding()
|
||||
}
|
||||
|
||||
handleSubmit = async () => {
|
||||
const {
|
||||
history,
|
||||
@ -86,8 +91,9 @@ export default class ConfirmSeedPhrase extends PureComponent {
|
||||
},
|
||||
})
|
||||
|
||||
setSeedPhraseBackedUp(true).then(() => {
|
||||
setSeedPhraseBackedUp(true).then(async () => {
|
||||
initializeThreeBox()
|
||||
this.setOnboardingCompleted()
|
||||
history.push(INITIALIZE_END_OF_FLOW_ROUTE)
|
||||
})
|
||||
} catch (error) {
|
||||
|
@ -3,12 +3,14 @@ import ConfirmSeedPhrase from './confirm-seed-phrase.component'
|
||||
import {
|
||||
setSeedPhraseBackedUp,
|
||||
initializeThreeBox,
|
||||
setCompletedOnboarding,
|
||||
} from '../../../../store/actions'
|
||||
|
||||
const mapDispatchToProps = (dispatch) => {
|
||||
return {
|
||||
setSeedPhraseBackedUp: (seedPhraseBackupState) => dispatch(setSeedPhraseBackedUp(seedPhraseBackupState)),
|
||||
initializeThreeBox: () => dispatch(initializeThreeBox()),
|
||||
completeOnboarding: () => dispatch(setCompletedOnboarding()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -142,6 +142,7 @@ describe('ConfirmSeedPhrase Component', function () {
|
||||
history: { push: pushSpy },
|
||||
setSeedPhraseBackedUp: () => Promise.resolve(),
|
||||
initializeThreeBox: initialize3BoxSpy,
|
||||
completeOnboarding: sinon.spy(),
|
||||
},
|
||||
{
|
||||
metricsEvent: metricsEventSpy,
|
||||
|
@ -98,11 +98,8 @@ export default class Routes extends Component {
|
||||
this.props.history.listen((locationObj, action) => {
|
||||
if (action === 'PUSH') {
|
||||
pageChanged(locationObj.pathname)
|
||||
const url = `&url=${encodeURIComponent('http://www.metamask.io/metametrics' + locationObj.pathname)}`
|
||||
this.context.metricsEvent({}, {
|
||||
currentPath: '',
|
||||
pathname: locationObj.pathname,
|
||||
url,
|
||||
currentPath: locationObj.pathname,
|
||||
pageOpts: {
|
||||
hideDimensions: true,
|
||||
},
|
||||
|
@ -106,7 +106,8 @@
|
||||
height: 20px;
|
||||
padding: 0;
|
||||
background: none;
|
||||
padding-left: 10px;
|
||||
padding-left: 0;
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,85 +1,102 @@
|
||||
import React, { PureComponent } from 'react'
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Redirect } from 'react-router-dom'
|
||||
|
||||
import Identicon from '../../../../components/ui/identicon'
|
||||
import Copy from '../../../../components/ui/icon/copy-icon.component'
|
||||
import Button from '../../../../components/ui/button/button.component'
|
||||
import copyToClipboard from 'copy-to-clipboard'
|
||||
|
||||
import Tooltip from '../../../../components/ui/tooltip-v2'
|
||||
import { useI18nContext } from '../../../../hooks/useI18nContext'
|
||||
import { useCopyToClipboard } from '../../../../hooks/useCopyToClipboard'
|
||||
|
||||
function quadSplit (address) {
|
||||
return '0x ' + address.slice(2).match(/.{1,4}/g).join(' ')
|
||||
return (
|
||||
'0x ' +
|
||||
address
|
||||
.slice(2)
|
||||
.match(/.{1,4}/g)
|
||||
.join(' ')
|
||||
)
|
||||
}
|
||||
|
||||
export default class ViewContact extends PureComponent {
|
||||
function ViewContact ({
|
||||
history,
|
||||
name,
|
||||
address,
|
||||
checkSummedAddress,
|
||||
memo,
|
||||
editRoute,
|
||||
listRoute,
|
||||
}) {
|
||||
const t = useI18nContext()
|
||||
const [copied, handleCopy] = useCopyToClipboard()
|
||||
|
||||
static contextTypes = {
|
||||
t: PropTypes.func,
|
||||
if (!address) {
|
||||
return <Redirect to={{ pathname: listRoute }} />
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
name: PropTypes.string,
|
||||
address: PropTypes.string,
|
||||
history: PropTypes.object,
|
||||
checkSummedAddress: PropTypes.string,
|
||||
memo: PropTypes.string,
|
||||
editRoute: PropTypes.string,
|
||||
listRoute: PropTypes.string.isRequired,
|
||||
}
|
||||
|
||||
render () {
|
||||
const { t } = this.context
|
||||
const { history, name, address, checkSummedAddress, memo, editRoute, listRoute } = this.props
|
||||
|
||||
if (!address) {
|
||||
return <Redirect to={{ pathname: listRoute }} />
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="settings-page__content-row">
|
||||
<div className="settings-page__content-item">
|
||||
<div className="settings-page__header address-book__header">
|
||||
<Identicon address={address} diameter={60} />
|
||||
<div className="address-book__header__name">{ name }</div>
|
||||
return (
|
||||
<div className="settings-page__content-row">
|
||||
<div className="settings-page__content-item">
|
||||
<div className="settings-page__header address-book__header">
|
||||
<Identicon address={address} diameter={60} />
|
||||
<div className="address-book__header__name">{name}</div>
|
||||
</div>
|
||||
<div className="address-book__view-contact__group">
|
||||
<Button
|
||||
type="secondary"
|
||||
onClick={() => {
|
||||
history.push(`${editRoute}/${address}`)
|
||||
}}
|
||||
>
|
||||
{t('edit')}
|
||||
</Button>
|
||||
</div>
|
||||
<div className="address-book__view-contact__group">
|
||||
<div className="address-book__view-contact__group__label">
|
||||
{t('ethereumPublicAddress')}
|
||||
</div>
|
||||
<div className="address-book__view-contact__group">
|
||||
<Button
|
||||
type="secondary"
|
||||
onClick={() => {
|
||||
history.push(`${editRoute}/${address}`)
|
||||
}}
|
||||
>
|
||||
{t('edit')}
|
||||
</Button>
|
||||
</div>
|
||||
<div className="address-book__view-contact__group">
|
||||
<div className="address-book__view-contact__group__label">
|
||||
{ t('ethereumPublicAddress') }
|
||||
<div className="address-book__view-contact__group__value">
|
||||
<div className="address-book__view-contact__group__static-address">
|
||||
{quadSplit(checkSummedAddress)}
|
||||
</div>
|
||||
<div className="address-book__view-contact__group__value">
|
||||
<div
|
||||
className="address-book__view-contact__group__static-address"
|
||||
>
|
||||
{ quadSplit(checkSummedAddress) }
|
||||
</div>
|
||||
<Tooltip
|
||||
position="bottom"
|
||||
title={copied ? t('copiedExclamation') : t('copyToClipboard')}
|
||||
>
|
||||
<button
|
||||
className="address-book__view-contact__group__static-address--copy-icon"
|
||||
onClick={() => copyToClipboard(checkSummedAddress)}
|
||||
onClick={() => {
|
||||
handleCopy(checkSummedAddress)
|
||||
}}
|
||||
>
|
||||
<Copy size={20} color="#3098DC" />
|
||||
</button>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div className="address-book__view-contact__group">
|
||||
<div className="address-book__view-contact__group__label--capitalized">
|
||||
{ t('memo') }
|
||||
</div>
|
||||
<div className="address-book__view-contact__group__static-address">
|
||||
{ memo }
|
||||
</div>
|
||||
</div>
|
||||
<div className="address-book__view-contact__group">
|
||||
<div className="address-book__view-contact__group__label--capitalized">
|
||||
{t('memo')}
|
||||
</div>
|
||||
<div className="address-book__view-contact__group__static-address">
|
||||
{memo}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
ViewContact.propTypes = {
|
||||
name: PropTypes.string,
|
||||
address: PropTypes.string,
|
||||
history: PropTypes.object,
|
||||
checkSummedAddress: PropTypes.string,
|
||||
memo: PropTypes.string,
|
||||
editRoute: PropTypes.string,
|
||||
listRoute: PropTypes.string.isRequired,
|
||||
}
|
||||
|
||||
export default React.memo(ViewContact)
|
||||
|
@ -76,12 +76,12 @@ export default class InfoTab extends PureComponent {
|
||||
</div>
|
||||
<div className="info-tab__link-item">
|
||||
<a
|
||||
href="mailto:help@metamask.io?subject=Feedback"
|
||||
href="https://metamask.zendesk.com/hc/en-us/requests/new"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<span className="info-tab__link-text">
|
||||
{ t('emailUs') }
|
||||
{ t('contactUs') }
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
|
@ -168,7 +168,7 @@ export function createNewVault (password) {
|
||||
|
||||
export function verifyPassword (password) {
|
||||
return new Promise((resolve, reject) => {
|
||||
background.submitPassword(password, (error) => {
|
||||
background.verifyPassword(password, (error) => {
|
||||
if (error) {
|
||||
return reject(error)
|
||||
}
|
||||
@ -193,7 +193,7 @@ export function verifySeedPhrase () {
|
||||
export function requestRevealSeedWords (password) {
|
||||
return async (dispatch) => {
|
||||
dispatch(showLoadingIndication())
|
||||
log.debug(`background.submitPassword`)
|
||||
log.debug(`background.verifyPassword`)
|
||||
|
||||
try {
|
||||
await verifyPassword(password)
|
||||
|
Loading…
Reference in New Issue
Block a user