diff options
author | Thomas Huang <tmashuang@users.noreply.github.com> | 2019-06-05 05:55:56 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-06-05 05:55:56 +0800 |
commit | be911711942914e7f6df535afff1a3ee87e23b6f (patch) | |
tree | bbd890e0a6a1bd72d2560e734ce66ed42a294722 /app | |
parent | 3d7bdd6ecb5e8e17cb8ccf83f56ad389971332c1 (diff) | |
parent | 28e030a11d75df81e16f3ebf726bfc59fcee679e (diff) | |
download | tangerine-wallet-browser-be911711942914e7f6df535afff1a3ee87e23b6f.tar tangerine-wallet-browser-be911711942914e7f6df535afff1a3ee87e23b6f.tar.gz tangerine-wallet-browser-be911711942914e7f6df535afff1a3ee87e23b6f.tar.bz2 tangerine-wallet-browser-be911711942914e7f6df535afff1a3ee87e23b6f.tar.lz tangerine-wallet-browser-be911711942914e7f6df535afff1a3ee87e23b6f.tar.xz tangerine-wallet-browser-be911711942914e7f6df535afff1a3ee87e23b6f.tar.zst tangerine-wallet-browser-be911711942914e7f6df535afff1a3ee87e23b6f.zip |
Merge pull request #6683 from MetaMask/develop
Merge dev to master
Diffstat (limited to 'app')
-rw-r--r-- | app/404.html | 52 | ||||
-rw-r--r-- | app/_locales/en/messages.json | 4 | ||||
-rw-r--r-- | app/_locales/it/messages.json | 234 | ||||
-rw-r--r-- | app/error.html | 79 | ||||
-rw-r--r-- | app/images/enslogo.svg | 1 | ||||
-rw-r--r-- | app/loading.html | 13 | ||||
-rw-r--r-- | app/manifest.json | 2 | ||||
-rw-r--r-- | app/scripts/controllers/currency.js | 205 | ||||
-rw-r--r-- | app/scripts/controllers/provider-approval.js | 3 | ||||
-rw-r--r-- | app/scripts/controllers/token-rates.js | 2 | ||||
-rw-r--r-- | app/scripts/controllers/transactions/index.js | 3 | ||||
-rw-r--r-- | app/scripts/controllers/transactions/nonce-tracker.js | 161 | ||||
-rw-r--r-- | app/scripts/controllers/transactions/pending-tx-tracker.js | 53 | ||||
-rw-r--r-- | app/scripts/inpage.js | 4 | ||||
-rw-r--r-- | app/scripts/lib/ens-ipfs/contracts/registry.js (renamed from app/scripts/lib/ens-ipfs/contracts/registrar.js) | 0 | ||||
-rw-r--r-- | app/scripts/lib/ens-ipfs/contracts/resolver.js | 2 | ||||
-rw-r--r-- | app/scripts/lib/ens-ipfs/resolver.js | 52 | ||||
-rw-r--r-- | app/scripts/lib/ens-ipfs/setup.js | 30 | ||||
-rw-r--r-- | app/scripts/metamask-controller.js | 32 |
19 files changed, 351 insertions, 581 deletions
diff --git a/app/404.html b/app/404.html deleted file mode 100644 index 8a6df9d7a..000000000 --- a/app/404.html +++ /dev/null @@ -1,52 +0,0 @@ -<html> -<head> - <title>MetaMask</title> - <style> - *{ - padding: 0; - margin: 0; - box-sizing: border-box; - } - img{ - display: block; - } - html, body{ - display: flex; - justify-content: center; - align-items: center; - width: 100%; - height: 100%; - } - .app{ - position: relative; - width: 100%; - height: auto; - overflow: hidden; - } - img{ - display: block; - width: 100%; - height: auto; - } - h2{ - display: block; - width: 100%; - overflow: hidden; - position: absolute; - bottom: 20%; - left: 0; - color: #1b243d; - text-align: center; - } - h2 > a{ - color: #1b243d; - } - </style> -</head> -<body> - <div class="app"> - <img src="./images/404.png" alt=""> - <h2>Powered by <a href="https://www.portal.network/">Portal Network</a></h2> - </div> -</body> -</html> diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index bef278f79..41ba7ef7a 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -549,7 +549,7 @@ "message": "Be careful of phishing! MetaMask will never spontaneously ask for your seed phrase." }, "endOfFlowMessage6": { - "message": "If you need to back your up seed phrase again, you can find it in Settings -> Security." + "message": "If you need to back up your seed phrase again, you can find it in Settings -> Security." }, "endOfFlowMessage7": { "message": "If you ever have questions or see something fishy, email support@metamask.io." @@ -1249,7 +1249,7 @@ "message": "Revert" }, "remove": { - "message": "remove" + "message": "Remove" }, "removeAccount": { "message": "Remove account" diff --git a/app/_locales/it/messages.json b/app/_locales/it/messages.json index 9e0f1f06d..09ba045b9 100644 --- a/app/_locales/it/messages.json +++ b/app/_locales/it/messages.json @@ -11,6 +11,9 @@ "exposeDescription": { "message": "Esporre gli account al sito Web corrente. Utile per dapps legacy." }, + "chartOnlyAvailableEth": { + "message": "Grafico disponibile solo per le reti Ethereum." + }, "confirmExpose": { "message": "Sei sicuro di voler esporre i tuoi account al sito web corrente?" }, @@ -41,6 +44,15 @@ "providerRequestInfo": { "message": "Il dominio elencato di seguito sta tentando di richiedere l'accesso all'API Ethereum in modo che possa interagire con la blockchain di Ethereum. Controlla sempre di essere sul sito corretto prima di approvare l'accesso a Ethereum." }, + "about": { + "message": "Informazioni" + }, + "aboutSettingsDescription": { + "message": "Version, centro di supporto e contatti." + }, + "aboutUs": { + "message": "Chi siamo" + }, "accept": { "message": "Accetta" }, @@ -71,6 +83,15 @@ "address": { "message": "Indirizzo" }, + "addNetwork": { + "message": "Aggiungi Rete" + }, + "advanced": { + "message": "Avanzate" + }, + "advancedSettingsDescription": { + "message": "Accedi alle funzionalità sviluppatore, download dei log di Stato, Reset Account, imposta reti di test e RPC personalizzata." + }, "advancedOptions": { "message": "Opzioni Avanzate" }, @@ -89,8 +110,14 @@ "addAcquiredTokens": { "message": "Aggiungi i token che hai acquistato usando MetaMask" }, - "advanced": { - "message": "Avanzato" + "agreeTermsOfService": { + "message": "Accetto i termini di servizio" + }, + "allDone": { + "message": "Tutto Fatto" + }, + "alreadyHaveSeedPhrase": { + "message": "No, ho già una frase seed" }, "amount": { "message": "Importo" @@ -115,6 +142,9 @@ "approved": { "message": "Approvato" }, + "asset": { + "message": "Asset" + }, "attemptingConnect": { "message": "Tentativo di connessione alla blockchain." }, @@ -127,6 +157,12 @@ "attributions": { "message": "Attribuzioni" }, + "autoLogoutTimeLimit": { + "message": "Timer di Logout Automatico (minuti)" + }, + "autoLogoutTimeLimitDescription": { + "message": "Imposta il tempo di inattività dopo il quale MetaMask fa il log out automaticamente" + }, "available": { "message": "Disponibile" }, @@ -158,6 +194,13 @@ "message": "deve essere maggiore o uguale a $1 e minore o uguale a $2.", "description": "aiuto per inserire un input esadecimale come decimale" }, + "blockExplorerUrl": { + "message": "Block Explorer" + }, + "blockExplorerView": { + "message": "Visualizza account su $1", + "description": "$1 replaced by URL for custom block explorer" + }, "blockiesIdenticon": { "message": "Usa le icone Blockie" }, @@ -179,6 +222,12 @@ "buyCoinbaseExplainer": { "message": "Coinbase è il servizio più popolare al mondo per comprare e vendere Bitcoin, Ethereum e Litecoin." }, + "buyWithWyre": { + "message": "Compra ETH con Wyre" + }, + "buyWithWyreDescription": { + "message": "Wyre ti consente di usare la carta di credito per depositare ETH direttamente nel tuo account MetaMask." + }, "buyCoinSwitch": { "message": "Compra su CoinSwitch" }, @@ -191,6 +240,9 @@ "ok": { "message": "Ok" }, + "optionalBlockExplorerUrl": { + "message": "URL del Block Explorer (opzionale)" + }, "cancel": { "message": "Annulla" }, @@ -206,6 +258,9 @@ "cancelN": { "message": "Annulla tutte le transazioni relative a $1" }, + "chainId": { + "message": "Chain ID" + }, "classicInterface": { "message": "Usa l'interfaccia classica" }, @@ -224,6 +279,9 @@ "chromeRequiredForHardwareWallets": { "message": "Devi usare MetaMask con Google Chrome per connettere il tuo Portafoglio Hardware" }, + "company": { + "message": "Azienda" + }, "confirm": { "message": "Conferma" }, @@ -245,6 +303,9 @@ "confirmTransaction": { "message": "Conferma Transazione" }, + "congratulations": { + "message": "Congratulazioni" + }, "connectHardwareWallet": { "message": "Connetti Portafoglio Hardware" }, @@ -272,6 +333,12 @@ "connectingToRinkeby": { "message": "Connessione alla Rete di test Rinkeby" }, + "connectingToLocalhost": { + "message": "Connessione a Localhost 8545" + }, + "connectingToGoerli": { + "message": "Connessione alla Rete di Test Goerli" + }, "connectingToUnknown": { "message": "Connessione ad una Rete Sconosciuta" }, @@ -287,6 +354,9 @@ "continueToCoinbase": { "message": "Continua su Coinbase" }, + "continueToWyre": { + "message": "Continua su Wyre" + }, "continueToCoinSwitch": { "message": "Continua su CoinSwitch" }, @@ -314,6 +384,12 @@ "copyAddress": { "message": "Copia l'indirizzo" }, + "copyTransactionId": { + "message": "Copia ID Transazione" + }, + "copiedTransactionId": { + "message": "ID Transazione Copiato" + }, "copyToClipboard": { "message": "Copia negli appunti" }, @@ -329,6 +405,9 @@ "createAccount": { "message": "Crea Account" }, + "createAWallet": { + "message": "Crea un Wallet" + }, "createDen": { "message": "Crea" }, @@ -439,6 +518,9 @@ "edit": { "message": "Modifica" }, + "editNetwork": { + "message": "Modifica Rete" + }, "editAccountName": { "message": "Modifica Nome Account" }, @@ -451,6 +533,30 @@ "encryptNewDen": { "message": "Cripta il tuo nuovo DEN" }, + "endOfFlowMessage1": { + "message": "Hai passato il test - tieni la tua frase seed al sicuro, è tua responsabilità!" + }, + "endOfFlowMessage2": { + "message": "Suggerimenti su come tenerla al sicuro" + }, + "endOfFlowMessage3": { + "message": "Salva un backup in più di un posto." + }, + "endOfFlowMessage4": { + "message": "Non condividerla mai con nessuno." + }, + "endOfFlowMessage5": { + "message": "Stai attento al phishing! MetaMask non ti chiederà mai spontaneamente la tua frase seed." + }, + "endOfFlowMessage6": { + "message": "Se vorrai fare nuovamente un backup della frase, la puoi trovare in Impostazioni -> Sicurezza & Privacy." + }, + "endOfFlowMessage7": { + "message": "Se hai delle domande o vedi delle attività sospette, manda una mail a support@metamask.io." + }, + "endOfFlowMessage8": { + "message": "MetaMask non può recuperare la tua frase seed. Impara di più." + }, "ensNameNotFound": { "message": "Nome ENS non trovato" }, @@ -571,6 +677,12 @@ "gasPriceRequired": { "message": "Prezzo Gas Richiesto" }, + "general": { + "message": "Generale" + }, + "generalSettingsDescription": { + "message": "Conversione moneta, moneta primaria, lingua, icone blockie" + }, "generatingTransaction": { "message": "Generando la transazione" }, @@ -584,10 +696,16 @@ "getHelp": { "message": "Aiuto." }, + "getStarted": { + "message": "Inizia" + }, "greaterThanMin": { "message": "deve essere maggiore o uguale a $1.", "description": "aiuto per inserire un input esadecimale come decimale" }, + "happyToSeeYou": { + "message": "Siamo contenti di vederti." + }, "hardware": { "message": "hardware" }, @@ -650,6 +768,12 @@ "importDen": { "message": "Importa un DEN Esistente" }, + "importWallet": { + "message": "Importa Portafoglio" + }, + "importYourExisting": { + "message": "Importa il tuo portafoglio esistente usando la tua frase seed a 12 parole" + }, "imported": { "message": "Importato", "description": "stato che conferma che un account è stato totalmente caricato nel portachiavi" @@ -687,6 +811,9 @@ "knownAddressRecipient": { "message": "Indirizzo del contratto conosciuto." }, + "invalidAddressRecipientNotEthNetwork": { + "message": "Non rete ETH, inserisci caratteri minuscoli" + }, "invalidGasParams": { "message": "Parametri del Gas non validi" }, @@ -727,10 +854,16 @@ "ledgerAccountRestriction": { "message": "E' necessario utilizzare l'ultimo account prima di poterne aggiungere uno nuovo." }, + "legal": { + "message": "Informazioni Legali" + }, "lessThanMax": { "message": "deve essere minore o uguale a $1.", "description": "aiuto per inserire un input esadecimale come decimale" }, + "letsGoSetUp": { + "message": "Si, iniziamo!" + }, "likeToAddTokens": { "message": "Vorresti aggiungere questi token?" }, @@ -794,6 +927,12 @@ "minutesShorthand": { "message": "Min" }, + "mobileSyncTitle": { + "message": "Sincronizza account con il dispositivo mobile" + }, + "mobileSyncText": { + "message": "Per favore inserisci la password per confermare che sei te!" + }, "myAccounts": { "message": "Miei Account" }, @@ -814,9 +953,15 @@ "negativeETH": { "message": "Non puoi inviare una quantità di ETH negativa." }, + "networkName": { + "message": "Nome Rete" + }, "networks": { "message": "Reti" }, + "networkSettingsDescription": { + "message": "Aggiungi e modifica reti RPC personalizzate" + }, "nevermind": { "message": "Non importa" }, @@ -842,7 +987,22 @@ "newNetwork": { "message": "Nuova Rete" }, - "rpcURL": { + "newToMetaMask": { + "message": "Nuovo a MetaMask?" + }, + "noAlreadyHaveSeed": { + "message": "No, ho già una frase seed" + }, + "protectYourKeys": { + "message": "Proteggi le tue chiavi!" + }, + "protectYourKeysMessage1": { + "message": "Stai attento con la tua frase seed - ci sono stati report di siti web che hanno tentato di imitare MetaMask. MetaMask non ti chiederà mai la tua frase seed!" + }, + "protectYourKeysMessage2": { + "message": "Tieni la tua frase al sicuro. Se vedi qualcosa di sospetto, o non sei sicuro di un sito web, manda una mail a support@metamask.io" + }, + "rpcUrl": { "message": "Nuovo URL RPC" }, "showAdvancedOptions": { @@ -884,6 +1044,9 @@ "noTransactions": { "message": "Nessuna Transazione" }, + "notEnoughGas": { + "message": "Gas Non Sufficiente" + }, "notFound": { "message": "Non Trovata" }, @@ -934,6 +1097,12 @@ "originalTotal": { "message": "Totale Precedente" }, + "participateInMetaMetrics": { + "message": "Participa in MetaMetrics" + }, + "participateInMetaMetricsDescription": { + "message": "Participa in MetaMetrics per aiutarci a rendere MetaMask migliore" + }, "password": { "message": "Password" }, @@ -1097,6 +1266,9 @@ "ropsten": { "message": "Rete di test Ropsten" }, + "goerli": { + "message": "Rete di test Goerli" + }, "rpc": { "message": "RPC Personalizzata" }, @@ -1147,6 +1319,12 @@ "secretPhrase": { "message": "Inserisci la tua frase segreta di dodici parole per ripristinare la cassaforte." }, + "securityAndPrivacy": { + "message": "Sicurezza & Privacy" + }, + "securitySettingsDescription": { + "message": "Impostazioni sulla Privacy e sulla frase seed del portafoglio" + }, "secondsShorthand": { "message": "Sec" }, @@ -1207,6 +1385,9 @@ "selectAnAccountHelp": { "message": "Selezione l'account da visualizzare in MetaMask" }, + "selectAnAsset": { + "message": "Seleziona un Asset" + }, "selectAHigherGasFee": { "message": "Seleziona un costo in gas maggiore per accelerare l'elaborazione della transazione.*" }, @@ -1229,7 +1410,13 @@ "message": "Controlli gas avanzati" }, "showAdvancedGasInlineDescription": { - "message": "Seleziona qui per visualizzare i controlli su prezzo e limite del gas nelle schermate di invio e conferma." + "message": "Seleziona per visualizzare i controlli su prezzo e limite del gas nelle schermate di invio e conferma." + }, + "showFiatConversionInTestnets": { + "message": "Mostra conversione nelle reti di test" + }, + "showFiatConversionInTestnetsDescription": { + "message": "Seleziona se vuoi vedere la conversione in valuta fiat nelle reti di test" }, "showPrivateKeys": { "message": "Mostra Chiave Privata" @@ -1330,9 +1517,33 @@ "supportCenter": { "message": "Visita il nostro Centro di Supporto" }, + "symbol": { + "message": "Simbolo" + }, "symbolBetweenZeroTwelve": { "message": "Il simbolo deve essere lungo tra 0 e 12 caratteri." }, + "syncWithMobile": { + "message": "Sincronizza con dispositivo mobile" + }, + "syncWithMobileTitle": { + "message": "Sincronizza con dispositivo mobile" + }, + "syncWithMobileDesc": { + "message": "Puoi sincronizzare i tuoi account e le tue informazioni con il tuo dispositivo mobile. Apri l'app di MetaMask, vai su \"Impostazioni\" e tocca \"Sincronizza da Estensione sul Browser\"" + }, + "syncWithMobileDescNewUsers": { + "message": "Se hai appena aperto l'app di MetaMask per la prima volta, segui i passaggi sul tuo telefono." + }, + "syncWithMobileScanThisCode": { + "message": "Scansiona questo codice con l'app di MetaMask" + }, + "syncWithMobileBeCareful": { + "message": "Assicurati che nessun'altro stia guardando al tuo schermo quando scansioni questo codice" + }, + "syncWithMobileComplete": { + "message": "I tuoi dati sono stati sincronizzati con successo. Goditi l'app di MetaMask!" + }, "takesTooLong": { "message": "Ci sta mettendo troppo?" }, @@ -1342,6 +1553,9 @@ "testFaucet": { "message": "Prova Faucet" }, + "thisWillCreate": { + "message": "Questo creerà un nuovo portafoglio e frase seed" + }, "tips": { "message": "Suggerimenti" }, @@ -1364,6 +1578,9 @@ "tokenBalance": { "message": "Bilancio Token:" }, + "tokenContractAddress": { + "message": "Indirizzo Contratto Token" + }, "tokenSelection": { "message": "Cerca un token o seleziona dalla lista di token più popolari." }, @@ -1525,9 +1742,15 @@ "viewAccount": { "message": "Vedi Account" }, + "viewOnCustomBlockExplorer": { + "message": "Vedi su $1" + }, "viewOnEtherscan": { "message": "Vedi su Etherscan" }, + "viewNetworkInfo": { + "message": "Visualizza Informazioni Rete" + }, "visitWebSite": { "message": "Visita il nostro sito web" }, @@ -1573,6 +1796,9 @@ "yourUniqueAccountImageDescription2": { "message": "Vedrai questa immagine ogni volta che dovrai confermare una transazione." }, + "yourUniqueAccountImageDescription3": { + "message": "MetaMask non ti chiederà mai la tua frase seed!" + }, "zeroGasPriceOnSpeedUpError": { "message": "Prezzo del gas maggiore di zero" } diff --git a/app/error.html b/app/error.html deleted file mode 100644 index 366b3d94a..000000000 --- a/app/error.html +++ /dev/null @@ -1,79 +0,0 @@ -<html> -<head> - <title>MetaMask Error</title> - <link href="https://fonts.googleapis.com/css?family=Rokkitt" rel="stylesheet"> - <style> - *{ - padding: 0; - margin: 0; - box-sizing: border-box; - } - img{ - display: block; - } - html, body{ - display: flex; - justify-content: center; - align-items: center; - width: 100%; - height: 100%; - } - @keyframes logoAmin{ - from {transform: scale(1);} - 50%{transform: scale(1.1);} - to {transform: scale(1);} - } - .errorBox{ - width: 70%; - height: auto; - overflow: hidden; - background-image: url("./images/deadface.png"); - background-repeat: no-repeat; - background-position: 100% 50%; - background-size: auto 90%; - padding: 5px; - } - .errorBox > img{ - width: 100px; - height: auto; - margin-bottom: 25px; - animation: logoAmin 1s infinite linear; - } - .errorBox > h1, .errorBox > h2{ - letter-spacing: 2px; - } - .errorBox > h1{ - color: #9b9b9b; - font-size: 40px; - } - .errorBox > h2{ - color: #1b243d; - font-size: 20px; - padding-top: 5px; - } - .errorBox > h2 >a{ - color: #1b243d; - } - .errorBox > h2 >a:hover{ - color: #44588e; - } - - .errorBox > h1 > span{ - color: #33559f; - } - - </style> -</head> -<body> - <div class="errorBox"> - <img src="./images/logo.png" alt=""> - <h1><span id="name"></span> not found</h1> - <h2>Powered by <a href="https://www.portal.network/">Portal Network</a></h2> - </div> - <script> - let index = location.href.lastIndexOf("?name=") - let name = location.href.slice(index + 6) - document.getElementById("name").innerHTML = name - </script> -</body> -</html> diff --git a/app/images/enslogo.svg b/app/images/enslogo.svg new file mode 100644 index 000000000..20d94c0b1 --- /dev/null +++ b/app/images/enslogo.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 72.52 80.95"><defs><style>.cls-3{fill:#a0a8d4}</style><linearGradient id="linear-gradient" x1="41.95" y1="2.57" x2="12.57" y2="34.42" gradientUnits="userSpaceOnUse"><stop offset=".58" stop-color="#a0a8d4"/><stop offset=".73" stop-color="#8791c7"/><stop offset=".91" stop-color="#6470b4"/></linearGradient><linearGradient id="linear-gradient-2" x1="42.57" y1="81.66" x2="71.96" y2="49.81" xlink:href="#linear-gradient"/><linearGradient id="linear-gradient-3" x1="42.26" y1="1.24" x2="42.26" y2="82.84" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#513eff"/><stop offset=".18" stop-color="#5157ff"/><stop offset=".57" stop-color="#5298ff"/><stop offset="1" stop-color="#52e5ff"/></linearGradient></defs><g style="isolation:isolate"><g id="Layer_1" data-name="Layer 1"><path d="M15.28 34.39c.8 1.71 2.78 5.09 2.78 5.09L40.95 1.64l-22.34 15.6a9.75 9.75 0 0 0-3.18 3.5 16.19 16.19 0 0 0-.15 13.65z" transform="translate(-6 -1.64)" fill="url(#linear-gradient)"/><path class="cls-3" d="M6.21 46.85a25.47 25.47 0 0 0 10 18.51l24.71 17.23s-15.46-22.28-28.5-44.45a22.39 22.39 0 0 1-2.62-7.56 12.1 12.1 0 0 1 0-3.63c-.34.63-1 1.92-1 1.92a29.35 29.35 0 0 0-2.67 8.55 52.28 52.28 0 0 0 .08 9.43z" transform="translate(-6 -1.64)"/><path d="M69.25 49.84c-.8-1.71-2.78-5.09-2.78-5.09L43.58 82.59 65.92 67a9.75 9.75 0 0 0 3.18-3.5 16.19 16.19 0 0 0 .15-13.66z" transform="translate(-6 -1.64)" fill="url(#linear-gradient-2)"/><path class="cls-3" d="M78.32 37.38a25.47 25.47 0 0 0-10-18.51L43.61 1.64s15.45 22.28 28.5 44.45a22.39 22.39 0 0 1 2.61 7.56 12.1 12.1 0 0 1 0 3.63c.34-.63 1-1.92 1-1.92a29.35 29.35 0 0 0 2.67-8.55 52.28 52.28 0 0 0-.07-9.43z" transform="translate(-6 -1.64)"/><path d="M15.43 20.74a9.75 9.75 0 0 1 3.18-3.5l22.34-15.6-22.89 37.85s-2-3.38-2.78-5.09a16.19 16.19 0 0 1 .15-13.66zM6.21 46.85a25.47 25.47 0 0 0 10 18.51l24.71 17.23s-15.46-22.28-28.5-44.45a22.39 22.39 0 0 1-2.62-7.56 12.1 12.1 0 0 1 0-3.63c-.34.63-1 1.92-1 1.92a29.35 29.35 0 0 0-2.67 8.55 52.28 52.28 0 0 0 .08 9.43zm63 3c-.8-1.71-2.78-5.09-2.78-5.09L43.58 82.59 65.92 67a9.75 9.75 0 0 0 3.18-3.5 16.19 16.19 0 0 0 .15-13.66zm9.07-12.46a25.47 25.47 0 0 0-10-18.51L43.61 1.64s15.45 22.28 28.5 44.45a22.39 22.39 0 0 1 2.61 7.56 12.1 12.1 0 0 1 0 3.63c.34-.63 1-1.92 1-1.92a29.35 29.35 0 0 0 2.67-8.55 52.28 52.28 0 0 0-.07-9.43z" transform="translate(-6 -1.64)" style="mix-blend-mode:color" fill="url(#linear-gradient-3)"/></g></g></svg>
\ No newline at end of file diff --git a/app/loading.html b/app/loading.html index 71403a5ac..3b896b718 100644 --- a/app/loading.html +++ b/app/loading.html @@ -11,10 +11,10 @@ top: 50%; left: 50%; transform: translate(-50%, -50%); - width: 256px; + text-align: center; } #logo { - width: 100%; + width: 256px; animation: pulse 1s ease-in-out infinite; } @keyframes pulse { @@ -33,13 +33,8 @@ </head> <body> <div id="div-logo"> - <img id="logo" src="./images/loginglogo.svg"> + <img id="logo" src="./images/enslogo.svg"> + <h1 class="center">MetaMask is querying ENS ...</h1> </div> - <script type="text/javascript"> - // redirect to 404 after one minute - setTimeout(() => { - location.href = './404.html' - }, 60000) - </script> </body> </html> diff --git a/app/manifest.json b/app/manifest.json index 2002dee76..0dbd09b65 100644 --- a/app/manifest.json +++ b/app/manifest.json @@ -1,7 +1,7 @@ { "name": "__MSG_appName__", "short_name": "__MSG_appName__", - "version": "6.5.2", + "version": "6.6.0", "manifest_version": 2, "author": "https://metamask.io", "description": "__MSG_appDescription__", diff --git a/app/scripts/controllers/currency.js b/app/scripts/controllers/currency.js deleted file mode 100644 index fce65fd9c..000000000 --- a/app/scripts/controllers/currency.js +++ /dev/null @@ -1,205 +0,0 @@ -const ObservableStore = require('obs-store') -const extend = require('xtend') -const log = require('loglevel') - -// every ten minutes -const POLLING_INTERVAL = 600000 - -class CurrencyController { - - /** - * Controller responsible for managing data associated with the currently selected currency. - * - * @typedef {Object} CurrencyController - * @param {object} opts Overrides the defaults for the initial state of this.store - * @property {array} opts.initState initializes the the state of the CurrencyController. Can contain an - * currentCurrency, conversionRate and conversionDate properties - * @property {string} currentCurrency A 2-4 character shorthand that describes a specific currency, currently - * selected by the user - * @property {number} conversionRate The conversion rate from ETH to the selected currency. - * @property {string} conversionDate The date at which the conversion rate was set. Expressed in in milliseconds - * since midnight of January 1, 1970 - * @property {number} conversionInterval The id of the interval created by the scheduleConversionInterval method. - * Used to clear an existing interval on subsequent calls of that method. - * @property {string} nativeCurrency The ticker/symbol of the native chain currency - * - */ - constructor (opts = {}) { - const initState = extend({ - currentCurrency: 'usd', - conversionRate: 0, - conversionDate: 'N/A', - nativeCurrency: 'ETH', - }, opts.initState) - this.store = new ObservableStore(initState) - } - - // - // PUBLIC METHODS - // - - /** - * A getter for the nativeCurrency property - * - * @returns {string} A 2-4 character shorthand that describes the specific currency - * - */ - getNativeCurrency () { - return this.store.getState().nativeCurrency - } - - /** - * A setter for the nativeCurrency property - * - * @param {string} nativeCurrency The new currency to set as the nativeCurrency in the store - * - */ - setNativeCurrency (nativeCurrency) { - this.store.updateState({ - nativeCurrency, - ticker: nativeCurrency, - }) - } - - /** - * A getter for the currentCurrency property - * - * @returns {string} A 2-4 character shorthand that describes a specific currency, currently selected by the user - * - */ - getCurrentCurrency () { - return this.store.getState().currentCurrency - } - - /** - * A setter for the currentCurrency property - * - * @param {string} currentCurrency The new currency to set as the currentCurrency in the store - * - */ - setCurrentCurrency (currentCurrency) { - this.store.updateState({ currentCurrency }) - } - - /** - * A getter for the conversionRate property - * - * @returns {string} The conversion rate from ETH to the selected currency. - * - */ - getConversionRate () { - return this.store.getState().conversionRate - } - - /** - * A setter for the conversionRate property - * - * @param {number} conversionRate The new rate to set as the conversionRate in the store - * - */ - setConversionRate (conversionRate) { - this.store.updateState({ conversionRate }) - } - - /** - * A getter for the conversionDate property - * - * @returns {string} The date at which the conversion rate was set. Expressed in milliseconds since midnight of - * January 1, 1970 - * - */ - getConversionDate () { - return this.store.getState().conversionDate - } - - /** - * A setter for the conversionDate property - * - * @param {number} conversionDate The date, expressed in milliseconds since midnight of January 1, 1970, that the - * conversionRate was set - * - */ - setConversionDate (conversionDate) { - this.store.updateState({ conversionDate }) - } - - /** - * Updates the conversionRate and conversionDate properties associated with the currentCurrency. Updated info is - * fetched from an external API - * - */ - async updateConversionRate () { - let currentCurrency, nativeCurrency - try { - currentCurrency = this.getCurrentCurrency() - nativeCurrency = this.getNativeCurrency() - // select api - let apiUrl - if (nativeCurrency === 'ETH') { - // ETH - apiUrl = `https://api.infura.io/v1/ticker/eth${currentCurrency.toLowerCase()}` - } else { - // ETC - apiUrl = `https://min-api.cryptocompare.com/data/price?fsym=${nativeCurrency.toUpperCase()}&tsyms=${currentCurrency.toUpperCase()}` - } - // attempt request - let response - try { - response = await fetch(apiUrl) - } catch (err) { - log.error(new Error(`CurrencyController - Failed to request currency from Infura:\n${err.stack}`)) - return - } - // parse response - let rawResponse - let parsedResponse - try { - rawResponse = await response.text() - parsedResponse = JSON.parse(rawResponse) - } catch (err) { - log.error(new Error(`CurrencyController - Failed to parse response "${rawResponse}"`)) - return - } - // set conversion rate - if (nativeCurrency === 'ETH') { - // ETH - this.setConversionRate(Number(parsedResponse.bid)) - this.setConversionDate(Number(parsedResponse.timestamp)) - } else { - // ETC - if (parsedResponse[currentCurrency.toUpperCase()]) { - this.setConversionRate(Number(parsedResponse[currentCurrency.toUpperCase()])) - this.setConversionDate(parseInt((new Date()).getTime() / 1000)) - } else { - this.setConversionRate(0) - this.setConversionDate('N/A') - } - } - } catch (err) { - // reset current conversion rate - log.warn(`MetaMask - Failed to query currency conversion:`, nativeCurrency, currentCurrency, err) - this.setConversionRate(0) - this.setConversionDate('N/A') - // throw error - log.error(new Error(`CurrencyController - Failed to query rate for currency "${currentCurrency}":\n${err.stack}`)) - return - } - } - - /** - * Creates a new poll, using setInterval, to periodically call updateConversionRate. The id of the interval is - * stored at the controller's conversionInterval property. If it is called and such an id already exists, the - * previous interval is clear and a new one is created. - * - */ - scheduleConversionInterval () { - if (this.conversionInterval) { - clearInterval(this.conversionInterval) - } - this.conversionInterval = setInterval(() => { - this.updateConversionRate() - }, POLLING_INTERVAL) - } -} - -module.exports = CurrencyController diff --git a/app/scripts/controllers/provider-approval.js b/app/scripts/controllers/provider-approval.js index 8206b2f8a..06c499780 100644 --- a/app/scripts/controllers/provider-approval.js +++ b/app/scripts/controllers/provider-approval.js @@ -38,7 +38,8 @@ class ProviderApprovalController extends SafeEventEmitter { // only handle requestAccounts if (req.method !== 'eth_requestAccounts') return next() // if already approved or privacy mode disabled, return early - if (this.shouldExposeAccounts(origin)) { + const isUnlocked = this.keyringController.memStore.getState().isUnlocked + if (this.shouldExposeAccounts(origin) && isUnlocked) { res.result = [this.preferencesController.getSelectedAddress()] return } diff --git a/app/scripts/controllers/token-rates.js b/app/scripts/controllers/token-rates.js index 6b6265dba..9b86a9ebf 100644 --- a/app/scripts/controllers/token-rates.js +++ b/app/scripts/controllers/token-rates.js @@ -30,7 +30,7 @@ class TokenRatesController { async updateExchangeRates () { if (!this.isActive) { return } const contractExchangeRates = {} - const nativeCurrency = this.currency ? this.currency.getState().nativeCurrency.toLowerCase() : 'eth' + const nativeCurrency = this.currency ? this.currency.state.nativeCurrency.toLowerCase() : 'eth' const pairs = this._tokens.map(token => token.address).join(',') const query = `contract_addresses=${pairs}&vs_currencies=${nativeCurrency}` if (this._tokens.length > 0) { diff --git a/app/scripts/controllers/transactions/index.js b/app/scripts/controllers/transactions/index.js index 79dba7833..1ae925835 100644 --- a/app/scripts/controllers/transactions/index.js +++ b/app/scripts/controllers/transactions/index.js @@ -17,7 +17,7 @@ const { const TransactionStateManager = require('./tx-state-manager') const TxGasUtil = require('./tx-gas-utils') const PendingTransactionTracker = require('./pending-tx-tracker') -const NonceTracker = require('./nonce-tracker') +const NonceTracker = require('nonce-tracker') const txUtils = require('./lib/util') const cleanErrorStack = require('../../lib/cleanErrorStack') const log = require('loglevel') @@ -555,6 +555,7 @@ class TransactionController extends EventEmitter { }) this.pendingTxTracker.on('tx:failed', this.txStateManager.setTxStatusFailed.bind(this.txStateManager)) this.pendingTxTracker.on('tx:confirmed', (txId) => this.confirmTransaction(txId)) + this.pendingTxTracker.on('tx:dropped', this.txStateManager.setTxStatusDropped.bind(this.txStateManager)) this.pendingTxTracker.on('tx:block-update', (txMeta, latestBlockNumber) => { if (!txMeta.firstRetryBlockNumber) { txMeta.firstRetryBlockNumber = latestBlockNumber diff --git a/app/scripts/controllers/transactions/nonce-tracker.js b/app/scripts/controllers/transactions/nonce-tracker.js deleted file mode 100644 index 421036368..000000000 --- a/app/scripts/controllers/transactions/nonce-tracker.js +++ /dev/null @@ -1,161 +0,0 @@ -const EthQuery = require('ethjs-query') -const assert = require('assert') -const Mutex = require('await-semaphore').Mutex -/** - @param opts {Object} - @param {Object} opts.provider a ethereum provider - @param {Function} opts.getPendingTransactions a function that returns an array of txMeta - whosee status is `submitted` - @param {Function} opts.getConfirmedTransactions a function that returns an array of txMeta - whose status is `confirmed` - @class -*/ -class NonceTracker { - - constructor ({ provider, blockTracker, getPendingTransactions, getConfirmedTransactions }) { - this.provider = provider - this.blockTracker = blockTracker - this.ethQuery = new EthQuery(provider) - this.getPendingTransactions = getPendingTransactions - this.getConfirmedTransactions = getConfirmedTransactions - this.lockMap = {} - } - - /** - @returns {Promise<Object>} with the key releaseLock (the gloabl mutex) - */ - async getGlobalLock () { - const globalMutex = this._lookupMutex('global') - // await global mutex free - const releaseLock = await globalMutex.acquire() - return { releaseLock } - } - - /** - * @typedef NonceDetails - * @property {number} highestLocallyConfirmed - A hex string of the highest nonce on a confirmed transaction. - * @property {number} nextNetworkNonce - The next nonce suggested by the eth_getTransactionCount method. - * @property {number} highestSuggested - The maximum between the other two, the number returned. - */ - - /** - this will return an object with the `nextNonce` `nonceDetails` of type NonceDetails, and the releaseLock - Note: releaseLock must be called after adding a signed tx to pending transactions (or discarding). - - @param address {string} the hex string for the address whose nonce we are calculating - @returns {Promise<NonceDetails>} - */ - async getNonceLock (address) { - // await global mutex free - await this._globalMutexFree() - // await lock free, then take lock - const releaseLock = await this._takeMutex(address) - try { - // evaluate multiple nextNonce strategies - const nonceDetails = {} - const networkNonceResult = await this._getNetworkNextNonce(address) - const highestLocallyConfirmed = this._getHighestLocallyConfirmed(address) - const nextNetworkNonce = networkNonceResult.nonce - const highestSuggested = Math.max(nextNetworkNonce, highestLocallyConfirmed) - - const pendingTxs = this.getPendingTransactions(address) - const localNonceResult = this._getHighestContinuousFrom(pendingTxs, highestSuggested) || 0 - - nonceDetails.params = { - highestLocallyConfirmed, - highestSuggested, - nextNetworkNonce, - } - nonceDetails.local = localNonceResult - nonceDetails.network = networkNonceResult - - const nextNonce = Math.max(networkNonceResult.nonce, localNonceResult.nonce) - assert(Number.isInteger(nextNonce), `nonce-tracker - nextNonce is not an integer - got: (${typeof nextNonce}) "${nextNonce}"`) - - // return nonce and release cb - return { nextNonce, nonceDetails, releaseLock } - } catch (err) { - // release lock if we encounter an error - releaseLock() - throw err - } - } - - async _globalMutexFree () { - const globalMutex = this._lookupMutex('global') - const releaseLock = await globalMutex.acquire() - releaseLock() - } - - async _takeMutex (lockId) { - const mutex = this._lookupMutex(lockId) - const releaseLock = await mutex.acquire() - return releaseLock - } - - _lookupMutex (lockId) { - let mutex = this.lockMap[lockId] - if (!mutex) { - mutex = new Mutex() - this.lockMap[lockId] = mutex - } - return mutex - } - - async _getNetworkNextNonce (address) { - // calculate next nonce - // we need to make sure our base count - // and pending count are from the same block - const blockNumber = await this.blockTracker.getLatestBlock() - const baseCountBN = await this.ethQuery.getTransactionCount(address, blockNumber) - const baseCount = baseCountBN.toNumber() - assert(Number.isInteger(baseCount), `nonce-tracker - baseCount is not an integer - got: (${typeof baseCount}) "${baseCount}"`) - const nonceDetails = { blockNumber, baseCount } - return { name: 'network', nonce: baseCount, details: nonceDetails } - } - - _getHighestLocallyConfirmed (address) { - const confirmedTransactions = this.getConfirmedTransactions(address) - const highest = this._getHighestNonce(confirmedTransactions) - return Number.isInteger(highest) ? highest + 1 : 0 - } - - _getHighestNonce (txList) { - const nonces = txList.map((txMeta) => { - const nonce = txMeta.txParams.nonce - assert(typeof nonce, 'string', 'nonces should be hex strings') - return parseInt(nonce, 16) - }) - const highestNonce = Math.max.apply(null, nonces) - return highestNonce - } - - /** - @typedef {object} highestContinuousFrom - @property {string} - name the name for how the nonce was calculated based on the data used - @property {number} - nonce the next suggested nonce - @property {object} - details the provided starting nonce that was used (for debugging) - */ - /** - @param txList {array} - list of txMeta's - @param startPoint {number} - the highest known locally confirmed nonce - @returns {highestContinuousFrom} - */ - _getHighestContinuousFrom (txList, startPoint) { - const nonces = txList.map((txMeta) => { - const nonce = txMeta.txParams.nonce - assert(typeof nonce, 'string', 'nonces should be hex strings') - return parseInt(nonce, 16) - }) - - let highest = startPoint - while (nonces.includes(highest)) { - highest++ - } - - return { name: 'local', nonce: highest, details: { startPoint, highest } } - } - -} - -module.exports = NonceTracker diff --git a/app/scripts/controllers/transactions/pending-tx-tracker.js b/app/scripts/controllers/transactions/pending-tx-tracker.js index 4bf40b1db..bc11f6633 100644 --- a/app/scripts/controllers/transactions/pending-tx-tracker.js +++ b/app/scripts/controllers/transactions/pending-tx-tracker.js @@ -22,6 +22,7 @@ const EthQuery = require('ethjs-query') class PendingTransactionTracker extends EventEmitter { constructor (config) { super() + this.droppedBuffer = {} this.query = new EthQuery(config.provider) this.nonceTracker = config.nonceTracker this.getPendingTransactions = config.getPendingTransactions @@ -139,22 +140,42 @@ class PendingTransactionTracker extends EventEmitter { const noTxHashErr = new Error('We had an error while submitting this transaction, please try again.') noTxHashErr.name = 'NoTxHashError' this.emit('tx:failed', txId, noTxHashErr) + return } - // If another tx with the same nonce is mined, set as failed. + // If another tx with the same nonce is mined, set as dropped. const taken = await this._checkIfNonceIsTaken(txMeta) - if (taken) { - const nonceTakenErr = new Error('Another transaction with this nonce has been mined.') - nonceTakenErr.name = 'NonceTakenErr' - return this.emit('tx:failed', txId, nonceTakenErr) + let dropped + try { + // check the network if the nonce is ahead the tx + // and the tx has not been mined into a block + + dropped = await this._checkIftxWasDropped(txMeta) + // the dropped buffer is in case we ask a node for the tx + // that is behind the node we asked for tx count + // IS A SECURITY FOR HITTING NODES IN INFURA THAT COULD GO OUT + // OF SYNC. + // on the next block event it will return fire as dropped + if (dropped && !this.droppedBuffer[txHash]) { + this.droppedBuffer[txHash] = true + dropped = false + } else if (dropped && this.droppedBuffer[txHash]) { + // clean up + delete this.droppedBuffer[txHash] + } + + } catch (e) { + log.error(e) + } + if (taken || dropped) { + return this.emit('tx:dropped', txId) } // get latest transaction status try { - const txParams = await this.query.getTransactionByHash(txHash) - if (!txParams) return - if (txParams.blockNumber) { + const { blockNumber } = await this.query.getTransactionByHash(txHash) || {} + if (blockNumber) { this.emit('tx:confirmed', txId) } } catch (err) { @@ -165,6 +186,22 @@ class PendingTransactionTracker extends EventEmitter { this.emit('tx:warning', txMeta, err) } } + /** + checks to see if if the tx's nonce has been used by another transaction + @param txMeta {Object} - txMeta object + @emits tx:dropped + @returns {boolean} + */ + + async _checkIftxWasDropped (txMeta) { + const { txParams: { nonce, from }, hash } = txMeta + const nextNonce = await this.query.getTransactionCount(from) + const { blockNumber } = await this.query.getTransactionByHash(hash) || {} + if (!blockNumber && parseInt(nextNonce) > parseInt(nonce)) { + return true + } + return false + } /** checks to see if a confirmed txMeta has the same nonce diff --git a/app/scripts/inpage.js b/app/scripts/inpage.js index a4fb552f1..fb16dd43d 100644 --- a/app/scripts/inpage.js +++ b/app/scripts/inpage.js @@ -33,8 +33,8 @@ inpageProvider.setMaxListeners(100) inpageProvider.enable = function ({ force } = {}) { return new Promise((resolve, reject) => { inpageProvider.sendAsync({ method: 'eth_requestAccounts', params: [force] }, (error, response) => { - if (error) { - reject(error) + if (error || response.error) { + reject(error || response.error) } else { resolve(response.result) } diff --git a/app/scripts/lib/ens-ipfs/contracts/registrar.js b/app/scripts/lib/ens-ipfs/contracts/registry.js index 99ca24458..99ca24458 100644 --- a/app/scripts/lib/ens-ipfs/contracts/registrar.js +++ b/app/scripts/lib/ens-ipfs/contracts/registry.js diff --git a/app/scripts/lib/ens-ipfs/contracts/resolver.js b/app/scripts/lib/ens-ipfs/contracts/resolver.js index 1bf3f90ce..b61fbed88 100644 --- a/app/scripts/lib/ens-ipfs/contracts/resolver.js +++ b/app/scripts/lib/ens-ipfs/contracts/resolver.js @@ -1,2 +1,2 @@ module.exports = -[{'constant': true, 'inputs': [{'name': 'interfaceID', 'type': 'bytes4'}], 'name': 'supportsInterface', 'outputs': [{'name': '', 'type': 'bool'}], 'payable': false, 'type': 'function'}, {'constant': true, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'contentTypes', 'type': 'uint256'}], 'name': 'ABI', 'outputs': [{'name': 'contentType', 'type': 'uint256'}, {'name': 'data', 'type': 'bytes'}], 'payable': false, 'type': 'function'}, {'constant': false, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'x', 'type': 'bytes32'}, {'name': 'y', 'type': 'bytes32'}], 'name': 'setPubkey', 'outputs': [], 'payable': false, 'type': 'function'}, {'constant': true, 'inputs': [{'name': 'node', 'type': 'bytes32'}], 'name': 'content', 'outputs': [{'name': 'ret', 'type': 'bytes32'}], 'payable': false, 'type': 'function'}, {'constant': true, 'inputs': [{'name': 'node', 'type': 'bytes32'}], 'name': 'addr', 'outputs': [{'name': 'ret', 'type': 'address'}], 'payable': false, 'type': 'function'}, {'constant': false, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'contentType', 'type': 'uint256'}, {'name': 'data', 'type': 'bytes'}], 'name': 'setABI', 'outputs': [], 'payable': false, 'type': 'function'}, {'constant': true, 'inputs': [{'name': 'node', 'type': 'bytes32'}], 'name': 'name', 'outputs': [{'name': 'ret', 'type': 'string'}], 'payable': false, 'type': 'function'}, {'constant': false, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'name', 'type': 'string'}], 'name': 'setName', 'outputs': [], 'payable': false, 'type': 'function'}, {'constant': false, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'hash', 'type': 'bytes32'}], 'name': 'setContent', 'outputs': [], 'payable': false, 'type': 'function'}, {'constant': true, 'inputs': [{'name': 'node', 'type': 'bytes32'}], 'name': 'pubkey', 'outputs': [{'name': 'x', 'type': 'bytes32'}, {'name': 'y', 'type': 'bytes32'}], 'payable': false, 'type': 'function'}, {'constant': false, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'addr', 'type': 'address'}], 'name': 'setAddr', 'outputs': [], 'payable': false, 'type': 'function'}, {'inputs': [{'name': 'ensAddr', 'type': 'address'}], 'payable': false, 'type': 'constructor'}, {'anonymous': false, 'inputs': [{'indexed': true, 'name': 'node', 'type': 'bytes32'}, {'indexed': false, 'name': 'a', 'type': 'address'}], 'name': 'AddrChanged', 'type': 'event'}, {'anonymous': false, 'inputs': [{'indexed': true, 'name': 'node', 'type': 'bytes32'}, {'indexed': false, 'name': 'hash', 'type': 'bytes32'}], 'name': 'ContentChanged', 'type': 'event'}, {'anonymous': false, 'inputs': [{'indexed': true, 'name': 'node', 'type': 'bytes32'}, {'indexed': false, 'name': 'name', 'type': 'string'}], 'name': 'NameChanged', 'type': 'event'}, {'anonymous': false, 'inputs': [{'indexed': true, 'name': 'node', 'type': 'bytes32'}, {'indexed': true, 'name': 'contentType', 'type': 'uint256'}], 'name': 'ABIChanged', 'type': 'event'}, {'anonymous': false, 'inputs': [{'indexed': true, 'name': 'node', 'type': 'bytes32'}, {'indexed': false, 'name': 'x', 'type': 'bytes32'}, {'indexed': false, 'name': 'y', 'type': 'bytes32'}], 'name': 'PubkeyChanged', 'type': 'event'}] +[{'constant': false, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'hash', 'type': 'bytes32'}], 'name': 'setContent', 'outputs': [], 'payable': false, 'stateMutability': 'nonpayable', 'type': 'function'}, {'constant': true, 'inputs': [{'name': 'node', 'type': 'bytes32'}], 'name': 'content', 'outputs': [{'name': '', 'type': 'bytes32'}], 'payable': false, 'stateMutability': 'view', 'type': 'function'}, {'constant': true, 'inputs': [{'name': 'interfaceID', 'type': 'bytes4'}], 'name': 'supportsInterface', 'outputs': [{'name': '', 'type': 'bool'}], 'payable': false, 'stateMutability': 'pure', 'type': 'function'}, {'constant': false, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'key', 'type': 'string'}, {'name': 'value', 'type': 'string'}], 'name': 'setText', 'outputs': [], 'payable': false, 'stateMutability': 'nonpayable', 'type': 'function'}, {'constant': true, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'contentTypes', 'type': 'uint256'}], 'name': 'ABI', 'outputs': [{'name': 'contentType', 'type': 'uint256'}, {'name': 'data', 'type': 'bytes'}], 'payable': false, 'stateMutability': 'view', 'type': 'function'}, {'constant': false, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'x', 'type': 'bytes32'}, {'name': 'y', 'type': 'bytes32'}], 'name': 'setPubkey', 'outputs': [], 'payable': false, 'stateMutability': 'nonpayable', 'type': 'function'}, {'constant': false, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'hash', 'type': 'bytes'}], 'name': 'setContenthash', 'outputs': [], 'payable': false, 'stateMutability': 'nonpayable', 'type': 'function'}, {'constant': true, 'inputs': [{'name': 'node', 'type': 'bytes32'}], 'name': 'addr', 'outputs': [{'name': '', 'type': 'address'}], 'payable': false, 'stateMutability': 'view', 'type': 'function'}, {'constant': true, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'key', 'type': 'string'}], 'name': 'text', 'outputs': [{'name': '', 'type': 'string'}], 'payable': false, 'stateMutability': 'view', 'type': 'function'}, {'constant': false, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'contentType', 'type': 'uint256'}, {'name': 'data', 'type': 'bytes'}], 'name': 'setABI', 'outputs': [], 'payable': false, 'stateMutability': 'nonpayable', 'type': 'function'}, {'constant': true, 'inputs': [{'name': 'node', 'type': 'bytes32'}], 'name': 'name', 'outputs': [{'name': '', 'type': 'string'}], 'payable': false, 'stateMutability': 'view', 'type': 'function'}, {'constant': false, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'name', 'type': 'string'}], 'name': 'setName', 'outputs': [], 'payable': false, 'stateMutability': 'nonpayable', 'type': 'function'}, {'constant': true, 'inputs': [{'name': 'node', 'type': 'bytes32'}], 'name': 'contenthash', 'outputs': [{'name': '', 'type': 'bytes'}], 'payable': false, 'stateMutability': 'view', 'type': 'function'}, {'constant': true, 'inputs': [{'name': 'node', 'type': 'bytes32'}], 'name': 'pubkey', 'outputs': [{'name': 'x', 'type': 'bytes32'}, {'name': 'y', 'type': 'bytes32'}], 'payable': false, 'stateMutability': 'view', 'type': 'function'}, {'constant': false, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'addr', 'type': 'address'}], 'name': 'setAddr', 'outputs': [], 'payable': false, 'stateMutability': 'nonpayable', 'type': 'function'}, {'inputs': [{'name': 'ensAddr', 'type': 'address'}], 'payable': false, 'stateMutability': 'nonpayable', 'type': 'constructor'}, {'anonymous': false, 'inputs': [{'indexed': true, 'name': 'node', 'type': 'bytes32'}, {'indexed': false, 'name': 'a', 'type': 'address'}], 'name': 'AddrChanged', 'type': 'event'}, {'anonymous': false, 'inputs': [{'indexed': true, 'name': 'node', 'type': 'bytes32'}, {'indexed': false, 'name': 'name', 'type': 'string'}], 'name': 'NameChanged', 'type': 'event'}, {'anonymous': false, 'inputs': [{'indexed': true, 'name': 'node', 'type': 'bytes32'}, {'indexed': true, 'name': 'contentType', 'type': 'uint256'}], 'name': 'ABIChanged', 'type': 'event'}, {'anonymous': false, 'inputs': [{'indexed': true, 'name': 'node', 'type': 'bytes32'}, {'indexed': false, 'name': 'x', 'type': 'bytes32'}, {'indexed': false, 'name': 'y', 'type': 'bytes32'}], 'name': 'PubkeyChanged', 'type': 'event'}, {'anonymous': false, 'inputs': [{'indexed': true, 'name': 'node', 'type': 'bytes32'}, {'indexed': false, 'name': 'indexedKey', 'type': 'string'}, {'indexed': false, 'name': 'key', 'type': 'string'}], 'name': 'TextChanged', 'type': 'event'}, {'anonymous': false, 'inputs': [{'indexed': true, 'name': 'node', 'type': 'bytes32'}, {'indexed': false, 'name': 'hash', 'type': 'bytes'}], 'name': 'ContenthashChanged', 'type': 'event'}] diff --git a/app/scripts/lib/ens-ipfs/resolver.js b/app/scripts/lib/ens-ipfs/resolver.js index b98566190..a0af263bc 100644 --- a/app/scripts/lib/ens-ipfs/resolver.js +++ b/app/scripts/lib/ens-ipfs/resolver.js @@ -1,9 +1,9 @@ const namehash = require('eth-ens-namehash') -const multihash = require('multihashes') const Eth = require('ethjs-query') const EthContract = require('ethjs-contract') -const registrarAbi = require('./contracts/registrar') +const registryAbi = require('./contracts/registry') const resolverAbi = require('./contracts/resolver') +const contentHash = require('content-hash') module.exports = resolveEnsToIpfsContentId @@ -12,37 +12,47 @@ async function resolveEnsToIpfsContentId ({ provider, name }) { const eth = new Eth(provider) const hash = namehash.hash(name) const contract = new EthContract(eth) - // lookup registrar + // lookup registry const chainId = Number.parseInt(await eth.net_version(), 10) - const registrarAddress = getRegistrarForChainId(chainId) - if (!registrarAddress) { - throw new Error(`EnsIpfsResolver - no known ens-ipfs registrar for chainId "${chainId}"`) + const registryAddress = getRegistryForChainId(chainId) + if (!registryAddress) { + throw new Error(`EnsIpfsResolver - no known ens-ipfs registry for chainId "${chainId}"`) } - const Registrar = contract(registrarAbi).at(registrarAddress) + const Registry = contract(registryAbi).at(registryAddress) // lookup resolver - const resolverLookupResult = await Registrar.resolver(hash) + const resolverLookupResult = await Registry.resolver(hash) const resolverAddress = resolverLookupResult[0] if (hexValueIsEmpty(resolverAddress)) { throw new Error(`EnsIpfsResolver - no resolver found for name "${name}"`) } const Resolver = contract(resolverAbi).at(resolverAddress) - // lookup content id - const contentLookupResult = await Resolver.content(hash) - const contentHash = contentLookupResult[0] - if (hexValueIsEmpty(contentHash)) { - throw new Error(`EnsIpfsResolver - no content ID found for name "${name}"`) + + const isEIP1577Compliant = await Resolver.supportsInterface('0xbc1c58d1') + const isLegacyResolver = await Resolver.supportsInterface('0xd8389dc5') + if (isEIP1577Compliant[0]) { + const contentLookupResult = await Resolver.contenthash(hash) + const rawContentHash = contentLookupResult[0] + const decodedContentHash = contentHash.decode(rawContentHash) + const type = contentHash.getCodec(rawContentHash) + return {type: type, hash: decodedContentHash} + } + if (isLegacyResolver[0]) { + // lookup content id + const contentLookupResult = await Resolver.content(hash) + const content = contentLookupResult[0] + if (hexValueIsEmpty(content)) { + throw new Error(`EnsIpfsResolver - no content ID found for name "${name}"`) + } + return {type: 'swarm-ns', hash: content.slice(2)} } - const nonPrefixedHex = contentHash.slice(2) - const buffer = multihash.fromHexString(nonPrefixedHex) - const contentId = multihash.toB58String(multihash.encode(buffer, 'sha2-256')) - return contentId + throw new Error(`EnsIpfsResolver - the resolver for name "${name}" is not standard, it should either supports contenthash() or content()`) } function hexValueIsEmpty (value) { return [undefined, null, '0x', '0x0', '0x0000000000000000000000000000000000000000000000000000000000000000'].includes(value) } -function getRegistrarForChainId (chainId) { +function getRegistryForChainId (chainId) { switch (chainId) { // mainnet case 1: @@ -50,5 +60,11 @@ function getRegistrarForChainId (chainId) { // ropsten case 3: return '0x112234455c3a32fd11230c42e7bccd4a84e02010' + // rinkeby + case 4: + return '0xe7410170f87102df0055eb195163a03b7f2bff4a' + // goerli + case 5: + return '0x112234455c3a32fd11230c42e7bccd4a84e02010' } } diff --git a/app/scripts/lib/ens-ipfs/setup.js b/app/scripts/lib/ens-ipfs/setup.js index df756d0f7..82679db5d 100644 --- a/app/scripts/lib/ens-ipfs/setup.js +++ b/app/scripts/lib/ens-ipfs/setup.js @@ -37,27 +37,25 @@ function setupEnsIpfsResolver ({ provider }) { async function attemptResolve ({ tabId, name, path, search }) { extension.tabs.update(tabId, { url: `loading.html` }) + let url = `https://manager.ens.domains/name/${name}` try { - const ipfsContentId = await resolveEnsToIpfsContentId({ provider, name }) - const url = `https://gateway.ipfs.io/ipfs/${ipfsContentId}${path}${search || ''}` - try { - // check if ipfs gateway has result - const response = await fetch(url, { method: 'HEAD' }) - // if failure, redirect to 404 page - if (response.status !== 200) { - extension.tabs.update(tabId, { url: '404.html' }) - return + const {type, hash} = await resolveEnsToIpfsContentId({ provider, name }) + if (type === 'ipfs-ns') { + const resolvedUrl = `https://gateway.ipfs.io/ipfs/${hash}${path}${search || ''}` + try { + // check if ipfs gateway has result + const response = await fetch(resolvedUrl, { method: 'HEAD' }) + if (response.status === 200) url = resolvedUrl + } catch (err) { + console.warn(err) } - // otherwise redirect to the correct page - extension.tabs.update(tabId, { url }) - } catch (err) { - console.warn(err) - // if HEAD fetch failed, redirect so user can see relevant error page - extension.tabs.update(tabId, { url }) + } else if (type === 'swarm-ns') { + url = `https://swarm-gateways.net/bzz:/${hash}${path}${search || ''}` } } catch (err) { console.warn(err) - extension.tabs.update(tabId, { url: `error.html?name=${name}` }) + } finally { + extension.tabs.update(tabId, { url }) } } } diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 242028c92..0b6f5fcb5 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -26,7 +26,6 @@ const KeyringController = require('eth-keyring-controller') const NetworkController = require('./controllers/network') const PreferencesController = require('./controllers/preferences') const AppStateController = require('./controllers/app-state') -const CurrencyController = require('./controllers/currency') const InfuraController = require('./controllers/infura') const CachedBalancesController = require('./controllers/cached-balances') const RecentBlocksController = require('./controllers/recent-blocks') @@ -56,6 +55,7 @@ const ethUtil = require('ethereumjs-util') const sigUtil = require('eth-sig-util') const { AddressBookController, + CurrencyRateController, ShapeShiftController, PhishingController, } = require('gaba') @@ -109,11 +109,7 @@ module.exports = class MetamaskController extends EventEmitter { }) // currency controller - this.currencyController = new CurrencyController({ - initState: initState.CurrencyController, - }) - this.currencyController.updateConversionRate() - this.currencyController.scheduleConversionInterval() + this.currencyRateController = new CurrencyRateController(undefined, initState.CurrencyController) // infura controller this.infuraController = new InfuraController({ @@ -130,7 +126,7 @@ module.exports = class MetamaskController extends EventEmitter { // token exchange rate tracker this.tokenRatesController = new TokenRatesController({ - currency: this.currencyController.store, + currency: this.currencyRateController, preferences: this.preferencesController.store, }) @@ -232,8 +228,7 @@ module.exports = class MetamaskController extends EventEmitter { }) this.networkController.on('networkDidChange', () => { this.balancesController.updateAllBalances() - const currentCurrency = this.currencyController.getCurrentCurrency() - this.setCurrentCurrency(currentCurrency, function () {}) + this.setCurrentCurrency(this.currencyRateController.state.currentCurrency, function () {}) }) this.balancesController.updateAllBalances() @@ -262,7 +257,7 @@ module.exports = class MetamaskController extends EventEmitter { KeyringController: this.keyringController.store, PreferencesController: this.preferencesController.store, AddressBookController: this.addressBookController, - CurrencyController: this.currencyController.store, + CurrencyController: this.currencyRateController, ShapeShiftController: this.shapeshiftController, NetworkController: this.networkController.store, InfuraController: this.infuraController.store, @@ -284,7 +279,7 @@ module.exports = class MetamaskController extends EventEmitter { PreferencesController: this.preferencesController.store, RecentBlocksController: this.recentBlocksController.store, AddressBookController: this.addressBookController, - CurrencyController: this.currencyController.store, + CurrencyController: this.currencyRateController, ShapeshiftController: this.shapeshiftController, InfuraController: this.infuraController.store, ProviderApprovalController: this.providerApprovalController.store, @@ -1596,16 +1591,13 @@ module.exports = class MetamaskController extends EventEmitter { setCurrentCurrency (currencyCode, cb) { const { ticker } = this.networkController.getNetworkConfig() try { - this.currencyController.setNativeCurrency(ticker) - this.currencyController.setCurrentCurrency(currencyCode) - this.currencyController.updateConversionRate() - const data = { - nativeCurrency: ticker || 'ETH', - conversionRate: this.currencyController.getConversionRate(), - currentCurrency: this.currencyController.getCurrentCurrency(), - conversionDate: this.currencyController.getConversionDate(), + const currencyState = { + nativeCurrency: ticker, + currentCurrency: currencyCode, } - cb(null, data) + this.currencyRateController.update(currencyState) + this.currencyRateController.configure(currencyState) + cb(null, this.currencyRateController.state) } catch (err) { cb(err) } |