aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Huang <tmashuang@users.noreply.github.com>2019-06-05 05:55:56 +0800
committerGitHub <noreply@github.com>2019-06-05 05:55:56 +0800
commitbe911711942914e7f6df535afff1a3ee87e23b6f (patch)
treebbd890e0a6a1bd72d2560e734ce66ed42a294722
parent3d7bdd6ecb5e8e17cb8ccf83f56ad389971332c1 (diff)
parent28e030a11d75df81e16f3ebf726bfc59fcee679e (diff)
downloadtangerine-wallet-browser-be911711942914e7f6df535afff1a3ee87e23b6f.tar.gz
tangerine-wallet-browser-be911711942914e7f6df535afff1a3ee87e23b6f.tar.zst
tangerine-wallet-browser-be911711942914e7f6df535afff1a3ee87e23b6f.zip
Merge pull request #6683 from MetaMask/develop
Merge dev to master
-rw-r--r--.editorconfig15
-rw-r--r--CHANGELOG.md14
-rw-r--r--app/404.html52
-rw-r--r--app/_locales/en/messages.json4
-rw-r--r--app/_locales/it/messages.json234
-rw-r--r--app/error.html79
-rw-r--r--app/images/enslogo.svg1
-rw-r--r--app/loading.html13
-rw-r--r--app/manifest.json2
-rw-r--r--app/scripts/controllers/currency.js205
-rw-r--r--app/scripts/controllers/provider-approval.js3
-rw-r--r--app/scripts/controllers/token-rates.js2
-rw-r--r--app/scripts/controllers/transactions/index.js3
-rw-r--r--app/scripts/controllers/transactions/nonce-tracker.js161
-rw-r--r--app/scripts/controllers/transactions/pending-tx-tracker.js53
-rw-r--r--app/scripts/inpage.js4
-rw-r--r--app/scripts/lib/ens-ipfs/contracts/registry.js (renamed from app/scripts/lib/ens-ipfs/contracts/registrar.js)0
-rw-r--r--app/scripts/lib/ens-ipfs/contracts/resolver.js2
-rw-r--r--app/scripts/lib/ens-ipfs/resolver.js52
-rw-r--r--app/scripts/lib/ens-ipfs/setup.js30
-rw-r--r--app/scripts/metamask-controller.js32
-rw-r--r--docs/creating-metrics-events.md71
-rw-r--r--package-lock.json2222
-rw-r--r--package.json4
-rw-r--r--test/e2e/beta/from-import-beta-ui.spec.js55
-rw-r--r--test/integration/lib/send-new-ui.js24
-rw-r--r--test/unit/app/controllers/currency-controller-test.js78
-rw-r--r--test/unit/app/controllers/metamask-controller-test.js9
-rw-r--r--test/unit/app/controllers/transactions/nonce-tracker-test.js238
-rw-r--r--test/unit/app/controllers/transactions/pending-tx-test.js72
-rw-r--r--ui/app/components/app/ens-input.js14
-rw-r--r--ui/app/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container.container.js72
-rw-r--r--ui/app/components/app/gas-customization/gas-modal-page-container/tests/gas-modal-page-container-container.test.js12
-rw-r--r--ui/app/components/ui/currency-input/currency-input.component.js4
-rw-r--r--ui/app/components/ui/currency-input/currency-input.container.js3
-rw-r--r--ui/app/components/ui/currency-input/tests/currency-input.container.test.js16
-rw-r--r--ui/app/components/ui/unit-input/index.scss9
-rw-r--r--ui/app/components/ui/unit-input/unit-input.component.js10
-rw-r--r--ui/app/css/itcss/components/send.scss93
-rw-r--r--ui/app/helpers/utils/conversions.util.js11
-rw-r--r--ui/app/helpers/utils/metametrics.util.js53
-rw-r--r--ui/app/pages/confirm-transaction-base/confirm-transaction-base.component.js9
-rw-r--r--ui/app/pages/create-account/connect-hardware/index.js8
-rw-r--r--ui/app/pages/send/send-content/send-amount-row/amount-max-button/amount-max-button.component.js41
-rw-r--r--ui/app/pages/send/send-content/send-amount-row/amount-max-button/amount-max-button.container.js5
-rw-r--r--ui/app/pages/send/send-content/send-amount-row/amount-max-button/tests/amount-max-button-component.test.js7
-rw-r--r--ui/app/pages/send/send-content/send-amount-row/amount-max-button/tests/amount-max-button-container.test.js2
-rw-r--r--ui/app/pages/send/send-content/send-amount-row/send-amount-row.component.js2
-rw-r--r--ui/app/pages/send/send-content/send-gas-row/send-gas-row.component.js37
-rw-r--r--ui/app/pages/send/send-content/send-gas-row/send-gas-row.container.js18
-rw-r--r--ui/app/pages/send/send-content/send-gas-row/tests/send-gas-row-container.test.js9
-rw-r--r--ui/app/pages/send/send-content/send-row-wrapper/send-row-error-message/send-row-error-message.component.js3
-rw-r--r--ui/app/pages/send/send-content/send-row-wrapper/send-row-wrapper.component.js46
-rw-r--r--ui/app/pages/send/to-autocomplete/to-autocomplete.js21
54 files changed, 2579 insertions, 1660 deletions
diff --git a/.editorconfig b/.editorconfig
index 609f684a0..c6c8b3621 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -1,24 +1,9 @@
-# EditorConfig helps developers define and maintain consistent
-# coding styles between different editors and IDEs
-# editorconfig.org
-
root = true
-
[*]
-
-# Change these settings to your own preference
indent_style = space
indent_size = 2
-
-[*.json]
-indent_size = 2
-
-# We recommend you to keep these unchanged
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
-
-[*.md]
-trim_trailing_whitespace = false
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 129af1281..b96648380 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,20 @@
## Current Develop Branch
+## 6.6.0 Mon Jun 03 2019
+
+- [#6659](https://github.com/MetaMask/metamask-extension/pull/6659): Enable Ledger hardware wallet support on Firefox
+- [#6671](https://github.com/MetaMask/metamask-extension/pull/6671): bugfix: reject enable promise on user rejection
+- [#6625](https://github.com/MetaMask/metamask-extension/pull/6625): Ensures that transactions cannot be confirmed if gas limit is below 21000.
+- [#6633](https://github.com/MetaMask/metamask-extension/pull/6633): Fix grammatical error in i18n endOfFlowMessage6
+
+## 6.5.3 Thu May 16 2019
+
+- [#6619](https://github.com/MetaMask/metamask-extension/pull/6619): bugfix: show extension window if locked regardless of approval
+- [#6388](https://github.com/MetaMask/metamask-extension/pull/6388): Transactions/pending - check nonce against the network and mark as dropped if not included in a block
+- [#6606](https://github.com/MetaMask/metamask-extension/pull/6606): Improve ENS Address Input
+- [#6615](https://github.com/MetaMask/metamask-extension/pull/6615): Adds e2e test for removing imported accounts.
+
## 6.5.2 Wed May 15 2019
- [#6613](https://github.com/MetaMask/metamask-extension/pull/6613): Hardware Wallet Fix
diff --git a/app/404.html b/app/404.html
deleted file mode 100644
index 8a6df9d7a..000000000
--- a/app/404.html
+++ /dev/null
@@ -1,52 +0,0 @@
-<html>
-<head>
- <title>MetaMask</title>
- <style>
- *{
- padding: 0;
- margin: 0;
- box-sizing: border-box;
- }
- img{
- display: block;
- }
- html, body{
- display: flex;
- justify-content: center;
- align-items: center;
- width: 100%;
- height: 100%;
- }
- .app{
- position: relative;
- width: 100%;
- height: auto;
- overflow: hidden;
- }
- img{
- display: block;
- width: 100%;
- height: auto;
- }
- h2{
- display: block;
- width: 100%;
- overflow: hidden;
- position: absolute;
- bottom: 20%;
- left: 0;
- color: #1b243d;
- text-align: center;
- }
- h2 > a{
- color: #1b243d;
- }
- </style>
-</head>
-<body>
- <div class="app">
- <img src="./images/404.png" alt="">
- <h2>Powered by <a href="https://www.portal.network/">Portal Network</a></h2>
- </div>
-</body>
-</html>
diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json
index bef278f79..41ba7ef7a 100644
--- a/app/_locales/en/messages.json
+++ b/app/_locales/en/messages.json
@@ -549,7 +549,7 @@
"message": "Be careful of phishing! MetaMask will never spontaneously ask for your seed phrase."
},
"endOfFlowMessage6": {
- "message": "If you need to back your up seed phrase again, you can find it in Settings -> Security."
+ "message": "If you need to back up your seed phrase again, you can find it in Settings -> Security."
},
"endOfFlowMessage7": {
"message": "If you ever have questions or see something fishy, email support@metamask.io."
@@ -1249,7 +1249,7 @@
"message": "Revert"
},
"remove": {
- "message": "remove"
+ "message": "Remove"
},
"removeAccount": {
"message": "Remove account"
diff --git a/app/_locales/it/messages.json b/app/_locales/it/messages.json
index 9e0f1f06d..09ba045b9 100644
--- a/app/_locales/it/messages.json
+++ b/app/_locales/it/messages.json
@@ -11,6 +11,9 @@
"exposeDescription": {
"message": "Esporre gli account al sito Web corrente. Utile per dapps legacy."
},
+ "chartOnlyAvailableEth": {
+ "message": "Grafico disponibile solo per le reti Ethereum."
+ },
"confirmExpose": {
"message": "Sei sicuro di voler esporre i tuoi account al sito web corrente?"
},
@@ -41,6 +44,15 @@
"providerRequestInfo": {
"message": "Il dominio elencato di seguito sta tentando di richiedere l'accesso all'API Ethereum in modo che possa interagire con la blockchain di Ethereum. Controlla sempre di essere sul sito corretto prima di approvare l'accesso a Ethereum."
},
+ "about": {
+ "message": "Informazioni"
+ },
+ "aboutSettingsDescription": {
+ "message": "Version, centro di supporto e contatti."
+ },
+ "aboutUs": {
+ "message": "Chi siamo"
+ },
"accept": {
"message": "Accetta"
},
@@ -71,6 +83,15 @@
"address": {
"message": "Indirizzo"
},
+ "addNetwork": {
+ "message": "Aggiungi Rete"
+ },
+ "advanced": {
+ "message": "Avanzate"
+ },
+ "advancedSettingsDescription": {
+ "message": "Accedi alle funzionalità sviluppatore, download dei log di Stato, Reset Account, imposta reti di test e RPC personalizzata."
+ },
"advancedOptions": {
"message": "Opzioni Avanzate"
},
@@ -89,8 +110,14 @@
"addAcquiredTokens": {
"message": "Aggiungi i token che hai acquistato usando MetaMask"
},
- "advanced": {
- "message": "Avanzato"
+ "agreeTermsOfService": {
+ "message": "Accetto i termini di servizio"
+ },
+ "allDone": {
+ "message": "Tutto Fatto"
+ },
+ "alreadyHaveSeedPhrase": {
+ "message": "No, ho già una frase seed"
},
"amount": {
"message": "Importo"
@@ -115,6 +142,9 @@
"approved": {
"message": "Approvato"
},
+ "asset": {
+ "message": "Asset"
+ },
"attemptingConnect": {
"message": "Tentativo di connessione alla blockchain."
},
@@ -127,6 +157,12 @@
"attributions": {
"message": "Attribuzioni"
},
+ "autoLogoutTimeLimit": {
+ "message": "Timer di Logout Automatico (minuti)"
+ },
+ "autoLogoutTimeLimitDescription": {
+ "message": "Imposta il tempo di inattività dopo il quale MetaMask fa il log out automaticamente"
+ },
"available": {
"message": "Disponibile"
},
@@ -158,6 +194,13 @@
"message": "deve essere maggiore o uguale a $1 e minore o uguale a $2.",
"description": "aiuto per inserire un input esadecimale come decimale"
},
+ "blockExplorerUrl": {
+ "message": "Block Explorer"
+ },
+ "blockExplorerView": {
+ "message": "Visualizza account su $1",
+ "description": "$1 replaced by URL for custom block explorer"
+ },
"blockiesIdenticon": {
"message": "Usa le icone Blockie"
},
@@ -179,6 +222,12 @@
"buyCoinbaseExplainer": {
"message": "Coinbase è il servizio più popolare al mondo per comprare e vendere Bitcoin, Ethereum e Litecoin."
},
+ "buyWithWyre": {
+ "message": "Compra ETH con Wyre"
+ },
+ "buyWithWyreDescription": {
+ "message": "Wyre ti consente di usare la carta di credito per depositare ETH direttamente nel tuo account MetaMask."
+ },
"buyCoinSwitch": {
"message": "Compra su CoinSwitch"
},
@@ -191,6 +240,9 @@
"ok": {
"message": "Ok"
},
+ "optionalBlockExplorerUrl": {
+ "message": "URL del Block Explorer (opzionale)"
+ },
"cancel": {
"message": "Annulla"
},
@@ -206,6 +258,9 @@
"cancelN": {
"message": "Annulla tutte le transazioni relative a $1"
},
+ "chainId": {
+ "message": "Chain ID"
+ },
"classicInterface": {
"message": "Usa l'interfaccia classica"
},
@@ -224,6 +279,9 @@
"chromeRequiredForHardwareWallets": {
"message": "Devi usare MetaMask con Google Chrome per connettere il tuo Portafoglio Hardware"
},
+ "company": {
+ "message": "Azienda"
+ },
"confirm": {
"message": "Conferma"
},
@@ -245,6 +303,9 @@
"confirmTransaction": {
"message": "Conferma Transazione"
},
+ "congratulations": {
+ "message": "Congratulazioni"
+ },
"connectHardwareWallet": {
"message": "Connetti Portafoglio Hardware"
},
@@ -272,6 +333,12 @@
"connectingToRinkeby": {
"message": "Connessione alla Rete di test Rinkeby"
},
+ "connectingToLocalhost": {
+ "message": "Connessione a Localhost 8545"
+ },
+ "connectingToGoerli": {
+ "message": "Connessione alla Rete di Test Goerli"
+ },
"connectingToUnknown": {
"message": "Connessione ad una Rete Sconosciuta"
},
@@ -287,6 +354,9 @@
"continueToCoinbase": {
"message": "Continua su Coinbase"
},
+ "continueToWyre": {
+ "message": "Continua su Wyre"
+ },
"continueToCoinSwitch": {
"message": "Continua su CoinSwitch"
},
@@ -314,6 +384,12 @@
"copyAddress": {
"message": "Copia l'indirizzo"
},
+ "copyTransactionId": {
+ "message": "Copia ID Transazione"
+ },
+ "copiedTransactionId": {
+ "message": "ID Transazione Copiato"
+ },
"copyToClipboard": {
"message": "Copia negli appunti"
},
@@ -329,6 +405,9 @@
"createAccount": {
"message": "Crea Account"
},
+ "createAWallet": {
+ "message": "Crea un Wallet"
+ },
"createDen": {
"message": "Crea"
},
@@ -439,6 +518,9 @@
"edit": {
"message": "Modifica"
},
+ "editNetwork": {
+ "message": "Modifica Rete"
+ },
"editAccountName": {
"message": "Modifica Nome Account"
},
@@ -451,6 +533,30 @@
"encryptNewDen": {
"message": "Cripta il tuo nuovo DEN"
},
+ "endOfFlowMessage1": {
+ "message": "Hai passato il test - tieni la tua frase seed al sicuro, è tua responsabilità!"
+ },
+ "endOfFlowMessage2": {
+ "message": "Suggerimenti su come tenerla al sicuro"
+ },
+ "endOfFlowMessage3": {
+ "message": "Salva un backup in più di un posto."
+ },
+ "endOfFlowMessage4": {
+ "message": "Non condividerla mai con nessuno."
+ },
+ "endOfFlowMessage5": {
+ "message": "Stai attento al phishing! MetaMask non ti chiederà mai spontaneamente la tua frase seed."
+ },
+ "endOfFlowMessage6": {
+ "message": "Se vorrai fare nuovamente un backup della frase, la puoi trovare in Impostazioni -> Sicurezza & Privacy."
+ },
+ "endOfFlowMessage7": {
+ "message": "Se hai delle domande o vedi delle attività sospette, manda una mail a support@metamask.io."
+ },
+ "endOfFlowMessage8": {
+ "message": "MetaMask non può recuperare la tua frase seed. Impara di più."
+ },
"ensNameNotFound": {
"message": "Nome ENS non trovato"
},
@@ -571,6 +677,12 @@
"gasPriceRequired": {
"message": "Prezzo Gas Richiesto"
},
+ "general": {
+ "message": "Generale"
+ },
+ "generalSettingsDescription": {
+ "message": "Conversione moneta, moneta primaria, lingua, icone blockie"
+ },
"generatingTransaction": {
"message": "Generando la transazione"
},
@@ -584,10 +696,16 @@
"getHelp": {
"message": "Aiuto."
},
+ "getStarted": {
+ "message": "Inizia"
+ },
"greaterThanMin": {
"message": "deve essere maggiore o uguale a $1.",
"description": "aiuto per inserire un input esadecimale come decimale"
},
+ "happyToSeeYou": {
+ "message": "Siamo contenti di vederti."
+ },
"hardware": {
"message": "hardware"
},
@@ -650,6 +768,12 @@
"importDen": {
"message": "Importa un DEN Esistente"
},
+ "importWallet": {
+ "message": "Importa Portafoglio"
+ },
+ "importYourExisting": {
+ "message": "Importa il tuo portafoglio esistente usando la tua frase seed a 12 parole"
+ },
"imported": {
"message": "Importato",
"description": "stato che conferma che un account è stato totalmente caricato nel portachiavi"
@@ -687,6 +811,9 @@
"knownAddressRecipient": {
"message": "Indirizzo del contratto conosciuto."
},
+ "invalidAddressRecipientNotEthNetwork": {
+ "message": "Non rete ETH, inserisci caratteri minuscoli"
+ },
"invalidGasParams": {
"message": "Parametri del Gas non validi"
},
@@ -727,10 +854,16 @@
"ledgerAccountRestriction": {
"message": "E' necessario utilizzare l'ultimo account prima di poterne aggiungere uno nuovo."
},
+ "legal": {
+ "message": "Informazioni Legali"
+ },
"lessThanMax": {
"message": "deve essere minore o uguale a $1.",
"description": "aiuto per inserire un input esadecimale come decimale"
},
+ "letsGoSetUp": {
+ "message": "Si, iniziamo!"
+ },
"likeToAddTokens": {
"message": "Vorresti aggiungere questi token?"
},
@@ -794,6 +927,12 @@
"minutesShorthand": {
"message": "Min"
},
+ "mobileSyncTitle": {
+ "message": "Sincronizza account con il dispositivo mobile"
+ },
+ "mobileSyncText": {
+ "message": "Per favore inserisci la password per confermare che sei te!"
+ },
"myAccounts": {
"message": "Miei Account"
},
@@ -814,9 +953,15 @@
"negativeETH": {
"message": "Non puoi inviare una quantità di ETH negativa."
},
+ "networkName": {
+ "message": "Nome Rete"
+ },
"networks": {
"message": "Reti"
},
+ "networkSettingsDescription": {
+ "message": "Aggiungi e modifica reti RPC personalizzate"
+ },
"nevermind": {
"message": "Non importa"
},
@@ -842,7 +987,22 @@
"newNetwork": {
"message": "Nuova Rete"
},
- "rpcURL": {
+ "newToMetaMask": {
+ "message": "Nuovo a MetaMask?"
+ },
+ "noAlreadyHaveSeed": {
+ "message": "No, ho già una frase seed"
+ },
+ "protectYourKeys": {
+ "message": "Proteggi le tue chiavi!"
+ },
+ "protectYourKeysMessage1": {
+ "message": "Stai attento con la tua frase seed - ci sono stati report di siti web che hanno tentato di imitare MetaMask. MetaMask non ti chiederà mai la tua frase seed!"
+ },
+ "protectYourKeysMessage2": {
+ "message": "Tieni la tua frase al sicuro. Se vedi qualcosa di sospetto, o non sei sicuro di un sito web, manda una mail a support@metamask.io"
+ },
+ "rpcUrl": {
"message": "Nuovo URL RPC"
},
"showAdvancedOptions": {
@@ -884,6 +1044,9 @@
"noTransactions": {
"message": "Nessuna Transazione"
},
+ "notEnoughGas": {
+ "message": "Gas Non Sufficiente"
+ },
"notFound": {
"message": "Non Trovata"
},
@@ -934,6 +1097,12 @@
"originalTotal": {
"message": "Totale Precedente"
},
+ "participateInMetaMetrics": {
+ "message": "Participa in MetaMetrics"
+ },
+ "participateInMetaMetricsDescription": {
+ "message": "Participa in MetaMetrics per aiutarci a rendere MetaMask migliore"
+ },
"password": {
"message": "Password"
},
@@ -1097,6 +1266,9 @@
"ropsten": {
"message": "Rete di test Ropsten"
},
+ "goerli": {
+ "message": "Rete di test Goerli"
+ },
"rpc": {
"message": "RPC Personalizzata"
},
@@ -1147,6 +1319,12 @@
"secretPhrase": {
"message": "Inserisci la tua frase segreta di dodici parole per ripristinare la cassaforte."
},
+ "securityAndPrivacy": {
+ "message": "Sicurezza & Privacy"
+ },
+ "securitySettingsDescription": {
+ "message": "Impostazioni sulla Privacy e sulla frase seed del portafoglio"
+ },
"secondsShorthand": {
"message": "Sec"
},
@@ -1207,6 +1385,9 @@
"selectAnAccountHelp": {
"message": "Selezione l'account da visualizzare in MetaMask"
},
+ "selectAnAsset": {
+ "message": "Seleziona un Asset"
+ },
"selectAHigherGasFee": {
"message": "Seleziona un costo in gas maggiore per accelerare l'elaborazione della transazione.*"
},
@@ -1229,7 +1410,13 @@
"message": "Controlli gas avanzati"
},
"showAdvancedGasInlineDescription": {
- "message": "Seleziona qui per visualizzare i controlli su prezzo e limite del gas nelle schermate di invio e conferma."
+ "message": "Seleziona per visualizzare i controlli su prezzo e limite del gas nelle schermate di invio e conferma."
+ },
+ "showFiatConversionInTestnets": {
+ "message": "Mostra conversione nelle reti di test"
+ },
+ "showFiatConversionInTestnetsDescription": {
+ "message": "Seleziona se vuoi vedere la conversione in valuta fiat nelle reti di test"
},
"showPrivateKeys": {
"message": "Mostra Chiave Privata"
@@ -1330,9 +1517,33 @@
"supportCenter": {
"message": "Visita il nostro Centro di Supporto"
},
+ "symbol": {
+ "message": "Simbolo"
+ },
"symbolBetweenZeroTwelve": {
"message": "Il simbolo deve essere lungo tra 0 e 12 caratteri."
},
+ "syncWithMobile": {
+ "message": "Sincronizza con dispositivo mobile"
+ },
+ "syncWithMobileTitle": {
+ "message": "Sincronizza con dispositivo mobile"
+ },
+ "syncWithMobileDesc": {
+ "message": "Puoi sincronizzare i tuoi account e le tue informazioni con il tuo dispositivo mobile. Apri l'app di MetaMask, vai su \"Impostazioni\" e tocca \"Sincronizza da Estensione sul Browser\""
+ },
+ "syncWithMobileDescNewUsers": {
+ "message": "Se hai appena aperto l'app di MetaMask per la prima volta, segui i passaggi sul tuo telefono."
+ },
+ "syncWithMobileScanThisCode": {
+ "message": "Scansiona questo codice con l'app di MetaMask"
+ },
+ "syncWithMobileBeCareful": {
+ "message": "Assicurati che nessun'altro stia guardando al tuo schermo quando scansioni questo codice"
+ },
+ "syncWithMobileComplete": {
+ "message": "I tuoi dati sono stati sincronizzati con successo. Goditi l'app di MetaMask!"
+ },
"takesTooLong": {
"message": "Ci sta mettendo troppo?"
},
@@ -1342,6 +1553,9 @@
"testFaucet": {
"message": "Prova Faucet"
},
+ "thisWillCreate": {
+ "message": "Questo creerà un nuovo portafoglio e frase seed"
+ },
"tips": {
"message": "Suggerimenti"
},
@@ -1364,6 +1578,9 @@
"tokenBalance": {
"message": "Bilancio Token:"
},
+ "tokenContractAddress": {
+ "message": "Indirizzo Contratto Token"
+ },
"tokenSelection": {
"message": "Cerca un token o seleziona dalla lista di token più popolari."
},
@@ -1525,9 +1742,15 @@
"viewAccount": {
"message": "Vedi Account"
},
+ "viewOnCustomBlockExplorer": {
+ "message": "Vedi su $1"
+ },
"viewOnEtherscan": {
"message": "Vedi su Etherscan"
},
+ "viewNetworkInfo": {
+ "message": "Visualizza Informazioni Rete"
+ },
"visitWebSite": {
"message": "Visita il nostro sito web"
},
@@ -1573,6 +1796,9 @@
"yourUniqueAccountImageDescription2": {
"message": "Vedrai questa immagine ogni volta che dovrai confermare una transazione."
},
+ "yourUniqueAccountImageDescription3": {
+ "message": "MetaMask non ti chiederà mai la tua frase seed!"
+ },
"zeroGasPriceOnSpeedUpError": {
"message": "Prezzo del gas maggiore di zero"
}
diff --git a/app/error.html b/app/error.html
deleted file mode 100644
index 366b3d94a..000000000
--- a/app/error.html
+++ /dev/null
@@ -1,79 +0,0 @@
-<html>
-<head>
- <title>MetaMask Error</title>
- <link href="https://fonts.googleapis.com/css?family=Rokkitt" rel="stylesheet">
- <style>
- *{
- padding: 0;
- margin: 0;
- box-sizing: border-box;
- }
- img{
- display: block;
- }
- html, body{
- display: flex;
- justify-content: center;
- align-items: center;
- width: 100%;
- height: 100%;
- }
- @keyframes logoAmin{
- from {transform: scale(1);}
- 50%{transform: scale(1.1);}
- to {transform: scale(1);}
- }
- .errorBox{
- width: 70%;
- height: auto;
- overflow: hidden;
- background-image: url("./images/deadface.png");
- background-repeat: no-repeat;
- background-position: 100% 50%;
- background-size: auto 90%;
- padding: 5px;
- }
- .errorBox > img{
- width: 100px;
- height: auto;
- margin-bottom: 25px;
- animation: logoAmin 1s infinite linear;
- }
- .errorBox > h1, .errorBox > h2{
- letter-spacing: 2px;
- }
- .errorBox > h1{
- color: #9b9b9b;
- font-size: 40px;
- }
- .errorBox > h2{
- color: #1b243d;
- font-size: 20px;
- padding-top: 5px;
- }
- .errorBox > h2 >a{
- color: #1b243d;
- }
- .errorBox > h2 >a:hover{
- color: #44588e;
- }
-
- .errorBox > h1 > span{
- color: #33559f;
- }
-
- </style>
-</head>
-<body>
- <div class="errorBox">
- <img src="./images/logo.png" alt="">
- <h1><span id="name"></span> not found</h1>
- <h2>Powered by <a href="https://www.portal.network/">Portal Network</a></h2>
- </div>
- <script>
- let index = location.href.lastIndexOf("?name=")
- let name = location.href.slice(index + 6)
- document.getElementById("name").innerHTML = name
- </script>
-</body>
-</html>
diff --git a/app/images/enslogo.svg b/app/images/enslogo.svg
new file mode 100644
index 000000000..20d94c0b1
--- /dev/null
+++ b/app/images/enslogo.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 72.52 80.95"><defs><style>.cls-3{fill:#a0a8d4}</style><linearGradient id="linear-gradient" x1="41.95" y1="2.57" x2="12.57" y2="34.42" gradientUnits="userSpaceOnUse"><stop offset=".58" stop-color="#a0a8d4"/><stop offset=".73" stop-color="#8791c7"/><stop offset=".91" stop-color="#6470b4"/></linearGradient><linearGradient id="linear-gradient-2" x1="42.57" y1="81.66" x2="71.96" y2="49.81" xlink:href="#linear-gradient"/><linearGradient id="linear-gradient-3" x1="42.26" y1="1.24" x2="42.26" y2="82.84" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#513eff"/><stop offset=".18" stop-color="#5157ff"/><stop offset=".57" stop-color="#5298ff"/><stop offset="1" stop-color="#52e5ff"/></linearGradient></defs><g style="isolation:isolate"><g id="Layer_1" data-name="Layer 1"><path d="M15.28 34.39c.8 1.71 2.78 5.09 2.78 5.09L40.95 1.64l-22.34 15.6a9.75 9.75 0 0 0-3.18 3.5 16.19 16.19 0 0 0-.15 13.65z" transform="translate(-6 -1.64)" fill="url(#linear-gradient)"/><path class="cls-3" d="M6.21 46.85a25.47 25.47 0 0 0 10 18.51l24.71 17.23s-15.46-22.28-28.5-44.45a22.39 22.39 0 0 1-2.62-7.56 12.1 12.1 0 0 1 0-3.63c-.34.63-1 1.92-1 1.92a29.35 29.35 0 0 0-2.67 8.55 52.28 52.28 0 0 0 .08 9.43z" transform="translate(-6 -1.64)"/><path d="M69.25 49.84c-.8-1.71-2.78-5.09-2.78-5.09L43.58 82.59 65.92 67a9.75 9.75 0 0 0 3.18-3.5 16.19 16.19 0 0 0 .15-13.66z" transform="translate(-6 -1.64)" fill="url(#linear-gradient-2)"/><path class="cls-3" d="M78.32 37.38a25.47 25.47 0 0 0-10-18.51L43.61 1.64s15.45 22.28 28.5 44.45a22.39 22.39 0 0 1 2.61 7.56 12.1 12.1 0 0 1 0 3.63c.34-.63 1-1.92 1-1.92a29.35 29.35 0 0 0 2.67-8.55 52.28 52.28 0 0 0-.07-9.43z" transform="translate(-6 -1.64)"/><path d="M15.43 20.74a9.75 9.75 0 0 1 3.18-3.5l22.34-15.6-22.89 37.85s-2-3.38-2.78-5.09a16.19 16.19 0 0 1 .15-13.66zM6.21 46.85a25.47 25.47 0 0 0 10 18.51l24.71 17.23s-15.46-22.28-28.5-44.45a22.39 22.39 0 0 1-2.62-7.56 12.1 12.1 0 0 1 0-3.63c-.34.63-1 1.92-1 1.92a29.35 29.35 0 0 0-2.67 8.55 52.28 52.28 0 0 0 .08 9.43zm63 3c-.8-1.71-2.78-5.09-2.78-5.09L43.58 82.59 65.92 67a9.75 9.75 0 0 0 3.18-3.5 16.19 16.19 0 0 0 .15-13.66zm9.07-12.46a25.47 25.47 0 0 0-10-18.51L43.61 1.64s15.45 22.28 28.5 44.45a22.39 22.39 0 0 1 2.61 7.56 12.1 12.1 0 0 1 0 3.63c.34-.63 1-1.92 1-1.92a29.35 29.35 0 0 0 2.67-8.55 52.28 52.28 0 0 0-.07-9.43z" transform="translate(-6 -1.64)" style="mix-blend-mode:color" fill="url(#linear-gradient-3)"/></g></g></svg> \ No newline at end of file
diff --git a/app/loading.html b/app/loading.html
index 71403a5ac..3b896b718 100644
--- a/app/loading.html
+++ b/app/loading.html
@@ -11,10 +11,10 @@
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
- width: 256px;
+ text-align: center;
}
#logo {
- width: 100%;
+ width: 256px;
animation: pulse 1s ease-in-out infinite;
}
@keyframes pulse {
@@ -33,13 +33,8 @@
</head>
<body>
<div id="div-logo">
- <img id="logo" src="./images/loginglogo.svg">
+ <img id="logo" src="./images/enslogo.svg">
+ <h1 class="center">MetaMask is querying ENS ...</h1>
</div>
- <script type="text/javascript">
- // redirect to 404 after one minute
- setTimeout(() => {
- location.href = './404.html'
- }, 60000)
- </script>
</body>
</html>
diff --git a/app/manifest.json b/app/manifest.json
index 2002dee76..0dbd09b65 100644
--- a/app/manifest.json
+++ b/app/manifest.json
@@ -1,7 +1,7 @@
{
"name": "__MSG_appName__",
"short_name": "__MSG_appName__",
- "version": "6.5.2",
+ "version": "6.6.0",
"manifest_version": 2,
"author": "https://metamask.io",
"description": "__MSG_appDescription__",
diff --git a/app/scripts/controllers/currency.js b/app/scripts/controllers/currency.js
deleted file mode 100644
index fce65fd9c..000000000
--- a/app/scripts/controllers/currency.js
+++ /dev/null
@@ -1,205 +0,0 @@
-const ObservableStore = require('obs-store')
-const extend = require('xtend')
-const log = require('loglevel')
-
-// every ten minutes
-const POLLING_INTERVAL = 600000
-
-class CurrencyController {
-
- /**
- * Controller responsible for managing data associated with the currently selected currency.
- *
- * @typedef {Object} CurrencyController
- * @param {object} opts Overrides the defaults for the initial state of this.store
- * @property {array} opts.initState initializes the the state of the CurrencyController. Can contain an
- * currentCurrency, conversionRate and conversionDate properties
- * @property {string} currentCurrency A 2-4 character shorthand that describes a specific currency, currently
- * selected by the user
- * @property {number} conversionRate The conversion rate from ETH to the selected currency.
- * @property {string} conversionDate The date at which the conversion rate was set. Expressed in in milliseconds
- * since midnight of January 1, 1970
- * @property {number} conversionInterval The id of the interval created by the scheduleConversionInterval method.
- * Used to clear an existing interval on subsequent calls of that method.
- * @property {string} nativeCurrency The ticker/symbol of the native chain currency
- *
- */
- constructor (opts = {}) {
- const initState = extend({
- currentCurrency: 'usd',
- conversionRate: 0,
- conversionDate: 'N/A',
- nativeCurrency: 'ETH',
- }, opts.initState)
- this.store = new ObservableStore(initState)
- }
-
- //
- // PUBLIC METHODS
- //
-
- /**
- * A getter for the nativeCurrency property
- *
- * @returns {string} A 2-4 character shorthand that describes the specific currency
- *
- */
- getNativeCurrency () {
- return this.store.getState().nativeCurrency
- }
-
- /**
- * A setter for the nativeCurrency property
- *
- * @param {string} nativeCurrency The new currency to set as the nativeCurrency in the store
- *
- */
- setNativeCurrency (nativeCurrency) {
- this.store.updateState({
- nativeCurrency,
- ticker: nativeCurrency,
- })
- }
-
- /**
- * A getter for the currentCurrency property
- *
- * @returns {string} A 2-4 character shorthand that describes a specific currency, currently selected by the user
- *
- */
- getCurrentCurrency () {
- return this.store.getState().currentCurrency
- }
-
- /**
- * A setter for the currentCurrency property
- *
- * @param {string} currentCurrency The new currency to set as the currentCurrency in the store
- *
- */
- setCurrentCurrency (currentCurrency) {
- this.store.updateState({ currentCurrency })
- }
-
- /**
- * A getter for the conversionRate property
- *
- * @returns {string} The conversion rate from ETH to the selected currency.
- *
- */
- getConversionRate () {
- return this.store.getState().conversionRate
- }
-
- /**
- * A setter for the conversionRate property
- *
- * @param {number} conversionRate The new rate to set as the conversionRate in the store
- *
- */
- setConversionRate (conversionRate) {
- this.store.updateState({ conversionRate })
- }
-
- /**
- * A getter for the conversionDate property
- *
- * @returns {string} The date at which the conversion rate was set. Expressed in milliseconds since midnight of
- * January 1, 1970
- *
- */
- getConversionDate () {
- return this.store.getState().conversionDate
- }
-
- /**
- * A setter for the conversionDate property
- *
- * @param {number} conversionDate The date, expressed in milliseconds since midnight of January 1, 1970, that the
- * conversionRate was set
- *
- */
- setConversionDate (conversionDate) {
- this.store.updateState({ conversionDate })
- }
-
- /**
- * Updates the conversionRate and conversionDate properties associated with the currentCurrency. Updated info is
- * fetched from an external API
- *
- */
- async updateConversionRate () {
- let currentCurrency, nativeCurrency
- try {
- currentCurrency = this.getCurrentCurrency()
- nativeCurrency = this.getNativeCurrency()
- // select api
- let apiUrl
- if (nativeCurrency === 'ETH') {
- // ETH
- apiUrl = `https://api.infura.io/v1/ticker/eth${currentCurrency.toLowerCase()}`
- } else {
- // ETC
- apiUrl = `https://min-api.cryptocompare.com/data/price?fsym=${nativeCurrency.toUpperCase()}&tsyms=${currentCurrency.toUpperCase()}`
- }
- // attempt request
- let response
- try {
- response = await fetch(apiUrl)
- } catch (err) {
- log.error(new Error(`CurrencyController - Failed to request currency from Infura:\n${err.stack}`))
- return
- }
- // parse response
- let rawResponse
- let parsedResponse
- try {
- rawResponse = await response.text()
- parsedResponse = JSON.parse(rawResponse)
- } catch (err) {
- log.error(new Error(`CurrencyController - Failed to parse response "${rawResponse}"`))
- return
- }
- // set conversion rate
- if (nativeCurrency === 'ETH') {
- // ETH
- this.setConversionRate(Number(parsedResponse.bid))
- this.setConversionDate(Number(parsedResponse.timestamp))
- } else {
- // ETC
- if (parsedResponse[currentCurrency.toUpperCase()]) {
- this.setConversionRate(Number(parsedResponse[currentCurrency.toUpperCase()]))
- this.setConversionDate(parseInt((new Date()).getTime() / 1000))
- } else {
- this.setConversionRate(0)
- this.setConversionDate('N/A')
- }
- }
- } catch (err) {
- // reset current conversion rate
- log.warn(`MetaMask - Failed to query currency conversion:`, nativeCurrency, currentCurrency, err)
- this.setConversionRate(0)
- this.setConversionDate('N/A')
- // throw error
- log.error(new Error(`CurrencyController - Failed to query rate for currency "${currentCurrency}":\n${err.stack}`))
- return
- }
- }
-
- /**
- * Creates a new poll, using setInterval, to periodically call updateConversionRate. The id of the interval is
- * stored at the controller's conversionInterval property. If it is called and such an id already exists, the
- * previous interval is clear and a new one is created.
- *
- */
- scheduleConversionInterval () {
- if (this.conversionInterval) {
- clearInterval(this.conversionInterval)
- }
- this.conversionInterval = setInterval(() => {
- this.updateConversionRate()
- }, POLLING_INTERVAL)
- }
-}
-
-module.exports = CurrencyController
diff --git a/app/scripts/controllers/provider-approval.js b/app/scripts/controllers/provider-approval.js
index 8206b2f8a..06c499780 100644
--- a/app/scripts/controllers/provider-approval.js
+++ b/app/scripts/controllers/provider-approval.js
@@ -38,7 +38,8 @@ class ProviderApprovalController extends SafeEventEmitter {
// only handle requestAccounts
if (req.method !== 'eth_requestAccounts') return next()
// if already approved or privacy mode disabled, return early
- if (this.shouldExposeAccounts(origin)) {
+ const isUnlocked = this.keyringController.memStore.getState().isUnlocked
+ if (this.shouldExposeAccounts(origin) && isUnlocked) {
res.result = [this.preferencesController.getSelectedAddress()]
return
}
diff --git a/app/scripts/controllers/token-rates.js b/app/scripts/controllers/token-rates.js
index 6b6265dba..9b86a9ebf 100644
--- a/app/scripts/controllers/token-rates.js
+++ b/app/scripts/controllers/token-rates.js
@@ -30,7 +30,7 @@ class TokenRatesController {
async updateExchangeRates () {
if (!this.isActive) { return }
const contractExchangeRates = {}
- const nativeCurrency = this.currency ? this.currency.getState().nativeCurrency.toLowerCase() : 'eth'
+ const nativeCurrency = this.currency ? this.currency.state.nativeCurrency.toLowerCase() : 'eth'
const pairs = this._tokens.map(token => token.address).join(',')
const query = `contract_addresses=${pairs}&vs_currencies=${nativeCurrency}`
if (this._tokens.length > 0) {
diff --git a/app/scripts/controllers/transactions/index.js b/app/scripts/controllers/transactions/index.js
index 79dba7833..1ae925835 100644
--- a/app/scripts/controllers/transactions/index.js
+++ b/app/scripts/controllers/transactions/index.js
@@ -17,7 +17,7 @@ const {
const TransactionStateManager = require('./tx-state-manager')
const TxGasUtil = require('./tx-gas-utils')
const PendingTransactionTracker = require('./pending-tx-tracker')
-const NonceTracker = require('./nonce-tracker')
+const NonceTracker = require('nonce-tracker')
const txUtils = require('./lib/util')
const cleanErrorStack = require('../../lib/cleanErrorStack')
const log = require('loglevel')
@@ -555,6 +555,7 @@ class TransactionController extends EventEmitter {
})
this.pendingTxTracker.on('tx:failed', this.txStateManager.setTxStatusFailed.bind(this.txStateManager))
this.pendingTxTracker.on('tx:confirmed', (txId) => this.confirmTransaction(txId))
+ this.pendingTxTracker.on('tx:dropped', this.txStateManager.setTxStatusDropped.bind(this.txStateManager))
this.pendingTxTracker.on('tx:block-update', (txMeta, latestBlockNumber) => {
if (!txMeta.firstRetryBlockNumber) {
txMeta.firstRetryBlockNumber = latestBlockNumber
diff --git a/app/scripts/controllers/transactions/nonce-tracker.js b/app/scripts/controllers/transactions/nonce-tracker.js
deleted file mode 100644
index 421036368..000000000
--- a/app/scripts/controllers/transactions/nonce-tracker.js
+++ /dev/null
@@ -1,161 +0,0 @@
-const EthQuery = require('ethjs-query')
-const assert = require('assert')
-const Mutex = require('await-semaphore').Mutex
-/**
- @param opts {Object}
- @param {Object} opts.provider a ethereum provider
- @param {Function} opts.getPendingTransactions a function that returns an array of txMeta
- whosee status is `submitted`
- @param {Function} opts.getConfirmedTransactions a function that returns an array of txMeta
- whose status is `confirmed`
- @class
-*/
-class NonceTracker {
-
- constructor ({ provider, blockTracker, getPendingTransactions, getConfirmedTransactions }) {
- this.provider = provider
- this.blockTracker = blockTracker
- this.ethQuery = new EthQuery(provider)
- this.getPendingTransactions = getPendingTransactions
- this.getConfirmedTransactions = getConfirmedTransactions
- this.lockMap = {}
- }
-
- /**
- @returns {Promise<Object>} with the key releaseLock (the gloabl mutex)
- */
- async getGlobalLock () {
- const globalMutex = this._lookupMutex('global')
- // await global mutex free
- const releaseLock = await globalMutex.acquire()
- return { releaseLock }
- }
-
- /**
- * @typedef NonceDetails
- * @property {number} highestLocallyConfirmed - A hex string of the highest nonce on a confirmed transaction.
- * @property {number} nextNetworkNonce - The next nonce suggested by the eth_getTransactionCount method.
- * @property {number} highestSuggested - The maximum between the other two, the number returned.
- */
-
- /**
- this will return an object with the `nextNonce` `nonceDetails` of type NonceDetails, and the releaseLock
- Note: releaseLock must be called after adding a signed tx to pending transactions (or discarding).
-
- @param address {string} the hex string for the address whose nonce we are calculating
- @returns {Promise<NonceDetails>}
- */
- async getNonceLock (address) {
- // await global mutex free
- await this._globalMutexFree()
- // await lock free, then take lock
- const releaseLock = await this._takeMutex(address)
- try {
- // evaluate multiple nextNonce strategies
- const nonceDetails = {}
- const networkNonceResult = await this._getNetworkNextNonce(address)
- const highestLocallyConfirmed = this._getHighestLocallyConfirmed(address)
- const nextNetworkNonce = networkNonceResult.nonce
- const highestSuggested = Math.max(nextNetworkNonce, highestLocallyConfirmed)
-
- const pendingTxs = this.getPendingTransactions(address)
- const localNonceResult = this._getHighestContinuousFrom(pendingTxs, highestSuggested) || 0
-
- nonceDetails.params = {
- highestLocallyConfirmed,
- highestSuggested,
- nextNetworkNonce,
- }
- nonceDetails.local = localNonceResult
- nonceDetails.network = networkNonceResult
-
- const nextNonce = Math.max(networkNonceResult.nonce, localNonceResult.nonce)
- assert(Number.isInteger(nextNonce), `nonce-tracker - nextNonce is not an integer - got: (${typeof nextNonce}) "${nextNonce}"`)
-
- // return nonce and release cb
- return { nextNonce, nonceDetails, releaseLock }
- } catch (err) {
- // release lock if we encounter an error
- releaseLock()
- throw err
- }
- }
-
- async _globalMutexFree () {
- const globalMutex = this._lookupMutex('global')
- const releaseLock = await globalMutex.acquire()
- releaseLock()
- }
-
- async _takeMutex (lockId) {
- const mutex = this._lookupMutex(lockId)
- const releaseLock = await mutex.acquire()
- return releaseLock
- }
-
- _lookupMutex (lockId) {
- let mutex = this.lockMap[lockId]
- if (!mutex) {
- mutex = new Mutex()
- this.lockMap[lockId] = mutex
- }
- return mutex
- }
-
- async _getNetworkNextNonce (address) {
- // calculate next nonce
- // we need to make sure our base count
- // and pending count are from the same block
- const blockNumber = await this.blockTracker.getLatestBlock()
- const baseCountBN = await this.ethQuery.getTransactionCount(address, blockNumber)
- const baseCount = baseCountBN.toNumber()
- assert(Number.isInteger(baseCount), `nonce-tracker - baseCount is not an integer - got: (${typeof baseCount}) "${baseCount}"`)
- const nonceDetails = { blockNumber, baseCount }
- return { name: 'network', nonce: baseCount, details: nonceDetails }
- }
-
- _getHighestLocallyConfirmed (address) {
- const confirmedTransactions = this.getConfirmedTransactions(address)
- const highest = this._getHighestNonce(confirmedTransactions)
- return Number.isInteger(highest) ? highest + 1 : 0
- }
-
- _getHighestNonce (txList) {
- const nonces = txList.map((txMeta) => {
- const nonce = txMeta.txParams.nonce
- assert(typeof nonce, 'string', 'nonces should be hex strings')
- return parseInt(nonce, 16)
- })
- const highestNonce = Math.max.apply(null, nonces)
- return highestNonce
- }
-
- /**
- @typedef {object} highestContinuousFrom
- @property {string} - name the name for how the nonce was calculated based on the data used
- @property {number} - nonce the next suggested nonce
- @property {object} - details the provided starting nonce that was used (for debugging)
- */
- /**
- @param txList {array} - list of txMeta's
- @param startPoint {number} - the highest known locally confirmed nonce
- @returns {highestContinuousFrom}
- */
- _getHighestContinuousFrom (txList, startPoint) {
- const nonces = txList.map((txMeta) => {
- const nonce = txMeta.txParams.nonce
- assert(typeof nonce, 'string', 'nonces should be hex strings')
- return parseInt(nonce, 16)
- })
-
- let highest = startPoint
- while (nonces.includes(highest)) {
- highest++
- }
-
- return { name: 'local', nonce: highest, details: { startPoint, highest } }
- }
-
-}
-
-module.exports = NonceTracker
diff --git a/app/scripts/controllers/transactions/pending-tx-tracker.js b/app/scripts/controllers/transactions/pending-tx-tracker.js
index 4bf40b1db..bc11f6633 100644
--- a/app/scripts/controllers/transactions/pending-tx-tracker.js
+++ b/app/scripts/controllers/transactions/pending-tx-tracker.js
@@ -22,6 +22,7 @@ const EthQuery = require('ethjs-query')
class PendingTransactionTracker extends EventEmitter {
constructor (config) {
super()
+ this.droppedBuffer = {}
this.query = new EthQuery(config.provider)
this.nonceTracker = config.nonceTracker
this.getPendingTransactions = config.getPendingTransactions
@@ -139,22 +140,42 @@ class PendingTransactionTracker extends EventEmitter {
const noTxHashErr = new Error('We had an error while submitting this transaction, please try again.')
noTxHashErr.name = 'NoTxHashError'
this.emit('tx:failed', txId, noTxHashErr)
+
return
}
- // If another tx with the same nonce is mined, set as failed.
+ // If another tx with the same nonce is mined, set as dropped.
const taken = await this._checkIfNonceIsTaken(txMeta)
- if (taken) {
- const nonceTakenErr = new Error('Another transaction with this nonce has been mined.')
- nonceTakenErr.name = 'NonceTakenErr'
- return this.emit('tx:failed', txId, nonceTakenErr)
+ let dropped
+ try {
+ // check the network if the nonce is ahead the tx
+ // and the tx has not been mined into a block
+
+ dropped = await this._checkIftxWasDropped(txMeta)
+ // the dropped buffer is in case we ask a node for the tx
+ // that is behind the node we asked for tx count
+ // IS A SECURITY FOR HITTING NODES IN INFURA THAT COULD GO OUT
+ // OF SYNC.
+ // on the next block event it will return fire as dropped
+ if (dropped && !this.droppedBuffer[txHash]) {
+ this.droppedBuffer[txHash] = true
+ dropped = false
+ } else if (dropped && this.droppedBuffer[txHash]) {
+ // clean up
+ delete this.droppedBuffer[txHash]
+ }
+
+ } catch (e) {
+ log.error(e)
+ }
+ if (taken || dropped) {
+ return this.emit('tx:dropped', txId)
}
// get latest transaction status
try {
- const txParams = await this.query.getTransactionByHash(txHash)
- if (!txParams) return
- if (txParams.blockNumber) {
+ const { blockNumber } = await this.query.getTransactionByHash(txHash) || {}
+ if (blockNumber) {
this.emit('tx:confirmed', txId)
}
} catch (err) {
@@ -165,6 +186,22 @@ class PendingTransactionTracker extends EventEmitter {
this.emit('tx:warning', txMeta, err)
}
}
+ /**
+ checks to see if if the tx's nonce has been used by another transaction
+ @param txMeta {Object} - txMeta object
+ @emits tx:dropped
+ @returns {boolean}
+ */
+
+ async _checkIftxWasDropped (txMeta) {
+ const { txParams: { nonce, from }, hash } = txMeta
+ const nextNonce = await this.query.getTransactionCount(from)
+ const { blockNumber } = await this.query.getTransactionByHash(hash) || {}
+ if (!blockNumber && parseInt(nextNonce) > parseInt(nonce)) {
+ return true
+ }
+ return false
+ }
/**
checks to see if a confirmed txMeta has the same nonce
diff --git a/app/scripts/inpage.js b/app/scripts/inpage.js
index a4fb552f1..fb16dd43d 100644
--- a/app/scripts/inpage.js
+++ b/app/scripts/inpage.js
@@ -33,8 +33,8 @@ inpageProvider.setMaxListeners(100)
inpageProvider.enable = function ({ force } = {}) {
return new Promise((resolve, reject) => {
inpageProvider.sendAsync({ method: 'eth_requestAccounts', params: [force] }, (error, response) => {
- if (error) {
- reject(error)
+ if (error || response.error) {
+ reject(error || response.error)
} else {
resolve(response.result)
}
diff --git a/app/scripts/lib/ens-ipfs/contracts/registrar.js b/app/scripts/lib/ens-ipfs/contracts/registry.js
index 99ca24458..99ca24458 100644
--- a/app/scripts/lib/ens-ipfs/contracts/registrar.js
+++ b/app/scripts/lib/ens-ipfs/contracts/registry.js
diff --git a/app/scripts/lib/ens-ipfs/contracts/resolver.js b/app/scripts/lib/ens-ipfs/contracts/resolver.js
index 1bf3f90ce..b61fbed88 100644
--- a/app/scripts/lib/ens-ipfs/contracts/resolver.js
+++ b/app/scripts/lib/ens-ipfs/contracts/resolver.js
@@ -1,2 +1,2 @@
module.exports =
-[{'constant': true, 'inputs': [{'name': 'interfaceID', 'type': 'bytes4'}], 'name': 'supportsInterface', 'outputs': [{'name': '', 'type': 'bool'}], 'payable': false, 'type': 'function'}, {'constant': true, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'contentTypes', 'type': 'uint256'}], 'name': 'ABI', 'outputs': [{'name': 'contentType', 'type': 'uint256'}, {'name': 'data', 'type': 'bytes'}], 'payable': false, 'type': 'function'}, {'constant': false, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'x', 'type': 'bytes32'}, {'name': 'y', 'type': 'bytes32'}], 'name': 'setPubkey', 'outputs': [], 'payable': false, 'type': 'function'}, {'constant': true, 'inputs': [{'name': 'node', 'type': 'bytes32'}], 'name': 'content', 'outputs': [{'name': 'ret', 'type': 'bytes32'}], 'payable': false, 'type': 'function'}, {'constant': true, 'inputs': [{'name': 'node', 'type': 'bytes32'}], 'name': 'addr', 'outputs': [{'name': 'ret', 'type': 'address'}], 'payable': false, 'type': 'function'}, {'constant': false, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'contentType', 'type': 'uint256'}, {'name': 'data', 'type': 'bytes'}], 'name': 'setABI', 'outputs': [], 'payable': false, 'type': 'function'}, {'constant': true, 'inputs': [{'name': 'node', 'type': 'bytes32'}], 'name': 'name', 'outputs': [{'name': 'ret', 'type': 'string'}], 'payable': false, 'type': 'function'}, {'constant': false, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'name', 'type': 'string'}], 'name': 'setName', 'outputs': [], 'payable': false, 'type': 'function'}, {'constant': false, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'hash', 'type': 'bytes32'}], 'name': 'setContent', 'outputs': [], 'payable': false, 'type': 'function'}, {'constant': true, 'inputs': [{'name': 'node', 'type': 'bytes32'}], 'name': 'pubkey', 'outputs': [{'name': 'x', 'type': 'bytes32'}, {'name': 'y', 'type': 'bytes32'}], 'payable': false, 'type': 'function'}, {'constant': false, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'addr', 'type': 'address'}], 'name': 'setAddr', 'outputs': [], 'payable': false, 'type': 'function'}, {'inputs': [{'name': 'ensAddr', 'type': 'address'}], 'payable': false, 'type': 'constructor'}, {'anonymous': false, 'inputs': [{'indexed': true, 'name': 'node', 'type': 'bytes32'}, {'indexed': false, 'name': 'a', 'type': 'address'}], 'name': 'AddrChanged', 'type': 'event'}, {'anonymous': false, 'inputs': [{'indexed': true, 'name': 'node', 'type': 'bytes32'}, {'indexed': false, 'name': 'hash', 'type': 'bytes32'}], 'name': 'ContentChanged', 'type': 'event'}, {'anonymous': false, 'inputs': [{'indexed': true, 'name': 'node', 'type': 'bytes32'}, {'indexed': false, 'name': 'name', 'type': 'string'}], 'name': 'NameChanged', 'type': 'event'}, {'anonymous': false, 'inputs': [{'indexed': true, 'name': 'node', 'type': 'bytes32'}, {'indexed': true, 'name': 'contentType', 'type': 'uint256'}], 'name': 'ABIChanged', 'type': 'event'}, {'anonymous': false, 'inputs': [{'indexed': true, 'name': 'node', 'type': 'bytes32'}, {'indexed': false, 'name': 'x', 'type': 'bytes32'}, {'indexed': false, 'name': 'y', 'type': 'bytes32'}], 'name': 'PubkeyChanged', 'type': 'event'}]
+[{'constant': false, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'hash', 'type': 'bytes32'}], 'name': 'setContent', 'outputs': [], 'payable': false, 'stateMutability': 'nonpayable', 'type': 'function'}, {'constant': true, 'inputs': [{'name': 'node', 'type': 'bytes32'}], 'name': 'content', 'outputs': [{'name': '', 'type': 'bytes32'}], 'payable': false, 'stateMutability': 'view', 'type': 'function'}, {'constant': true, 'inputs': [{'name': 'interfaceID', 'type': 'bytes4'}], 'name': 'supportsInterface', 'outputs': [{'name': '', 'type': 'bool'}], 'payable': false, 'stateMutability': 'pure', 'type': 'function'}, {'constant': false, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'key', 'type': 'string'}, {'name': 'value', 'type': 'string'}], 'name': 'setText', 'outputs': [], 'payable': false, 'stateMutability': 'nonpayable', 'type': 'function'}, {'constant': true, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'contentTypes', 'type': 'uint256'}], 'name': 'ABI', 'outputs': [{'name': 'contentType', 'type': 'uint256'}, {'name': 'data', 'type': 'bytes'}], 'payable': false, 'stateMutability': 'view', 'type': 'function'}, {'constant': false, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'x', 'type': 'bytes32'}, {'name': 'y', 'type': 'bytes32'}], 'name': 'setPubkey', 'outputs': [], 'payable': false, 'stateMutability': 'nonpayable', 'type': 'function'}, {'constant': false, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'hash', 'type': 'bytes'}], 'name': 'setContenthash', 'outputs': [], 'payable': false, 'stateMutability': 'nonpayable', 'type': 'function'}, {'constant': true, 'inputs': [{'name': 'node', 'type': 'bytes32'}], 'name': 'addr', 'outputs': [{'name': '', 'type': 'address'}], 'payable': false, 'stateMutability': 'view', 'type': 'function'}, {'constant': true, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'key', 'type': 'string'}], 'name': 'text', 'outputs': [{'name': '', 'type': 'string'}], 'payable': false, 'stateMutability': 'view', 'type': 'function'}, {'constant': false, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'contentType', 'type': 'uint256'}, {'name': 'data', 'type': 'bytes'}], 'name': 'setABI', 'outputs': [], 'payable': false, 'stateMutability': 'nonpayable', 'type': 'function'}, {'constant': true, 'inputs': [{'name': 'node', 'type': 'bytes32'}], 'name': 'name', 'outputs': [{'name': '', 'type': 'string'}], 'payable': false, 'stateMutability': 'view', 'type': 'function'}, {'constant': false, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'name', 'type': 'string'}], 'name': 'setName', 'outputs': [], 'payable': false, 'stateMutability': 'nonpayable', 'type': 'function'}, {'constant': true, 'inputs': [{'name': 'node', 'type': 'bytes32'}], 'name': 'contenthash', 'outputs': [{'name': '', 'type': 'bytes'}], 'payable': false, 'stateMutability': 'view', 'type': 'function'}, {'constant': true, 'inputs': [{'name': 'node', 'type': 'bytes32'}], 'name': 'pubkey', 'outputs': [{'name': 'x', 'type': 'bytes32'}, {'name': 'y', 'type': 'bytes32'}], 'payable': false, 'stateMutability': 'view', 'type': 'function'}, {'constant': false, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'addr', 'type': 'address'}], 'name': 'setAddr', 'outputs': [], 'payable': false, 'stateMutability': 'nonpayable', 'type': 'function'}, {'inputs': [{'name': 'ensAddr', 'type': 'address'}], 'payable': false, 'stateMutability': 'nonpayable', 'type': 'constructor'}, {'anonymous': false, 'inputs': [{'indexed': true, 'name': 'node', 'type': 'bytes32'}, {'indexed': false, 'name': 'a', 'type': 'address'}], 'name': 'AddrChanged', 'type': 'event'}, {'anonymous': false, 'inputs': [{'indexed': true, 'name': 'node', 'type': 'bytes32'}, {'indexed': false, 'name': 'name', 'type': 'string'}], 'name': 'NameChanged', 'type': 'event'}, {'anonymous': false, 'inputs': [{'indexed': true, 'name': 'node', 'type': 'bytes32'}, {'indexed': true, 'name': 'contentType', 'type': 'uint256'}], 'name': 'ABIChanged', 'type': 'event'}, {'anonymous': false, 'inputs': [{'indexed': true, 'name': 'node', 'type': 'bytes32'}, {'indexed': false, 'name': 'x', 'type': 'bytes32'}, {'indexed': false, 'name': 'y', 'type': 'bytes32'}], 'name': 'PubkeyChanged', 'type': 'event'}, {'anonymous': false, 'inputs': [{'indexed': true, 'name': 'node', 'type': 'bytes32'}, {'indexed': false, 'name': 'indexedKey', 'type': 'string'}, {'indexed': false, 'name': 'key', 'type': 'string'}], 'name': 'TextChanged', 'type': 'event'}, {'anonymous': false, 'inputs': [{'indexed': true, 'name': 'node', 'type': 'bytes32'}, {'indexed': false, 'name': 'hash', 'type': 'bytes'}], 'name': 'ContenthashChanged', 'type': 'event'}]
diff --git a/app/scripts/lib/ens-ipfs/resolver.js b/app/scripts/lib/ens-ipfs/resolver.js
index b98566190..a0af263bc 100644
--- a/app/scripts/lib/ens-ipfs/resolver.js
+++ b/app/scripts/lib/ens-ipfs/resolver.js
@@ -1,9 +1,9 @@
const namehash = require('eth-ens-namehash')
-const multihash = require('multihashes')
const Eth = require('ethjs-query')
const EthContract = require('ethjs-contract')
-const registrarAbi = require('./contracts/registrar')
+const registryAbi = require('./contracts/registry')
const resolverAbi = require('./contracts/resolver')
+const contentHash = require('content-hash')
module.exports = resolveEnsToIpfsContentId
@@ -12,37 +12,47 @@ async function resolveEnsToIpfsContentId ({ provider, name }) {
const eth = new Eth(provider)
const hash = namehash.hash(name)
const contract = new EthContract(eth)
- // lookup registrar
+ // lookup registry
const chainId = Number.parseInt(await eth.net_version(), 10)
- const registrarAddress = getRegistrarForChainId(chainId)
- if (!registrarAddress) {
- throw new Error(`EnsIpfsResolver - no known ens-ipfs registrar for chainId "${chainId}"`)
+ const registryAddress = getRegistryForChainId(chainId)
+ if (!registryAddress) {
+ throw new Error(`EnsIpfsResolver - no known ens-ipfs registry for chainId "${chainId}"`)
}
- const Registrar = contract(registrarAbi).at(registrarAddress)
+ const Registry = contract(registryAbi).at(registryAddress)
// lookup resolver
- const resolverLookupResult = await Registrar.resolver(hash)
+ const resolverLookupResult = await Registry.resolver(hash)
const resolverAddress = resolverLookupResult[0]
if (hexValueIsEmpty(resolverAddress)) {
throw new Error(`EnsIpfsResolver - no resolver found for name "${name}"`)
}
const Resolver = contract(resolverAbi).at(resolverAddress)
- // lookup content id
- const contentLookupResult = await Resolver.content(hash)
- const contentHash = contentLookupResult[0]
- if (hexValueIsEmpty(contentHash)) {
- throw new Error(`EnsIpfsResolver - no content ID found for name "${name}"`)
+
+ const isEIP1577Compliant = await Resolver.supportsInterface('0xbc1c58d1')
+ const isLegacyResolver = await Resolver.supportsInterface('0xd8389dc5')
+ if (isEIP1577Compliant[0]) {
+ const contentLookupResult = await Resolver.contenthash(hash)
+ const rawContentHash = contentLookupResult[0]
+ const decodedContentHash = contentHash.decode(rawContentHash)
+ const type = contentHash.getCodec(rawContentHash)
+ return {type: type, hash: decodedContentHash}
+ }
+ if (isLegacyResolver[0]) {
+ // lookup content id
+ const contentLookupResult = await Resolver.content(hash)
+ const content = contentLookupResult[0]
+ if (hexValueIsEmpty(content)) {
+ throw new Error(`EnsIpfsResolver - no content ID found for name "${name}"`)
+ }
+ return {type: 'swarm-ns', hash: content.slice(2)}
}
- const nonPrefixedHex = contentHash.slice(2)
- const buffer = multihash.fromHexString(nonPrefixedHex)
- const contentId = multihash.toB58String(multihash.encode(buffer, 'sha2-256'))
- return contentId
+ throw new Error(`EnsIpfsResolver - the resolver for name "${name}" is not standard, it should either supports contenthash() or content()`)
}
function hexValueIsEmpty (value) {
return [undefined, null, '0x', '0x0', '0x0000000000000000000000000000000000000000000000000000000000000000'].includes(value)
}
-function getRegistrarForChainId (chainId) {
+function getRegistryForChainId (chainId) {
switch (chainId) {
// mainnet
case 1:
@@ -50,5 +60,11 @@ function getRegistrarForChainId (chainId) {
// ropsten
case 3:
return '0x112234455c3a32fd11230c42e7bccd4a84e02010'
+ // rinkeby
+ case 4:
+ return '0xe7410170f87102df0055eb195163a03b7f2bff4a'
+ // goerli
+ case 5:
+ return '0x112234455c3a32fd11230c42e7bccd4a84e02010'
}
}
diff --git a/app/scripts/lib/ens-ipfs/setup.js b/app/scripts/lib/ens-ipfs/setup.js
index df756d0f7..82679db5d 100644
--- a/app/scripts/lib/ens-ipfs/setup.js
+++ b/app/scripts/lib/ens-ipfs/setup.js
@@ -37,27 +37,25 @@ function setupEnsIpfsResolver ({ provider }) {
async function attemptResolve ({ tabId, name, path, search }) {
extension.tabs.update(tabId, { url: `loading.html` })
+ let url = `https://manager.ens.domains/name/${name}`
try {
- const ipfsContentId = await resolveEnsToIpfsContentId({ provider, name })
- const url = `https://gateway.ipfs.io/ipfs/${ipfsContentId}${path}${search || ''}`
- try {
- // check if ipfs gateway has result
- const response = await fetch(url, { method: 'HEAD' })
- // if failure, redirect to 404 page
- if (response.status !== 200) {
- extension.tabs.update(tabId, { url: '404.html' })
- return
+ const {type, hash} = await resolveEnsToIpfsContentId({ provider, name })
+ if (type === 'ipfs-ns') {
+ const resolvedUrl = `https://gateway.ipfs.io/ipfs/${hash}${path}${search || ''}`
+ try {
+ // check if ipfs gateway has result
+ const response = await fetch(resolvedUrl, { method: 'HEAD' })
+ if (response.status === 200) url = resolvedUrl
+ } catch (err) {
+ console.warn(err)
}
- // otherwise redirect to the correct page
- extension.tabs.update(tabId, { url })
- } catch (err) {
- console.warn(err)
- // if HEAD fetch failed, redirect so user can see relevant error page
- extension.tabs.update(tabId, { url })
+ } else if (type === 'swarm-ns') {
+ url = `https://swarm-gateways.net/bzz:/${hash}${path}${search || ''}`
}
} catch (err) {
console.warn(err)
- extension.tabs.update(tabId, { url: `error.html?name=${name}` })
+ } finally {
+ extension.tabs.update(tabId, { url })
}
}
}
diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js
index 242028c92..0b6f5fcb5 100644
--- a/app/scripts/metamask-controller.js
+++ b/app/scripts/metamask-controller.js
@@ -26,7 +26,6 @@ const KeyringController = require('eth-keyring-controller')
const NetworkController = require('./controllers/network')
const PreferencesController = require('./controllers/preferences')
const AppStateController = require('./controllers/app-state')
-const CurrencyController = require('./controllers/currency')
const InfuraController = require('./controllers/infura')
const CachedBalancesController = require('./controllers/cached-balances')
const RecentBlocksController = require('./controllers/recent-blocks')
@@ -56,6 +55,7 @@ const ethUtil = require('ethereumjs-util')
const sigUtil = require('eth-sig-util')
const {
AddressBookController,
+ CurrencyRateController,
ShapeShiftController,
PhishingController,
} = require('gaba')
@@ -109,11 +109,7 @@ module.exports = class MetamaskController extends EventEmitter {
})
// currency controller
- this.currencyController = new CurrencyController({
- initState: initState.CurrencyController,
- })
- this.currencyController.updateConversionRate()
- this.currencyController.scheduleConversionInterval()
+ this.currencyRateController = new CurrencyRateController(undefined, initState.CurrencyController)
// infura controller
this.infuraController = new InfuraController({
@@ -130,7 +126,7 @@ module.exports = class MetamaskController extends EventEmitter {
// token exchange rate tracker
this.tokenRatesController = new TokenRatesController({
- currency: this.currencyController.store,
+ currency: this.currencyRateController,
preferences: this.preferencesController.store,
})
@@ -232,8 +228,7 @@ module.exports = class MetamaskController extends EventEmitter {
})
this.networkController.on('networkDidChange', () => {
this.balancesController.updateAllBalances()
- const currentCurrency = this.currencyController.getCurrentCurrency()
- this.setCurrentCurrency(currentCurrency, function () {})
+ this.setCurrentCurrency(this.currencyRateController.state.currentCurrency, function () {})
})
this.balancesController.updateAllBalances()
@@ -262,7 +257,7 @@ module.exports = class MetamaskController extends EventEmitter {
KeyringController: this.keyringController.store,
PreferencesController: this.preferencesController.store,
AddressBookController: this.addressBookController,
- CurrencyController: this.currencyController.store,
+ CurrencyController: this.currencyRateController,
ShapeShiftController: this.shapeshiftController,
NetworkController: this.networkController.store,
InfuraController: this.infuraController.store,
@@ -284,7 +279,7 @@ module.exports = class MetamaskController extends EventEmitter {
PreferencesController: this.preferencesController.store,
RecentBlocksController: this.recentBlocksController.store,
AddressBookController: this.addressBookController,
- CurrencyController: this.currencyController.store,
+ CurrencyController: this.currencyRateController,
ShapeshiftController: this.shapeshiftController,
InfuraController: this.infuraController.store,
ProviderApprovalController: this.providerApprovalController.store,
@@ -1596,16 +1591,13 @@ module.exports = class MetamaskController extends EventEmitter {
setCurrentCurrency (currencyCode, cb) {
const { ticker } = this.networkController.getNetworkConfig()
try {
- this.currencyController.setNativeCurrency(ticker)
- this.currencyController.setCurrentCurrency(currencyCode)
- this.currencyController.updateConversionRate()
- const data = {
- nativeCurrency: ticker || 'ETH',
- conversionRate: this.currencyController.getConversionRate(),
- currentCurrency: this.currencyController.getCurrentCurrency(),
- conversionDate: this.currencyController.getConversionDate(),
+ const currencyState = {
+ nativeCurrency: ticker,
+ currentCurrency: currencyCode,
}
- cb(null, data)
+ this.currencyRateController.update(currencyState)
+ this.currencyRateController.configure(currencyState)
+ cb(null, this.currencyRateController.state)
} catch (err) {
cb(err)
}
diff --git a/docs/creating-metrics-events.md b/docs/creating-metrics-events.md
new file mode 100644
index 000000000..7e7f2d77d
--- /dev/null
+++ b/docs/creating-metrics-events.md
@@ -0,0 +1,71 @@
+## Creating Metrics Events
+
+The `metricsEvent` method is made available to all components via context. This is done in `metamask-extension/ui/app/helpers/higher-order-components/metametrics/metametrics.provider.js`. As such, it can be called in all components by first adding it to the context proptypes:
+
+```
+static contextTypes = {
+ t: PropTypes.func,
+ metricsEvent: PropTypes.func,
+}
+```
+
+and then accessing it on `this.context`.
+
+Below is an example of a metrics event call:
+
+```
+this.context.metricsEvent({
+ eventOpts: {
+ category: 'Navigation',
+ action: 'Main Menu',
+ name: 'Switched Account',
+ },
+})
+```
+
+### Base Schema
+
+Every `metricsEvent` call is passed an object that must have an `eventOpts` property. This property is an object that itself must have three properties:
+- category: categorizes events according to the schema we have set up in our matomo.org instance
+- action: usually describes the page on which the event takes place, or sometimes a significant subsections of a page
+- name: a very specific descriptor of the event
+
+### Implicit properties
+
+All metrics events send the following data when called:
+- network
+- environmentType
+- activeCurrency
+- accountType
+- numberOfTokens
+- numberOfAccounts
+
+These are added to the metrics event via the metametrics provider.
+
+### Custom Variables
+
+Metrics events can include custom variables. These are included within the `customVariables` property that is a first-level property within first param passed to `metricsEvent`.
+
+For example:
+```
+this.context.metricsEvent({
+ eventOpts: {
+ category: 'Settings',
+ action: 'Custom RPC',
+ name: 'Error',
+ },
+ customVariables: {
+ networkId: newRpc,
+ chainId,
+ },
+})
+```
+
+Custom variables can have custom property names and values can be strings or numbers.
+
+**To include a custom variable, there are a set of necessary steps you must take.**
+
+1. First you must declare a constant equal to the desired name of the custom variable property in `metamask-extension/ui/app/helpers/utils/metametrics.util.js` under `//Custom Variable Declarations`
+1. Then you must add that name to the `customVariableNameIdMap` declaration
+ 1. The id must be between 1 and 5
+ 1. There can be no more than 5 custom variables assigned ids on a given url
diff --git a/package-lock.json b/package-lock.json
index 8844b21a5..e6cdb5cff 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1819,7 +1819,8 @@
"abbrev": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
- "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
+ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
+ "dev": true
},
"abi-decoder": {
"version": "1.0.9",
@@ -1838,7 +1839,7 @@
"dependencies": {
"bignumber.js": {
"version": "git+https://github.com/debris/bignumber.js.git#94d7146671b9719e00a09c29b01a691bc85048c2",
- "from": "git+https://github.com/debris/bignumber.js.git#94d7146671b9719e00a09c29b01a691bc85048c2"
+ "from": "bignumber.js@git+https://github.com/debris/bignumber.js.git#94d7146671b9719e00a09c29b01a691bc85048c2"
},
"chai": {
"version": "3.5.0",
@@ -2273,6 +2274,535 @@
"locate-path": "^3.0.0"
}
},
+ "fsevents": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.4.tgz",
+ "integrity": "sha512-z8H8/diyk76B7q5wg+Ud0+CqzcAF3mBBI/bA5ne5zrRUUIvNkJY//D3BqyH571KuAC4Nr7Rw7CjWX4r0y9DvNg==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "nan": "^2.9.2",
+ "node-pre-gyp": "^0.10.0"
+ },
+ "dependencies": {
+ "abbrev": {
+ "version": "1.1.1",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "ansi-regex": {
+ "version": "2.1.1",
+ "bundled": true,
+ "dev": true
+ },
+ "aproba": {
+ "version": "1.2.0",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "are-we-there-yet": {
+ "version": "1.1.4",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "delegates": "^1.0.0",
+ "readable-stream": "^2.0.6"
+ }
+ },
+ "balanced-match": {
+ "version": "1.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "chownr": {
+ "version": "1.0.1",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "code-point-at": {
+ "version": "1.1.0",
+ "bundled": true,
+ "dev": true
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "bundled": true,
+ "dev": true
+ },
+ "console-control-strings": {
+ "version": "1.1.0",
+ "bundled": true,
+ "dev": true
+ },
+ "core-util-is": {
+ "version": "1.0.2",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "debug": {
+ "version": "2.6.9",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "deep-extend": {
+ "version": "0.5.1",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "delegates": {
+ "version": "1.0.0",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "detect-libc": {
+ "version": "1.0.3",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "fs-minipass": {
+ "version": "1.2.5",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "minipass": "^2.2.1"
+ }
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "gauge": {
+ "version": "2.7.4",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "aproba": "^1.0.3",
+ "console-control-strings": "^1.0.0",
+ "has-unicode": "^2.0.0",
+ "object-assign": "^4.1.0",
+ "signal-exit": "^3.0.0",
+ "string-width": "^1.0.1",
+ "strip-ansi": "^3.0.1",
+ "wide-align": "^1.1.0"
+ }
+ },
+ "glob": {
+ "version": "7.1.2",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "has-unicode": {
+ "version": "2.0.1",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "iconv-lite": {
+ "version": "0.4.21",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "safer-buffer": "^2.1.0"
+ }
+ },
+ "ignore-walk": {
+ "version": "3.0.1",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "minimatch": "^3.0.4"
+ }
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.3",
+ "bundled": true,
+ "dev": true
+ },
+ "ini": {
+ "version": "1.3.5",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "1.0.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "number-is-nan": "^1.0.0"
+ }
+ },
+ "isarray": {
+ "version": "1.0.0",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "minimatch": {
+ "version": "3.0.4",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "minimist": {
+ "version": "0.0.8",
+ "bundled": true,
+ "dev": true
+ },
+ "minipass": {
+ "version": "2.2.4",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "safe-buffer": "^5.1.1",
+ "yallist": "^3.0.0"
+ }
+ },
+ "minizlib": {
+ "version": "1.1.0",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "minipass": "^2.2.1"
+ }
+ },
+ "mkdirp": {
+ "version": "0.5.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "minimist": "0.0.8"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "needle": {
+ "version": "2.2.0",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "debug": "^2.1.2",
+ "iconv-lite": "^0.4.4",
+ "sax": "^1.2.4"
+ }
+ },
+ "node-pre-gyp": {
+ "version": "0.10.0",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "detect-libc": "^1.0.2",
+ "mkdirp": "^0.5.1",
+ "needle": "^2.2.0",
+ "nopt": "^4.0.1",
+ "npm-packlist": "^1.1.6",
+ "npmlog": "^4.0.2",
+ "rc": "^1.1.7",
+ "rimraf": "^2.6.1",
+ "semver": "^5.3.0",
+ "tar": "^4"
+ }
+ },
+ "nopt": {
+ "version": "4.0.1",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "abbrev": "1",
+ "osenv": "^0.1.4"
+ }
+ },
+ "npm-bundled": {
+ "version": "1.0.3",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "npm-packlist": {
+ "version": "1.1.10",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "ignore-walk": "^3.0.1",
+ "npm-bundled": "^1.0.1"
+ }
+ },
+ "npmlog": {
+ "version": "4.1.2",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "are-we-there-yet": "~1.1.2",
+ "console-control-strings": "~1.1.0",
+ "gauge": "~2.7.3",
+ "set-blocking": "~2.0.0"
+ }
+ },
+ "number-is-nan": {
+ "version": "1.0.1",
+ "bundled": true,
+ "dev": true
+ },
+ "object-assign": {
+ "version": "4.1.1",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "once": {
+ "version": "1.4.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "os-homedir": {
+ "version": "1.0.2",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "os-tmpdir": {
+ "version": "1.0.2",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "osenv": {
+ "version": "0.1.5",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "os-homedir": "^1.0.0",
+ "os-tmpdir": "^1.0.0"
+ }
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "process-nextick-args": {
+ "version": "2.0.0",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "rc": {
+ "version": "1.2.7",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "deep-extend": "^0.5.1",
+ "ini": "~1.3.0",
+ "minimist": "^1.2.0",
+ "strip-json-comments": "~2.0.1"
+ },
+ "dependencies": {
+ "minimist": {
+ "version": "1.2.0",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ }
+ }
+ },
+ "readable-stream": {
+ "version": "2.3.6",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "rimraf": {
+ "version": "2.6.2",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "glob": "^7.0.5"
+ }
+ },
+ "safe-buffer": {
+ "version": "5.1.1",
+ "bundled": true,
+ "dev": true
+ },
+ "safer-buffer": {
+ "version": "2.1.2",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "sax": {
+ "version": "1.2.4",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "semver": {
+ "version": "5.5.0",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "set-blocking": {
+ "version": "2.0.0",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "signal-exit": {
+ "version": "3.0.2",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "string-width": {
+ "version": "1.0.2",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "code-point-at": "^1.0.0",
+ "is-fullwidth-code-point": "^1.0.0",
+ "strip-ansi": "^3.0.0"
+ }
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "3.0.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ }
+ },
+ "strip-json-comments": {
+ "version": "2.0.1",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "tar": {
+ "version": "4.4.1",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "chownr": "^1.0.1",
+ "fs-minipass": "^1.2.5",
+ "minipass": "^2.2.4",
+ "minizlib": "^1.1.0",
+ "mkdirp": "^0.5.0",
+ "safe-buffer": "^5.1.1",
+ "yallist": "^3.0.2"
+ }
+ },
+ "util-deprecate": {
+ "version": "1.0.2",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "wide-align": {
+ "version": "1.1.2",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "string-width": "^1.0.2"
+ }
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "bundled": true,
+ "dev": true
+ },
+ "yallist": {
+ "version": "3.0.2",
+ "bundled": true,
+ "dev": true
+ }
+ }
+ },
"glob": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
@@ -2398,6 +2928,13 @@
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
"dev": true
},
+ "nan": {
+ "version": "2.14.0",
+ "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz",
+ "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==",
+ "dev": true,
+ "optional": true
+ },
"os-locale": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.0.1.tgz",
@@ -2956,7 +3493,8 @@
"aproba": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
- "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
+ "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
+ "dev": true
},
"arch": {
"version": "2.1.0",
@@ -2974,6 +3512,7 @@
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz",
"integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=",
+ "dev": true,
"requires": {
"delegates": "^1.0.0",
"readable-stream": "^2.0.6"
@@ -5856,7 +6395,8 @@
"chownr": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.0.1.tgz",
- "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE="
+ "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=",
+ "dev": true
},
"chromedriver": {
"version": "2.41.0",
@@ -5871,6 +6411,49 @@
"request": "^2.87.0"
}
},
+ "cids": {
+ "version": "0.5.8",
+ "resolved": "https://registry.npmjs.org/cids/-/cids-0.5.8.tgz",
+ "integrity": "sha512-Ye8TZP3YQfy0j+i5k+LPHdTY3JOvTwN1pxds44p6BRUv8PTMOAF/Vt4Bc+oiIQ0Sktn0iftkUHgqKNHIMwhshA==",
+ "requires": {
+ "class-is": "^1.1.0",
+ "multibase": "~0.6.0",
+ "multicodec": "~0.5.0",
+ "multihashes": "~0.4.14"
+ },
+ "dependencies": {
+ "base-x": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.5.tgz",
+ "integrity": "sha512-C3picSgzPSLE+jW3tcBzJoGwitOtazb5B+5YmAxZm2ybmTi9LNgAtDO/jjVEBZwHoXmDBZ9m/IELj3elJVRBcA==",
+ "requires": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "bs58": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz",
+ "integrity": "sha1-vhYedsNU9veIrkBx9j806MTwpCo=",
+ "requires": {
+ "base-x": "^3.0.2"
+ }
+ },
+ "multihashes": {
+ "version": "0.4.14",
+ "resolved": "https://registry.npmjs.org/multihashes/-/multihashes-0.4.14.tgz",
+ "integrity": "sha512-V/g/EIN6nALXfS/xHUAgtfPP3mn3sPIF/i9beuGKf25QXS2QZYCpeVJbDPEannkz32B2fihzCe2D/KMrbcmefg==",
+ "requires": {
+ "bs58": "^4.0.1",
+ "varint": "^5.0.0"
+ }
+ },
+ "varint": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/varint/-/varint-5.0.0.tgz",
+ "integrity": "sha1-2Ca4n3SQcy+rwMDtaT7Uddyynr8="
+ }
+ }
+ },
"cipher-base": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
@@ -5895,6 +6478,11 @@
"chalk": "^1.1.3"
}
},
+ "class-is": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/class-is/-/class-is-1.1.0.tgz",
+ "integrity": "sha512-rhjH9AG1fvabIDoGRVH587413LPjTZgmDF9fOFCbFJQV4yuocX1mHxxvXI4g3cGwbVY9wAYIoKlg1N79frJKQw=="
+ },
"class-utils": {
"version": "0.3.6",
"resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
@@ -6531,7 +7119,8 @@
"console-control-strings": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
- "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4="
+ "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
+ "dev": true
},
"consolidate": {
"version": "0.15.1",
@@ -6553,6 +7142,49 @@
"integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=",
"dev": true
},
+ "content-hash": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/content-hash/-/content-hash-2.4.0.tgz",
+ "integrity": "sha512-T6LRy2xaOxgLT3jKj8N9xBiTbY+s60AWP7IUYsz+qmvon92r14z6EWgXt74y/5H+DPuBNPzdYH+jyhtDTTCRMA==",
+ "requires": {
+ "cids": "^0.5.7",
+ "multicodec": "^0.5.0",
+ "multihashes": "^0.4.14",
+ "varint": "^5.0.0"
+ },
+ "dependencies": {
+ "base-x": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.5.tgz",
+ "integrity": "sha512-C3picSgzPSLE+jW3tcBzJoGwitOtazb5B+5YmAxZm2ybmTi9LNgAtDO/jjVEBZwHoXmDBZ9m/IELj3elJVRBcA==",
+ "requires": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "bs58": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz",
+ "integrity": "sha1-vhYedsNU9veIrkBx9j806MTwpCo=",
+ "requires": {
+ "base-x": "^3.0.2"
+ }
+ },
+ "multihashes": {
+ "version": "0.4.14",
+ "resolved": "https://registry.npmjs.org/multihashes/-/multihashes-0.4.14.tgz",
+ "integrity": "sha512-V/g/EIN6nALXfS/xHUAgtfPP3mn3sPIF/i9beuGKf25QXS2QZYCpeVJbDPEannkz32B2fihzCe2D/KMrbcmefg==",
+ "requires": {
+ "bs58": "^4.0.1",
+ "varint": "^5.0.0"
+ }
+ },
+ "varint": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/varint/-/varint-5.0.0.tgz",
+ "integrity": "sha1-2Ca4n3SQcy+rwMDtaT7Uddyynr8="
+ }
+ }
+ },
"content-type": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
@@ -7780,7 +8412,8 @@
"delegates": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
- "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o="
+ "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=",
+ "dev": true
},
"depd": {
"version": "1.1.1",
@@ -7840,12 +8473,6 @@
"repeating": "^2.0.0"
}
},
- "detect-libc": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
- "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=",
- "optional": true
- },
"detect-newline": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz",
@@ -8854,7 +9481,7 @@
},
"engine.io-client": {
"version": "3.2.1",
- "resolved": "http://registry.npmjs.org/engine.io-client/-/engine.io-client-3.2.1.tgz",
+ "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.2.1.tgz",
"integrity": "sha512-y5AbkytWeM4jQr7m/koQLc5AxpRKC1hEVUb/s1FUAWEJq5AzJJ4NLvzuKPuxtDi5Mq755WuDvZ6Iv2rXj4PTzw==",
"dev": true,
"requires": {
@@ -9473,7 +10100,7 @@
},
"inquirer": {
"version": "0.12.0",
- "resolved": "http://registry.npmjs.org/inquirer/-/inquirer-0.12.0.tgz",
+ "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-0.12.0.tgz",
"integrity": "sha1-HvK/1jUE3wvHV4X/+MLEHfEvB34=",
"dev": true,
"requires": {
@@ -9566,7 +10193,7 @@
},
"table": {
"version": "3.8.3",
- "resolved": "http://registry.npmjs.org/table/-/table-3.8.3.tgz",
+ "resolved": "https://registry.npmjs.org/table/-/table-3.8.3.tgz",
"integrity": "sha1-K7xULw/amGGnVdOUf+/Ys/UThV8=",
"dev": true,
"requires": {
@@ -9827,7 +10454,7 @@
},
"eth-contract-metadata": {
"version": "github:MetaMask/eth-contract-metadata#dc68506221859bc90792bc5e0279a6835f2484d8",
- "from": "github:MetaMask/eth-contract-metadata#dc68506221859bc90792bc5e0279a6835f2484d8"
+ "from": "eth-contract-metadata@github:MetaMask/eth-contract-metadata#dc68506221859bc90792bc5e0279a6835f2484d8"
},
"eth-ens-namehash": {
"version": "2.0.8",
@@ -9865,30 +10492,28 @@
"requires": {
"ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git#8431eab7b3384e65e8126a4602520b78031666fb",
"ethereumjs-util": "^5.1.1"
+ }
+ },
+ "ethereumjs-abi": {
+ "version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#8431eab7b3384e65e8126a4602520b78031666fb",
+ "from": "ethereumjs-abi@git+https://github.com/ethereumjs/ethereumjs-abi.git#8431eab7b3384e65e8126a4602520b78031666fb",
+ "requires": {
+ "bn.js": "^4.11.8",
+ "ethereumjs-util": "^6.0.0"
},
"dependencies": {
- "ethereumjs-abi": {
- "version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#8431eab7b3384e65e8126a4602520b78031666fb",
- "from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
+ "ethereumjs-util": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.1.0.tgz",
+ "integrity": "sha512-URESKMFbDeJxnAxPppnk2fN6Y3BIatn9fwn76Lm8bQlt+s52TpG8dN9M66MLPuRAiAOIqL3dfwqWJf0sd0fL0Q==",
"requires": {
- "bn.js": "^4.11.8",
- "ethereumjs-util": "^6.0.0"
- },
- "dependencies": {
- "ethereumjs-util": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.1.0.tgz",
- "integrity": "sha512-URESKMFbDeJxnAxPppnk2fN6Y3BIatn9fwn76Lm8bQlt+s52TpG8dN9M66MLPuRAiAOIqL3dfwqWJf0sd0fL0Q==",
- "requires": {
- "bn.js": "^4.11.0",
- "create-hash": "^1.1.2",
- "ethjs-util": "0.1.6",
- "keccak": "^1.0.2",
- "rlp": "^2.0.0",
- "safe-buffer": "^5.1.1",
- "secp256k1": "^3.0.1"
- }
- }
+ "bn.js": "^4.11.0",
+ "create-hash": "^1.1.2",
+ "ethjs-util": "0.1.6",
+ "keccak": "^1.0.2",
+ "rlp": "^2.0.0",
+ "safe-buffer": "^5.1.1",
+ "secp256k1": "^3.0.1"
}
},
"ethjs-util": {
@@ -9902,13 +10527,6 @@
}
}
},
- "ethereumjs-abi": {
- "version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#2863c40e0982acfc0b7163f0285d4c56427c7799",
- "from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
- "requires": {
- "bn.js": "^4.11.8"
- }
- },
"ethereumjs-util": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.0.tgz",
@@ -9983,7 +10601,7 @@
},
"ethereumjs-abi": {
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#8431eab7b3384e65e8126a4602520b78031666fb",
- "from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
+ "from": "ethereumjs-abi@git+https://github.com/ethereumjs/ethereumjs-abi.git#8431eab7b3384e65e8126a4602520b78031666fb",
"requires": {
"bn.js": "^4.11.8",
"ethereumjs-util": "^6.0.0"
@@ -10173,32 +10791,30 @@
"requires": {
"ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git#8431eab7b3384e65e8126a4602520b78031666fb",
"ethereumjs-util": "^5.1.1"
+ }
+ },
+ "ethereumjs-abi": {
+ "version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#8431eab7b3384e65e8126a4602520b78031666fb",
+ "from": "ethereumjs-abi@git+https://github.com/ethereumjs/ethereumjs-abi.git#8431eab7b3384e65e8126a4602520b78031666fb",
+ "dev": true,
+ "requires": {
+ "bn.js": "^4.11.8",
+ "ethereumjs-util": "^6.0.0"
},
"dependencies": {
- "ethereumjs-abi": {
- "version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#8431eab7b3384e65e8126a4602520b78031666fb",
- "from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
+ "ethereumjs-util": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.1.0.tgz",
+ "integrity": "sha512-URESKMFbDeJxnAxPppnk2fN6Y3BIatn9fwn76Lm8bQlt+s52TpG8dN9M66MLPuRAiAOIqL3dfwqWJf0sd0fL0Q==",
"dev": true,
"requires": {
- "bn.js": "^4.11.8",
- "ethereumjs-util": "^6.0.0"
- },
- "dependencies": {
- "ethereumjs-util": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.1.0.tgz",
- "integrity": "sha512-URESKMFbDeJxnAxPppnk2fN6Y3BIatn9fwn76Lm8bQlt+s52TpG8dN9M66MLPuRAiAOIqL3dfwqWJf0sd0fL0Q==",
- "dev": true,
- "requires": {
- "bn.js": "^4.11.0",
- "create-hash": "^1.1.2",
- "ethjs-util": "0.1.6",
- "keccak": "^1.0.2",
- "rlp": "^2.0.0",
- "safe-buffer": "^5.1.1",
- "secp256k1": "^3.0.1"
- }
- }
+ "bn.js": "^4.11.0",
+ "create-hash": "^1.1.2",
+ "ethjs-util": "0.1.6",
+ "keccak": "^1.0.2",
+ "rlp": "^2.0.0",
+ "safe-buffer": "^5.1.1",
+ "secp256k1": "^3.0.1"
}
},
"ethjs-util": {
@@ -10213,13 +10829,6 @@
}
}
},
- "ethereumjs-abi": {
- "version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#2863c40e0982acfc0b7163f0285d4c56427c7799",
- "from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
- "requires": {
- "bn.js": "^4.11.8"
- }
- },
"ethereumjs-util": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.0.tgz",
@@ -10309,30 +10918,28 @@
"requires": {
"ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git#8431eab7b3384e65e8126a4602520b78031666fb",
"ethereumjs-util": "^5.1.1"
+ }
+ },
+ "ethereumjs-abi": {
+ "version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#8431eab7b3384e65e8126a4602520b78031666fb",
+ "from": "ethereumjs-abi@git+https://github.com/ethereumjs/ethereumjs-abi.git#8431eab7b3384e65e8126a4602520b78031666fb",
+ "requires": {
+ "bn.js": "^4.11.8",
+ "ethereumjs-util": "^6.0.0"
},
"dependencies": {
- "ethereumjs-abi": {
- "version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#8431eab7b3384e65e8126a4602520b78031666fb",
- "from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
+ "ethereumjs-util": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.1.0.tgz",
+ "integrity": "sha512-URESKMFbDeJxnAxPppnk2fN6Y3BIatn9fwn76Lm8bQlt+s52TpG8dN9M66MLPuRAiAOIqL3dfwqWJf0sd0fL0Q==",
"requires": {
- "bn.js": "^4.11.8",
- "ethereumjs-util": "^6.0.0"
- },
- "dependencies": {
- "ethereumjs-util": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.1.0.tgz",
- "integrity": "sha512-URESKMFbDeJxnAxPppnk2fN6Y3BIatn9fwn76Lm8bQlt+s52TpG8dN9M66MLPuRAiAOIqL3dfwqWJf0sd0fL0Q==",
- "requires": {
- "bn.js": "^4.11.0",
- "create-hash": "^1.1.2",
- "ethjs-util": "0.1.6",
- "keccak": "^1.0.2",
- "rlp": "^2.0.0",
- "safe-buffer": "^5.1.1",
- "secp256k1": "^3.0.1"
- }
- }
+ "bn.js": "^4.11.0",
+ "create-hash": "^1.1.2",
+ "ethjs-util": "0.1.6",
+ "keccak": "^1.0.2",
+ "rlp": "^2.0.0",
+ "safe-buffer": "^5.1.1",
+ "secp256k1": "^3.0.1"
}
},
"ethjs-util": {
@@ -10346,13 +10953,6 @@
}
}
},
- "ethereumjs-abi": {
- "version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#d84a96796079c8595a0c78accd1e7709f2277215",
- "from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
- "requires": {
- "bn.js": "^4.11.8"
- }
- },
"ethereumjs-util": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.0.tgz",
@@ -10400,32 +11000,6 @@
"requires": {
"ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git#8431eab7b3384e65e8126a4602520b78031666fb",
"ethereumjs-util": "^5.1.1"
- },
- "dependencies": {
- "ethereumjs-abi": {
- "version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#8431eab7b3384e65e8126a4602520b78031666fb",
- "from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
- "requires": {
- "bn.js": "^4.11.8",
- "ethereumjs-util": "^6.0.0"
- },
- "dependencies": {
- "ethereumjs-util": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.1.0.tgz",
- "integrity": "sha512-URESKMFbDeJxnAxPppnk2fN6Y3BIatn9fwn76Lm8bQlt+s52TpG8dN9M66MLPuRAiAOIqL3dfwqWJf0sd0fL0Q==",
- "requires": {
- "bn.js": "^4.11.0",
- "create-hash": "^1.1.2",
- "ethjs-util": "0.1.6",
- "keccak": "^1.0.2",
- "rlp": "^2.0.0",
- "safe-buffer": "^5.1.1",
- "secp256k1": "^3.0.1"
- }
- }
- }
- }
}
},
"ethereum-common": {
@@ -10434,8 +11008,8 @@
"integrity": "sha1-L9w1dvIykDNYl26znaeDIT/5Uj8="
},
"ethereumjs-abi": {
- "version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#572d4bafe08a8a231137e1f9daeb0f8a23f197d2",
- "from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
+ "version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#8431eab7b3384e65e8126a4602520b78031666fb",
+ "from": "ethereumjs-abi@git+https://github.com/ethereumjs/ethereumjs-abi.git#8431eab7b3384e65e8126a4602520b78031666fb",
"requires": {
"bn.js": "^4.11.8",
"ethereumjs-util": "^6.0.0"
@@ -10669,30 +11243,28 @@
"requires": {
"ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git#8431eab7b3384e65e8126a4602520b78031666fb",
"ethereumjs-util": "^5.1.1"
+ }
+ },
+ "ethereumjs-abi": {
+ "version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#8431eab7b3384e65e8126a4602520b78031666fb",
+ "from": "ethereumjs-abi@git+https://github.com/ethereumjs/ethereumjs-abi.git#8431eab7b3384e65e8126a4602520b78031666fb",
+ "requires": {
+ "bn.js": "^4.11.8",
+ "ethereumjs-util": "^6.0.0"
},
"dependencies": {
- "ethereumjs-abi": {
- "version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#8431eab7b3384e65e8126a4602520b78031666fb",
- "from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
+ "ethereumjs-util": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.1.0.tgz",
+ "integrity": "sha512-URESKMFbDeJxnAxPppnk2fN6Y3BIatn9fwn76Lm8bQlt+s52TpG8dN9M66MLPuRAiAOIqL3dfwqWJf0sd0fL0Q==",
"requires": {
- "bn.js": "^4.11.8",
- "ethereumjs-util": "^6.0.0"
- },
- "dependencies": {
- "ethereumjs-util": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.1.0.tgz",
- "integrity": "sha512-URESKMFbDeJxnAxPppnk2fN6Y3BIatn9fwn76Lm8bQlt+s52TpG8dN9M66MLPuRAiAOIqL3dfwqWJf0sd0fL0Q==",
- "requires": {
- "bn.js": "^4.11.0",
- "create-hash": "^1.1.2",
- "ethjs-util": "0.1.6",
- "keccak": "^1.0.2",
- "rlp": "^2.0.0",
- "safe-buffer": "^5.1.1",
- "secp256k1": "^3.0.1"
- }
- }
+ "bn.js": "^4.11.0",
+ "create-hash": "^1.1.2",
+ "ethjs-util": "0.1.6",
+ "keccak": "^1.0.2",
+ "rlp": "^2.0.0",
+ "safe-buffer": "^5.1.1",
+ "secp256k1": "^3.0.1"
}
},
"ethjs-util": {
@@ -10706,13 +11278,6 @@
}
}
},
- "ethereumjs-abi": {
- "version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#d84a96796079c8595a0c78accd1e7709f2277215",
- "from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
- "requires": {
- "bn.js": "^4.11.8"
- }
- },
"ethereumjs-util": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.0.tgz",
@@ -10902,32 +11467,6 @@
"requires": {
"ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git#8431eab7b3384e65e8126a4602520b78031666fb",
"ethereumjs-util": "^5.1.1"
- },
- "dependencies": {
- "ethereumjs-abi": {
- "version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#8431eab7b3384e65e8126a4602520b78031666fb",
- "from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
- "requires": {
- "bn.js": "^4.11.8",
- "ethereumjs-util": "^6.0.0"
- },
- "dependencies": {
- "ethereumjs-util": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.1.0.tgz",
- "integrity": "sha512-URESKMFbDeJxnAxPppnk2fN6Y3BIatn9fwn76Lm8bQlt+s52TpG8dN9M66MLPuRAiAOIqL3dfwqWJf0sd0fL0Q==",
- "requires": {
- "bn.js": "^4.11.0",
- "create-hash": "^1.1.2",
- "ethjs-util": "0.1.6",
- "keccak": "^1.0.2",
- "rlp": "^2.0.0",
- "safe-buffer": "^5.1.1",
- "secp256k1": "^3.0.1"
- }
- }
- }
- }
}
},
"ethereum-common": {
@@ -10936,8 +11475,8 @@
"integrity": "sha1-L9w1dvIykDNYl26znaeDIT/5Uj8="
},
"ethereumjs-abi": {
- "version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#572d4bafe08a8a231137e1f9daeb0f8a23f197d2",
- "from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
+ "version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#8431eab7b3384e65e8126a4602520b78031666fb",
+ "from": "ethereumjs-abi@git+https://github.com/ethereumjs/ethereumjs-abi.git#8431eab7b3384e65e8126a4602520b78031666fb",
"requires": {
"bn.js": "^4.11.8",
"ethereumjs-util": "^6.0.0"
@@ -11076,8 +11615,8 @@
}
},
"ethereumjs-abi": {
- "version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#572d4bafe08a8a231137e1f9daeb0f8a23f197d2",
- "from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
+ "version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#8431eab7b3384e65e8126a4602520b78031666fb",
+ "from": "ethereumjs-abi@git+https://github.com/ethereumjs/ethereumjs-abi.git#8431eab7b3384e65e8126a4602520b78031666fb",
"requires": {
"bn.js": "^4.11.8",
"ethereumjs-util": "^6.0.0"
@@ -11209,63 +11748,6 @@
"ethereumjs-util": "^5.1.1"
},
"dependencies": {
- "ethereumjs-abi": {
- "version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#8431eab7b3384e65e8126a4602520b78031666fb",
- "from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
- "requires": {
- "bn.js": "^4.11.8",
- "ethereumjs-util": "^6.0.0"
- },
- "dependencies": {
- "ethereumjs-util": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.1.0.tgz",
- "integrity": "sha512-URESKMFbDeJxnAxPppnk2fN6Y3BIatn9fwn76Lm8bQlt+s52TpG8dN9M66MLPuRAiAOIqL3dfwqWJf0sd0fL0Q==",
- "requires": {
- "bn.js": "^4.11.0",
- "create-hash": "^1.1.2",
- "ethjs-util": "0.1.6",
- "keccak": "^1.0.2",
- "rlp": "^2.0.0",
- "safe-buffer": "^5.1.1",
- "secp256k1": "^3.0.1"
- }
- }
- }
- },
- "ethjs-util": {
- "version": "0.1.6",
- "resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz",
- "integrity": "sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w==",
- "requires": {
- "is-hex-prefixed": "1.0.0",
- "strip-hex-prefix": "1.0.0"
- }
- }
- }
- },
- "ethereumjs-abi": {
- "version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#572d4bafe08a8a231137e1f9daeb0f8a23f197d2",
- "from": "git+https://github.com/ethereumjs/ethereumjs-abi.git#572d4bafe08a8a231137e1f9daeb0f8a23f197d2",
- "requires": {
- "bn.js": "^4.11.8",
- "ethereumjs-util": "^6.0.0"
- },
- "dependencies": {
- "ethereumjs-util": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.1.0.tgz",
- "integrity": "sha512-URESKMFbDeJxnAxPppnk2fN6Y3BIatn9fwn76Lm8bQlt+s52TpG8dN9M66MLPuRAiAOIqL3dfwqWJf0sd0fL0Q==",
- "requires": {
- "bn.js": "^4.11.0",
- "create-hash": "^1.1.2",
- "ethjs-util": "0.1.6",
- "keccak": "^1.0.2",
- "rlp": "^2.0.0",
- "safe-buffer": "^5.1.1",
- "secp256k1": "^3.0.1"
- }
- },
"ethjs-util": {
"version": "0.1.6",
"resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz",
@@ -11417,7 +11899,7 @@
},
"ethereumjs-util": {
"version": "github:ethereumjs/ethereumjs-util#ac5d0908536b447083ea422b435da27f26615de9",
- "from": "github:ethereumjs/ethereumjs-util#ac5d0908536b447083ea422b435da27f26615de9",
+ "from": "ethereumjs-util@github:ethereumjs/ethereumjs-util#ac5d0908536b447083ea422b435da27f26615de9",
"requires": {
"bn.js": "^4.8.0",
"create-hash": "^1.1.2",
@@ -13171,15 +13653,6 @@
}
}
},
- "fs-minipass": {
- "version": "1.2.5",
- "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz",
- "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==",
- "optional": true,
- "requires": {
- "minipass": "^2.2.1"
- }
- },
"fs-mkdirp-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz",
@@ -13238,20 +13711,470 @@
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
},
"fsevents": {
- "version": "1.2.4",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.4.tgz",
- "integrity": "sha512-z8H8/diyk76B7q5wg+Ud0+CqzcAF3mBBI/bA5ne5zrRUUIvNkJY//D3BqyH571KuAC4Nr7Rw7CjWX4r0y9DvNg==",
+ "version": "1.2.9",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz",
+ "integrity": "sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==",
"optional": true,
"requires": {
- "nan": "^2.9.2",
- "node-pre-gyp": "^0.10.0"
+ "nan": "^2.12.1",
+ "node-pre-gyp": "^0.12.0"
},
"dependencies": {
+ "abbrev": {
+ "version": "1.1.1",
+ "bundled": true,
+ "optional": true
+ },
+ "ansi-regex": {
+ "version": "2.1.1",
+ "bundled": true
+ },
+ "aproba": {
+ "version": "1.2.0",
+ "bundled": true,
+ "optional": true
+ },
+ "are-we-there-yet": {
+ "version": "1.1.5",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "delegates": "^1.0.0",
+ "readable-stream": "^2.0.6"
+ }
+ },
+ "balanced-match": {
+ "version": "1.0.0",
+ "bundled": true
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "bundled": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "chownr": {
+ "version": "1.1.1",
+ "bundled": true,
+ "optional": true
+ },
+ "code-point-at": {
+ "version": "1.1.0",
+ "bundled": true
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "bundled": true
+ },
+ "console-control-strings": {
+ "version": "1.1.0",
+ "bundled": true
+ },
+ "core-util-is": {
+ "version": "1.0.2",
+ "bundled": true,
+ "optional": true
+ },
+ "debug": {
+ "version": "4.1.1",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "deep-extend": {
+ "version": "0.6.0",
+ "bundled": true,
+ "optional": true
+ },
+ "delegates": {
+ "version": "1.0.0",
+ "bundled": true,
+ "optional": true
+ },
+ "detect-libc": {
+ "version": "1.0.3",
+ "bundled": true,
+ "optional": true
+ },
+ "fs-minipass": {
+ "version": "1.2.5",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "minipass": "^2.2.1"
+ }
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "bundled": true,
+ "optional": true
+ },
+ "gauge": {
+ "version": "2.7.4",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "aproba": "^1.0.3",
+ "console-control-strings": "^1.0.0",
+ "has-unicode": "^2.0.0",
+ "object-assign": "^4.1.0",
+ "signal-exit": "^3.0.0",
+ "string-width": "^1.0.1",
+ "strip-ansi": "^3.0.1",
+ "wide-align": "^1.1.0"
+ }
+ },
+ "glob": {
+ "version": "7.1.3",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "has-unicode": {
+ "version": "2.0.1",
+ "bundled": true,
+ "optional": true
+ },
+ "iconv-lite": {
+ "version": "0.4.24",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ }
+ },
+ "ignore-walk": {
+ "version": "3.0.1",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "minimatch": "^3.0.4"
+ }
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.3",
+ "bundled": true
+ },
+ "ini": {
+ "version": "1.3.5",
+ "bundled": true,
+ "optional": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "1.0.0",
+ "bundled": true,
+ "requires": {
+ "number-is-nan": "^1.0.0"
+ }
+ },
+ "isarray": {
+ "version": "1.0.0",
+ "bundled": true,
+ "optional": true
+ },
+ "minimatch": {
+ "version": "3.0.4",
+ "bundled": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "minimist": {
+ "version": "0.0.8",
+ "bundled": true
+ },
+ "minipass": {
+ "version": "2.3.5",
+ "bundled": true,
+ "requires": {
+ "safe-buffer": "^5.1.2",
+ "yallist": "^3.0.0"
+ }
+ },
+ "minizlib": {
+ "version": "1.2.1",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "minipass": "^2.2.1"
+ }
+ },
+ "mkdirp": {
+ "version": "0.5.1",
+ "bundled": true,
+ "requires": {
+ "minimist": "0.0.8"
+ }
+ },
+ "ms": {
+ "version": "2.1.1",
+ "bundled": true,
+ "optional": true
+ },
"nan": {
- "version": "2.10.0",
- "resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz",
- "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==",
+ "version": "2.14.0",
+ "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz",
+ "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==",
"optional": true
+ },
+ "needle": {
+ "version": "2.3.0",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "debug": "^4.1.0",
+ "iconv-lite": "^0.4.4",
+ "sax": "^1.2.4"
+ }
+ },
+ "node-pre-gyp": {
+ "version": "0.12.0",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "detect-libc": "^1.0.2",
+ "mkdirp": "^0.5.1",
+ "needle": "^2.2.1",
+ "nopt": "^4.0.1",
+ "npm-packlist": "^1.1.6",
+ "npmlog": "^4.0.2",
+ "rc": "^1.2.7",
+ "rimraf": "^2.6.1",
+ "semver": "^5.3.0",
+ "tar": "^4"
+ }
+ },
+ "nopt": {
+ "version": "4.0.1",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "abbrev": "1",
+ "osenv": "^0.1.4"
+ }
+ },
+ "npm-bundled": {
+ "version": "1.0.6",
+ "bundled": true,
+ "optional": true
+ },
+ "npm-packlist": {
+ "version": "1.4.1",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "ignore-walk": "^3.0.1",
+ "npm-bundled": "^1.0.1"
+ }
+ },
+ "npmlog": {
+ "version": "4.1.2",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "are-we-there-yet": "~1.1.2",
+ "console-control-strings": "~1.1.0",
+ "gauge": "~2.7.3",
+ "set-blocking": "~2.0.0"
+ }
+ },
+ "number-is-nan": {
+ "version": "1.0.1",
+ "bundled": true
+ },
+ "object-assign": {
+ "version": "4.1.1",
+ "bundled": true,
+ "optional": true
+ },
+ "once": {
+ "version": "1.4.0",
+ "bundled": true,
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "os-homedir": {
+ "version": "1.0.2",
+ "bundled": true,
+ "optional": true
+ },
+ "os-tmpdir": {
+ "version": "1.0.2",
+ "bundled": true,
+ "optional": true
+ },
+ "osenv": {
+ "version": "0.1.5",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "os-homedir": "^1.0.0",
+ "os-tmpdir": "^1.0.0"
+ }
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "bundled": true,
+ "optional": true
+ },
+ "process-nextick-args": {
+ "version": "2.0.0",
+ "bundled": true,
+ "optional": true
+ },
+ "rc": {
+ "version": "1.2.8",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "deep-extend": "^0.6.0",
+ "ini": "~1.3.0",
+ "minimist": "^1.2.0",
+ "strip-json-comments": "~2.0.1"
+ },
+ "dependencies": {
+ "minimist": {
+ "version": "1.2.0",
+ "bundled": true,
+ "optional": true
+ }
+ }
+ },
+ "readable-stream": {
+ "version": "2.3.6",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "rimraf": {
+ "version": "2.6.3",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "glob": "^7.1.3"
+ }
+ },
+ "safe-buffer": {
+ "version": "5.1.2",
+ "bundled": true
+ },
+ "safer-buffer": {
+ "version": "2.1.2",
+ "bundled": true,
+ "optional": true
+ },
+ "sax": {
+ "version": "1.2.4",
+ "bundled": true,
+ "optional": true
+ },
+ "semver": {
+ "version": "5.7.0",
+ "bundled": true,
+ "optional": true
+ },
+ "set-blocking": {
+ "version": "2.0.0",
+ "bundled": true,
+ "optional": true
+ },
+ "signal-exit": {
+ "version": "3.0.2",
+ "bundled": true,
+ "optional": true
+ },
+ "string-width": {
+ "version": "1.0.2",
+ "bundled": true,
+ "requires": {
+ "code-point-at": "^1.0.0",
+ "is-fullwidth-code-point": "^1.0.0",
+ "strip-ansi": "^3.0.0"
+ }
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "3.0.1",
+ "bundled": true,
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ }
+ },
+ "strip-json-comments": {
+ "version": "2.0.1",
+ "bundled": true,
+ "optional": true
+ },
+ "tar": {
+ "version": "4.4.8",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "chownr": "^1.1.1",
+ "fs-minipass": "^1.2.5",
+ "minipass": "^2.3.4",
+ "minizlib": "^1.1.1",
+ "mkdirp": "^0.5.0",
+ "safe-buffer": "^5.1.2",
+ "yallist": "^3.0.2"
+ }
+ },
+ "util-deprecate": {
+ "version": "1.0.2",
+ "bundled": true,
+ "optional": true
+ },
+ "wide-align": {
+ "version": "1.1.3",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "string-width": "^1.0.2 || 2"
+ }
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "bundled": true
+ },
+ "yallist": {
+ "version": "3.0.3",
+ "bundled": true
}
}
},
@@ -13326,9 +14249,9 @@
"integrity": "sha1-8ESOgGmFW/Kj5oPNwdMg5+KgfvQ="
},
"gaba": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/gaba/-/gaba-1.0.1.tgz",
- "integrity": "sha512-67Zoaq6wnaBASIXGfu2L+jzx8m+l1tfn6FAEIZI/pMvn/ymk4V9raeqz73QQKq1fF4WcRy2H1Ru1r45J1tDQoQ==",
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/gaba/-/gaba-1.3.0.tgz",
+ "integrity": "sha512-Vt0dqv5d6UY1eKYsOlwBQOVH1gPcG7qWL/xXArTB59y7HmBKL/g7FpBbgxsYPJl/Ii5XP+9Of6uP7Os51BdYhQ==",
"dev": true,
"requires": {
"await-semaphore": "^0.1.3",
@@ -13473,9 +14396,9 @@
}
},
"eth-sig-util": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/eth-sig-util/-/eth-sig-util-2.1.2.tgz",
- "integrity": "sha512-bNgt7txkEmaNlLf+PrbwYIdp4KRkB3E7hW0/QwlBpgv920pVVyQnF0evoovfiRveNKM4OrtPYZTojjmsfuCUOw==",
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/eth-sig-util/-/eth-sig-util-2.2.0.tgz",
+ "integrity": "sha512-bAxW35bL4U2lrtjjV8rFGJ8B27z4Sn5v9eIaNdpPUnPfUAtrvx5j8atfyV+k+JOnbppcvKhWCO1rQSBk4kkAhw==",
"dev": true,
"requires": {
"buffer": "^5.2.1",
@@ -15784,7 +16707,7 @@
"dependencies": {
"async-eventemitter": {
"version": "github:ahultgren/async-eventemitter#fa06e39e56786ba541c180061dbf2c0a5bbf951c",
- "from": "github:ahultgren/async-eventemitter#fa06e39e56786ba541c180061dbf2c0a5bbf951c",
+ "from": "async-eventemitter@github:ahultgren/async-eventemitter#fa06e39e56786ba541c180061dbf2c0a5bbf951c",
"dev": true,
"requires": {
"async": "^2.4.0"
@@ -15798,7 +16721,7 @@
"integrity": "sha1-jZWCAsftuq6Dlwf7pvCf8ydgYhA=",
"dev": true,
"requires": {
- "ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git#d84a96796079c8595a0c78accd1e7709f2277215",
+ "ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git#8431eab7b3384e65e8126a4602520b78031666fb",
"ethereumjs-util": "^5.1.1"
}
},
@@ -15809,8 +16732,8 @@
"dev": true
},
"ethereumjs-abi": {
- "version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#d84a96796079c8595a0c78accd1e7709f2277215",
- "from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
+ "version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#8431eab7b3384e65e8126a4602520b78031666fb",
+ "from": "ethereumjs-abi@git+https://github.com/ethereumjs/ethereumjs-abi.git#8431eab7b3384e65e8126a4602520b78031666fb",
"dev": true,
"requires": {
"bn.js": "^4.11.8",
@@ -19781,7 +20704,7 @@
"integrity": "sha1-jZWCAsftuq6Dlwf7pvCf8ydgYhA=",
"dev": true,
"requires": {
- "ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git#d84a96796079c8595a0c78accd1e7709f2277215",
+ "ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git#8431eab7b3384e65e8126a4602520b78031666fb",
"ethereumjs-util": "^5.1.1"
}
},
@@ -19792,8 +20715,8 @@
"dev": true
},
"ethereumjs-abi": {
- "version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#d84a96796079c8595a0c78accd1e7709f2277215",
- "from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
+ "version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#8431eab7b3384e65e8126a4602520b78031666fb",
+ "from": "ethereumjs-abi@git+https://github.com/ethereumjs/ethereumjs-abi.git#8431eab7b3384e65e8126a4602520b78031666fb",
"dev": true,
"requires": {
"bn.js": "^4.11.8",
@@ -19861,14 +20784,13 @@
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dev": true,
- "optional": true,
"requires": {
"ms": "2.0.0"
}
},
"websocket": {
"version": "git://github.com/frozeman/WebSocket-Node.git#6c72925e3f8aaaea8dc8450f97627e85263999f2",
- "from": "git://github.com/frozeman/WebSocket-Node.git#browserifyCompatible",
+ "from": "websocket@git://github.com/frozeman/WebSocket-Node.git#6c72925e3f8aaaea8dc8450f97627e85263999f2",
"dev": true,
"requires": {
"debug": "^2.2.0",
@@ -20107,6 +21029,7 @@
"version": "2.7.4",
"resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
"integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
+ "dev": true,
"requires": {
"aproba": "^1.0.3",
"console-control-strings": "^1.0.0",
@@ -20477,16 +21400,169 @@
"integrity": "sha512-fyPCII4vn9Gvjq2U/oDAfP433aiE64cyP/CJjRJcpVGjqqNdioUYn9+r0cSzT1XPwmGAHuTT7iv+rQT8u/YHKQ==",
"dev": true
},
- "glob-watcher": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-5.0.1.tgz",
- "integrity": "sha512-fK92r2COMC199WCyGUblrZKhjra3cyVMDiypDdqg1vsSDmexnbYivK1kNR4QItiNXLKmGlqan469ks67RtNa2g==",
+ "global": {
+ "version": "4.3.2",
+ "resolved": "https://registry.npmjs.org/global/-/global-4.3.2.tgz",
+ "integrity": "sha1-52mJJopsdMOJCLEwWxD8DjlOnQ8=",
+ "requires": {
+ "min-document": "^2.19.0",
+ "process": "~0.5.1"
+ }
+ },
+ "global-modules": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz",
+ "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==",
"dev": true,
"requires": {
- "async-done": "^1.2.0",
- "chokidar": "^2.0.0",
- "just-debounce": "^1.0.0",
- "object.defaults": "^1.1.0"
+ "global-prefix": "^1.0.1",
+ "is-windows": "^1.0.1",
+ "resolve-dir": "^1.0.0"
+ }
+ },
+ "global-prefix": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz",
+ "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=",
+ "dev": true,
+ "requires": {
+ "expand-tilde": "^2.0.2",
+ "homedir-polyfill": "^1.0.1",
+ "ini": "^1.3.4",
+ "is-windows": "^1.0.1",
+ "which": "^1.2.14"
+ }
+ },
+ "globals": {
+ "version": "9.18.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz",
+ "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ=="
+ },
+ "globby": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz",
+ "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=",
+ "dev": true,
+ "requires": {
+ "array-union": "^1.0.1",
+ "arrify": "^1.0.0",
+ "glob": "^7.0.3",
+ "object-assign": "^4.0.1",
+ "pify": "^2.0.0",
+ "pinkie-promise": "^2.0.0"
+ },
+ "dependencies": {
+ "pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+ "dev": true
+ }
+ }
+ },
+ "globjoin": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/globjoin/-/globjoin-0.1.4.tgz",
+ "integrity": "sha1-L0SUrIkZ43Z8XLtpHp9GMyQoXUM=",
+ "dev": true
+ },
+ "globule": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/globule/-/globule-1.2.0.tgz",
+ "integrity": "sha1-HcScaCLdnoovoAuiopUAboZkvQk=",
+ "dev": true,
+ "requires": {
+ "glob": "~7.1.1",
+ "lodash": "~4.17.4",
+ "minimatch": "~3.0.2"
+ }
+ },
+ "glogg": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.0.tgz",
+ "integrity": "sha1-f+DxmfV6yQbPUS/urY+Q7kooT8U=",
+ "dev": true,
+ "requires": {
+ "sparkles": "^1.0.0"
+ }
+ },
+ "got": {
+ "version": "5.6.0",
+ "resolved": "http://registry.npmjs.org/got/-/got-5.6.0.tgz",
+ "integrity": "sha1-ux1+4WO3gIK7yOuDbz85UATqb78=",
+ "dev": true,
+ "requires": {
+ "create-error-class": "^3.0.1",
+ "duplexer2": "^0.1.4",
+ "is-plain-obj": "^1.0.0",
+ "is-redirect": "^1.0.0",
+ "is-retry-allowed": "^1.0.0",
+ "is-stream": "^1.0.0",
+ "lowercase-keys": "^1.0.0",
+ "node-status-codes": "^1.0.0",
+ "object-assign": "^4.0.1",
+ "parse-json": "^2.1.0",
+ "pinkie-promise": "^2.0.0",
+ "read-all-stream": "^3.0.0",
+ "readable-stream": "^2.0.5",
+ "timed-out": "^2.0.0",
+ "unzip-response": "^1.0.0",
+ "url-parse-lax": "^1.0.0"
+ },
+ "dependencies": {
+ "duplexer2": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz",
+ "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=",
+ "dev": true,
+ "requires": {
+ "readable-stream": "^2.0.2"
+ }
+ }
+ }
+ },
+ "graceful-fs": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
+ "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg="
+ },
+ "graceful-readlink": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz",
+ "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=",
+ "dev": true
+ },
+ "graphlib": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/graphlib/-/graphlib-2.1.5.tgz",
+ "integrity": "sha512-XvtbqCcw+EM5SqQrIetIKKD+uZVNQtDPD1goIg7K73RuRZtVI5rYMdcCVSHm/AS1sCBZ7vt0p5WgXouucHQaOA==",
+ "dev": true,
+ "requires": {
+ "lodash": "^4.11.1"
+ }
+ },
+ "growl": {
+ "version": "1.10.3",
+ "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz",
+ "integrity": "sha512-hKlsbA5Vu3xsh1Cg3J7jSmX/WaW6A5oBeqzM88oNbCRQFz+zUaXm6yxS4RVytp1scBoJzSYl4YAEOQIt6O8V1Q==",
+ "dev": true
+ },
+ "growly": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz",
+ "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=",
+ "dev": true
+ },
+ "gulp": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/gulp/-/gulp-4.0.2.tgz",
+ "integrity": "sha512-dvEs27SCZt2ibF29xYgmnwwCYZxdxhQ/+LFWlbAW8y7jt68L/65402Lz3+CKy0Ov4rOs+NERmDq7YlZaDqUIfA==",
+ "dev": true,
+ "requires": {
+ "glob-watcher": "^5.0.3",
+ "gulp-cli": "^2.2.0",
+ "undertaker": "^1.2.1",
+ "vinyl-fs": "^3.0.0"
},
"dependencies": {
"anymatch": {
@@ -20541,24 +21617,31 @@
}
},
"chokidar": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz",
- "integrity": "sha512-z9n7yt9rOvIJrMhvDtDictKrkFHeihkNl6uWMmZlmL6tJtX9Cs+87oK+teBx+JIgzvbX3yZHT3eF8vpbDxHJXQ==",
+ "version": "2.1.6",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.6.tgz",
+ "integrity": "sha512-V2jUo67OKkc6ySiRpJrjlpJKl9kDuG+Xb8VgsGzb+aEouhgS1D0weyPU4lEzdAcsCAvrih2J2BqyXqHWvVLw5g==",
"dev": true,
"requires": {
"anymatch": "^2.0.0",
- "async-each": "^1.0.0",
- "braces": "^2.3.0",
- "fsevents": "^1.2.2",
+ "async-each": "^1.0.1",
+ "braces": "^2.3.2",
+ "fsevents": "^1.2.7",
"glob-parent": "^3.1.0",
- "inherits": "^2.0.1",
+ "inherits": "^2.0.3",
"is-binary-path": "^1.0.0",
"is-glob": "^4.0.0",
- "lodash.debounce": "^4.0.8",
- "normalize-path": "^2.1.1",
+ "normalize-path": "^3.0.0",
"path-is-absolute": "^1.0.0",
- "readdirp": "^2.0.0",
- "upath": "^1.0.5"
+ "readdirp": "^2.2.1",
+ "upath": "^1.1.1"
+ },
+ "dependencies": {
+ "normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true
+ }
}
},
"define-property": {
@@ -20703,6 +21786,18 @@
}
}
},
+ "findup-sync": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz",
+ "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==",
+ "dev": true,
+ "requires": {
+ "detect-file": "^1.0.0",
+ "is-glob": "^4.0.0",
+ "micromatch": "^3.0.4",
+ "resolve-dir": "^1.0.1"
+ }
+ },
"glob-parent": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
@@ -20724,6 +21819,46 @@
}
}
},
+ "glob-watcher": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-5.0.3.tgz",
+ "integrity": "sha512-8tWsULNEPHKQ2MR4zXuzSmqbdyV5PtwwCaWSGQ1WwHsJ07ilNeN1JB8ntxhckbnpSHaf9dXFUHzIWvm1I13dsg==",
+ "dev": true,
+ "requires": {
+ "anymatch": "^2.0.0",
+ "async-done": "^1.2.0",
+ "chokidar": "^2.0.0",
+ "is-negated-glob": "^1.0.0",
+ "just-debounce": "^1.0.0",
+ "object.defaults": "^1.1.0"
+ }
+ },
+ "gulp-cli": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/gulp-cli/-/gulp-cli-2.2.0.tgz",
+ "integrity": "sha512-rGs3bVYHdyJpLqR0TUBnlcZ1O5O++Zs4bA0ajm+zr3WFCfiSLjGwoCBqFs18wzN+ZxahT9DkOK5nDf26iDsWjA==",
+ "dev": true,
+ "requires": {
+ "ansi-colors": "^1.0.1",
+ "archy": "^1.0.0",
+ "array-sort": "^1.0.0",
+ "color-support": "^1.1.3",
+ "concat-stream": "^1.6.0",
+ "copy-props": "^2.0.1",
+ "fancy-log": "^1.3.2",
+ "gulplog": "^1.0.0",
+ "interpret": "^1.1.0",
+ "isobject": "^3.0.1",
+ "liftoff": "^3.1.0",
+ "matchdep": "^2.0.0",
+ "mute-stdout": "^1.0.0",
+ "pretty-hrtime": "^1.0.0",
+ "replace-homedir": "^1.0.0",
+ "semver-greatest-satisfied-range": "^1.1.0",
+ "v8flags": "^3.0.1",
+ "yargs": "^7.1.0"
+ }
+ },
"is-accessor-descriptor": {
"version": "0.1.6",
"resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
@@ -20771,9 +21906,9 @@
"dev": true
},
"is-glob": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz",
- "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=",
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
+ "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
"dev": true,
"requires": {
"is-extglob": "^2.1.1"
@@ -20811,6 +21946,22 @@
"integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
"dev": true
},
+ "liftoff": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-3.1.0.tgz",
+ "integrity": "sha512-DlIPlJUkCV0Ips2zf2pJP0unEoT1kwYhiiPUGF3s/jtxTCjziNLoiVVh+jqWOWeFi6mmwQ5fNxvAUyPad4Dfog==",
+ "dev": true,
+ "requires": {
+ "extend": "^3.0.0",
+ "findup-sync": "^3.0.0",
+ "fined": "^1.0.1",
+ "flagged-respawn": "^1.0.0",
+ "is-plain-object": "^2.0.4",
+ "object.map": "^1.0.0",
+ "rechoir": "^0.6.2",
+ "resolve": "^1.1.7"
+ }
+ },
"micromatch": {
"version": "3.1.10",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
@@ -20832,6 +21983,17 @@
"to-regex": "^3.0.2"
}
},
+ "readdirp": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz",
+ "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.11",
+ "micromatch": "^3.1.10",
+ "readable-stream": "^2.0.2"
+ }
+ },
"to-regex": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz",
@@ -20856,208 +22018,27 @@
}
}
},
- "upath": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.0.tgz",
- "integrity": "sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw==",
- "dev": true
- }
- }
- },
- "global": {
- "version": "4.3.2",
- "resolved": "https://registry.npmjs.org/global/-/global-4.3.2.tgz",
- "integrity": "sha1-52mJJopsdMOJCLEwWxD8DjlOnQ8=",
- "requires": {
- "min-document": "^2.19.0",
- "process": "~0.5.1"
- }
- },
- "global-modules": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz",
- "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==",
- "dev": true,
- "requires": {
- "global-prefix": "^1.0.1",
- "is-windows": "^1.0.1",
- "resolve-dir": "^1.0.0"
- }
- },
- "global-prefix": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz",
- "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=",
- "dev": true,
- "requires": {
- "expand-tilde": "^2.0.2",
- "homedir-polyfill": "^1.0.1",
- "ini": "^1.3.4",
- "is-windows": "^1.0.1",
- "which": "^1.2.14"
- }
- },
- "globals": {
- "version": "9.18.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz",
- "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ=="
- },
- "globby": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz",
- "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=",
- "dev": true,
- "requires": {
- "array-union": "^1.0.1",
- "arrify": "^1.0.0",
- "glob": "^7.0.3",
- "object-assign": "^4.0.1",
- "pify": "^2.0.0",
- "pinkie-promise": "^2.0.0"
- },
- "dependencies": {
- "pify": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
- "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
- "dev": true
- }
- }
- },
- "globjoin": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/globjoin/-/globjoin-0.1.4.tgz",
- "integrity": "sha1-L0SUrIkZ43Z8XLtpHp9GMyQoXUM=",
- "dev": true
- },
- "globule": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/globule/-/globule-1.2.0.tgz",
- "integrity": "sha1-HcScaCLdnoovoAuiopUAboZkvQk=",
- "dev": true,
- "requires": {
- "glob": "~7.1.1",
- "lodash": "~4.17.4",
- "minimatch": "~3.0.2"
- }
- },
- "glogg": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.0.tgz",
- "integrity": "sha1-f+DxmfV6yQbPUS/urY+Q7kooT8U=",
- "dev": true,
- "requires": {
- "sparkles": "^1.0.0"
- }
- },
- "got": {
- "version": "5.6.0",
- "resolved": "http://registry.npmjs.org/got/-/got-5.6.0.tgz",
- "integrity": "sha1-ux1+4WO3gIK7yOuDbz85UATqb78=",
- "dev": true,
- "requires": {
- "create-error-class": "^3.0.1",
- "duplexer2": "^0.1.4",
- "is-plain-obj": "^1.0.0",
- "is-redirect": "^1.0.0",
- "is-retry-allowed": "^1.0.0",
- "is-stream": "^1.0.0",
- "lowercase-keys": "^1.0.0",
- "node-status-codes": "^1.0.0",
- "object-assign": "^4.0.1",
- "parse-json": "^2.1.0",
- "pinkie-promise": "^2.0.0",
- "read-all-stream": "^3.0.0",
- "readable-stream": "^2.0.5",
- "timed-out": "^2.0.0",
- "unzip-response": "^1.0.0",
- "url-parse-lax": "^1.0.0"
- },
- "dependencies": {
- "duplexer2": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz",
- "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=",
- "dev": true,
- "requires": {
- "readable-stream": "^2.0.2"
- }
- }
- }
- },
- "graceful-fs": {
- "version": "4.1.11",
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
- "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg="
- },
- "graceful-readlink": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz",
- "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=",
- "dev": true
- },
- "graphlib": {
- "version": "2.1.5",
- "resolved": "https://registry.npmjs.org/graphlib/-/graphlib-2.1.5.tgz",
- "integrity": "sha512-XvtbqCcw+EM5SqQrIetIKKD+uZVNQtDPD1goIg7K73RuRZtVI5rYMdcCVSHm/AS1sCBZ7vt0p5WgXouucHQaOA==",
- "dev": true,
- "requires": {
- "lodash": "^4.11.1"
- }
- },
- "growl": {
- "version": "1.10.3",
- "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz",
- "integrity": "sha512-hKlsbA5Vu3xsh1Cg3J7jSmX/WaW6A5oBeqzM88oNbCRQFz+zUaXm6yxS4RVytp1scBoJzSYl4YAEOQIt6O8V1Q==",
- "dev": true
- },
- "growly": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz",
- "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=",
- "dev": true
- },
- "gulp": {
- "version": "4.0.0",
- "resolved": "github:gulpjs/gulp#55eb23a268dcc7340bb40808600fd4802848c06f",
- "dev": true,
- "requires": {
- "glob-watcher": "^5.0.0",
- "gulp-cli": "^2.0.0",
- "undertaker": "^1.0.0",
- "vinyl-fs": "^3.0.0"
- },
- "dependencies": {
- "gulp-cli": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/gulp-cli/-/gulp-cli-2.0.1.tgz",
- "integrity": "sha512-RxujJJdN8/O6IW2nPugl7YazhmrIEjmiVfPKrWt68r71UCaLKS71Hp0gpKT+F6qOUFtr7KqtifDKaAJPRVvMYQ==",
+ "undertaker": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/undertaker/-/undertaker-1.2.1.tgz",
+ "integrity": "sha512-71WxIzDkgYk9ZS+spIB8iZXchFhAdEo2YU8xYqBYJ39DIUIqziK78ftm26eecoIY49X0J2MLhG4hr18Yp6/CMA==",
"dev": true,
"requires": {
- "ansi-colors": "^1.0.1",
- "archy": "^1.0.0",
- "array-sort": "^1.0.0",
- "color-support": "^1.1.3",
- "concat-stream": "^1.6.0",
- "copy-props": "^2.0.1",
- "fancy-log": "^1.3.2",
- "gulplog": "^1.0.0",
- "interpret": "^1.1.0",
- "isobject": "^3.0.1",
- "liftoff": "^2.5.0",
- "matchdep": "^2.0.0",
- "mute-stdout": "^1.0.0",
- "pretty-hrtime": "^1.0.0",
- "replace-homedir": "^1.0.0",
- "semver-greatest-satisfied-range": "^1.1.0",
- "v8flags": "^3.0.1",
- "yargs": "^7.1.0"
+ "arr-flatten": "^1.0.1",
+ "arr-map": "^2.0.0",
+ "bach": "^1.0.0",
+ "collection-map": "^1.0.0",
+ "es6-weak-map": "^2.0.1",
+ "last-run": "^1.1.0",
+ "object.defaults": "^1.0.0",
+ "object.reduce": "^1.0.0",
+ "undertaker-registry": "^1.0.0"
}
},
- "isobject": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
- "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
+ "upath": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.2.tgz",
+ "integrity": "sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q==",
"dev": true
},
"yargs": {
@@ -22516,7 +23497,8 @@
"has-unicode": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
- "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk="
+ "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=",
+ "dev": true
},
"has-value": {
"version": "1.0.0",
@@ -23119,15 +24101,6 @@
"integrity": "sha512-YGG3ejvBNHRqu0559EOxxNFihD0AjpvHlC/pdGKd3X3ofe+CoJkYazwNJYTNebqpPKN+VVQbh4ZFn1DivMNuHA==",
"dev": true
},
- "ignore-walk": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz",
- "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==",
- "optional": true,
- "requires": {
- "minimatch": "^3.0.4"
- }
- },
"image-size": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/image-size/-/image-size-0.6.2.tgz",
@@ -23209,7 +24182,8 @@
"ini": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
- "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw=="
+ "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
+ "dev": true
},
"inject-css": {
"version": "0.1.1",
@@ -24459,7 +25433,7 @@
"dependencies": {
"async-eventemitter": {
"version": "github:ahultgren/async-eventemitter#fa06e39e56786ba541c180061dbf2c0a5bbf951c",
- "from": "github:ahultgren/async-eventemitter#fa06e39e56786ba541c180061dbf2c0a5bbf951c",
+ "from": "async-eventemitter@github:ahultgren/async-eventemitter#fa06e39e56786ba541c180061dbf2c0a5bbf951c",
"requires": {
"async": "^2.4.0"
}
@@ -25693,22 +26667,6 @@
}
}
},
- "liftoff": {
- "version": "2.5.0",
- "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-2.5.0.tgz",
- "integrity": "sha1-IAkpG7Mc6oYbvxCnwVooyvdcMew=",
- "dev": true,
- "requires": {
- "extend": "^3.0.0",
- "findup-sync": "^2.0.0",
- "fined": "^1.0.1",
- "flagged-respawn": "^1.0.0",
- "is-plain-object": "^2.0.4",
- "object.map": "^1.0.0",
- "rechoir": "^0.6.2",
- "resolve": "^1.1.7"
- }
- },
"lil-uuid": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/lil-uuid/-/lil-uuid-0.1.1.tgz",
@@ -27021,6 +27979,7 @@
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-2.2.4.tgz",
"integrity": "sha512-hzXIWWet/BzWhYs2b+u7dRHlruXhwdgvlTMDKC6Cb1U7ps6Ac6yQlR39xsbjWJE377YTCtKwIXIpJ5oP+j5y8g==",
+ "dev": true,
"requires": {
"safe-buffer": "^5.1.1",
"yallist": "^3.0.0"
@@ -27029,7 +27988,8 @@
"yallist": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz",
- "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k="
+ "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=",
+ "dev": true
}
}
},
@@ -27037,6 +27997,7 @@
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.1.0.tgz",
"integrity": "sha512-4T6Ur/GctZ27nHfpt9THOdRZNgyJ9FZchYO1ceg5S8Q3DNLCKYy44nCZzgCJgcvx2UM8czmqak5BCxJMrq37lA==",
+ "dev": true,
"requires": {
"minipass": "^2.2.1"
}
@@ -27269,6 +28230,39 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
+ "multibase": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/multibase/-/multibase-0.6.0.tgz",
+ "integrity": "sha512-R9bNLQhbD7MsitPm1NeY7w9sDgu6d7cuj25snAWH7k5PSNPSwIQQBpcpj8jx1W96dLbdigZqmUWOdQRMnAmgjA==",
+ "requires": {
+ "base-x": "3.0.4"
+ },
+ "dependencies": {
+ "base-x": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.4.tgz",
+ "integrity": "sha512-UYOadoSIkEI/VrRGSG6qp93rp2WdokiAiNYDfGW5qURAY8GiAQkvMbwNNSDYiVJopqv4gCna7xqf4rrNGp+5AA==",
+ "requires": {
+ "safe-buffer": "^5.0.1"
+ }
+ }
+ }
+ },
+ "multicodec": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/multicodec/-/multicodec-0.5.0.tgz",
+ "integrity": "sha512-lKsJeT4cKeSq0rVEWhO3oSBgDN4sMY1sNZKlvl68g/ZAahjPS1KIVyF4IqhuYmCdtOyKs4Q4hQ6M0C3iqRnuqQ==",
+ "requires": {
+ "varint": "^5.0.0"
+ },
+ "dependencies": {
+ "varint": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/varint/-/varint-5.0.0.tgz",
+ "integrity": "sha1-2Ca4n3SQcy+rwMDtaT7Uddyynr8="
+ }
+ }
+ },
"multihashes": {
"version": "0.4.13",
"resolved": "https://registry.npmjs.org/multihashes/-/multihashes-0.4.13.tgz",
@@ -27533,6 +28527,7 @@
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/needle/-/needle-2.2.4.tgz",
"integrity": "sha512-HyoqEb4wr/rsoaIDfTH2aVL9nWtQqba2/HvMv+++m8u0dz808MaagKILxtfeSN7QU7nvbQ79zk3vYOJp9zsNEA==",
+ "dev": true,
"requires": {
"debug": "^2.1.2",
"iconv-lite": "^0.4.4",
@@ -27777,71 +28772,6 @@
"which": "^1.3.0"
}
},
- "node-pre-gyp": {
- "version": "0.10.3",
- "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.10.3.tgz",
- "integrity": "sha512-d1xFs+C/IPS8Id0qPTZ4bUT8wWryfR/OzzAFxweG+uLN85oPzyo2Iw6bVlLQ/JOdgNonXLCoRyqDzDWq4iw72A==",
- "optional": true,
- "requires": {
- "detect-libc": "^1.0.2",
- "mkdirp": "^0.5.1",
- "needle": "^2.2.1",
- "nopt": "^4.0.1",
- "npm-packlist": "^1.1.6",
- "npmlog": "^4.0.2",
- "rc": "^1.2.7",
- "rimraf": "^2.6.1",
- "semver": "^5.3.0",
- "tar": "^4"
- },
- "dependencies": {
- "minipass": {
- "version": "2.3.4",
- "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.4.tgz",
- "integrity": "sha512-mlouk1OHlaUE8Odt1drMtG1bAJA4ZA6B/ehysgV0LUIrDHdKgo1KorZq3pK0b/7Z7LJIQ12MNM6aC+Tn6lUZ5w==",
- "optional": true,
- "requires": {
- "safe-buffer": "^5.1.2",
- "yallist": "^3.0.0"
- }
- },
- "nopt": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz",
- "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=",
- "optional": true,
- "requires": {
- "abbrev": "1",
- "osenv": "^0.1.4"
- }
- },
- "safe-buffer": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
- "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
- },
- "tar": {
- "version": "4.4.6",
- "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.6.tgz",
- "integrity": "sha512-tMkTnh9EdzxyfW+6GK6fCahagXsnYk6kE6S9Gr9pjVdys769+laCTbodXDhPAjzVtEBazRgP0gYqOjnk9dQzLg==",
- "optional": true,
- "requires": {
- "chownr": "^1.0.1",
- "fs-minipass": "^1.2.5",
- "minipass": "^2.3.3",
- "minizlib": "^1.1.0",
- "mkdirp": "^0.5.0",
- "safe-buffer": "^5.1.2",
- "yallist": "^3.0.2"
- }
- },
- "yallist": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz",
- "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k="
- }
- }
- },
"node-sass": {
"version": "4.9.2",
"resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.9.2.tgz",
@@ -27909,6 +28839,37 @@
"underscore": "~1.4.4"
}
},
+ "nonce-tracker": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/nonce-tracker/-/nonce-tracker-1.0.0.tgz",
+ "integrity": "sha512-hxKokxgLvOZx9A5qPQKwL34G1/YwMC5xJWZHFUKfvwxypkn2nP0KVJjbcoXwY6pXsRRa11KdFEPW61N4YCGnWQ==",
+ "requires": {
+ "assert": "^1.4.1",
+ "await-semaphore": "^0.1.3",
+ "ethjs-query": "^0.3.8"
+ },
+ "dependencies": {
+ "ethjs-query": {
+ "version": "0.3.8",
+ "resolved": "https://registry.npmjs.org/ethjs-query/-/ethjs-query-0.3.8.tgz",
+ "integrity": "sha512-/J5JydqrOzU8O7VBOwZKUWXxHDGr46VqNjBCJgBVNNda+tv7Xc8Y2uJc6aMHHVbeN3YOQ7YRElgIc0q1CI02lQ==",
+ "requires": {
+ "babel-runtime": "^6.26.0",
+ "ethjs-format": "0.2.7",
+ "ethjs-rpc": "0.2.0",
+ "promise-to-callback": "^1.0.0"
+ }
+ },
+ "ethjs-rpc": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/ethjs-rpc/-/ethjs-rpc-0.2.0.tgz",
+ "integrity": "sha512-RINulkNZTKnj4R/cjYYtYMnFFaBcVALzbtEJEONrrka8IeoarNB9Jbzn+2rT00Cv8y/CxAI+GgY1d0/i2iQeOg==",
+ "requires": {
+ "promise-to-callback": "^1.0.0"
+ }
+ }
+ }
+ },
"nopt": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz",
@@ -27963,22 +28924,6 @@
"once": "^1.3.2"
}
},
- "npm-bundled": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.5.tgz",
- "integrity": "sha512-m/e6jgWu8/v5niCUKQi9qQl8QdeEduFA96xHDDzFGqly0OOjI7c+60KM/2sppfnUU9JJagf+zs+yGhqSOFj71g==",
- "optional": true
- },
- "npm-packlist": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.1.11.tgz",
- "integrity": "sha512-CxKlZ24urLkJk+9kCm48RTQ7L4hsmgSVzEk0TLGPzzyuFxD7VNgy5Sl24tOLMzQv773a/NeJ1ce1DKeacqffEA==",
- "optional": true,
- "requires": {
- "ignore-walk": "^3.0.1",
- "npm-bundled": "^1.0.1"
- }
- },
"npm-run-path": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
@@ -27992,6 +28937,7 @@
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
"integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
+ "dev": true,
"requires": {
"are-we-there-yet": "~1.1.2",
"console-control-strings": "~1.1.0",
@@ -29722,6 +30668,7 @@
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz",
"integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
+ "dev": true,
"requires": {
"os-homedir": "^1.0.0",
"os-tmpdir": "^1.0.0"
@@ -30423,7 +31370,7 @@
},
"po2json": {
"version": "0.4.5",
- "resolved": "http://registry.npmjs.org/po2json/-/po2json-0.4.5.tgz",
+ "resolved": "https://registry.npmjs.org/po2json/-/po2json-0.4.5.tgz",
"integrity": "sha1-R7spUtoy1Yob4vJWpZjuvAt0URg=",
"dev": true,
"requires": {
@@ -32733,26 +33680,6 @@
"unpipe": "1.0.0"
}
},
- "rc": {
- "version": "1.2.8",
- "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
- "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
- "optional": true,
- "requires": {
- "deep-extend": "^0.6.0",
- "ini": "~1.3.0",
- "minimist": "^1.2.0",
- "strip-json-comments": "~2.0.1"
- },
- "dependencies": {
- "deep-extend": {
- "version": "0.6.0",
- "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
- "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
- "optional": true
- }
- }
- },
"react": {
"version": "15.6.2",
"resolved": "https://registry.npmjs.org/react/-/react-15.6.2.tgz",
@@ -34502,7 +35429,8 @@
"sax": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
- "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
+ "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
+ "dev": true
},
"schema-utils": {
"version": "0.4.5",
@@ -35000,7 +35928,8 @@
"signal-exit": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
- "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0="
+ "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
+ "dev": true
},
"simple-concat": {
"version": "1.0.0",
@@ -35856,7 +36785,7 @@
},
"socket.io-parser": {
"version": "3.2.0",
- "resolved": "http://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.2.0.tgz",
+ "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.2.0.tgz",
"integrity": "sha512-FYiBx7rc/KORMJlgsXysflWx/RIvtqZbyGLlHZvjfmPTPeuD/I8MaW7cfFrj5tRltICJdgwflhfZ3NVVbVLFQA==",
"dev": true,
"requires": {
@@ -36682,7 +37611,8 @@
"strip-json-comments": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
- "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo="
+ "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
+ "dev": true
},
"strip-outer": {
"version": "1.0.1",
@@ -38479,23 +39409,6 @@
}
}
},
- "undertaker": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/undertaker/-/undertaker-1.2.0.tgz",
- "integrity": "sha1-M52kZGJS0ILcN45wgGcpl1DhG0k=",
- "dev": true,
- "requires": {
- "arr-flatten": "^1.0.1",
- "arr-map": "^2.0.0",
- "bach": "^1.0.0",
- "collection-map": "^1.0.0",
- "es6-weak-map": "^2.0.1",
- "last-run": "^1.1.0",
- "object.defaults": "^1.0.0",
- "object.reduce": "^1.0.0",
- "undertaker-registry": "^1.0.0"
- }
- },
"undertaker-registry": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/undertaker-registry/-/undertaker-registry-1.0.1.tgz",
@@ -39284,7 +40197,7 @@
"dependencies": {
"bignumber.js": {
"version": "git+https://github.com/frozeman/bignumber.js-nolookahead.git#57692b3ecfc98bbdd6b3a516cb2353652ea49934",
- "from": "git+https://github.com/frozeman/bignumber.js-nolookahead.git"
+ "from": "bignumber.js@git+https://github.com/frozeman/bignumber.js-nolookahead.git#57692b3ecfc98bbdd6b3a516cb2353652ea49934"
}
}
},
@@ -39387,9 +40300,9 @@
}
},
"ethereumjs-common": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/ethereumjs-common/-/ethereumjs-common-1.1.0.tgz",
- "integrity": "sha512-LUmYkKV/HcZbWRyu3OU9YOevsH3VJDXtI6kEd8VZweQec+JjDGKCmAVKUyzhYUHqxRJu7JNALZ3A/b3NXOP6tA==",
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/ethereumjs-common/-/ethereumjs-common-1.2.0.tgz",
+ "integrity": "sha512-vTGT1atoU0ysM9hV9mT51CpVpet6HEJsAkqHynH7N5IMFjzKJ57XQeUa/NviDbh1FQTHdRAk+LP/G2+Oml0E/g==",
"dev": true
},
"ethereumjs-util": {
@@ -39804,6 +40717,7 @@
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz",
"integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==",
+ "dev": true,
"requires": {
"string-width": "^1.0.2"
}
diff --git a/package.json b/package.json
index 0ae402600..db0b0203f 100644
--- a/package.json
+++ b/package.json
@@ -72,6 +72,7 @@
"c3": "^0.6.7",
"classnames": "^2.2.5",
"clone": "^2.1.2",
+ "content-hash": "^2.3.2",
"copy-to-clipboard": "^3.0.8",
"currency-formatter": "^1.4.2",
"d3": "^5.7.0",
@@ -133,6 +134,7 @@
"mkdirp": "^0.5.1",
"multihashes": "^0.4.12",
"multiplex": "^6.7.0",
+ "nonce-tracker": "^1.0.0",
"number-to-bn": "^1.7.0",
"obj-multiplex": "^1.0.0",
"obs-store": "^3.0.2",
@@ -228,7 +230,7 @@
"file-loader": "^1.1.11",
"fs-extra": "^6.0.1",
"fs-promise": "^2.0.3",
- "gaba": "^1.0.1",
+ "gaba": "^1.3.0",
"ganache-cli": "^6.1.0",
"ganache-core": "^2.5.3",
"geckodriver": "^1.14.1",
diff --git a/test/e2e/beta/from-import-beta-ui.spec.js b/test/e2e/beta/from-import-beta-ui.spec.js
index a913caa79..625330dbb 100644
--- a/test/e2e/beta/from-import-beta-ui.spec.js
+++ b/test/e2e/beta/from-import-beta-ui.spec.js
@@ -27,6 +27,8 @@ describe('Using MetaMask with an existing account', function () {
const testSeedPhrase = 'forum vessel pink push lonely enact gentle tail admit parrot grunt dress'
const testAddress = '0x0Cc5261AB8cE458dc977078A3623E2BaDD27afD3'
const testPrivateKey2 = '14abe6f4aab7f9f626fe981c864d0adeb5685f289ac9270c27b8fd790b4235d6'
+ const testPrivateKey3 = 'F4EC2590A0C10DE95FBF4547845178910E40F5035320C516A18C117DE02B5669'
+ const tinyDelayMs = 200
const regularDelayMs = 1000
const largeDelayMs = regularDelayMs * 2
@@ -323,11 +325,60 @@ describe('Using MetaMask with an existing account', function () {
})
})
- describe('Connects to a Hardware wallet', () => {
- it('choose Connect Hardware Wallet from the account menu', async () => {
+ describe('Imports and removes an account', () => {
+ it('choose Create Account from the account menu', async () => {
await driver.findElement(By.css('.account-menu__icon')).click()
await delay(regularDelayMs)
+ const [importAccount] = await findElements(driver, By.xpath(`//div[contains(text(), 'Import Account')]`))
+ await importAccount.click()
+ await delay(regularDelayMs)
+ })
+
+ it('enter private key', async () => {
+ const privateKeyInput = await findElement(driver, By.css('#private-key-box'))
+ await privateKeyInput.sendKeys(testPrivateKey3)
+ await delay(regularDelayMs)
+ const importButtons = await findElements(driver, By.xpath(`//button[contains(text(), 'Import')]`))
+ await importButtons[0].click()
+ await delay(regularDelayMs)
+ })
+
+ it('should open the remove account modal', async () => {
+ const [accountName] = await findElements(driver, By.css('.account-name'))
+ assert.equal(await accountName.getText(), 'Account 5')
+ await delay(regularDelayMs)
+
+ await driver.findElement(By.css('.account-menu__icon')).click()
+ await delay(regularDelayMs)
+
+ const accountListItems = await findElements(driver, By.css('.account-menu__account'))
+ assert.equal(accountListItems.length, 5)
+
+ const removeAccountIcons = await findElements(driver, By.css('.remove-account-icon'))
+ await removeAccountIcons[1].click()
+ await delay(tinyDelayMs)
+
+ await findElement(driver, By.css('.confirm-remove-account__account'))
+ })
+
+ it('should remove the account', async () => {
+ const removeButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Remove')]`))
+ await removeButton.click()
+
+ await delay(regularDelayMs)
+
+ const [accountName] = await findElements(driver, By.css('.account-name'))
+ assert.equal(await accountName.getText(), 'Account 1')
+ await delay(regularDelayMs)
+
+ const accountListItems = await findElements(driver, By.css('.account-menu__account'))
+ assert.equal(accountListItems.length, 4)
+ })
+ })
+
+ describe('Connects to a Hardware wallet', () => {
+ it('choose Connect Hardware Wallet from the account menu', async () => {
const [connectAccount] = await findElements(driver, By.xpath(`//div[contains(text(), 'Connect Hardware Wallet')]`))
await connectAccount.click()
await delay(regularDelayMs)
diff --git a/test/integration/lib/send-new-ui.js b/test/integration/lib/send-new-ui.js
index 78014feef..0cca9e959 100644
--- a/test/integration/lib/send-new-ui.js
+++ b/test/integration/lib/send-new-ui.js
@@ -71,11 +71,29 @@ async function runSendFlowTest (assert) {
assert.equal(sendToAccountAddress, '0x2f8D4a878cFA04A6E60D46362f5644DeAb66572D', 'send to dropdown selects the correct address')
const sendAmountField = await queryAsync($, '.send-v2__form-row:eq(3)')
- sendAmountField.find('.unit-input')[0].click()
-
const sendAmountFieldInput = await findAsync(sendAmountField, '.unit-input__input')
+
+ const amountMaxButton = await queryAsync($, '.send-v2__amount-max')
+ amountMaxButton.click()
+ reactTriggerChange(sendAmountField.find('input')[1])
+ assert.equal(sendAmountFieldInput.is(':disabled'), true, 'disabled the send amount input when max mode is on')
+
+ const gasPriceButtonGroup = await queryAsync($, '.gas-price-button-group--small')
+ const gasPriceButton = await gasPriceButtonGroup.find('button')[0]
+ const valueBeforeGasPriceChange = sendAmountFieldInput.prop('value')
+ gasPriceButton.click()
+ reactTriggerChange(sendAmountField.find('input')[1])
+
+ await timeout(1000)
+
+ assert.notEqual(valueBeforeGasPriceChange, sendAmountFieldInput.prop('value'), 'send amount value changes when gas price changes')
+
+ amountMaxButton.click()
+ reactTriggerChange(sendAmountField.find('input')[1])
+
+ sendAmountField.find('.unit-input').click()
sendAmountFieldInput.val('5.1')
- reactTriggerChange(sendAmountField.find('input')[0])
+ reactTriggerChange(sendAmountField.find('input')[1])
let errorMessage = await queryAsync($, '.send-v2__error')
assert.equal(errorMessage[0].textContent, 'Insufficient funds.', 'send should render an insufficient fund error message')
diff --git a/test/unit/app/controllers/currency-controller-test.js b/test/unit/app/controllers/currency-controller-test.js
deleted file mode 100644
index 8b6fbb719..000000000
--- a/test/unit/app/controllers/currency-controller-test.js
+++ /dev/null
@@ -1,78 +0,0 @@
-const assert = require('assert')
-const nock = require('nock')
-const CurrencyController = require('../../../../app/scripts/controllers/currency')
-
-describe('currency-controller', function () {
- var currencyController
-
- beforeEach(function () {
- currencyController = new CurrencyController()
- })
-
- describe('currency conversions', function () {
- describe('#setCurrentCurrency', function () {
- it('should return USD as default', function () {
- assert.equal(currencyController.getCurrentCurrency(), 'usd')
- })
-
- it('should be able to set to other currency', function () {
- assert.equal(currencyController.getCurrentCurrency(), 'usd')
- currencyController.setCurrentCurrency('JPY')
- var result = currencyController.getCurrentCurrency()
- assert.equal(result, 'JPY')
- })
- })
-
- describe('#getConversionRate', function () {
- it('should return undefined if non-existent', function () {
- var result = currencyController.getConversionRate()
- assert.ok(!result)
- })
- })
-
- describe('#updateConversionRate', function () {
- it('should retrieve an update for ETH to USD and set it in memory', function (done) {
- this.timeout(15000)
- nock('https://api.infura.io')
- .get('/v1/ticker/ethusd')
- .reply(200, '{"base": "ETH", "quote": "USD", "bid": 288.45, "ask": 288.46, "volume": 112888.17569277, "exchange": "bitfinex", "total_volume": 272175.00106721005, "num_exchanges": 8, "timestamp": 1506444677}')
-
- assert.equal(currencyController.getConversionRate(), 0)
- currencyController.setCurrentCurrency('usd')
- currencyController.updateConversionRate()
- .then(function () {
- var result = currencyController.getConversionRate()
- assert.equal(typeof result, 'number')
- done()
- }).catch(function (err) {
- done(err)
- })
- })
-
- it('should work for JPY as well.', function () {
- this.timeout(15000)
- assert.equal(currencyController.getConversionRate(), 0)
-
- nock('https://api.infura.io')
- .get('/v1/ticker/ethjpy')
- .reply(200, '{"base": "ETH", "quote": "JPY", "bid": 32300.0, "ask": 32400.0, "volume": 247.4616071, "exchange": "kraken", "total_volume": 247.4616071, "num_exchanges": 1, "timestamp": 1506444676}')
-
-
- var promise = new Promise(
- function (resolve) {
- currencyController.setCurrentCurrency('jpy')
- currencyController.updateConversionRate().then(function () {
- resolve()
- })
- })
-
- promise.then(function () {
- var result = currencyController.getConversionRate()
- assert.equal(typeof result, 'number')
- }).catch(function (done, err) {
- done(err)
- })
- })
- })
- })
-})
diff --git a/test/unit/app/controllers/metamask-controller-test.js b/test/unit/app/controllers/metamask-controller-test.js
index a56b8adbd..1b8cde42e 100644
--- a/test/unit/app/controllers/metamask-controller-test.js
+++ b/test/unit/app/controllers/metamask-controller-test.js
@@ -45,6 +45,11 @@ describe('MetaMaskController', function () {
.get(/.*/)
.reply(200)
+ nock('https://min-api.cryptocompare.com')
+ .persist()
+ .get(/.*/)
+ .reply(200, '{"JPY":12415.9}')
+
metamaskController = new MetaMaskController({
showUnapprovedTx: noop,
showUnconfirmedMessage: noop,
@@ -441,7 +446,7 @@ describe('MetaMaskController', function () {
let defaultMetaMaskCurrency
beforeEach(function () {
- defaultMetaMaskCurrency = metamaskController.currencyController.getCurrentCurrency()
+ defaultMetaMaskCurrency = metamaskController.currencyRateController.state.currentCurrency
})
it('defaults to usd', function () {
@@ -450,7 +455,7 @@ describe('MetaMaskController', function () {
it('sets currency to JPY', function () {
metamaskController.setCurrentCurrency('JPY', noop)
- assert.equal(metamaskController.currencyController.getCurrentCurrency(), 'JPY')
+ assert.equal(metamaskController.currencyRateController.state.currentCurrency, 'JPY')
})
})
diff --git a/test/unit/app/controllers/transactions/nonce-tracker-test.js b/test/unit/app/controllers/transactions/nonce-tracker-test.js
deleted file mode 100644
index 51ac390e9..000000000
--- a/test/unit/app/controllers/transactions/nonce-tracker-test.js
+++ /dev/null
@@ -1,238 +0,0 @@
-const assert = require('assert')
-const NonceTracker = require('../../../../../app/scripts/controllers/transactions/nonce-tracker')
-const MockTxGen = require('../../../../lib/mock-tx-gen')
-const providerResultStub = {}
-
-describe('Nonce Tracker', function () {
- let nonceTracker, pendingTxs, confirmedTxs
-
- describe('#getNonceLock', function () {
-
- describe('with 3 confirmed and 1 pending', function () {
- beforeEach(function () {
- const txGen = new MockTxGen()
- confirmedTxs = txGen.generate({ status: 'confirmed' }, { count: 3 })
- pendingTxs = txGen.generate({ status: 'submitted' }, { count: 1 })
- nonceTracker = generateNonceTrackerWith(pendingTxs, confirmedTxs, '0x1')
- })
-
- it('should return 4', async function () {
- this.timeout(15000)
- const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926')
- assert.equal(nonceLock.nextNonce, '4', `nonce should be 4 got ${nonceLock.nextNonce}`)
- await nonceLock.releaseLock()
- })
-
- it('should use localNonce if network returns a nonce lower then a confirmed tx in state', async function () {
- this.timeout(15000)
- const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926')
- assert.equal(nonceLock.nextNonce, '4', 'nonce should be 4')
- await nonceLock.releaseLock()
- })
- })
-
- describe('sentry issue 476304902', function () {
- beforeEach(function () {
- const txGen = new MockTxGen()
- pendingTxs = txGen.generate({ status: 'submitted' }, {
- fromNonce: 3,
- count: 29,
- })
- nonceTracker = generateNonceTrackerWith(pendingTxs, [], '0x3')
- })
-
- it('should return 9', async function () {
- this.timeout(15000)
- const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926')
- assert.equal(nonceLock.nextNonce, '32', `nonce should be 32 got ${nonceLock.nextNonce}`)
- await nonceLock.releaseLock()
- })
- })
-
- describe('issue 3670', function () {
- beforeEach(function () {
- const txGen = new MockTxGen()
- pendingTxs = txGen.generate({ status: 'submitted' }, {
- fromNonce: 6,
- count: 3,
- })
- nonceTracker = generateNonceTrackerWith(pendingTxs, [], '0x6')
- })
-
- it('should return 9', async function () {
- this.timeout(15000)
- const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926')
- assert.equal(nonceLock.nextNonce, '9', `nonce should be 9 got ${nonceLock.nextNonce}`)
- await nonceLock.releaseLock()
- })
- })
-
- describe('with no previous txs', function () {
- beforeEach(function () {
- nonceTracker = generateNonceTrackerWith([], [])
- })
-
- it('should return 0', async function () {
- this.timeout(15000)
- const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926')
- assert.equal(nonceLock.nextNonce, '0', `nonce should be 0 returned ${nonceLock.nextNonce}`)
- await nonceLock.releaseLock()
- })
- })
-
- describe('with multiple previous txs with same nonce', function () {
- beforeEach(function () {
- const txGen = new MockTxGen()
- confirmedTxs = txGen.generate({ status: 'confirmed' }, { count: 1 })
- pendingTxs = txGen.generate({
- status: 'submitted',
- txParams: { nonce: '0x01' },
- }, { count: 5 })
-
- nonceTracker = generateNonceTrackerWith(pendingTxs, confirmedTxs, '0x0')
- })
-
- it('should return nonce after those', async function () {
- this.timeout(15000)
- const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926')
- assert.equal(nonceLock.nextNonce, '2', `nonce should be 2 got ${nonceLock.nextNonce}`)
- await nonceLock.releaseLock()
- })
- })
-
- describe('when local confirmed count is higher than network nonce', function () {
- beforeEach(function () {
- const txGen = new MockTxGen()
- confirmedTxs = txGen.generate({ status: 'confirmed' }, { count: 3 })
- nonceTracker = generateNonceTrackerWith([], confirmedTxs, '0x1')
- })
-
- it('should return nonce after those', async function () {
- this.timeout(15000)
- const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926')
- assert.equal(nonceLock.nextNonce, '3', `nonce should be 3 got ${nonceLock.nextNonce}`)
- await nonceLock.releaseLock()
- })
- })
-
- describe('when local pending count is higher than other metrics', function () {
- beforeEach(function () {
- const txGen = new MockTxGen()
- pendingTxs = txGen.generate({ status: 'submitted' }, { count: 2 })
- nonceTracker = generateNonceTrackerWith(pendingTxs, [])
- })
-
- it('should return nonce after those', async function () {
- this.timeout(15000)
- const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926')
- assert.equal(nonceLock.nextNonce, '2', `nonce should be 2 got ${nonceLock.nextNonce}`)
- await nonceLock.releaseLock()
- })
- })
-
- describe('when provider nonce is higher than other metrics', function () {
- beforeEach(function () {
- const txGen = new MockTxGen()
- pendingTxs = txGen.generate({ status: 'submitted' }, { count: 2 })
- nonceTracker = generateNonceTrackerWith(pendingTxs, [], '0x05')
- })
-
- it('should return nonce after those', async function () {
- this.timeout(15000)
- const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926')
- assert.equal(nonceLock.nextNonce, '5', `nonce should be 5 got ${nonceLock.nextNonce}`)
- await nonceLock.releaseLock()
- })
- })
-
- describe('when there are some pending nonces below the remote one and some over.', function () {
- beforeEach(function () {
- const txGen = new MockTxGen()
- pendingTxs = txGen.generate({ status: 'submitted' }, { count: 5 })
- nonceTracker = generateNonceTrackerWith(pendingTxs, [], '0x03')
- })
-
- it('should return nonce after those', async function () {
- this.timeout(15000)
- const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926')
- assert.equal(nonceLock.nextNonce, '5', `nonce should be 5 got ${nonceLock.nextNonce}`)
- await nonceLock.releaseLock()
- })
- })
-
- describe('when there are pending nonces non sequentially over the network nonce.', function () {
- beforeEach(function () {
- const txGen = new MockTxGen()
- txGen.generate({ status: 'submitted' }, { count: 5 })
- // 5 over that number
- pendingTxs = txGen.generate({ status: 'submitted' }, { count: 5 })
- nonceTracker = generateNonceTrackerWith(pendingTxs, [], '0x00')
- })
-
- it('should return nonce after network nonce', async function () {
- this.timeout(15000)
- const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926')
- assert.equal(nonceLock.nextNonce, '0', `nonce should be 0 got ${nonceLock.nextNonce}`)
- await nonceLock.releaseLock()
- })
- })
-
- describe('When all three return different values', function () {
- beforeEach(function () {
- const txGen = new MockTxGen()
- confirmedTxs = txGen.generate({ status: 'confirmed' }, { count: 10 })
- pendingTxs = txGen.generate({
- status: 'submitted',
- nonce: 100,
- }, { count: 1 })
- // 0x32 is 50 in hex:
- nonceTracker = generateNonceTrackerWith(pendingTxs, confirmedTxs, '0x32')
- })
-
- it('should return nonce after network nonce', async function () {
- this.timeout(15000)
- const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926')
- assert.equal(nonceLock.nextNonce, '50', `nonce should be 50 got ${nonceLock.nextNonce}`)
- await nonceLock.releaseLock()
- })
- })
-
- describe('Faq issue 67', function () {
- beforeEach(function () {
- const txGen = new MockTxGen()
- confirmedTxs = txGen.generate({ status: 'confirmed' }, { count: 64 })
- pendingTxs = txGen.generate({
- status: 'submitted',
- }, { count: 10 })
- // 0x40 is 64 in hex:
- nonceTracker = generateNonceTrackerWith(pendingTxs, [], '0x40')
- })
-
- it('should return nonce after network nonce', async function () {
- this.timeout(15000)
- const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926')
- assert.equal(nonceLock.nextNonce, '74', `nonce should be 74 got ${nonceLock.nextNonce}`)
- await nonceLock.releaseLock()
- })
- })
- })
-})
-
-function generateNonceTrackerWith (pending, confirmed, providerStub = '0x0') {
- const getPendingTransactions = () => pending
- const getConfirmedTransactions = () => confirmed
- providerResultStub.result = providerStub
- const provider = {
- sendAsync: (_, cb) => { cb(undefined, providerResultStub) },
- }
- const blockTracker = {
- getCurrentBlock: () => '0x11b568',
- getLatestBlock: async () => '0x11b568',
- }
- return new NonceTracker({
- provider,
- blockTracker,
- getPendingTransactions,
- getConfirmedTransactions,
- })
-}
diff --git a/test/unit/app/controllers/transactions/pending-tx-test.js b/test/unit/app/controllers/transactions/pending-tx-test.js
index 1c5f59f5a..b37ac2766 100644
--- a/test/unit/app/controllers/transactions/pending-tx-test.js
+++ b/test/unit/app/controllers/transactions/pending-tx-test.js
@@ -58,7 +58,7 @@ describe('PendingTransactionTracker', function () {
}
})
- it('should become failed if another tx with the same nonce succeeds', async function () {
+ it('should emit dropped if another tx with the same nonce succeeds', async function () {
// SETUP
const txGen = new MockTxGen()
@@ -84,17 +84,16 @@ describe('PendingTransactionTracker', function () {
// THE EXPECTATION
const spy = sinon.spy()
- pendingTxTracker.on('tx:failed', (txId, err) => {
+ pendingTxTracker.on('tx:dropped', (txId) => {
assert.equal(txId, pending.id, 'should fail the pending tx')
- assert.equal(err.name, 'NonceTakenErr', 'should emit a nonce taken error.')
- spy(txId, err)
+ spy(txId)
})
// THE METHOD
await pendingTxTracker._checkPendingTx(pending)
// THE ASSERTION
- assert.ok(spy.calledWith(pending.id), 'tx failed should be emitted')
+ assert.ok(spy.calledWith(pending.id), 'tx dropped should be emitted')
})
})
@@ -107,6 +106,38 @@ describe('PendingTransactionTracker', function () {
pendingTxTracker._checkPendingTx(txMetaNoHash)
})
+ it('should emit tx:dropped with the txMetas id only after the second call', function (done) {
+ txMeta = {
+ id: 1,
+ hash: '0x0593ee121b92e10d63150ad08b4b8f9c7857d1bd160195ee648fb9a0f8d00eeb',
+ status: 'submitted',
+ txParams: {
+ from: '0x1678a085c290ebd122dc42cba69373b5953b831d',
+ nonce: '0x1',
+ value: '0xfffff',
+ },
+ history: [{}],
+ rawTx: '0xf86c808504a817c800827b0d940c62bb85faa3311a998d3aba8098c1235c564966880de0b6b3a7640000802aa08ff665feb887a25d4099e40e11f0fef93ee9608f404bd3f853dd9e84ed3317a6a02ec9d3d1d6e176d4d2593dd760e74ccac753e6a0ea0d00cc9789d0d7ff1f471d',
+ }
+
+ providerResultStub['eth_getTransactionCount'] = '0x02'
+ providerResultStub['eth_getTransactionByHash'] = {}
+ pendingTxTracker.once('tx:dropped', (id) => {
+ if (id === txMeta.id) {
+ delete providerResultStub['eth_getTransactionCount']
+ delete providerResultStub['eth_getTransactionByHash']
+ return done()
+ } else {
+ done(new Error('wrong tx Id'))
+ }
+ })
+
+ pendingTxTracker._checkPendingTx(txMeta).then(() => {
+ pendingTxTracker._checkPendingTx(txMeta).catch(done)
+ }).catch(done)
+ })
+
+
it('should should return if query does not return txParams', function () {
providerResultStub.eth_getTransactionByHash = null
pendingTxTracker._checkPendingTx(txMeta)
@@ -283,6 +314,37 @@ describe('PendingTransactionTracker', function () {
})
})
+ describe('#_checkIftxWasDropped', () => {
+ const txMeta = {
+ id: 1,
+ hash: '0x0593ee121b92e10d63150ad08b4b8f9c7857d1bd160195ee648fb9a0f8d00eeb',
+ status: 'submitted',
+ txParams: {
+ from: '0x1678a085c290ebd122dc42cba69373b5953b831d',
+ nonce: '0x1',
+ value: '0xfffff',
+ },
+ rawTx: '0xf86c808504a817c800827b0d940c62bb85faa3311a998d3aba8098c1235c564966880de0b6b3a7640000802aa08ff665feb887a25d4099e40e11f0fef93ee9608f404bd3f853dd9e84ed3317a6a02ec9d3d1d6e176d4d2593dd760e74ccac753e6a0ea0d00cc9789d0d7ff1f471d',
+ }
+ it('should return false when the nonce is the suggested network nonce', (done) => {
+ providerResultStub['eth_getTransactionCount'] = '0x01'
+ providerResultStub['eth_getTransactionByHash'] = {}
+ pendingTxTracker._checkIftxWasDropped(txMeta).then((dropped) => {
+ assert(!dropped, 'should be false')
+ done()
+ }).catch(done)
+ })
+
+ it('should return true when the network nonce is higher then the txMeta nonce', function (done) {
+ providerResultStub['eth_getTransactionCount'] = '0x02'
+ providerResultStub['eth_getTransactionByHash'] = {}
+ pendingTxTracker._checkIftxWasDropped(txMeta).then((dropped) => {
+ assert(dropped, 'should be true')
+ done()
+ }).catch(done)
+ })
+ })
+
describe('#_checkIfNonceIsTaken', function () {
beforeEach(function () {
const confirmedTxList = [{
diff --git a/ui/app/components/app/ens-input.js b/ui/app/components/app/ens-input.js
index 5eea0dd90..f17f6c3d6 100644
--- a/ui/app/components/app/ens-input.js
+++ b/ui/app/components/app/ens-input.js
@@ -41,12 +41,15 @@ EnsInput.prototype.onChange = function (recipient) {
ensResolution: null,
ensFailure: null,
toError: null,
+ recipient,
})
}
this.setState({
loadingEns: true,
+ recipient,
})
+
this.checkName(recipient)
}
@@ -56,6 +59,7 @@ EnsInput.prototype.render = function () {
list: 'addresses',
onChange: this.onChange.bind(this),
qrScanner: true,
+ recipient: (this.state || {}).recipient,
})
return h('div', {
style: { width: '100%', position: 'relative' },
@@ -79,19 +83,21 @@ EnsInput.prototype.componentDidMount = function () {
EnsInput.prototype.lookupEnsName = function (recipient) {
const { ensResolution } = this.state
+ recipient = recipient.trim()
log.info(`ENS attempting to resolve name: ${recipient}`)
- this.ens.lookup(recipient.trim())
+ this.ens.lookup(recipient)
.then((address) => {
if (address === ZERO_ADDRESS) throw new Error(this.context.t('noAddressForName'))
if (address !== ensResolution) {
this.setState({
loadingEns: false,
ensResolution: address,
- nickname: recipient.trim(),
+ nickname: recipient,
hoverText: address + '\n' + this.context.t('clickCopy'),
ensFailure: false,
toError: null,
+ recipient,
})
}
})
@@ -101,11 +107,11 @@ EnsInput.prototype.lookupEnsName = function (recipient) {
ensResolution: recipient,
ensFailure: true,
toError: null,
+ recipient: null,
}
if (isValidENSAddress(recipient) && reason.message === 'ENS name not defined.') {
setStateObj.hoverText = this.context.t('ensNameNotFound')
setStateObj.toError = 'ensNameNotFound'
- setStateObj.ensFailure = false
} else {
log.error(reason)
setStateObj.hoverText = reason.message
@@ -128,7 +134,7 @@ EnsInput.prototype.componentDidUpdate = function (prevProps, prevState) {
}
if (prevState && ensResolution && this.props.onChange &&
ensResolution !== prevState.ensResolution) {
- this.props.onChange({ toAddress: ensResolution, nickname, toError: state.toError, toWarning: state.toWarning })
+ this.props.onChange({ toAddress: ensResolution, recipient: state.recipient, nickname, toError: state.toError, toWarning: state.toWarning })
}
}
diff --git a/ui/app/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container.container.js b/ui/app/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container.container.js
index 0e7e30347..9da9a2ef6 100644
--- a/ui/app/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container.container.js
+++ b/ui/app/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container.container.js
@@ -7,6 +7,8 @@ import {
setGasPrice,
createSpeedUpTransaction,
hideSidebar,
+ updateSendAmount,
+ setGasTotal,
} from '../../../../store/actions'
import {
setCustomGasPrice,
@@ -18,6 +20,7 @@ import {
} from '../../../../ducks/gas/gas.duck'
import {
hideGasButtonGroup,
+ updateSendErrors,
} from '../../../../ducks/send/send.duck'
import {
updateGasAndCalculate,
@@ -46,6 +49,9 @@ import {
isCustomPriceSafe,
} from '../../../../selectors/custom-gas'
import {
+ getTokenBalance,
+} from '../../../../pages/send/send.selectors'
+import {
submittedPendingTransactionsSelector,
} from '../../../../selectors/transactions'
import {
@@ -53,6 +59,7 @@ import {
} from '../../../../helpers/utils/confirm-tx.util'
import {
addHexWEIsToDec,
+ subtractHexWEIsToDec,
decEthToConvertedCurrency as ethTotalToConvertedCurrency,
decGWEIToHexWEI,
hexWEIToDecGWEI,
@@ -66,6 +73,8 @@ import {
} from '../../../../pages/send/send.utils'
import { addHexPrefix } from 'ethereumjs-util'
import { getAdjacentGasPrices, extrapolateY } from '../gas-price-chart/gas-price-chart.utils'
+import { getMaxModeOn } from '../../../../pages/send/send-content/send-amount-row/amount-max-button/amount-max-button.selectors'
+import { calcMaxAmount } from '../../../../pages/send/send-content/send-amount-row/amount-max-button/amount-max-button.utils'
const mapStateToProps = (state, ownProps) => {
const { transaction = {} } = ownProps
@@ -75,8 +84,6 @@ const mapStateToProps = (state, ownProps) => {
const { gasPrice: currentGasPrice, gas: currentGasLimit, value } = getTxParams(state, transaction.id)
const customModalGasPriceInHex = getCustomGasPrice(state) || currentGasPrice
const customModalGasLimitInHex = getCustomGasLimit(state) || currentGasLimit
- const gasTotal = calcGasTotal(customModalGasLimitInHex, customModalGasPriceInHex)
-
const customGasTotal = calcGasTotal(customModalGasLimitInHex, customModalGasPriceInHex)
const gasButtonInfo = getRenderableBasicEstimateData(state, customModalGasLimitInHex)
@@ -90,6 +97,8 @@ const mapStateToProps = (state, ownProps) => {
const customGasPrice = calcCustomGasPrice(customModalGasPriceInHex)
+ const maxModeOn = getMaxModeOn(state)
+
const gasPrices = getEstimatedGasPrices(state)
const estimatedTimes = getEstimatedGasTimes(state)
const balance = getCurrentEthBalance(state)
@@ -98,9 +107,13 @@ const mapStateToProps = (state, ownProps) => {
const isMainnet = getIsMainnet(state)
const showFiat = Boolean(isMainnet || showFiatInTestnets)
- const insufficientBalance = !isBalanceSufficient({
+ const newTotalEth = maxModeOn ? addHexWEIsToRenderableEth(balance, '0x0') : addHexWEIsToRenderableEth(value, customGasTotal)
+
+ const sendAmount = maxModeOn ? subtractHexWEIsFromRenderableEth(balance, customGasTotal) : addHexWEIsToRenderableEth(value, '0x0')
+
+ const insufficientBalance = maxModeOn ? false : !isBalanceSufficient({
amount: value,
- gasTotal,
+ gasTotal: customGasTotal,
balance,
conversionRate,
})
@@ -112,10 +125,12 @@ const mapStateToProps = (state, ownProps) => {
customModalGasLimitInHex,
customGasPrice,
customGasLimit: calcCustomGasLimit(customModalGasLimitInHex),
+ customGasTotal,
newTotalFiat,
currentTimeEstimate: getRenderableTimeEstimate(customGasPrice, gasPrices, estimatedTimes),
blockTime: getBasicGasEstimateBlockTime(state),
customPriceIsSafe: isCustomPriceSafe(state),
+ maxModeOn,
gasPriceButtonGroupProps: {
buttonDataLoading,
defaultActiveButtonIndex: getDefaultActiveButtonIndex(gasButtonInfo, customModalGasPriceInHex),
@@ -129,12 +144,12 @@ const mapStateToProps = (state, ownProps) => {
estimatedTimesMax: estimatedTimes[0],
},
infoRowProps: {
- originalTotalFiat: addHexWEIsToRenderableFiat(value, gasTotal, currentCurrency, conversionRate),
- originalTotalEth: addHexWEIsToRenderableEth(value, gasTotal),
+ originalTotalFiat: addHexWEIsToRenderableFiat(value, customGasTotal, currentCurrency, conversionRate),
+ originalTotalEth: addHexWEIsToRenderableEth(value, customGasTotal),
newTotalFiat: showFiat ? newTotalFiat : '',
- newTotalEth: addHexWEIsToRenderableEth(value, customGasTotal),
+ newTotalEth,
transactionFee: addHexWEIsToRenderableEth('0x0', customGasTotal),
- sendAmount: addHexWEIsToRenderableEth(value, '0x0'),
+ sendAmount,
},
isSpeedUp: transaction.status === 'submitted',
txId: transaction.id,
@@ -142,6 +157,9 @@ const mapStateToProps = (state, ownProps) => {
gasEstimatesLoading,
isMainnet,
isEthereumNetwork: isEthereumNetwork(state),
+ selectedToken: getSelectedToken(state),
+ balance,
+ tokenBalance: getTokenBalance(state),
}
}
@@ -174,11 +192,29 @@ const mapDispatchToProps = dispatch => {
hideSidebar: () => dispatch(hideSidebar()),
fetchGasEstimates: (blockTime) => dispatch(fetchGasEstimates(blockTime)),
fetchBasicGasAndTimeEstimates: () => dispatch(fetchBasicGasAndTimeEstimates()),
+ setGasTotal: (total) => dispatch(setGasTotal(total)),
+ setAmountToMax: (maxAmountDataObject) => {
+ dispatch(updateSendErrors({ amount: null }))
+ dispatch(updateSendAmount(calcMaxAmount(maxAmountDataObject)))
+ },
}
}
const mergeProps = (stateProps, dispatchProps, ownProps) => {
- const { gasPriceButtonGroupProps, isConfirm, txId, isSpeedUp, insufficientBalance, customGasPrice } = stateProps
+ const {
+ gasPriceButtonGroupProps,
+ isConfirm,
+ txId,
+ isSpeedUp,
+ insufficientBalance,
+ maxModeOn,
+ customGasPrice,
+ customGasTotal,
+ balance,
+ selectedToken,
+ tokenBalance,
+ customGasLimit,
+ } = stateProps
const {
updateCustomGasPrice: dispatchUpdateCustomGasPrice,
hideGasButtonGroup: dispatchHideGasButtonGroup,
@@ -188,6 +224,7 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => {
hideSidebar: dispatchHideSidebar,
cancelAndClose: dispatchCancelAndClose,
hideModal: dispatchHideModal,
+ setAmountToMax: dispatchSetAmountToMax,
...otherDispatchProps
} = dispatchProps
@@ -208,6 +245,14 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => {
dispatchHideGasButtonGroup()
dispatchCancelAndClose()
}
+ if (maxModeOn) {
+ dispatchSetAmountToMax({
+ balance,
+ gasTotal: customGasTotal,
+ selectedToken,
+ tokenBalance,
+ })
+ }
},
gasPriceButtonGroupProps: {
...gasPriceButtonGroupProps,
@@ -219,7 +264,7 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => {
dispatchHideSidebar()
}
},
- disableSave: insufficientBalance || (isSpeedUp && customGasPrice === 0),
+ disableSave: insufficientBalance || (isSpeedUp && customGasPrice === 0) || customGasLimit < 21000,
}
}
@@ -258,6 +303,13 @@ function addHexWEIsToRenderableEth (aHexWEI, bHexWEI) {
)(aHexWEI, bHexWEI)
}
+function subtractHexWEIsFromRenderableEth (aHexWEI, bHexWei) {
+ return pipe(
+ subtractHexWEIsToDec,
+ formatETHFee
+ )(aHexWEI, bHexWei)
+}
+
function addHexWEIsToRenderableFiat (aHexWEI, bHexWEI, convertedCurrency, conversionRate) {
return pipe(
addHexWEIsToDec,
diff --git a/ui/app/components/app/gas-customization/gas-modal-page-container/tests/gas-modal-page-container-container.test.js b/ui/app/components/app/gas-customization/gas-modal-page-container/tests/gas-modal-page-container-container.test.js
index ab24b9c0e..dbe61d5cf 100644
--- a/ui/app/components/app/gas-customization/gas-modal-page-container/tests/gas-modal-page-container-container.test.js
+++ b/ui/app/components/app/gas-customization/gas-modal-page-container/tests/gas-modal-page-container-container.test.js
@@ -46,6 +46,10 @@ proxyquire('../gas-modal-page-container.container.js', {
'../../../../ducks/send/send.duck': sendActionSpies,
'../../../../selectors/selectors.js': {
getCurrentEthBalance: (state) => state.metamask.balance || '0x0',
+ getSelectedToken: () => null,
+ },
+ '../../../../pages/send/send.selectors': {
+ getTokenBalance: (state) => state.metamask.send.tokenBalance || '0x0',
},
})
@@ -68,6 +72,7 @@ describe('gas-modal-page-container container', () => {
gasLimit: '16',
gasPrice: '32',
amount: '64',
+ maxModeOn: false,
},
currentCurrency: 'abc',
conversionRate: 50,
@@ -106,6 +111,7 @@ describe('gas-modal-page-container container', () => {
},
}
const baseExpectedResult = {
+ balance: '0x0',
isConfirm: true,
customGasPrice: 4.294967295,
customGasLimit: 2863311530,
@@ -114,6 +120,7 @@ describe('gas-modal-page-container container', () => {
blockTime: 12,
customModalGasLimitInHex: 'aaaaaaaa',
customModalGasPriceInHex: 'ffffffff',
+ customGasTotal: 'aaaaaaa955555556',
customPriceIsSafe: true,
gasChartProps: {
'currentPrice': 4.294967295,
@@ -142,6 +149,9 @@ describe('gas-modal-page-container container', () => {
txId: 34,
isEthereumNetwork: true,
isMainnet: true,
+ maxModeOn: false,
+ selectedToken: null,
+ tokenBalance: '0x0',
}
const baseMockOwnProps = { transaction: { id: 34 } }
const tests = [
@@ -150,7 +160,7 @@ describe('gas-modal-page-container container', () => {
mockState: Object.assign({}, baseMockState, {
metamask: { ...baseMockState.metamask, balance: '0xfffffffffffffffffffff' },
}),
- expectedResult: Object.assign({}, baseExpectedResult, { insufficientBalance: false }),
+ expectedResult: Object.assign({}, baseExpectedResult, { balance: '0xfffffffffffffffffffff', insufficientBalance: false }),
mockOwnProps: baseMockOwnProps,
},
{
diff --git a/ui/app/components/ui/currency-input/currency-input.component.js b/ui/app/components/ui/currency-input/currency-input.component.js
index b5be0972b..1876c9591 100644
--- a/ui/app/components/ui/currency-input/currency-input.component.js
+++ b/ui/app/components/ui/currency-input/currency-input.component.js
@@ -18,6 +18,7 @@ export default class CurrencyInput extends PureComponent {
static propTypes = {
conversionRate: PropTypes.number,
currentCurrency: PropTypes.string,
+ maxModeOn: PropTypes.bool,
nativeCurrency: PropTypes.string,
onChange: PropTypes.func,
onBlur: PropTypes.func,
@@ -136,7 +137,7 @@ export default class CurrencyInput extends PureComponent {
}
render () {
- const { fiatSuffix, nativeSuffix, ...restProps } = this.props
+ const { fiatSuffix, nativeSuffix, maxModeOn, ...restProps } = this.props
const { decimalValue } = this.state
return (
@@ -146,6 +147,7 @@ export default class CurrencyInput extends PureComponent {
onChange={this.handleChange}
onBlur={this.handleBlur}
value={decimalValue}
+ maxModeOn={maxModeOn}
actionComponent={(
<div
className="currency-input__swap-component"
diff --git a/ui/app/components/ui/currency-input/currency-input.container.js b/ui/app/components/ui/currency-input/currency-input.container.js
index b5d7dfe6d..46e70bace 100644
--- a/ui/app/components/ui/currency-input/currency-input.container.js
+++ b/ui/app/components/ui/currency-input/currency-input.container.js
@@ -1,18 +1,21 @@
import { connect } from 'react-redux'
import CurrencyInput from './currency-input.component'
import { ETH } from '../../../helpers/constants/common'
+import { getMaxModeOn } from '../../../pages/send/send-content/send-amount-row/amount-max-button/amount-max-button.selectors'
import {getIsMainnet, preferencesSelector} from '../../../selectors/selectors'
const mapStateToProps = state => {
const { metamask: { nativeCurrency, currentCurrency, conversionRate } } = state
const { showFiatInTestnets } = preferencesSelector(state)
const isMainnet = getIsMainnet(state)
+ const maxModeOn = getMaxModeOn(state)
return {
nativeCurrency,
currentCurrency,
conversionRate,
hideFiat: (!isMainnet && !showFiatInTestnets),
+ maxModeOn,
}
}
diff --git a/ui/app/components/ui/currency-input/tests/currency-input.container.test.js b/ui/app/components/ui/currency-input/tests/currency-input.container.test.js
index 259fe594a..f10abe09a 100644
--- a/ui/app/components/ui/currency-input/tests/currency-input.container.test.js
+++ b/ui/app/components/ui/currency-input/tests/currency-input.container.test.js
@@ -30,6 +30,9 @@ describe('CurrencyInput container', () => {
provider: {
type: 'mainnet',
},
+ send: {
+ maxModeOn: false,
+ },
},
},
expected: {
@@ -37,6 +40,7 @@ describe('CurrencyInput container', () => {
currentCurrency: 'usd',
nativeCurrency: 'ETH',
hideFiat: false,
+ maxModeOn: false,
},
},
// Test # 2
@@ -53,6 +57,9 @@ describe('CurrencyInput container', () => {
provider: {
type: 'rinkeby',
},
+ send: {
+ maxModeOn: false,
+ },
},
},
expected: {
@@ -60,6 +67,7 @@ describe('CurrencyInput container', () => {
currentCurrency: 'usd',
nativeCurrency: 'ETH',
hideFiat: true,
+ maxModeOn: false,
},
},
// Test # 3
@@ -76,6 +84,9 @@ describe('CurrencyInput container', () => {
provider: {
type: 'rinkeby',
},
+ send: {
+ maxModeOn: false,
+ },
},
},
expected: {
@@ -83,6 +94,7 @@ describe('CurrencyInput container', () => {
currentCurrency: 'usd',
nativeCurrency: 'ETH',
hideFiat: false,
+ maxModeOn: false,
},
},
// Test # 4
@@ -99,6 +111,9 @@ describe('CurrencyInput container', () => {
provider: {
type: 'mainnet',
},
+ send: {
+ maxModeOn: false,
+ },
},
},
expected: {
@@ -106,6 +121,7 @@ describe('CurrencyInput container', () => {
currentCurrency: 'usd',
nativeCurrency: 'ETH',
hideFiat: false,
+ maxModeOn: false,
},
},
]
diff --git a/ui/app/components/ui/unit-input/index.scss b/ui/app/components/ui/unit-input/index.scss
index adc4a3531..58a10c9a1 100644
--- a/ui/app/components/ui/unit-input/index.scss
+++ b/ui/app/components/ui/unit-input/index.scss
@@ -42,6 +42,10 @@
max-width: 22ch;
height: 16px;
line-height: 18px;
+
+ &__disabled {
+ background-color: rgb(222, 222, 222);
+ }
}
&__input-container {
@@ -59,4 +63,9 @@
&--error {
border-color: $red;
}
+
+ &__disabled {
+ background-color: #F2F3F4;
+ }
+
}
diff --git a/ui/app/components/ui/unit-input/unit-input.component.js b/ui/app/components/ui/unit-input/unit-input.component.js
index 6a53f4c6f..9085a0677 100644
--- a/ui/app/components/ui/unit-input/unit-input.component.js
+++ b/ui/app/components/ui/unit-input/unit-input.component.js
@@ -13,6 +13,7 @@ export default class UnitInput extends PureComponent {
children: PropTypes.node,
actionComponent: PropTypes.node,
error: PropTypes.bool,
+ maxModeOn: PropTypes.bool,
onBlur: PropTypes.func,
onChange: PropTypes.func,
placeholder: PropTypes.string,
@@ -71,25 +72,26 @@ export default class UnitInput extends PureComponent {
}
render () {
- const { error, placeholder, suffix, actionComponent, children } = this.props
+ const { error, placeholder, suffix, actionComponent, children, maxModeOn } = this.props
const { value } = this.state
return (
<div
- className={classnames('unit-input', { 'unit-input--error': error })}
- onClick={this.handleFocus}
+ className={classnames('unit-input', { 'unit-input--error': error }, { 'unit-input__disabled': maxModeOn })}
+ onClick={maxModeOn ? null : this.handleFocus}
>
<div className="unit-input__inputs">
<div className="unit-input__input-container">
<input
type="number"
- className="unit-input__input"
+ className={classnames('unit-input__input', { 'unit-input__disabled': maxModeOn })}
value={value}
placeholder={placeholder}
onChange={this.handleChange}
onBlur={this.handleBlur}
style={{ width: this.getInputWidth(value) }}
ref={ref => { this.unitInput = ref }}
+ disabled={maxModeOn}
/>
{
suffix && (
diff --git a/ui/app/css/itcss/components/send.scss b/ui/app/css/itcss/components/send.scss
index 927640f0b..e2f0f9b2f 100644
--- a/ui/app/css/itcss/components/send.scss
+++ b/ui/app/css/itcss/components/send.scss
@@ -520,6 +520,10 @@
color: $red;
}
+ &__error-amount {
+ margin-top: 5px;
+ }
+
&__warning {
font-size: 12px;
line-height: 12px;
@@ -557,6 +561,12 @@
justify-content: space-between;
}
+ &__form-field-container {
+ display: flex;
+ flex-direction: column;
+ width: 277px;
+ }
+
&__form-field {
flex: 1 1 auto;
min-width: 0;
@@ -763,7 +773,43 @@
}
}
- &__to-autocomplete, &__memo-text-area, &__hex-data {
+ &__to-autocomplete {
+ display: flex;
+ flex-direction: column;
+ z-index: 1025;
+ position: relative;
+ height: 54px;
+ width: 100%;
+ border: 1px solid $alto;
+ border-radius: 4px;
+ background-color: $white;
+ color: $tundora;
+ padding: 0 10px;
+ font-family: Roboto;
+ line-height: 21px;
+
+ &__input {
+ font-size: 16px;
+ height: 100%;
+ border: none;
+ }
+
+ &__resolved {
+ font-size: 12px;
+ text-overflow: ellipsis;
+ overflow: hidden;
+ white-space: nowrap;
+ height: 30px;
+ cursor: pointer;
+
+ + .send-v2__to-autocomplete__qr-code {
+ top: 2px;
+ right: 0;
+ }
+ }
+ }
+
+ &__memo-text-area, &__hex-data {
&__input {
z-index: 1025;
position: relative;
@@ -781,12 +827,47 @@
}
&__amount-max {
- color: $curious-blue;
font-family: Roboto;
font-size: 12px;
- left: 8px;
- border: none;
- cursor: pointer;
+ position: relative;
+ display: inline-block;
+ width: 56px;
+ height: 20px;
+ margin-top: 5px;
+
+ &__button {
+ width: 56px;
+ height: 20px;
+ position: absolute;
+ border: 2px solid #B0D7F2;
+ border-radius: 6px;
+ cursor: pointer;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: #2f9ae0;
+
+ &__disabled {
+ color: #B0D7F2;
+ cursor: auto;
+ }
+ }
+
+ input:checked + &__button {
+ background-color: #037DD6;
+ border: 2px solid #037DD6;
+ color: #fff;
+ }
+ }
+
+ &__amount-max input {
+ opacity: 0;
+ width: 0;
+ height: 0;
}
&__gas-fee-display {
@@ -1041,7 +1122,7 @@
font-size: 14px;
color: #2f9ae0;
cursor: pointer;
- margin-top: 16px;
+ margin-top: 5px;
}
.sliders-icon-container {
diff --git a/ui/app/helpers/utils/conversions.util.js b/ui/app/helpers/utils/conversions.util.js
index b4ec50626..5e1c21ff7 100644
--- a/ui/app/helpers/utils/conversions.util.js
+++ b/ui/app/helpers/utils/conversions.util.js
@@ -1,6 +1,6 @@
import ethUtil from 'ethereumjs-util'
import { ETH, GWEI, WEI } from '../constants/common'
-import { conversionUtil, addCurrencies } from './conversion-util'
+import { conversionUtil, addCurrencies, subtractCurrencies } from './conversion-util'
export function bnToHex (inputBn) {
return ethUtil.addHexPrefix(inputBn.toString(16))
@@ -92,6 +92,15 @@ export function addHexWEIsToDec (aHexWEI, bHexWEI) {
})
}
+export function subtractHexWEIsToDec (aHexWEI, bHexWEI) {
+ return subtractCurrencies(aHexWEI, bHexWEI, {
+ aBase: 16,
+ bBase: 16,
+ fromDenomination: 'WEI',
+ numberOfDecimals: 6,
+ })
+}
+
export function decEthToConvertedCurrency (ethTotal, convertedCurrency, conversionRate) {
return conversionUtil(ethTotal, {
fromNumericBase: 'dec',
diff --git a/ui/app/helpers/utils/metametrics.util.js b/ui/app/helpers/utils/metametrics.util.js
index cafbd5c07..50270c6a8 100644
--- a/ui/app/helpers/utils/metametrics.util.js
+++ b/ui/app/helpers/utils/metametrics.util.js
@@ -12,6 +12,8 @@ const METAMETRICS_TRACKING_URL = inDevelopment
? 'http://www.metamask.io/metametrics'
: 'http://www.metamask.io/metametrics-prod'
+/** ***************Custom variables*************** **/
+// Custon variable declarations
const METAMETRICS_CUSTOM_GAS_LIMIT_CHANGE = 'gasLimitChange'
const METAMETRICS_CUSTOM_GAS_PRICE_CHANGE = 'gasPriceChange'
const METAMETRICS_CUSTOM_FUNCTION_TYPE = 'functionType'
@@ -24,13 +26,7 @@ const METAMETRICS_CUSTOM_ERROR_MESSAGE = 'errorMessage'
const METAMETRICS_CUSTOM_RPC_NETWORK_ID = 'networkId'
const METAMETRICS_CUSTOM_RPC_CHAIN_ID = 'chainId'
const METAMETRICS_CUSTOM_GAS_CHANGED = 'gasChanged'
-
-const METAMETRICS_CUSTOM_NETWORK = 'network'
-const METAMETRICS_CUSTOM_ENVIRONMENT_TYPE = 'environmentType'
-const METAMETRICS_CUSTOM_ACTIVE_CURRENCY = 'activeCurrency'
-const METAMETRICS_CUSTOM_ACCOUNT_TYPE = 'accountType'
-const METAMETRICS_CUSTOM_NUMBER_OF_TOKENS = 'numberOfTokens'
-const METAMETRICS_CUSTOM_NUMBER_OF_ACCOUNTS = 'numberOfAccounts'
+const METAMETRICS_CUSTOM_ASSET_SELECTED = 'assetSelected'
const customVariableNameIdMap = {
[METAMETRICS_CUSTOM_FUNCTION_TYPE]: 1,
@@ -38,14 +34,28 @@ const customVariableNameIdMap = {
[METAMETRICS_CUSTOM_CONFIRM_SCREEN_ORIGIN]: 3,
[METAMETRICS_CUSTOM_GAS_LIMIT_CHANGE]: 4,
[METAMETRICS_CUSTOM_GAS_PRICE_CHANGE]: 5,
+
[METAMETRICS_CUSTOM_FROM_NETWORK]: 1,
[METAMETRICS_CUSTOM_TO_NETWORK]: 2,
+
[METAMETRICS_CUSTOM_RPC_NETWORK_ID]: 1,
[METAMETRICS_CUSTOM_RPC_CHAIN_ID]: 2,
- [METAMETRICS_CUSTOM_ERROR_FIELD]: 1,
- [METAMETRICS_CUSTOM_ERROR_MESSAGE]: 2,
+
+ [METAMETRICS_CUSTOM_ERROR_FIELD]: 3,
+ [METAMETRICS_CUSTOM_ERROR_MESSAGE]: 4,
+
[METAMETRICS_CUSTOM_GAS_CHANGED]: 1,
+ [METAMETRICS_CUSTOM_ASSET_SELECTED]: 2,
}
+/** ********************************************************** **/
+
+const METAMETRICS_CUSTOM_NETWORK = 'network'
+const METAMETRICS_CUSTOM_ENVIRONMENT_TYPE = 'environmentType'
+const METAMETRICS_CUSTOM_ACTIVE_CURRENCY = 'activeCurrency'
+const METAMETRICS_CUSTOM_ACCOUNT_TYPE = 'accountType'
+const METAMETRICS_CUSTOM_NUMBER_OF_TOKENS = 'numberOfTokens'
+const METAMETRICS_CUSTOM_NUMBER_OF_ACCOUNTS = 'numberOfAccounts'
+
const customDimensionsNameIdMap = {
[METAMETRICS_CUSTOM_NETWORK]: 5,
@@ -61,6 +71,7 @@ function composeUrlRefParamAddition (previousPath, confirmTransactionOrigin) {
return `&urlref=${externalOrigin ? 'EXTERNAL' : encodeURIComponent(previousPath.replace(/chrome-extension:\/\/\w+/, METAMETRICS_TRACKING_URL))}`
}
+// composes query params of the form &dimension[0-999]=[value]
function composeCustomDimensionParamAddition (customDimensions) {
const customDimensionParamStrings = Object.keys(customDimensions).reduce((acc, name) => {
return [...acc, `dimension${customDimensionsNameIdMap[name]}=${customDimensions[name]}`]
@@ -68,6 +79,8 @@ function composeCustomDimensionParamAddition (customDimensions) {
return `&${customDimensionParamStrings.join('&')}`
}
+// composes query params in form: &cvar={[id]:[[name],[value]]}
+// Example: &cvar={"1":["OS","iphone 5.0"],"2":["Matomo Mobile Version","1.6.2"],"3":["Locale","en::en"],"4":["Num Accounts","2"]}
function composeCustomVarParamAddition (customVariables) {
const customVariableIdValuePairs = Object.keys(customVariables).reduce((acc, name) => {
return {
@@ -84,6 +97,28 @@ function composeParamAddition (paramValue, paramName) {
: `&${paramName}=${paramValue}`
}
+/**
+ * @name composeUrl
+ * @param {Object} config - configuration object for composing the metametrics url
+ * @property {object} config.eventOpts Object containing event category, action and name descriptors
+ * @property {object} config.customVariables Object containing custom properties with values relevant to a specific event
+ * @property {object} config.pageOpts Objects containing information about a page/route the event is dispatched from
+ * @property {number} config.network The selected network of the user when the event occurs
+ * @property {string} config.environmentType The "environment" the user is using the app from: 'popup', 'notification' or 'fullscreen'
+ * @property {string} config.activeCurrency The current the user has select as their primary currency at the time of the event
+ * @property {string} config.accountType The account type being used at the time of the event: 'hardware', 'imported' or 'default'
+ * @property {number} config.numberOfTokens The number of tokens that the user has added at the time of the event
+ * @property {number} config.numberOfAccounts The number of accounts the user has added at the time of the event
+ * @property {string} config.previousPath The location path the user was on prior to the path they are on at the time of the event
+ * @property {string} config.currentPath The location path the user is on at the time of the event
+ * @property {string} config.metaMetricsId A random id assigned to a user at the time of opting in to metametrics. A hexadecimal number
+ * @property {string} config.confirmTransactionOrigin The origin on a transaction
+ * @property {string} config.url The url to track an event at. Overrides `currentPath`
+ * @property {boolean} config.excludeMetaMetricsId Whether or not the tracked event data should be associated with a metametrics id
+ * @property {boolean} config.isNewVisit Whether or not the event should be tracked as a new visit/user sessions
+ * @returns {String} Returns a url to be passed to fetch to make the appropriate request to matomo.
+ * Example: https://chromeextensionmm.innocraft.cloud/piwik.php?idsite=1&rec=1&apiv=1&e_c=Navigation&e_a=Home&e_n=Clicked%20Send:%20Eth&urlref=http%3A%2F%2Fwww.metamask.io%2Fmetametrics%2Fhome.html%23send&dimension5=3&dimension6=fullscreen&dimension7=ETH&dimension8=default&dimension9=0&dimension10=3&url=http%3A%2F%2Fwww.metamask.io%2Fmetametrics%2Fhome.html%23&_id=49c10aff19795e9a&rand=7906028754863992&pv_id=53acad&uid=49c1
+ */
function composeUrl (config) {
const {
eventOpts = {},
diff --git a/ui/app/pages/confirm-transaction-base/confirm-transaction-base.component.js b/ui/app/pages/confirm-transaction-base/confirm-transaction-base.component.js
index 3c4e6dcac..c6a05cf0f 100644
--- a/ui/app/pages/confirm-transaction-base/confirm-transaction-base.component.js
+++ b/ui/app/pages/confirm-transaction-base/confirm-transaction-base.component.js
@@ -9,6 +9,7 @@ import { DEFAULT_ROUTE, CONFIRM_TRANSACTION_ROUTE } from '../../helpers/constant
import {
INSUFFICIENT_FUNDS_ERROR_KEY,
TRANSACTION_ERROR_KEY,
+ GAS_LIMIT_TOO_LOW_ERROR_KEY,
} from '../../helpers/constants/error-keys'
import { CONFIRMED_STATUS, DROPPED_STATUS } from '../../helpers/constants/transactions'
import UserPreferencedCurrencyDisplay from '../../components/app/user-preferenced-currency-display'
@@ -134,6 +135,7 @@ export default class ConfirmTransactionBase extends Component {
value: amount,
} = {},
} = {},
+ customGas,
} = this.props
const insufficientBalance = balance && !isBalanceSufficient({
@@ -150,6 +152,13 @@ export default class ConfirmTransactionBase extends Component {
}
}
+ if (customGas.gasLimit < 21000) {
+ return {
+ valid: false,
+ errorKey: GAS_LIMIT_TOO_LOW_ERROR_KEY,
+ }
+ }
+
if (simulationFails) {
return {
valid: true,
diff --git a/ui/app/pages/create-account/connect-hardware/index.js b/ui/app/pages/create-account/connect-hardware/index.js
index 5a91a2725..80a160205 100644
--- a/ui/app/pages/create-account/connect-hardware/index.js
+++ b/ui/app/pages/create-account/connect-hardware/index.js
@@ -8,8 +8,6 @@ const ConnectScreen = require('./connect-screen')
const AccountList = require('./account-list')
const { DEFAULT_ROUTE } = require('../../../helpers/constants/routes')
const { formatBalance } = require('../../../helpers/utils/util')
-const { getPlatform } = require('../../../../../app/scripts/lib/util')
-const { PLATFORM_FIREFOX } = require('../../../../../app/scripts/lib/enums')
class ConnectHardwareForm extends Component {
constructor (props) {
@@ -51,12 +49,6 @@ class ConnectHardwareForm extends Component {
}
connectToHardwareWallet = (device) => {
- // Ledger hardware wallets are not supported on firefox
- if (getPlatform() === PLATFORM_FIREFOX && device === 'ledger') {
- this.setState({ browserSupported: false, error: null})
- return null
- }
-
if (this.state.accounts.length) {
return null
}
diff --git a/ui/app/pages/send/send-content/send-amount-row/amount-max-button/amount-max-button.component.js b/ui/app/pages/send/send-content/send-amount-row/amount-max-button/amount-max-button.component.js
index e256d1442..7901ccef6 100644
--- a/ui/app/pages/send/send-content/send-amount-row/amount-max-button/amount-max-button.component.js
+++ b/ui/app/pages/send/send-content/send-amount-row/amount-max-button/amount-max-button.component.js
@@ -1,16 +1,21 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
+import classnames from 'classnames'
export default class AmountMaxButton extends Component {
static propTypes = {
balance: PropTypes.string,
+ buttonDataLoading: PropTypes.bool,
+ clearMaxAmount: PropTypes.func,
+ inError: PropTypes.bool,
gasTotal: PropTypes.string,
maxModeOn: PropTypes.bool,
selectedToken: PropTypes.object,
setAmountToMax: PropTypes.func,
setMaxModeTo: PropTypes.func,
tokenBalance: PropTypes.string,
+
}
static contextTypes = {
@@ -35,8 +40,8 @@ export default class AmountMaxButton extends Component {
})
}
- onMaxClick = (event) => {
- const { setMaxModeTo } = this.props
+ onMaxClick = () => {
+ const { setMaxModeTo, clearMaxAmount, maxModeOn } = this.props
const { metricsEvent } = this.context
metricsEvent({
@@ -46,25 +51,25 @@ export default class AmountMaxButton extends Component {
name: 'Clicked "Amount Max"',
},
})
-
- event.preventDefault()
- setMaxModeTo(true)
- this.setMaxAmount()
+ if (!maxModeOn) {
+ setMaxModeTo(true)
+ this.setMaxAmount()
+ } else {
+ setMaxModeTo(false)
+ clearMaxAmount()
+ }
}
render () {
- return this.props.maxModeOn
- ? null
- : (
- <div>
- <span
- className="send-v2__amount-max"
- onClick={this.onMaxClick}
- >
- {this.context.t('max')}
- </span>
- </div>
+ const { maxModeOn, buttonDataLoading, inError } = this.props
+
+ return (
+ <div className={'send-v2__amount-max'} onClick={buttonDataLoading || inError ? null : this.onMaxClick}>
+ <input type="checkbox" checked={maxModeOn} />
+ <div className={classnames('send-v2__amount-max__button', { 'send-v2__amount-max__button__disabled': buttonDataLoading || inError })}>
+ {this.context.t('max')}
+ </div>
+ </div>
)
}
-
}
diff --git a/ui/app/pages/send/send-content/send-amount-row/amount-max-button/amount-max-button.container.js b/ui/app/pages/send/send-content/send-amount-row/amount-max-button/amount-max-button.container.js
index cd48a105f..e444589a1 100644
--- a/ui/app/pages/send/send-content/send-amount-row/amount-max-button/amount-max-button.container.js
+++ b/ui/app/pages/send/send-content/send-amount-row/amount-max-button/amount-max-button.container.js
@@ -5,6 +5,7 @@ import {
getSendFromBalance,
getTokenBalance,
} from '../../../send.selectors.js'
+import { getBasicGasEstimateLoadingStatus } from '../../../../../selectors/custom-gas'
import { getMaxModeOn } from './amount-max-button.selectors.js'
import { calcMaxAmount } from './amount-max-button.utils.js'
import {
@@ -22,6 +23,7 @@ function mapStateToProps (state) {
return {
balance: getSendFromBalance(state),
+ buttonDataLoading: getBasicGasEstimateLoadingStatus(state),
gasTotal: getGasTotal(state),
maxModeOn: getMaxModeOn(state),
selectedToken: getSelectedToken(state),
@@ -35,6 +37,9 @@ function mapDispatchToProps (dispatch) {
dispatch(updateSendErrors({ amount: null }))
dispatch(updateSendAmount(calcMaxAmount(maxAmountDataObject)))
},
+ clearMaxAmount: () => {
+ dispatch(updateSendAmount('0'))
+ },
setMaxModeTo: bool => dispatch(setMaxModeTo(bool)),
}
}
diff --git a/ui/app/pages/send/send-content/send-amount-row/amount-max-button/tests/amount-max-button-component.test.js b/ui/app/pages/send/send-content/send-amount-row/amount-max-button/tests/amount-max-button-component.test.js
index a6cb29d4c..f986b26bb 100644
--- a/ui/app/pages/send/send-content/send-amount-row/amount-max-button/tests/amount-max-button-component.test.js
+++ b/ui/app/pages/send/send-content/send-amount-row/amount-max-button/tests/amount-max-button-component.test.js
@@ -65,7 +65,7 @@ describe('AmountMaxButton Component', function () {
assert(wrapper.exists('.send-v2__amount-max'))
})
- it('should call setMaxModeTo and setMaxAmount when the send-v2__amount-max div is clicked', () => {
+ it('should call setMaxModeTo and setMaxAmount when the checkbox is checked', () => {
const {
onClick,
} = wrapper.find('.send-v2__amount-max').props()
@@ -81,11 +81,6 @@ describe('AmountMaxButton Component', function () {
)
})
- it('should not render anything when maxModeOn is true', () => {
- wrapper.setProps({ maxModeOn: true })
- assert.ok(!wrapper.exists('.send-v2__amount-max'))
- })
-
it('should render the expected text when maxModeOn is false', () => {
wrapper.setProps({ maxModeOn: false })
assert.equal(wrapper.find('.send-v2__amount-max').text(), 'max_t')
diff --git a/ui/app/pages/send/send-content/send-amount-row/amount-max-button/tests/amount-max-button-container.test.js b/ui/app/pages/send/send-content/send-amount-row/amount-max-button/tests/amount-max-button-container.test.js
index a75ed5e8f..dcee8fda0 100644
--- a/ui/app/pages/send/send-content/send-amount-row/amount-max-button/tests/amount-max-button-container.test.js
+++ b/ui/app/pages/send/send-content/send-amount-row/amount-max-button/tests/amount-max-button-container.test.js
@@ -29,6 +29,7 @@ proxyquire('../amount-max-button.container.js', {
},
'./amount-max-button.selectors.js': { getMaxModeOn: (s) => `mockMaxModeOn:${s}` },
'./amount-max-button.utils.js': { calcMaxAmount: (mockObj) => mockObj.val + 1 },
+ '../../../../../selectors/custom-gas': { getBasicGasEstimateLoadingStatus: (s) => `mockButtonDataLoading:${s}`},
'../../../../../store/actions': actionSpies,
'../../../../../ducks/send/send.duck': duckActionSpies,
})
@@ -40,6 +41,7 @@ describe('amount-max-button container', () => {
it('should map the correct properties to props', () => {
assert.deepEqual(mapStateToProps('mockState'), {
balance: 'mockBalance:mockState',
+ buttonDataLoading: 'mockButtonDataLoading:mockState',
gasTotal: 'mockGasTotal:mockState',
maxModeOn: 'mockMaxModeOn:mockState',
selectedToken: 'mockSelectedToken:mockState',
diff --git a/ui/app/pages/send/send-content/send-amount-row/send-amount-row.component.js b/ui/app/pages/send/send-content/send-amount-row/send-amount-row.component.js
index c0241ea91..10e90c419 100644
--- a/ui/app/pages/send/send-content/send-amount-row/send-amount-row.component.js
+++ b/ui/app/pages/send/send-content/send-amount-row/send-amount-row.component.js
@@ -110,7 +110,7 @@ export default class SendAmountRow extends Component {
showError={inError}
errorType={'amount'}
>
- {!inError && gasTotal && <AmountMaxButton />}
+ {gasTotal && <AmountMaxButton inError={inError} />}
{ this.renderInput() }
</SendRowWrapper>
)
diff --git a/ui/app/pages/send/send-content/send-gas-row/send-gas-row.component.js b/ui/app/pages/send/send-content/send-gas-row/send-gas-row.component.js
index 1b850ac57..4c09ed564 100644
--- a/ui/app/pages/send/send-content/send-gas-row/send-gas-row.component.js
+++ b/ui/app/pages/send/send-content/send-gas-row/send-gas-row.component.js
@@ -8,14 +8,19 @@ import AdvancedGasInputs from '../../../../components/app/gas-customization/adva
export default class SendGasRow extends Component {
static propTypes = {
+ balance: PropTypes.string,
conversionRate: PropTypes.number,
convertedCurrency: PropTypes.string,
gasFeeError: PropTypes.bool,
gasLoadingError: PropTypes.bool,
gasTotal: PropTypes.string,
+ maxModeOn: PropTypes.bool,
showCustomizeGasModal: PropTypes.func,
+ selectedToken: PropTypes.object,
+ setAmountToMax: PropTypes.func,
setGasPrice: PropTypes.func,
setGasLimit: PropTypes.func,
+ tokenBalance: PropTypes.string,
gasPriceButtonGroupProps: PropTypes.object,
gasButtonGroupShown: PropTypes.bool,
advancedInlineGasShown: PropTypes.bool,
@@ -47,6 +52,23 @@ export default class SendGasRow extends Component {
</div>
}
+ setMaxAmount () {
+ const {
+ balance,
+ gasTotal,
+ selectedToken,
+ setAmountToMax,
+ tokenBalance,
+ } = this.props
+
+ setAmountToMax({
+ balance,
+ gasTotal,
+ selectedToken,
+ tokenBalance,
+ })
+ }
+
renderContent () {
const {
conversionRate,
@@ -57,6 +79,7 @@ export default class SendGasRow extends Component {
gasPriceButtonGroupProps,
gasButtonGroupShown,
advancedInlineGasShown,
+ maxModeOn,
resetGasButtons,
setGasPrice,
setGasLimit,
@@ -71,7 +94,7 @@ export default class SendGasRow extends Component {
className="gas-price-button-group--small"
showCheck={false}
{...gasPriceButtonGroupProps}
- handleGasPriceSelection={(...args) => {
+ handleGasPriceSelection={async (...args) => {
metricsEvent({
eventOpts: {
category: 'Transactions',
@@ -79,7 +102,10 @@ export default class SendGasRow extends Component {
name: 'Changed Gas Button',
},
})
- gasPriceButtonGroupProps.handleGasPriceSelection(...args)
+ await gasPriceButtonGroupProps.handleGasPriceSelection(...args)
+ if (maxModeOn) {
+ this.setMaxAmount()
+ }
}}
/>
{ this.renderAdvancedOptionsButton() }
@@ -89,7 +115,12 @@ export default class SendGasRow extends Component {
convertedCurrency={convertedCurrency}
gasLoadingError={gasLoadingError}
gasTotal={gasTotal}
- onReset={resetGasButtons}
+ onReset={() => {
+ resetGasButtons()
+ if (maxModeOn) {
+ this.setMaxAmount()
+ }
+ }}
onClick={() => showCustomizeGasModal()}
/>
const advancedGasInputs = <div>
diff --git a/ui/app/pages/send/send-content/send-gas-row/send-gas-row.container.js b/ui/app/pages/send/send-content/send-gas-row/send-gas-row.container.js
index c4daa98af..10eaa50b8 100644
--- a/ui/app/pages/send/send-content/send-gas-row/send-gas-row.container.js
+++ b/ui/app/pages/send/send-content/send-gas-row/send-gas-row.container.js
@@ -6,11 +6,17 @@ import {
getGasPrice,
getGasLimit,
getSendAmount,
+ getSendFromBalance,
+ getTokenBalance,
} from '../../send.selectors.js'
import {
+ getMaxModeOn,
+} from '../send-amount-row/amount-max-button/amount-max-button.selectors'
+import {
isBalanceSufficient,
calcGasTotal,
} from '../../send.utils.js'
+import { calcMaxAmount } from '../send-amount-row/amount-max-button/amount-max-button.utils'
import {
getBasicGasEstimateLoadingStatus,
getRenderableEstimateDataForSmallButtonsFromGWEI,
@@ -18,6 +24,7 @@ import {
} from '../../../../selectors/custom-gas'
import {
showGasButtonGroup,
+ updateSendErrors,
} from '../../../../ducks/send/send.duck'
import {
resetCustomData,
@@ -25,10 +32,11 @@ import {
setCustomGasLimit,
} from '../../../../ducks/gas/gas.duck'
import { getGasLoadingError, gasFeeIsInError, getGasButtonGroupShown } from './send-gas-row.selectors.js'
-import { showModal, setGasPrice, setGasLimit, setGasTotal } from '../../../../store/actions'
+import { showModal, setGasPrice, setGasLimit, setGasTotal, updateSendAmount } from '../../../../store/actions'
import { getAdvancedInlineGasShown, getCurrentEthBalance, getSelectedToken } from '../../../../selectors/selectors'
import SendGasRow from './send-gas-row.component'
+
export default connect(mapStateToProps, mapDispatchToProps, mergeProps)(SendGasRow)
function mapStateToProps (state) {
@@ -49,6 +57,7 @@ function mapStateToProps (state) {
})
return {
+ balance: getSendFromBalance(state),
conversionRate,
convertedCurrency: getCurrentCurrency(state),
gasTotal,
@@ -65,6 +74,9 @@ function mapStateToProps (state) {
gasPrice,
gasLimit,
insufficientBalance,
+ maxModeOn: getMaxModeOn(state),
+ selectedToken: getSelectedToken(state),
+ tokenBalance: getTokenBalance(state),
}
}
@@ -85,6 +97,10 @@ function mapDispatchToProps (dispatch) {
dispatch(setGasTotal(calcGasTotal(newLimit, gasPrice)))
}
},
+ setAmountToMax: maxAmountDataObject => {
+ dispatch(updateSendErrors({ amount: null }))
+ dispatch(updateSendAmount(calcMaxAmount(maxAmountDataObject)))
+ },
showGasButtonGroup: () => dispatch(showGasButtonGroup()),
resetCustomData: () => dispatch(resetCustomData()),
}
diff --git a/ui/app/pages/send/send-content/send-gas-row/tests/send-gas-row-container.test.js b/ui/app/pages/send/send-content/send-gas-row/tests/send-gas-row-container.test.js
index ddc6ea985..4acb310f8 100644
--- a/ui/app/pages/send/send-content/send-gas-row/tests/send-gas-row-container.test.js
+++ b/ui/app/pages/send/send-content/send-gas-row/tests/send-gas-row-container.test.js
@@ -44,6 +44,11 @@ proxyquire('../send-gas-row.container.js', {
getGasPrice: (s) => `mockGasPrice:${s}`,
getGasLimit: (s) => `mockGasLimit:${s}`,
getSendAmount: (s) => `mockSendAmount:${s}`,
+ getSendFromBalance: (s) => `mockBalance:${s}`,
+ getTokenBalance: (s) => `mockTokenBalance:${s}`,
+ },
+ '../send-amount-row/amount-max-button/amount-max-button.selectors': {
+ getMaxModeOn: (s) => `mockMaxModeOn:${s}`,
},
'../../send.utils.js': {
isBalanceSufficient: ({
@@ -75,6 +80,7 @@ describe('send-gas-row container', () => {
it('should map the correct properties to props', () => {
assert.deepEqual(mapStateToProps('mockState'), {
+ balance: 'mockBalance:mockState',
conversionRate: 'mockConversionRate:mockState',
convertedCurrency: 'mockConvertedCurrency:mockState',
gasTotal: 'mockGasTotal:mockState',
@@ -91,6 +97,9 @@ describe('send-gas-row container', () => {
gasLimit: 'mockGasLimit:mockState',
gasPrice: 'mockGasPrice:mockState',
insufficientBalance: false,
+ maxModeOn: 'mockMaxModeOn:mockState',
+ selectedToken: false,
+ tokenBalance: 'mockTokenBalance:mockState',
})
})
diff --git a/ui/app/pages/send/send-content/send-row-wrapper/send-row-error-message/send-row-error-message.component.js b/ui/app/pages/send/send-content/send-row-wrapper/send-row-error-message/send-row-error-message.component.js
index 61bc7bab7..0be01996a 100644
--- a/ui/app/pages/send/send-content/send-row-wrapper/send-row-error-message/send-row-error-message.component.js
+++ b/ui/app/pages/send/send-content/send-row-wrapper/send-row-error-message/send-row-error-message.component.js
@@ -1,5 +1,6 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
+import classnames from 'classnames'
export default class SendRowErrorMessage extends Component {
@@ -19,7 +20,7 @@ export default class SendRowErrorMessage extends Component {
return (
errorMessage
- ? <div className="send-v2__error">{this.context.t(errorMessage)}</div>
+ ? <div className={classnames('send-v2__error', {'send-v2__error-amount': errorType === 'amount'})}>{this.context.t(errorMessage)}</div>
: null
)
}
diff --git a/ui/app/pages/send/send-content/send-row-wrapper/send-row-wrapper.component.js b/ui/app/pages/send/send-content/send-row-wrapper/send-row-wrapper.component.js
index 94309bd96..075b86633 100644
--- a/ui/app/pages/send/send-content/send-row-wrapper/send-row-wrapper.component.js
+++ b/ui/app/pages/send/send-content/send-row-wrapper/send-row-wrapper.component.js
@@ -18,7 +18,7 @@ export default class SendRowWrapper extends Component {
t: PropTypes.func,
};
- render () {
+ renderAmountFormRow () {
const {
children,
errorType = '',
@@ -34,7 +34,39 @@ export default class SendRowWrapper extends Component {
<div className="send-v2__form-row">
<div className="send-v2__form-label">
{label}
- {showError && <SendRowErrorMessage errorType={errorType}/>}
+ {customLabelContent}
+ </div>
+ <div className="send-v2__form-field-container">
+ <div className="send-v2__form-field">
+ {formField}
+ </div>
+ <div>
+ {showError && <SendRowErrorMessage errorType={errorType} />}
+ {!showError && showWarning && <SendRowWarningMessage warningType={warningType} />}
+ </div>
+ </div>
+ </div>
+ )
+ }
+
+ renderFormRow () {
+ const {
+ children,
+ errorType = '',
+ label,
+ showError = false,
+ showWarning = false,
+ warningType = '',
+ } = this.props
+
+ const formField = Array.isArray(children) ? children[1] || children[0] : children
+ const customLabelContent = (Array.isArray(children) && children.length) > 1 ? children[0] : null
+
+ return (
+ <div className="send-v2__form-row">
+ <div className="send-v2__form-label">
+ {label}
+ {showError && <SendRowErrorMessage errorType={errorType} />}
{!showError && showWarning && <SendRowWarningMessage warningType={warningType} />}
{customLabelContent}
</div>
@@ -45,4 +77,14 @@ export default class SendRowWrapper extends Component {
)
}
+ render () {
+ const {
+ errorType = '',
+ } = this.props
+
+ return (
+ errorType === 'amount' ? this.renderAmountFormRow() : this.renderFormRow()
+ )
+ }
+
}
diff --git a/ui/app/pages/send/to-autocomplete/to-autocomplete.js b/ui/app/pages/send/to-autocomplete/to-autocomplete.js
index 328a5b62b..11f86acf3 100644
--- a/ui/app/pages/send/to-autocomplete/to-autocomplete.js
+++ b/ui/app/pages/send/to-autocomplete/to-autocomplete.js
@@ -1,6 +1,7 @@
const Component = require('react').Component
const PropTypes = require('prop-types')
const h = require('react-hyperscript')
+const copyToClipboard = require('copy-to-clipboard')
const inherits = require('util').inherits
const AccountListItem = require('../account-list-item/account-list-item.component').default
const connect = require('react-redux').connect
@@ -93,24 +94,34 @@ ToAutoComplete.prototype.componentDidUpdate = function (nextProps) {
ToAutoComplete.prototype.render = function () {
const {
to,
+ recipient,
dropdownOpen,
onChange,
inError,
qrScanner,
} = this.props
- return h('div.send-v2__to-autocomplete', {}, [
+ const isRecipientToDiff = recipient && recipient !== to
+
+ return h('div.send-v2__to-autocomplete', {style: {
+ borderColor: inError ? 'red' : null,
+ }}, [
h(`input.send-v2__to-autocomplete__input${qrScanner ? '.with-qr' : ''}`, {
placeholder: this.context.t('recipientAddress'),
className: inError ? `send-v2__error-border` : '',
- value: to,
+ value: recipient,
onChange: event => onChange(event.target.value),
onFocus: event => this.handleInputEvent(event),
- style: {
- borderColor: inError ? 'red' : null,
- },
}),
+ isRecipientToDiff && h(Tooltip, {title: this.context.t('copyToClipboard')},
+ h('div.send-v2__to-autocomplete__resolved', {
+ onClick: (event) => {
+ event.preventDefault()
+ event.stopPropagation()
+ copyToClipboard(to)
+ },
+ }, to)),
qrScanner && h(Tooltip, {
title: this.context.t('scanQrCode'),
position: 'bottom',