mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Merge branch 'develop' into develop
This commit is contained in:
commit
3b46478024
@ -36,6 +36,10 @@ workflows:
|
||||
requires:
|
||||
- prep-deps-npm
|
||||
- prep-build
|
||||
- test-e2e-beta-drizzle:
|
||||
requires:
|
||||
- prep-deps-npm
|
||||
- prep-build
|
||||
- test-unit:
|
||||
requires:
|
||||
- prep-deps-npm
|
||||
@ -68,6 +72,7 @@ workflows:
|
||||
- test-e2e-firefox
|
||||
- test-e2e-beta-chrome
|
||||
- test-e2e-beta-firefox
|
||||
- test-e2e-beta-drizzle
|
||||
- test-integration-mascara-chrome
|
||||
- test-integration-mascara-firefox
|
||||
- test-integration-flat-chrome
|
||||
@ -222,6 +227,19 @@ jobs:
|
||||
path: test-artifacts
|
||||
destination: test-artifacts
|
||||
|
||||
test-e2e-beta-drizzle:
|
||||
docker:
|
||||
- image: circleci/node:8.11.3-browsers
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: .
|
||||
- run:
|
||||
name: test:e2e:drizzle:beta
|
||||
command: npm run test:e2e:drizzle:beta
|
||||
- store_artifacts:
|
||||
path: test-artifacts
|
||||
destination: test-artifacts
|
||||
test-e2e-beta-chrome:
|
||||
docker:
|
||||
- image: circleci/node:8.11.3-browsers
|
||||
|
@ -20,3 +20,4 @@ test/integration/bundle.js
|
||||
test/integration/jquery-3.1.0.min.js
|
||||
test/integration/helpers.js
|
||||
test/integration/lib/first-time.js
|
||||
|
||||
|
27
CHANGELOG.md
27
CHANGELOG.md
@ -2,9 +2,34 @@
|
||||
|
||||
## Current Develop Branch
|
||||
|
||||
- [#5283](https://github.com/MetaMask/metamask-extension/pull/5283): Fix bug when eth.getCode() called with no contract
|
||||
|
||||
## 4.16.0 Wednesday October 17 2018
|
||||
|
||||
- Feature: Add toggle for primary currency (eth/fiat)
|
||||
- Feature: add tooltip for view etherscan tx
|
||||
- Feature: add Polish translations
|
||||
- Feature: improve Korean translations
|
||||
- Feature: improve Italian translations
|
||||
- Bug Fix: Fix bug with "pending" block reference
|
||||
- Bug Fix: Force AccountTracker to update balances on network change
|
||||
- Bug Fix: Fix document extension check when injecting web3
|
||||
- Bug Fix: Fix some support links
|
||||
|
||||
## 4.15.0 Thursday October 11 2018
|
||||
|
||||
- A rollback release, equivalent to `v4.11.1` to be deployed in the case that `v4.14.0` is found to have bugs.
|
||||
|
||||
## 4.14.0 Thursday October 11 2018
|
||||
|
||||
- Update transaction statuses when switching networks.
|
||||
- [#5470](https://github.com/MetaMask/metamask-extension/pull/5470) 100% coverage in French locale, fixed the procedure to verify proposed locale.
|
||||
- [#5283](https://github.com/MetaMask/metamask-extension/pull/5283): Fix bug when eth.getCode() called with no contract
|
||||
- Added rudimentary support for the subscription API to support web3 1.0 and Truffle's Drizzle.
|
||||
- [#5502](https://github.com/MetaMask/metamask-extension/pull/5502) Update Italian translation.
|
||||
|
||||
## 4.13.0
|
||||
|
||||
- A rollback release, equivalent to `v4.11.1` to be deployed in the case that `v4.12.0` is found to have bugs.
|
||||
|
||||
## 4.12.0 Thursday September 27 2018
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
# MetaMask Browser Extension
|
||||
[](https://circleci.com/gh/MetaMask/metamask-extension) [](https://coveralls.io/github/MetaMask/metamask-extension?branch=master) [](https://greenkeeper.io/) [](https://waffle.io/MetaMask/metamask-extension)
|
||||
[](https://circleci.com/gh/MetaMask/metamask-extension) [](https://coveralls.io/github/MetaMask/metamask-extension?branch=master) [](https://waffle.io/MetaMask/metamask-extension)
|
||||
|
||||
## Support
|
||||
|
||||
|
@ -372,7 +372,7 @@
|
||||
"message": "Import účtu"
|
||||
},
|
||||
"importAccountMsg": {
|
||||
"message":"Importované účty nebudou spojeny s vaší původní MetaMaskovou klíčovou frází. Zjistěte více o importovaných účtech "
|
||||
"message": "Importované účty nebudou spojeny s vaší původní MetaMaskovou klíčovou frází. Zjistěte více o importovaných účtech "
|
||||
},
|
||||
"importAnAccount": {
|
||||
"message": "Import účtu"
|
||||
@ -730,7 +730,7 @@
|
||||
"message": "Nastavení"
|
||||
},
|
||||
"info": {
|
||||
"message": "Informace"
|
||||
"message": "Informace"
|
||||
},
|
||||
"shapeshiftBuy": {
|
||||
"message": "Nakoupit na ShapeShift"
|
||||
|
@ -68,13 +68,13 @@
|
||||
"message": "Muss größer oder gleich $1 und kleiner oder gleich $2 sein.",
|
||||
"description": "Helfer für die Eingabe von hex als dezimal"
|
||||
},
|
||||
"blockiesIdenticon": {
|
||||
"blockiesIdenticon": {
|
||||
"message": "Blockies Identicon verwenden"
|
||||
},
|
||||
"borrowDharma": {
|
||||
"message": "Mit Dharma ausleihen (Beta)"
|
||||
},
|
||||
"builtInCalifornia": {
|
||||
"builtInCalifornia": {
|
||||
"message": "MetaMask wurde in Kalifornien entwickelt und gebaut."
|
||||
},
|
||||
"buy": {
|
||||
@ -86,13 +86,13 @@
|
||||
"buyCoinbaseExplainer": {
|
||||
"message": "Coinbase ist die weltweit bekannteste Art und Weise um Bitcoin, Ethereum und Litecoin zu kaufen und verkaufen."
|
||||
},
|
||||
"ok": {
|
||||
"ok": {
|
||||
"message": "Ok"
|
||||
},
|
||||
"cancel": {
|
||||
"message": "Abbrechen"
|
||||
},
|
||||
"classicInterface": {
|
||||
"classicInterface": {
|
||||
"message": "Klassische Oberfläche verwenden"
|
||||
},
|
||||
"clickCopy": {
|
||||
@ -101,7 +101,7 @@
|
||||
"confirm": {
|
||||
"message": "Bestätigen"
|
||||
},
|
||||
"confirmed": {
|
||||
"confirmed": {
|
||||
"message": "Bestätigt"
|
||||
},
|
||||
"confirmContract": {
|
||||
@ -369,7 +369,7 @@
|
||||
"message": "Account importieren"
|
||||
},
|
||||
"importAccountMsg": {
|
||||
"message":" Importierte Accounts werden nicht mit der Seed-Wörterfolge deines ursprünglichen MetaMask Accounts verknüpft. Erfahre mehr über importierte Accounts."
|
||||
"message": " Importierte Accounts werden nicht mit der Seed-Wörterfolge deines ursprünglichen MetaMask Accounts verknüpft. Erfahre mehr über importierte Accounts."
|
||||
},
|
||||
"importAnAccount": {
|
||||
"message": "Einen Account importieren"
|
||||
@ -709,7 +709,7 @@
|
||||
"message": "Einstellungen"
|
||||
},
|
||||
"info": {
|
||||
"message": "Info"
|
||||
"message": "Info"
|
||||
},
|
||||
"shapeshiftBuy": {
|
||||
"message": "Mit Shapeshift kaufen"
|
||||
|
@ -14,6 +14,9 @@
|
||||
"accountName": {
|
||||
"message": "Account Name"
|
||||
},
|
||||
"accountOptions": {
|
||||
"message": "Account Options"
|
||||
},
|
||||
"accountSelectionRequired": {
|
||||
"message": "You need to select an account!"
|
||||
},
|
||||
@ -137,10 +140,13 @@
|
||||
"clickCopy": {
|
||||
"message": "Click to Copy"
|
||||
},
|
||||
"clickToAdd": {
|
||||
"message": "Click on $1 to add them to your account"
|
||||
},
|
||||
"close": {
|
||||
"message": "Close"
|
||||
},
|
||||
"chromeRequiredForHardwareWallets":{
|
||||
"chromeRequiredForHardwareWallets": {
|
||||
"message": "You need to use MetaMask on Google Chrome in order to connect to your Hardware Wallet."
|
||||
},
|
||||
"confirm": {
|
||||
@ -358,12 +364,18 @@
|
||||
"enterPasswordContinue": {
|
||||
"message": "Enter password to continue"
|
||||
},
|
||||
"eth": {
|
||||
"message": "ETH"
|
||||
},
|
||||
"etherscanView": {
|
||||
"message": "View account on Etherscan"
|
||||
},
|
||||
"exchangeRate": {
|
||||
"message": "Exchange Rate"
|
||||
},
|
||||
"expandView": {
|
||||
"message": "Expand View"
|
||||
},
|
||||
"exportPrivateKey": {
|
||||
"message": "Export Private Key"
|
||||
},
|
||||
@ -374,7 +386,7 @@
|
||||
"message": "Failed"
|
||||
},
|
||||
"fiat": {
|
||||
"message": "FIAT",
|
||||
"message": "Fiat",
|
||||
"description": "Exchange type"
|
||||
},
|
||||
"fileImportFail": {
|
||||
@ -418,6 +430,9 @@
|
||||
"gasLimitTooLow": {
|
||||
"message": "Gas limit must be at least 21000"
|
||||
},
|
||||
"gasUsed": {
|
||||
"message": "Gas Used"
|
||||
},
|
||||
"generatingSeed": {
|
||||
"message": "Generating Seed..."
|
||||
},
|
||||
@ -498,7 +513,7 @@
|
||||
"message": "Import Account"
|
||||
},
|
||||
"importAccountMsg": {
|
||||
"message":" Imported accounts will not be associated with your originally created MetaMask account seedphrase. Learn more about imported accounts "
|
||||
"message": " Imported accounts will not be associated with your originally created MetaMask account seedphrase. Learn more about imported accounts "
|
||||
},
|
||||
"importAnAccount": {
|
||||
"message": "Import an account"
|
||||
@ -629,6 +644,9 @@
|
||||
"min": {
|
||||
"message": "Minimum"
|
||||
},
|
||||
"missingYourTokens": {
|
||||
"message": "Don't see your tokens?"
|
||||
},
|
||||
"myAccounts": {
|
||||
"message": "My Accounts"
|
||||
},
|
||||
@ -686,7 +704,7 @@
|
||||
"noDeposits": {
|
||||
"message": "No deposits received"
|
||||
},
|
||||
"noConversionRateAvailable":{
|
||||
"noConversionRateAvailable": {
|
||||
"message": "No Conversion Rate Available"
|
||||
},
|
||||
"noTransactionHistory": {
|
||||
@ -739,22 +757,22 @@
|
||||
"parameters": {
|
||||
"message": "Parameters"
|
||||
},
|
||||
"passwordNotLongEnough": {
|
||||
"message": "Password not long enough"
|
||||
},
|
||||
"passwordsDontMatch": {
|
||||
"message": "Passwords Don't Match"
|
||||
},
|
||||
"password": {
|
||||
"message": "Password"
|
||||
},
|
||||
"passwordCorrect": {
|
||||
"message": "Please make sure your password is correct."
|
||||
},
|
||||
"passwordsDontMatch": {
|
||||
"message": "Passwords Don't Match"
|
||||
},
|
||||
"passwordMismatch": {
|
||||
"message": "passwords don't match",
|
||||
"description": "in password creation process, the two new password fields did not match"
|
||||
},
|
||||
"passwordNotLongEnough": {
|
||||
"message": "Password not long enough"
|
||||
},
|
||||
"passwordShort": {
|
||||
"message": "password not long enough",
|
||||
"description": "in password creation process, the password is not long enough to be secure"
|
||||
@ -781,6 +799,12 @@
|
||||
"prev": {
|
||||
"message": "Prev"
|
||||
},
|
||||
"primaryCurrencySetting": {
|
||||
"message": "Primary Currency"
|
||||
},
|
||||
"primaryCurrencySettingDescription": {
|
||||
"message": "Select ETH to prioritize displaying values in ETH. Select Fiat to prioritize displaying values in your selected currency."
|
||||
},
|
||||
"privacyMsg": {
|
||||
"message": "Privacy Policy"
|
||||
},
|
||||
@ -789,7 +813,7 @@
|
||||
"description": "select this type of file to use to import an account"
|
||||
},
|
||||
"privateKeyWarning": {
|
||||
"message": "Warning: Never disclose this key. Anyone with your private keys can take steal any assets held in your account."
|
||||
"message": "Warning: Never disclose this key. Anyone with your private keys can steal any assets held in your account."
|
||||
},
|
||||
"privateNetwork": {
|
||||
"message": "Private Network"
|
||||
@ -857,9 +881,6 @@
|
||||
"retryWithMoreGas": {
|
||||
"message": "Retry with a higher gas price here"
|
||||
},
|
||||
"walletSeed": {
|
||||
"message": "Wallet Seed"
|
||||
},
|
||||
"restore": {
|
||||
"message": "Restore"
|
||||
},
|
||||
@ -1186,7 +1207,7 @@
|
||||
"message": "These 12 words are the only way to restore your MetaMask accounts.\nSave them somewhere safe and secret."
|
||||
},
|
||||
"typePassword": {
|
||||
"message": "Type Your Password"
|
||||
"message": "Type your MetaMask password"
|
||||
},
|
||||
"uiWelcome": {
|
||||
"message": "Welcome to the New UI (Beta)"
|
||||
@ -1222,7 +1243,7 @@
|
||||
"message": "Ooops! Something went wrong...."
|
||||
},
|
||||
"unknownCameraError": {
|
||||
"message": "There was an error while trying to access you camera. Please try again..."
|
||||
"message": "There was an error while trying to access your camera. Please try again..."
|
||||
},
|
||||
"unlock": {
|
||||
"message": "Unlock"
|
||||
@ -1261,6 +1282,9 @@
|
||||
"visitWebSite": {
|
||||
"message": "Visit our web site"
|
||||
},
|
||||
"walletSeed": {
|
||||
"message": "Wallet Seed"
|
||||
},
|
||||
"warning": {
|
||||
"message": "Warning"
|
||||
},
|
||||
|
@ -156,7 +156,7 @@
|
||||
"message": " Copiar "
|
||||
},
|
||||
"copyPrivateKey": {
|
||||
"message": "Ésta es tu llave privada (haz click para copiar)"
|
||||
"message": "Ésta es tu clave privada (haz click para copiar)"
|
||||
},
|
||||
"copyToClipboard": {
|
||||
"message": "Copiar al portapapeles"
|
||||
@ -278,10 +278,10 @@
|
||||
"message": "Tipo de cambio"
|
||||
},
|
||||
"exportPrivateKey": {
|
||||
"message": "Exportar llave privada"
|
||||
"message": "Exportar clave privada"
|
||||
},
|
||||
"exportPrivateKeyWarning": {
|
||||
"message": "Exportar llaves privadas bajo TU PROPIO riesgo"
|
||||
"message": "Exportar claves privadas bajo TU PROPIO riesgo"
|
||||
},
|
||||
"failed": {
|
||||
"message": "Fallo"
|
||||
@ -579,8 +579,8 @@
|
||||
"description": "En el proceso de creación de contraseña, esta no es lo suficientemente larga para ser segura"
|
||||
},
|
||||
"pastePrivateKey": {
|
||||
"message": "Pega tu llave privada aqui",
|
||||
"description": "Para importar una cuenta desde una llave privada"
|
||||
"message": "Pega tu clave privada aqui",
|
||||
"description": "Para importar una cuenta desde una clave privada"
|
||||
},
|
||||
"pasteSeed": {
|
||||
"message": "¡Pega tu frase semilla aquí!"
|
||||
@ -595,7 +595,7 @@
|
||||
"message": "Política de privacidad"
|
||||
},
|
||||
"privateKey": {
|
||||
"message": "Llave privada",
|
||||
"message": "Clave privada",
|
||||
"description": "Selecciona este tupo de archivo para importar una cuenta"
|
||||
},
|
||||
"privateKeyWarning": {
|
||||
@ -712,7 +712,7 @@
|
||||
"message": "Comprar con ShapeShift"
|
||||
},
|
||||
"showPrivateKeys": {
|
||||
"message": "Mostrar llaves privadas"
|
||||
"message": "Mostrar claves privadas"
|
||||
},
|
||||
"showQRCode": {
|
||||
"message": "Mostrar codigo QR"
|
||||
|
@ -140,7 +140,7 @@
|
||||
"close": {
|
||||
"message": "Fermer"
|
||||
},
|
||||
"chromeRequiredForHardwareWallets":{
|
||||
"chromeRequiredForHardwareWallets": {
|
||||
"message": "Pour connecter votre portefeuille hardware, vous devez utiliser MetaMask pour Google Chrome."
|
||||
},
|
||||
"confirm": {
|
||||
@ -171,7 +171,7 @@
|
||||
"message": "Connection au réseau de test Kovan"
|
||||
},
|
||||
"connectingToMainnet": {
|
||||
"message": "Connection au Réseau principal Ethereum"
|
||||
"message": "Connection au réseau principal Ethereum"
|
||||
},
|
||||
"connectingToRopsten": {
|
||||
"message": "Connection au réseau de test Ropsten"
|
||||
@ -350,13 +350,13 @@
|
||||
"message": "Nom ENS inconnu"
|
||||
},
|
||||
"enterPassword": {
|
||||
"message": "Entrer le mot de passe"
|
||||
"message": "Entrez votre mot de passe"
|
||||
},
|
||||
"enterPasswordConfirm": {
|
||||
"message": "Enter your password to confirm"
|
||||
"message": "Entrez votre mot de passe pour confirmer"
|
||||
},
|
||||
"enterPasswordContinue": {
|
||||
"message": "Enter password to continue"
|
||||
"message": "Entrez votre mot de passe pour continuer"
|
||||
},
|
||||
"etherscanView": {
|
||||
"message": "Afficher le compte sur Etherscan"
|
||||
@ -498,7 +498,7 @@
|
||||
"message": "Importer un compte"
|
||||
},
|
||||
"importAccountMsg": {
|
||||
"message":" Les comptes importés ne seront pas associés avec votre phrase Seed que vous avez créé au départ dans MetaMask. Obtenir plus d'information sur les comptes importés"
|
||||
"message": " Les comptes importés ne seront pas associés avec votre phrase Seed que vous avez créé au départ dans MetaMask. Obtenir plus d'information sur les comptes importés"
|
||||
},
|
||||
"importAnAccount": {
|
||||
"message": "Importer un compte"
|
||||
@ -686,7 +686,7 @@
|
||||
"noDeposits": {
|
||||
"message": "Aucun dépôt reçu"
|
||||
},
|
||||
"noConversionRateAvailable":{
|
||||
"noConversionRateAvailable": {
|
||||
"message": "Aucun taux de conversion disponible"
|
||||
},
|
||||
"noTransactionHistory": {
|
||||
|
@ -11,6 +11,7 @@
|
||||
{ "code": "ko", "name": "Korean" },
|
||||
{ "code": "nl", "name": "Dutch" },
|
||||
{ "code": "ph", "name": "Tagalog" },
|
||||
{ "code": "pl", "name": "Polish" },
|
||||
{ "code": "pt", "name": "Portuguese" },
|
||||
{ "code": "ru", "name": "Russian" },
|
||||
{ "code": "sl", "name": "Slovenian" },
|
||||
|
@ -2,6 +2,9 @@
|
||||
"accept": {
|
||||
"message": "Accetta"
|
||||
},
|
||||
"accessingYourCamera": {
|
||||
"message": "Accesso alla fotocamera..."
|
||||
},
|
||||
"account": {
|
||||
"message": "Account"
|
||||
},
|
||||
@ -11,6 +14,15 @@
|
||||
"accountName": {
|
||||
"message": "Nome Account"
|
||||
},
|
||||
"accountOptions": {
|
||||
"message": "Account Options"
|
||||
},
|
||||
"accountSelectionRequired": {
|
||||
"message": "Devi selezionare un account!"
|
||||
},
|
||||
"activityLog": {
|
||||
"message": "log attività"
|
||||
},
|
||||
"address": {
|
||||
"message": "Indirizzo"
|
||||
},
|
||||
@ -23,6 +35,12 @@
|
||||
"addTokens": {
|
||||
"message": "Aggiungi più token"
|
||||
},
|
||||
"addSuggestedTokens": {
|
||||
"message": "Aggiungi Token Suggeriti"
|
||||
},
|
||||
"addAcquiredTokens": {
|
||||
"message": "Aggiungi i token che hai acquistato usando MetaMask"
|
||||
},
|
||||
"amount": {
|
||||
"message": "Importo"
|
||||
},
|
||||
@ -37,9 +55,21 @@
|
||||
"message": "MetaMask",
|
||||
"description": "Il nome dell'applicazione"
|
||||
},
|
||||
"approve": {
|
||||
"message": "Approva"
|
||||
},
|
||||
"approved": {
|
||||
"message": "Approvato"
|
||||
},
|
||||
"attemptingConnect": {
|
||||
"message": "Tentativo di connessione alla blockchain."
|
||||
},
|
||||
"attemptToCancel": {
|
||||
"message": "Tentativo di Annullamento?"
|
||||
},
|
||||
"attemptToCancelDescription": {
|
||||
"message": "Tentare di annullare non garantisce che la transazione verrà annullata. Se annullata, è comunque richiesto pagare alla rete la commissione sulla transazione."
|
||||
},
|
||||
"attributions": {
|
||||
"message": "Attribuzioni"
|
||||
},
|
||||
@ -71,8 +101,11 @@
|
||||
"borrowDharma": {
|
||||
"message": "Prendi in presisito con Dharma (Beta)"
|
||||
},
|
||||
"browserNotSupported": {
|
||||
"message": "Il tuo Browser non è supportato..."
|
||||
},
|
||||
"builtInCalifornia": {
|
||||
"message": "MetaMask è progettato e costruito in California."
|
||||
"message": "MetaMask è progettato e realizzato in California."
|
||||
},
|
||||
"buy": {
|
||||
"message": "Compra"
|
||||
@ -83,8 +116,23 @@
|
||||
"buyCoinbaseExplainer": {
|
||||
"message": "Coinbase è il servizio più popolare al mondo per comprare e vendere Bitcoin, Ethereum e Litecoin."
|
||||
},
|
||||
"bytes": {
|
||||
"message": "Bytes"
|
||||
},
|
||||
"ok": {
|
||||
"message": "Ok"
|
||||
},
|
||||
"cancel": {
|
||||
"message": "Cancella"
|
||||
"message": "Annulla"
|
||||
},
|
||||
"cancelAttempt": {
|
||||
"message": "Tentativo di Annullamento"
|
||||
},
|
||||
"cancellationGasFee": {
|
||||
"message": "Commissione di Annullamento in Gas"
|
||||
},
|
||||
"cancelN": {
|
||||
"message": "Cancel all $1 transactions"
|
||||
},
|
||||
"classicInterface": {
|
||||
"message": "Usa l'interfaccia classica"
|
||||
@ -92,9 +140,18 @@
|
||||
"clickCopy": {
|
||||
"message": "Clicca per Copiare"
|
||||
},
|
||||
"close": {
|
||||
"message": "Chiudi"
|
||||
},
|
||||
"chromeRequiredForHardwareWallets": {
|
||||
"message": "Devi usare MetaMask con Google Chrome per connettere il tuo Portafoglio Hardware"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Conferma"
|
||||
},
|
||||
"confirmed": {
|
||||
"message": "Confermata"
|
||||
},
|
||||
"confirmContract": {
|
||||
"message": "Conferma Contratto"
|
||||
},
|
||||
@ -104,6 +161,36 @@
|
||||
"confirmTransaction": {
|
||||
"message": "Conferma Transazione"
|
||||
},
|
||||
"connectHardwareWallet": {
|
||||
"message": "Connetti Portafoglio Hardware"
|
||||
},
|
||||
"connect": {
|
||||
"message": "Connetti"
|
||||
},
|
||||
"connecting": {
|
||||
"message": "Connessione..."
|
||||
},
|
||||
"connectingToKovan": {
|
||||
"message": "Connessione alla Rete di test Kovan"
|
||||
},
|
||||
"connectingToMainnet": {
|
||||
"message": "Connessione alla Rete Ethereum Principale"
|
||||
},
|
||||
"connectingToRopsten": {
|
||||
"message": "Connessione alla Rete di test Ropsten"
|
||||
},
|
||||
"connectingToRinkeby": {
|
||||
"message": "Connessione alla Rete di test Rinkeby"
|
||||
},
|
||||
"connectingToUnknown": {
|
||||
"message": "Connessione ad una Rete Sconosciuta"
|
||||
},
|
||||
"connectToLedger": {
|
||||
"message": "Connettersi al Ledger"
|
||||
},
|
||||
"connectToTrezor": {
|
||||
"message": "Connettersi al Trezor"
|
||||
},
|
||||
"continue": {
|
||||
"message": "Continua"
|
||||
},
|
||||
@ -126,11 +213,14 @@
|
||||
"message": "Copiato!"
|
||||
},
|
||||
"copiedSafe": {
|
||||
"message": "Le ho copiate in un posto sicuro"
|
||||
"message": "Le ho copiate in un posto sicuro"
|
||||
},
|
||||
"copy": {
|
||||
"message": "Copia"
|
||||
},
|
||||
"copyAddress": {
|
||||
"message": "Copia l'indirizzo"
|
||||
},
|
||||
"copyToClipboard": {
|
||||
"message": "Copia negli appunti"
|
||||
},
|
||||
@ -156,12 +246,21 @@
|
||||
"currentConversion": {
|
||||
"message": "Cambio Corrente"
|
||||
},
|
||||
"currentLanguage": {
|
||||
"message": "Lingua Corrente"
|
||||
},
|
||||
"currentNetwork": {
|
||||
"message": "Rete Corrente"
|
||||
},
|
||||
"currentRpc": {
|
||||
"message": "RPC Corrente"
|
||||
},
|
||||
"customGas": {
|
||||
"message": "Personalizza Gas"
|
||||
},
|
||||
"customToken": {
|
||||
"message": "Token Personalizzato"
|
||||
},
|
||||
"customize": {
|
||||
"message": "Personalizza"
|
||||
},
|
||||
@ -169,11 +268,11 @@
|
||||
"message": "RPC Personalizzata"
|
||||
},
|
||||
"decimalsMustZerotoTen": {
|
||||
"message": "Il numero di decimali deve essere almeno 0, e non oltre 36."
|
||||
"message": "Il numero di decimali deve essere almeno 0, e non oltre 36."
|
||||
},
|
||||
"decimal": {
|
||||
"message": "Precisione Decimali"
|
||||
},
|
||||
},
|
||||
"defaultNetwork": {
|
||||
"message": "La rete predefinita per transazioni in Ether è la Rete Ethereum Principale."
|
||||
},
|
||||
@ -218,38 +317,59 @@
|
||||
"message": "Deposita Direttamente Ether"
|
||||
},
|
||||
"directDepositEtherExplainer": {
|
||||
"message": "Se possiedi già degli Ether, questa è la via più veloce per aggiungere Ether al tuo portafoglio con un deposito diretto."
|
||||
"message": "Se possiedi già degli Ether, questa è la via più veloce per aggiungere Ether al tuo portafoglio con un deposito diretto."
|
||||
},
|
||||
"done": {
|
||||
"message": "Finito"
|
||||
},
|
||||
"downloadGoogleChrome": {
|
||||
"message": "Scarica Google Chrome"
|
||||
},
|
||||
"downloadStateLogs": {
|
||||
"message": "Scarica i log di Stato"
|
||||
},
|
||||
"dontHaveAHardwareWallet": {
|
||||
"message": "Non hai un portafoglio hardware?"
|
||||
},
|
||||
"dropped": {
|
||||
"message": "Abbandonata"
|
||||
},
|
||||
"edit": {
|
||||
"message": "Modifica"
|
||||
},
|
||||
"editAccountName": {
|
||||
"message": "Modifica Nome Account"
|
||||
},
|
||||
"editingTransaction": {
|
||||
"message": "Modifica la transazione"
|
||||
},
|
||||
"emailUs": {
|
||||
"message": "Mandaci una mail!"
|
||||
},
|
||||
"encryptNewDen": {
|
||||
"message": "Cripta il tuo nuovo DEN"
|
||||
},
|
||||
"ensNameNotFound": {
|
||||
"message": "Nome ENS non trovato"
|
||||
},
|
||||
"enterPassword": {
|
||||
"message": "Inserisci password"
|
||||
},
|
||||
"enterPasswordConfirm": {
|
||||
"message": "Inserisci la tua password per confermare"
|
||||
},
|
||||
"enterPasswordContinue": {
|
||||
"message": "Inserisci la tua password per continuare"
|
||||
},
|
||||
"etherscanView": {
|
||||
"message": "Vedi account su Etherscan"
|
||||
},
|
||||
"exchangeRate": {
|
||||
"message": "Tasso di cambio"
|
||||
},
|
||||
"expandView": {
|
||||
"message": "Expand View"
|
||||
},
|
||||
"exportPrivateKey": {
|
||||
"message": "Esporta Chiave Privata"
|
||||
},
|
||||
@ -257,7 +377,7 @@
|
||||
"message": "Esporta chiave privata a tuo rischio."
|
||||
},
|
||||
"failed": {
|
||||
"message": "Fallito"
|
||||
"message": "Fallita"
|
||||
},
|
||||
"fiat": {
|
||||
"message": "FIAT",
|
||||
@ -270,6 +390,9 @@
|
||||
"followTwitter": {
|
||||
"message": "Seguici su Twitter"
|
||||
},
|
||||
"forgetDevice": {
|
||||
"message": "Dimentica questo dispositivo"
|
||||
},
|
||||
"from": {
|
||||
"message": "Da"
|
||||
},
|
||||
@ -279,6 +402,9 @@
|
||||
"fromShapeShift": {
|
||||
"message": "Da ShapeShift"
|
||||
},
|
||||
"functionType": {
|
||||
"message": "Tipo della Funzione"
|
||||
},
|
||||
"gas": {
|
||||
"message": "Gas",
|
||||
"description": "Piccola indicazione del costo del gas"
|
||||
@ -310,6 +436,9 @@
|
||||
"gasPriceRequired": {
|
||||
"message": "Prezzo Gas Richiesto"
|
||||
},
|
||||
"generatingTransaction": {
|
||||
"message": "Generando la transazione"
|
||||
},
|
||||
"getEther": {
|
||||
"message": "Ottieni Ether"
|
||||
},
|
||||
@ -317,10 +446,28 @@
|
||||
"message": "Ottieni Get Ether da un faucet per $1",
|
||||
"description": "Visualizza il nome della rete per il faucet Ether"
|
||||
},
|
||||
"getHelp": {
|
||||
"message": "Aiuto."
|
||||
},
|
||||
"greaterThanMin": {
|
||||
"message": "deve essere maggiore o uguale a $1.",
|
||||
"description": "aiuto per inserire un input esadecimale come decimale"
|
||||
},
|
||||
"hardware": {
|
||||
"message": "hardware"
|
||||
},
|
||||
"hardwareWalletConnected": {
|
||||
"message": "Portafoglio hardware connesso"
|
||||
},
|
||||
"hardwareWallets": {
|
||||
"message": "Connetti portafoglio hardware"
|
||||
},
|
||||
"hardwareWalletsMsg": {
|
||||
"message": "Selezione un portafoglio hardware che vuoi utilizzare con MetaMask"
|
||||
},
|
||||
"havingTroubleConnecting": {
|
||||
"message": "Problemi di connessione?"
|
||||
},
|
||||
"here": {
|
||||
"message": "qui",
|
||||
"description": "per intendere -clicca qui- per maggiori informazioni (va con troubleTokenBalances)"
|
||||
@ -328,6 +475,9 @@
|
||||
"hereList": {
|
||||
"message": "Questa è una lista!!!!"
|
||||
},
|
||||
"hexData": {
|
||||
"message": "Dati Hex"
|
||||
},
|
||||
"hide": {
|
||||
"message": "Nascondi"
|
||||
},
|
||||
@ -337,11 +487,14 @@
|
||||
"hideTokenPrompt": {
|
||||
"message": "Nascondi Token?"
|
||||
},
|
||||
"history": {
|
||||
"message": "Storico"
|
||||
},
|
||||
"howToDeposit": {
|
||||
"message": "Come vuoi depositare Ether?"
|
||||
},
|
||||
"holdEther": {
|
||||
"message": "Ti permette di tenere ether & token, e serve da ponte per le applicazioni decentralizzate."
|
||||
"message": "Ti permette di tenere ether & token, e serve da ponte per le applicazioni decentralizzate."
|
||||
},
|
||||
"import": {
|
||||
"message": "Importa",
|
||||
@ -351,7 +504,7 @@
|
||||
"message": "Importa Account"
|
||||
},
|
||||
"importAccountMsg": {
|
||||
"message":" Gli account importati non saranno associati alla frase seed originariamente creata con MetaMask. Impara di più sugli account importati "
|
||||
"message": " Gli account importati non saranno associati alla frase seed originariamente creata con MetaMask. Impara di più sugli account importati "
|
||||
},
|
||||
"importAnAccount": {
|
||||
"message": "Importa un account"
|
||||
@ -363,9 +516,18 @@
|
||||
"message": "Importato",
|
||||
"description": "stato che conferma che un account è stato totalmente caricato nel portachiavi"
|
||||
},
|
||||
"importUsingSeed": {
|
||||
"message": "Importa account con frase seed"
|
||||
},
|
||||
"info": {
|
||||
"message": "Informazioni"
|
||||
},
|
||||
"infoHelp": {
|
||||
"message": "Informazioni & Aiuto"
|
||||
},
|
||||
"initialTransactionConfirmed": {
|
||||
"message": "La transazione iniziale è stata confermata dalla rete. Clicca OK per tornare indietro."
|
||||
},
|
||||
"insufficientFunds": {
|
||||
"message": "Fondi non sufficienti."
|
||||
},
|
||||
@ -390,19 +552,34 @@
|
||||
"invalidRPC": {
|
||||
"message": "URI RPC invalido"
|
||||
},
|
||||
"invalidSeedPhrase": {
|
||||
"message": "Frase seed non valida"
|
||||
},
|
||||
"jsonFail": {
|
||||
"message": "Qualcosa è andato storto. Assicurati che il file JSON sia formattato correttamente."
|
||||
"message": "Qualcosa è andato storto. Assicurati che il file JSON sia formattato correttamente."
|
||||
},
|
||||
"jsonFile": {
|
||||
"message": "File JSON",
|
||||
"description": "formato per importare un account"
|
||||
},
|
||||
"keepTrackTokens": {
|
||||
"message": "Tieni traccia dei tokens che hai acquistato con il tuo account MetaMask."
|
||||
},
|
||||
"kovan": {
|
||||
"message": "Rete di test Kovan"
|
||||
},
|
||||
"knowledgeDataBase": {
|
||||
"message": "Visita la nostra Knowledge Base"
|
||||
},
|
||||
"max": {
|
||||
"message": "Massimo"
|
||||
},
|
||||
"learnMore": {
|
||||
"message": "Scopri di più"
|
||||
},
|
||||
"ledgerAccountRestriction": {
|
||||
"message": "E' necessario utilizzare l'ultimo account prima di poterne aggiungere uno nuovo."
|
||||
},
|
||||
"lessThanMax": {
|
||||
"message": "deve essere minore o uguale a $1.",
|
||||
"description": "aiuto per inserire un input esadecimale come decimale"
|
||||
@ -410,6 +587,9 @@
|
||||
"likeToAddTokens": {
|
||||
"message": "Vorresti aggiungere questi token?"
|
||||
},
|
||||
"links": {
|
||||
"message": "Collegamenti"
|
||||
},
|
||||
"limit": {
|
||||
"message": "Limite"
|
||||
},
|
||||
@ -437,17 +617,26 @@
|
||||
"mainnet": {
|
||||
"message": "Rete Ethereum Principale"
|
||||
},
|
||||
"menu": {
|
||||
"message": "Menu"
|
||||
},
|
||||
"message": {
|
||||
"message": "Messaggio"
|
||||
},
|
||||
"metamaskDescription": {
|
||||
"message": "MetaMask è una cassaforte sicura per identità su Ethereum."
|
||||
},
|
||||
"metamaskSeedWords": {
|
||||
"message": "Parole Seed di MetaMask"
|
||||
},
|
||||
"metamaskVersion": {
|
||||
"message": "versione di MetaMask"
|
||||
},
|
||||
"min": {
|
||||
"message": "Minimo"
|
||||
},
|
||||
"myAccounts": {
|
||||
"message": "Account Miei"
|
||||
"message": "Miei Account"
|
||||
},
|
||||
"mustSelectOne": {
|
||||
"message": "Devi selezionare almeno un token."
|
||||
@ -469,6 +658,9 @@
|
||||
"networks": {
|
||||
"message": "Reti"
|
||||
},
|
||||
"nevermind": {
|
||||
"message": "Non importa"
|
||||
},
|
||||
"newAccount": {
|
||||
"message": "Nuovo Account"
|
||||
},
|
||||
@ -482,6 +674,9 @@
|
||||
"newPassword": {
|
||||
"message": "Nuova Password (minimo 8 caratteri)"
|
||||
},
|
||||
"newPassword8Chars": {
|
||||
"message": "Nuova Password (minimo 8 caratteri)"
|
||||
},
|
||||
"newRecipient": {
|
||||
"message": "Nuovo Destinatario"
|
||||
},
|
||||
@ -489,7 +684,7 @@
|
||||
"message": "Nuovo URL RPC"
|
||||
},
|
||||
"next": {
|
||||
"message": "Prossimo"
|
||||
"message": "Avanti"
|
||||
},
|
||||
"noAddressForName": {
|
||||
"message": "Nessun indirizzo è stato impostato per questo nome."
|
||||
@ -497,32 +692,75 @@
|
||||
"noDeposits": {
|
||||
"message": "Nessun deposito ricevuto"
|
||||
},
|
||||
"noConversionRateAvailable": {
|
||||
"message": "Tasso di Conversione non Disponibile"
|
||||
},
|
||||
"noTransactionHistory": {
|
||||
"message": "Nessuna cronologia delle transazioni."
|
||||
},
|
||||
"noTransactions": {
|
||||
"message": "Nessuna Transazione"
|
||||
},
|
||||
"notFound": {
|
||||
"message": "Non Trovata"
|
||||
},
|
||||
"notStarted": {
|
||||
"message": "Non Iniziato"
|
||||
},
|
||||
"noWebcamFoundTitle": {
|
||||
"message": "Webcam non trovata"
|
||||
},
|
||||
"noWebcamFound": {
|
||||
"message": "La webcam del tuo computer non è stata trovata. Per favore riprovaci."
|
||||
},
|
||||
"oldUI": {
|
||||
"message": "Vecchia interfaccia"
|
||||
},
|
||||
"oldUIMessage": {
|
||||
"message": "Sei ritornato alla vecchia interfaccia. Puoi ritornare alla nuova interfaccia tramite l'opzione nel menu a discesa in alto a destra."
|
||||
},
|
||||
"onlySendToEtherAddress": {
|
||||
"message": "Invia ETH solamente ad un account Ethereum."
|
||||
},
|
||||
"onlySendTokensToAccountAddress": {
|
||||
"message": "Invia solamente $1 ad un account Ethereum.",
|
||||
"description": "mostra il simbolo del token"
|
||||
},
|
||||
"openInTab": {
|
||||
"message": "Apri in una scheda"
|
||||
},
|
||||
"or": {
|
||||
"message": "o",
|
||||
"description": "scelta tra creare o importare un nuovo account"
|
||||
},
|
||||
"orderOneHere": {
|
||||
"message": "Compra un Trezor o un Ledger e tieni i tuoi soldi al sicuro"
|
||||
},
|
||||
"origin": {
|
||||
"message": "Origine"
|
||||
},
|
||||
"outgoing": {
|
||||
"message": "In Uscita"
|
||||
},
|
||||
"parameters": {
|
||||
"message": "Parametri"
|
||||
},
|
||||
"password": {
|
||||
"message": "Password"
|
||||
},
|
||||
"passwordCorrect": {
|
||||
"message": "Assicurati che la password sia corretta."
|
||||
},
|
||||
"passwordsDontMatch": {
|
||||
"message": "Le Password Non Corrispondonos"
|
||||
},
|
||||
"passwordMismatch": {
|
||||
"message": "le password non corrispondono",
|
||||
"description": "nella creazione della password, le due password all'interno dei campi non corrispondono"
|
||||
},
|
||||
"passwordNotLongEnough": {
|
||||
"message": "Password non abbastanza lunga"
|
||||
},
|
||||
"passwordShort": {
|
||||
"message": "password non sufficientemente lunga",
|
||||
"description": "nella creazione della password, la password non è lunga abbastanza"
|
||||
@ -534,12 +772,21 @@
|
||||
"pasteSeed": {
|
||||
"message": "Incolla la tua frase seed qui!"
|
||||
},
|
||||
"pending": {
|
||||
"message": "in corso"
|
||||
},
|
||||
"personalAddressDetected": {
|
||||
"message": "Rilevato indirizzo personale. Inserisci l'indirizzo del contratto del token."
|
||||
},
|
||||
"pleaseReviewTransaction": {
|
||||
"message": "Ricontrolla la tua transazione."
|
||||
},
|
||||
"popularTokens": {
|
||||
"message": "Tokens Popolari"
|
||||
},
|
||||
"prev": {
|
||||
"message": "Precedente"
|
||||
},
|
||||
"privacyMsg": {
|
||||
"message": "Politica sulla Privacy"
|
||||
},
|
||||
@ -556,8 +803,11 @@
|
||||
"qrCode": {
|
||||
"message": "Mostra Codice QR"
|
||||
},
|
||||
"queue": {
|
||||
"message": "Coda"
|
||||
},
|
||||
"readdToken": {
|
||||
"message": "Puoi aggiungere nuovamente questo token in futuro andando in “Aggiungi token” nel menu delle opzioni del tuo account."
|
||||
"message": "Puoi aggiungere nuovamente questo token in futuro andando in “Aggiungi token” nel menu delle opzioni del tuo account."
|
||||
},
|
||||
"readMore": {
|
||||
"message": "Leggi di più qui."
|
||||
@ -574,36 +824,87 @@
|
||||
"refundAddress": {
|
||||
"message": "Indirizzo di Rimborso"
|
||||
},
|
||||
"reject": {
|
||||
"message": "Reject"
|
||||
},
|
||||
"rejectAll": {
|
||||
"message": "Reject All"
|
||||
},
|
||||
"rejectTxsN": {
|
||||
"message": "Reject $1 transactions"
|
||||
},
|
||||
"rejectTxsDescription": {
|
||||
"message": "You are about to batch reject $1 transactions."
|
||||
},
|
||||
"rejected": {
|
||||
"message": "Respinta"
|
||||
},
|
||||
"reset": {
|
||||
"message": "Reset"
|
||||
},
|
||||
"resetAccount": {
|
||||
"message": "Resetta Account"
|
||||
},
|
||||
"resetAccountDescription": {
|
||||
"message": "Resettare il tuo account cancellerà lo storico delle transazioni."
|
||||
},
|
||||
"restoreFromSeed": {
|
||||
"message": "Ripristina da una frase seed"
|
||||
},
|
||||
"restoreVault": {
|
||||
"message": "Ripristina Cassaforte"
|
||||
},
|
||||
"restoreAccountWithSeed": {
|
||||
"message": "Ripristina Account con la Frase Seed"
|
||||
},
|
||||
"required": {
|
||||
"message": "Richiesto"
|
||||
},
|
||||
"retryWithMoreGas": {
|
||||
"message": "Riprova con un prezzo del Gas maggiore qui"
|
||||
},
|
||||
"restore": {
|
||||
"message": "Ripristina"
|
||||
},
|
||||
"revealSeedWords": {
|
||||
"message": "Rivela Frase Seed"
|
||||
},
|
||||
"revealSeedWordsTitle": {
|
||||
"message": "Frase Seed"
|
||||
},
|
||||
"revealSeedWordsDescription": {
|
||||
"message": "Se cambierai browser o computer, ti servirà questa frase seed per accedere ai tuoi account. Salvala in un posto sicuro e segreto."
|
||||
},
|
||||
"revealSeedWordsWarningTitle": {
|
||||
"message": "NON CONDIVIDERE questa frase con nessuno!"
|
||||
},
|
||||
"revealSeedWordsWarning": {
|
||||
"message": "Non ripristinare la tua frase seed in pubblico!. Queste parole possono essere usate per rubare il tuo account."
|
||||
},
|
||||
"revert": {
|
||||
"message": "Annulla"
|
||||
},
|
||||
"remove": {
|
||||
"message": "rimuovi"
|
||||
},
|
||||
"removeAccount": {
|
||||
"message": "Rimuovi account"
|
||||
},
|
||||
"removeAccountDescription": {
|
||||
"message": "Questo account sarà rimosso dal tuo portafoglio. Per favore assicurati che hai la frase seed originale o la chiave privata per questo account importato prima di continuare. Puoi nuovamente importare o creare un account dal menù a tendina. "
|
||||
},
|
||||
"readyToConnect": {
|
||||
"message": "Pronto a Connetterti?"
|
||||
},
|
||||
"rinkeby": {
|
||||
"message": "Rete di test Rinkeby"
|
||||
},
|
||||
"ropsten": {
|
||||
"message": "Rete di test Ropsten"
|
||||
},
|
||||
"rpc": {
|
||||
"message": "RPC Personalizzata"
|
||||
},
|
||||
"sampleAccountName": {
|
||||
"message": "Es: Il mio nuovo account",
|
||||
"description": "Aiuta l'utente a capire il concetto di aggiungere un nome leggibile al loro account"
|
||||
@ -611,6 +912,9 @@
|
||||
"save": {
|
||||
"message": "Salva"
|
||||
},
|
||||
"saveAsCsvFile": {
|
||||
"message": "Salva Come File CSV"
|
||||
},
|
||||
"saveAsFile": {
|
||||
"message": "Salva come File",
|
||||
"description": "Processo per esportare un account"
|
||||
@ -618,9 +922,18 @@
|
||||
"saveSeedAsFile": {
|
||||
"message": "Salva la Frase Seed come File"
|
||||
},
|
||||
"scanInstructions": {
|
||||
"message": "Posizione il codice QR davanti alla fotocamera"
|
||||
},
|
||||
"scanQrCode": {
|
||||
"message": "Scansiona Codice QR"
|
||||
},
|
||||
"search": {
|
||||
"message": "Cerca"
|
||||
},
|
||||
"searchResults": {
|
||||
"message": "Risultati Ricerca"
|
||||
},
|
||||
"secretPhrase": {
|
||||
"message": "Inserisci la tua frase segreta di dodici parole per ripristinare la cassaforte."
|
||||
},
|
||||
@ -633,6 +946,9 @@
|
||||
"selectCurrency": {
|
||||
"message": "Seleziona Moneta"
|
||||
},
|
||||
"selectLocale": {
|
||||
"message": "Selezione Lingua"
|
||||
},
|
||||
"selectService": {
|
||||
"message": "Seleziona Servizio"
|
||||
},
|
||||
@ -648,6 +964,33 @@
|
||||
"sendTokens": {
|
||||
"message": "Invia Tokens"
|
||||
},
|
||||
"sentEther": {
|
||||
"message": "ether inviati"
|
||||
},
|
||||
"sentTokens": {
|
||||
"message": "tokens inviati"
|
||||
},
|
||||
"separateEachWord": {
|
||||
"message": "Separa ogni parola con un solo spazio"
|
||||
},
|
||||
"searchTokens": {
|
||||
"message": "Cerca Tokens"
|
||||
},
|
||||
"selectAnAddress": {
|
||||
"message": "Seleziona un Indirizzo"
|
||||
},
|
||||
"selectAnAccount": {
|
||||
"message": "Seleziona un Account"
|
||||
},
|
||||
"selectAnAccountHelp": {
|
||||
"message": "Selezione l'account da visualizzare in MetaMask"
|
||||
},
|
||||
"selectHdPath": {
|
||||
"message": "Seleziona Percorso HD"
|
||||
},
|
||||
"selectPathHelp": {
|
||||
"message": "Se non vedi il tuo account Ledger esistente di seguito, prova a cambiare il percorso in \"Legacy (MEW / MyCrypto)\""
|
||||
},
|
||||
"sendTokensAnywhere": {
|
||||
"message": "Invia Tokens a chiunque abbia un account Ethereum"
|
||||
},
|
||||
@ -663,14 +1006,26 @@
|
||||
"showQRCode": {
|
||||
"message": "Mostra Codie QR"
|
||||
},
|
||||
"showHexData": {
|
||||
"message": "Mostra Dati Hex"
|
||||
},
|
||||
"showHexDataDescription": {
|
||||
"message": "Seleziona per mostrare il campo dei dati hex nella schermata di invio"
|
||||
},
|
||||
"sign": {
|
||||
"message": "Firma"
|
||||
},
|
||||
"signatureRequest": {
|
||||
"message": "Firma Richiesta"
|
||||
},
|
||||
"signed": {
|
||||
"message": "Firmata"
|
||||
},
|
||||
"signMessage": {
|
||||
"message": "Firma Messaggio"
|
||||
},
|
||||
"signNotice": {
|
||||
"message": "Firmare questo messaggio può avere effetti collaterali pericolosi. \nFirma messaggi da siti di cui ti fidi totalmente. \nQuesto metodo pericoloso sarà rimosso in versioni future."
|
||||
"message": "Firmare questo messaggio può avere effetti collaterali pericolosi. \nFirma messaggi da siti di cui ti fidi totalmente. \nQuesto metodo pericoloso sarà rimosso in versioni future."
|
||||
},
|
||||
"sigRequest": {
|
||||
"message": "Firma Richiesta"
|
||||
@ -681,6 +1036,15 @@
|
||||
"spaceBetween": {
|
||||
"message": "ci può essere solo uno spazio tra le parole"
|
||||
},
|
||||
"speedUp": {
|
||||
"message": "velocizza"
|
||||
},
|
||||
"speedUpTitle": {
|
||||
"message": "Velocizza Transazione"
|
||||
},
|
||||
"speedUpSubtitle": {
|
||||
"message": "Aumenta il prezzo del gas per tentare di sovrascrivere e velocizzare la transazione"
|
||||
},
|
||||
"status": {
|
||||
"message": "Stato"
|
||||
},
|
||||
@ -690,9 +1054,33 @@
|
||||
"stateLogsDescription": {
|
||||
"message": "I log di stato contengono i tuoi indirizzi pubblici e le transazioni effettuate."
|
||||
},
|
||||
"stateLogError": {
|
||||
"message": "Errore nel recupero dei log di stato."
|
||||
},
|
||||
"step1HardwareWallet": {
|
||||
"message": "1. Connetti Portafoglio Hardware"
|
||||
},
|
||||
"step1HardwareWalletMsg": {
|
||||
"message": "Connetti il tuo portafoglio hardware al tuo computer."
|
||||
},
|
||||
"step2HardwareWallet": {
|
||||
"message": "2. Seleziona un Account"
|
||||
},
|
||||
"step2HardwareWalletMsg": {
|
||||
"message": "Selezione l'account che vuoi vedere. Puoi selezionarne solo uno alla volta."
|
||||
},
|
||||
"step3HardwareWallet": {
|
||||
"message": "3. Inizia a usare dApps e molto altro ancora!"
|
||||
},
|
||||
"step3HardwareWalletMsg": {
|
||||
"message": "Usa il tuo account hardware come utilizzeresti qualsiasi account Ethereum. Accedi alle dApps, invia Eth, compra e conserva token ERC20 e token non fungibili come CryptoKitties"
|
||||
},
|
||||
"submit": {
|
||||
"message": "Invia"
|
||||
},
|
||||
"submitted": {
|
||||
"message": "Inviata"
|
||||
},
|
||||
"supportCenter": {
|
||||
"message": "Visita il nostro Centro di Supporto"
|
||||
},
|
||||
@ -715,6 +1103,9 @@
|
||||
"message": "$1 a ETH via ShapeShift",
|
||||
"description": "il sistema riempirà il tipo di deposito all'inizio del messaggio"
|
||||
},
|
||||
"token": {
|
||||
"message": "Token"
|
||||
},
|
||||
"tokenAddress": {
|
||||
"message": "Indirizzo Token"
|
||||
},
|
||||
@ -736,23 +1127,62 @@
|
||||
"total": {
|
||||
"message": "Totale"
|
||||
},
|
||||
"transaction": {
|
||||
"message": "transazione"
|
||||
},
|
||||
"transactionConfirmed": {
|
||||
"message": "Transazione confermata il $2."
|
||||
},
|
||||
"transactionCreated": {
|
||||
"message": "Transazione di valore $1 creata il $2."
|
||||
},
|
||||
"transactionWithNonce": {
|
||||
"message": "Transazione $1"
|
||||
},
|
||||
"transactionDropped": {
|
||||
"message": "Transazione abbandonata il $2."
|
||||
},
|
||||
"transactionSubmitted": {
|
||||
"message": "Transazione inviata il $2."
|
||||
},
|
||||
"transactionUpdated": {
|
||||
"message": "Transazione aggiornata il $2."
|
||||
},
|
||||
"transactionUpdatedGas": {
|
||||
"message": "Transazione aggiornata con un prezzo del gas di $1 il $2."
|
||||
},
|
||||
"transactions": {
|
||||
"message": "transazioni"
|
||||
},
|
||||
"transactionError": {
|
||||
"message": "Errore Transazione. Eccceziona generata nel codice del contratto."
|
||||
},
|
||||
"transactionMemo": {
|
||||
"message": "Promemoria Transazione (opzionale)"
|
||||
},
|
||||
"transactionNumber": {
|
||||
"message": "Numero Transazione"
|
||||
},
|
||||
"transfer": {
|
||||
"message": "Trasferisci"
|
||||
},
|
||||
"transferFrom": {
|
||||
"message": "Transfer From"
|
||||
},
|
||||
"transfers": {
|
||||
"message": "Trasferimenti"
|
||||
},
|
||||
"trezorHardwareWallet": {
|
||||
"message": "TREZOR Portafoglio Hardware"
|
||||
},
|
||||
"troubleTokenBalances": {
|
||||
"message": "Abbiamo avuto un problema a caricare il bilancio dei tuoi token. Puoi vederlo ",
|
||||
"description": "Seguito da un link (qui) per vedere il bilancio dei token"
|
||||
},
|
||||
"twelveWords": {
|
||||
"tryAgain": {
|
||||
"message": "Prova di nuovo"
|
||||
},
|
||||
"twelveWords": {
|
||||
"message": "Queste 12 parole sono l'unico modo per ripristinare i tuoi account MetaMask. \nSalvale in un posto sicuro e segreto."
|
||||
},
|
||||
"typePassword": {
|
||||
@ -762,20 +1192,47 @@
|
||||
"message": "Benvenuto alla nuova interfaccia (Beta)"
|
||||
},
|
||||
"uiWelcomeMessage": {
|
||||
"message": "Stai utilizzanto la nuova interfaccia di MetaMask. Guarda in giro, prova nuove funzionalità come inviare token, e facci sapere se hai dei problemi."
|
||||
"message": "Stai utilizzanto la nuova interfaccia di MetaMask. Guarda in giro, prova nuove funzionalità come inviare token, e facci sapere se hai dei problemi."
|
||||
},
|
||||
"unapproved": {
|
||||
"message": "Non approvata"
|
||||
},
|
||||
"unavailable": {
|
||||
"message": "Non Disponibile"
|
||||
},
|
||||
"units": {
|
||||
"message": "unità"
|
||||
},
|
||||
"unknown": {
|
||||
"message": "Sconosciuto"
|
||||
},
|
||||
"unknownFunction": {
|
||||
"message": "Funzione Sconosciuta"
|
||||
},
|
||||
"unknownNetwork": {
|
||||
"message": "Rete Privata Sconosciuta"
|
||||
},
|
||||
"unknownNetworkId": {
|
||||
"message": "ID rete sconosciuto"
|
||||
},
|
||||
"unknownQrCode": {
|
||||
"message": "Errore: Non siamo riusciti a identificare il codice QR"
|
||||
},
|
||||
"unknownCameraErrorTitle": {
|
||||
"message": "Ooops! Qualcosa è andato storto...."
|
||||
},
|
||||
"unknownCameraError": {
|
||||
"message": "C'è stato un errore nel tentativo di accedere alla fotocamera. Per favore prova di nuovo..."
|
||||
},
|
||||
"unlock": {
|
||||
"message": "Sblocca"
|
||||
},
|
||||
"unlockMessage": {
|
||||
"message": "Il web decentralizzato ti attende"
|
||||
},
|
||||
"updatedWithDate": {
|
||||
"message": "Aggiornata $1"
|
||||
},
|
||||
"uriErrorMsg": {
|
||||
"message": "Gli URI richiedono un prefisso HTTP/HTTPS."
|
||||
},
|
||||
@ -798,22 +1255,40 @@
|
||||
"viewAccount": {
|
||||
"message": "Vedi Account"
|
||||
},
|
||||
"viewOnEtherscan": {
|
||||
"message": "Vedi su Etherscan"
|
||||
},
|
||||
"visitWebSite": {
|
||||
"message": "Visita il nostro sito web"
|
||||
},
|
||||
},
|
||||
"walletSeed": {
|
||||
"message": "Seed del Portafoglio"
|
||||
},
|
||||
"warning": {
|
||||
"message": "Attenzione"
|
||||
},
|
||||
"welcomeBack": {
|
||||
"message": "Bentornato!"
|
||||
},
|
||||
"welcomeBeta": {
|
||||
"message": "Benvenuto nella Beta di MetaMask"
|
||||
},
|
||||
"whatsThis": {
|
||||
"message": "Cos'è questo?"
|
||||
},
|
||||
"yesLetsTry": {
|
||||
"message": "Si, proviamo"
|
||||
},
|
||||
"youNeedToAllowCameraAccess": {
|
||||
"message": "Devi consentire l'accesso alla fotocamera per usare questa funzionalità."
|
||||
},
|
||||
"yourSigRequested": {
|
||||
"message": "E' richiesta la tua firma"
|
||||
},
|
||||
"youSign": {
|
||||
"message": "Ti stai connettendo"
|
||||
},
|
||||
"yourPrivateSeedPhrase": {
|
||||
"message": "La tua frase seed privata"
|
||||
}
|
||||
}
|
||||
|
@ -315,7 +315,7 @@
|
||||
"message": "アカウントのインポート"
|
||||
},
|
||||
"importAccountMsg": {
|
||||
"message":"追加したアカウントはMetaMaskのアカウントパスフレーズとは関連付けられません。インポートしたアカウントについての詳細は"
|
||||
"message": "追加したアカウントはMetaMaskのアカウントパスフレーズとは関連付けられません。インポートしたアカウントについての詳細は"
|
||||
},
|
||||
"importAnAccount": {
|
||||
"message": "アカウントをインポート"
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -351,7 +351,7 @@
|
||||
"message": "Account importeren"
|
||||
},
|
||||
"importAccountMsg": {
|
||||
"message":" Geïmporteerde accounts worden niet gekoppeld aan de seedphrase van uw oorspronkelijk gemaakte MetaMask-account. Meer informatie over geïmporteerde accounts"
|
||||
"message": " Geïmporteerde accounts worden niet gekoppeld aan de seedphrase van uw oorspronkelijk gemaakte MetaMask-account. Meer informatie over geïmporteerde accounts"
|
||||
},
|
||||
"importAnAccount": {
|
||||
"message": "Importeer een account"
|
||||
|
1213
app/_locales/pl/messages.json
Normal file
1213
app/_locales/pl/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -173,7 +173,7 @@
|
||||
},
|
||||
"decimal": {
|
||||
"message": "Precisão em Decimais"
|
||||
},
|
||||
},
|
||||
"defaultNetwork": {
|
||||
"message": "A rede pré definida para transações em Ether é a Main Net."
|
||||
},
|
||||
@ -351,7 +351,7 @@
|
||||
"message": "Importar Conta"
|
||||
},
|
||||
"importAccountMsg": {
|
||||
"message":"Contas importadas não irão ser associadas com a frase seed da conta criada originalmente pelo MetaMask. Saiba mais sobre contas importadas."
|
||||
"message": "Contas importadas não irão ser associadas com a frase seed da conta criada originalmente pelo MetaMask. Saiba mais sobre contas importadas."
|
||||
},
|
||||
"importAnAccount": {
|
||||
"message": "Importar uma conta"
|
||||
@ -800,7 +800,7 @@
|
||||
},
|
||||
"visitWebSite": {
|
||||
"message": "Visite o nosso site"
|
||||
},
|
||||
},
|
||||
"warning": {
|
||||
"message": "Aviso"
|
||||
},
|
||||
|
@ -366,7 +366,7 @@
|
||||
"message": "Импортировать счет"
|
||||
},
|
||||
"importAccountMsg": {
|
||||
"message":" Импортированные счета не будут ассоциированы с вашей ключевой фразой, созданной MetaMask. Узнать больше про импорт счетов "
|
||||
"message": " Импортированные счета не будут ассоциированы с вашей ключевой фразой, созданной MetaMask. Узнать больше про импорт счетов "
|
||||
},
|
||||
"importAnAccount": {
|
||||
"message": "Импортировать аккаунт"
|
||||
|
@ -173,7 +173,7 @@
|
||||
},
|
||||
"decimal": {
|
||||
"message": "Decimalke natančnosti"
|
||||
},
|
||||
},
|
||||
"defaultNetwork": {
|
||||
"message": "Privzeto omrežje za transakcije je Main Net."
|
||||
},
|
||||
@ -351,7 +351,7 @@
|
||||
"message": "Uvozi račun"
|
||||
},
|
||||
"importAccountMsg": {
|
||||
"message":" Uvoženi računi ne bodo povezani s prvotnim seedphaseom. Preberite več o uvoženih računih "
|
||||
"message": " Uvoženi računi ne bodo povezani s prvotnim seedphaseom. Preberite več o uvoženih računih "
|
||||
},
|
||||
"importAnAccount": {
|
||||
"message": "Uvozi račun"
|
||||
@ -800,7 +800,7 @@
|
||||
},
|
||||
"visitWebSite": {
|
||||
"message": "Obiščite našo spletno stran"
|
||||
},
|
||||
},
|
||||
"warning": {
|
||||
"message": "Opozorilo"
|
||||
},
|
||||
|
@ -351,7 +351,7 @@
|
||||
"message": "นำเข้าบัญชี"
|
||||
},
|
||||
"importAccountMsg": {
|
||||
"message":"บัญชีที่นำเข้าจะไม่ถูกรวมกับบัญชีที่สร้างด้วยคำเเริ่มต้นบนเมต้ามาร์สในตอนแรก เรียนรู้เพิ่มเติมเกี่ยวกับบัญชีที่นำเข้า"
|
||||
"message": "บัญชีที่นำเข้าจะไม่ถูกรวมกับบัญชีที่สร้างด้วยคำเเริ่มต้นบนเมต้ามาร์สในตอนแรก เรียนรู้เพิ่มเติมเกี่ยวกับบัญชีที่นำเข้า"
|
||||
},
|
||||
"importAnAccount": {
|
||||
"message": "นำเข้าบัญชี"
|
||||
|
@ -372,7 +372,7 @@
|
||||
"message": "கணக்கை இறக்குமதி செய்க"
|
||||
},
|
||||
"importAccountMsg": {
|
||||
"message":" இறக்குமதி செய்யப்பட்ட கணக்கு உங்கள் முதலில் உருவாக்கப்பட்ட மெட்டாமாஸ்க் கணக்கு விதை மூலம் தொடர்புடையதாக இருக்காது. இறக்குமதி செய்யப்பட்ட கணக்குகள் பற்றி மேலும் அறிக "
|
||||
"message": " இறக்குமதி செய்யப்பட்ட கணக்கு உங்கள் முதலில் உருவாக்கப்பட்ட மெட்டாமாஸ்க் கணக்கு விதை மூலம் தொடர்புடையதாக இருக்காது. இறக்குமதி செய்யப்பட்ட கணக்குகள் பற்றி மேலும் அறிக "
|
||||
},
|
||||
"importAnAccount": {
|
||||
"message": "ஒரு கணக்கை இறக்குமதி செய்க"
|
||||
@ -730,7 +730,7 @@
|
||||
"message": "அமைப்புகள்"
|
||||
},
|
||||
"info": {
|
||||
"message": "தகவல்"
|
||||
"message": "தகவல்"
|
||||
},
|
||||
"shapeshiftBuy": {
|
||||
"message": "Shapeshift உடன் வாங்கவும்"
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -387,7 +387,7 @@
|
||||
"message": "导入账户"
|
||||
},
|
||||
"importAccountMsg": {
|
||||
"message":" Imported accounts will not be associated with your originally created MetaMask account seedphrase. Learn more about imported accounts "
|
||||
"message": " Imported accounts will not be associated with your originally created MetaMask account seedphrase. Learn more about imported accounts "
|
||||
},
|
||||
"importAnAccount": {
|
||||
"message": "导入一个账户"
|
||||
@ -511,7 +511,7 @@
|
||||
"description": "User is important an account and needs to add a file to continue"
|
||||
},
|
||||
"needImportPassword": {
|
||||
"message": "必须为已选择的文件输入密码。",
|
||||
"message": "必须为已选择的文件输入密码。",
|
||||
"description": "Password and file needed to import an account"
|
||||
},
|
||||
"negativeETH": {
|
||||
@ -561,7 +561,7 @@
|
||||
"message": "旧版界面"
|
||||
},
|
||||
"oldUIMessage": {
|
||||
"message": "你已经切换到旧版界面。 你可以通过右上方下拉菜单中的选项切换回新的用户界面。"
|
||||
"message": "你已经切换到旧版界面。 你可以通过右上方下拉菜单中的选项切换回新的用户界面。"
|
||||
},
|
||||
"or": {
|
||||
"message": "或",
|
||||
@ -605,7 +605,7 @@
|
||||
"description": "select this type of file to use to import an account"
|
||||
},
|
||||
"privateKeyWarning": {
|
||||
"message": "注意:永远不要公开这个私钥。任何拥有你的私钥的人都可以窃取你帐户中的任何资产。"
|
||||
"message": "注意:永远不要公开这个私钥。任何拥有你的私钥的人都可以窃取你帐户中的任何资产。"
|
||||
},
|
||||
"privateNetwork": {
|
||||
"message": "私有网络"
|
||||
@ -614,7 +614,7 @@
|
||||
"message": "显示二维码"
|
||||
},
|
||||
"readdToken": {
|
||||
"message": "之后你还可以通过帐户选项菜单中的“添加代币”来添加此代币。"
|
||||
"message": "之后你还可以通过帐户选项菜单中的“添加代币”来添加此代币。"
|
||||
},
|
||||
"readMore": {
|
||||
"message": "了解更多。"
|
||||
@ -766,7 +766,7 @@
|
||||
"message": "设置"
|
||||
},
|
||||
"info": {
|
||||
"message": "信息"
|
||||
"message": "信息"
|
||||
},
|
||||
"shapeshiftBuy": {
|
||||
"message": "使用 Shapeshift 购买"
|
||||
@ -888,7 +888,7 @@
|
||||
"message": "欢迎使用新版界面 (Beta)"
|
||||
},
|
||||
"uiWelcomeMessage": {
|
||||
"message": "你现在正在使用新的 MetaMask 界面。 尝试发送代币等新功能,有任何问题请告知我们。"
|
||||
"message": "你现在正在使用新的 MetaMask 界面。 尝试发送代币等新功能,有任何问题请告知我们。"
|
||||
},
|
||||
"unapproved": {
|
||||
"message": "未批准"
|
||||
|
@ -372,7 +372,7 @@
|
||||
"message": "導入帳戶"
|
||||
},
|
||||
"importAccountMsg": {
|
||||
"message":" 匯入的帳戶與您原有 MetaMask 帳戶的助憶詞並無關聯. 請查看與導入帳戶相關的資料 "
|
||||
"message": " 匯入的帳戶與您原有 MetaMask 帳戶的助憶詞並無關聯. 請查看與導入帳戶相關的資料 "
|
||||
},
|
||||
"importAnAccount": {
|
||||
"message": "導入一個帳戶"
|
||||
|
14
app/images/eth.svg
Normal file
14
app/images/eth.svg
Normal file
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 40 40" style="enable-background:new 0 0 40 40;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#38393A;}
|
||||
</style>
|
||||
<title>deposit-eth</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<g id="deposit-eth" transform="translate(0.000000, 14.000000)">
|
||||
<path id="Shape" class="st0" d="M19.9,16L7.5,8.7L19.9,26L32.3,8.7L19.9,16L19.9,16z M20.1-14L7.7,6.4l12.4,7.3l12.4-7.2L20.1-14z"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 670 B |
13
app/images/expand.svg
Normal file
13
app/images/expand.svg
Normal file
@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 50.2 (55047) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>expand</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs></defs>
|
||||
<g id="Extension-(mobile-form-factor)" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="expand" fill="#FFFFFF" fill-rule="nonzero">
|
||||
<path d="M15.4802977,0.0647543729 C15.3777028,0.0227060788 15.2692212,0 15.1590578,0 L10.1134046,0 C9.64920448,0 9.27246238,0.376752715 9.27246238,0.840965882 C9.27246238,1.30602001 9.64920448,1.68193176 10.1134046,1.68193176 L13.1290233,1.68193176 L8.77993963,6.03113791 C8.45113123,6.35995557 8.45113123,6.89228697 8.77993963,7.22026366 C8.94392336,7.38509298 9.15920457,7.46666667 9.37448577,7.46666667 C9.58976698,7.46666667 9.80504818,7.38509298 9.96903191,7.22026366 L14.3181156,2.87105752 L14.3181156,5.88676117 C14.3181156,6.35181531 14.6948577,6.72772705 15.1590578,6.72772705 C15.6232579,6.72772705 16,6.35181531 16,5.88676117 L16,0.840965882 C16,0.731640317 15.9781355,0.623155718 15.9352475,0.519716915 C15.8494713,0.31452124 15.6863286,0.150532893 15.4802977,0.0647543729 Z" id="Fill-1" transform="translate(12.266667, 3.733333) rotate(-360.000000) translate(-12.266667, -3.733333) "></path>
|
||||
<path d="M6.94696439,8.59808771 C6.84436944,8.55603941 6.73588789,8.53333333 6.62572446,8.53333333 L1.58007124,8.53333333 C1.11587115,8.53333333 0.739129042,8.91008605 0.739129042,9.37429922 C0.739129042,9.83935335 1.11587115,10.2152651 1.58007124,10.2152651 L4.59568999,10.2152651 L0.246606301,14.5644712 C-0.0822021004,14.8932889 -0.0822021004,15.4256203 0.246606301,15.753597 C0.410590031,15.9184263 0.625871235,16 0.841152439,16 C1.05643364,16 1.27171485,15.9184263 1.43569858,15.753597 L5.78478226,11.4043909 L5.78478226,14.4200945 C5.78478226,14.8851486 6.16152437,15.2610604 6.62572446,15.2610604 C7.08992456,15.2610604 7.46666667,14.8851486 7.46666667,14.4200945 L7.46666667,9.37429922 C7.46666667,9.26497365 7.44480217,9.15648905 7.40191412,9.05305025 C7.31613801,8.84785457 7.15299522,8.68386623 6.94696439,8.59808771 Z" id="Fill-1-Copy" transform="translate(3.733333, 12.266667) rotate(-180.000000) translate(-3.733333, -12.266667) "></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.4 KiB |
16
app/images/hide.svg
Normal file
16
app/images/hide.svg
Normal file
@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="16px" height="12px" viewBox="0 0 16 12" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 50.2 (55047) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>hide</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs></defs>
|
||||
<g id="Extension-(mobile-form-factor)" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="hide" fill="#FFFFFF">
|
||||
<polygon id="Fill-1" points="12.7924541 0.685525644 2.5021101 11.2520394 3.21020258 11.978038 13.4995465 1.41152421"></polygon>
|
||||
<path d="M4.53087507,6.33167913 C4.53087507,4.36419221 6.08507807,2.76945986 8.00132835,2.76945986 C8.40338086,2.76945986 8.78243037,2.85366337 9.14147727,2.98202238 L10.0225923,2.07734807 C9.36950705,1.93153223 8.69641914,1.83808687 8.00132835,1.83808687 C4.64088944,1.83808687 1.68850383,3.6289518 0.000283333333,6.33167913 C0.757382218,7.54338819 1.77751546,8.55691095 2.96667077,9.32295752 L4.73890224,7.50334018 C4.61288579,7.1346931 4.53087507,6.74448171 4.53087507,6.33167913" id="Fill-2"></path>
|
||||
<path d="M13.0194751,3.34029805 L11.2513649,5.16094226 C11.3770883,5.52958934 11.4589083,5.91980074 11.4589083,6.33157644 C11.4589083,8.30009024 9.90831963,9.89482259 7.99652557,9.89482259 C7.59441023,9.89482259 7.21724,9.81061908 6.85803026,9.68123319 L5.97896421,10.5859075 C6.63152857,10.7317233 7.30305122,10.8261956 7.99652557,10.8261956 C11.348152,10.8261956 14.2936719,9.03430378 15.9789642,6.33157644 C15.223626,5.11986738 14.2048672,4.10634463 13.0194751,3.34029805" id="Fill-4"></path>
|
||||
<path d="M8.00102831,8.7499629 C9.30219826,8.7499629 10.3553358,7.66763972 10.3553358,6.33167913 C10.3553358,6.25774434 10.341334,6.18689017 10.3343331,6.11398225 L7.78900062,8.72839858 C7.86000989,8.73455982 7.92901891,8.7499629 8.00102831,8.7499629" id="Fill-7"></path>
|
||||
<path d="M8.00102831,3.9139088 C6.69985837,3.9139088 5.64672082,4.99623198 5.64672082,6.33116569 C5.64672082,6.40612736 5.66072264,6.47698153 5.66772356,6.54886258 L8.213056,3.93547311 C8.14204673,3.92931188 8.07303772,3.9139088 8.00102831,3.9139088" id="Fill-9"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.2 KiB |
17
app/images/info.svg
Normal file
17
app/images/info.svg
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 50.2 (55047) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>info</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs></defs>
|
||||
<g id="Extension-(mobile-form-factor)" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="info" fill="#FFFFFF">
|
||||
<path d="M13.2142857,0.642857143 L13.2142857,0.5 L0.5,0.5 L0.5,13.2142857 L0.642857143,13.2142857 L0.642857143,0.642857143 L13.2142857,0.642857143 Z" id="Combined-Shape" stroke="#FFFFFF"></path>
|
||||
<path d="M3.42857143,3.42857143 L3.42857143,14.8571429 L14.8571429,14.8571429 L14.8571429,3.42857143 L3.42857143,3.42857143 Z M2.28571429,2.28571429 L16,2.28571429 L16,16 L2.28571429,16 L2.28571429,2.28571429 Z" id="Rectangle-18-Copy" fill-rule="nonzero"></path>
|
||||
<g id="Group-44" transform="translate(8.000000, 5.000000)">
|
||||
<rect id="Rectangle-39" x="0" y="3" width="2" height="5" rx="1"></rect>
|
||||
<rect id="Rectangle-39-Copy" x="0" y="0" width="2" height="2" rx="1"></rect>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
12
app/images/open-etherscan.svg
Normal file
12
app/images/open-etherscan.svg
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 50.2 (55047) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>open-etherscan</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs></defs>
|
||||
<g id="Extension-(mobile-form-factor)" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="open-etherscan" fill="#FFFFFF">
|
||||
<path d="M3.00020312,0 C2.44821175,0 2.00021875,0.447993 2.00021875,0.999984375 C2.00021875,1.55197575 2.44821175,1.99996875 3.00020312,1.99996875 L12.5860533,1.99996875 L0.293245418,14.2927767 C-0.0977484727,14.6837706 -0.0977484727,15.3157607 0.293245418,15.7067546 C0.488242371,15.9017515 0.744238371,15.99975 1.00023437,15.99975 C1.25623037,15.99975 1.51222637,15.9017515 1.70722332,15.7067546 L14.0000312,3.41394666 L14.0000312,12.9997969 C14.0000312,13.5517883 14.4480242,13.9997813 15.0000156,13.9997813 C15.552007,13.9997813 16,13.5517883 16,12.9997969 L16,0 L3.00020312,0 Z"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "__MSG_appName__",
|
||||
"short_name": "__MSG_appName__",
|
||||
"version": "4.12.0",
|
||||
"version": "4.16.0",
|
||||
"manifest_version": 2,
|
||||
"author": "https://metamask.io",
|
||||
"description": "__MSG_appDescription__",
|
||||
|
@ -2,6 +2,9 @@
|
||||
* @file The entry point for the web extension singleton process.
|
||||
*/
|
||||
|
||||
// this needs to run before anything else
|
||||
require('./lib/setupFetchDebugging')()
|
||||
|
||||
const urlUtil = require('url')
|
||||
const endOfStream = require('end-of-stream')
|
||||
const pump = require('pump')
|
||||
|
@ -135,17 +135,22 @@ function doctypeCheck () {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the current document extension
|
||||
* Returns whether or not the extension (suffix) of the current document is prohibited
|
||||
*
|
||||
* @returns {boolean} {@code true} if the current extension is not prohibited
|
||||
* This checks {@code window.location.pathname} against a set of file extensions
|
||||
* that should not have web3 injected into them. This check is indifferent of query parameters
|
||||
* in the location.
|
||||
*
|
||||
* @returns {boolean} whether or not the extension of the current document is prohibited
|
||||
*/
|
||||
function suffixCheck () {
|
||||
var prohibitedTypes = ['xml', 'pdf']
|
||||
var currentUrl = window.location.href
|
||||
var currentRegex
|
||||
const prohibitedTypes = [
|
||||
/\.xml$/,
|
||||
/\.pdf$/,
|
||||
]
|
||||
const currentUrl = window.location.pathname
|
||||
for (let i = 0; i < prohibitedTypes.length; i++) {
|
||||
currentRegex = new RegExp(`\\.${prohibitedTypes[i]}$`)
|
||||
if (currentRegex.test(currentUrl)) {
|
||||
if (prohibitedTypes[i].test(currentUrl)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
@ -38,6 +38,9 @@ class PreferencesController {
|
||||
lostIdentities: {},
|
||||
seedWords: null,
|
||||
forgottenPassword: false,
|
||||
preferences: {
|
||||
useETHAsPrimaryCurrency: true,
|
||||
},
|
||||
}, opts.initState)
|
||||
|
||||
this.diagnostics = opts.diagnostics
|
||||
@ -371,22 +374,6 @@ class PreferencesController {
|
||||
return Promise.resolve(label)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an updated rpc list from this.addToFrequentRpcList() and sets the `frequentRpcList` to this update list.
|
||||
*
|
||||
* @param {string} _url The the new rpc url to add to the updated list
|
||||
* @param {bool} remove Remove selected url
|
||||
* @returns {Promise<void>} Promise resolves with undefined
|
||||
*
|
||||
*/
|
||||
updateFrequentRpcList (_url, remove = false) {
|
||||
return this.addToFrequentRpcList(_url, remove)
|
||||
.then((rpcList) => {
|
||||
this.store.updateState({ frequentRpcList: rpcList })
|
||||
return Promise.resolve()
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for the `currentAccountTab` property
|
||||
*
|
||||
@ -402,24 +389,39 @@ class PreferencesController {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an updated rpcList based on the passed url and the current list.
|
||||
* The returned list will have a max length of 3. If the _url currently exists it the list, it will be moved to the
|
||||
* end of the list. The current list is modified and returned as a promise.
|
||||
* Adds custom RPC url to state.
|
||||
*
|
||||
* @param {string} _url The rpc url to add to the frequentRpcList.
|
||||
* @param {bool} remove Remove selected url
|
||||
* @returns {Promise<array>} The updated frequentRpcList.
|
||||
* @param {string} url The RPC url to add to frequentRpcList.
|
||||
* @returns {Promise<array>} Promise resolving to updated frequentRpcList.
|
||||
*
|
||||
*/
|
||||
addToFrequentRpcList (_url, remove = false) {
|
||||
addToFrequentRpcList (url) {
|
||||
const rpcList = this.getFrequentRpcList()
|
||||
const index = rpcList.findIndex((element) => { return element === _url })
|
||||
const index = rpcList.findIndex((element) => { return element === url })
|
||||
if (index !== -1) {
|
||||
rpcList.splice(index, 1)
|
||||
}
|
||||
if (!remove && _url !== 'http://localhost:8545') {
|
||||
rpcList.push(_url)
|
||||
if (url !== 'http://localhost:8545') {
|
||||
rpcList.push(url)
|
||||
}
|
||||
this.store.updateState({ frequentRpcList: rpcList })
|
||||
return Promise.resolve(rpcList)
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes custom RPC url from state.
|
||||
*
|
||||
* @param {string} url The RPC url to remove from frequentRpcList.
|
||||
* @returns {Promise<array>} Promise resolving to updated frequentRpcList.
|
||||
*
|
||||
*/
|
||||
removeFromFrequentRpcList (url) {
|
||||
const rpcList = this.getFrequentRpcList()
|
||||
const index = rpcList.findIndex((element) => { return element === url })
|
||||
if (index !== -1) {
|
||||
rpcList.splice(index, 1)
|
||||
}
|
||||
this.store.updateState({ frequentRpcList: rpcList })
|
||||
return Promise.resolve(rpcList)
|
||||
}
|
||||
|
||||
@ -463,6 +465,33 @@ class PreferencesController {
|
||||
getFeatureFlags () {
|
||||
return this.store.getState().featureFlags
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the `preferences` property, which is an object. These are user-controlled features
|
||||
* found in the settings page.
|
||||
* @param {string} preference The preference to enable or disable.
|
||||
* @param {boolean} value Indicates whether or not the preference should be enabled or disabled.
|
||||
* @returns {Promise<object>} Promises a new object; the updated preferences object.
|
||||
*/
|
||||
setPreference (preference, value) {
|
||||
const currentPreferences = this.getPreferences()
|
||||
const updatedPreferences = {
|
||||
...currentPreferences,
|
||||
[preference]: value,
|
||||
}
|
||||
|
||||
this.store.updateState({ preferences: updatedPreferences })
|
||||
return Promise.resolve(updatedPreferences)
|
||||
}
|
||||
|
||||
/**
|
||||
* A getter for the `preferences` property
|
||||
* @returns {object} A key-boolean map of user-selected preferences.
|
||||
*/
|
||||
getPreferences () {
|
||||
return this.store.getState().preferences
|
||||
}
|
||||
|
||||
//
|
||||
// PRIVATE METHODS
|
||||
//
|
||||
|
@ -166,6 +166,10 @@ class TransactionController extends EventEmitter {
|
||||
async addUnapprovedTransaction (txParams) {
|
||||
// validate
|
||||
const normalizedTxParams = txUtils.normalizeTxParams(txParams)
|
||||
// Assert the from address is the selected address
|
||||
if (normalizedTxParams.from !== this.getSelectedAddress()) {
|
||||
throw new Error(`Transaction from address isn't valid for this account`)
|
||||
}
|
||||
txUtils.validateTxParams(normalizedTxParams)
|
||||
// construct txMeta
|
||||
let txMeta = this.txStateManager.generateTxMeta({
|
||||
@ -362,7 +366,40 @@ class TransactionController extends EventEmitter {
|
||||
this.txStateManager.setTxStatusSubmitted(txId)
|
||||
}
|
||||
|
||||
confirmTransaction (txId) {
|
||||
/**
|
||||
* Sets the status of the transaction to confirmed and sets the status of nonce duplicates as
|
||||
* dropped if the txParams have data it will fetch the txReceipt
|
||||
* @param {number} txId - The tx's ID
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async confirmTransaction (txId) {
|
||||
// get the txReceipt before marking the transaction confirmed
|
||||
// to ensure the receipt is gotten before the ui revives the tx
|
||||
const txMeta = this.txStateManager.getTx(txId)
|
||||
|
||||
if (!txMeta) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const txReceipt = await this.query.getTransactionReceipt(txMeta.hash)
|
||||
|
||||
// It seems that sometimes the numerical values being returned from
|
||||
// this.query.getTransactionReceipt are BN instances and not strings.
|
||||
const gasUsed = typeof txReceipt.gasUsed !== 'string'
|
||||
? txReceipt.gasUsed.toString(16)
|
||||
: txReceipt.gasUsed
|
||||
|
||||
txMeta.txReceipt = {
|
||||
...txReceipt,
|
||||
gasUsed,
|
||||
}
|
||||
|
||||
this.txStateManager.updateTx(txMeta, 'transactions#confirmTransaction - add txReceipt')
|
||||
} catch (err) {
|
||||
log.error(err)
|
||||
}
|
||||
|
||||
this.txStateManager.setTxStatusConfirmed(txId)
|
||||
this._markNonceDuplicatesDropped(txId)
|
||||
}
|
||||
|
@ -400,6 +400,11 @@ class TransactionStateManager extends EventEmitter {
|
||||
*/
|
||||
_setTxStatus (txId, status) {
|
||||
const txMeta = this.getTx(txId)
|
||||
|
||||
if (!txMeta) {
|
||||
return
|
||||
}
|
||||
|
||||
txMeta.status = status
|
||||
setTimeout(() => {
|
||||
try {
|
||||
|
@ -27,6 +27,8 @@ var metamaskStream = new LocalMessageDuplexStream({
|
||||
|
||||
// compose the inpage provider
|
||||
var inpageProvider = new MetamaskInpageProvider(metamaskStream)
|
||||
// set a high max listener count to avoid unnecesary warnings
|
||||
inpageProvider.setMaxListeners(100)
|
||||
|
||||
// Augment the provider with its enable method
|
||||
inpageProvider.enable = function (options = {}) {
|
||||
|
34
app/scripts/lib/setupFetchDebugging.js
Normal file
34
app/scripts/lib/setupFetchDebugging.js
Normal file
@ -0,0 +1,34 @@
|
||||
module.exports = setupFetchDebugging
|
||||
|
||||
//
|
||||
// This is a utility to help resolve cases where `window.fetch` throws a
|
||||
// `TypeError: Failed to Fetch` without any stack or context for the request
|
||||
// https://github.com/getsentry/sentry-javascript/pull/1293
|
||||
//
|
||||
|
||||
function setupFetchDebugging() {
|
||||
if (!global.fetch) return
|
||||
const originalFetch = global.fetch
|
||||
|
||||
global.fetch = wrappedFetch
|
||||
|
||||
async function wrappedFetch(...args) {
|
||||
const initialStack = getCurrentStack()
|
||||
try {
|
||||
return await originalFetch.call(window, ...args)
|
||||
} catch (err) {
|
||||
console.warn('FetchDebugger - fetch encountered an Error', err)
|
||||
console.warn('FetchDebugger - overriding stack to point of original call')
|
||||
err.stack = initialStack
|
||||
throw err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getCurrentStack() {
|
||||
try {
|
||||
throw new Error('Fake error for generating stack trace')
|
||||
} catch (err) {
|
||||
return err.stack
|
||||
}
|
||||
}
|
@ -129,6 +129,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
provider: this.provider,
|
||||
blockTracker: this.blockTracker,
|
||||
})
|
||||
|
||||
// start and stop polling for balances based on activeControllerConnections
|
||||
this.on('controllerConnectionChanged', (activeControllerConnections) => {
|
||||
if (activeControllerConnections > 0) {
|
||||
@ -137,7 +138,12 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
this.accountTracker.stop()
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
// ensure accountTracker updates balances after network change
|
||||
this.networkController.on('networkDidChange', () => {
|
||||
this.accountTracker._updateAccounts()
|
||||
})
|
||||
|
||||
// key mgmt
|
||||
const additionalKeyrings = [TrezorKeyring, LedgerBridgeKeyring]
|
||||
this.keyringController = new KeyringController({
|
||||
@ -387,6 +393,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
setCurrentAccountTab: nodeify(preferencesController.setCurrentAccountTab, preferencesController),
|
||||
setAccountLabel: nodeify(preferencesController.setAccountLabel, preferencesController),
|
||||
setFeatureFlag: nodeify(preferencesController.setFeatureFlag, preferencesController),
|
||||
setPreference: nodeify(preferencesController.setPreference, preferencesController),
|
||||
|
||||
// BlacklistController
|
||||
whitelistPhishingDomain: this.whitelistPhishingDomain.bind(this),
|
||||
@ -1451,7 +1458,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
*/
|
||||
async setCustomRpc (rpcTarget) {
|
||||
this.networkController.setRpcTarget(rpcTarget)
|
||||
await this.preferencesController.updateFrequentRpcList(rpcTarget)
|
||||
await this.preferencesController.addToFrequentRpcList(rpcTarget)
|
||||
return rpcTarget
|
||||
}
|
||||
|
||||
@ -1460,7 +1467,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
* @param {string} rpcTarget - A RPC URL to delete.
|
||||
*/
|
||||
async delCustomRpc (rpcTarget) {
|
||||
await this.preferencesController.updateFrequentRpcList(rpcTarget, true)
|
||||
await this.preferencesController.removeFromFrequentRpcList(rpcTarget)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,4 +1,4 @@
|
||||
window.onload = function() {
|
||||
window.onload = function () {
|
||||
if (window.location.pathname === '/phishing.html') {
|
||||
const {hostname} = parseHash()
|
||||
document.getElementById('esdbLink').innerHTML = '<b>To read more about this scam, navigate to: <a href="https://etherscamdb.info/domain/' + hostname + '"> https://etherscamdb.info/domain/' + hostname + '</a></b>'
|
||||
|
@ -14,21 +14,27 @@ async function start () {
|
||||
const versionAlreadyExists = await checkIfVersionExists()
|
||||
// abort if versions exists
|
||||
if (versionAlreadyExists) {
|
||||
console.log(`Version "${VERSION}" already exists on Sentry, aborting sourcemap upload.`)
|
||||
return
|
||||
console.log(`Version "${VERSION}" already exists on Sentry, skipping version creation`)
|
||||
} else {
|
||||
// create sentry release
|
||||
console.log(`creating Sentry release for "${VERSION}"...`)
|
||||
await exec(`sentry-cli releases --org 'metamask' --project 'metamask' new ${VERSION}`)
|
||||
console.log(`removing any existing files from Sentry release "${VERSION}"...`)
|
||||
await exec(`sentry-cli releases --org 'metamask' --project 'metamask' files ${VERSION} delete --all`)
|
||||
}
|
||||
|
||||
// create sentry release
|
||||
console.log(`creating Sentry release for "${VERSION}"...`)
|
||||
await exec(`sentry-cli releases --org 'metamask' --project 'metamask' new ${VERSION}`)
|
||||
console.log(`removing any existing files from Sentry release "${VERSION}"...`)
|
||||
await exec(`sentry-cli releases --org 'metamask' --project 'metamask' files ${VERSION} delete --all`)
|
||||
// upload sentry source and sourcemaps
|
||||
console.log(`uploading source files Sentry release "${VERSION}"...`)
|
||||
await exec(`for FILEPATH in ./dist/chrome/*.js; do [ -e $FILEPATH ] || continue; export FILE=\`basename $FILEPATH\` && echo uploading $FILE && sentry-cli releases --org 'metamask' --project 'metamask' files ${VERSION} upload $FILEPATH metamask/$FILE; done;`)
|
||||
console.log(`uploading sourcemaps Sentry release "${VERSION}"...`)
|
||||
await exec(`sentry-cli releases --org 'metamask' --project 'metamask' files ${VERSION} upload-sourcemaps ./dist/sourcemaps/ --url-prefix 'sourcemaps'`)
|
||||
console.log('all done!')
|
||||
// check if version has artifacts or not
|
||||
const versionHasArtifacts = versionAlreadyExists && await checkIfVersionHasArtifacts()
|
||||
if (!versionHasArtifacts) {
|
||||
// upload sentry source and sourcemaps
|
||||
console.log(`uploading source files Sentry release "${VERSION}"...`)
|
||||
await exec(`for FILEPATH in ./dist/chrome/*.js; do [ -e $FILEPATH ] || continue; export FILE=\`basename $FILEPATH\` && echo uploading $FILE && sentry-cli releases --org 'metamask' --project 'metamask' files ${VERSION} upload $FILEPATH metamask/$FILE; done;`)
|
||||
console.log(`uploading sourcemaps Sentry release "${VERSION}"...`)
|
||||
await exec(`sentry-cli releases --org 'metamask' --project 'metamask' files ${VERSION} upload-sourcemaps ./dist/sourcemaps/ --url-prefix 'sourcemaps'`)
|
||||
console.log('all done!')
|
||||
} else {
|
||||
console.log(`Version "${VERSION}" already has artifacts on Sentry, skipping sourcemap upload`)
|
||||
}
|
||||
}
|
||||
|
||||
async function checkIfAuthWorks () {
|
||||
@ -45,6 +51,12 @@ async function checkIfVersionExists () {
|
||||
return versionAlreadyExists
|
||||
}
|
||||
|
||||
async function checkIfVersionHasArtifacts () {
|
||||
const artifacts = await exec(`sentry-cli releases --org 'metamask' --project 'metamask' files ${VERSION} list`)
|
||||
// When there's no artifacts, we get a response from the shell like this ['', '']
|
||||
return artifacts[0] && artifacts[0].length > 0
|
||||
}
|
||||
|
||||
async function doesNotFail (asyncFn) {
|
||||
try {
|
||||
await asyncFn()
|
||||
|
@ -107,7 +107,10 @@
|
||||
"maxModeOn": false,
|
||||
"editingTransactionId": null
|
||||
},
|
||||
"currentLocale": "en"
|
||||
"currentLocale": "en",
|
||||
"preferences": {
|
||||
"useETHAsPrimaryCurrency": true
|
||||
}
|
||||
},
|
||||
"appState": {
|
||||
"menuOpen": false,
|
||||
|
@ -150,7 +150,10 @@
|
||||
"maxModeOn": false,
|
||||
"editingTransactionId": null
|
||||
},
|
||||
"currentLocale": "en"
|
||||
"currentLocale": "en",
|
||||
"preferences": {
|
||||
"useETHAsPrimaryCurrency": true
|
||||
}
|
||||
},
|
||||
"appState": {
|
||||
"menuOpen": false,
|
||||
|
@ -108,7 +108,10 @@
|
||||
"maxModeOn": false,
|
||||
"editingTransactionId": null
|
||||
},
|
||||
"currentLocale": "en"
|
||||
"currentLocale": "en",
|
||||
"preferences": {
|
||||
"useETHAsPrimaryCurrency": true
|
||||
}
|
||||
},
|
||||
"appState": {
|
||||
"menuOpen": false,
|
||||
|
@ -37,7 +37,10 @@
|
||||
"shapeShiftTxList": [],
|
||||
"lostAccounts": [],
|
||||
"tokens": [],
|
||||
"currentLocale": "en"
|
||||
"currentLocale": "en",
|
||||
"preferences": {
|
||||
"useETHAsPrimaryCurrency": true
|
||||
}
|
||||
},
|
||||
"appState": {
|
||||
"menuOpen": false,
|
||||
|
@ -109,7 +109,10 @@
|
||||
"maxModeOn": false,
|
||||
"editingTransactionId": null
|
||||
},
|
||||
"currentLocale": "en"
|
||||
"currentLocale": "en",
|
||||
"preferences": {
|
||||
"useETHAsPrimaryCurrency": true
|
||||
}
|
||||
},
|
||||
"appState": {
|
||||
"menuOpen": false,
|
||||
|
@ -102,7 +102,10 @@
|
||||
"shapeShiftTxList": [{"depositAddress":"34vJ3AfmNcLiziA4VFgEVcQTwxVLD1qkke","depositType":"BTC","key":"shapeshift","response":{"status":"no_deposits","address":"34vJ3AfmNcLiziA4VFgEVcQTwxVLD1qkke"},"time":1522377459106}],
|
||||
"lostAccounts": [],
|
||||
"send": {},
|
||||
"currentLocale": "en"
|
||||
"currentLocale": "en",
|
||||
"preferences": {
|
||||
"useETHAsPrimaryCurrency": true
|
||||
}
|
||||
},
|
||||
"appState": {
|
||||
"menuOpen": false,
|
||||
|
@ -462,7 +462,9 @@ function generateBundler (opts, performBundle) {
|
||||
bundler.transform(envify({
|
||||
METAMASK_DEBUG: opts.devMode,
|
||||
NODE_ENV: opts.devMode ? 'development' : 'production',
|
||||
}))
|
||||
}), {
|
||||
global: true,
|
||||
})
|
||||
|
||||
if (opts.watch) {
|
||||
bundler = watchify(bundler)
|
||||
|
@ -17,7 +17,7 @@
|
||||
font-family: Roboto;
|
||||
}
|
||||
|
||||
@media screen and (min-height: 576px) {
|
||||
@media screen and (min-height: 601px) {
|
||||
.first-time-flow {
|
||||
height: 100vh;
|
||||
}
|
||||
|
834
package-lock.json
generated
834
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -16,6 +16,7 @@
|
||||
"test:integration": "npm run test:integration:build && npm run test:flat && npm run test:mascara",
|
||||
"test:integration:build": "gulp build:scss",
|
||||
"test:e2e:chrome": "shell-parallel -s 'npm run ganache:start' -x 'sleep 3 && npm run test:e2e:run:chrome'",
|
||||
"test:e2e:drizzle:beta": "SELENIUM_BROWSER=chrome test/e2e/beta/run-drizzle.sh",
|
||||
"test:e2e:chrome:beta": "SELENIUM_BROWSER=chrome test/e2e/beta/run-all.sh",
|
||||
"test:e2e:firefox": "shell-parallel -s 'npm run ganache:start' -x 'sleep 3 && npm run test:e2e:run:firefox'",
|
||||
"test:e2e:firefox:beta": "SELENIUM_BROWSER=firefox test/e2e/beta/run-all.sh",
|
||||
@ -123,7 +124,7 @@
|
||||
"eth-phishing-detect": "^1.1.4",
|
||||
"eth-query": "^2.1.2",
|
||||
"eth-sig-util": "^2.0.2",
|
||||
"eth-token-tracker": "^1.1.4",
|
||||
"eth-token-tracker": "^1.1.5",
|
||||
"eth-trezor-keyring": "^0.1.0",
|
||||
"ethereumjs-abi": "^0.6.4",
|
||||
"ethereumjs-tx": "^1.3.0",
|
||||
@ -142,7 +143,7 @@
|
||||
"fast-levenshtein": "^2.0.6",
|
||||
"file-loader": "^1.1.11",
|
||||
"fuse.js": "^3.2.0",
|
||||
"gulp": "github:gulpjs/gulp#4.0",
|
||||
"gulp": "github:gulpjs/gulp#v4.0.0",
|
||||
"gulp-autoprefixer": "^5.0.0",
|
||||
"gulp-debug": "^3.2.0",
|
||||
"gulp-eslint": "^4.0.0",
|
||||
@ -260,8 +261,9 @@
|
||||
"eslint-plugin-json": "^1.2.0",
|
||||
"eslint-plugin-mocha": "^5.0.0",
|
||||
"eslint-plugin-react": "^7.4.0",
|
||||
"eth-json-rpc-middleware": "^3.1.1",
|
||||
"eth-json-rpc-middleware": "^3.1.3",
|
||||
"eth-keyring-controller": "^3.3.1",
|
||||
"fetch-mock": "^6.5.2",
|
||||
"file-loader": "^1.1.11",
|
||||
"fs-extra": "^6.0.1",
|
||||
"fs-promise": "^2.0.3",
|
||||
|
70
test/data/2-state.json
Normal file
70
test/data/2-state.json
Normal file
@ -0,0 +1,70 @@
|
||||
{ "isInitialized": true,
|
||||
"provider": { "type": "rpc", "rpcTarget": "http://localhost:8545" },
|
||||
"network": "loading",
|
||||
"accounts": {
|
||||
"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc": {
|
||||
"address": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc",
|
||||
"balance": "0x0"
|
||||
},
|
||||
"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b": {
|
||||
"address": "0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b",
|
||||
"balance": "0x0"
|
||||
}
|
||||
},
|
||||
"currentBlockGasLimit": "",
|
||||
"unapprovedTxs": {},
|
||||
"selectedAddressTxList": [],
|
||||
"computedBalances": {},
|
||||
"unapprovedMsgs": {},
|
||||
"unapprovedMsgCount": 0,
|
||||
"unapprovedPersonalMsgs": {},
|
||||
"unapprovedPersonalMsgCount": 0,
|
||||
"unapprovedTypedMessages": {},
|
||||
"unapprovedTypedMessagesCount": 0,
|
||||
"isUnlocked": true,
|
||||
"keyringTypes": [ "Simple Key Pair", "HD Key Tree" ],
|
||||
"keyrings":[
|
||||
{ "type": "HD Key Tree",
|
||||
"accounts": [
|
||||
"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Simple Key Pair",
|
||||
"accounts": [
|
||||
"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b"
|
||||
]
|
||||
}
|
||||
],
|
||||
"frequentRpcList": [],
|
||||
"currentAccountTab": "history",
|
||||
"tokens": [],
|
||||
"useBlockie": false,
|
||||
"featureFlags": {},
|
||||
"currentLocale": null,
|
||||
"identities": {
|
||||
"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc": {
|
||||
"name": "Account 1",
|
||||
"address": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc"
|
||||
},
|
||||
"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b": {
|
||||
"name": "Account 2",
|
||||
"address": "0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b"
|
||||
}
|
||||
},
|
||||
|
||||
"lostIdentities": {},
|
||||
"selectedAddress": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc",
|
||||
"recentBlocks": [],
|
||||
"addressBook": [],
|
||||
"currentCurrency": "usd",
|
||||
"conversionRate": 288.45,
|
||||
"conversionDate": 1506444677,
|
||||
"nextUnreadNotice": null,
|
||||
"noActiveNotices": true,
|
||||
"shapeShiftTxList": [],
|
||||
"infuraNetworkStatus": {},
|
||||
"lostAccounts": [],
|
||||
"seedWords": "debris dizzy just program just float decrease vacant alarm reduce speak stadium",
|
||||
"forgottenPassword": null
|
||||
}
|
286
test/e2e/beta/drizzle.spec.js
Normal file
286
test/e2e/beta/drizzle.spec.js
Normal file
@ -0,0 +1,286 @@
|
||||
const path = require('path')
|
||||
const assert = require('assert')
|
||||
const webdriver = require('selenium-webdriver')
|
||||
const { By, until } = webdriver
|
||||
const {
|
||||
delay,
|
||||
buildChromeWebDriver,
|
||||
buildFirefoxWebdriver,
|
||||
installWebExt,
|
||||
getExtensionIdChrome,
|
||||
getExtensionIdFirefox,
|
||||
} = require('../func')
|
||||
const {
|
||||
checkBrowserForConsoleErrors,
|
||||
closeAllWindowHandlesExcept,
|
||||
findElement,
|
||||
findElements,
|
||||
loadExtension,
|
||||
openNewPage,
|
||||
verboseReportOnFailure,
|
||||
waitUntilXWindowHandles,
|
||||
} = require('./helpers')
|
||||
|
||||
describe('MetaMask', function () {
|
||||
let extensionId
|
||||
let driver
|
||||
|
||||
const tinyDelayMs = 200
|
||||
const regularDelayMs = tinyDelayMs * 2
|
||||
const largeDelayMs = regularDelayMs * 2
|
||||
|
||||
this.timeout(0)
|
||||
this.bail(true)
|
||||
|
||||
before(async function () {
|
||||
switch (process.env.SELENIUM_BROWSER) {
|
||||
case 'chrome': {
|
||||
const extPath = path.resolve('dist/chrome')
|
||||
driver = buildChromeWebDriver(extPath)
|
||||
extensionId = await getExtensionIdChrome(driver)
|
||||
await driver.get(`chrome-extension://${extensionId}/popup.html`)
|
||||
break
|
||||
}
|
||||
case 'firefox': {
|
||||
const extPath = path.resolve('dist/firefox')
|
||||
driver = buildFirefoxWebdriver()
|
||||
await installWebExt(driver, extPath)
|
||||
await delay(700)
|
||||
extensionId = await getExtensionIdFirefox(driver)
|
||||
await driver.get(`moz-extension://${extensionId}/popup.html`)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
if (process.env.SELENIUM_BROWSER === 'chrome') {
|
||||
const errors = await 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 verboseReportOnFailure(driver, this.currentTest)
|
||||
}
|
||||
})
|
||||
|
||||
after(async function () {
|
||||
await driver.quit()
|
||||
})
|
||||
|
||||
|
||||
describe('New UI setup', async function () {
|
||||
it('switches to first tab', async function () {
|
||||
await delay(tinyDelayMs)
|
||||
const [firstTab] = await driver.getAllWindowHandles()
|
||||
await driver.switchTo().window(firstTab)
|
||||
await delay(regularDelayMs)
|
||||
})
|
||||
|
||||
it('selects the new UI option', async () => {
|
||||
try {
|
||||
const overlay = await findElement(driver, By.css('.full-flex-height'))
|
||||
await driver.wait(until.stalenessOf(overlay))
|
||||
} catch (e) {}
|
||||
|
||||
let button
|
||||
try {
|
||||
button = await findElement(driver, By.xpath("//button[contains(text(), 'Try it now')]"))
|
||||
} catch (e) {
|
||||
await loadExtension(driver, extensionId)
|
||||
await delay(largeDelayMs)
|
||||
button = await findElement(driver, By.xpath("//button[contains(text(), 'Try it now')]"))
|
||||
}
|
||||
await button.click()
|
||||
await delay(regularDelayMs)
|
||||
|
||||
// Close all other tabs
|
||||
const [tab0, tab1, tab2] = await driver.getAllWindowHandles()
|
||||
await driver.switchTo().window(tab0)
|
||||
await delay(tinyDelayMs)
|
||||
|
||||
let selectedUrl = await driver.getCurrentUrl()
|
||||
await delay(tinyDelayMs)
|
||||
if (tab0 && selectedUrl.match(/popup.html/)) {
|
||||
await closeAllWindowHandlesExcept(driver, tab0)
|
||||
} else if (tab1) {
|
||||
await driver.switchTo().window(tab1)
|
||||
selectedUrl = await driver.getCurrentUrl()
|
||||
await delay(tinyDelayMs)
|
||||
if (selectedUrl.match(/popup.html/)) {
|
||||
await closeAllWindowHandlesExcept(driver, tab1)
|
||||
} else if (tab2) {
|
||||
await driver.switchTo().window(tab2)
|
||||
selectedUrl = await driver.getCurrentUrl()
|
||||
selectedUrl.match(/popup.html/) && await closeAllWindowHandlesExcept(driver, tab2)
|
||||
}
|
||||
} else {
|
||||
throw new Error('popup.html not found')
|
||||
}
|
||||
await delay(regularDelayMs)
|
||||
const [appTab] = await driver.getAllWindowHandles()
|
||||
await driver.switchTo().window(appTab)
|
||||
await delay(tinyDelayMs)
|
||||
|
||||
await loadExtension(driver, extensionId)
|
||||
await delay(regularDelayMs)
|
||||
|
||||
const continueBtn = await findElement(driver, By.css('.welcome-screen__button'))
|
||||
await continueBtn.click()
|
||||
await delay(regularDelayMs)
|
||||
})
|
||||
})
|
||||
|
||||
describe('Going through the first time flow', () => {
|
||||
it('accepts a secure password', async () => {
|
||||
const passwordBox = await findElement(driver, By.css('.create-password #create-password'))
|
||||
const passwordBoxConfirm = await findElement(driver, By.css('.create-password #confirm-password'))
|
||||
const button = await findElement(driver, By.css('.create-password button'))
|
||||
|
||||
await passwordBox.sendKeys('correct horse battery staple')
|
||||
await passwordBoxConfirm.sendKeys('correct horse battery staple')
|
||||
await button.click()
|
||||
await delay(regularDelayMs)
|
||||
})
|
||||
|
||||
it('clicks through the unique image screen', async () => {
|
||||
const nextScreen = await findElement(driver, By.css('.unique-image button'))
|
||||
await nextScreen.click()
|
||||
await delay(regularDelayMs)
|
||||
})
|
||||
|
||||
it('clicks through the ToS', async () => {
|
||||
// terms of use
|
||||
const canClickThrough = await driver.findElement(By.css('.tou button')).isEnabled()
|
||||
assert.equal(canClickThrough, false, 'disabled continue button')
|
||||
const bottomOfTos = await findElement(driver, By.linkText('Attributions'))
|
||||
await driver.executeScript('arguments[0].scrollIntoView(true)', bottomOfTos)
|
||||
await delay(regularDelayMs)
|
||||
const acceptTos = await findElement(driver, By.css('.tou button'))
|
||||
driver.wait(until.elementIsEnabled(acceptTos))
|
||||
await acceptTos.click()
|
||||
await delay(regularDelayMs)
|
||||
})
|
||||
|
||||
it('clicks through the privacy notice', async () => {
|
||||
// privacy notice
|
||||
const nextScreen = await findElement(driver, By.css('.tou button'))
|
||||
await nextScreen.click()
|
||||
await delay(regularDelayMs)
|
||||
})
|
||||
|
||||
it('clicks through the phishing notice', async () => {
|
||||
// phishing notice
|
||||
const noticeElement = await driver.findElement(By.css('.markdown'))
|
||||
await driver.executeScript('arguments[0].scrollTop = arguments[0].scrollHeight', noticeElement)
|
||||
await delay(regularDelayMs)
|
||||
const nextScreen = await findElement(driver, By.css('.tou button'))
|
||||
await nextScreen.click()
|
||||
await delay(regularDelayMs)
|
||||
})
|
||||
|
||||
let seedPhrase
|
||||
|
||||
it('reveals the seed phrase', async () => {
|
||||
const byRevealButton = By.css('.backup-phrase__secret-blocker .backup-phrase__reveal-button')
|
||||
await driver.wait(until.elementLocated(byRevealButton, 10000))
|
||||
const revealSeedPhraseButton = await findElement(driver, byRevealButton, 10000)
|
||||
await revealSeedPhraseButton.click()
|
||||
await delay(regularDelayMs)
|
||||
|
||||
seedPhrase = await driver.findElement(By.css('.backup-phrase__secret-words')).getText()
|
||||
assert.equal(seedPhrase.split(' ').length, 12)
|
||||
await delay(regularDelayMs)
|
||||
|
||||
const nextScreen = await findElement(driver, By.css('.backup-phrase button'))
|
||||
await nextScreen.click()
|
||||
await delay(regularDelayMs)
|
||||
})
|
||||
|
||||
async function clickWordAndWait (word) {
|
||||
const xpathClass = 'backup-phrase__confirm-seed-option backup-phrase__confirm-seed-option--unselected'
|
||||
const xpath = `//button[@class='${xpathClass}' and contains(text(), '${word}')]`
|
||||
const word0 = await findElement(driver, By.xpath(xpath), 10000)
|
||||
|
||||
await word0.click()
|
||||
await delay(tinyDelayMs)
|
||||
}
|
||||
|
||||
async function retypeSeedPhrase (words, wasReloaded, count = 0) {
|
||||
try {
|
||||
if (wasReloaded) {
|
||||
const byRevealButton = By.css('.backup-phrase__secret-blocker .backup-phrase__reveal-button')
|
||||
await driver.wait(until.elementLocated(byRevealButton, 10000))
|
||||
const revealSeedPhraseButton = await findElement(driver, byRevealButton, 10000)
|
||||
await revealSeedPhraseButton.click()
|
||||
await delay(regularDelayMs)
|
||||
|
||||
const nextScreen = await findElement(driver, By.css('.backup-phrase button'))
|
||||
await nextScreen.click()
|
||||
await delay(regularDelayMs)
|
||||
}
|
||||
|
||||
for (let i = 0; i < 12; i++) {
|
||||
await clickWordAndWait(words[i])
|
||||
}
|
||||
} catch (e) {
|
||||
if (count > 2) {
|
||||
throw e
|
||||
} else {
|
||||
await loadExtension(driver, extensionId)
|
||||
await retypeSeedPhrase(words, true, count + 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
it('can retype the seed phrase', async () => {
|
||||
const words = seedPhrase.split(' ')
|
||||
|
||||
await retypeSeedPhrase(words)
|
||||
|
||||
const confirm = await findElement(driver, By.xpath(`//button[contains(text(), 'Confirm')]`))
|
||||
await confirm.click()
|
||||
await delay(regularDelayMs)
|
||||
})
|
||||
|
||||
it('clicks through the deposit modal', async () => {
|
||||
const byBuyModal = By.css('span .modal')
|
||||
const buyModal = await driver.wait(until.elementLocated(byBuyModal))
|
||||
const closeModal = await findElement(driver, By.css('.page-container__header-close'))
|
||||
await closeModal.click()
|
||||
await driver.wait(until.stalenessOf(buyModal))
|
||||
await delay(regularDelayMs)
|
||||
})
|
||||
|
||||
it('switches to localhost', async () => {
|
||||
const networkDropdown = await findElement(driver, By.css('.network-name'))
|
||||
await networkDropdown.click()
|
||||
await delay(regularDelayMs)
|
||||
|
||||
const [localhost] = await findElements(driver, By.xpath(`//span[contains(text(), 'Localhost')]`))
|
||||
await localhost.click()
|
||||
await delay(largeDelayMs * 2)
|
||||
})
|
||||
})
|
||||
|
||||
describe('Drizzle', () => {
|
||||
it('should be able to detect our eth address', async () => {
|
||||
await openNewPage(driver, 'http://127.0.0.1:3000/')
|
||||
await delay(regularDelayMs)
|
||||
|
||||
await waitUntilXWindowHandles(driver, 2)
|
||||
const windowHandles = await driver.getAllWindowHandles()
|
||||
const dapp = windowHandles[1]
|
||||
|
||||
await driver.switchTo().window(dapp)
|
||||
await delay(regularDelayMs)
|
||||
|
||||
|
||||
const addressElement = await findElement(driver, By.css(`.pure-u-1-1 h4`))
|
||||
const addressText = await addressElement.getText()
|
||||
assert(addressText.match(/^0x[a-fA-F0-9]{40}$/))
|
||||
})
|
||||
})
|
||||
})
|
@ -286,7 +286,7 @@ describe('Using MetaMask with an existing account', function () {
|
||||
await delay(regularDelayMs)
|
||||
|
||||
const inputAddress = await findElement(driver, By.css('input[placeholder="Recipient Address"]'))
|
||||
const inputAmount = await findElement(driver, By.css('.currency-display__input'))
|
||||
const inputAmount = await findElement(driver, By.css('.unit-input__input'))
|
||||
await inputAddress.sendKeys('0x2f318C334780961FB129D2a6c30D0763d9a5C970')
|
||||
await inputAmount.sendKeys('1')
|
||||
|
||||
|
@ -271,6 +271,17 @@ describe('MetaMask', function () {
|
||||
await driver.wait(until.stalenessOf(accountModal))
|
||||
await delay(regularDelayMs)
|
||||
})
|
||||
it('show account details dropdown menu', async () => {
|
||||
|
||||
const {width, height} = await driver.manage().window().getSize()
|
||||
driver.manage().window().setSize(320, 480)
|
||||
await driver.findElement(By.css('div.menu-bar__open-in-browser')).click()
|
||||
const options = await driver.findElements(By.css('div.menu.account-details-dropdown div.menu__item'))
|
||||
assert.equal(options.length, 3) // HD Wallet type does not have to show the Remove Account option
|
||||
await delay(regularDelayMs)
|
||||
driver.manage().window().setSize(width, height)
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
describe('Log out an log back in', () => {
|
||||
@ -372,7 +383,7 @@ describe('MetaMask', function () {
|
||||
await delay(regularDelayMs)
|
||||
|
||||
const inputAddress = await findElement(driver, By.css('input[placeholder="Recipient Address"]'))
|
||||
const inputAmount = await findElement(driver, By.css('.currency-display__input'))
|
||||
const inputAmount = await findElement(driver, By.css('.unit-input__input'))
|
||||
await inputAddress.sendKeys('0x2f318C334780961FB129D2a6c30D0763d9a5C970')
|
||||
await inputAmount.sendKeys('1')
|
||||
|
||||
@ -651,7 +662,7 @@ describe('MetaMask', function () {
|
||||
})
|
||||
|
||||
it('clicks on the Add Token button', async () => {
|
||||
const addToken = await driver.findElement(By.css('.wallet-view__add-token-button'))
|
||||
const addToken = await driver.findElement(By.xpath(`//div[contains(text(), 'Add Token')]`))
|
||||
await addToken.click()
|
||||
await delay(regularDelayMs)
|
||||
})
|
||||
@ -691,7 +702,7 @@ describe('MetaMask', function () {
|
||||
await delay(regularDelayMs)
|
||||
|
||||
const inputAddress = await findElement(driver, By.css('input[placeholder="Recipient Address"]'))
|
||||
const inputAmount = await findElement(driver, By.css('.currency-display__input'))
|
||||
const inputAmount = await findElement(driver, By.css('.unit-input__input'))
|
||||
await inputAddress.sendKeys('0x2f318C334780961FB129D2a6c30D0763d9a5C970')
|
||||
await inputAmount.sendKeys('50')
|
||||
|
||||
@ -823,8 +834,8 @@ describe('MetaMask', function () {
|
||||
await save.click()
|
||||
await driver.wait(until.stalenessOf(gasModal))
|
||||
|
||||
const gasFeeInputs = await findElements(driver, By.css('.confirm-detail-row__eth'))
|
||||
assert.equal(await gasFeeInputs[0].getText(), '♦ 0.0006')
|
||||
const gasFeeInputs = await findElements(driver, By.css('.confirm-detail-row__primary'))
|
||||
assert.equal(await gasFeeInputs[0].getText(), '0.0006')
|
||||
})
|
||||
|
||||
it('submits the transaction', async function () {
|
||||
@ -946,8 +957,8 @@ describe('MetaMask', function () {
|
||||
await save.click()
|
||||
await driver.wait(until.stalenessOf(gasModal))
|
||||
|
||||
const gasFeeInputs = await findElements(driver, By.css('.confirm-detail-row__eth'))
|
||||
assert.equal(await gasFeeInputs[0].getText(), '♦ 0.0006')
|
||||
const gasFeeInputs = await findElements(driver, By.css('.confirm-detail-row__primary'))
|
||||
assert.equal(await gasFeeInputs[0].getText(), '0.0006')
|
||||
})
|
||||
|
||||
it('submits the transaction', async function () {
|
||||
@ -991,7 +1002,7 @@ describe('MetaMask', function () {
|
||||
|
||||
describe('Add existing token using search', () => {
|
||||
it('clicks on the Add Token button', async () => {
|
||||
const addToken = await findElement(driver, By.xpath(`//button[contains(text(), 'Add Token')]`))
|
||||
const addToken = await findElement(driver, By.xpath(`//div[contains(text(), 'Add Token')]`))
|
||||
await addToken.click()
|
||||
await delay(regularDelayMs)
|
||||
})
|
||||
|
@ -6,5 +6,5 @@ set -o pipefail
|
||||
|
||||
export PATH="$PATH:./node_modules/.bin"
|
||||
|
||||
shell-parallel -s 'npm run ganache:start -- -b 2' -x 'sleep 5 && static-server test/e2e/beta/contract-test/ --port 8080' -x 'sleep 5 && mocha test/e2e/beta/metamask-beta-ui.spec'
|
||||
shell-parallel -s 'npm run ganache:start -- -d -b 2' -x 'sleep 5 && static-server test/e2e/beta/contract-test/ --port 8080' -x 'sleep 5 && mocha test/e2e/beta/from-import-beta-ui.spec'
|
||||
shell-parallel -s 'npm run ganache:start -- -b 2' -x 'sleep 5 && static-server test/e2e/beta/contract-test --port 8080' -x 'sleep 5 && mocha test/e2e/beta/metamask-beta-ui.spec'
|
||||
shell-parallel -s 'npm run ganache:start -- -d -b 2' -x 'sleep 5 && mocha test/e2e/beta/from-import-beta-ui.spec'
|
||||
|
20
test/e2e/beta/run-drizzle.sh
Executable file
20
test/e2e/beta/run-drizzle.sh
Executable file
@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
set -u
|
||||
set -o pipefail
|
||||
|
||||
export PATH="$PATH:./node_modules/.bin"
|
||||
|
||||
npm run ganache:start -- -b 2 >> /dev/null 2>&1 &
|
||||
sleep 5
|
||||
cd test/e2e/beta/
|
||||
rm -rf drizzle-test
|
||||
mkdir drizzle-test && cd drizzle-test
|
||||
npm install truffle
|
||||
truffle unbox drizzle
|
||||
echo "Deploying contracts for Drizzle test..."
|
||||
truffle compile && truffle migrate
|
||||
BROWSER=none npm start >> /dev/null 2>&1 &
|
||||
cd ../../../../
|
||||
mocha test/e2e/beta/drizzle.spec
|
@ -1,140 +0,0 @@
|
||||
const reactTriggerChange = require('react-trigger-change')
|
||||
const {
|
||||
timeout,
|
||||
queryAsync,
|
||||
findAsync,
|
||||
} = require('../../lib/util')
|
||||
|
||||
QUnit.module('Add token flow')
|
||||
|
||||
QUnit.test('successful add token flow', (assert) => {
|
||||
const done = assert.async()
|
||||
runAddTokenFlowTest(assert)
|
||||
.then(done)
|
||||
.catch(err => {
|
||||
assert.notOk(err, `Error was thrown: ${err.stack}`)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
async function runAddTokenFlowTest (assert, done) {
|
||||
const selectState = await queryAsync($, 'select')
|
||||
selectState.val('add token')
|
||||
reactTriggerChange(selectState[0])
|
||||
|
||||
// Used to set values on TextField input component
|
||||
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(
|
||||
window.HTMLInputElement.prototype, 'value'
|
||||
).set
|
||||
|
||||
// Check that no tokens have been added
|
||||
assert.ok($('.token-list-item').length === 0, 'no tokens added')
|
||||
|
||||
// Go to Add Token screen
|
||||
let addTokenButton = await queryAsync($, 'button.btn-primary.wallet-view__add-token-button')
|
||||
assert.ok(addTokenButton[0], 'add token button present')
|
||||
addTokenButton[0].click()
|
||||
|
||||
// Verify Add Token screen
|
||||
let addTokenWrapper = await queryAsync($, '.page-container')
|
||||
assert.ok(addTokenWrapper[0], 'add token wrapper renders')
|
||||
|
||||
let addTokenTitle = await queryAsync($, '.page-container__title')
|
||||
assert.equal(addTokenTitle[0].textContent, 'Add Tokens', 'add token title is correct')
|
||||
|
||||
// Cancel Add Token
|
||||
const cancelAddTokenButton = await queryAsync($, 'button.btn-default.btn--large.page-container__footer-button')
|
||||
assert.ok(cancelAddTokenButton[0], 'cancel add token button present')
|
||||
cancelAddTokenButton.click()
|
||||
|
||||
assert.ok($('.wallet-view')[0], 'cancelled and returned to account detail wallet view')
|
||||
|
||||
// Return to Add Token Screen
|
||||
addTokenButton = await queryAsync($, 'button.btn-primary.wallet-view__add-token-button')
|
||||
assert.ok(addTokenButton[0], 'add token button present')
|
||||
addTokenButton[0].click()
|
||||
|
||||
// Verify Add Token Screen
|
||||
addTokenWrapper = await queryAsync($, '.page-container')
|
||||
addTokenTitle = await queryAsync($, '.page-container__title')
|
||||
assert.ok(addTokenWrapper[0], 'add token wrapper renders')
|
||||
assert.equal(addTokenTitle[0].textContent, 'Add Tokens', 'add token title is correct')
|
||||
|
||||
// Search for token
|
||||
const searchInput = (await findAsync(addTokenWrapper, '#search-tokens'))[0]
|
||||
searchInput.focus()
|
||||
await timeout(1000)
|
||||
nativeInputValueSetter.call(searchInput, 'a')
|
||||
searchInput.dispatchEvent(new Event('input', { bubbles: true}))
|
||||
|
||||
// Click token to add
|
||||
const tokenWrapper = await queryAsync($, 'div.token-list__token')
|
||||
assert.ok(tokenWrapper[0], 'token found')
|
||||
const tokenImageProp = tokenWrapper.find('.token-list__token-icon').css('background-image')
|
||||
const tokenImageUrl = tokenImageProp.slice(5, -2)
|
||||
tokenWrapper[0].click()
|
||||
|
||||
// Click Next button
|
||||
const nextButton = await queryAsync($, 'button.btn-primary.btn--large')
|
||||
assert.equal(nextButton[0].textContent, 'Next', 'next button rendered')
|
||||
nextButton[0].click()
|
||||
|
||||
// Confirm Add token
|
||||
const confirmAddToken = await queryAsync($, '.confirm-add-token')
|
||||
assert.ok(confirmAddToken[0], 'confirm add token rendered')
|
||||
assert.ok($('button.btn-primary.btn--large')[0], 'confirm add token button found')
|
||||
$('button.btn-primary.btn--large')[0].click()
|
||||
|
||||
// Verify added token image
|
||||
let heroBalance = await queryAsync($, '.transaction-view-balance__balance-container')
|
||||
assert.ok(heroBalance, 'rendered hero balance')
|
||||
assert.ok(tokenImageUrl.indexOf(heroBalance.find('img').attr('src')) > -1, 'token added')
|
||||
|
||||
// Return to Add Token Screen
|
||||
addTokenButton = await queryAsync($, 'button.btn-primary.wallet-view__add-token-button')
|
||||
assert.ok(addTokenButton[0], 'add token button present')
|
||||
addTokenButton[0].click()
|
||||
|
||||
addTokenWrapper = await queryAsync($, '.page-container')
|
||||
const addTokenTabs = await queryAsync($, '.page-container__tab')
|
||||
assert.equal(addTokenTabs.length, 2, 'expected number of tabs')
|
||||
assert.equal(addTokenTabs[1].textContent, 'Custom Token', 'Custom Token tab present')
|
||||
assert.ok(addTokenTabs[1], 'add custom token tab present')
|
||||
addTokenTabs[1].click()
|
||||
await timeout(1000)
|
||||
|
||||
// Input token contract address
|
||||
const customInput = (await findAsync(addTokenWrapper, '#custom-address'))[0]
|
||||
customInput.focus()
|
||||
await timeout(1000)
|
||||
nativeInputValueSetter.call(customInput, '0x177af043D3A1Aed7cc5f2397C70248Fc6cDC056c')
|
||||
customInput.dispatchEvent(new Event('input', { bubbles: true}))
|
||||
|
||||
|
||||
// Click Next button
|
||||
// nextButton = await queryAsync($, 'button.btn-primary--lg')
|
||||
// assert.equal(nextButton[0].textContent, 'Next', 'next button rendered')
|
||||
// nextButton[0].click()
|
||||
|
||||
// // Verify symbol length error since contract address won't return symbol
|
||||
const errorMessage = await queryAsync($, '#custom-symbol-helper-text')
|
||||
assert.ok(errorMessage[0], 'error rendered')
|
||||
|
||||
$('button.btn-default.btn--large')[0].click()
|
||||
|
||||
// await timeout(100000)
|
||||
|
||||
// Confirm Add token
|
||||
// assert.equal(
|
||||
// $('.page-container__subtitle')[0].textContent,
|
||||
// 'Would you like to add these tokens?',
|
||||
// 'confirm add token rendered'
|
||||
// )
|
||||
// assert.ok($('button.btn-primary--lg')[0], 'confirm add token button found')
|
||||
// $('button.btn-primary--lg')[0].click()
|
||||
|
||||
// Verify added token image
|
||||
heroBalance = await queryAsync($, '.transaction-view-balance__balance-container')
|
||||
assert.ok(heroBalance, 'rendered hero balance')
|
||||
assert.ok(heroBalance.find('.identicon')[0], 'token added')
|
||||
}
|
@ -40,7 +40,7 @@ async function customizeGas (assert, price, limit, ethFee, usdFee) {
|
||||
const sendGasField = await queryAsync($, '.send-v2__gas-fee-display')
|
||||
|
||||
assert.equal(
|
||||
(await findAsync(sendGasField, '.currency-display__input-wrapper > input')).val(),
|
||||
(await findAsync(sendGasField, '.currency-display-component'))[0].textContent,
|
||||
ethFee,
|
||||
'send gas field should show customized gas total'
|
||||
)
|
||||
@ -94,12 +94,12 @@ async function runSendFlowTest (assert, done) {
|
||||
sendToDropdownList.children()[2].click()
|
||||
|
||||
const sendToAccountAddress = sendToFieldInput.val()
|
||||
assert.equal(sendToAccountAddress, '0x2f8d4a878cfa04a6e60d46362f5644deab66572d', 'send to dropdown selects the correct address')
|
||||
assert.equal(sendToAccountAddress, '0x2f8D4a878cFA04A6E60D46362f5644DeAb66572D', 'send to dropdown selects the correct address')
|
||||
|
||||
const sendAmountField = await queryAsync($, '.send-v2__form-row:eq(2)')
|
||||
sendAmountField.find('.currency-display')[0].click()
|
||||
sendAmountField.find('.unit-input')[0].click()
|
||||
|
||||
const sendAmountFieldInput = await findAsync(sendAmountField, '.currency-display__input')
|
||||
const sendAmountFieldInput = await findAsync(sendAmountField, '.unit-input__input')
|
||||
sendAmountFieldInput.val('5.1')
|
||||
reactTriggerChange(sendAmountField.find('input')[0])
|
||||
|
||||
@ -112,9 +112,9 @@ async function runSendFlowTest (assert, done) {
|
||||
errorMessage = $('.send-v2__error')
|
||||
assert.equal(errorMessage.length, 0, 'send should stop rendering amount error message after amount is corrected')
|
||||
|
||||
await customizeGas(assert, 0, 21000, '0', '$0.00 USD')
|
||||
await customizeGas(assert, 1, 21000, '0.000021', '$0.03 USD')
|
||||
await customizeGas(assert, 500, 60000, '0.03', '$36.03 USD')
|
||||
await customizeGas(assert, 0, 21000, '0 ETH', '$0.00 USD')
|
||||
await customizeGas(assert, 1, 21000, '0.000021 ETH', '$0.03 USD')
|
||||
await customizeGas(assert, 500, 60000, '0.03 ETH', '$36.03 USD')
|
||||
|
||||
const sendButton = await queryAsync($, 'button.btn-primary.btn--large.page-container__footer-button')
|
||||
assert.equal(sendButton[0].textContent, 'Next', 'next button rendered')
|
||||
@ -130,11 +130,11 @@ async function runSendFlowTest (assert, done) {
|
||||
const confirmToName = (await queryAsync($, '.sender-to-recipient__name')).last()
|
||||
assert.equal(confirmToName[0].textContent, 'Send Account 3', 'confirm screen should show correct to name')
|
||||
|
||||
const confirmScreenRowFiats = await queryAsync($, '.confirm-detail-row__fiat')
|
||||
const confirmScreenRowFiats = await queryAsync($, '.confirm-detail-row__secondary')
|
||||
const confirmScreenGas = confirmScreenRowFiats[0]
|
||||
assert.equal(confirmScreenGas.textContent, '$3.60', 'confirm screen should show correct gas')
|
||||
const confirmScreenTotal = confirmScreenRowFiats[1]
|
||||
assert.equal(confirmScreenTotal.textContent, '$2,405.36', 'confirm screen should show correct total')
|
||||
assert.equal(confirmScreenTotal.textContent, '$2,405.37', 'confirm screen should show correct total')
|
||||
|
||||
const confirmScreenBackButton = await queryAsync($, '.confirm-page-container-header__back-button')
|
||||
confirmScreenBackButton[0].click()
|
||||
@ -150,9 +150,9 @@ async function runSendFlowTest (assert, done) {
|
||||
sendToFieldInputInEdit.val('0xd85a4b6a394794842887b8284293d69163007bbb')
|
||||
|
||||
const sendAmountFieldInEdit = await queryAsync($, '.send-v2__form-row:eq(2)')
|
||||
sendAmountFieldInEdit.find('.currency-display')[0].click()
|
||||
sendAmountFieldInEdit.find('.unit-input')[0].click()
|
||||
|
||||
const sendAmountFieldInputInEdit = sendAmountFieldInEdit.find('.currency-display__input')
|
||||
const sendAmountFieldInputInEdit = sendAmountFieldInEdit.find('.unit-input__input')
|
||||
sendAmountFieldInputInEdit.val('1.0')
|
||||
reactTriggerChange(sendAmountFieldInputInEdit[0])
|
||||
|
||||
|
@ -479,5 +479,24 @@ describe('preferences controller', function () {
|
||||
assert.equal(preferencesController.store.getState().seedWords, 'foo bar baz')
|
||||
})
|
||||
})
|
||||
|
||||
describe('on updateFrequentRpcList', function () {
|
||||
it('should add custom RPC url to state', function () {
|
||||
preferencesController.addToFrequentRpcList('rpc_url')
|
||||
preferencesController.addToFrequentRpcList('http://localhost:8545')
|
||||
assert.deepEqual(preferencesController.store.getState().frequentRpcList, ['rpc_url'])
|
||||
preferencesController.addToFrequentRpcList('rpc_url')
|
||||
assert.deepEqual(preferencesController.store.getState().frequentRpcList, ['rpc_url'])
|
||||
})
|
||||
|
||||
it('should remove custom RPC url from state', function () {
|
||||
preferencesController.addToFrequentRpcList('rpc_url')
|
||||
assert.deepEqual(preferencesController.store.getState().frequentRpcList, ['rpc_url'])
|
||||
preferencesController.removeFromFrequentRpcList('other_rpc_url')
|
||||
preferencesController.removeFromFrequentRpcList('http://localhost:8545')
|
||||
preferencesController.removeFromFrequentRpcList('rpc_url')
|
||||
assert.deepEqual(preferencesController.store.getState().frequentRpcList, [])
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -158,9 +158,19 @@ describe('Transaction Controller', function () {
|
||||
})
|
||||
|
||||
describe('#addUnapprovedTransaction', function () {
|
||||
const selectedAddress = '0x1678a085c290ebd122dc42cba69373b5953b831d'
|
||||
|
||||
let getSelectedAddress
|
||||
beforeEach(function () {
|
||||
getSelectedAddress = sinon.stub(txController, 'getSelectedAddress').returns(selectedAddress)
|
||||
})
|
||||
|
||||
afterEach(function () {
|
||||
getSelectedAddress.restore()
|
||||
})
|
||||
|
||||
it('should add an unapproved transaction and return a valid txMeta', function (done) {
|
||||
txController.addUnapprovedTransaction({ from: '0x1678a085c290ebd122dc42cba69373b5953b831d' })
|
||||
txController.addUnapprovedTransaction({ from: selectedAddress })
|
||||
.then((txMeta) => {
|
||||
assert(('id' in txMeta), 'should have a id')
|
||||
assert(('time' in txMeta), 'should have a time stamp')
|
||||
@ -180,25 +190,37 @@ describe('Transaction Controller', function () {
|
||||
assert(txMetaFromEmit, 'txMeta is falsey')
|
||||
done()
|
||||
})
|
||||
txController.addUnapprovedTransaction({ from: '0x1678a085c290ebd122dc42cba69373b5953b831d' })
|
||||
txController.addUnapprovedTransaction({ from: selectedAddress })
|
||||
.catch(done)
|
||||
})
|
||||
|
||||
it('should fail if recipient is public', function (done) {
|
||||
txController.networkStore = new ObservableStore(1)
|
||||
txController.addUnapprovedTransaction({ from: '0x1678a085c290ebd122dc42cba69373b5953b831d', to: '0x0d1d4e623D10F9FBA5Db95830F7d3839406C6AF2' })
|
||||
txController.addUnapprovedTransaction({ from: selectedAddress, to: '0x0d1d4e623D10F9FBA5Db95830F7d3839406C6AF2' })
|
||||
.catch((err) => {
|
||||
if (err.message === 'Recipient is a public account') done()
|
||||
else done(err)
|
||||
})
|
||||
})
|
||||
|
||||
it('should fail if the from address isn\'t the selected address', function (done) {
|
||||
txController.addUnapprovedTransaction({from: '0x0d1d4e623D10F9FBA5Db95830F7d3839406C6AF2'})
|
||||
.then(function () {
|
||||
assert.fail('transaction should not have been added')
|
||||
done()
|
||||
})
|
||||
.catch(function () {
|
||||
assert.ok('pass')
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('should not fail if recipient is public but not on mainnet', function (done) {
|
||||
txController.once('newUnapprovedTx', (txMetaFromEmit) => {
|
||||
assert(txMetaFromEmit, 'txMeta is falsey')
|
||||
done()
|
||||
})
|
||||
txController.addUnapprovedTransaction({ from: '0x1678a085c290ebd122dc42cba69373b5953b831d', to: '0x0d1d4e623D10F9FBA5Db95830F7d3839406C6AF2' })
|
||||
txController.addUnapprovedTransaction({ from: selectedAddress, to: '0x0d1d4e623D10F9FBA5Db95830F7d3839406C6AF2' })
|
||||
.catch(done)
|
||||
})
|
||||
})
|
||||
|
@ -1,44 +0,0 @@
|
||||
const assert = require('assert')
|
||||
const h = require('react-hyperscript')
|
||||
const { createMockStore } = require('redux-test-utils')
|
||||
const { shallowWithStore } = require('../../lib/render-helpers')
|
||||
const BalanceComponent = require('../../../ui/app/components/balance-component')
|
||||
const mockState = {
|
||||
metamask: {
|
||||
accounts: { abc: {} },
|
||||
network: 1,
|
||||
selectedAddress: 'abc',
|
||||
},
|
||||
}
|
||||
|
||||
describe('BalanceComponent', function () {
|
||||
let balanceComponent
|
||||
let store
|
||||
let component
|
||||
beforeEach(function () {
|
||||
store = createMockStore(mockState)
|
||||
component = shallowWithStore(h(BalanceComponent), store)
|
||||
balanceComponent = component.dive()
|
||||
})
|
||||
|
||||
it('shows token balance and convert to fiat value based on conversion rate', function () {
|
||||
const formattedBalance = '1.23 ETH'
|
||||
|
||||
const tokenBalance = balanceComponent.instance().getTokenBalance(formattedBalance, false)
|
||||
const fiatDisplayNumber = balanceComponent.instance().getFiatDisplayNumber(formattedBalance, 2)
|
||||
|
||||
assert.equal('1.23 ETH', tokenBalance)
|
||||
assert.equal(2.46, fiatDisplayNumber)
|
||||
})
|
||||
|
||||
it('shows only the token balance when conversion rate is not available', function () {
|
||||
const formattedBalance = '1.23 ETH'
|
||||
|
||||
const tokenBalance = balanceComponent.instance().getTokenBalance(formattedBalance, false)
|
||||
const fiatDisplayNumber = balanceComponent.instance().getFiatDisplayNumber(formattedBalance, 0)
|
||||
|
||||
assert.equal('1.23 ETH', tokenBalance)
|
||||
assert.equal('N/A', fiatDisplayNumber)
|
||||
})
|
||||
|
||||
})
|
1468
test/unit/ui/app/actions.spec.js
Normal file
1468
test/unit/ui/app/actions.spec.js
Normal file
File diff suppressed because it is too large
Load Diff
@ -305,6 +305,12 @@ var actions = {
|
||||
updateFeatureFlags,
|
||||
UPDATE_FEATURE_FLAGS: 'UPDATE_FEATURE_FLAGS',
|
||||
|
||||
// Preferences
|
||||
setPreference,
|
||||
updatePreferences,
|
||||
UPDATE_PREFERENCES: 'UPDATE_PREFERENCES',
|
||||
setUseETHAsPrimaryCurrencyPreference,
|
||||
|
||||
setMouseUserState,
|
||||
SET_MOUSE_USER_STATE: 'SET_MOUSE_USER_STATE',
|
||||
|
||||
@ -1762,7 +1768,7 @@ function markNoticeRead (notice) {
|
||||
background.markNoticeRead(notice, (err, notice) => {
|
||||
dispatch(actions.hideLoadingIndication())
|
||||
if (err) {
|
||||
dispatch(actions.displayWarning(err))
|
||||
dispatch(actions.displayWarning(err.message))
|
||||
return reject(err)
|
||||
}
|
||||
|
||||
@ -1852,7 +1858,7 @@ function setProviderType (type) {
|
||||
background.setProviderType(type, (err, result) => {
|
||||
if (err) {
|
||||
log.error(err)
|
||||
return dispatch(self.displayWarning('Had a problem changing networks!'))
|
||||
return dispatch(actions.displayWarning('Had a problem changing networks!'))
|
||||
}
|
||||
dispatch(actions.updateProviderType(type))
|
||||
dispatch(actions.setSelectedToken())
|
||||
@ -1874,7 +1880,7 @@ function setRpcTarget (newRpc) {
|
||||
background.setCustomRpc(newRpc, (err, result) => {
|
||||
if (err) {
|
||||
log.error(err)
|
||||
return dispatch(self.displayWarning('Had a problem changing networks!'))
|
||||
return dispatch(actions.displayWarning('Had a problem changing networks!'))
|
||||
}
|
||||
dispatch(actions.setSelectedToken())
|
||||
})
|
||||
@ -2298,6 +2304,36 @@ function updateFeatureFlags (updatedFeatureFlags) {
|
||||
}
|
||||
}
|
||||
|
||||
function setPreference (preference, value) {
|
||||
return dispatch => {
|
||||
dispatch(actions.showLoadingIndication())
|
||||
return new Promise((resolve, reject) => {
|
||||
background.setPreference(preference, value, (err, updatedPreferences) => {
|
||||
dispatch(actions.hideLoadingIndication())
|
||||
|
||||
if (err) {
|
||||
dispatch(actions.displayWarning(err.message))
|
||||
return reject(err)
|
||||
}
|
||||
|
||||
dispatch(actions.updatePreferences(updatedPreferences))
|
||||
resolve(updatedPreferences)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function updatePreferences (value) {
|
||||
return {
|
||||
type: actions.UPDATE_PREFERENCES,
|
||||
value,
|
||||
}
|
||||
}
|
||||
|
||||
function setUseETHAsPrimaryCurrencyPreference (value) {
|
||||
return setPreference('useETHAsPrimaryCurrency', value)
|
||||
}
|
||||
|
||||
function setNetworkNonce (networkNonce) {
|
||||
return {
|
||||
type: actions.SET_NETWORK_NONCE,
|
||||
@ -2309,6 +2345,10 @@ function updateNetworkNonce (address) {
|
||||
return (dispatch) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
global.ethQuery.getTransactionCount(address, (err, data) => {
|
||||
if (err) {
|
||||
dispatch(actions.displayWarning(err.message))
|
||||
return reject(err)
|
||||
}
|
||||
dispatch(setNetworkNonce(data))
|
||||
resolve(data)
|
||||
})
|
||||
@ -2396,7 +2436,7 @@ function setUseBlockie (val) {
|
||||
function updateCurrentLocale (key) {
|
||||
return (dispatch) => {
|
||||
dispatch(actions.showLoadingIndication())
|
||||
fetchLocale(key)
|
||||
return fetchLocale(key)
|
||||
.then((localeMessages) => {
|
||||
log.debug(`background.setCurrentLocale`)
|
||||
background.setCurrentLocale(key, (err) => {
|
||||
|
@ -8,11 +8,11 @@ const h = require('react-hyperscript')
|
||||
const actions = require('../../actions')
|
||||
const { Menu, Item, Divider, CloseArea } = require('../dropdowns/components/menu')
|
||||
const Identicon = require('../identicon')
|
||||
const { formatBalance } = require('../../util')
|
||||
const { ENVIRONMENT_TYPE_POPUP } = require('../../../../app/scripts/lib/enums')
|
||||
const { getEnvironmentType } = require('../../../../app/scripts/lib/util')
|
||||
const Tooltip = require('../tooltip')
|
||||
|
||||
import UserPreferencedCurrencyDisplay from '../user-preferenced-currency-display'
|
||||
import { PRIMARY } from '../../constants/common'
|
||||
|
||||
const {
|
||||
SETTINGS_ROUTE,
|
||||
@ -163,7 +163,6 @@ AccountMenu.prototype.renderAccounts = function () {
|
||||
const isSelected = identity.address === selectedAddress
|
||||
|
||||
const balanceValue = accounts[address] ? accounts[address].balance : ''
|
||||
const formattedBalance = balanceValue ? formatBalance(balanceValue, 6) : '...'
|
||||
const simpleAddress = identity.address.substring(2).toLowerCase()
|
||||
|
||||
const keyring = keyrings.find((kr) => {
|
||||
@ -189,7 +188,11 @@ AccountMenu.prototype.renderAccounts = function () {
|
||||
|
||||
h('div.account-menu__account-info', [
|
||||
h('div.account-menu__name', identity.name || ''),
|
||||
h('div.account-menu__balance', formattedBalance),
|
||||
h(UserPreferencedCurrencyDisplay, {
|
||||
className: 'account-menu__balance',
|
||||
value: balanceValue,
|
||||
type: PRIMARY,
|
||||
}),
|
||||
]),
|
||||
|
||||
this.renderKeyringType(keyring),
|
||||
|
@ -0,0 +1,34 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import React, {PureComponent} from 'react'
|
||||
|
||||
export default class AddTokenButton extends PureComponent {
|
||||
static contextTypes = {
|
||||
t: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
onClick: () => {},
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
onClick: PropTypes.func,
|
||||
}
|
||||
|
||||
render () {
|
||||
const { t } = this.context
|
||||
const { onClick } = this.props
|
||||
|
||||
return (
|
||||
<div className="add-token-button">
|
||||
<h1 className="add-token-button__help-header">{t('missingYourTokens')}</h1>
|
||||
<p className="add-token-button__help-desc">{t('clickToAdd', [t('addToken')])}</p>
|
||||
<div
|
||||
className="add-token-button__button"
|
||||
onClick={onClick}
|
||||
>
|
||||
{t('addToken')}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
1
ui/app/components/add-token-button/index.js
Normal file
1
ui/app/components/add-token-button/index.js
Normal file
@ -0,0 +1 @@
|
||||
export { default } from './add-token-button.component'
|
26
ui/app/components/add-token-button/index.scss
Normal file
26
ui/app/components/add-token-button/index.scss
Normal file
@ -0,0 +1,26 @@
|
||||
.add-token-button {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
color: lighten($scorpion, 25%);
|
||||
width: 185px;
|
||||
margin: 36px auto;
|
||||
text-align: center;
|
||||
|
||||
&__help-header {
|
||||
font-weight: bold;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
&__help-desc {
|
||||
font-size: 0.75rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
&__button {
|
||||
font-size: 0.75rem;
|
||||
margin: 1rem;
|
||||
text-transform: uppercase;
|
||||
color: $curious-blue;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
@ -4,10 +4,11 @@ const h = require('react-hyperscript')
|
||||
const inherits = require('util').inherits
|
||||
const TokenBalance = require('./token-balance')
|
||||
const Identicon = require('./identicon')
|
||||
import CurrencyDisplay from './currency-display'
|
||||
import UserPreferencedCurrencyDisplay from './user-preferenced-currency-display'
|
||||
import { PRIMARY, SECONDARY } from '../constants/common'
|
||||
const { getAssetImages, conversionRateSelector, getCurrentCurrency} = require('../selectors')
|
||||
|
||||
const { formatBalance, generateBalanceObject } = require('../util')
|
||||
const { formatBalance } = require('../util')
|
||||
|
||||
module.exports = connect(mapStateToProps)(BalanceComponent)
|
||||
|
||||
@ -65,7 +66,7 @@ BalanceComponent.prototype.renderTokenBalance = function () {
|
||||
|
||||
BalanceComponent.prototype.renderBalance = function () {
|
||||
const props = this.props
|
||||
const { shorten, account } = props
|
||||
const { account } = props
|
||||
const balanceValue = account && account.balance
|
||||
const needsParse = 'needsParse' in props ? props.needsParse : true
|
||||
const formattedBalance = balanceValue ? formatBalance(balanceValue, 6, needsParse) : '...'
|
||||
@ -80,25 +81,20 @@ BalanceComponent.prototype.renderBalance = function () {
|
||||
}
|
||||
|
||||
return h('div.flex-column.balance-display', {}, [
|
||||
h('div.token-amount', {
|
||||
style: {},
|
||||
}, this.getTokenBalance(formattedBalance, shorten)),
|
||||
|
||||
showFiat && h(CurrencyDisplay, {
|
||||
h('div.token-amount', {}, h(UserPreferencedCurrencyDisplay, {
|
||||
value: balanceValue,
|
||||
type: PRIMARY,
|
||||
ethNumberOfDecimals: 3,
|
||||
})),
|
||||
|
||||
showFiat && h(UserPreferencedCurrencyDisplay, {
|
||||
value: balanceValue,
|
||||
type: SECONDARY,
|
||||
ethNumberOfDecimals: 3,
|
||||
}),
|
||||
])
|
||||
}
|
||||
|
||||
BalanceComponent.prototype.getTokenBalance = function (formattedBalance, shorten) {
|
||||
const balanceObj = generateBalanceObject(formattedBalance, shorten ? 1 : 3)
|
||||
|
||||
const balanceValue = shorten ? balanceObj.shortBalance : balanceObj.balance
|
||||
const label = balanceObj.label
|
||||
|
||||
return `${balanceValue} ${label}`
|
||||
}
|
||||
|
||||
BalanceComponent.prototype.getFiatDisplayNumber = function (formattedBalance, conversionRate) {
|
||||
if (formattedBalance === 'None') return formattedBalance
|
||||
if (conversionRate === 0) return 'N/A'
|
||||
|
@ -1,16 +1,19 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import classnames from 'classnames'
|
||||
import UserPreferencedCurrencyDisplay from '../../user-preferenced-currency-display'
|
||||
import { PRIMARY, SECONDARY } from '../../../constants/common'
|
||||
|
||||
const ConfirmDetailRow = props => {
|
||||
const {
|
||||
label,
|
||||
fiatText,
|
||||
ethText,
|
||||
primaryText,
|
||||
secondaryText,
|
||||
onHeaderClick,
|
||||
fiatTextColor,
|
||||
primaryValueTextColor,
|
||||
headerText,
|
||||
headerTextClassName,
|
||||
value,
|
||||
} = props
|
||||
|
||||
return (
|
||||
@ -25,28 +28,57 @@ const ConfirmDetailRow = props => {
|
||||
>
|
||||
{ headerText }
|
||||
</div>
|
||||
<div
|
||||
className="confirm-detail-row__fiat"
|
||||
style={{ color: fiatTextColor }}
|
||||
>
|
||||
{ fiatText }
|
||||
</div>
|
||||
<div className="confirm-detail-row__eth">
|
||||
{ ethText }
|
||||
</div>
|
||||
{
|
||||
primaryText
|
||||
? (
|
||||
<div
|
||||
className="confirm-detail-row__primary"
|
||||
style={{ color: primaryValueTextColor }}
|
||||
>
|
||||
{ primaryText }
|
||||
</div>
|
||||
) : (
|
||||
<UserPreferencedCurrencyDisplay
|
||||
className="confirm-detail-row__primary"
|
||||
type={PRIMARY}
|
||||
value={value}
|
||||
showEthLogo
|
||||
ethLogoHeight="18"
|
||||
style={{ color: primaryValueTextColor }}
|
||||
hideLabel
|
||||
/>
|
||||
)
|
||||
}
|
||||
{
|
||||
secondaryText
|
||||
? (
|
||||
<div className="confirm-detail-row__secondary">
|
||||
{ secondaryText }
|
||||
</div>
|
||||
) : (
|
||||
<UserPreferencedCurrencyDisplay
|
||||
className="confirm-detail-row__secondary"
|
||||
type={SECONDARY}
|
||||
value={value}
|
||||
showEthLogo
|
||||
hideLabel
|
||||
/>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
ConfirmDetailRow.propTypes = {
|
||||
label: PropTypes.string,
|
||||
fiatText: PropTypes.string,
|
||||
ethText: PropTypes.string,
|
||||
fiatTextColor: PropTypes.string,
|
||||
onHeaderClick: PropTypes.func,
|
||||
headerText: PropTypes.string,
|
||||
headerTextClassName: PropTypes.string,
|
||||
label: PropTypes.string,
|
||||
onHeaderClick: PropTypes.func,
|
||||
primaryValueTextColor: PropTypes.string,
|
||||
primaryText: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
|
||||
secondaryText: PropTypes.string,
|
||||
value: PropTypes.string,
|
||||
}
|
||||
|
||||
export default ConfirmDetailRow
|
||||
|
@ -18,18 +18,14 @@
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
&__fiat {
|
||||
&__primary {
|
||||
font-size: 1.5rem;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
&__eth {
|
||||
&__secondary {
|
||||
color: $oslo-gray;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
&__header-text {
|
||||
|
@ -12,17 +12,19 @@ describe('Confirm Detail Row Component', function () {
|
||||
let wrapper
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = shallow(<ConfirmDetailRow
|
||||
errorType={'mockErrorType'}
|
||||
label={'mockLabel'}
|
||||
showError={false}
|
||||
fiatText = {'mockFiatText'}
|
||||
ethText = {'mockEthText'}
|
||||
fiatTextColor= {'mockColor'}
|
||||
onHeaderClick= {propsMethodSpies.onHeaderClick}
|
||||
headerText = {'mockHeaderText'}
|
||||
headerTextClassName = {'mockHeaderClass'}
|
||||
/>)
|
||||
wrapper = shallow(
|
||||
<ConfirmDetailRow
|
||||
errorType={'mockErrorType'}
|
||||
label={'mockLabel'}
|
||||
showError={false}
|
||||
primaryText = {'mockFiatText'}
|
||||
secondaryText = {'mockEthText'}
|
||||
primaryValueTextColor= {'mockColor'}
|
||||
onHeaderClick= {propsMethodSpies.onHeaderClick}
|
||||
headerText = {'mockHeaderText'}
|
||||
headerTextClassName = {'mockHeaderClass'}
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
||||
describe('render', () => {
|
||||
@ -38,16 +40,16 @@ describe('Confirm Detail Row Component', function () {
|
||||
assert.equal(wrapper.find('.confirm-detail-row__details > .confirm-detail-row__header-text').childAt(0).text(), 'mockHeaderText')
|
||||
})
|
||||
|
||||
it('should render the fiatText as a child of the confirm-detail-row__fiat', () => {
|
||||
assert.equal(wrapper.find('.confirm-detail-row__details > .confirm-detail-row__fiat').childAt(0).text(), 'mockFiatText')
|
||||
it('should render the primaryText as a child of the confirm-detail-row__primary', () => {
|
||||
assert.equal(wrapper.find('.confirm-detail-row__details > .confirm-detail-row__primary').childAt(0).text(), 'mockFiatText')
|
||||
})
|
||||
|
||||
it('should render the ethText as a child of the confirm-detail-row__eth', () => {
|
||||
assert.equal(wrapper.find('.confirm-detail-row__details > .confirm-detail-row__eth').childAt(0).text(), 'mockEthText')
|
||||
it('should render the ethText as a child of the confirm-detail-row__secondary', () => {
|
||||
assert.equal(wrapper.find('.confirm-detail-row__details > .confirm-detail-row__secondary').childAt(0).text(), 'mockEthText')
|
||||
})
|
||||
|
||||
it('should set the fiatTextColor on confirm-detail-row__fiat', () => {
|
||||
assert.equal(wrapper.find('.confirm-detail-row__fiat').props().style.color, 'mockColor')
|
||||
it('should set the fiatTextColor on confirm-detail-row__primary', () => {
|
||||
assert.equal(wrapper.find('.confirm-detail-row__primary').props().style.color, 'mockColor')
|
||||
})
|
||||
|
||||
it('should assure the confirm-detail-row__header-text classname is correct', () => {
|
||||
@ -58,7 +60,5 @@ describe('Confirm Detail Row Component', function () {
|
||||
wrapper.find('.confirm-detail-row__header-text').props().onClick()
|
||||
assert.equal(assert.equal(propsMethodSpies.onHeaderClick.callCount, 1))
|
||||
})
|
||||
|
||||
|
||||
})
|
||||
})
|
||||
|
@ -17,9 +17,10 @@ export default class ConfirmPageContainerContent extends Component {
|
||||
nonce: PropTypes.string,
|
||||
assetImage: PropTypes.string,
|
||||
subtitle: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
subtitleComponent: PropTypes.node,
|
||||
summaryComponent: PropTypes.node,
|
||||
title: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
titleComponent: PropTypes.func,
|
||||
titleComponent: PropTypes.node,
|
||||
warning: PropTypes.string,
|
||||
}
|
||||
|
||||
@ -54,7 +55,9 @@ export default class ConfirmPageContainerContent extends Component {
|
||||
errorKey,
|
||||
errorMessage,
|
||||
title,
|
||||
titleComponent,
|
||||
subtitle,
|
||||
subtitleComponent,
|
||||
hideSubtitle,
|
||||
identiconAddress,
|
||||
nonce,
|
||||
@ -80,7 +83,9 @@ export default class ConfirmPageContainerContent extends Component {
|
||||
})}
|
||||
action={action}
|
||||
title={title}
|
||||
titleComponent={titleComponent}
|
||||
subtitle={subtitle}
|
||||
subtitleComponent={subtitleComponent}
|
||||
hideSubtitle={hideSubtitle}
|
||||
identiconAddress={identiconAddress}
|
||||
nonce={nonce}
|
||||
|
@ -4,7 +4,18 @@ import classnames from 'classnames'
|
||||
import Identicon from '../../../identicon'
|
||||
|
||||
const ConfirmPageContainerSummary = props => {
|
||||
const { action, title, subtitle, hideSubtitle, className, identiconAddress, nonce, assetImage } = props
|
||||
const {
|
||||
action,
|
||||
title,
|
||||
titleComponent,
|
||||
subtitle,
|
||||
subtitleComponent,
|
||||
hideSubtitle,
|
||||
className,
|
||||
identiconAddress,
|
||||
nonce,
|
||||
assetImage,
|
||||
} = props
|
||||
|
||||
return (
|
||||
<div className={classnames('confirm-page-container-summary', className)}>
|
||||
@ -32,12 +43,12 @@ const ConfirmPageContainerSummary = props => {
|
||||
)
|
||||
}
|
||||
<div className="confirm-page-container-summary__title-text">
|
||||
{ title }
|
||||
{ titleComponent || title }
|
||||
</div>
|
||||
</div>
|
||||
{
|
||||
hideSubtitle || <div className="confirm-page-container-summary__subtitle">
|
||||
{ subtitle }
|
||||
{ subtitleComponent || subtitle }
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
@ -47,7 +58,9 @@ const ConfirmPageContainerSummary = props => {
|
||||
ConfirmPageContainerSummary.propTypes = {
|
||||
action: PropTypes.string,
|
||||
title: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
titleComponent: PropTypes.node,
|
||||
subtitle: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
subtitleComponent: PropTypes.node,
|
||||
hideSubtitle: PropTypes.bool,
|
||||
className: PropTypes.string,
|
||||
identiconAddress: PropTypes.string,
|
||||
|
@ -16,8 +16,9 @@ export default class ConfirmPageContainer extends Component {
|
||||
onEdit: PropTypes.func,
|
||||
showEdit: PropTypes.bool,
|
||||
subtitle: PropTypes.string,
|
||||
subtitleComponent: PropTypes.node,
|
||||
title: PropTypes.string,
|
||||
titleComponent: PropTypes.func,
|
||||
titleComponent: PropTypes.node,
|
||||
// Sender to Recipient
|
||||
fromAddress: PropTypes.string,
|
||||
fromName: PropTypes.string,
|
||||
@ -65,6 +66,7 @@ export default class ConfirmPageContainer extends Component {
|
||||
title,
|
||||
titleComponent,
|
||||
subtitle,
|
||||
subtitleComponent,
|
||||
hideSubtitle,
|
||||
summaryComponent,
|
||||
detailsComponent,
|
||||
@ -101,6 +103,7 @@ export default class ConfirmPageContainer extends Component {
|
||||
title={title}
|
||||
titleComponent={titleComponent}
|
||||
subtitle={subtitle}
|
||||
subtitleComponent={subtitleComponent}
|
||||
hideSubtitle={hideSubtitle}
|
||||
summaryComponent={summaryComponent}
|
||||
detailsComponent={detailsComponent}
|
||||
|
@ -1,5 +1,6 @@
|
||||
import React, { PureComponent } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import classnames from 'classnames'
|
||||
import { ETH, GWEI } from '../../constants/common'
|
||||
|
||||
export default class CurrencyDisplay extends PureComponent {
|
||||
@ -7,6 +8,8 @@ export default class CurrencyDisplay extends PureComponent {
|
||||
className: PropTypes.string,
|
||||
displayValue: PropTypes.string,
|
||||
prefix: PropTypes.string,
|
||||
prefixComponent: PropTypes.node,
|
||||
style: PropTypes.object,
|
||||
// Used in container
|
||||
currency: PropTypes.oneOf([ETH]),
|
||||
denomination: PropTypes.oneOf([GWEI]),
|
||||
@ -16,15 +19,17 @@ export default class CurrencyDisplay extends PureComponent {
|
||||
}
|
||||
|
||||
render () {
|
||||
const { className, displayValue, prefix } = this.props
|
||||
const { className, displayValue, prefix, prefixComponent, style } = this.props
|
||||
const text = `${prefix || ''}${displayValue}`
|
||||
|
||||
return (
|
||||
<div
|
||||
className={className}
|
||||
className={classnames('currency-display-component', className)}
|
||||
style={style}
|
||||
title={text}
|
||||
>
|
||||
{ text }
|
||||
{ prefixComponent}
|
||||
<span className="currency-display-component__text">{ text }</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -2,10 +2,26 @@ import { connect } from 'react-redux'
|
||||
import CurrencyDisplay from './currency-display.component'
|
||||
import { getValueFromWeiHex, formatCurrency } from '../../helpers/confirm-transaction/util'
|
||||
|
||||
const mapStateToProps = (state, ownProps) => {
|
||||
const { value, numberOfDecimals = 2, currency, denomination, hideLabel } = ownProps
|
||||
const mapStateToProps = state => {
|
||||
const { metamask: { currentCurrency, conversionRate } } = state
|
||||
|
||||
return {
|
||||
currentCurrency,
|
||||
conversionRate,
|
||||
}
|
||||
}
|
||||
|
||||
const mergeProps = (stateProps, dispatchProps, ownProps) => {
|
||||
const { currentCurrency, conversionRate, ...restStateProps } = stateProps
|
||||
const {
|
||||
value,
|
||||
numberOfDecimals = 2,
|
||||
currency,
|
||||
denomination,
|
||||
hideLabel,
|
||||
...restOwnProps
|
||||
} = ownProps
|
||||
|
||||
const toCurrency = currency || currentCurrency
|
||||
const convertedValue = getValueFromWeiHex({
|
||||
value, toCurrency, conversionRate, numberOfDecimals, toDenomination: denomination,
|
||||
@ -14,8 +30,11 @@ const mapStateToProps = (state, ownProps) => {
|
||||
const displayValue = hideLabel ? formattedValue : `${formattedValue} ${toCurrency.toUpperCase()}`
|
||||
|
||||
return {
|
||||
...restStateProps,
|
||||
...dispatchProps,
|
||||
...restOwnProps,
|
||||
displayValue,
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(CurrencyDisplay)
|
||||
export default connect(mapStateToProps, null, mergeProps)(CurrencyDisplay)
|
||||
|
10
ui/app/components/currency-display/index.scss
Normal file
10
ui/app/components/currency-display/index.scss
Normal file
@ -0,0 +1,10 @@
|
||||
.currency-display-component {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&__text {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
@ -1,12 +1,13 @@
|
||||
import assert from 'assert'
|
||||
import proxyquire from 'proxyquire'
|
||||
|
||||
let mapStateToProps
|
||||
let mapStateToProps, mergeProps
|
||||
|
||||
proxyquire('../currency-display.container.js', {
|
||||
'react-redux': {
|
||||
connect: ms => {
|
||||
connect: (ms, md, mp) => {
|
||||
mapStateToProps = ms
|
||||
mergeProps = mp
|
||||
return () => ({})
|
||||
},
|
||||
},
|
||||
@ -22,6 +23,20 @@ describe('CurrencyDisplay container', () => {
|
||||
},
|
||||
}
|
||||
|
||||
assert.deepEqual(mapStateToProps(mockState), {
|
||||
conversionRate: 280.45,
|
||||
currentCurrency: 'usd',
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('mergeProps()', () => {
|
||||
it('should return the correct props', () => {
|
||||
const mockStateProps = {
|
||||
conversionRate: 280.45,
|
||||
currentCurrency: 'usd',
|
||||
}
|
||||
|
||||
const tests = [
|
||||
{
|
||||
props: {
|
||||
@ -98,7 +113,7 @@ describe('CurrencyDisplay container', () => {
|
||||
]
|
||||
|
||||
tests.forEach(({ props, result }) => {
|
||||
assert.deepEqual(mapStateToProps(mockState, props), result)
|
||||
assert.deepEqual(mergeProps(mockStateProps, {}, { ...props }), result)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
120
ui/app/components/currency-input/currency-input.component.js
Normal file
120
ui/app/components/currency-input/currency-input.component.js
Normal file
@ -0,0 +1,120 @@
|
||||
import React, { PureComponent } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import UnitInput from '../unit-input'
|
||||
import CurrencyDisplay from '../currency-display'
|
||||
import { getValueFromWeiHex, getWeiHexFromDecimalValue } from '../../helpers/conversions.util'
|
||||
import { ETH } from '../../constants/common'
|
||||
|
||||
/**
|
||||
* Component that allows user to enter currency values as a number, and props receive a converted
|
||||
* hex value in WEI. props.value, used as a default or forced value, should be a hex value, which
|
||||
* gets converted into a decimal value depending on the currency (ETH or Fiat).
|
||||
*/
|
||||
export default class CurrencyInput extends PureComponent {
|
||||
static propTypes = {
|
||||
conversionRate: PropTypes.number,
|
||||
currentCurrency: PropTypes.string,
|
||||
onChange: PropTypes.func,
|
||||
onBlur: PropTypes.func,
|
||||
suffix: PropTypes.string,
|
||||
useFiat: PropTypes.bool,
|
||||
value: PropTypes.string,
|
||||
}
|
||||
|
||||
constructor (props) {
|
||||
super(props)
|
||||
|
||||
const { value: hexValue } = props
|
||||
const decimalValue = hexValue ? this.getDecimalValue(props) : 0
|
||||
|
||||
this.state = {
|
||||
decimalValue,
|
||||
hexValue,
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate (prevProps) {
|
||||
const { value: prevPropsHexValue } = prevProps
|
||||
const { value: propsHexValue } = this.props
|
||||
const { hexValue: stateHexValue } = this.state
|
||||
|
||||
if (prevPropsHexValue !== propsHexValue && propsHexValue !== stateHexValue) {
|
||||
const decimalValue = this.getDecimalValue(this.props)
|
||||
this.setState({ hexValue: propsHexValue, decimalValue })
|
||||
}
|
||||
}
|
||||
|
||||
getDecimalValue (props) {
|
||||
const { value: hexValue, useFiat, currentCurrency, conversionRate } = props
|
||||
const decimalValueString = useFiat
|
||||
? getValueFromWeiHex({
|
||||
value: hexValue, toCurrency: currentCurrency, conversionRate, numberOfDecimals: 2,
|
||||
})
|
||||
: getValueFromWeiHex({
|
||||
value: hexValue, toCurrency: ETH, numberOfDecimals: 6,
|
||||
})
|
||||
|
||||
return Number(decimalValueString) || 0
|
||||
}
|
||||
|
||||
handleChange = decimalValue => {
|
||||
const { useFiat, currentCurrency: fromCurrency, conversionRate, onChange } = this.props
|
||||
|
||||
const hexValue = useFiat
|
||||
? getWeiHexFromDecimalValue({
|
||||
value: decimalValue, fromCurrency, conversionRate, invertConversionRate: true,
|
||||
})
|
||||
: getWeiHexFromDecimalValue({
|
||||
value: decimalValue, fromCurrency: ETH, fromDenomination: ETH, conversionRate,
|
||||
})
|
||||
|
||||
this.setState({ hexValue, decimalValue })
|
||||
onChange(hexValue)
|
||||
}
|
||||
|
||||
handleBlur = () => {
|
||||
this.props.onBlur(this.state.hexValue)
|
||||
}
|
||||
|
||||
renderConversionComponent () {
|
||||
const { useFiat, currentCurrency } = this.props
|
||||
const { hexValue } = this.state
|
||||
let currency, numberOfDecimals
|
||||
|
||||
if (useFiat) {
|
||||
// Display ETH
|
||||
currency = ETH
|
||||
numberOfDecimals = 6
|
||||
} else {
|
||||
// Display Fiat
|
||||
currency = currentCurrency
|
||||
numberOfDecimals = 2
|
||||
}
|
||||
|
||||
return (
|
||||
<CurrencyDisplay
|
||||
className="currency-input__conversion-component"
|
||||
currency={currency}
|
||||
value={hexValue}
|
||||
numberOfDecimals={numberOfDecimals}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
render () {
|
||||
const { suffix, ...restProps } = this.props
|
||||
const { decimalValue } = this.state
|
||||
|
||||
return (
|
||||
<UnitInput
|
||||
{...restProps}
|
||||
suffix={suffix}
|
||||
onChange={this.handleChange}
|
||||
onBlur={this.handleBlur}
|
||||
value={decimalValue}
|
||||
>
|
||||
{ this.renderConversionComponent() }
|
||||
</UnitInput>
|
||||
)
|
||||
}
|
||||
}
|
27
ui/app/components/currency-input/currency-input.container.js
Normal file
27
ui/app/components/currency-input/currency-input.container.js
Normal file
@ -0,0 +1,27 @@
|
||||
import { connect } from 'react-redux'
|
||||
import CurrencyInput from './currency-input.component'
|
||||
import { ETH } from '../../constants/common'
|
||||
|
||||
const mapStateToProps = state => {
|
||||
const { metamask: { currentCurrency, conversionRate } } = state
|
||||
|
||||
return {
|
||||
currentCurrency,
|
||||
conversionRate,
|
||||
}
|
||||
}
|
||||
|
||||
const mergeProps = (stateProps, dispatchProps, ownProps) => {
|
||||
const { currentCurrency } = stateProps
|
||||
const { useFiat } = ownProps
|
||||
const suffix = useFiat ? currentCurrency.toUpperCase() : ETH
|
||||
|
||||
return {
|
||||
...stateProps,
|
||||
...dispatchProps,
|
||||
...ownProps,
|
||||
suffix,
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, null, mergeProps)(CurrencyInput)
|
1
ui/app/components/currency-input/index.js
Normal file
1
ui/app/components/currency-input/index.js
Normal file
@ -0,0 +1 @@
|
||||
export { default } from './currency-input.container'
|
7
ui/app/components/currency-input/index.scss
Normal file
7
ui/app/components/currency-input/index.scss
Normal file
@ -0,0 +1,7 @@
|
||||
.currency-input {
|
||||
&__conversion-component {
|
||||
font-size: 12px;
|
||||
line-height: 12px;
|
||||
padding-left: 1px;
|
||||
}
|
||||
}
|
@ -0,0 +1,239 @@
|
||||
import React from 'react'
|
||||
import assert from 'assert'
|
||||
import { shallow, mount } from 'enzyme'
|
||||
import sinon from 'sinon'
|
||||
import { Provider } from 'react-redux'
|
||||
import configureMockStore from 'redux-mock-store'
|
||||
import CurrencyInput from '../currency-input.component'
|
||||
import UnitInput from '../../unit-input'
|
||||
import CurrencyDisplay from '../../currency-display'
|
||||
|
||||
describe('CurrencyInput Component', () => {
|
||||
describe('rendering', () => {
|
||||
it('should render properly without a suffix', () => {
|
||||
const wrapper = shallow(
|
||||
<CurrencyInput />
|
||||
)
|
||||
|
||||
assert.ok(wrapper)
|
||||
assert.equal(wrapper.find(UnitInput).length, 1)
|
||||
})
|
||||
|
||||
it('should render properly with a suffix', () => {
|
||||
const mockStore = {
|
||||
metamask: {
|
||||
currentCurrency: 'usd',
|
||||
conversionRate: 231.06,
|
||||
},
|
||||
}
|
||||
const store = configureMockStore()(mockStore)
|
||||
|
||||
const wrapper = mount(
|
||||
<Provider store={store}>
|
||||
<CurrencyInput
|
||||
suffix="ETH"
|
||||
/>
|
||||
</Provider>
|
||||
)
|
||||
|
||||
assert.ok(wrapper)
|
||||
assert.equal(wrapper.find('.unit-input__suffix').length, 1)
|
||||
assert.equal(wrapper.find('.unit-input__suffix').text(), 'ETH')
|
||||
assert.equal(wrapper.find(CurrencyDisplay).length, 1)
|
||||
})
|
||||
|
||||
it('should render properly with an ETH value', () => {
|
||||
const mockStore = {
|
||||
metamask: {
|
||||
currentCurrency: 'usd',
|
||||
conversionRate: 231.06,
|
||||
},
|
||||
}
|
||||
const store = configureMockStore()(mockStore)
|
||||
|
||||
const wrapper = mount(
|
||||
<Provider store={store}>
|
||||
<CurrencyInput
|
||||
value="de0b6b3a7640000"
|
||||
suffix="ETH"
|
||||
currentCurrency="usd"
|
||||
conversionRate={231.06}
|
||||
/>
|
||||
</Provider>
|
||||
)
|
||||
|
||||
assert.ok(wrapper)
|
||||
const currencyInputInstance = wrapper.find(CurrencyInput).at(0).instance()
|
||||
assert.equal(currencyInputInstance.state.decimalValue, 1)
|
||||
assert.equal(currencyInputInstance.state.hexValue, 'de0b6b3a7640000')
|
||||
assert.equal(wrapper.find('.unit-input__suffix').length, 1)
|
||||
assert.equal(wrapper.find('.unit-input__suffix').text(), 'ETH')
|
||||
assert.equal(wrapper.find('.unit-input__input').props().value, '1')
|
||||
assert.equal(wrapper.find('.currency-display-component').text(), '$231.06 USD')
|
||||
})
|
||||
|
||||
it('should render properly with a fiat value', () => {
|
||||
const mockStore = {
|
||||
metamask: {
|
||||
currentCurrency: 'usd',
|
||||
conversionRate: 231.06,
|
||||
},
|
||||
}
|
||||
const store = configureMockStore()(mockStore)
|
||||
|
||||
const wrapper = mount(
|
||||
<Provider store={store}>
|
||||
<CurrencyInput
|
||||
value="f602f2234d0ea"
|
||||
suffix="USD"
|
||||
useFiat
|
||||
currentCurrency="usd"
|
||||
conversionRate={231.06}
|
||||
/>
|
||||
</Provider>
|
||||
)
|
||||
|
||||
assert.ok(wrapper)
|
||||
const currencyInputInstance = wrapper.find(CurrencyInput).at(0).instance()
|
||||
assert.equal(currencyInputInstance.state.decimalValue, 1)
|
||||
assert.equal(currencyInputInstance.state.hexValue, 'f602f2234d0ea')
|
||||
assert.equal(wrapper.find('.unit-input__suffix').length, 1)
|
||||
assert.equal(wrapper.find('.unit-input__suffix').text(), 'USD')
|
||||
assert.equal(wrapper.find('.unit-input__input').props().value, '1')
|
||||
assert.equal(wrapper.find('.currency-display-component').text(), '0.004328 ETH')
|
||||
})
|
||||
})
|
||||
|
||||
describe('handling actions', () => {
|
||||
const handleChangeSpy = sinon.spy()
|
||||
const handleBlurSpy = sinon.spy()
|
||||
|
||||
afterEach(() => {
|
||||
handleChangeSpy.resetHistory()
|
||||
handleBlurSpy.resetHistory()
|
||||
})
|
||||
|
||||
it('should call onChange and onBlur on input changes with the hex value for ETH', () => {
|
||||
const mockStore = {
|
||||
metamask: {
|
||||
currentCurrency: 'usd',
|
||||
conversionRate: 231.06,
|
||||
},
|
||||
}
|
||||
const store = configureMockStore()(mockStore)
|
||||
const wrapper = mount(
|
||||
<Provider store={store}>
|
||||
<CurrencyInput
|
||||
onChange={handleChangeSpy}
|
||||
onBlur={handleBlurSpy}
|
||||
suffix="ETH"
|
||||
currentCurrency="usd"
|
||||
conversionRate={231.06}
|
||||
/>
|
||||
</Provider>
|
||||
)
|
||||
|
||||
assert.ok(wrapper)
|
||||
assert.equal(handleChangeSpy.callCount, 0)
|
||||
assert.equal(handleBlurSpy.callCount, 0)
|
||||
|
||||
const currencyInputInstance = wrapper.find(CurrencyInput).at(0).instance()
|
||||
assert.equal(currencyInputInstance.state.decimalValue, 0)
|
||||
assert.equal(currencyInputInstance.state.hexValue, undefined)
|
||||
assert.equal(wrapper.find('.currency-display-component').text(), '$0.00 USD')
|
||||
const input = wrapper.find('input')
|
||||
assert.equal(input.props().value, 0)
|
||||
|
||||
input.simulate('change', { target: { value: 1 } })
|
||||
assert.equal(handleChangeSpy.callCount, 1)
|
||||
assert.ok(handleChangeSpy.calledWith('de0b6b3a7640000'))
|
||||
assert.equal(wrapper.find('.currency-display-component').text(), '$231.06 USD')
|
||||
assert.equal(currencyInputInstance.state.decimalValue, 1)
|
||||
assert.equal(currencyInputInstance.state.hexValue, 'de0b6b3a7640000')
|
||||
|
||||
assert.equal(handleBlurSpy.callCount, 0)
|
||||
input.simulate('blur')
|
||||
assert.equal(handleBlurSpy.callCount, 1)
|
||||
assert.ok(handleBlurSpy.calledWith('de0b6b3a7640000'))
|
||||
})
|
||||
|
||||
it('should call onChange and onBlur on input changes with the hex value for fiat', () => {
|
||||
const mockStore = {
|
||||
metamask: {
|
||||
currentCurrency: 'usd',
|
||||
conversionRate: 231.06,
|
||||
},
|
||||
}
|
||||
const store = configureMockStore()(mockStore)
|
||||
const wrapper = mount(
|
||||
<Provider store={store}>
|
||||
<CurrencyInput
|
||||
onChange={handleChangeSpy}
|
||||
onBlur={handleBlurSpy}
|
||||
suffix="USD"
|
||||
currentCurrency="usd"
|
||||
conversionRate={231.06}
|
||||
useFiat
|
||||
/>
|
||||
</Provider>
|
||||
)
|
||||
|
||||
assert.ok(wrapper)
|
||||
assert.equal(handleChangeSpy.callCount, 0)
|
||||
assert.equal(handleBlurSpy.callCount, 0)
|
||||
|
||||
const currencyInputInstance = wrapper.find(CurrencyInput).at(0).instance()
|
||||
assert.equal(currencyInputInstance.state.decimalValue, 0)
|
||||
assert.equal(currencyInputInstance.state.hexValue, undefined)
|
||||
assert.equal(wrapper.find('.currency-display-component').text(), '0 ETH')
|
||||
const input = wrapper.find('input')
|
||||
assert.equal(input.props().value, 0)
|
||||
|
||||
input.simulate('change', { target: { value: 1 } })
|
||||
assert.equal(handleChangeSpy.callCount, 1)
|
||||
assert.ok(handleChangeSpy.calledWith('f602f2234d0ea'))
|
||||
assert.equal(wrapper.find('.currency-display-component').text(), '0.004328 ETH')
|
||||
assert.equal(currencyInputInstance.state.decimalValue, 1)
|
||||
assert.equal(currencyInputInstance.state.hexValue, 'f602f2234d0ea')
|
||||
|
||||
assert.equal(handleBlurSpy.callCount, 0)
|
||||
input.simulate('blur')
|
||||
assert.equal(handleBlurSpy.callCount, 1)
|
||||
assert.ok(handleBlurSpy.calledWith('f602f2234d0ea'))
|
||||
})
|
||||
|
||||
it('should change the state and pass in a new decimalValue when props.value changes', () => {
|
||||
const mockStore = {
|
||||
metamask: {
|
||||
currentCurrency: 'usd',
|
||||
conversionRate: 231.06,
|
||||
},
|
||||
}
|
||||
const store = configureMockStore()(mockStore)
|
||||
const wrapper = shallow(
|
||||
<Provider store={store}>
|
||||
<CurrencyInput
|
||||
onChange={handleChangeSpy}
|
||||
onBlur={handleBlurSpy}
|
||||
suffix="USD"
|
||||
currentCurrency="usd"
|
||||
conversionRate={231.06}
|
||||
useFiat
|
||||
/>
|
||||
</Provider>
|
||||
)
|
||||
|
||||
assert.ok(wrapper)
|
||||
const currencyInputInstance = wrapper.find(CurrencyInput).dive()
|
||||
assert.equal(currencyInputInstance.state('decimalValue'), 0)
|
||||
assert.equal(currencyInputInstance.state('hexValue'), undefined)
|
||||
assert.equal(currencyInputInstance.find(UnitInput).props().value, 0)
|
||||
|
||||
currencyInputInstance.setProps({ value: '1ec05e43e72400' })
|
||||
currencyInputInstance.update()
|
||||
assert.equal(currencyInputInstance.state('decimalValue'), 2)
|
||||
assert.equal(currencyInputInstance.state('hexValue'), '1ec05e43e72400')
|
||||
assert.equal(currencyInputInstance.find(UnitInput).props().value, 2)
|
||||
})
|
||||
})
|
||||
})
|
@ -0,0 +1,55 @@
|
||||
import assert from 'assert'
|
||||
import proxyquire from 'proxyquire'
|
||||
|
||||
let mapStateToProps, mergeProps
|
||||
|
||||
proxyquire('../currency-input.container.js', {
|
||||
'react-redux': {
|
||||
connect: (ms, md, mp) => {
|
||||
mapStateToProps = ms
|
||||
mergeProps = mp
|
||||
return () => ({})
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
describe('CurrencyInput container', () => {
|
||||
describe('mapStateToProps()', () => {
|
||||
it('should return the correct props', () => {
|
||||
const mockState = {
|
||||
metamask: {
|
||||
conversionRate: 280.45,
|
||||
currentCurrency: 'usd',
|
||||
},
|
||||
}
|
||||
|
||||
assert.deepEqual(mapStateToProps(mockState), {
|
||||
conversionRate: 280.45,
|
||||
currentCurrency: 'usd',
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('mergeProps()', () => {
|
||||
it('should return the correct props', () => {
|
||||
const mockStateProps = {
|
||||
conversionRate: 280.45,
|
||||
currentCurrency: 'usd',
|
||||
}
|
||||
const mockDispatchProps = {}
|
||||
|
||||
assert.deepEqual(mergeProps(mockStateProps, mockDispatchProps, { useFiat: true }), {
|
||||
conversionRate: 280.45,
|
||||
currentCurrency: 'usd',
|
||||
useFiat: true,
|
||||
suffix: 'USD',
|
||||
})
|
||||
|
||||
assert.deepEqual(mergeProps(mockStateProps, mockDispatchProps, {}), {
|
||||
conversionRate: 280.45,
|
||||
currentCurrency: 'usd',
|
||||
suffix: 'ETH',
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
109
ui/app/components/dropdowns/account-details-dropdown.js
Normal file
109
ui/app/components/dropdowns/account-details-dropdown.js
Normal file
@ -0,0 +1,109 @@
|
||||
const Component = require('react').Component
|
||||
const PropTypes = require('prop-types')
|
||||
const h = require('react-hyperscript')
|
||||
const inherits = require('util').inherits
|
||||
const connect = require('react-redux').connect
|
||||
const actions = require('../../actions')
|
||||
const { getSelectedIdentity } = require('../../selectors')
|
||||
const genAccountLink = require('../../../lib/account-link.js')
|
||||
const { Menu, Item, CloseArea } = require('./components/menu')
|
||||
|
||||
AccountDetailsDropdown.contextTypes = {
|
||||
t: PropTypes.func,
|
||||
}
|
||||
|
||||
module.exports = connect(mapStateToProps, mapDispatchToProps)(AccountDetailsDropdown)
|
||||
|
||||
function mapStateToProps (state) {
|
||||
return {
|
||||
selectedIdentity: getSelectedIdentity(state),
|
||||
network: state.metamask.network,
|
||||
keyrings: state.metamask.keyrings,
|
||||
}
|
||||
}
|
||||
|
||||
function mapDispatchToProps (dispatch) {
|
||||
return {
|
||||
showAccountDetailModal: () => {
|
||||
dispatch(actions.showModal({ name: 'ACCOUNT_DETAILS' }))
|
||||
},
|
||||
viewOnEtherscan: (address, network) => {
|
||||
global.platform.openWindow({ url: genAccountLink(address, network) })
|
||||
},
|
||||
showRemoveAccountConfirmationModal: (identity) => {
|
||||
return dispatch(actions.showModal({ name: 'CONFIRM_REMOVE_ACCOUNT', identity }))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
inherits(AccountDetailsDropdown, Component)
|
||||
function AccountDetailsDropdown () {
|
||||
Component.call(this)
|
||||
|
||||
this.onClose = this.onClose.bind(this)
|
||||
}
|
||||
|
||||
AccountDetailsDropdown.prototype.onClose = function (e) {
|
||||
e.stopPropagation()
|
||||
this.props.onClose()
|
||||
}
|
||||
|
||||
AccountDetailsDropdown.prototype.render = function () {
|
||||
const {
|
||||
selectedIdentity,
|
||||
network,
|
||||
keyrings,
|
||||
showAccountDetailModal,
|
||||
viewOnEtherscan,
|
||||
showRemoveAccountConfirmationModal } = this.props
|
||||
|
||||
const address = selectedIdentity.address
|
||||
|
||||
const keyring = keyrings.find((kr) => {
|
||||
return kr.accounts.includes(address)
|
||||
})
|
||||
|
||||
const isRemovable = keyring.type !== 'HD Key Tree'
|
||||
|
||||
return h(Menu, { className: 'account-details-dropdown', isShowing: true }, [
|
||||
h(CloseArea, {
|
||||
onClick: this.onClose,
|
||||
}),
|
||||
h(Item, {
|
||||
onClick: (e) => {
|
||||
e.stopPropagation()
|
||||
global.platform.openExtensionInBrowser()
|
||||
this.props.onClose()
|
||||
},
|
||||
text: this.context.t('expandView'),
|
||||
icon: h(`img`, { src: 'images/expand.svg', style: { height: '15px' } }),
|
||||
}),
|
||||
h(Item, {
|
||||
onClick: (e) => {
|
||||
e.stopPropagation()
|
||||
showAccountDetailModal()
|
||||
this.props.onClose()
|
||||
},
|
||||
text: this.context.t('accountDetails'),
|
||||
icon: h(`img`, { src: 'images/info.svg', style: { height: '15px' } }),
|
||||
}),
|
||||
h(Item, {
|
||||
onClick: (e) => {
|
||||
e.stopPropagation()
|
||||
viewOnEtherscan(address, network)
|
||||
this.props.onClose()
|
||||
},
|
||||
text: this.context.t('viewOnEtherscan'),
|
||||
icon: h(`img`, { src: 'images/open-etherscan.svg', style: { height: '15px' } }),
|
||||
}),
|
||||
isRemovable ? h(Item, {
|
||||
onClick: (e) => {
|
||||
e.stopPropagation()
|
||||
showRemoveAccountConfirmationModal(selectedIdentity)
|
||||
this.props.onClose()
|
||||
},
|
||||
text: this.context.t('removeAccount'),
|
||||
icon: h(`img`, { src: 'images/hide.svg', style: { height: '15px' } }),
|
||||
}) : null,
|
||||
])
|
||||
}
|
@ -1,11 +1,17 @@
|
||||
@import './app-header/index';
|
||||
|
||||
@import './add-token-button/index';
|
||||
|
||||
@import './button-group/index';
|
||||
|
||||
@import './card/index';
|
||||
|
||||
@import './confirm-page-container/index';
|
||||
|
||||
@import './currency-input/index';
|
||||
|
||||
@import './currency-display/index';
|
||||
|
||||
@import './error-message/index';
|
||||
|
||||
@import './export-text-container/index';
|
||||
@ -49,3 +55,5 @@
|
||||
@import './app-header/index';
|
||||
|
||||
@import './sidebars/index';
|
||||
|
||||
@import './unit-input/index';
|
||||
|
@ -2,6 +2,7 @@ import React, { PureComponent } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import Tooltip from '../tooltip'
|
||||
import SelectedAccount from '../selected-account'
|
||||
import AccountDetailsDropdown from '../dropdowns/account-details-dropdown.js'
|
||||
|
||||
export default class MenuBar extends PureComponent {
|
||||
static contextTypes = {
|
||||
@ -15,9 +16,12 @@ export default class MenuBar extends PureComponent {
|
||||
showSidebar: PropTypes.func,
|
||||
}
|
||||
|
||||
state = { accountDetailsMenuOpen: false }
|
||||
|
||||
render () {
|
||||
const { t } = this.context
|
||||
const { isMascara, sidebarOpen, hideSidebar, showSidebar } = this.props
|
||||
const { accountDetailsMenuOpen } = this.state
|
||||
|
||||
return (
|
||||
<div className="menu-bar">
|
||||
@ -34,18 +38,25 @@ export default class MenuBar extends PureComponent {
|
||||
{
|
||||
!isMascara && (
|
||||
<Tooltip
|
||||
title={t('openInTab')}
|
||||
title={t('accountOptions')}
|
||||
position="bottom"
|
||||
>
|
||||
<div
|
||||
className="menu-bar__open-in-browser"
|
||||
onClick={() => global.platform.openExtensionInBrowser()}
|
||||
className="fa fa-ellipsis-h fa-lg menu-bar__open-in-browser"
|
||||
onClick={() => this.setState({ accountDetailsMenuOpen: true })}
|
||||
>
|
||||
<img src="images/popout.svg" />
|
||||
</div>
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
{
|
||||
accountDetailsMenuOpen && (
|
||||
<AccountDetailsDropdown
|
||||
className="menu-bar__account-details-dropdown"
|
||||
onClose={() => this.setState({ accountDetailsMenuOpen: false })}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { PureComponent } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import CurrencyDisplay from '../../../currency-display'
|
||||
import { ETH } from '../../../../constants/common'
|
||||
import UserPreferencedCurrencyDisplay from '../../../user-preferenced-currency-display'
|
||||
import { PRIMARY, SECONDARY } from '../../../../constants/common'
|
||||
|
||||
export default class CancelTransaction extends PureComponent {
|
||||
static propTypes = {
|
||||
@ -13,15 +13,15 @@ export default class CancelTransaction extends PureComponent {
|
||||
|
||||
return (
|
||||
<div className="cancel-transaction-gas-fee">
|
||||
<CurrencyDisplay
|
||||
<UserPreferencedCurrencyDisplay
|
||||
className="cancel-transaction-gas-fee__eth"
|
||||
currency={ETH}
|
||||
value={value}
|
||||
numberOfDecimals={6}
|
||||
type={PRIMARY}
|
||||
/>
|
||||
<CurrencyDisplay
|
||||
<UserPreferencedCurrencyDisplay
|
||||
className="cancel-transaction-gas-fee__fiat"
|
||||
value={value}
|
||||
type={SECONDARY}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
@ -2,7 +2,7 @@ import React from 'react'
|
||||
import assert from 'assert'
|
||||
import { shallow } from 'enzyme'
|
||||
import CancelTransactionGasFee from '../cancel-transaction-gas-fee.component'
|
||||
import CurrencyDisplay from '../../../../currency-display'
|
||||
import UserPreferencedCurrencyDisplay from '../../../../user-preferenced-currency-display'
|
||||
|
||||
describe('CancelTransactionGasFee Component', () => {
|
||||
it('should render', () => {
|
||||
@ -13,12 +13,11 @@ describe('CancelTransactionGasFee Component', () => {
|
||||
)
|
||||
|
||||
assert.ok(wrapper)
|
||||
assert.equal(wrapper.find(CurrencyDisplay).length, 2)
|
||||
const ethDisplay = wrapper.find(CurrencyDisplay).at(0)
|
||||
const fiatDisplay = wrapper.find(CurrencyDisplay).at(1)
|
||||
assert.equal(wrapper.find(UserPreferencedCurrencyDisplay).length, 2)
|
||||
const ethDisplay = wrapper.find(UserPreferencedCurrencyDisplay).at(0)
|
||||
const fiatDisplay = wrapper.find(UserPreferencedCurrencyDisplay).at(1)
|
||||
|
||||
assert.equal(ethDisplay.props().value, '0x3b9aca00')
|
||||
assert.equal(ethDisplay.props().currency, 'ETH')
|
||||
assert.equal(ethDisplay.props().className, 'cancel-transaction-gas-fee__eth')
|
||||
|
||||
assert.equal(fiatDisplay.props().value, '0x3b9aca00')
|
||||
|
@ -78,7 +78,7 @@ export default class ConfirmRemoveAccount extends Component {
|
||||
<a
|
||||
className="confirm-remove-account__link"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank" href="https://consensys.zendesk.com/hc/en-us/articles/360004180111-What-are-imported-accounts-New-UI-">
|
||||
target="_blank" href="https://metamask.zendesk.com/hc/en-us/articles/360015289932">
|
||||
{ t('learnMore') }
|
||||
</a>
|
||||
</div>
|
||||
|
@ -15,7 +15,7 @@ export default class TokenListPlaceholder extends Component {
|
||||
</div>
|
||||
<a
|
||||
className="token-list-placeholder__link"
|
||||
href="https://consensys.zendesk.com/hc/en-us/articles/360004135092"
|
||||
href="https://metamask.zendesk.com/hc/en-us/articles/360015489031"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
|
@ -1,12 +1,15 @@
|
||||
import React, { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import ConfirmTransactionBase from '../confirm-transaction-base'
|
||||
import UserPreferencedCurrencyDisplay from '../../user-preferenced-currency-display'
|
||||
import {
|
||||
formatCurrency,
|
||||
convertTokenToFiat,
|
||||
addFiat,
|
||||
roundExponential,
|
||||
} from '../../../helpers/confirm-transaction/util'
|
||||
import { getWeiHexFromDecimalValue } from '../../../helpers/conversions.util'
|
||||
import { ETH, PRIMARY } from '../../../constants/common'
|
||||
|
||||
export default class ConfirmTokenTransactionBase extends Component {
|
||||
static contextTypes = {
|
||||
@ -36,19 +39,48 @@ export default class ConfirmTokenTransactionBase extends Component {
|
||||
})
|
||||
}
|
||||
|
||||
getSubtitle () {
|
||||
const { currentCurrency, contractExchangeRate } = this.props
|
||||
renderSubtitleComponent () {
|
||||
const { contractExchangeRate, tokenAmount } = this.props
|
||||
|
||||
if (typeof contractExchangeRate === 'undefined') {
|
||||
return this.context.t('noConversionRateAvailable')
|
||||
} else {
|
||||
const fiatTransactionAmount = this.getFiatTransactionAmount()
|
||||
const roundedFiatTransactionAmount = roundExponential(fiatTransactionAmount)
|
||||
return formatCurrency(roundedFiatTransactionAmount, currentCurrency)
|
||||
}
|
||||
const decimalEthValue = (tokenAmount * contractExchangeRate) || 0
|
||||
const hexWeiValue = getWeiHexFromDecimalValue({
|
||||
value: decimalEthValue,
|
||||
fromCurrency: ETH,
|
||||
fromDenomination: ETH,
|
||||
})
|
||||
|
||||
return typeof contractExchangeRate === 'undefined'
|
||||
? (
|
||||
<span>
|
||||
{ this.context.t('noConversionRateAvailable') }
|
||||
</span>
|
||||
) : (
|
||||
<UserPreferencedCurrencyDisplay
|
||||
value={hexWeiValue}
|
||||
type={PRIMARY}
|
||||
showEthLogo
|
||||
hideLabel
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
getFiatTotalTextOverride () {
|
||||
renderPrimaryTotalTextOverride () {
|
||||
const { tokenAmount, tokenSymbol, ethTransactionTotal } = this.props
|
||||
const tokensText = `${tokenAmount} ${tokenSymbol}`
|
||||
|
||||
return (
|
||||
<div>
|
||||
<span>{ `${tokensText} + ` }</span>
|
||||
<img
|
||||
src="/images/eth.svg"
|
||||
height="18"
|
||||
/>
|
||||
<span>{ ethTransactionTotal }</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
getSecondaryTotalTextOverride () {
|
||||
const { fiatTransactionTotal, currentCurrency, contractExchangeRate } = this.props
|
||||
|
||||
if (typeof contractExchangeRate === 'undefined') {
|
||||
@ -67,7 +99,6 @@ export default class ConfirmTokenTransactionBase extends Component {
|
||||
tokenAddress,
|
||||
tokenSymbol,
|
||||
tokenAmount,
|
||||
ethTransactionTotal,
|
||||
...restProps
|
||||
} = this.props
|
||||
|
||||
@ -78,9 +109,9 @@ export default class ConfirmTokenTransactionBase extends Component {
|
||||
toAddress={toAddress}
|
||||
identiconAddress={tokenAddress}
|
||||
title={tokensText}
|
||||
subtitle={this.getSubtitle()}
|
||||
ethTotalTextOverride={`${tokensText} + \u2666 ${ethTransactionTotal}`}
|
||||
fiatTotalTextOverride={this.getFiatTotalTextOverride()}
|
||||
subtitleComponent={this.renderSubtitleComponent()}
|
||||
primaryTotalTextOverride={this.renderPrimaryTotalTextOverride()}
|
||||
secondaryTotalTextOverride={this.getSecondaryTotalTextOverride()}
|
||||
{...restProps}
|
||||
/>
|
||||
)
|
||||
|
@ -1,7 +1,6 @@
|
||||
import React, { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import ConfirmPageContainer, { ConfirmDetailRow } from '../../confirm-page-container'
|
||||
import { formatCurrency } from '../../../helpers/confirm-transaction/util'
|
||||
import { isBalanceSufficient } from '../../send/send.utils'
|
||||
import { DEFAULT_ROUTE } from '../../../routes'
|
||||
import {
|
||||
@ -9,6 +8,8 @@ import {
|
||||
TRANSACTION_ERROR_KEY,
|
||||
} from '../../../constants/error-keys'
|
||||
import { CONFIRMED_STATUS, DROPPED_STATUS } from '../../../constants/transactions'
|
||||
import UserPreferencedCurrencyDisplay from '../../user-preferenced-currency-display'
|
||||
import { PRIMARY, SECONDARY } from '../../../constants/common'
|
||||
|
||||
export default class ConfirmTransactionBase extends Component {
|
||||
static contextTypes = {
|
||||
@ -36,7 +37,9 @@ export default class ConfirmTransactionBase extends Component {
|
||||
fiatTransactionTotal: PropTypes.string,
|
||||
fromAddress: PropTypes.string,
|
||||
fromName: PropTypes.string,
|
||||
hexGasTotal: PropTypes.string,
|
||||
hexTransactionAmount: PropTypes.string,
|
||||
hexTransactionFee: PropTypes.string,
|
||||
hexTransactionTotal: PropTypes.string,
|
||||
isTxReprice: PropTypes.bool,
|
||||
methodData: PropTypes.object,
|
||||
nonce: PropTypes.string,
|
||||
@ -59,8 +62,8 @@ export default class ConfirmTransactionBase extends Component {
|
||||
detailsComponent: PropTypes.node,
|
||||
errorKey: PropTypes.string,
|
||||
errorMessage: PropTypes.string,
|
||||
ethTotalTextOverride: PropTypes.string,
|
||||
fiatTotalTextOverride: PropTypes.string,
|
||||
primaryTotalTextOverride: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
|
||||
secondaryTotalTextOverride: PropTypes.string,
|
||||
hideData: PropTypes.bool,
|
||||
hideDetails: PropTypes.bool,
|
||||
hideSubtitle: PropTypes.bool,
|
||||
@ -70,8 +73,10 @@ export default class ConfirmTransactionBase extends Component {
|
||||
onEditGas: PropTypes.func,
|
||||
onSubmit: PropTypes.func,
|
||||
subtitle: PropTypes.string,
|
||||
subtitleComponent: PropTypes.node,
|
||||
summaryComponent: PropTypes.node,
|
||||
title: PropTypes.string,
|
||||
titleComponent: PropTypes.node,
|
||||
valid: PropTypes.bool,
|
||||
warning: PropTypes.string,
|
||||
}
|
||||
@ -105,7 +110,7 @@ export default class ConfirmTransactionBase extends Component {
|
||||
const {
|
||||
balance,
|
||||
conversionRate,
|
||||
hexGasTotal,
|
||||
hexTransactionFee,
|
||||
txData: {
|
||||
simulationFails,
|
||||
txParams: {
|
||||
@ -116,7 +121,7 @@ export default class ConfirmTransactionBase extends Component {
|
||||
|
||||
const insufficientBalance = balance && !isBalanceSufficient({
|
||||
amount,
|
||||
gasTotal: hexGasTotal || '0x0',
|
||||
gasTotal: hexTransactionFee || '0x0',
|
||||
balance,
|
||||
conversionRate,
|
||||
})
|
||||
@ -153,13 +158,10 @@ export default class ConfirmTransactionBase extends Component {
|
||||
renderDetails () {
|
||||
const {
|
||||
detailsComponent,
|
||||
fiatTransactionFee,
|
||||
ethTransactionFee,
|
||||
currentCurrency,
|
||||
fiatTransactionTotal,
|
||||
ethTransactionTotal,
|
||||
fiatTotalTextOverride,
|
||||
ethTotalTextOverride,
|
||||
primaryTotalTextOverride,
|
||||
secondaryTotalTextOverride,
|
||||
hexTransactionFee,
|
||||
hexTransactionTotal,
|
||||
hideDetails,
|
||||
} = this.props
|
||||
|
||||
@ -167,16 +169,13 @@ export default class ConfirmTransactionBase extends Component {
|
||||
return null
|
||||
}
|
||||
|
||||
const formattedCurrency = formatCurrency(fiatTransactionTotal, currentCurrency)
|
||||
|
||||
return (
|
||||
detailsComponent || (
|
||||
<div className="confirm-page-container-content__details">
|
||||
<div className="confirm-page-container-content__gas-fee">
|
||||
<ConfirmDetailRow
|
||||
label="Gas Fee"
|
||||
fiatText={formatCurrency(fiatTransactionFee, currentCurrency)}
|
||||
ethText={`\u2666 ${ethTransactionFee}`}
|
||||
value={hexTransactionFee}
|
||||
headerText="Edit"
|
||||
headerTextClassName="confirm-detail-row__header-text--edit"
|
||||
onHeaderClick={() => this.handleEditGas()}
|
||||
@ -185,11 +184,12 @@ export default class ConfirmTransactionBase extends Component {
|
||||
<div>
|
||||
<ConfirmDetailRow
|
||||
label="Total"
|
||||
fiatText={fiatTotalTextOverride || formattedCurrency}
|
||||
ethText={ethTotalTextOverride || `\u2666 ${ethTransactionTotal}`}
|
||||
value={hexTransactionTotal}
|
||||
primaryText={primaryTotalTextOverride}
|
||||
secondaryText={secondaryTotalTextOverride}
|
||||
headerText="Amount + Gas Fee"
|
||||
headerTextClassName="confirm-detail-row__header-text--total"
|
||||
fiatTextColor="#2f9ae0"
|
||||
primaryValueTextColor="#2f9ae0"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -311,6 +311,43 @@ export default class ConfirmTransactionBase extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
renderTitleComponent () {
|
||||
const { title, titleComponent, hexTransactionAmount } = this.props
|
||||
|
||||
// Title string passed in by props takes priority
|
||||
if (title) {
|
||||
return null
|
||||
}
|
||||
|
||||
return titleComponent || (
|
||||
<UserPreferencedCurrencyDisplay
|
||||
value={hexTransactionAmount}
|
||||
type={PRIMARY}
|
||||
showEthLogo
|
||||
ethLogoHeight="26"
|
||||
hideLabel
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
renderSubtitleComponent () {
|
||||
const { subtitle, subtitleComponent, hexTransactionAmount } = this.props
|
||||
|
||||
// Subtitle string passed in by props takes priority
|
||||
if (subtitle) {
|
||||
return null
|
||||
}
|
||||
|
||||
return subtitleComponent || (
|
||||
<UserPreferencedCurrencyDisplay
|
||||
value={hexTransactionAmount}
|
||||
type={SECONDARY}
|
||||
showEthLogo
|
||||
hideLabel
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
render () {
|
||||
const {
|
||||
isTxReprice,
|
||||
@ -319,12 +356,9 @@ export default class ConfirmTransactionBase extends Component {
|
||||
toName,
|
||||
toAddress,
|
||||
methodData,
|
||||
ethTransactionAmount,
|
||||
fiatTransactionAmount,
|
||||
valid: propsValid = true,
|
||||
errorMessage,
|
||||
errorKey: propsErrorKey,
|
||||
currentCurrency,
|
||||
action,
|
||||
title,
|
||||
subtitle,
|
||||
@ -341,7 +375,6 @@ export default class ConfirmTransactionBase extends Component {
|
||||
const { submitting, submitError } = this.state
|
||||
|
||||
const { name } = methodData
|
||||
const fiatConvertedAmount = formatCurrency(fiatTransactionAmount, currentCurrency)
|
||||
const { valid, errorKey } = this.getErrorKey()
|
||||
|
||||
return (
|
||||
@ -352,8 +385,10 @@ export default class ConfirmTransactionBase extends Component {
|
||||
toAddress={toAddress}
|
||||
showEdit={onEdit && !isTxReprice}
|
||||
action={action || name || this.context.t('unknownFunction')}
|
||||
title={title || `${fiatConvertedAmount} ${currentCurrency.toUpperCase()}`}
|
||||
subtitle={subtitle || `\u2666 ${ethTransactionAmount}`}
|
||||
title={title}
|
||||
titleComponent={this.renderTitleComponent()}
|
||||
subtitle={subtitle}
|
||||
subtitleComponent={this.renderSubtitleComponent()}
|
||||
hideSubtitle={hideSubtitle}
|
||||
summaryComponent={summaryComponent}
|
||||
detailsComponent={this.renderDetails()}
|
||||
|
@ -36,7 +36,9 @@ const mapStateToProps = (state, props) => {
|
||||
fiatTransactionAmount,
|
||||
fiatTransactionFee,
|
||||
fiatTransactionTotal,
|
||||
hexGasTotal,
|
||||
hexTransactionAmount,
|
||||
hexTransactionFee,
|
||||
hexTransactionTotal,
|
||||
tokenData,
|
||||
methodData,
|
||||
txData,
|
||||
@ -87,7 +89,9 @@ const mapStateToProps = (state, props) => {
|
||||
fiatTransactionAmount,
|
||||
fiatTransactionFee,
|
||||
fiatTransactionTotal,
|
||||
hexGasTotal,
|
||||
hexTransactionAmount,
|
||||
hexTransactionFee,
|
||||
hexTransactionTotal,
|
||||
txData,
|
||||
tokenData,
|
||||
methodData,
|
||||
|
@ -46,7 +46,7 @@ AccountImportSubview.prototype.render = function () {
|
||||
},
|
||||
onClick: () => {
|
||||
global.platform.openWindow({
|
||||
url: 'https://consensys.zendesk.com/hc/en-us/articles/360004180111-What-are-imported-accounts-New-UI',
|
||||
url: 'https://metamask.zendesk.com/hc/en-us/articles/360015289932',
|
||||
})
|
||||
},
|
||||
}, this.context.t('here')),
|
||||
|
@ -49,7 +49,7 @@ class NewAccountCreateForm extends Component {
|
||||
h(Button, {
|
||||
type: 'primary',
|
||||
large: true,
|
||||
className:'new-account-create-form__button',
|
||||
className: 'new-account-create-form__button',
|
||||
onClick: () => {
|
||||
createAccount(newAccountName || defaultAccountName)
|
||||
.then(() => history.push(DEFAULT_ROUTE))
|
||||
|
@ -48,4 +48,22 @@
|
||||
border-color: $ecstasy;
|
||||
}
|
||||
}
|
||||
|
||||
&__radio-buttons {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&__radio-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&:not(:last-child) {
|
||||
margin-right: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
&__radio-label {
|
||||
padding-left: 4px;
|
||||
}
|
||||
}
|
||||
|
@ -55,6 +55,8 @@ export default class SettingsTab extends PureComponent {
|
||||
sendHexData: PropTypes.bool,
|
||||
currentCurrency: PropTypes.string,
|
||||
conversionDate: PropTypes.number,
|
||||
useETHAsPrimaryCurrency: PropTypes.bool,
|
||||
setUseETHAsPrimaryCurrencyPreference: PropTypes.func,
|
||||
}
|
||||
|
||||
state = {
|
||||
@ -339,6 +341,56 @@ export default class SettingsTab extends PureComponent {
|
||||
)
|
||||
}
|
||||
|
||||
renderUseEthAsPrimaryCurrency () {
|
||||
const { t } = this.context
|
||||
const { useETHAsPrimaryCurrency, setUseETHAsPrimaryCurrencyPreference } = this.props
|
||||
|
||||
return (
|
||||
<div className="settings-page__content-row">
|
||||
<div className="settings-page__content-item">
|
||||
<span>{ t('primaryCurrencySetting') }</span>
|
||||
<div className="settings-page__content-description">
|
||||
{ t('primaryCurrencySettingDescription') }
|
||||
</div>
|
||||
</div>
|
||||
<div className="settings-page__content-item">
|
||||
<div className="settings-page__content-item-col">
|
||||
<div className="settings-tab__radio-buttons">
|
||||
<div className="settings-tab__radio-button">
|
||||
<input
|
||||
type="radio"
|
||||
id="eth-primary-currency"
|
||||
onChange={() => setUseETHAsPrimaryCurrencyPreference(true)}
|
||||
checked={Boolean(useETHAsPrimaryCurrency)}
|
||||
/>
|
||||
<label
|
||||
htmlFor="eth-primary-currency"
|
||||
className="settings-tab__radio-label"
|
||||
>
|
||||
{ t('eth') }
|
||||
</label>
|
||||
</div>
|
||||
<div className="settings-tab__radio-button">
|
||||
<input
|
||||
type="radio"
|
||||
id="fiat-primary-currency"
|
||||
onChange={() => setUseETHAsPrimaryCurrencyPreference(false)}
|
||||
checked={!useETHAsPrimaryCurrency}
|
||||
/>
|
||||
<label
|
||||
htmlFor="fiat-primary-currency"
|
||||
className="settings-tab__radio-label"
|
||||
>
|
||||
{ t('fiat') }
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
render () {
|
||||
const { warning, isMascara } = this.props
|
||||
|
||||
@ -346,6 +398,7 @@ export default class SettingsTab extends PureComponent {
|
||||
<div className="settings-page__content">
|
||||
{ warning && <div className="settings-tab__error">{ warning }</div> }
|
||||
{ this.renderCurrentConversion() }
|
||||
{ this.renderUseEthAsPrimaryCurrency() }
|
||||
{ this.renderCurrentLocale() }
|
||||
{ this.renderNewRpcUrl() }
|
||||
{ this.renderStateLogs() }
|
||||
|
@ -11,7 +11,9 @@ import {
|
||||
updateCurrentLocale,
|
||||
setFeatureFlag,
|
||||
showModal,
|
||||
setUseETHAsPrimaryCurrencyPreference,
|
||||
} from '../../../../actions'
|
||||
import { preferencesSelector } from '../../../../selectors'
|
||||
|
||||
const mapStateToProps = state => {
|
||||
const { appState: { warning }, metamask } = state
|
||||
@ -24,6 +26,7 @@ const mapStateToProps = state => {
|
||||
isMascara,
|
||||
currentLocale,
|
||||
} = metamask
|
||||
const { useETHAsPrimaryCurrency } = preferencesSelector(state)
|
||||
|
||||
return {
|
||||
warning,
|
||||
@ -34,6 +37,7 @@ const mapStateToProps = state => {
|
||||
useBlockie,
|
||||
sendHexData,
|
||||
provider,
|
||||
useETHAsPrimaryCurrency,
|
||||
}
|
||||
}
|
||||
|
||||
@ -50,6 +54,9 @@ const mapDispatchToProps = dispatch => {
|
||||
},
|
||||
setHexDataFeatureFlag: shouldShow => dispatch(setFeatureFlag('sendHexData', shouldShow)),
|
||||
showResetAccountConfirmationModal: () => dispatch(showModal({ name: 'CONFIRM_RESET_ACCOUNT' })),
|
||||
setUseETHAsPrimaryCurrencyPreference: value => {
|
||||
return dispatch(setUseETHAsPrimaryCurrencyPreference(value))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,7 @@ function QrCodeView () {
|
||||
QrCodeView.prototype.render = function () {
|
||||
const props = this.props
|
||||
const { message, data } = props.Qr
|
||||
const address = `${isHexPrefixed(data) ? 'ethereum:' : ''}${data}`
|
||||
const address = `${isHexPrefixed(data) ? 'ethereum:' : ''}${checksumAddress(data)}`
|
||||
const qrImage = qrCode(4, 'M')
|
||||
qrImage.addData(address)
|
||||
qrImage.make()
|
||||
|
@ -2,7 +2,8 @@ import React, { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { checksumAddress } from '../../../util'
|
||||
import Identicon from '../../identicon'
|
||||
import CurrencyDisplay from '../currency-display'
|
||||
import UserPreferencedCurrencyDisplay from '../../user-preferenced-currency-display'
|
||||
import { PRIMARY, SECONDARY } from '../../../constants/common'
|
||||
|
||||
export default class AccountListItem extends Component {
|
||||
|
||||
@ -25,8 +26,6 @@ export default class AccountListItem extends Component {
|
||||
const {
|
||||
account,
|
||||
className,
|
||||
conversionRate,
|
||||
currentCurrency,
|
||||
displayAddress = false,
|
||||
displayBalance = true,
|
||||
handleClick,
|
||||
@ -57,16 +56,20 @@ export default class AccountListItem extends Component {
|
||||
{ checksumAddress(address) }
|
||||
</div>}
|
||||
|
||||
{displayBalance && <CurrencyDisplay
|
||||
className="account-list-item__account-balances"
|
||||
conversionRate={conversionRate}
|
||||
convertedBalanceClassName="account-list-item__account-secondary-balance"
|
||||
convertedCurrency={currentCurrency}
|
||||
primaryBalanceClassName="account-list-item__account-primary-balance"
|
||||
primaryCurrency="ETH"
|
||||
readOnly={true}
|
||||
value={balance}
|
||||
/>}
|
||||
{
|
||||
displayBalance && (
|
||||
<div className="account-list-item__account-balances">
|
||||
<UserPreferencedCurrencyDisplay
|
||||
type={PRIMARY}
|
||||
value={balance}
|
||||
/>
|
||||
<UserPreferencedCurrencyDisplay
|
||||
type={SECONDARY}
|
||||
value={balance}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
</div>)
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user