aboutsummaryrefslogtreecommitdiffstats
path: root/app
diff options
context:
space:
mode:
authorpldespaigne <pl.despaigne@gmail.com>2019-05-31 00:22:55 +0800
committerpldespaigne <pl.despaigne@gmail.com>2019-05-31 00:22:55 +0800
commit9a658ee53d1f75ce07c33581ac1189fa8c4fd173 (patch)
treeea92ef1971ffaa72c29bf16904906bc1841654c7 /app
parent9b87aaae1907eb04ca0a4055b5bb2c863e56aa39 (diff)
parent681f3f67b89b64fc837df1103198b641c7e7b2d6 (diff)
downloadtangerine-wallet-browser-9a658ee53d1f75ce07c33581ac1189fa8c4fd173.tar
tangerine-wallet-browser-9a658ee53d1f75ce07c33581ac1189fa8c4fd173.tar.gz
tangerine-wallet-browser-9a658ee53d1f75ce07c33581ac1189fa8c4fd173.tar.bz2
tangerine-wallet-browser-9a658ee53d1f75ce07c33581ac1189fa8c4fd173.tar.lz
tangerine-wallet-browser-9a658ee53d1f75ce07c33581ac1189fa8c4fd173.tar.xz
tangerine-wallet-browser-9a658ee53d1f75ce07c33581ac1189fa8c4fd173.tar.zst
tangerine-wallet-browser-9a658ee53d1f75ce07c33581ac1189fa8c4fd173.zip
merge
Diffstat (limited to 'app')
-rw-r--r--app/_locales/en/messages.json61
-rw-r--r--app/_locales/it/messages.json234
-rw-r--r--app/manifest.json2
-rw-r--r--app/scripts/contentscript.js214
-rw-r--r--app/scripts/controllers/app-state.js73
-rw-r--r--app/scripts/controllers/balance.js2
-rw-r--r--app/scripts/controllers/blacklist.js136
-rw-r--r--app/scripts/controllers/network/createBlockTracker.js19
-rw-r--r--app/scripts/controllers/network/createInfuraClient.js10
-rw-r--r--app/scripts/controllers/network/createJsonRpcClient.js6
-rw-r--r--app/scripts/controllers/network/createLocalhostClient.js6
-rw-r--r--app/scripts/controllers/network/enums.js6
-rw-r--r--app/scripts/controllers/network/network.js19
-rw-r--r--app/scripts/controllers/network/util.js5
-rw-r--r--app/scripts/controllers/preferences.js46
-rw-r--r--app/scripts/controllers/provider-approval.js124
-rw-r--r--app/scripts/controllers/recent-blocks.js3
-rw-r--r--app/scripts/controllers/shapeshift.js180
-rw-r--r--app/scripts/controllers/token-rates.js18
-rw-r--r--app/scripts/controllers/transactions/index.js59
-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/controllers/transactions/tx-gas-utils.js16
-rw-r--r--app/scripts/controllers/transactions/tx-state-manager.js4
-rw-r--r--app/scripts/controllers/user-actions.js17
-rw-r--r--app/scripts/createStandardProvider.js29
-rw-r--r--app/scripts/inpage.js128
-rw-r--r--app/scripts/lib/backend-metametrics.js26
-rw-r--r--app/scripts/lib/buy-eth-url.js4
-rw-r--r--app/scripts/lib/createDnodeRemoteGetter.js16
-rw-r--r--app/scripts/lib/createProviderMiddleware.js16
-rw-r--r--app/scripts/lib/message-manager.js2
-rw-r--r--app/scripts/lib/personal-message-manager.js2
-rw-r--r--app/scripts/metamask-controller.js184
-rw-r--r--app/scripts/migrations/024.js2
-rw-r--r--app/scripts/migrations/025.js2
-rw-r--r--app/scripts/platforms/extension.js14
37 files changed, 909 insertions, 990 deletions
diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json
index 154925d1a..41ba7ef7a 100644
--- a/app/_locales/en/messages.json
+++ b/app/_locales/en/messages.json
@@ -83,6 +83,9 @@
"address": {
"message": "Address"
},
+ "addNetwork": {
+ "message": "Add Network"
+ },
"advanced": {
"message": "Advanced"
},
@@ -139,6 +142,9 @@
"approved": {
"message": "Approved"
},
+ "asset": {
+ "message": "Asset"
+ },
"attemptingConnect": {
"message": "Attempting to connect to blockchain."
},
@@ -151,6 +157,12 @@
"attributions": {
"message": "Attributions"
},
+ "autoLogoutTimeLimit": {
+ "message": "Auto-Logout Timer (minutes)"
+ },
+ "autoLogoutTimeLimitDescription": {
+ "message": "Set the idle time in minutes before MetaMask will automatically log out"
+ },
"available": {
"message": "Available"
},
@@ -182,6 +194,13 @@
"message": "must be greater than or equal to $1 and less than or equal to $2.",
"description": "helper for inputting hex as decimal input"
},
+ "blockExplorerUrl": {
+ "message": "Block Explorer"
+ },
+ "blockExplorerView": {
+ "message": "View account at $1",
+ "description": "$1 replaced by URL for custom block explorer"
+ },
"blockiesIdenticon": {
"message": "Use Blockies Identicon"
},
@@ -221,6 +240,9 @@
"ok": {
"message": "Ok"
},
+ "optionalBlockExplorerUrl": {
+ "message": "Block Explorer URL (optional)"
+ },
"cancel": {
"message": "Cancel"
},
@@ -236,6 +258,9 @@
"cancelN": {
"message": "Cancel all $1 transactions"
},
+ "chainId": {
+ "message": "Chain ID"
+ },
"classicInterface": {
"message": "Use classic interface"
},
@@ -308,6 +333,12 @@
"connectingToRinkeby": {
"message": "Connecting to Rinkeby Test Network"
},
+ "connectingToLocalhost": {
+ "message": "Connecting to Localhost 8545"
+ },
+ "connectingToGoerli": {
+ "message": "Connecting to Goerli Test Network"
+ },
"connectingToUnknown": {
"message": "Connecting to Unknown Network"
},
@@ -487,6 +518,9 @@
"edit": {
"message": "Edit"
},
+ "editNetwork": {
+ "message": "Edit Network"
+ },
"editAccountName": {
"message": "Edit Account Name"
},
@@ -515,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."
@@ -919,9 +953,15 @@
"negativeETH": {
"message": "Can not send negative amounts of ETH."
},
+ "networkName": {
+ "message": "Network Name"
+ },
"networks": {
"message": "Networks"
},
+ "networkSettingsDescription": {
+ "message": "Add and edit custom RPC networks"
+ },
"nevermind": {
"message": "Nevermind"
},
@@ -962,7 +1002,7 @@
"protectYourKeysMessage2": {
"message": "Keep your phrase safe. If you see something fishy, or you’re uncertain about a website, email support@metamask.io"
},
- "rpcURL": {
+ "rpcUrl": {
"message": "New RPC URL"
},
"showAdvancedOptions": {
@@ -1209,7 +1249,7 @@
"message": "Revert"
},
"remove": {
- "message": "remove"
+ "message": "Remove"
},
"removeAccount": {
"message": "Remove account"
@@ -1226,6 +1266,9 @@
"ropsten": {
"message": "Ropsten Test Network"
},
+ "goerli": {
+ "message": "Goerli Test Network"
+ },
"rpc": {
"message": "Custom RPC"
},
@@ -1342,6 +1385,9 @@
"selectAnAccountHelp": {
"message": "Select the account to view in MetaMask"
},
+ "selectAnAsset": {
+ "message": "Select an Asset"
+ },
"selectAHigherGasFee": {
"message": "Select a higher gas fee to accelerate the processing of your transaction.*"
},
@@ -1471,6 +1517,9 @@
"supportCenter": {
"message": "Visit our Support Center"
},
+ "symbol": {
+ "message": "Symbol"
+ },
"symbolBetweenZeroTwelve": {
"message": "Symbol must be between 0 and 12 characters."
},
@@ -1693,9 +1742,15 @@
"viewAccount": {
"message": "View Account"
},
+ "viewOnCustomBlockExplorer": {
+ "message": "View at $1"
+ },
"viewOnEtherscan": {
"message": "View on Etherscan"
},
+ "viewNetworkInfo": {
+ "message": "View Network Info"
+ },
"visitWebSite": {
"message": "Visit our web site"
},
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/manifest.json b/app/manifest.json
index 941842636..8471fffac 100644
--- a/app/manifest.json
+++ b/app/manifest.json
@@ -1,7 +1,7 @@
{
"name": "__MSG_appName__",
"short_name": "__MSG_appName__",
- "version": "6.3.1",
+ "version": "6.5.3",
"manifest_version": 2,
"author": "https://metamask.io",
"description": "__MSG_appDescription__",
diff --git a/app/scripts/contentscript.js b/app/scripts/contentscript.js
index 68b6117e5..0c55ae39f 100644
--- a/app/scripts/contentscript.js
+++ b/app/scripts/contentscript.js
@@ -1,18 +1,17 @@
const fs = require('fs')
const path = require('path')
const pump = require('pump')
+const log = require('loglevel')
+const Dnode = require('dnode')
const querystring = require('querystring')
const LocalMessageDuplexStream = require('post-message-stream')
-const PongStream = require('ping-pong-stream/pong')
const ObjectMultiplex = require('obj-multiplex')
const extension = require('extensionizer')
const PortStream = require('extension-port-stream')
-const {Transform: TransformStream} = require('stream')
const inpageContent = fs.readFileSync(path.join(__dirname, '..', '..', 'dist', 'chrome', 'inpage.js')).toString()
const inpageSuffix = '//# sourceURL=' + extension.extension.getURL('inpage.js') + '\n'
const inpageBundle = inpageContent + inpageSuffix
-let isEnabled = false
// Eventually this streaming injection could be replaced with:
// https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Language_Bindings/Components.utils.exportFunction
@@ -23,9 +22,7 @@ let isEnabled = false
if (shouldInjectWeb3()) {
injectScript(inpageBundle)
- setupStreams()
- listenForProviderRequest()
- checkPrivacyMode()
+ start()
}
/**
@@ -47,148 +44,107 @@ function injectScript (content) {
}
/**
+ * Sets up the stream communication and submits site metadata
+ *
+ */
+async function start () {
+ await setupStreams()
+ await domIsReady()
+}
+
+/**
* Sets up two-way communication streams between the
- * browser extension and local per-page browser context
+ * browser extension and local per-page browser context.
+ *
*/
-function setupStreams () {
- // setup communication to page and plugin
+async function setupStreams () {
+ // the transport-specific streams for communication between inpage and background
const pageStream = new LocalMessageDuplexStream({
name: 'contentscript',
target: 'inpage',
})
- const pluginPort = extension.runtime.connect({ name: 'contentscript' })
- const pluginStream = new PortStream(pluginPort)
+ const extensionPort = extension.runtime.connect({ name: 'contentscript' })
+ const extensionStream = new PortStream(extensionPort)
- // Filter out selectedAddress until this origin is enabled
- const approvalTransform = new TransformStream({
- objectMode: true,
- transform: (data, _, done) => {
- if (typeof data === 'object' && data.name && data.name === 'publicConfig' && !isEnabled) {
- data.data.selectedAddress = undefined
- }
- done(null, { ...data })
- },
- })
+ // create and connect channel muxers
+ // so we can handle the channels individually
+ const pageMux = new ObjectMultiplex()
+ pageMux.setMaxListeners(25)
+ const extensionMux = new ObjectMultiplex()
+ extensionMux.setMaxListeners(25)
- // forward communication plugin->inpage
pump(
+ pageMux,
pageStream,
- pluginStream,
- approvalTransform,
- pageStream,
- (err) => logStreamDisconnectWarning('MetaMask Contentscript Forwarding', err)
+ pageMux,
+ (err) => logStreamDisconnectWarning('MetaMask Inpage Multiplex', err)
)
-
- // setup local multistream channels
- const mux = new ObjectMultiplex()
- mux.setMaxListeners(25)
-
pump(
- mux,
- pageStream,
- mux,
- (err) => logStreamDisconnectWarning('MetaMask Inpage', err)
- )
- pump(
- mux,
- pluginStream,
- mux,
- (err) => logStreamDisconnectWarning('MetaMask Background', err)
+ extensionMux,
+ extensionStream,
+ extensionMux,
+ (err) => logStreamDisconnectWarning('MetaMask Background Multiplex', err)
)
- // connect ping stream
- const pongStream = new PongStream({ objectMode: true })
- pump(
- mux,
- pongStream,
- mux,
- (err) => logStreamDisconnectWarning('MetaMask PingPongStream', err)
- )
+ // forward communication across inpage-background for these channels only
+ forwardTrafficBetweenMuxers('provider', pageMux, extensionMux)
+ forwardTrafficBetweenMuxers('publicConfig', pageMux, extensionMux)
- // connect phishing warning stream
- const phishingStream = mux.createStream('phishing')
+ // connect "phishing" channel to warning system
+ const phishingStream = extensionMux.createStream('phishing')
phishingStream.once('data', redirectToPhishingWarning)
- // ignore unused channels (handled by background, inpage)
- mux.ignoreStream('provider')
- mux.ignoreStream('publicConfig')
+ // connect "publicApi" channel to submit page metadata
+ const publicApiStream = extensionMux.createStream('publicApi')
+ const background = await setupPublicApi(publicApiStream)
+
+ return { background }
}
-/**
- * Establishes listeners for requests to fully-enable the provider from the dapp context
- * and for full-provider approvals and rejections from the background script context. Dapps
- * should not post messages directly and should instead call provider.enable(), which
- * handles posting these messages internally.
- */
-function listenForProviderRequest () {
- window.addEventListener('message', ({ source, data }) => {
- if (source !== window || !data || !data.type) { return }
- switch (data.type) {
- case 'ETHEREUM_ENABLE_PROVIDER':
- extension.runtime.sendMessage({
- action: 'init-provider-request',
- force: data.force,
- origin: source.location.hostname,
- siteImage: getSiteIcon(source),
- siteTitle: getSiteName(source),
- })
- break
- case 'ETHEREUM_IS_APPROVED':
- extension.runtime.sendMessage({
- action: 'init-is-approved',
- origin: source.location.hostname,
- })
- break
- case 'METAMASK_IS_UNLOCKED':
- extension.runtime.sendMessage({
- action: 'init-is-unlocked',
- })
- break
- }
- })
+function forwardTrafficBetweenMuxers (channelName, muxA, muxB) {
+ const channelA = muxA.createStream(channelName)
+ const channelB = muxB.createStream(channelName)
+ pump(
+ channelA,
+ channelB,
+ channelA,
+ (err) => logStreamDisconnectWarning(`MetaMask muxed traffic for channel "${channelName}" failed.`, err)
+ )
+}
- extension.runtime.onMessage.addListener(({ action = '', isApproved, caching, isUnlocked, selectedAddress }) => {
- switch (action) {
- case 'approve-provider-request':
- isEnabled = true
- window.postMessage({ type: 'ethereumprovider', selectedAddress }, '*')
- break
- case 'approve-legacy-provider-request':
- isEnabled = true
- window.postMessage({ type: 'ethereumproviderlegacy', selectedAddress }, '*')
- break
- case 'reject-provider-request':
- window.postMessage({ type: 'ethereumprovider', error: 'User denied account authorization' }, '*')
- break
- case 'answer-is-approved':
- window.postMessage({ type: 'ethereumisapproved', isApproved, caching }, '*')
- break
- case 'answer-is-unlocked':
- window.postMessage({ type: 'metamaskisunlocked', isUnlocked }, '*')
- break
- case 'metamask-set-locked':
- isEnabled = false
- window.postMessage({ type: 'metamasksetlocked' }, '*')
- break
- case 'ethereum-ping-success':
- window.postMessage({ type: 'ethereumpingsuccess' }, '*')
- break
- case 'ethereum-ping-error':
- window.postMessage({ type: 'ethereumpingerror' }, '*')
+async function setupPublicApi (outStream) {
+ const api = {
+ getSiteMetadata: (cb) => cb(null, getSiteMetadata()),
+ }
+ const dnode = Dnode(api)
+ pump(
+ outStream,
+ dnode,
+ outStream,
+ (err) => {
+ // report any error
+ if (err) log.error(err)
}
- })
+ )
+ const background = await new Promise(resolve => dnode.once('remote', resolve))
+ return background
}
/**
- * Checks if MetaMask is currently operating in "privacy mode", meaning
- * dapps must call ethereum.enable in order to access user accounts
+ * Gets site metadata and returns it
+ *
*/
-function checkPrivacyMode () {
- extension.runtime.sendMessage({ action: 'init-privacy-request' })
+function getSiteMetadata () {
+ // get metadata
+ const metadata = {
+ name: getSiteName(window),
+ icon: getSiteIcon(window),
+ }
+ return metadata
}
/**
- * Error handler for page to plugin stream disconnections
+ * Error handler for page to extension stream disconnections
*
* @param {string} remoteLabel Remote stream name
* @param {Error} err Stream connection error
@@ -275,6 +231,7 @@ function blacklistedDomainCheck () {
'harbourair.com',
'ani.gamer.com.tw',
'blueskybooking.com',
+ 'sharefile.com',
]
const currentUrl = window.location.href
let currentRegex
@@ -300,6 +257,10 @@ function redirectToPhishingWarning () {
})}`
}
+
+/**
+ * Extracts a name for the site from the DOM
+ */
function getSiteName (window) {
const document = window.document
const siteName = document.querySelector('head > meta[property="og:site_name"]')
@@ -315,6 +276,9 @@ function getSiteName (window) {
return document.title
}
+/**
+ * Extracts an icon for the site from the DOM
+ */
function getSiteIcon (window) {
const document = window.document
@@ -332,3 +296,13 @@ function getSiteIcon (window) {
return null
}
+
+/**
+ * Returns a promise that resolves when the DOM is loaded (does not wait for images to load)
+ */
+async function domIsReady () {
+ // already loaded
+ if (['interactive', 'complete'].includes(document.readyState)) return
+ // wait for load
+ await new Promise(resolve => window.addEventListener('DOMContentLoaded', resolve, { once: true }))
+}
diff --git a/app/scripts/controllers/app-state.js b/app/scripts/controllers/app-state.js
new file mode 100644
index 000000000..9533fd458
--- /dev/null
+++ b/app/scripts/controllers/app-state.js
@@ -0,0 +1,73 @@
+const ObservableStore = require('obs-store')
+const extend = require('xtend')
+
+class AppStateController {
+ /**
+ * @constructor
+ * @param opts
+ */
+ constructor (opts = {}) {
+ const {initState, onInactiveTimeout, preferencesStore} = opts
+ const {preferences} = preferencesStore.getState()
+
+ this.onInactiveTimeout = onInactiveTimeout || (() => {})
+ this.store = new ObservableStore(extend({
+ timeoutMinutes: 0,
+ }, initState))
+ this.timer = null
+
+ preferencesStore.subscribe(state => {
+ this._setInactiveTimeout(state.preferences.autoLogoutTimeLimit)
+ })
+
+ this._setInactiveTimeout(preferences.autoLogoutTimeLimit)
+ }
+
+ /**
+ * Sets the last active time to the current time
+ * @return {void}
+ */
+ setLastActiveTime () {
+ this._resetTimer()
+ }
+
+ /**
+ * Sets the inactive timeout for the app
+ * @param {number} timeoutMinutes the inactive timeout in minutes
+ * @return {void}
+ * @private
+ */
+ _setInactiveTimeout (timeoutMinutes) {
+ this.store.putState({
+ timeoutMinutes,
+ })
+
+ this._resetTimer()
+ }
+
+ /**
+ * Resets the internal inactive timer
+ *
+ * If the {@code timeoutMinutes} state is falsy (i.e., zero) then a new
+ * timer will not be created.
+ *
+ * @return {void}
+ * @private
+ */
+ _resetTimer () {
+ const {timeoutMinutes} = this.store.getState()
+
+ if (this.timer) {
+ clearTimeout(this.timer)
+ }
+
+ if (!timeoutMinutes) {
+ return
+ }
+
+ this.timer = setTimeout(() => this.onInactiveTimeout(), timeoutMinutes * 60 * 1000)
+ }
+}
+
+module.exports = AppStateController
+
diff --git a/app/scripts/controllers/balance.js b/app/scripts/controllers/balance.js
index 465751e61..b227d5d0a 100644
--- a/app/scripts/controllers/balance.js
+++ b/app/scripts/controllers/balance.js
@@ -68,7 +68,7 @@ class BalanceController {
_registerUpdates () {
const update = this.updateBalance.bind(this)
- this.txController.on('tx:status-update', (txId, status) => {
+ this.txController.on('tx:status-update', (_, status) => {
switch (status) {
case 'submitted':
case 'confirmed':
diff --git a/app/scripts/controllers/blacklist.js b/app/scripts/controllers/blacklist.js
deleted file mode 100644
index e55b09d03..000000000
--- a/app/scripts/controllers/blacklist.js
+++ /dev/null
@@ -1,136 +0,0 @@
-const ObservableStore = require('obs-store')
-const extend = require('xtend')
-const PhishingDetector = require('eth-phishing-detect/src/detector')
-const log = require('loglevel')
-
-// compute phishing lists
-const PHISHING_DETECTION_CONFIG = require('eth-phishing-detect/src/config.json')
-// every four minutes
-const POLLING_INTERVAL = 4 * 60 * 1000
-
-class BlacklistController {
-
- /**
- * Responsible for polling for and storing an up to date 'eth-phishing-detect' config.json file, while
- * exposing a method that can check whether a given url is a phishing attempt. The 'eth-phishing-detect'
- * config.json file contains a fuzzylist, whitelist and blacklist.
- *
- *
- * @typedef {Object} BlacklistController
- * @param {object} opts Overrides the defaults for the initial state of this.store
- * @property {object} store The the store of the current phishing config
- * @property {object} store.phishing Contains fuzzylist, whitelist and blacklist arrays. @see
- * {@link https://github.com/MetaMask/eth-phishing-detect/blob/master/src/config.json}
- * @property {object} _phishingDetector The PhishingDetector instantiated by passing store.phishing to
- * PhishingDetector.
- * @property {object} _phishingUpdateIntervalRef Id of the interval created to periodically update the blacklist
- *
- */
- constructor (opts = {}) {
- const initState = extend({
- phishing: PHISHING_DETECTION_CONFIG,
- whitelist: [],
- }, opts.initState)
- this.store = new ObservableStore(initState)
- // phishing detector
- this._phishingDetector = null
- this._setupPhishingDetector(initState.phishing)
- // polling references
- this._phishingUpdateIntervalRef = null
- }
-
- /**
- * Adds the given hostname to the runtime whitelist
- * @param {string} hostname the hostname to whitelist
- */
- whitelistDomain (hostname) {
- if (!hostname) {
- return
- }
-
- const { whitelist } = this.store.getState()
- this.store.updateState({
- whitelist: [...new Set([hostname, ...whitelist])],
- })
- }
-
- /**
- * Given a url, returns the result of checking if that url is in the store.phishing blacklist
- *
- * @param {string} hostname The hostname portion of a url; the one that will be checked against the white and
- * blacklists of store.phishing
- * @returns {boolean} Whether or not the passed hostname is on our phishing blacklist
- *
- */
- checkForPhishing (hostname) {
- if (!hostname) return false
-
- const { whitelist } = this.store.getState()
- if (whitelist.some((e) => e === hostname)) {
- return false
- }
-
- const { result } = this._phishingDetector.check(hostname)
- return result
- }
-
- /**
- * Queries `https://api.infura.io/v2/blacklist` for an updated blacklist config. This is passed to this._phishingDetector
- * to update our phishing detector instance, and is updated in the store. The new phishing config is returned
- *
- *
- * @returns {Promise<object>} Promises the updated blacklist config for the phishingDetector
- *
- */
- async updatePhishingList () {
- // make request
- let response
- try {
- response = await fetch('https://api.infura.io/v2/blacklist')
- } catch (err) {
- log.error(new Error(`BlacklistController - failed to fetch blacklist:\n${err.stack}`))
- return
- }
- // parse response
- let rawResponse
- let phishing
- try {
- const rawResponse = await response.text()
- phishing = JSON.parse(rawResponse)
- } catch (err) {
- log.error(new Error(`BlacklistController - failed to parse blacklist:\n${rawResponse}`))
- return
- }
- // update current blacklist
- this.store.updateState({ phishing })
- this._setupPhishingDetector(phishing)
- return phishing
- }
-
- /**
- * Initiates the updating of the local blacklist at a set interval. The update is done via this.updatePhishingList().
- * Also, this method store a reference to that interval at this._phishingUpdateIntervalRef
- *
- */
- scheduleUpdates () {
- if (this._phishingUpdateIntervalRef) return
- this.updatePhishingList()
- this._phishingUpdateIntervalRef = setInterval(() => {
- this.updatePhishingList()
- }, POLLING_INTERVAL)
- }
-
- /**
- * Sets this._phishingDetector to a new PhishingDetector instance.
- * @see {@link https://github.com/MetaMask/eth-phishing-detect}
- *
- * @private
- * @param {object} config A config object like that found at {@link https://github.com/MetaMask/eth-phishing-detect/blob/master/src/config.json}
- *
- */
- _setupPhishingDetector (config) {
- this._phishingDetector = new PhishingDetector(config)
- }
-}
-
-module.exports = BlacklistController
diff --git a/app/scripts/controllers/network/createBlockTracker.js b/app/scripts/controllers/network/createBlockTracker.js
deleted file mode 100644
index 6573b18a1..000000000
--- a/app/scripts/controllers/network/createBlockTracker.js
+++ /dev/null
@@ -1,19 +0,0 @@
-const BlockTracker = require('eth-block-tracker')
-
-/**
- * Creates a block tracker that sends platform events on success and failure
- */
-module.exports = function createBlockTracker (args, platform) {
- const blockTracker = new BlockTracker(args)
- blockTracker.on('latest', () => {
- if (platform && platform.sendMessage) {
- platform.sendMessage({ action: 'ethereum-ping-success' })
- }
- })
- blockTracker.on('error', () => {
- if (platform && platform.sendMessage) {
- platform.sendMessage({ action: 'ethereum-ping-error' })
- }
- })
- return blockTracker
-}
diff --git a/app/scripts/controllers/network/createInfuraClient.js b/app/scripts/controllers/network/createInfuraClient.js
index 884b94db3..0a6e9ecb0 100644
--- a/app/scripts/controllers/network/createInfuraClient.js
+++ b/app/scripts/controllers/network/createInfuraClient.js
@@ -7,14 +7,14 @@ const createInflightMiddleware = require('eth-json-rpc-middleware/inflight-cache
const createBlockTrackerInspectorMiddleware = require('eth-json-rpc-middleware/block-tracker-inspector')
const providerFromMiddleware = require('eth-json-rpc-middleware/providerFromMiddleware')
const createInfuraMiddleware = require('eth-json-rpc-infura')
-const createBlockTracker = require('./createBlockTracker')
+const BlockTracker = require('eth-block-tracker')
module.exports = createInfuraClient
-function createInfuraClient ({ network, platform }) {
+function createInfuraClient ({ network }) {
const infuraMiddleware = createInfuraMiddleware({ network, maxAttempts: 5, source: 'metamask' })
const infuraProvider = providerFromMiddleware(infuraMiddleware)
- const blockTracker = createBlockTracker({ provider: infuraProvider }, platform)
+ const blockTracker = new BlockTracker({ provider: infuraProvider })
const networkMiddleware = mergeMiddleware([
createNetworkAndChainIdMiddleware({ network }),
@@ -49,6 +49,10 @@ function createNetworkAndChainIdMiddleware ({ network }) {
netId = '42'
chainId = '0x2a'
break
+ case 'goerli':
+ netId = '5'
+ chainId = '0x05'
+ break
default:
throw new Error(`createInfuraClient - unknown network "${network}"`)
}
diff --git a/app/scripts/controllers/network/createJsonRpcClient.js b/app/scripts/controllers/network/createJsonRpcClient.js
index 369dcd299..a8cbf2aaf 100644
--- a/app/scripts/controllers/network/createJsonRpcClient.js
+++ b/app/scripts/controllers/network/createJsonRpcClient.js
@@ -5,14 +5,14 @@ const createBlockCacheMiddleware = require('eth-json-rpc-middleware/block-cache'
const createInflightMiddleware = require('eth-json-rpc-middleware/inflight-cache')
const createBlockTrackerInspectorMiddleware = require('eth-json-rpc-middleware/block-tracker-inspector')
const providerFromMiddleware = require('eth-json-rpc-middleware/providerFromMiddleware')
-const createBlockTracker = require('./createBlockTracker')
+const BlockTracker = require('eth-block-tracker')
module.exports = createJsonRpcClient
-function createJsonRpcClient ({ rpcUrl, platform }) {
+function createJsonRpcClient ({ rpcUrl }) {
const fetchMiddleware = createFetchMiddleware({ rpcUrl })
const blockProvider = providerFromMiddleware(fetchMiddleware)
- const blockTracker = createBlockTracker({ provider: blockProvider }, platform)
+ const blockTracker = new BlockTracker({ provider: blockProvider })
const networkMiddleware = mergeMiddleware([
createBlockRefRewriteMiddleware({ blockTracker }),
diff --git a/app/scripts/controllers/network/createLocalhostClient.js b/app/scripts/controllers/network/createLocalhostClient.js
index 36593dc70..09b1d3c1c 100644
--- a/app/scripts/controllers/network/createLocalhostClient.js
+++ b/app/scripts/controllers/network/createLocalhostClient.js
@@ -3,14 +3,14 @@ const createFetchMiddleware = require('eth-json-rpc-middleware/fetch')
const createBlockRefRewriteMiddleware = require('eth-json-rpc-middleware/block-ref-rewrite')
const createBlockTrackerInspectorMiddleware = require('eth-json-rpc-middleware/block-tracker-inspector')
const providerFromMiddleware = require('eth-json-rpc-middleware/providerFromMiddleware')
-const createBlockTracker = require('./createBlockTracker')
+const BlockTracker = require('eth-block-tracker')
module.exports = createLocalhostClient
-function createLocalhostClient ({ platform }) {
+function createLocalhostClient () {
const fetchMiddleware = createFetchMiddleware({ rpcUrl: 'http://localhost:8545/' })
const blockProvider = providerFromMiddleware(fetchMiddleware)
- const blockTracker = createBlockTracker({ provider: blockProvider, pollingInterval: 1000 }, platform)
+ const blockTracker = new BlockTracker({ provider: blockProvider, pollingInterval: 1000 })
const networkMiddleware = mergeMiddleware([
createBlockRefRewriteMiddleware({ blockTracker }),
diff --git a/app/scripts/controllers/network/enums.js b/app/scripts/controllers/network/enums.js
index 3190eb37c..2f7025392 100644
--- a/app/scripts/controllers/network/enums.js
+++ b/app/scripts/controllers/network/enums.js
@@ -3,16 +3,19 @@ const RINKEBY = 'rinkeby'
const KOVAN = 'kovan'
const MAINNET = 'mainnet'
const LOCALHOST = 'localhost'
+const GOERLI = 'goerli'
const MAINNET_CODE = 1
const ROPSTEN_CODE = 3
const RINKEYBY_CODE = 4
const KOVAN_CODE = 42
+const GOERLI_CODE = 5
const ROPSTEN_DISPLAY_NAME = 'Ropsten'
const RINKEBY_DISPLAY_NAME = 'Rinkeby'
const KOVAN_DISPLAY_NAME = 'Kovan'
const MAINNET_DISPLAY_NAME = 'Main Ethereum Network'
+const GOERLI_DISPLAY_NAME = 'Goerli'
module.exports = {
ROPSTEN,
@@ -20,12 +23,15 @@ module.exports = {
KOVAN,
MAINNET,
LOCALHOST,
+ GOERLI,
MAINNET_CODE,
ROPSTEN_CODE,
RINKEYBY_CODE,
KOVAN_CODE,
+ GOERLI_CODE,
ROPSTEN_DISPLAY_NAME,
RINKEBY_DISPLAY_NAME,
KOVAN_DISPLAY_NAME,
MAINNET_DISPLAY_NAME,
+ GOERLI_DISPLAY_NAME,
}
diff --git a/app/scripts/controllers/network/network.js b/app/scripts/controllers/network/network.js
index 47432c1e2..2c68e4378 100644
--- a/app/scripts/controllers/network/network.js
+++ b/app/scripts/controllers/network/network.js
@@ -20,8 +20,9 @@ const {
KOVAN,
MAINNET,
LOCALHOST,
+ GOERLI,
} = require('./enums')
-const INFURA_PROVIDER_TYPES = [ROPSTEN, RINKEBY, KOVAN, MAINNET]
+const INFURA_PROVIDER_TYPES = [ROPSTEN, RINKEBY, KOVAN, MAINNET, GOERLI]
const env = process.env.METAMASK_ENV
const METAMASK_DEBUG = process.env.METAMASK_DEBUG
@@ -45,9 +46,8 @@ const defaultNetworkConfig = {
module.exports = class NetworkController extends EventEmitter {
- constructor (opts = {}, platform) {
+ constructor (opts = {}) {
super()
- this.platform = platform
// parse options
const providerConfig = opts.provider || defaultProviderConfig
@@ -129,21 +129,22 @@ module.exports = class NetworkController extends EventEmitter {
})
}
- setRpcTarget (rpcTarget, chainId, ticker = 'ETH', nickname = '') {
+ setRpcTarget (rpcTarget, chainId, ticker = 'ETH', nickname = '', rpcPrefs) {
const providerConfig = {
type: 'rpc',
rpcTarget,
chainId,
ticker,
nickname,
+ rpcPrefs,
}
this.providerConfig = providerConfig
}
- async setProviderType (type) {
+ async setProviderType (type, rpcTarget = '', ticker = 'ETH', nickname = '') {
assert.notEqual(type, 'rpc', `NetworkController - cannot call "setProviderType" with type 'rpc'. use "setRpcTarget"`)
assert(INFURA_PROVIDER_TYPES.includes(type) || type === LOCALHOST, `NetworkController - Unknown rpc type "${type}"`)
- const providerConfig = { type }
+ const providerConfig = { type, rpcTarget, ticker, nickname }
this.providerConfig = providerConfig
}
@@ -189,7 +190,7 @@ module.exports = class NetworkController extends EventEmitter {
_configureInfuraProvider ({ type }) {
log.info('NetworkController - configureInfuraProvider', type)
- const networkClient = createInfuraClient({ network: type, platform: this.platform })
+ const networkClient = createInfuraClient({ network: type })
this._setNetworkClient(networkClient)
// setup networkConfig
var settings = {
@@ -200,13 +201,13 @@ module.exports = class NetworkController extends EventEmitter {
_configureLocalhostProvider () {
log.info('NetworkController - configureLocalhostProvider')
- const networkClient = createLocalhostClient({ platform: this.platform })
+ const networkClient = createLocalhostClient()
this._setNetworkClient(networkClient)
}
_configureStandardProvider ({ rpcUrl, chainId, ticker, nickname }) {
log.info('NetworkController - configureStandardProvider', rpcUrl)
- const networkClient = createJsonRpcClient({ rpcUrl, platform: this.platform })
+ const networkClient = createJsonRpcClient({ rpcUrl })
// hack to add a 'rpc' network with chainId
networks.networkList['rpc'] = {
chainId: chainId,
diff --git a/app/scripts/controllers/network/util.js b/app/scripts/controllers/network/util.js
index 261dae721..b0afccd7e 100644
--- a/app/scripts/controllers/network/util.js
+++ b/app/scripts/controllers/network/util.js
@@ -3,13 +3,16 @@ const {
RINKEBY,
KOVAN,
MAINNET,
+ GOERLI,
ROPSTEN_CODE,
RINKEYBY_CODE,
KOVAN_CODE,
+ GOERLI_CODE,
ROPSTEN_DISPLAY_NAME,
RINKEBY_DISPLAY_NAME,
KOVAN_DISPLAY_NAME,
MAINNET_DISPLAY_NAME,
+ GOERLI_DISPLAY_NAME,
} = require('./enums')
const networkToNameMap = {
@@ -17,9 +20,11 @@ const networkToNameMap = {
[RINKEBY]: RINKEBY_DISPLAY_NAME,
[KOVAN]: KOVAN_DISPLAY_NAME,
[MAINNET]: MAINNET_DISPLAY_NAME,
+ [GOERLI]: GOERLI_DISPLAY_NAME,
[ROPSTEN_CODE]: ROPSTEN_DISPLAY_NAME,
[RINKEYBY_CODE]: RINKEBY_DISPLAY_NAME,
[KOVAN_CODE]: KOVAN_DISPLAY_NAME,
+ [GOERLI_CODE]: GOERLI_DISPLAY_NAME,
}
const getNetworkDisplayName = key => networkToNameMap[key]
diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js
index 737411890..acf952bb1 100644
--- a/app/scripts/controllers/preferences.js
+++ b/app/scripts/controllers/preferences.js
@@ -117,6 +117,14 @@ class PreferencesController {
return metaMetricsId
}
+ getMetaMetricsId () {
+ return this.store.getState().metaMetricsId
+ }
+
+ getParticipateInMetaMetrics () {
+ return this.store.getState().participateInMetaMetrics
+ }
+
setMetaMetricsSendCount (val) {
this.store.updateState({ metaMetricsSendCount: val })
}
@@ -331,7 +339,7 @@ class PreferencesController {
}
removeSuggestedTokens () {
- return new Promise((resolve, reject) => {
+ return new Promise((resolve) => {
this.store.updateState({ suggestedTokens: {} })
resolve({})
})
@@ -388,7 +396,7 @@ class PreferencesController {
const newEntry = { address, symbol, decimals }
const tokens = this.store.getState().tokens
const assetImages = this.getAssetImages()
- const previousEntry = tokens.find((token, index) => {
+ const previousEntry = tokens.find((token) => {
return token.address === address
})
const previousIndex = tokens.indexOf(previousEntry)
@@ -453,7 +461,7 @@ class PreferencesController {
*
*/
setCurrentAccountTab (currentAccountTab) {
- return new Promise((resolve, reject) => {
+ return new Promise((resolve) => {
this.store.updateState({ currentAccountTab })
resolve()
})
@@ -480,8 +488,8 @@ class PreferencesController {
rpcList[index] = updatedRpc
this.store.updateState({ frequentRpcListDetail: rpcList })
} else {
- const { rpcUrl, chainId, ticker, nickname } = newRpcDetails
- return this.addToFrequentRpcList(rpcUrl, chainId, ticker, nickname)
+ const { rpcUrl, chainId, ticker, nickname, rpcPrefs = {} } = newRpcDetails
+ return this.addToFrequentRpcList(rpcUrl, chainId, ticker, nickname, rpcPrefs)
}
return Promise.resolve(rpcList)
}
@@ -495,22 +503,22 @@ class PreferencesController {
* @returns {Promise<array>} Promise resolving to updated frequentRpcList.
*
*/
- addToFrequentRpcList (url, chainId, ticker = 'ETH', nickname = '') {
- const rpcList = this.getFrequentRpcListDetail()
- const index = rpcList.findIndex((element) => { return element.rpcUrl === url })
- if (index !== -1) {
- rpcList.splice(index, 1)
- }
- if (url !== 'http://localhost:8545') {
- let checkedChainId
- if (!!chainId && !Number.isNaN(parseInt(chainId))) {
- checkedChainId = chainId
+ addToFrequentRpcList (url, chainId, ticker = 'ETH', nickname = '', rpcPrefs = {}) {
+ const rpcList = this.getFrequentRpcListDetail()
+ const index = rpcList.findIndex((element) => { return element.rpcUrl === url })
+ if (index !== -1) {
+ rpcList.splice(index, 1)
}
- rpcList.push({ rpcUrl: url, chainId: checkedChainId, ticker, nickname })
+ if (url !== 'http://localhost:8545') {
+ let checkedChainId
+ if (!!chainId && !Number.isNaN(parseInt(chainId))) {
+ checkedChainId = chainId
+ }
+ rpcList.push({ rpcUrl: url, chainId: checkedChainId, ticker, nickname, rpcPrefs })
+ }
+ this.store.updateState({ frequentRpcListDetail: rpcList })
+ return Promise.resolve(rpcList)
}
- this.store.updateState({ frequentRpcListDetail: rpcList })
- return Promise.resolve(rpcList)
- }
/**
* Removes custom RPC url from state.
diff --git a/app/scripts/controllers/provider-approval.js b/app/scripts/controllers/provider-approval.js
index 2c9182b52..06c499780 100644
--- a/app/scripts/controllers/provider-approval.js
+++ b/app/scripts/controllers/provider-approval.js
@@ -1,9 +1,11 @@
const ObservableStore = require('obs-store')
+const SafeEventEmitter = require('safe-event-emitter')
+const createAsyncMiddleware = require('json-rpc-engine/src/createAsyncMiddleware')
/**
* A controller that services user-approved requests for a full Ethereum provider API
*/
-class ProviderApprovalController {
+class ProviderApprovalController extends SafeEventEmitter {
/**
* Determines if caching is enabled
*/
@@ -14,38 +16,44 @@ class ProviderApprovalController {
*
* @param {Object} [config] - Options to configure controller
*/
- constructor ({ closePopup, keyringController, openPopup, platform, preferencesController, publicConfigStore } = {}) {
+ constructor ({ closePopup, keyringController, openPopup, preferencesController } = {}) {
+ super()
this.approvedOrigins = {}
this.closePopup = closePopup
this.keyringController = keyringController
this.openPopup = openPopup
- this.platform = platform
this.preferencesController = preferencesController
- this.publicConfigStore = publicConfigStore
this.store = new ObservableStore({
providerRequests: [],
})
+ }
- if (platform && platform.addMessageListener) {
- platform.addMessageListener(({ action = '', force, origin, siteTitle, siteImage }, { tab }) => {
- if (tab && tab.id) {
- switch (action) {
- case 'init-provider-request':
- this._handleProviderRequest(origin, siteTitle, siteImage, force, tab.id)
- break
- case 'init-is-approved':
- this._handleIsApproved(origin, tab.id)
- break
- case 'init-is-unlocked':
- this._handleIsUnlocked(tab.id)
- break
- case 'init-privacy-request':
- this._handlePrivacyRequest(tab.id)
- break
- }
- }
- })
- }
+ /**
+ * Called when a user approves access to a full Ethereum provider API
+ *
+ * @param {object} opts - opts for the middleware contains the origin for the middleware
+ */
+ createMiddleware ({ origin, getSiteMetadata }) {
+ return createAsyncMiddleware(async (req, res, next) => {
+ // only handle requestAccounts
+ if (req.method !== 'eth_requestAccounts') return next()
+ // if already approved or privacy mode disabled, return early
+ const isUnlocked = this.keyringController.memStore.getState().isUnlocked
+ if (this.shouldExposeAccounts(origin) && isUnlocked) {
+ res.result = [this.preferencesController.getSelectedAddress()]
+ return
+ }
+ // register the provider request
+ const metadata = await getSiteMetadata(origin)
+ this._handleProviderRequest(origin, metadata.name, metadata.icon, false, null)
+ // wait for resolution of request
+ const approved = await new Promise(resolve => this.once(`resolvedRequest:${origin}`, ({ approved }) => resolve(approved)))
+ if (approved) {
+ res.result = [this.preferencesController.getSelectedAddress()]
+ } else {
+ throw new Error('User denied account authorization')
+ }
+ })
}
/**
@@ -59,79 +67,37 @@ class ProviderApprovalController {
this.store.updateState({ providerRequests: [{ origin, siteTitle, siteImage, tabID }] })
const isUnlocked = this.keyringController.memStore.getState().isUnlocked
if (!force && this.approvedOrigins[origin] && this.caching && isUnlocked) {
- this.approveProviderRequest(tabID)
return
}
this.openPopup && this.openPopup()
}
/**
- * Called by a tab to determine if an origin has been approved in the past
- *
- * @param {string} origin - Origin of the window
- */
- _handleIsApproved (origin, tabID) {
- this.platform && this.platform.sendMessage({
- action: 'answer-is-approved',
- isApproved: this.approvedOrigins[origin] && this.caching,
- caching: this.caching,
- }, { id: tabID })
- }
-
- /**
- * Called by a tab to determine if MetaMask is currently locked or unlocked
- */
- _handleIsUnlocked (tabID) {
- const isUnlocked = this.keyringController.memStore.getState().isUnlocked
- this.platform && this.platform.sendMessage({ action: 'answer-is-unlocked', isUnlocked }, { id: tabID })
- }
-
- /**
- * Called to check privacy mode; if privacy mode is off, this will automatically enable the provider (legacy behavior)
- */
- _handlePrivacyRequest (tabID) {
- const privacyMode = this.preferencesController.getFeatureFlags().privacyMode
- if (!privacyMode) {
- this.platform && this.platform.sendMessage({
- action: 'approve-legacy-provider-request',
- selectedAddress: this.publicConfigStore.getState().selectedAddress,
- }, { id: tabID })
- this.publicConfigStore.emit('update', this.publicConfigStore.getState())
- }
- }
-
- /**
* Called when a user approves access to a full Ethereum provider API
*
- * @param {string} tabID - ID of the target window that approved provider access
+ * @param {string} origin - origin of the domain that had provider access approved
*/
- approveProviderRequest (tabID) {
+ approveProviderRequestByOrigin (origin) {
this.closePopup && this.closePopup()
const requests = this.store.getState().providerRequests
- const origin = requests.find(request => request.tabID === tabID).origin
- this.platform && this.platform.sendMessage({
- action: 'approve-provider-request',
- selectedAddress: this.publicConfigStore.getState().selectedAddress,
- }, { id: tabID })
- this.publicConfigStore.emit('update', this.publicConfigStore.getState())
- const providerRequests = requests.filter(request => request.tabID !== tabID)
+ const providerRequests = requests.filter(request => request.origin !== origin)
this.store.updateState({ providerRequests })
this.approvedOrigins[origin] = true
+ this.emit(`resolvedRequest:${origin}`, { approved: true })
}
/**
* Called when a tab rejects access to a full Ethereum provider API
*
- * @param {string} tabID - ID of the target window that rejected provider access
+ * @param {string} origin - origin of the domain that had provider access approved
*/
- rejectProviderRequest (tabID) {
+ rejectProviderRequestByOrigin (origin) {
this.closePopup && this.closePopup()
const requests = this.store.getState().providerRequests
- const origin = requests.find(request => request.tabID === tabID).origin
- this.platform && this.platform.sendMessage({ action: 'reject-provider-request' }, { id: tabID })
- const providerRequests = requests.filter(request => request.tabID !== tabID)
+ const providerRequests = requests.filter(request => request.origin !== origin)
this.store.updateState({ providerRequests })
delete this.approvedOrigins[origin]
+ this.emit(`resolvedRequest:${origin}`, { approved: false })
}
/**
@@ -149,16 +115,10 @@ class ProviderApprovalController {
*/
shouldExposeAccounts (origin) {
const privacyMode = this.preferencesController.getFeatureFlags().privacyMode
- return !privacyMode || this.approvedOrigins[origin]
+ const result = !privacyMode || Boolean(this.approvedOrigins[origin])
+ return result
}
- /**
- * Tells all tabs that MetaMask is now locked. This is primarily used to set
- * internal flags in the contentscript and inpage script.
- */
- setLocked () {
- this.platform.sendMessage({ action: 'metamask-set-locked' })
- }
}
module.exports = ProviderApprovalController
diff --git a/app/scripts/controllers/recent-blocks.js b/app/scripts/controllers/recent-blocks.js
index 982ad2aa4..a2b5d1bae 100644
--- a/app/scripts/controllers/recent-blocks.js
+++ b/app/scripts/controllers/recent-blocks.js
@@ -8,8 +8,9 @@ const {
RINKEBY,
KOVAN,
MAINNET,
+ GOERLI,
} = require('./network/enums')
-const INFURA_PROVIDER_TYPES = [ROPSTEN, RINKEBY, KOVAN, MAINNET]
+const INFURA_PROVIDER_TYPES = [ROPSTEN, RINKEBY, KOVAN, MAINNET, GOERLI]
class RecentBlocksController {
diff --git a/app/scripts/controllers/shapeshift.js b/app/scripts/controllers/shapeshift.js
deleted file mode 100644
index b2a1462c2..000000000
--- a/app/scripts/controllers/shapeshift.js
+++ /dev/null
@@ -1,180 +0,0 @@
-const ObservableStore = require('obs-store')
-const extend = require('xtend')
-const log = require('loglevel')
-
-// every three seconds when an incomplete tx is waiting
-const POLLING_INTERVAL = 3000
-
-class ShapeshiftController {
-
- /**
- * Controller responsible for managing the list of shapeshift transactions. On construction, it initiates a poll
- * that queries a shapeshift.io API for updates to any pending shapeshift transactions
- *
- * @typedef {Object} ShapeshiftController
- * @param {object} opts Overrides the defaults for the initial state of this.store
- * @property {array} opts.initState initializes the the state of the ShapeshiftController. Can contain an
- * shapeShiftTxList array.
- * @property {array} shapeShiftTxList An array of ShapeShiftTx objects
- *
- */
- constructor (opts = {}) {
- const initState = extend({
- shapeShiftTxList: [],
- }, opts.initState)
- this.store = new ObservableStore(initState)
- this.pollForUpdates()
- }
-
- /**
- * Represents, and contains data about, a single shapeshift transaction.
- * @typedef {Object} ShapeShiftTx
- * @property {string} depositAddress - An address at which to send a crypto deposit, so that eth can be sent to the
- * user's Metamask account
- * @property {string} depositType - An abbreviation of the type of crypto currency to be deposited.
- * @property {string} key - The 'shapeshift' key differentiates this from other types of txs in Metamask
- * @property {number} time - The time at which the tx was created
- * @property {object} response - Initiated as an empty object, which will be replaced by a Response object. @see {@link
- * https://developer.mozilla.org/en-US/docs/Web/API/Response}
- */
-
- //
- // PUBLIC METHODS
- //
-
- /**
- * A getter for the shapeShiftTxList property
- *
- * @returns {array<ShapeShiftTx>}
- *
- */
- getShapeShiftTxList () {
- const shapeShiftTxList = this.store.getState().shapeShiftTxList
- return shapeShiftTxList
- }
-
- /**
- * A getter for all ShapeShiftTx in the shapeShiftTxList that have not successfully completed a deposit.
- *
- * @returns {array<ShapeShiftTx>} Only includes ShapeShiftTx which has a response property with a status !== complete
- *
- */
- getPendingTxs () {
- const txs = this.getShapeShiftTxList()
- const pending = txs.filter(tx => tx.response && tx.response.status !== 'complete')
- return pending
- }
-
- /**
- * A poll that exists as long as there are pending transactions. Each call attempts to update the data of any
- * pendingTxs, and then calls itself again. If there are no pending txs, the recursive call is not made and
- * the polling stops.
- *
- * this.updateTx is used to attempt the update to the pendingTxs in the ShapeShiftTxList, and that updated data
- * is saved with saveTx.
- *
- */
- pollForUpdates () {
- const pendingTxs = this.getPendingTxs()
-
- if (pendingTxs.length === 0) {
- return
- }
-
- Promise.all(pendingTxs.map((tx) => {
- return this.updateTx(tx)
- }))
- .then((results) => {
- results.forEach(tx => this.saveTx(tx))
- this.timeout = setTimeout(this.pollForUpdates.bind(this), POLLING_INTERVAL)
- })
- }
-
- /**
- * Attempts to update a ShapeShiftTx with data from a shapeshift.io API. Both the response and time properties
- * can be updated. The response property is updated with every call, but the time property is only updated when
- * the response status updates to 'complete'. This will occur once the user makes a deposit as the ShapeShiftTx
- * depositAddress
- *
- * @param {ShapeShiftTx} tx The tx to update
- *
- */
- async updateTx (tx) {
- try {
- const url = `https://shapeshift.io/txStat/${tx.depositAddress}`
- const response = await fetch(url)
- const json = await response.json()
- tx.response = json
- if (tx.response.status === 'complete') {
- tx.time = new Date().getTime()
- }
- return tx
- } catch (err) {
- log.warn(err)
- }
- }
-
- /**
- * Saves an updated to a ShapeShiftTx in the shapeShiftTxList. If the passed ShapeShiftTx is not in the
- * shapeShiftTxList, nothing happens.
- *
- * @param {ShapeShiftTx} tx The updated tx to save, if it exists in the current shapeShiftTxList
- *
- */
- saveTx (tx) {
- const { shapeShiftTxList } = this.store.getState()
- const index = shapeShiftTxList.indexOf(tx)
- if (index !== -1) {
- shapeShiftTxList[index] = tx
- this.store.updateState({ shapeShiftTxList })
- }
- }
-
- /**
- * Removes a ShapeShiftTx from the shapeShiftTxList
- *
- * @param {ShapeShiftTx} tx The tx to remove
- *
- */
- removeShapeShiftTx (tx) {
- const { shapeShiftTxList } = this.store.getState()
- const index = shapeShiftTxList.indexOf(index)
- if (index !== -1) {
- shapeShiftTxList.splice(index, 1)
- }
- this.updateState({ shapeShiftTxList })
- }
-
- /**
- * Creates a new ShapeShiftTx, adds it to the shapeShiftTxList, and initiates a new poll for updates of pending txs
- *
- * @param {string} depositAddress - An address at which to send a crypto deposit, so that eth can be sent to the
- * user's Metamask account
- * @param {string} depositType - An abbreviation of the type of crypto currency to be deposited.
- *
- */
- createShapeShiftTx (depositAddress, depositType) {
- const state = this.store.getState()
- let { shapeShiftTxList } = state
-
- var shapeShiftTx = {
- depositAddress,
- depositType,
- key: 'shapeshift',
- time: new Date().getTime(),
- response: {},
- }
-
- if (!shapeShiftTxList) {
- shapeShiftTxList = [shapeShiftTx]
- } else {
- shapeShiftTxList.push(shapeShiftTx)
- }
-
- this.store.updateState({ shapeShiftTxList })
- this.pollForUpdates()
- }
-
-}
-
-module.exports = ShapeshiftController
diff --git a/app/scripts/controllers/token-rates.js b/app/scripts/controllers/token-rates.js
index 867d36433..6b6265dba 100644
--- a/app/scripts/controllers/token-rates.js
+++ b/app/scripts/controllers/token-rates.js
@@ -1,6 +1,8 @@
const ObservableStore = require('obs-store')
const log = require('loglevel')
const normalizeAddress = require('eth-sig-util').normalize
+const ethUtil = require('ethereumjs-util')
+
// By default, poll every 3 minutes
const DEFAULT_INTERVAL = 180 * 1000
@@ -28,16 +30,16 @@ class TokenRatesController {
async updateExchangeRates () {
if (!this.isActive) { return }
const contractExchangeRates = {}
- const nativeCurrency = this.currency ? this.currency.getState().nativeCurrency.toUpperCase() : 'ETH'
- const pairs = this._tokens.map(token => `pairs[]=${token.address}/${nativeCurrency}`)
- const query = pairs.join('&')
+ const nativeCurrency = this.currency ? this.currency.getState().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) {
try {
- const response = await fetch(`https://exchanges.balanc3.net/pie?${query}&autoConversion=false`)
- const { prices = [] } = await response.json()
- prices.forEach(({ pair, price }) => {
- const address = pair.split('/')[0]
- contractExchangeRates[normalizeAddress(address)] = typeof price === 'number' ? price : 0
+ const response = await fetch(`https://api.coingecko.com/api/v3/simple/token_price/ethereum?${query}`)
+ const prices = await response.json()
+ this._tokens.forEach(token => {
+ const price = prices[token.address.toLowerCase()] || prices[ethUtil.toChecksumAddress(token.address)]
+ contractExchangeRates[normalizeAddress(token.address)] = price ? price[nativeCurrency] : 0
})
} catch (error) {
log.warn(`MetaMask - TokenRatesController exchange rate fetch failed.`, error)
diff --git a/app/scripts/controllers/transactions/index.js b/app/scripts/controllers/transactions/index.js
index 2ce736beb..1ae925835 100644
--- a/app/scripts/controllers/transactions/index.js
+++ b/app/scripts/controllers/transactions/index.js
@@ -3,10 +3,21 @@ const ObservableStore = require('obs-store')
const ethUtil = require('ethereumjs-util')
const Transaction = require('ethereumjs-tx')
const EthQuery = require('ethjs-query')
+const abi = require('human-standard-token-abi')
+const abiDecoder = require('abi-decoder')
+abiDecoder.addABI(abi)
+const {
+ TOKEN_METHOD_APPROVE,
+ TOKEN_METHOD_TRANSFER,
+ TOKEN_METHOD_TRANSFER_FROM,
+ SEND_ETHER_ACTION_KEY,
+ DEPLOY_CONTRACT_ACTION_KEY,
+ CONTRACT_INTERACTION_KEY,
+} = require('../../../../ui/app/helpers/constants/transactions.js')
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')
@@ -180,9 +191,11 @@ class TransactionController extends EventEmitter {
}
txUtils.validateTxParams(normalizedTxParams)
// construct txMeta
+ const { transactionCategory, getCodeResponse } = await this._determineTransactionCategory(txParams)
let txMeta = this.txStateManager.generateTxMeta({
txParams: normalizedTxParams,
type: TRANSACTION_TYPE_STANDARD,
+ transactionCategory,
})
this.addTx(txMeta)
this.emit('newUnapprovedTx', txMeta)
@@ -191,7 +204,7 @@ class TransactionController extends EventEmitter {
// check whether recipient account is blacklisted
recipientBlacklistChecker.checkAccount(txMeta.metamaskNetworkId, normalizedTxParams.to)
// add default tx params
- txMeta = await this.addTxGasDefaults(txMeta)
+ txMeta = await this.addTxGasDefaults(txMeta, getCodeResponse)
} catch (error) {
log.warn(error)
txMeta.loadingDefaults = false
@@ -211,7 +224,7 @@ class TransactionController extends EventEmitter {
@param txMeta {Object} - the txMeta object
@returns {Promise<object>} resolves with txMeta
*/
- async addTxGasDefaults (txMeta) {
+ async addTxGasDefaults (txMeta, getCodeResponse) {
const txParams = txMeta.txParams
// ensure value
txParams.value = txParams.value ? ethUtil.addHexPrefix(txParams.value) : '0x0'
@@ -222,7 +235,7 @@ class TransactionController extends EventEmitter {
}
txParams.gasPrice = ethUtil.addHexPrefix(gasPrice.toString(16))
// set gasLimit
- return await this.txGasUtil.analyzeGasUsage(txMeta)
+ return await this.txGasUtil.analyzeGasUsage(txMeta, getCodeResponse)
}
/**
@@ -542,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
@@ -556,6 +570,43 @@ class TransactionController extends EventEmitter {
}
/**
+ Returns a "type" for a transaction out of the following list: simpleSend, tokenTransfer, tokenApprove,
+ contractDeployment, contractMethodCall
+ */
+ async _determineTransactionCategory (txParams) {
+ const { data, to } = txParams
+ const { name } = data && abiDecoder.decodeMethod(data) || {}
+ const tokenMethodName = [
+ TOKEN_METHOD_APPROVE,
+ TOKEN_METHOD_TRANSFER,
+ TOKEN_METHOD_TRANSFER_FROM,
+ ].find(tokenMethodName => tokenMethodName === name && name.toLowerCase())
+
+ let result
+ let code
+ if (!txParams.data) {
+ result = SEND_ETHER_ACTION_KEY
+ } else if (tokenMethodName) {
+ result = tokenMethodName
+ } else if (!to) {
+ result = DEPLOY_CONTRACT_ACTION_KEY
+ } else {
+ try {
+ code = await this.query.getCode(to)
+ } catch (e) {
+ code = null
+ log.warn(e)
+ }
+ // For an address with no code, geth will return '0x', and ganache-core v2.2.1 will return '0x0'
+ const codeIsEmpty = !code || code === '0x' || code === '0x0'
+
+ result = codeIsEmpty ? SEND_ETHER_ACTION_KEY : CONTRACT_INTERACTION_KEY
+ }
+
+ return { transactionCategory: result, getCodeResponse: code }
+ }
+
+ /**
Sets other txMeta statuses to dropped if the txMeta that has been confirmed has other transactions
in the list have the same nonce
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/controllers/transactions/tx-gas-utils.js b/app/scripts/controllers/transactions/tx-gas-utils.js
index 765551167..287fb6f44 100644
--- a/app/scripts/controllers/transactions/tx-gas-utils.js
+++ b/app/scripts/controllers/transactions/tx-gas-utils.js
@@ -4,7 +4,9 @@ const {
BnMultiplyByFraction,
bnToHex,
} = require('../../lib/util')
+const log = require('loglevel')
const { addHexPrefix } = require('ethereumjs-util')
+const { SEND_ETHER_ACTION_KEY } = require('../../../../ui/app/helpers/constants/transactions.js')
const SIMPLE_GAS_COST = '0x5208' // Hex for 21000, cost of a simple send.
import { TRANSACTION_NO_CONTRACT_ERROR_KEY } from '../../../../ui/app/helpers/constants/error-keys'
@@ -26,12 +28,13 @@ class TxGasUtil {
@param txMeta {Object} - the txMeta object
@returns {object} the txMeta object with the gas written to the txParams
*/
- async analyzeGasUsage (txMeta) {
+ async analyzeGasUsage (txMeta, getCodeResponse) {
const block = await this.query.getBlockByNumber('latest', false)
let estimatedGasHex
try {
- estimatedGasHex = await this.estimateTxGas(txMeta, block.gasLimit)
+ estimatedGasHex = await this.estimateTxGas(txMeta, block.gasLimit, getCodeResponse)
} catch (err) {
+ log.warn(err)
txMeta.simulationFails = {
reason: err.message,
errorKey: err.errorKey,
@@ -54,7 +57,7 @@ class TxGasUtil {
@param blockGasLimitHex {string} - hex string of the block's gas limit
@returns {string} the estimated gas limit as a hex string
*/
- async estimateTxGas (txMeta, blockGasLimitHex) {
+ async estimateTxGas (txMeta, blockGasLimitHex, getCodeResponse) {
const txParams = txMeta.txParams
// check if gasLimit is already specified
@@ -70,11 +73,10 @@ class TxGasUtil {
// see if we can set the gas based on the recipient
if (hasRecipient) {
- const code = await this.query.getCode(recipient)
// For an address with no code, geth will return '0x', and ganache-core v2.2.1 will return '0x0'
- const codeIsEmpty = !code || code === '0x' || code === '0x0'
+ const categorizedAsSimple = txMeta.transactionCategory === SEND_ETHER_ACTION_KEY
- if (codeIsEmpty) {
+ if (categorizedAsSimple) {
// if there's data in the params, but there's no contract code, it's not a valid transaction
if (txParams.data) {
const err = new Error('TxGasUtil - Trying to call a function on a non-contract address')
@@ -82,7 +84,7 @@ class TxGasUtil {
err.errorKey = TRANSACTION_NO_CONTRACT_ERROR_KEY
// set the response on the error so that we can see in logs what the actual response was
- err.getCodeResponse = code
+ err.getCodeResponse = getCodeResponse
throw err
}
diff --git a/app/scripts/controllers/transactions/tx-state-manager.js b/app/scripts/controllers/transactions/tx-state-manager.js
index 420191d9c..1a2cb5dee 100644
--- a/app/scripts/controllers/transactions/tx-state-manager.js
+++ b/app/scripts/controllers/transactions/tx-state-manager.js
@@ -126,10 +126,10 @@ class TransactionStateManager extends EventEmitter {
@returns {object} the txMeta
*/
addTx (txMeta) {
- this.once(`${txMeta.id}:signed`, function (txId) {
+ this.once(`${txMeta.id}:signed`, function () {
this.removeAllListeners(`${txMeta.id}:rejected`)
})
- this.once(`${txMeta.id}:rejected`, function (txId) {
+ this.once(`${txMeta.id}:rejected`, function () {
this.removeAllListeners(`${txMeta.id}:signed`)
})
// initialize history
diff --git a/app/scripts/controllers/user-actions.js b/app/scripts/controllers/user-actions.js
deleted file mode 100644
index f777054b8..000000000
--- a/app/scripts/controllers/user-actions.js
+++ /dev/null
@@ -1,17 +0,0 @@
-const MessageManager = require('./lib/message-manager')
-const PersonalMessageManager = require('./lib/personal-message-manager')
-const TypedMessageManager = require('./lib/typed-message-manager')
-
-class UserActionController {
-
- constructor (opts = {}) {
-
- this.messageManager = new MessageManager()
- this.personalMessageManager = new PersonalMessageManager()
- this.typedMessageManager = new TypedMessageManager()
-
- }
-
-}
-
-module.exports = UserActionController
diff --git a/app/scripts/createStandardProvider.js b/app/scripts/createStandardProvider.js
index a5f9c5d03..2059b9b3a 100644
--- a/app/scripts/createStandardProvider.js
+++ b/app/scripts/createStandardProvider.js
@@ -4,18 +4,10 @@ class StandardProvider {
constructor (provider) {
this._provider = provider
- this._onMessage('ethereumpingerror', this._onClose.bind(this))
- this._onMessage('ethereumpingsuccess', this._onConnect.bind(this))
- window.addEventListener('load', () => {
- this._subscribe()
- this._ping()
- })
- }
-
- _onMessage (type, handler) {
- window.addEventListener('message', function ({ data }) {
- if (!data || data.type !== type) return
- handler.apply(this, arguments)
+ this._subscribe()
+ // indicate that we've connected, mostly just for standard compliance
+ setTimeout(() => {
+ this._onConnect()
})
}
@@ -34,15 +26,6 @@ class StandardProvider {
this._isConnected = true
}
- async _ping () {
- try {
- await this.send('net_version')
- window.postMessage({ type: 'ethereumpingsuccess' }, '*')
- } catch (error) {
- window.postMessage({ type: 'ethereumpingerror' }, '*')
- }
- }
-
_subscribe () {
this._provider.on('data', (error, { method, params }) => {
if (!error && method === 'eth_subscription') {
@@ -59,11 +42,9 @@ class StandardProvider {
* @returns {Promise<*>} Promise resolving to the result if successful
*/
send (method, params = []) {
- if (method === 'eth_requestAccounts') return this._provider.enable()
-
return new Promise((resolve, reject) => {
try {
- this._provider.sendAsync({ method, params, beta: true }, (error, response) => {
+ this._provider.sendAsync({ id: 1, jsonrpc: '2.0', method, params }, (error, response) => {
error = error || response.error
error ? reject(error) : resolve(response)
})
diff --git a/app/scripts/inpage.js b/app/scripts/inpage.js
index 68394d1ae..a4fb552f1 100644
--- a/app/scripts/inpage.js
+++ b/app/scripts/inpage.js
@@ -7,32 +7,12 @@ const setupDappAutoReload = require('./lib/auto-reload.js')
const MetamaskInpageProvider = require('metamask-inpage-provider')
const createStandardProvider = require('./createStandardProvider').default
-let isEnabled = false
let warned = false
-let providerHandle
-let isApprovedHandle
-let isUnlockedHandle
restoreContextAfterImports()
log.setDefaultLevel(process.env.METAMASK_DEBUG ? 'debug' : 'warn')
-/**
- * Adds a postMessage listener for a specific message type
- *
- * @param {string} messageType - postMessage type to listen for
- * @param {Function} handler - event handler
- * @param {boolean} remove - removes this handler after being triggered
- */
-function onMessage (messageType, callback, remove) {
- const handler = function ({ data }) {
- if (!data || data.type !== messageType) { return }
- remove && window.removeEventListener('message', handler)
- callback.apply(window, arguments)
- }
- window.addEventListener('message', handler)
-}
-
//
// setup plugin communication
//
@@ -49,45 +29,16 @@ const inpageProvider = new MetamaskInpageProvider(metamaskStream)
// set a high max listener count to avoid unnecesary warnings
inpageProvider.setMaxListeners(100)
-// set up a listener for when MetaMask is locked
-onMessage('metamasksetlocked', () => { isEnabled = false })
-
-// set up a listener for privacy mode responses
-onMessage('ethereumproviderlegacy', ({ data: { selectedAddress } }) => {
- isEnabled = true
- setTimeout(() => {
- inpageProvider.publicConfigStore.updateState({ selectedAddress })
- }, 0)
-}, true)
-
// augment the provider with its enable method
inpageProvider.enable = function ({ force } = {}) {
return new Promise((resolve, reject) => {
- providerHandle = ({ data: { error, selectedAddress } }) => {
- if (typeof error !== 'undefined') {
- reject({
- message: error,
- code: 4001,
- })
+ inpageProvider.sendAsync({ method: 'eth_requestAccounts', params: [force] }, (error, response) => {
+ if (error) {
+ reject(error)
} else {
- window.removeEventListener('message', providerHandle)
- setTimeout(() => {
- inpageProvider.publicConfigStore.updateState({ selectedAddress })
- }, 0)
-
- // wait for the background to update with an account
- inpageProvider.sendAsync({ method: 'eth_accounts', params: [] }, (error, response) => {
- if (error) {
- reject(error)
- } else {
- isEnabled = true
- resolve(response.result)
- }
- })
+ resolve(response.result)
}
- }
- onMessage('ethereumprovider', providerHandle, true)
- window.postMessage({ type: 'ETHEREUM_ENABLE_PROVIDER', force }, '*')
+ })
})
}
@@ -98,31 +49,23 @@ inpageProvider.autoRefreshOnNetworkChange = true
// add metamask-specific convenience methods
inpageProvider._metamask = new Proxy({
/**
- * Determines if this domain is currently enabled
+ * Synchronously determines if this domain is currently enabled, with a potential false negative if called to soon
*
- * @returns {boolean} - true if this domain is currently enabled
+ * @returns {boolean} - returns true if this domain is currently enabled
*/
isEnabled: function () {
- return isEnabled
+ const { isEnabled } = inpageProvider.publicConfigStore.getState()
+ return Boolean(isEnabled)
},
/**
- * Determines if this domain has been previously approved
+ * Asynchronously determines if this domain is currently enabled
*
- * @returns {Promise<boolean>} - Promise resolving to true if this domain has been previously approved
+ * @returns {Promise<boolean>} - Promise resolving to true if this domain is currently enabled
*/
- isApproved: function () {
- return new Promise((resolve) => {
- isApprovedHandle = ({ data: { caching, isApproved } }) => {
- if (caching) {
- resolve(!!isApproved)
- } else {
- resolve(false)
- }
- }
- onMessage('ethereumisapproved', isApprovedHandle, true)
- window.postMessage({ type: 'ETHEREUM_IS_APPROVED' }, '*')
- })
+ isApproved: async function () {
+ const { isEnabled } = await getPublicConfigWhenReady()
+ return Boolean(isEnabled)
},
/**
@@ -130,14 +73,9 @@ inpageProvider._metamask = new Proxy({
*
* @returns {Promise<boolean>} - Promise resolving to true if MetaMask is currently unlocked
*/
- isUnlocked: function () {
- return new Promise((resolve) => {
- isUnlockedHandle = ({ data: { isUnlocked } }) => {
- resolve(!!isUnlocked)
- }
- onMessage('metamaskisunlocked', isUnlockedHandle, true)
- window.postMessage({ type: 'METAMASK_IS_UNLOCKED' }, '*')
- })
+ isUnlocked: async function () {
+ const { isUnlocked } = await getPublicConfigWhenReady()
+ return Boolean(isUnlocked)
},
}, {
get: function (obj, prop) {
@@ -149,6 +87,19 @@ inpageProvider._metamask = new Proxy({
},
})
+// publicConfig isn't populated until we get a message from background.
+// Using this getter will ensure the state is available
+async function getPublicConfigWhenReady () {
+ const store = inpageProvider.publicConfigStore
+ let state = store.getState()
+ // if state is missing, wait for first update
+ if (!state.networkVersion) {
+ state = await new Promise(resolve => store.once('update', resolve))
+ console.log('new state', state)
+ }
+ return state
+}
+
// Work around for web3@1.0 deleting the bound `sendAsync` but not the unbound
// `sendAsync` method on the prototype, causing `this` reference issues with drizzle
const proxiedInpageProvider = new Proxy(inpageProvider, {
@@ -159,19 +110,6 @@ const proxiedInpageProvider = new Proxy(inpageProvider, {
window.ethereum = createStandardProvider(proxiedInpageProvider)
-// detect eth_requestAccounts and pipe to enable for now
-function detectAccountRequest (method) {
- const originalMethod = inpageProvider[method]
- inpageProvider[method] = function ({ method }) {
- if (method === 'eth_requestAccounts') {
- return window.ethereum.enable()
- }
- return originalMethod.apply(this, arguments)
- }
-}
-detectAccountRequest('send')
-detectAccountRequest('sendAsync')
-
//
// setup web3
//
@@ -218,6 +156,12 @@ inpageProvider.publicConfigStore.subscribe(function (state) {
web3.eth.defaultAccount = state.selectedAddress
})
+inpageProvider.publicConfigStore.subscribe(function (state) {
+ if (state.onboardingcomplete) {
+ window.postMessage('onboardingcomplete', '*')
+ }
+})
+
// need to make sure we aren't affected by overlapping namespaces
// and that we dont affect the app with our namespace
// mostly a fix for web3's BigNumber if AMD's "define" is defined...
diff --git a/app/scripts/lib/backend-metametrics.js b/app/scripts/lib/backend-metametrics.js
new file mode 100644
index 000000000..e3c163c1a
--- /dev/null
+++ b/app/scripts/lib/backend-metametrics.js
@@ -0,0 +1,26 @@
+const {
+ getMetaMetricState,
+} = require('../../../ui/app/selectors/selectors')
+const {
+ sendMetaMetricsEvent,
+} = require('../../../ui/app/helpers/utils/metametrics.util')
+
+const inDevelopment = process.env.NODE_ENV === 'development'
+
+const METAMETRICS_TRACKING_URL = inDevelopment
+ ? 'http://www.metamask.io/metametrics'
+ : 'http://www.metamask.io/metametrics-prod'
+
+function backEndMetaMetricsEvent (metaMaskState, eventData) {
+ const stateEventData = getMetaMetricState({ metamask: metaMaskState })
+
+ if (stateEventData.participateInMetaMetrics) {
+ sendMetaMetricsEvent({
+ ...stateEventData,
+ ...eventData,
+ url: METAMETRICS_TRACKING_URL + '/backend',
+ })
+ }
+}
+
+module.exports = backEndMetaMetricsEvent
diff --git a/app/scripts/lib/buy-eth-url.js b/app/scripts/lib/buy-eth-url.js
index fbe6c6c9e..5cae83a9f 100644
--- a/app/scripts/lib/buy-eth-url.js
+++ b/app/scripts/lib/buy-eth-url.js
@@ -28,6 +28,8 @@ function getBuyEthUrl ({ network, amount, address, service }) {
return 'https://www.rinkeby.io/'
case 'kovan-faucet':
return 'https://github.com/kovan-testnet/faucet'
+ case 'goerli-faucet':
+ return 'https://goerli-faucet.slock.it/'
}
throw new Error(`Unknown cryptocurrency exchange or faucet: "${service}"`)
}
@@ -42,6 +44,8 @@ function getDefaultServiceForNetwork (network) {
return 'rinkeby-faucet'
case '42':
return 'kovan-faucet'
+ case '5':
+ return 'goerli-faucet'
}
throw new Error(`No default cryptocurrency exchange or faucet for networkId: "${network}"`)
}
diff --git a/app/scripts/lib/createDnodeRemoteGetter.js b/app/scripts/lib/createDnodeRemoteGetter.js
new file mode 100644
index 000000000..b70d218f3
--- /dev/null
+++ b/app/scripts/lib/createDnodeRemoteGetter.js
@@ -0,0 +1,16 @@
+module.exports = createDnodeRemoteGetter
+
+function createDnodeRemoteGetter (dnode) {
+ let remote
+
+ dnode.once('remote', (_remote) => {
+ remote = _remote
+ })
+
+ async function getRemote () {
+ if (remote) return remote
+ return await new Promise(resolve => dnode.once('remote', resolve))
+ }
+
+ return getRemote
+}
diff --git a/app/scripts/lib/createProviderMiddleware.js b/app/scripts/lib/createProviderMiddleware.js
deleted file mode 100644
index 8a939ba4e..000000000
--- a/app/scripts/lib/createProviderMiddleware.js
+++ /dev/null
@@ -1,16 +0,0 @@
-module.exports = createProviderMiddleware
-
-/**
- * Forwards an HTTP request to the current Web3 provider
- *
- * @param {{ provider: Object }} config Configuration containing current Web3 provider
- */
-function createProviderMiddleware ({ provider }) {
- return (req, res, next, end) => {
- provider.sendAsync(req, (err, _res) => {
- if (err) return end(err)
- res.result = _res.result
- end()
- })
- }
-}
diff --git a/app/scripts/lib/message-manager.js b/app/scripts/lib/message-manager.js
index e86629590..ac41de523 100644
--- a/app/scripts/lib/message-manager.js
+++ b/app/scripts/lib/message-manager.js
@@ -34,7 +34,7 @@ module.exports = class MessageManager extends EventEmitter {
* @property {array} messages Holds all messages that have been created by this MessageManager
*
*/
- constructor (opts) {
+ constructor () {
super()
this.memStore = new ObservableStore({
unapprovedMsgs: {},
diff --git a/app/scripts/lib/personal-message-manager.js b/app/scripts/lib/personal-message-manager.js
index fdb94f5ec..7c13e521a 100644
--- a/app/scripts/lib/personal-message-manager.js
+++ b/app/scripts/lib/personal-message-manager.js
@@ -36,7 +36,7 @@ module.exports = class PersonalMessageManager extends EventEmitter {
* @property {array} messages Holds all messages that have been created by this PersonalMessageManager
*
*/
- constructor (opts) {
+ constructor () {
super()
this.memStore = new ObservableStore({
unapprovedPersonalMsgs: {},
diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js
index 4108ed4c0..242028c92 100644
--- a/app/scripts/metamask-controller.js
+++ b/app/scripts/metamask-controller.js
@@ -7,8 +7,10 @@
const EventEmitter = require('events')
const pump = require('pump')
const Dnode = require('dnode')
+const pify = require('pify')
const ObservableStore = require('obs-store')
const ComposableObservableStore = require('./lib/ComposableObservableStore')
+const createDnodeRemoteGetter = require('./lib/createDnodeRemoteGetter')
const asStream = require('obs-store/lib/asStream')
const AccountTracker = require('./lib/account-tracker')
const RpcEngine = require('json-rpc-engine')
@@ -18,15 +20,14 @@ const createFilterMiddleware = require('eth-json-rpc-filters')
const createSubscriptionManager = require('eth-json-rpc-filters/subscriptionManager')
const createOriginMiddleware = require('./lib/createOriginMiddleware')
const createLoggerMiddleware = require('./lib/createLoggerMiddleware')
-const createProviderMiddleware = require('./lib/createProviderMiddleware')
+const providerAsMiddleware = require('eth-json-rpc-middleware/providerAsMiddleware')
const {setupMultiplex} = require('./lib/stream-utils.js')
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 ShapeShiftController = require('./controllers/shapeshift')
const InfuraController = require('./controllers/infura')
-const BlacklistController = require('./controllers/blacklist')
const CachedBalancesController = require('./controllers/cached-balances')
const RecentBlocksController = require('./controllers/recent-blocks')
const MessageManager = require('./lib/message-manager')
@@ -53,7 +54,12 @@ const HW_WALLETS_KEYRINGS = [TrezorKeyring.type, LedgerBridgeKeyring.type]
const EthQuery = require('eth-query')
const ethUtil = require('ethereumjs-util')
const sigUtil = require('eth-sig-util')
-const { AddressBookController } = require('gaba')
+const {
+ AddressBookController,
+ ShapeShiftController,
+ PhishingController,
+} = require('gaba')
+const backEndMetaMetricsEvent = require('./lib/backend-metametrics')
module.exports = class MetamaskController extends EventEmitter {
@@ -86,7 +92,7 @@ module.exports = class MetamaskController extends EventEmitter {
this.createVaultMutex = new Mutex()
// network store
- this.networkController = new NetworkController(initState.NetworkController, this.platform)
+ this.networkController = new NetworkController(initState.NetworkController)
// preferences controller
this.preferencesController = new PreferencesController({
@@ -96,6 +102,12 @@ module.exports = class MetamaskController extends EventEmitter {
network: this.networkController,
})
+ // app-state controller
+ this.appStateController = new AppStateController({
+ preferencesStore: this.preferencesController.store,
+ onInactiveTimeout: () => this.setLocked(),
+ })
+
// currency controller
this.currencyController = new CurrencyController({
initState: initState.CurrencyController,
@@ -109,8 +121,7 @@ module.exports = class MetamaskController extends EventEmitter {
})
this.infuraController.scheduleInfuraNetworkCheck()
- this.blacklistController = new BlacklistController()
- this.blacklistController.scheduleUpdates()
+ this.phishingController = new PhishingController()
// rpc provider
this.initializeProvider()
@@ -190,10 +201,26 @@ module.exports = class MetamaskController extends EventEmitter {
})
this.txController.on('newUnapprovedTx', () => opts.showUnapprovedTx())
- this.txController.on(`tx:status-update`, (txId, status) => {
+ this.txController.on(`tx:status-update`, async (txId, status) => {
if (status === 'confirmed' || status === 'failed') {
const txMeta = this.txController.txStateManager.getTx(txId)
this.platform.showTransactionNotification(txMeta)
+
+ const { txReceipt } = txMeta
+ const participateInMetaMetrics = this.preferencesController.getParticipateInMetaMetrics()
+ if (txReceipt && txReceipt.status === '0x0' && participateInMetaMetrics) {
+ const metamaskState = await this.getState()
+ backEndMetaMetricsEvent(metamaskState, {
+ customVariables: {
+ errorMessage: txMeta.simulationFails.reason,
+ },
+ eventOpts: {
+ category: 'backend',
+ action: 'Transactions',
+ name: 'On Chain Failure',
+ },
+ })
+ }
}
})
@@ -210,38 +237,40 @@ module.exports = class MetamaskController extends EventEmitter {
})
this.balancesController.updateAllBalances()
- this.shapeshiftController = new ShapeShiftController({
- initState: initState.ShapeShiftController,
- })
+ this.shapeshiftController = new ShapeShiftController(undefined, initState.ShapeShiftController)
this.networkController.lookupNetwork()
this.messageManager = new MessageManager()
this.personalMessageManager = new PersonalMessageManager()
this.typedMessageManager = new TypedMessageManager({ networkController: this.networkController })
- this.publicConfigStore = this.initPublicConfigStore()
+
+ // ensure isClientOpenAndUnlocked is updated when memState updates
+ this.on('update', (memState) => {
+ this.isClientOpenAndUnlocked = memState.isUnlocked && this._isClientOpen
+ })
this.providerApprovalController = new ProviderApprovalController({
closePopup: opts.closePopup,
keyringController: this.keyringController,
openPopup: opts.openPopup,
- platform: opts.platform,
preferencesController: this.preferencesController,
- publicConfigStore: this.publicConfigStore,
})
this.store.updateStructure({
+ AppStateController: this.appStateController.store,
TransactionController: this.txController.store,
KeyringController: this.keyringController.store,
PreferencesController: this.preferencesController.store,
AddressBookController: this.addressBookController,
CurrencyController: this.currencyController.store,
- ShapeShiftController: this.shapeshiftController.store,
+ ShapeShiftController: this.shapeshiftController,
NetworkController: this.networkController.store,
InfuraController: this.infuraController.store,
CachedBalancesController: this.cachedBalancesController.store,
})
this.memStore = new ComposableObservableStore(null, {
+ AppStateController: this.appStateController.store,
NetworkController: this.networkController.store,
AccountTracker: this.accountTracker.store,
TxController: this.txController.memStore,
@@ -256,7 +285,7 @@ module.exports = class MetamaskController extends EventEmitter {
RecentBlocksController: this.recentBlocksController.store,
AddressBookController: this.addressBookController,
CurrencyController: this.currencyController.store,
- ShapeshiftController: this.shapeshiftController.store,
+ ShapeshiftController: this.shapeshiftController,
InfuraController: this.infuraController.store,
ProviderApprovalController: this.providerApprovalController.store,
})
@@ -305,21 +334,32 @@ module.exports = class MetamaskController extends EventEmitter {
* Constructor helper: initialize a public config store.
* This store is used to make some config info available to Dapps synchronously.
*/
- initPublicConfigStore () {
- // get init state
+ createPublicConfigStore ({ checkIsEnabled }) {
+ // subset of state for metamask inpage provider
const publicConfigStore = new ObservableStore()
- // memStore -> transform -> publicConfigStore
- this.on('update', (memState) => {
- this.isClientOpenAndUnlocked = memState.isUnlocked && this._isClientOpen
+ // setup memStore subscription hooks
+ this.on('update', updatePublicConfigStore)
+ updatePublicConfigStore(this.getState())
+
+ publicConfigStore.destroy = () => {
+ this.removeEventListener && this.removeEventListener('update', updatePublicConfigStore)
+ }
+
+ function updatePublicConfigStore (memState) {
const publicState = selectPublicState(memState)
publicConfigStore.putState(publicState)
- })
+ }
- function selectPublicState (memState) {
+ function selectPublicState ({ isUnlocked, selectedAddress, network, completedOnboarding }) {
+ const isEnabled = checkIsEnabled()
+ const isReady = isUnlocked && isEnabled
const result = {
- selectedAddress: memState.isUnlocked ? memState.selectedAddress : undefined,
- networkVersion: memState.network,
+ isUnlocked,
+ isEnabled,
+ selectedAddress: isReady ? selectedAddress : undefined,
+ networkVersion: network,
+ onboardingcomplete: completedOnboarding,
}
return result
}
@@ -429,6 +469,9 @@ module.exports = class MetamaskController extends EventEmitter {
// AddressController
setAddressBook: this.addressBookController.set.bind(this.addressBookController),
+ // AppStateController
+ setLastActiveTime: nodeify(this.appStateController.setLastActiveTime, this.appStateController),
+
// KeyringController
setLocked: nodeify(this.setLocked, this),
createNewVaultAndKeychain: nodeify(this.createNewVaultAndKeychain, this),
@@ -459,9 +502,10 @@ module.exports = class MetamaskController extends EventEmitter {
signTypedMessage: nodeify(this.signTypedMessage, this),
cancelTypedMessage: this.cancelTypedMessage.bind(this),
- approveProviderRequest: providerApprovalController.approveProviderRequest.bind(providerApprovalController),
+ // provider approval
+ approveProviderRequestByOrigin: providerApprovalController.approveProviderRequestByOrigin.bind(providerApprovalController),
+ rejectProviderRequestByOrigin: providerApprovalController.rejectProviderRequestByOrigin.bind(providerApprovalController),
clearApprovedOrigins: providerApprovalController.clearApprovedOrigins.bind(providerApprovalController),
- rejectProviderRequest: providerApprovalController.rejectProviderRequest.bind(providerApprovalController),
}
}
@@ -1189,9 +1233,8 @@ module.exports = class MetamaskController extends EventEmitter {
* with higher gas.
*
* @param {string} txId - The ID of the transaction to speed up.
- * @param {Function} cb - The callback function called with a full state update.
*/
- async retryTransaction (txId, gasPrice, cb) {
+ async retryTransaction (txId, gasPrice) {
await this.txController.retryTransaction(txId, gasPrice)
const state = await this.getState()
return state
@@ -1204,7 +1247,7 @@ module.exports = class MetamaskController extends EventEmitter {
* @param {string=} customGasPrice - the hex value to use for the cancel transaction
* @returns {object} MetaMask state
*/
- async createCancelTransaction (originalTxId, customGasPrice, cb) {
+ async createCancelTransaction (originalTxId, customGasPrice) {
try {
await this.txController.createCancelTransaction(originalTxId, customGasPrice)
const state = await this.getState()
@@ -1214,7 +1257,7 @@ module.exports = class MetamaskController extends EventEmitter {
}
}
- async createSpeedUpTransaction (originalTxId, customGasPrice, cb) {
+ async createSpeedUpTransaction (originalTxId, customGasPrice) {
await this.txController.createSpeedUpTransaction(originalTxId, customGasPrice)
const state = await this.getState()
return state
@@ -1269,7 +1312,7 @@ module.exports = class MetamaskController extends EventEmitter {
*/
setupUntrustedCommunication (connectionStream, originDomain) {
// Check if new connection is blacklisted
- if (this.blacklistController.checkForPhishing(originDomain)) {
+ if (this.phishingController.test(originDomain)) {
log.debug('MetaMask - sending phishing warning for', originDomain)
this.sendPhishingWarning(connectionStream, originDomain)
return
@@ -1278,8 +1321,9 @@ module.exports = class MetamaskController extends EventEmitter {
// setup multiplexing
const mux = setupMultiplex(connectionStream)
// connect features
- this.setupProviderConnection(mux.createStream('provider'), originDomain)
- this.setupPublicConfig(mux.createStream('publicConfig'))
+ const publicApi = this.setupPublicApi(mux.createStream('publicApi'), originDomain)
+ this.setupProviderConnection(mux.createStream('provider'), originDomain, publicApi)
+ this.setupPublicConfig(mux.createStream('publicConfig'), originDomain)
}
/**
@@ -1352,7 +1396,7 @@ module.exports = class MetamaskController extends EventEmitter {
* @param {*} outStream - The stream to provide over.
* @param {string} origin - The URI of the requesting resource.
*/
- setupProviderConnection (outStream, origin) {
+ setupProviderConnection (outStream, origin, publicApi) {
// setup json rpc engine stack
const engine = new RpcEngine()
const provider = this.provider
@@ -1372,8 +1416,13 @@ module.exports = class MetamaskController extends EventEmitter {
engine.push(subscriptionManager.middleware)
// watch asset
engine.push(this.preferencesController.requestWatchAsset.bind(this.preferencesController))
+ // requestAccounts
+ engine.push(this.providerApprovalController.createMiddleware({
+ origin,
+ getSiteMetadata: publicApi && publicApi.getSiteMetadata,
+ }))
// forward to metamask primary provider
- engine.push(createProviderMiddleware({ provider }))
+ engine.push(providerAsMiddleware(provider))
// setup connection
const providerStream = createEngineStream({ engine })
@@ -1400,12 +1449,18 @@ module.exports = class MetamaskController extends EventEmitter {
*
* @param {*} outStream - The stream to provide public config over.
*/
- setupPublicConfig (outStream) {
- const configStream = asStream(this.publicConfigStore)
+ setupPublicConfig (outStream, originDomain) {
+ const configStore = this.createPublicConfigStore({
+ // check the providerApprovalController's approvedOrigins
+ checkIsEnabled: () => this.providerApprovalController.shouldExposeAccounts(originDomain),
+ })
+ const configStream = asStream(configStore)
+
pump(
configStream,
outStream,
(err) => {
+ configStore.destroy()
configStream.destroy()
if (err) log.error(err)
}
@@ -1413,6 +1468,38 @@ module.exports = class MetamaskController extends EventEmitter {
}
/**
+ * A method for providing our public api over a stream.
+ * This includes a method for setting site metadata like title and image
+ *
+ * @param {*} outStream - The stream to provide the api over.
+ */
+ setupPublicApi (outStream) {
+ const dnode = Dnode()
+ // connect dnode api to remote connection
+ pump(
+ outStream,
+ dnode,
+ outStream,
+ (err) => {
+ // report any error
+ if (err) log.error(err)
+ }
+ )
+
+ const getRemote = createDnodeRemoteGetter(dnode)
+
+ const publicApi = {
+ // wrap with an await remote
+ getSiteMetadata: async () => {
+ const remote = await getRemote()
+ return await pify(remote.getSiteMetadata)()
+ },
+ }
+
+ return publicApi
+ }
+
+ /**
* Handle a KeyringController update
* @param {object} state the KC state
* @return {Promise<void>}
@@ -1544,7 +1631,7 @@ module.exports = class MetamaskController extends EventEmitter {
* @property {string} depositType - An abbreviation of the type of crypto currency to be deposited.
*/
createShapeShiftTx (depositAddress, depositType) {
- this.shapeshiftController.createShapeShiftTx(depositAddress, depositType)
+ this.shapeshiftController.createTransaction(depositAddress, depositType)
}
// network
@@ -1557,9 +1644,9 @@ module.exports = class MetamaskController extends EventEmitter {
* @returns {Promise<String>} - The RPC Target URL confirmed.
*/
- async updateAndSetCustomRpc (rpcUrl, chainId, ticker = 'ETH', nickname) {
- await this.preferencesController.updateRpc({ rpcUrl, chainId, ticker, nickname })
- this.networkController.setRpcTarget(rpcUrl, chainId, ticker, nickname)
+ async updateAndSetCustomRpc (rpcUrl, chainId, ticker = 'ETH', nickname, rpcPrefs) {
+ await this.preferencesController.updateRpc({ rpcUrl, chainId, ticker, nickname, rpcPrefs })
+ this.networkController.setRpcTarget(rpcUrl, chainId, ticker, nickname, rpcPrefs)
return rpcUrl
}
@@ -1572,15 +1659,15 @@ module.exports = class MetamaskController extends EventEmitter {
* @param {string} nickname - Optional nickname of the selected network.
* @returns {Promise<String>} - The RPC Target URL confirmed.
*/
- async setCustomRpc (rpcTarget, chainId, ticker = 'ETH', nickname = '') {
+ async setCustomRpc (rpcTarget, chainId, ticker = 'ETH', nickname = '', rpcPrefs = {}) {
const frequentRpcListDetail = this.preferencesController.getFrequentRpcListDetail()
const rpcSettings = frequentRpcListDetail.find((rpc) => rpcTarget === rpc.rpcUrl)
if (rpcSettings) {
- this.networkController.setRpcTarget(rpcSettings.rpcUrl, rpcSettings.chainId, rpcSettings.ticker, rpcSettings.nickname)
+ this.networkController.setRpcTarget(rpcSettings.rpcUrl, rpcSettings.chainId, rpcSettings.ticker, rpcSettings.nickname, rpcPrefs)
} else {
- this.networkController.setRpcTarget(rpcTarget, chainId, ticker, nickname)
- await this.preferencesController.addToFrequentRpcList(rpcTarget, chainId, ticker, nickname)
+ this.networkController.setRpcTarget(rpcTarget, chainId, ticker, nickname, rpcPrefs)
+ await this.preferencesController.addToFrequentRpcList(rpcTarget, chainId, ticker, nickname, rpcPrefs)
}
return rpcTarget
}
@@ -1705,18 +1792,17 @@ module.exports = class MetamaskController extends EventEmitter {
*/
/**
- * Adds a domain to the {@link BlacklistController} whitelist
+ * Adds a domain to the PhishingController whitelist
* @param {string} hostname the domain to whitelist
*/
whitelistPhishingDomain (hostname) {
- return this.blacklistController.whitelistDomain(hostname)
+ return this.phishingController.bypass(hostname)
}
/**
* Locks MetaMask
*/
setLocked () {
- this.providerApprovalController.setLocked()
return this.keyringController.setLocked()
}
}
diff --git a/app/scripts/migrations/024.js b/app/scripts/migrations/024.js
index d0b276a79..6239bab13 100644
--- a/app/scripts/migrations/024.js
+++ b/app/scripts/migrations/024.js
@@ -27,7 +27,7 @@ function transformState (state) {
const newState = state
if (!newState.TransactionController) return newState
const transactions = newState.TransactionController.transactions
- newState.TransactionController.transactions = transactions.map((txMeta, _, txList) => {
+ newState.TransactionController.transactions = transactions.map((txMeta, _) => {
if (
txMeta.status === 'unapproved' &&
txMeta.txParams &&
diff --git a/app/scripts/migrations/025.js b/app/scripts/migrations/025.js
index fc3b20a44..fd4faa782 100644
--- a/app/scripts/migrations/025.js
+++ b/app/scripts/migrations/025.js
@@ -43,7 +43,7 @@ function normalizeTxParams (txParams) {
// functions that handle normalizing of that key in txParams
const whiteList = {
from: from => ethUtil.addHexPrefix(from).toLowerCase(),
- to: to => ethUtil.addHexPrefix(txParams.to).toLowerCase(),
+ to: () => ethUtil.addHexPrefix(txParams.to).toLowerCase(),
nonce: nonce => ethUtil.addHexPrefix(nonce),
value: value => ethUtil.addHexPrefix(value),
data: data => ethUtil.addHexPrefix(data),
diff --git a/app/scripts/platforms/extension.js b/app/scripts/platforms/extension.js
index 099b0d7ea..0c2d222b8 100644
--- a/app/scripts/platforms/extension.js
+++ b/app/scripts/platforms/extension.js
@@ -60,20 +60,6 @@ class ExtensionPlatform {
}
}
- addMessageListener (cb) {
- extension.runtime.onMessage.addListener(cb)
- }
-
- sendMessage (message, query = {}) {
- const id = query.id
- delete query.id
- extension.tabs.query({ ...query }, tabs => {
- tabs.forEach(tab => {
- extension.tabs.sendMessage(id || tab.id, message)
- })
- })
- }
-
_showConfirmedTransaction (txMeta) {
this._subscribeToNotificationClicked()