aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.eslintrc1
-rw-r--r--.jshintrc27
-rw-r--r--CHANGELOG.md4
-rw-r--r--README.md1
-rw-r--r--app/_locales/cs/messages.json912
-rw-r--r--app/_locales/index.json1
-rw-r--r--app/scripts/background.js68
-rw-r--r--app/scripts/controllers/blacklist.js1
-rw-r--r--app/scripts/controllers/currency.js1
-rw-r--r--app/scripts/controllers/infura.js1
-rw-r--r--app/scripts/controllers/network.js1
-rw-r--r--app/scripts/controllers/recent-blocks.js1
-rw-r--r--app/scripts/controllers/shapeshift.js1
-rw-r--r--app/scripts/controllers/token-rates.js77
-rw-r--r--app/scripts/controllers/transactions.js1
-rw-r--r--app/scripts/inpage.js7
-rw-r--r--app/scripts/lib/ComposableObservableStore.js49
-rw-r--r--app/scripts/lib/createLoggerMiddleware.js2
-rw-r--r--app/scripts/lib/enums.js9
-rw-r--r--app/scripts/lib/environment-type.js10
-rw-r--r--app/scripts/lib/is-popup-or-notification.js12
-rw-r--r--app/scripts/lib/local-store.js1
-rw-r--r--app/scripts/lib/personal-message-manager.js1
-rw-r--r--app/scripts/lib/seed-phrase-verifier.js1
-rw-r--r--app/scripts/lib/typed-message-manager.js2
-rw-r--r--app/scripts/lib/util.js32
-rw-r--r--app/scripts/metamask-controller.js123
-rw-r--r--app/scripts/ui.js8
-rw-r--r--docs/team.md78
-rw-r--r--old-ui/app/app.js1
-rw-r--r--old-ui/app/components/ens-input.js1
-rw-r--r--old-ui/app/components/pending-tx.js1
-rw-r--r--old-ui/app/components/token-list.js1
-rw-r--r--old-ui/app/conf-tx.js6
-rw-r--r--old-ui/lib/tx-helper.js1
-rw-r--r--package-lock.json20
-rw-r--r--package.json2
-rw-r--r--test/setup.js5
-rw-r--r--test/unit/ComposableObservableStore.js35
-rw-r--r--test/unit/token-rates-controller.js29
-rw-r--r--ui/app/actions.js25
-rw-r--r--ui/app/app.js1
-rw-r--r--ui/app/components/ens-input.js1
-rw-r--r--ui/app/components/modals/modal.js11
-rw-r--r--ui/app/components/pages/home.js1
-rw-r--r--ui/app/components/pages/keychains/restore-vault.js1
-rw-r--r--ui/app/components/pages/unlock.js5
-rw-r--r--ui/app/components/pending-tx/confirm-send-token.js8
-rw-r--r--ui/app/components/send/send-v2-container.js1
-rw-r--r--ui/app/components/token-balance.js1
-rw-r--r--ui/app/components/token-cell.js20
-rw-r--r--ui/app/components/token-list.js1
-rw-r--r--ui/app/components/tx-list-item.js18
-rw-r--r--ui/app/conf-tx.js1
-rw-r--r--ui/app/first-time/init-menu.js5
-rw-r--r--ui/app/keychains/hd/restore-vault.js1
-rw-r--r--ui/app/main-container.js1
-rw-r--r--ui/app/reducers/app.js1
-rw-r--r--ui/app/reducers/metamask.js17
-rw-r--r--ui/app/selectors.js19
-rw-r--r--ui/app/send-v2.js11
-rw-r--r--ui/app/unlock.js5
-rw-r--r--ui/index.js3
-rw-r--r--ui/lib/tx-helper.js1
64 files changed, 1408 insertions, 286 deletions
diff --git a/.eslintrc b/.eslintrc
index 8af71bf4e..511d6f216 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -35,7 +35,6 @@
"globals": {
"document": false,
- "log": true,
"navigator": false,
"web3": true,
"window": false
diff --git a/.jshintrc b/.jshintrc
deleted file mode 100644
index 6dc5ecc6b..000000000
--- a/.jshintrc
+++ /dev/null
@@ -1,27 +0,0 @@
-{
- "node": true,
- "browser": true,
- "esnext": true,
- "bitwise": true,
- "camelcase": true,
- "curly": true,
- "eqeqeq": true,
- "immed": true,
- "indent": 2,
- "latedef": true,
- "newcap": true,
- "noarg": true,
- "quotmark": "single",
- "regexp": true,
- "undef": true,
- "unused": true,
- "strict": true,
- "trailing": true,
- "smarttabs": true,
- "globals" : {
- "chrome": true,
- "crypto": true,
- "describe": true,
- "it": true
- }
-}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7f8c1c6b8..9275f6c7a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,7 +2,9 @@
## Current Master
-- Improved performance of 3D fox logo.
+- Improved performance of 3D fox logo
+- Fetch token prices based on contract address, not symbol
+- Fix bug that prevents setting language locale in settings.
## 4.5.5 Fri Apr 06 2018
diff --git a/README.md b/README.md
index fc7de00e2..ca25fc0b9 100644
--- a/README.md
+++ b/README.md
@@ -69,6 +69,7 @@ To write tests that will be run in the browser using QUnit, add your test files
- [How to develop a live-reloading UI](./docs/ui-dev-mode.md)
- [How to add a new translation to MetaMask](./docs/translating-guide.md)
- [Publishing Guide](./docs/publishing.md)
+- [The MetaMask Team](./docs/team.md)
- [How to develop an in-browser mocked UI](./docs/ui-mock-mode.md)
- [How to live reload on local dependency changes](./docs/developing-on-deps.md)
- [How to add new networks to the Provider Menu](./docs/adding-new-networks.md)
diff --git a/app/_locales/cs/messages.json b/app/_locales/cs/messages.json
new file mode 100644
index 000000000..6a4ebc8a5
--- /dev/null
+++ b/app/_locales/cs/messages.json
@@ -0,0 +1,912 @@
+{
+ "accept": {
+ "message": "Přijmout"
+ },
+ "account": {
+ "message": "Účet"
+ },
+ "accountDetails": {
+ "message": "Detaily účtu"
+ },
+ "accountName": {
+ "message": "Název účtu"
+ },
+ "address": {
+ "message": "Adresa"
+ },
+ "addCustomToken": {
+ "message": "Přidat vlastní token"
+ },
+ "addToken": {
+ "message": "Přidat token"
+ },
+ "addTokens": {
+ "message": "Přidat tokeny"
+ },
+ "amount": {
+ "message": "Částka"
+ },
+ "amountPlusGas": {
+ "message": "Částka + palivo"
+ },
+ "appDescription": {
+ "message": "Ethereum rozšíření prohlížeče",
+ "description": "The description of the application"
+ },
+ "appName": {
+ "message": "MetaMask",
+ "description": "The name of the application"
+ },
+ "approved": {
+ "message": "Schváleno"
+ },
+ "attemptingConnect": {
+ "message": "Pokouším se připojit k blockchainu."
+ },
+ "attributions": {
+ "message": "Zásluhy"
+ },
+ "available": {
+ "message": "Dostupné"
+ },
+ "back": {
+ "message": "Zpět"
+ },
+ "balance": {
+ "message": "Zůstatek:"
+ },
+ "balances": {
+ "message": "Zůstatek tokenu"
+ },
+ "balanceIsInsufficientGas": {
+ "message": "Nedostatek prostředků pro aktuální množství paliva"
+ },
+ "beta": {
+ "message": "BETA"
+ },
+ "betweenMinAndMax": {
+ "message": "musí být větší nebo roven $1 a menší nebo roven $2.",
+ "description": "helper for inputting hex as decimal input"
+ },
+ "blockiesIdenticon": {
+ "message": "Použít Blockies Identicon"
+ },
+ "borrowDharma": {
+ "message": "Pújčit si přes Dharma (Beta)"
+ },
+ "builtInCalifornia": {
+ "message": "MetaMask je navržen a vytvořen v Kalifornii."
+ },
+ "buy": {
+ "message": "Koupit"
+ },
+ "buyCoinbase": {
+ "message": "Nákup na Coinbase"
+ },
+ "buyCoinbaseExplainer": {
+ "message": "Coinbase je světově nejoblíbenější místo k nákupu a prodeji bitcoinu, etherea nebo litecoinu."
+ },
+ "ok": {
+ "message": "Ok"
+ },
+ "cancel": {
+ "message": "Zrušit"
+ },
+ "classicInterface": {
+ "message": "Použít klasické rozhraní"
+ },
+ "clickCopy": {
+ "message": "Kliknutím zkopírovat"
+ },
+ "confirm": {
+ "message": "Potvrdit"
+ },
+ "confirmed": {
+ "message": "Potvrzeno"
+ },
+ "confirmContract": {
+ "message": "Potvrdit kontrakt"
+ },
+ "confirmPassword": {
+ "message": "Potvrdit heslo"
+ },
+ "confirmTransaction": {
+ "message": "Potvrdit transakci"
+ },
+ "continue": {
+ "message": "Pokračovat"
+ },
+ "continueToCoinbase": {
+ "message": "Přejít na Coinbase"
+ },
+ "contractDeployment": {
+ "message": "Nasazení kontraktu"
+ },
+ "conversionProgress": {
+ "message": "Provádí se převod"
+ },
+ "copiedButton": {
+ "message": "Zkopírováno"
+ },
+ "copiedClipboard": {
+ "message": "Zkopírováno do schránky"
+ },
+ "copiedExclamation": {
+ "message": "Zkopírováno!"
+ },
+ "copiedSafe": {
+ "message": "Zkopíroval jsem to na bezpečné místo"
+ },
+ "copy": {
+ "message": "Kopírovat"
+ },
+ "copyToClipboard": {
+ "message": "Kopírovat do schránky"
+ },
+ "copyButton": {
+ "message": " Kopírovat "
+ },
+ "copyPrivateKey": {
+ "message": "Toto je váš privátní klíč (kliknutím zkopírujte)"
+ },
+ "create": {
+ "message": "Vytvořit"
+ },
+ "createAccount": {
+ "message": "Vytvořit účet"
+ },
+ "createDen": {
+ "message": "Vytvořit"
+ },
+ "crypto": {
+ "message": "Krypto",
+ "description": "Exchange type (cryptocurrencies)"
+ },
+ "currentConversion": {
+ "message": "Aktuální převod"
+ },
+ "currentNetwork": {
+ "message": "Aktuální síť"
+ },
+ "customGas": {
+ "message": "Nastavit palivo"
+ },
+ "customToken": {
+ "message": "Vlastní token"
+ },
+ "customize": {
+ "message": "Nastavit"
+ },
+ "customRPC": {
+ "message": "Vlastní RPC"
+ },
+ "decimalsMustZerotoTen": {
+ "message": "Desetinných míst musí být od 0 do 36."
+ },
+ "decimal": {
+ "message": "Počet desetinných míst přesnosti"
+ },
+ "defaultNetwork": {
+ "message": "Výchozí síť pro Etherové transakce je Main Net."
+ },
+ "denExplainer": {
+ "message": "Váš DEN je heslem šifrované uložiště v MetaMasku."
+ },
+ "deposit": {
+ "message": "Vklad"
+ },
+ "depositBTC": {
+ "message": "Vložte BTC na níže uvedenou adresu:"
+ },
+ "depositCoin": {
+ "message": "Vložte $1 na níže uvedenou adresu",
+ "description": "Tells the user what coin they have selected to deposit with shapeshift"
+ },
+ "depositEth": {
+ "message": "Vložit Eth"
+ },
+ "depositEther": {
+ "message": "Vložit Ether"
+ },
+ "depositFiat": {
+ "message": "Vklad s fiat měnou"
+ },
+ "depositFromAccount": {
+ "message": "Vložte z jiného účtu"
+ },
+ "depositShapeShift": {
+ "message": "Vklad přes ShapeShift"
+ },
+ "depositShapeShiftExplainer": {
+ "message": "Pokud vlastníte jiné kryptoměny, můžete je směnit Ether a vložit ho přímo do peněženky MetaMask. Bez založení účtu."
+ },
+ "details": {
+ "message": "Podrobnosti"
+ },
+ "directDeposit": {
+ "message": "Přímý vklad"
+ },
+ "directDepositEther": {
+ "message": "Vložit Ether přímo"
+ },
+ "directDepositEtherExplainer": {
+ "message": "Pokud už vlastníte nějaký Ether, nejrychleji ho dostanete do peněženky přímým vkladem."
+ },
+ "done": {
+ "message": "Hotovo"
+ },
+ "downloadStateLogs": {
+ "message": "Stáhnout stavové protokoly"
+ },
+ "dropped": {
+ "message": "Zrušeno"
+ },
+ "edit": {
+ "message": "Upravit"
+ },
+ "editAccountName": {
+ "message": "Upravit název účtu"
+ },
+ "emailUs": {
+ "message": "Napište nám e-mail!"
+ },
+ "encryptNewDen": {
+ "message": "Zašifrujte svůj nový DEN"
+ },
+ "enterPassword": {
+ "message": "Zadejte heslo"
+ },
+ "enterPasswordConfirm": {
+ "message": "Zadejte heslo k potvrzení"
+ },
+ "passwordNotLongEnough": {
+ "message": "Heslo není dost dlouhé"
+ },
+ "passwordsDontMatch": {
+ "message": "Hesla nejsou stejná"
+ },
+ "etherscanView": {
+ "message": "Prohlédněte si účet na Etherscan"
+ },
+ "exchangeRate": {
+ "message": "Směnný kurz"
+ },
+ "exportPrivateKey": {
+ "message": "Exportovat privátní klíč"
+ },
+ "exportPrivateKeyWarning": {
+ "message": "Exportujte privátní klíč na vlastní riziko."
+ },
+ "failed": {
+ "message": "Neúspěšné"
+ },
+ "fiat": {
+ "message": "FIAT",
+ "description": "Exchange type"
+ },
+ "fileImportFail": {
+ "message": "Import souboru nefunguje? Klikněte sem!",
+ "description": "Helps user import their account from a JSON file"
+ },
+ "followTwitter": {
+ "message": "Sledujte nás na Twitteru"
+ },
+ "from": {
+ "message": "Od"
+ },
+ "fromToSame": {
+ "message": "Adresy odesílatele a příjemce nemohou být stejné"
+ },
+ "fromShapeShift": {
+ "message": "Z ShapeShift"
+ },
+ "gas": {
+ "message": "Palivo",
+ "description": "Short indication of gas cost"
+ },
+ "gasFee": {
+ "message": "Poplatek za palivo"
+ },
+ "gasLimit": {
+ "message": "Limit paliva"
+ },
+ "gasLimitCalculation": {
+ "message": "Počítáme doporučený limit paliva na základě úspěšnosti v síti."
+ },
+ "gasLimitRequired": {
+ "message": "Limit paliva je povinný"
+ },
+ "gasLimitTooLow": {
+ "message": "Limit paliva musí být alespoň 21000"
+ },
+ "generatingSeed": {
+ "message": "Generuji klíčovou frázi..."
+ },
+ "gasPrice": {
+ "message": "Cena paliva (GWEI)"
+ },
+ "gasPriceCalculation": {
+ "message": "Počítáme doporučenou cenu paliva na základě úspěšnosti v síti."
+ },
+ "gasPriceRequired": {
+ "message": "Cena paliva je povinná"
+ },
+ "getEther": {
+ "message": "Získejte Ether"
+ },
+ "getEtherFromFaucet": {
+ "message": "Získejte Ether z faucetu za $1.",
+ "description": "Displays network name for Ether faucet"
+ },
+ "greaterThanMin": {
+ "message": "musí být větší nebo roven $1.",
+ "description": "helper for inputting hex as decimal input"
+ },
+ "here": {
+ "message": "zde",
+ "description": "as in -click here- for more information (goes with troubleTokenBalances)"
+ },
+ "hereList": {
+ "message": "Tady je seznam!!!!"
+ },
+ "hide": {
+ "message": "Skrýt"
+ },
+ "hideToken": {
+ "message": "Skrýt token"
+ },
+ "hideTokenPrompt": {
+ "message": "Skrýt token?"
+ },
+ "howToDeposit": {
+ "message": "Jakým způsobem chcete vložit Ether?"
+ },
+ "holdEther": {
+ "message": "Dovoluje vám držet ether a tokeny a slouží jako most k decentralizovaným aplikacím."
+ },
+ "import": {
+ "message": "Import",
+ "description": "Button to import an account from a selected file"
+ },
+ "importAccount": {
+ "message": "Import účtu"
+ },
+ "importAccountMsg": {
+ "message":"Importované účty nebudou spojeny s vaší původní MetaMaskovou klíčovou frází. Zjistěte více o importovaných účtech "
+ },
+ "importAnAccount": {
+ "message": "Import účtu"
+ },
+ "importDen": {
+ "message": "Import existujícího DEN"
+ },
+ "imported": {
+ "message": "Importováno",
+ "description": "status showing that an account has been fully loaded into the keyring"
+ },
+ "infoHelp": {
+ "message": "Informace a nápověda"
+ },
+ "insufficientFunds": {
+ "message": "Nedostatek finančních prostředků."
+ },
+ "insufficientTokens": {
+ "message": "Nedostatek tokenů."
+ },
+ "invalidAddress": {
+ "message": "Neplatná adresa"
+ },
+ "invalidAddressRecipient": {
+ "message": "Adresa příjemce je neplatná"
+ },
+ "invalidGasParams": {
+ "message": "Neplatná parametry paliva"
+ },
+ "invalidInput": {
+ "message": "Neplatný vstup."
+ },
+ "invalidRequest": {
+ "message": "Neplatný požadavek"
+ },
+ "invalidRPC": {
+ "message": "Neplatné RPC URI"
+ },
+ "jsonFail": {
+ "message": "Něco se pokazilo. Prosím, ujistěte se, že váš JSON soubor má správný formát."
+ },
+ "jsonFile": {
+ "message": "JSON soubor",
+ "description": "format for importing an account"
+ },
+ "keepTrackTokens": {
+ "message": "Udržujte si záznamy o tokenech, které jste koupili s účtem v MetaMasku."
+ },
+ "kovan": {
+ "message": "Kovan Test Network"
+ },
+ "knowledgeDataBase": {
+ "message": "Navštivte naši Knowledge Base"
+ },
+ "max": {
+ "message": "Max"
+ },
+ "learnMore": {
+ "message": "Zjistěte více."
+ },
+ "lessThanMax": {
+ "message": "musí být menší nebo roven $1.",
+ "description": "helper for inputting hex as decimal input"
+ },
+ "likeToAddTokens": {
+ "message": "Chcete přidat tyto tokeny?"
+ },
+ "links": {
+ "message": "Odkazy"
+ },
+ "limit": {
+ "message": "Limit"
+ },
+ "loading": {
+ "message": "Načítám..."
+ },
+ "loadingTokens": {
+ "message": "Načítám tokeny..."
+ },
+ "localhost": {
+ "message": "Localhost 8545"
+ },
+ "login": {
+ "message": "Přihlásit"
+ },
+ "logout": {
+ "message": "Odhlásit"
+ },
+ "loose": {
+ "message": "Nevázané"
+ },
+ "loweCaseWords": {
+ "message": "slova klíčové fráze mají pouze malá písmena"
+ },
+ "mainnet": {
+ "message": "Main Ethereum Network"
+ },
+ "message": {
+ "message": "Zpráva"
+ },
+ "metamaskDescription": {
+ "message": "MetaMask je bezpečný osobní trezor pro Ethereum."
+ },
+ "min": {
+ "message": "Minimum"
+ },
+ "myAccounts": {
+ "message": "Moje účty"
+ },
+ "mustSelectOne": {
+ "message": "Musíte zvolit aspoň 1 token."
+ },
+ "needEtherInWallet": {
+ "message": "Potřebujete Ether v peněžence, abyste mohli pomocí MetaMasku interagovat s decentralizovanými aplikacemi."
+ },
+ "needImportFile": {
+ "message": "Musíte zvolit soubor k importu.",
+ "description": "User is important an account and needs to add a file to continue"
+ },
+ "needImportPassword": {
+ "message": "Musíte zadat heslo pro zvolený soubor.",
+ "description": "Password and file needed to import an account"
+ },
+ "negativeETH": {
+ "message": "Nelze odeslat zápornou částku ETH."
+ },
+ "networks": {
+ "message": "Sítě"
+ },
+ "newAccount": {
+ "message": "Nový účet"
+ },
+ "newAccountNumberName": {
+ "message": "Účet $1",
+ "description": "Default name of next account to be created on create account screen"
+ },
+ "newContract": {
+ "message": "Nový kontrakt"
+ },
+ "newPassword": {
+ "message": "Nové heslo (min 8 znaků)"
+ },
+ "newRecipient": {
+ "message": "Nový příjemce"
+ },
+ "newRPC": {
+ "message": "Nová RPC URL"
+ },
+ "next": {
+ "message": "Další"
+ },
+ "noAddressForName": {
+ "message": "Pro toto jméno nebyla nastavena žádná adresa."
+ },
+ "noDeposits": {
+ "message": "Žádný vklad"
+ },
+ "noTransactionHistory": {
+ "message": "Žádná historie transakcí."
+ },
+ "noTransactions": {
+ "message": "Žádné transakce"
+ },
+ "notStarted": {
+ "message": "Nezačalo"
+ },
+ "oldUI": {
+ "message": "Staré rozhraní"
+ },
+ "oldUIMessage": {
+ "message": "Vrátili jste se ke starému rozhraní. Můžete přepnout na nové rozhraní v nastavení v pravém horním menu."
+ },
+ "or": {
+ "message": "nebo",
+ "description": "choice between creating or importing a new account"
+ },
+ "passwordCorrect": {
+ "message": "Ujistěte se, že je vaše heslo správně."
+ },
+ "passwordMismatch": {
+ "message": "hesla nesouhlasí",
+ "description": "in password creation process, the two new password fields did not match"
+ },
+ "passwordShort": {
+ "message": "heslo je krátké",
+ "description": "in password creation process, the password is not long enough to be secure"
+ },
+ "pastePrivateKey": {
+ "message": "Vložte zde svůj privátní klíč:",
+ "description": "For importing an account from a private key"
+ },
+ "pasteSeed": {
+ "message": "Svou klíčovou frázi vložte zde!"
+ },
+ "personalAddressDetected": {
+ "message": "Detekována osobní adresa. Zadejte adresu kontraktu tokenu."
+ },
+ "pleaseReviewTransaction": {
+ "message": "Zkontrolujte si transakci."
+ },
+ "popularTokens": {
+ "message": "Oblíbené tokeny"
+ },
+ "privacyMsg": {
+ "message": "Zásady ochrany osobních údajů"
+ },
+ "privateKey": {
+ "message": "Privátní klíč",
+ "description": "select this type of file to use to import an account"
+ },
+ "privateKeyWarning": {
+ "message": "Upozornění: Nikdy nezveřejněte tento klíč. Kdokoli může s vaším privátním klíčem odcizit vaše aktiva z účtu."
+ },
+ "privateNetwork": {
+ "message": "Soukromá síť"
+ },
+ "qrCode": {
+ "message": "Ukázat QR kód"
+ },
+ "readdToken": {
+ "message": "Tento token můžete v budoucnu přidat zpět s „Přidat token“ v nastavení účtu."
+ },
+ "readMore": {
+ "message": "Přečtěte si více zde."
+ },
+ "readMore2": {
+ "message": "Přečtěte si více."
+ },
+ "receive": {
+ "message": "Obrdžet"
+ },
+ "recipientAddress": {
+ "message": "Adresa příjemce"
+ },
+ "refundAddress": {
+ "message": "Adresa pro vrácení peněz"
+ },
+ "rejected": {
+ "message": "Odmítnuto"
+ },
+ "resetAccount": {
+ "message": "Resetovat účet"
+ },
+ "restoreFromSeed": {
+ "message": "Obnovit z seed fráze"
+ },
+ "restoreVault": {
+ "message": "Obnovit trezor"
+ },
+ "required": {
+ "message": "Povinné"
+ },
+ "retryWithMoreGas": {
+ "message": "Opakujte s vyšší cenou paliva"
+ },
+ "walletSeed": {
+ "message": "Klíčová fráze peněženky"
+ },
+ "revealSeedWords": {
+ "message": "Zobrazit slova klíčové fráze"
+ },
+ "revealSeedWordsWarning": {
+ "message": "Nebnovujte slova klíčové fráze na veřejnosti! Tato slova mohou být použita k odcizení veškerých vyašich účtů."
+ },
+ "revert": {
+ "message": "Zvrátit"
+ },
+ "rinkeby": {
+ "message": "Rinkeby Test Network"
+ },
+ "ropsten": {
+ "message": "Ropsten Test Network"
+ },
+ "currentRpc": {
+ "message": "Současné RPC"
+ },
+ "connectingToMainnet": {
+ "message": "Připojuji se k Main Ethereum Network"
+ },
+ "connectingToRopsten": {
+ "message": "Připojuji se k Ropsten Test Network"
+ },
+ "connectingToKovan": {
+ "message": "Připojuji se k Kovan Test Network"
+ },
+ "connectingToRinkeby": {
+ "message": "Připojuji se k Rinkeby Test Network"
+ },
+ "connectingToUnknown": {
+ "message": "Připojuji se k neznámé síti"
+ },
+ "sampleAccountName": {
+ "message": "Např. můj nový účet",
+ "description": "Help user understand concept of adding a human-readable name to their account"
+ },
+ "save": {
+ "message": "Uložit"
+ },
+ "reprice_title": {
+ "message": "Změnit cenu transakce"
+ },
+ "reprice_subtitle": {
+ "message": "Navyšte cenu paliva ve snaze k přepsání a urychlení vyší transakce"
+ },
+ "saveAsFile": {
+ "message": "Uložit do souboru",
+ "description": "Account export process"
+ },
+ "saveSeedAsFile": {
+ "message": "Uložit slova klíčové fráze do souboru"
+ },
+ "search": {
+ "message": "Hledat"
+ },
+ "secretPhrase": {
+ "message": "Zadejte svých 12 slov tajné fráze k obnovení trezoru."
+ },
+ "newPassword8Chars": {
+ "message": "Nové heslo (min 8 znaků)"
+ },
+ "seedPhraseReq": {
+ "message": "klíčové fráze mají 12 slov"
+ },
+ "select": {
+ "message": "Vybrat"
+ },
+ "selectCurrency": {
+ "message": "Vybrat měnu"
+ },
+ "selectService": {
+ "message": "Vybrat službu"
+ },
+ "selectType": {
+ "message": "Vybrat typ"
+ },
+ "send": {
+ "message": "Odeslat"
+ },
+ "sendETH": {
+ "message": "Odeslat ETH"
+ },
+ "sendTokens": {
+ "message": "Odeslat tokeny"
+ },
+ "onlySendToEtherAddress": {
+ "message": "Posílejte jen ETH na Ethereum adresu."
+ },
+ "searchTokens": {
+ "message": "Hledat tokeny"
+ },
+ "sendTokensAnywhere": {
+ "message": "Posílejte tokeny komukoli s Ethereum účtem"
+ },
+ "settings": {
+ "message": "Nastavení"
+ },
+ "info": {
+ "message": "Informace"
+ },
+ "shapeshiftBuy": {
+ "message": "Nakoupit na ShapeShift"
+ },
+ "showPrivateKeys": {
+ "message": "Zobrazit privátní klíče"
+ },
+ "showQRCode": {
+ "message": "Zobrazit QR kód"
+ },
+ "sign": {
+ "message": "Podepsat"
+ },
+ "signed": {
+ "message": "Podepsáno"
+ },
+ "signMessage": {
+ "message": "Podepsat zprávu"
+ },
+ "signNotice": {
+ "message": "Podepsání zprávy může mít \nnebezpečný vedlejší učinek. Podepisujte zprávy pouze ze \nstránek, kterým plně důvěřujete celým svým účtem.\n Tato nebezpečná metoda bude odebrána v budoucí verzi. "
+ },
+ "sigRequest": {
+ "message": "Požadavek podpisu"
+ },
+ "sigRequested": {
+ "message": "Požádáno o podpis"
+ },
+ "spaceBetween": {
+ "message": "mezi slovy může být pouze mezera"
+ },
+ "status": {
+ "message": "Stav"
+ },
+ "stateLogs": {
+ "message": "Stavové protokoly"
+ },
+ "stateLogsDescription": {
+ "message": "Stavové protokoly obsahují vaše veřejné adresy účtů a odeslané transakce."
+ },
+ "stateLogError": {
+ "message": "Chyba během získávání stavových protokolů."
+ },
+ "submit": {
+ "message": "Odeslat"
+ },
+ "submitted": {
+ "message": "Odesláno"
+ },
+ "supportCenter": {
+ "message": "Navštivte naše centrum podpory"
+ },
+ "symbolBetweenZeroTen": {
+ "message": "Symbol musí být mezi 0 a 10 znaky."
+ },
+ "takesTooLong": {
+ "message": "Trvá to dlouho?"
+ },
+ "terms": {
+ "message": "Podmínky použití"
+ },
+ "testFaucet": {
+ "message": "Testovací faucet"
+ },
+ "to": {
+ "message": "Komu: "
+ },
+ "toETHviaShapeShift": {
+ "message": "$1 na ETH přes ShapeShift",
+ "description": "system will fill in deposit type in start of message"
+ },
+ "tokenAddress": {
+ "message": "Adresa tokenu"
+ },
+ "tokenAlreadyAdded": {
+ "message": "Token byl už přidán."
+ },
+ "tokenBalance": {
+ "message": "Váš zůstatek tokenu je:"
+ },
+ "tokenSelection": {
+ "message": "Vyhledejte token nebo je vyberte z našeho seznamu oblíbených tokenů."
+ },
+ "tokenSymbol": {
+ "message": "Symbol tokenu"
+ },
+ "tokenWarning1": {
+ "message": "Mějte přehled o tokenech, které jste koupili s účtem MetaMasku. Pokud jste koupili tokeny s jiným účtem, tyto tokeny se zde nezobrazí."
+ },
+ "total": {
+ "message": "Celkem"
+ },
+ "transactions": {
+ "message": "transakce"
+ },
+ "transactionError": {
+ "message": "Chyba transakce. Vyhozena výjimka v kódu kontraktu."
+ },
+ "transactionMemo": {
+ "message": "Poznámka transakce (nepovinné)"
+ },
+ "transactionNumber": {
+ "message": "Číslo transakce"
+ },
+ "transfers": {
+ "message": "Převody"
+ },
+ "troubleTokenBalances": {
+ "message": "Měli jsme problém s načtením vašich tokenových zůstatků. Můžete je vidět ",
+ "description": "Followed by a link (here) to view token balances"
+ },
+ "twelveWords": {
+ "message": "Těchto 12 slov je jedinou možností, jak obnovit MetaMask účet. \nUložte je na bezpečné a neveřejné místo."
+ },
+ "typePassword": {
+ "message": "Zadejte své heslo"
+ },
+ "uiWelcome": {
+ "message": "Vítejte v novém rozhraní (Beta)"
+ },
+ "uiWelcomeMessage": {
+ "message": "Používáte nyní nové rozhraní MetaMasku. Rozhlédněte se kolem, vyzkoušejte nové funkce, jako jsou zasílání tokenů, a dejte nám vědět, pokud narazíte na problém."
+ },
+ "unapproved": {
+ "message": "Neschváleno"
+ },
+ "unavailable": {
+ "message": "Nedostupné"
+ },
+ "unknown": {
+ "message": "Neznámé"
+ },
+ "unknownNetwork": {
+ "message": "Neznámá soukromá síť"
+ },
+ "unknownNetworkId": {
+ "message": "Neznámé ID sítě"
+ },
+ "uriErrorMsg": {
+ "message": "URI vyžadují korektní HTTP/HTTPS prefix."
+ },
+ "usaOnly": {
+ "message": "jen v USA",
+ "description": "Using this exchange is limited to people inside the USA"
+ },
+ "usedByClients": {
+ "message": "Používána různými klienty"
+ },
+ "useOldUI": {
+ "message": "Použijte staré rozhraní"
+ },
+ "validFileImport": {
+ "message": "Musíte vybrat validní soubor k importu."
+ },
+ "vaultCreated": {
+ "message": "Trezor vytvořen"
+ },
+ "viewAccount": {
+ "message": "Zobrazit účet"
+ },
+ "visitWebSite": {
+ "message": "Navštivte naši stránku"
+ },
+ "warning": {
+ "message": "Varování"
+ },
+ "welcomeBeta": {
+ "message": "Vítejte v MetaMask Beta"
+ },
+ "whatsThis": {
+ "message": "Co to je?"
+ },
+ "yourSigRequested": {
+ "message": "Je vyžadován váš podpis"
+ },
+ "youSign": {
+ "message": "Podepisujete"
+ }
+}
diff --git a/app/_locales/index.json b/app/_locales/index.json
index 727504bda..facee3c35 100644
--- a/app/_locales/index.json
+++ b/app/_locales/index.json
@@ -1,4 +1,5 @@
[
+ { "code": "cs", "name": "Czech" },
{ "code": "de", "name": "German" },
{ "code": "en", "name": "English" },
{ "code": "es", "name": "Spanish" },
diff --git a/app/scripts/background.js b/app/scripts/background.js
index 5878cd2e8..6550e8944 100644
--- a/app/scripts/background.js
+++ b/app/scripts/background.js
@@ -21,12 +21,16 @@ const setupMetamaskMeshMetrics = require('./lib/setupMetamaskMeshMetrics')
const EdgeEncryptor = require('./edge-encryptor')
const getFirstPreferredLangCode = require('./lib/get-first-preferred-lang-code')
const getObjStructure = require('./lib/getObjStructure')
+const {
+ ENVIRONMENT_TYPE_POPUP,
+ ENVIRONMENT_TYPE_NOTIFICATION,
+ ENVIRONMENT_TYPE_FULLSCREEN,
+} = require('./lib/enums')
const STORAGE_KEY = 'metamask-config'
const METAMASK_DEBUG = process.env.METAMASK_DEBUG
-window.log = log
-log.setDefaultLevel(METAMASK_DEBUG ? 'debug' : 'warn')
+log.setDefaultLevel(process.env.METAMASK_DEBUG ? 'debug' : 'warn')
const platform = new ExtensionPlatform()
const notificationManager = new NotificationManager()
@@ -44,7 +48,7 @@ const isEdge = !isIE && !!window.StyleMedia
let popupIsOpen = false
let notificationIsOpen = false
-let openMetamaskTabsIDs = {}
+const openMetamaskTabsIDs = {}
// state persistence
const diskStore = new LocalStorageStore({ storageKey: STORAGE_KEY })
@@ -173,7 +177,7 @@ function setupController (initState, initLangCode) {
return versionedData
}
- function persistData(state) {
+ function persistData (state) {
if (!state) {
throw new Error('MetaMask - updated state is missing', state)
}
@@ -192,30 +196,53 @@ function setupController (initState, initLangCode) {
//
// connect to other contexts
//
-
extension.runtime.onConnect.addListener(connectRemote)
+
+ const metamaskInternalProcessHash = {
+ [ENVIRONMENT_TYPE_POPUP]: true,
+ [ENVIRONMENT_TYPE_NOTIFICATION]: true,
+ [ENVIRONMENT_TYPE_FULLSCREEN]: true,
+ }
+
+ const isClientOpenStatus = () => {
+ return popupIsOpen || Boolean(Object.keys(openMetamaskTabsIDs).length) || notificationIsOpen
+ }
+
function connectRemote (remotePort) {
- const isMetaMaskInternalProcess = remotePort.name === 'popup' || remotePort.name === 'notification'
+ const processName = remotePort.name
+ const isMetaMaskInternalProcess = metamaskInternalProcessHash[processName]
const portStream = new PortStream(remotePort)
+
if (isMetaMaskInternalProcess) {
// communication with popup
- popupIsOpen = popupIsOpen || (remotePort.name === 'popup')
+ controller.isClientOpen = true
controller.setupTrustedCommunication(portStream, 'MetaMask')
- // record popup as closed
- if (remotePort.sender.url.match(/home.html$/)) {
- openMetamaskTabsIDs[remotePort.sender.tab.id] = true
- }
- if (remotePort.name === 'popup') {
+
+ if (processName === ENVIRONMENT_TYPE_POPUP) {
+ popupIsOpen = true
+
endOfStream(portStream, () => {
popupIsOpen = false
- if (remotePort.sender.url.match(/home.html$/)) {
- openMetamaskTabsIDs[remotePort.sender.tab.id] = false
- }
+ controller.isClientOpen = isClientOpenStatus()
})
}
- if (remotePort.name === 'notification') {
+
+ if (processName === ENVIRONMENT_TYPE_NOTIFICATION) {
+ notificationIsOpen = true
+
endOfStream(portStream, () => {
notificationIsOpen = false
+ controller.isClientOpen = isClientOpenStatus()
+ })
+ }
+
+ if (processName === ENVIRONMENT_TYPE_FULLSCREEN) {
+ const tabId = remotePort.sender.tab.id
+ openMetamaskTabsIDs[tabId] = true
+
+ endOfStream(portStream, () => {
+ delete openMetamaskTabsIDs[tabId]
+ controller.isClientOpen = isClientOpenStatus()
})
}
} else {
@@ -258,10 +285,11 @@ function setupController (initState, initLangCode) {
// popup trigger
function triggerUi () {
- extension.tabs.query({ active: true }, (tabs) => {
- const currentlyActiveMetamaskTab = tabs.find(tab => openMetamaskTabsIDs[tab.id])
- if (!popupIsOpen && !currentlyActiveMetamaskTab && !notificationIsOpen) notificationManager.showPopup()
- notificationIsOpen = true
+ extension.tabs.query({ active: true }, tabs => {
+ const currentlyActiveMetamaskTab = Boolean(tabs.find(tab => openMetamaskTabsIDs[tab.id]))
+ if (!popupIsOpen && !currentlyActiveMetamaskTab && !notificationIsOpen) {
+ notificationManager.showPopup()
+ }
})
}
diff --git a/app/scripts/controllers/blacklist.js b/app/scripts/controllers/blacklist.js
index df41c90c0..d965f80b8 100644
--- a/app/scripts/controllers/blacklist.js
+++ b/app/scripts/controllers/blacklist.js
@@ -1,6 +1,7 @@
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')
diff --git a/app/scripts/controllers/currency.js b/app/scripts/controllers/currency.js
index 36b8808aa..d9e0a3e34 100644
--- a/app/scripts/controllers/currency.js
+++ b/app/scripts/controllers/currency.js
@@ -1,5 +1,6 @@
const ObservableStore = require('obs-store')
const extend = require('xtend')
+const log = require('loglevel')
// every ten minutes
const POLLING_INTERVAL = 600000
diff --git a/app/scripts/controllers/infura.js b/app/scripts/controllers/infura.js
index c6b4c9de2..8f6dd837e 100644
--- a/app/scripts/controllers/infura.js
+++ b/app/scripts/controllers/infura.js
@@ -1,5 +1,6 @@
const ObservableStore = require('obs-store')
const extend = require('xtend')
+const log = require('loglevel')
// every ten minutes
const POLLING_INTERVAL = 10 * 60 * 1000
diff --git a/app/scripts/controllers/network.js b/app/scripts/controllers/network.js
index 617456cd7..45574e673 100644
--- a/app/scripts/controllers/network.js
+++ b/app/scripts/controllers/network.js
@@ -9,6 +9,7 @@ const extend = require('xtend')
const EthQuery = require('eth-query')
const createEventEmitterProxy = require('../lib/events-proxy.js')
const networkConfig = require('../config.js')
+const log = require('loglevel')
const { OLD_UI_NETWORK_TYPE, DEFAULT_RPC } = networkConfig.enums
const INFURA_PROVIDER_TYPES = ['ropsten', 'rinkeby', 'kovan', 'mainnet']
diff --git a/app/scripts/controllers/recent-blocks.js b/app/scripts/controllers/recent-blocks.js
index 4ae3810eb..0c1ee4e38 100644
--- a/app/scripts/controllers/recent-blocks.js
+++ b/app/scripts/controllers/recent-blocks.js
@@ -2,6 +2,7 @@ const ObservableStore = require('obs-store')
const extend = require('xtend')
const BN = require('ethereumjs-util').BN
const EthQuery = require('eth-query')
+const log = require('loglevel')
class RecentBlocksController {
diff --git a/app/scripts/controllers/shapeshift.js b/app/scripts/controllers/shapeshift.js
index 3bbfaa1c5..b38b3812d 100644
--- a/app/scripts/controllers/shapeshift.js
+++ b/app/scripts/controllers/shapeshift.js
@@ -1,5 +1,6 @@
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
diff --git a/app/scripts/controllers/token-rates.js b/app/scripts/controllers/token-rates.js
new file mode 100644
index 000000000..22e3e8154
--- /dev/null
+++ b/app/scripts/controllers/token-rates.js
@@ -0,0 +1,77 @@
+const ObservableStore = require('obs-store')
+
+// By default, poll every 3 minutes
+const DEFAULT_INTERVAL = 180 * 1000
+
+/**
+ * A controller that polls for token exchange
+ * rates based on a user's current token list
+ */
+class TokenRatesController {
+ /**
+ * Creates a TokenRatesController
+ *
+ * @param {Object} [config] - Options to configure controller
+ */
+ constructor ({ interval = DEFAULT_INTERVAL, preferences } = {}) {
+ this.store = new ObservableStore()
+ this.preferences = preferences
+ this.interval = interval
+ }
+
+ /**
+ * Updates exchange rates for all tokens
+ */
+ async updateExchangeRates () {
+ if (!this.isActive) { return }
+ const contractExchangeRates = {}
+ for (const i in this._tokens) {
+ const address = this._tokens[i].address
+ contractExchangeRates[address] = await this.fetchExchangeRate(address)
+ }
+ this.store.putState({ contractExchangeRates })
+ }
+
+ /**
+ * Fetches a token exchange rate by address
+ *
+ * @param {String} address - Token contract address
+ */
+ async fetchExchangeRate (address) {
+ try {
+ const response = await fetch(`https://exchanges.balanc3.net/prices?from=${address}&to=ETH&autoConversion=false&summaryOnly=true`)
+ const json = await response.json()
+ return json && json.length ? json[0].averagePrice : 0
+ } catch (error) { }
+ }
+
+ /**
+ * @type {Number} - Interval used to poll for exchange rates
+ */
+ set interval (interval) {
+ this._handle && clearInterval(this._handle)
+ if (!interval) { return }
+ this._handle = setInterval(() => { this.updateExchangeRates() }, interval)
+ }
+
+ /**
+ * @type {Object} - Preferences controller instance
+ */
+ set preferences (preferences) {
+ this._preferences && this._preferences.unsubscribe()
+ if (!preferences) { return }
+ this._preferences = preferences
+ this.tokens = preferences.getState().tokens
+ preferences.subscribe(({ tokens = [] }) => { this.tokens = tokens })
+ }
+
+ /**
+ * @type {Array} - Array of token objects with contract addresses
+ */
+ set tokens (tokens) {
+ this._tokens = tokens
+ this.updateExchangeRates()
+ }
+}
+
+module.exports = TokenRatesController
diff --git a/app/scripts/controllers/transactions.js b/app/scripts/controllers/transactions.js
index 336b0d8f7..c8211ebd7 100644
--- a/app/scripts/controllers/transactions.js
+++ b/app/scripts/controllers/transactions.js
@@ -7,6 +7,7 @@ const TransactionStateManager = require('../lib/tx-state-manager')
const TxGasUtil = require('../lib/tx-gas-utils')
const PendingTransactionTracker = require('../lib/pending-tx-tracker')
const NonceTracker = require('../lib/nonce-tracker')
+const log = require('loglevel')
/*
Transaction Controller is an aggregate of sub-controllers and trackers
diff --git a/app/scripts/inpage.js b/app/scripts/inpage.js
index ec99bfc35..92c732813 100644
--- a/app/scripts/inpage.js
+++ b/app/scripts/inpage.js
@@ -3,16 +3,11 @@ cleanContextForImports()
require('web3/dist/web3.min.js')
const log = require('loglevel')
const LocalMessageDuplexStream = require('post-message-stream')
-// const PingStream = require('ping-pong-stream/ping')
-// const endOfStream = require('end-of-stream')
const setupDappAutoReload = require('./lib/auto-reload.js')
const MetamaskInpageProvider = require('./lib/inpage-provider.js')
restoreContextAfterImports()
-const METAMASK_DEBUG = process.env.METAMASK_DEBUG
-window.log = log
-log.setDefaultLevel(METAMASK_DEBUG ? 'debug' : 'warn')
-
+log.setDefaultLevel(process.env.METAMASK_DEBUG ? 'debug' : 'warn')
//
// setup plugin communication
diff --git a/app/scripts/lib/ComposableObservableStore.js b/app/scripts/lib/ComposableObservableStore.js
new file mode 100644
index 000000000..d5ee708a1
--- /dev/null
+++ b/app/scripts/lib/ComposableObservableStore.js
@@ -0,0 +1,49 @@
+const ObservableStore = require('obs-store')
+
+/**
+ * An ObservableStore that can composes a flat
+ * structure of child stores based on configuration
+ */
+class ComposableObservableStore extends ObservableStore {
+ /**
+ * Create a new store
+ *
+ * @param {Object} [initState] - The initial store state
+ * @param {Object} [config] - Map of internal state keys to child stores
+ */
+ constructor (initState, config) {
+ super(initState)
+ this.updateStructure(config)
+ }
+
+ /**
+ * Composes a new internal store subscription structure
+ *
+ * @param {Object} [config] - Map of internal state keys to child stores
+ */
+ updateStructure (config) {
+ this.config = config
+ this.removeAllListeners()
+ for (const key in config) {
+ config[key].subscribe((state) => {
+ this.updateState({ [key]: state })
+ })
+ }
+ }
+
+ /**
+ * Merges all child store state into a single object rather than
+ * returning an object keyed by child store class name
+ *
+ * @returns {Object} - Object containing merged child store state
+ */
+ getFlatState () {
+ let flatState = {}
+ for (const key in this.config) {
+ flatState = { ...flatState, ...this.config[key].getState() }
+ }
+ return flatState
+ }
+}
+
+module.exports = ComposableObservableStore
diff --git a/app/scripts/lib/createLoggerMiddleware.js b/app/scripts/lib/createLoggerMiddleware.js
index 2707cbd9e..fc6abf828 100644
--- a/app/scripts/lib/createLoggerMiddleware.js
+++ b/app/scripts/lib/createLoggerMiddleware.js
@@ -1,3 +1,5 @@
+const log = require('loglevel')
+
// log rpc activity
module.exports = createLoggerMiddleware
diff --git a/app/scripts/lib/enums.js b/app/scripts/lib/enums.js
new file mode 100644
index 000000000..0a3afca47
--- /dev/null
+++ b/app/scripts/lib/enums.js
@@ -0,0 +1,9 @@
+const ENVIRONMENT_TYPE_POPUP = 'popup'
+const ENVIRONMENT_TYPE_NOTIFICATION = 'notification'
+const ENVIRONMENT_TYPE_FULLSCREEN = 'fullscreen'
+
+module.exports = {
+ ENVIRONMENT_TYPE_POPUP,
+ ENVIRONMENT_TYPE_NOTIFICATION,
+ ENVIRONMENT_TYPE_FULLSCREEN,
+}
diff --git a/app/scripts/lib/environment-type.js b/app/scripts/lib/environment-type.js
deleted file mode 100644
index 7966926eb..000000000
--- a/app/scripts/lib/environment-type.js
+++ /dev/null
@@ -1,10 +0,0 @@
-module.exports = function environmentType () {
- const url = window.location.href
- if (url.match(/popup.html$/)) {
- return 'popup'
- } else if (url.match(/home.html$/)) {
- return 'responsive'
- } else {
- return 'notification'
- }
-}
diff --git a/app/scripts/lib/is-popup-or-notification.js b/app/scripts/lib/is-popup-or-notification.js
deleted file mode 100644
index ad3e825c0..000000000
--- a/app/scripts/lib/is-popup-or-notification.js
+++ /dev/null
@@ -1,12 +0,0 @@
-module.exports = function isPopupOrNotification () {
- const url = window.location.href
- // if (url.match(/popup.html$/) || url.match(/home.html$/)) {
- // Below regexes needed for feature toggles (e.g. see line ~340 in ui/app/app.js)
- // Revert below regexes to above commented out regexes before merge to master
- if (url.match(/popup.html(?:\?.+)*$/) ||
- url.match(/home.html(?:\?.+)*$/) || url.match(/home.html(?:#.*)*$/)) {
- return 'popup'
- } else {
- return 'notification'
- }
-}
diff --git a/app/scripts/lib/local-store.js b/app/scripts/lib/local-store.js
index 5b47985f6..2dda0ba1f 100644
--- a/app/scripts/lib/local-store.js
+++ b/app/scripts/lib/local-store.js
@@ -3,6 +3,7 @@
// https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/storage/local
const extension = require('extensionizer')
+const log = require('loglevel')
module.exports = class ExtensionStore {
constructor() {
diff --git a/app/scripts/lib/personal-message-manager.js b/app/scripts/lib/personal-message-manager.js
index 6602f5aa8..43a7d0b42 100644
--- a/app/scripts/lib/personal-message-manager.js
+++ b/app/scripts/lib/personal-message-manager.js
@@ -3,6 +3,7 @@ const ObservableStore = require('obs-store')
const ethUtil = require('ethereumjs-util')
const createId = require('./random-id')
const hexRe = /^[0-9A-Fa-f]+$/g
+const log = require('loglevel')
module.exports = class PersonalMessageManager extends EventEmitter {
diff --git a/app/scripts/lib/seed-phrase-verifier.js b/app/scripts/lib/seed-phrase-verifier.js
index 9cea22029..7ba712c0d 100644
--- a/app/scripts/lib/seed-phrase-verifier.js
+++ b/app/scripts/lib/seed-phrase-verifier.js
@@ -1,4 +1,5 @@
const KeyringController = require('eth-keyring-controller')
+const log = require('loglevel')
const seedPhraseVerifier = {
diff --git a/app/scripts/lib/typed-message-manager.js b/app/scripts/lib/typed-message-manager.js
index 8b760790e..60042155e 100644
--- a/app/scripts/lib/typed-message-manager.js
+++ b/app/scripts/lib/typed-message-manager.js
@@ -3,7 +3,7 @@ const ObservableStore = require('obs-store')
const createId = require('./random-id')
const assert = require('assert')
const sigUtil = require('eth-sig-util')
-
+const log = require('loglevel')
module.exports = class TypedMessageManager extends EventEmitter {
constructor (opts) {
diff --git a/app/scripts/lib/util.js b/app/scripts/lib/util.js
index 6dee9edf0..df815906f 100644
--- a/app/scripts/lib/util.js
+++ b/app/scripts/lib/util.js
@@ -1,20 +1,27 @@
const ethUtil = require('ethereumjs-util')
const assert = require('assert')
const BN = require('bn.js')
-
-module.exports = {
- getStack,
- sufficientBalance,
- hexToBn,
- bnToHex,
- BnMultiplyByFraction,
-}
+const {
+ ENVIRONMENT_TYPE_POPUP,
+ ENVIRONMENT_TYPE_NOTIFICATION,
+ ENVIRONMENT_TYPE_FULLSCREEN,
+} = require('./enums')
function getStack () {
const stack = new Error('Stack trace generator - not an error').stack
return stack
}
+const getEnvironmentType = (url = window.location.href) => {
+ if (url.match(/popup.html(?:\?.+)*$/)) {
+ return ENVIRONMENT_TYPE_POPUP
+ } else if (url.match(/home.html(?:\?.+)*$/) || url.match(/home.html(?:#.*)*$/)) {
+ return ENVIRONMENT_TYPE_FULLSCREEN
+ } else {
+ return ENVIRONMENT_TYPE_NOTIFICATION
+ }
+}
+
function sufficientBalance (txParams, hexBalance) {
// validate hexBalance is a hex string
assert.equal(typeof hexBalance, 'string', 'sufficientBalance - hexBalance is not a hex string')
@@ -42,3 +49,12 @@ function BnMultiplyByFraction (targetBN, numerator, denominator) {
const denomBN = new BN(denominator)
return targetBN.mul(numBN).div(denomBN)
}
+
+module.exports = {
+ getStack,
+ getEnvironmentType,
+ sufficientBalance,
+ hexToBn,
+ bnToHex,
+ BnMultiplyByFraction,
+}
diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js
index b96acc9da..a12b6776e 100644
--- a/app/scripts/metamask-controller.js
+++ b/app/scripts/metamask-controller.js
@@ -5,10 +5,10 @@
*/
const EventEmitter = require('events')
-const extend = require('xtend')
const pump = require('pump')
const Dnode = require('dnode')
const ObservableStore = require('obs-store')
+const ComposableObservableStore = require('./lib/ComposableObservableStore')
const asStream = require('obs-store/lib/asStream')
const AccountTracker = require('./lib/account-tracker')
const RpcEngine = require('json-rpc-engine')
@@ -34,6 +34,7 @@ const PersonalMessageManager = require('./lib/personal-message-manager')
const TypedMessageManager = require('./lib/typed-message-manager')
const TransactionController = require('./controllers/transactions')
const BalancesController = require('./controllers/computed-balances')
+const TokenRatesController = require('./controllers/token-rates')
const ConfigManager = require('./lib/config-manager')
const nodeify = require('./lib/nodeify')
const accountImporter = require('./account-import-strategies')
@@ -44,6 +45,7 @@ const BN = require('ethereumjs-util').BN
const GWEI_BN = new BN('1000000000')
const percentile = require('percentile')
const seedPhraseVerifier = require('./lib/seed-phrase-verifier')
+const log = require('loglevel')
module.exports = class MetamaskController extends EventEmitter {
@@ -65,7 +67,7 @@ module.exports = class MetamaskController extends EventEmitter {
this.platform = opts.platform
// observable state store
- this.store = new ObservableStore(initState)
+ this.store = new ComposableObservableStore(initState)
// lock to ensure only one vault created at once
this.createVaultMutex = new Mutex()
@@ -104,6 +106,11 @@ module.exports = class MetamaskController extends EventEmitter {
this.provider = this.initializeProvider()
this.blockTracker = this.provider._blockTracker
+ // token exchange rate tracker
+ this.tokenRatesController = new TokenRatesController({
+ preferences: this.preferencesController.store,
+ })
+
this.recentBlocksController = new RecentBlocksController({
blockTracker: this.blockTracker,
provider: this.provider,
@@ -184,53 +191,37 @@ module.exports = class MetamaskController extends EventEmitter {
this.typedMessageManager = new TypedMessageManager()
this.publicConfigStore = this.initPublicConfigStore()
- // manual disk state subscriptions
- this.txController.store.subscribe((state) => {
- this.store.updateState({ TransactionController: state })
- })
- this.keyringController.store.subscribe((state) => {
- this.store.updateState({ KeyringController: state })
- })
- this.preferencesController.store.subscribe((state) => {
- this.store.updateState({ PreferencesController: state })
- })
- this.addressBookController.store.subscribe((state) => {
- this.store.updateState({ AddressBookController: state })
- })
- this.currencyController.store.subscribe((state) => {
- this.store.updateState({ CurrencyController: state })
- })
- this.noticeController.store.subscribe((state) => {
- this.store.updateState({ NoticeController: state })
- })
- this.shapeshiftController.store.subscribe((state) => {
- this.store.updateState({ ShapeShiftController: state })
- })
- this.networkController.store.subscribe((state) => {
- this.store.updateState({ NetworkController: state })
+ this.store.updateStructure({
+ TransactionController: this.txController.store,
+ KeyringController: this.keyringController.store,
+ PreferencesController: this.preferencesController.store,
+ AddressBookController: this.addressBookController.store,
+ CurrencyController: this.currencyController.store,
+ NoticeController: this.noticeController.store,
+ ShapeShiftController: this.shapeshiftController.store,
+ NetworkController: this.networkController.store,
+ InfuraController: this.infuraController.store,
})
- this.infuraController.store.subscribe((state) => {
- this.store.updateState({ InfuraController: state })
+ this.memStore = new ComposableObservableStore(null, {
+ NetworkController: this.networkController.store,
+ AccountTracker: this.accountTracker.store,
+ TxController: this.txController.memStore,
+ BalancesController: this.balancesController.store,
+ TokenRatesController: this.tokenRatesController.store,
+ MessageManager: this.messageManager.memStore,
+ PersonalMessageManager: this.personalMessageManager.memStore,
+ TypesMessageManager: this.typedMessageManager.memStore,
+ KeyringController: this.keyringController.memStore,
+ PreferencesController: this.preferencesController.store,
+ RecentBlocksController: this.recentBlocksController.store,
+ AddressBookController: this.addressBookController.store,
+ CurrencyController: this.currencyController.store,
+ NoticeController: this.noticeController.memStore,
+ ShapeshiftController: this.shapeshiftController.store,
+ InfuraController: this.infuraController.store,
})
-
- // manual mem state subscriptions
- const sendUpdate = this.sendUpdate.bind(this)
- this.networkController.store.subscribe(sendUpdate)
- this.accountTracker.store.subscribe(sendUpdate)
- this.txController.memStore.subscribe(sendUpdate)
- this.balancesController.store.subscribe(sendUpdate)
- this.messageManager.memStore.subscribe(sendUpdate)
- this.personalMessageManager.memStore.subscribe(sendUpdate)
- this.typedMessageManager.memStore.subscribe(sendUpdate)
- this.keyringController.memStore.subscribe(sendUpdate)
- this.preferencesController.store.subscribe(sendUpdate)
- this.recentBlocksController.store.subscribe(sendUpdate)
- this.addressBookController.store.subscribe(sendUpdate)
- this.currencyController.store.subscribe(sendUpdate)
- this.noticeController.memStore.subscribe(sendUpdate)
- this.shapeshiftController.store.subscribe(sendUpdate)
- this.infuraController.store.subscribe(sendUpdate)
+ this.memStore.subscribe(this.sendUpdate.bind(this))
}
/**
@@ -279,6 +270,7 @@ module.exports = class MetamaskController extends EventEmitter {
// memStore -> transform -> publicConfigStore
this.on('update', (memState) => {
+ this.isClientOpenAndUnlocked = memState.isUnlocked && this._isClientOpen
const publicState = selectPublicState(memState)
publicConfigStore.putState(publicState)
})
@@ -308,33 +300,16 @@ module.exports = class MetamaskController extends EventEmitter {
const vault = this.keyringController.store.getState().vault
const isInitialized = (!!wallet || !!vault)
- return extend(
- {
- isInitialized,
- },
- this.networkController.store.getState(),
- this.accountTracker.store.getState(),
- this.txController.memStore.getState(),
- this.messageManager.memStore.getState(),
- this.personalMessageManager.memStore.getState(),
- this.typedMessageManager.memStore.getState(),
- this.keyringController.memStore.getState(),
- this.balancesController.store.getState(),
- this.preferencesController.store.getState(),
- this.addressBookController.store.getState(),
- this.currencyController.store.getState(),
- this.noticeController.memStore.getState(),
- this.infuraController.store.getState(),
- this.recentBlocksController.store.getState(),
- // config manager
- this.configManager.getConfig(),
- this.shapeshiftController.store.getState(),
- {
+ return {
+ ...{ isInitialized },
+ ...this.memStore.getFlatState(),
+ ...this.configManager.getConfig(),
+ ...{
lostAccounts: this.configManager.getLostAccounts(),
seedWords: this.configManager.getSeedWords(),
forgottenPassword: this.configManager.getPasswordForgotten(),
- }
- )
+ },
+ }
}
/**
@@ -1057,4 +1032,12 @@ module.exports = class MetamaskController extends EventEmitter {
}
}
+ set isClientOpen (open) {
+ this._isClientOpen = open
+ this.isClientOpenAndUnlocked = this.getState().isUnlocked && open
+ }
+
+ set isClientOpenAndUnlocked (active) {
+ this.tokenRatesController.isActive = active
+ }
}
diff --git a/app/scripts/ui.js b/app/scripts/ui.js
index 13c7ac5ec..bdab29c1e 100644
--- a/app/scripts/ui.js
+++ b/app/scripts/ui.js
@@ -3,12 +3,14 @@ const OldMetaMaskUiCss = require('../../old-ui/css')
const NewMetaMaskUiCss = require('../../ui/css')
const startPopup = require('./popup-core')
const PortStream = require('./lib/port-stream.js')
-const isPopupOrNotification = require('./lib/is-popup-or-notification')
+const { getEnvironmentType } = require('./lib/util')
+const { ENVIRONMENT_TYPE_NOTIFICATION } = require('./lib/enums')
const extension = require('extensionizer')
const ExtensionPlatform = require('./platforms/extension')
const NotificationManager = require('./lib/notification-manager')
const notificationManager = new NotificationManager()
const setupRaven = require('./lib/setupRaven')
+const log = require('loglevel')
start().catch(log.error)
@@ -26,7 +28,7 @@ async function start() {
// injectCss(css)
// identify window type (popup, notification)
- const windowType = isPopupOrNotification()
+ const windowType = getEnvironmentType(window.location.href)
global.METAMASK_UI_TYPE = windowType
closePopupIfOpen(windowType)
@@ -68,7 +70,7 @@ async function start() {
function closePopupIfOpen (windowType) {
- if (windowType !== 'notification') {
+ if (windowType !== ENVIRONMENT_TYPE_NOTIFICATION) {
// should close only chrome popup
notificationManager.closePopup()
}
diff --git a/docs/team.md b/docs/team.md
new file mode 100644
index 000000000..72f9d3226
--- /dev/null
+++ b/docs/team.md
@@ -0,0 +1,78 @@
+# The Team
+
+Here is an overview of the current MetaMask team, and their primary roles and responsibilities, in the order they joined the team.
+
+## Core Team Members
+
+The core team maintains aspects of the main product (the extension) and the core libraries (the MetaMask Controller, provider-engine, etc).
+
+### Aaron Davis
+
+@kumavis
+Founder / Technical Lead
+
+Especially in charge of connection to the blockchain. Wrote [provider-engine](https://github.com/MetaMask/provider-engine), and is currently working with @hermanjunge on our JavaScript light-client.
+
+### Dan Finlay
+
+@danfinlay
+Software Engineer / Product Lead
+
+Focused on the deliverable, user-valuable aspects of MetaMask, including usability and documentation. Coordinates efforts between different branches of the team, and integrations with other projects.
+
+### Frankie Pangilinan
+
+@frankiebee
+Software Engineer / Transaction Manager Lead
+
+Frankie contributes code throughout MetaMask, but has become especially specialized in the way MetaMask manages transactions. She is also the original lead of the [Mascara](https://github.com/MetaMask/mascara) project.
+
+### Kevin Serrano
+
+@Zanibas
+Software Engineer / Project Management Lead
+
+Kevin is a software engineer, but also spends a lot of his time keeping the team's administrative operations running smoothly.
+
+### Thomas Huang
+
+@tmashuang
+QA Engineer
+
+Thomas is the head of MetaMask Quality Assurance. He both takes the final pass of branches of code before we ship to production, and is also in charge of continuously improving our automated quality assurance process.
+
+### Christian Jeria
+
+@cjeria
+User Experience Designer
+
+Christian is the lead of MetaMask's user experience. He is continuously designing prototypes, testing them with users, and refining them with our developers for production.
+
+### Paul Bouchon
+
+@bitpshr
+Software Engineer
+
+The newest member of the team! Paul is currently being onboarded, and finding his niche within the team.
+
+## Laboratory Team Members
+
+These team members are working on projects that will benefit MetaMask, but are not directly working on the product itself.
+
+### Herman Junge
+
+@hermanjunge
+Software Engineer
+
+Herman is currently leading the Mustekala project, a JavaScript, IPFS-based Ethereum light client.
+
+## Kyokan Team Members
+
+[Kyokan](http://kyokan.io/) is a consulting firm that has been working closely with the MetaMask team on the latest version of our user interface. Their team members are not members of ConsenSys LLC, but they contribute a lot to the project.
+
+- Daniel Tsui (@sdsui)
+- Chi Kei Chan (@chikeichan)
+- Dan Miller (@danjm)
+- David Yoo (@yookd)
+- Whymarrh Whitby (@whymarrh)
+
diff --git a/old-ui/app/app.js b/old-ui/app/app.js
index 37611987f..fc0d634aa 100644
--- a/old-ui/app/app.js
+++ b/old-ui/app/app.js
@@ -3,6 +3,7 @@ const Component = require('react').Component
const connect = require('react-redux').connect
const h = require('react-hyperscript')
const actions = require('../../ui/app/actions')
+const log = require('loglevel')
// mascara
const MascaraFirstTime = require('../../mascara/src/app/first-time').default
const MascaraBuyEtherScreen = require('../../mascara/src/app/first-time/buy-ether-screen').default
diff --git a/old-ui/app/components/ens-input.js b/old-ui/app/components/ens-input.js
index c85a23514..d09c30644 100644
--- a/old-ui/app/components/ens-input.js
+++ b/old-ui/app/components/ens-input.js
@@ -8,6 +8,7 @@ const ENS = require('ethjs-ens')
const networkMap = require('ethjs-ens/lib/network-map.json')
const ensRE = /.+\..+$/
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'
+const log = require('loglevel')
module.exports = EnsInput
diff --git a/old-ui/app/components/pending-tx.js b/old-ui/app/components/pending-tx.js
index 7f63d9fdf..cd4189fc4 100644
--- a/old-ui/app/components/pending-tx.js
+++ b/old-ui/app/components/pending-tx.js
@@ -3,6 +3,7 @@ const h = require('react-hyperscript')
const inherits = require('util').inherits
const actions = require('../../../ui/app/actions')
const clone = require('clone')
+const log = require('loglevel')
const ethUtil = require('ethereumjs-util')
const BN = ethUtil.BN
diff --git a/old-ui/app/components/token-list.js b/old-ui/app/components/token-list.js
index 149733b89..e20594b61 100644
--- a/old-ui/app/components/token-list.js
+++ b/old-ui/app/components/token-list.js
@@ -3,6 +3,7 @@ const h = require('react-hyperscript')
const inherits = require('util').inherits
const TokenTracker = require('eth-token-tracker')
const TokenCell = require('./token-cell.js')
+const log = require('loglevel')
module.exports = TokenList
diff --git a/old-ui/app/conf-tx.js b/old-ui/app/conf-tx.js
index 1bb8eb97c..dc1dc0538 100644
--- a/old-ui/app/conf-tx.js
+++ b/old-ui/app/conf-tx.js
@@ -6,7 +6,9 @@ const actions = require('../../ui/app/actions')
const NetworkIndicator = require('./components/network')
const LoadingIndicator = require('./components/loading')
const txHelper = require('../lib/tx-helper')
-const isPopupOrNotification = require('../../app/scripts/lib/is-popup-or-notification')
+const log = require('loglevel')
+const { ENVIRONMENT_TYPE_NOTIFICATION } = require('../../app/scripts/lib/enums')
+const { getEnvironmentType } = require('../../app/scripts/lib/util')
const PendingTx = require('./components/pending-tx')
const PendingMsg = require('./components/pending-msg')
@@ -50,7 +52,7 @@ ConfirmTxScreen.prototype.render = function () {
var txData = unconfTxList[props.index] || {}
var txParams = txData.params || {}
- var isNotification = isPopupOrNotification() === 'notification'
+ var isNotification = getEnvironmentType(window.location.href) === ENVIRONMENT_TYPE_NOTIFICATION
log.info(`rendering a combined ${unconfTxList.length} unconf msg & txs`)
if (unconfTxList.length === 0) return h(Loading, { isLoading: true })
diff --git a/old-ui/lib/tx-helper.js b/old-ui/lib/tx-helper.js
index de3f00d2d..0a6f55a63 100644
--- a/old-ui/lib/tx-helper.js
+++ b/old-ui/lib/tx-helper.js
@@ -1,4 +1,5 @@
const valuesFor = require('../app/util').valuesFor
+const log = require('loglevel')
module.exports = function (unapprovedTxs, unapprovedMsgs, personalMsgs, typedMessages, network) {
log.debug('tx-helper called with params:')
diff --git a/package-lock.json b/package-lock.json
index 86444fe56..b39a0ff89 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -5836,29 +5836,15 @@
"resolved": "https://registry.npmjs.org/eth-sig-util/-/eth-sig-util-1.4.2.tgz",
"integrity": "sha1-jZWCAsftuq6Dlwf7pvCf8ydgYhA=",
"requires": {
- "ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git#71f123b676f2b2d81bc20f343670d90045a3d3d8",
+ "ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git#4ea2fdfed09e8f99117d9362d17c6b01b64a2bcf",
"ethereumjs-util": "5.1.3"
},
"dependencies": {
"ethereumjs-abi": {
- "version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#71f123b676f2b2d81bc20f343670d90045a3d3d8",
+ "version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#4ea2fdfed09e8f99117d9362d17c6b01b64a2bcf",
"requires": {
"bn.js": "4.11.8",
- "ethereumjs-util": "4.5.0"
- },
- "dependencies": {
- "ethereumjs-util": {
- "version": "4.5.0",
- "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-4.5.0.tgz",
- "integrity": "sha1-PpQosxfuvaPXJg2FT93alUsfG8Y=",
- "requires": {
- "bn.js": "4.11.8",
- "create-hash": "1.1.3",
- "keccakjs": "0.2.1",
- "rlp": "2.0.0",
- "secp256k1": "3.4.0"
- }
- }
+ "ethereumjs-util": "5.1.3"
}
},
"ethereumjs-util": {
diff --git a/package.json b/package.json
index 9ddf479b3..985733cf0 100644
--- a/package.json
+++ b/package.json
@@ -9,7 +9,7 @@
"dist": "gulp dist",
"doc": "jsdoc -c development/tools/.jsdoc.json",
"test": "npm run test:unit && npm run test:integration && npm run lint",
- "test:unit": "cross-env METAMASK_ENV=test mocha --exit --require babel-core/register --require test/helper.js --recursive \"test/unit/**/*.js\"",
+ "test:unit": "cross-env METAMASK_ENV=test mocha --exit --require test/setup.js --recursive \"test/unit/**/*.js\"",
"test:single": "cross-env METAMASK_ENV=test mocha --require test/helper.js",
"test:integration": "npm run test:integration:build && npm run test:flat && npm run test:mascara",
"test:integration:build": "gulp build:scss",
diff --git a/test/setup.js b/test/setup.js
new file mode 100644
index 000000000..8e7965a37
--- /dev/null
+++ b/test/setup.js
@@ -0,0 +1,5 @@
+require('babel-register')({
+ ignore: name => name.includes('node_modules') && !name.includes('obs-store'),
+})
+
+require('./helper')
diff --git a/test/unit/ComposableObservableStore.js b/test/unit/ComposableObservableStore.js
new file mode 100644
index 000000000..3fba200c1
--- /dev/null
+++ b/test/unit/ComposableObservableStore.js
@@ -0,0 +1,35 @@
+const assert = require('assert')
+const ComposableObservableStore = require('../../app/scripts/lib/ComposableObservableStore')
+const ObservableStore = require('obs-store')
+
+describe('ComposableObservableStore', () => {
+ it('should register initial state', () => {
+ const store = new ComposableObservableStore('state')
+ assert.strictEqual(store.getState(), 'state')
+ })
+
+ it('should register initial structure', () => {
+ const testStore = new ObservableStore()
+ const store = new ComposableObservableStore(null, { TestStore: testStore })
+ testStore.putState('state')
+ assert.deepEqual(store.getState(), { TestStore: 'state' })
+ })
+
+ it('should update structure', () => {
+ const testStore = new ObservableStore()
+ const store = new ComposableObservableStore()
+ store.updateStructure({ TestStore: testStore })
+ testStore.putState('state')
+ assert.deepEqual(store.getState(), { TestStore: 'state' })
+ })
+
+ it('should return flattened state', () => {
+ const fooStore = new ObservableStore({ foo: 'foo' })
+ const barStore = new ObservableStore({ bar: 'bar' })
+ const store = new ComposableObservableStore(null, {
+ FooStore: fooStore,
+ BarStore: barStore,
+ })
+ assert.deepEqual(store.getFlatState(), { foo: 'foo', bar: 'bar' })
+ })
+})
diff --git a/test/unit/token-rates-controller.js b/test/unit/token-rates-controller.js
new file mode 100644
index 000000000..a49547313
--- /dev/null
+++ b/test/unit/token-rates-controller.js
@@ -0,0 +1,29 @@
+const assert = require('assert')
+const sinon = require('sinon')
+const TokenRatesController = require('../../app/scripts/controllers/token-rates')
+const ObservableStore = require('obs-store')
+
+describe('TokenRatesController', () => {
+ it('should listen for preferences store updates', () => {
+ const preferences = new ObservableStore({ tokens: [] })
+ const controller = new TokenRatesController({ preferences })
+ preferences.putState({ tokens: ['foo'] })
+ assert.deepEqual(controller._tokens, ['foo'])
+ })
+
+ it('should poll on correct interval', async () => {
+ const stub = sinon.stub(global, 'setInterval')
+ new TokenRatesController({ interval: 1337 }) // eslint-disable-line no-new
+ assert.strictEqual(stub.getCall(0).args[1], 1337)
+ stub.restore()
+ })
+
+ it('should fetch each token rate based on address', async () => {
+ const controller = new TokenRatesController()
+ controller.isActive = true
+ controller.fetchExchangeRate = address => address
+ controller.tokens = [{ address: 'foo' }, { address: 'bar' }]
+ await controller.updateExchangeRates()
+ assert.deepEqual(controller.store.getState().contractExchangeRates, { foo: 'foo', bar: 'bar' })
+ })
+})
diff --git a/ui/app/actions.js b/ui/app/actions.js
index 0748a5bea..f5cdd32bc 100644
--- a/ui/app/actions.js
+++ b/ui/app/actions.js
@@ -3,6 +3,7 @@ const getBuyEthUrl = require('../../app/scripts/lib/buy-eth-url')
const { getTokenAddressFromTokenObject } = require('./util')
const ethUtil = require('ethereumjs-util')
const { fetchLocale } = require('../i18n-helper')
+const log = require('loglevel')
var actions = {
_setBackgroundConnection: _setBackgroundConnection,
@@ -220,8 +221,6 @@ var actions = {
coinBaseSubview: coinBaseSubview,
SHAPESHIFT_SUBVIEW: 'SHAPESHIFT_SUBVIEW',
shapeShiftSubview: shapeShiftSubview,
- UPDATE_TOKEN_EXCHANGE_RATE: 'UPDATE_TOKEN_EXCHANGE_RATE',
- updateTokenExchangeRate,
PAIR_UPDATE: 'PAIR_UPDATE',
pairUpdate: pairUpdate,
coinShiftRquest: coinShiftRquest,
@@ -1751,28 +1750,6 @@ function shapeShiftRequest (query, options, cb) {
}
}
-function updateTokenExchangeRate (token = '') {
- const pair = `${token.toLowerCase()}_eth`
-
- return dispatch => {
- if (!token) {
- return
- }
-
- shapeShiftRequest('marketinfo', { pair }, marketinfo => {
- if (!marketinfo.error) {
- dispatch({
- type: actions.UPDATE_TOKEN_EXCHANGE_RATE,
- payload: {
- pair,
- marketinfo,
- },
- })
- }
- })
- }
-}
-
function setFeatureFlag (feature, activated, notificationType) {
return (dispatch) => {
dispatch(actions.showLoadingIndication())
diff --git a/ui/app/app.js b/ui/app/app.js
index 75c3febb0..827b4e9ce 100644
--- a/ui/app/app.js
+++ b/ui/app/app.js
@@ -6,6 +6,7 @@ const { compose } = require('recompose')
const h = require('react-hyperscript')
const actions = require('./actions')
const classnames = require('classnames')
+const log = require('loglevel')
// init
const InitializeScreen = require('../../mascara/src/app/first-time').default
diff --git a/ui/app/components/ens-input.js b/ui/app/components/ens-input.js
index feb0a7037..aff4b6ef6 100644
--- a/ui/app/components/ens-input.js
+++ b/ui/app/components/ens-input.js
@@ -11,6 +11,7 @@ const ensRE = /.+\..+$/
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'
const connect = require('react-redux').connect
const ToAutoComplete = require('./send/to-autocomplete')
+const log = require('loglevel')
EnsInput.contextTypes = {
t: PropTypes.func,
diff --git a/ui/app/components/modals/modal.js b/ui/app/components/modals/modal.js
index 9250cc77e..43dcd20ae 100644
--- a/ui/app/components/modals/modal.js
+++ b/ui/app/components/modals/modal.js
@@ -5,7 +5,8 @@ const connect = require('react-redux').connect
const FadeModal = require('boron').FadeModal
const actions = require('../../actions')
const isMobileView = require('../../../lib/is-mobile-view')
-const isPopupOrNotification = require('../../../../app/scripts/lib/is-popup-or-notification')
+const { getEnvironmentType } = require('../../../../app/scripts/lib/util')
+const { ENVIRONMENT_TYPE_POPUP } = require('../../../../app/scripts/lib/enums')
// Modal Components
const BuyOptions = require('./buy-options-modal')
@@ -162,7 +163,7 @@ const MODALS = {
],
mobileModalStyle: {
width: '95%',
- top: isPopupOrNotification() === 'popup' ? '52vh' : '36.5vh',
+ top: getEnvironmentType(window.location.href) === ENVIRONMENT_TYPE_POPUP ? '52vh' : '36.5vh',
},
laptopModalStyle: {
width: '449px',
@@ -179,7 +180,7 @@ const MODALS = {
],
mobileModalStyle: {
width: '95%',
- top: isPopupOrNotification() === 'popup' ? '52vh' : '36.5vh',
+ top: getEnvironmentType(window.location.href) === ENVIRONMENT_TYPE_POPUP ? '52vh' : '36.5vh',
},
laptopModalStyle: {
width: '449px',
@@ -196,7 +197,7 @@ const MODALS = {
],
mobileModalStyle: {
width: '95%',
- top: isPopupOrNotification() === 'popup' ? '52vh' : '36.5vh',
+ top: getEnvironmentType(window.location.href) === ENVIRONMENT_TYPE_POPUP ? '52vh' : '36.5vh',
},
laptopModalStyle: {
width: '449px',
@@ -208,7 +209,7 @@ const MODALS = {
contents: h(ConfirmResetAccount),
mobileModalStyle: {
width: '95%',
- top: isPopupOrNotification() === 'popup' ? '52vh' : '36.5vh',
+ top: getEnvironmentType(window.location.href) === ENVIRONMENT_TYPE_POPUP ? '52vh' : '36.5vh',
},
laptopModalStyle: {
width: '473px',
diff --git a/ui/app/components/pages/home.js b/ui/app/components/pages/home.js
index 7857a2a99..90b8e1d37 100644
--- a/ui/app/components/pages/home.js
+++ b/ui/app/components/pages/home.js
@@ -5,6 +5,7 @@ const { Redirect, withRouter } = require('react-router-dom')
const { compose } = require('recompose')
const h = require('react-hyperscript')
const actions = require('../../actions')
+const log = require('loglevel')
// init
const NewKeyChainScreen = require('../../new-keychain')
diff --git a/ui/app/components/pages/keychains/restore-vault.js b/ui/app/components/pages/keychains/restore-vault.js
index d57894e00..33575bfbb 100644
--- a/ui/app/components/pages/keychains/restore-vault.js
+++ b/ui/app/components/pages/keychains/restore-vault.js
@@ -6,6 +6,7 @@ const connect = require('../../../metamask-connect')
const h = require('react-hyperscript')
const { createNewVaultAndRestore, unMarkPasswordForgotten } = require('../../../actions')
const { DEFAULT_ROUTE } = require('../../../routes')
+const log = require('loglevel')
class RestoreVaultPage extends PersistentForm {
constructor (props) {
diff --git a/ui/app/components/pages/unlock.js b/ui/app/components/pages/unlock.js
index b3320da21..567b72518 100644
--- a/ui/app/components/pages/unlock.js
+++ b/ui/app/components/pages/unlock.js
@@ -11,7 +11,8 @@ const {
setNetworkEndpoints,
setFeatureFlag,
} = require('../../actions')
-const environmentType = require('../../../../app/scripts/lib/environment-type')
+const { ENVIRONMENT_TYPE_POPUP } = require('../../../../app/scripts/lib/enums')
+const { getEnvironmentType } = require('../../../../app/scripts/lib/util')
const getCaretCoordinates = require('textarea-caret')
const EventEmitter = require('events').EventEmitter
const Mascot = require('../mascot')
@@ -131,7 +132,7 @@ class UnlockScreen extends Component {
this.props.markPasswordForgotten()
this.props.history.push(RESTORE_VAULT_ROUTE)
- if (environmentType() === 'popup') {
+ if (getEnvironmentType(window.location.href) === ENVIRONMENT_TYPE_POPUP) {
global.platform.openExtensionInBrowser()
}
},
diff --git a/ui/app/components/pending-tx/confirm-send-token.js b/ui/app/components/pending-tx/confirm-send-token.js
index 6942f9b51..37130a1cc 100644
--- a/ui/app/components/pending-tx/confirm-send-token.js
+++ b/ui/app/components/pending-tx/confirm-send-token.js
@@ -48,7 +48,7 @@ module.exports = compose(
function mapStateToProps (state, ownProps) {
- const { token: { symbol }, txData } = ownProps
+ const { token: { address }, txData } = ownProps
const { txParams } = txData || {}
const tokenData = txParams.data && abiDecoder.decodeMethod(txParams.data)
@@ -59,7 +59,7 @@ function mapStateToProps (state, ownProps) {
} = state.metamask
const accounts = state.metamask.accounts
const selectedAddress = getSelectedAddress(state)
- const tokenExchangeRate = getTokenExchangeRate(state, symbol)
+ const tokenExchangeRate = getTokenExchangeRate(state, address)
const { balance } = accounts[selectedAddress]
return {
conversionRate,
@@ -75,12 +75,9 @@ function mapStateToProps (state, ownProps) {
}
function mapDispatchToProps (dispatch, ownProps) {
- const { token: { symbol } } = ownProps
-
return {
backToAccountDetail: address => dispatch(actions.backToAccountDetail(address)),
cancelTransaction: ({ id }) => dispatch(actions.cancelTx({ id })),
- updateTokenExchangeRate: () => dispatch(actions.updateTokenExchangeRate(symbol)),
editTransaction: txMeta => {
const { token: { address } } = ownProps
const { txParams = {}, id } = txMeta
@@ -203,7 +200,6 @@ ConfirmSendToken.prototype.componentWillMount = function () {
.balanceOf(selectedAddress)
.then(usersToken => {
})
- this.props.updateTokenExchangeRate()
this.updateComponentSendErrors({})
}
diff --git a/ui/app/components/send/send-v2-container.js b/ui/app/components/send/send-v2-container.js
index aca1a5a0a..adfc91240 100644
--- a/ui/app/components/send/send-v2-container.js
+++ b/ui/app/components/send/send-v2-container.js
@@ -66,7 +66,6 @@ function mapDispatchToProps (dispatch) {
showCustomizeGasModal: () => dispatch(actions.showModal({ name: 'CUSTOMIZE_GAS' })),
estimateGas: params => dispatch(actions.estimateGas(params)),
getGasPrice: () => dispatch(actions.getGasPrice()),
- updateTokenExchangeRate: token => dispatch(actions.updateTokenExchangeRate(token)),
signTokenTx: (tokenAddress, toAddress, amount, txData) => (
dispatch(actions.signTokenTx(tokenAddress, toAddress, amount, txData))
),
diff --git a/ui/app/components/token-balance.js b/ui/app/components/token-balance.js
index 2f71c0687..1900ccec7 100644
--- a/ui/app/components/token-balance.js
+++ b/ui/app/components/token-balance.js
@@ -4,6 +4,7 @@ const inherits = require('util').inherits
const TokenTracker = require('eth-token-tracker')
const connect = require('react-redux').connect
const selectors = require('../selectors')
+const log = require('loglevel')
function mapStateToProps (state) {
return {
diff --git a/ui/app/components/token-cell.js b/ui/app/components/token-cell.js
index 0332fde88..c84117d84 100644
--- a/ui/app/components/token-cell.js
+++ b/ui/app/components/token-cell.js
@@ -16,7 +16,7 @@ function mapStateToProps (state) {
currentCurrency: state.metamask.currentCurrency,
selectedTokenAddress: state.metamask.selectedTokenAddress,
userAddress: selectors.getSelectedAddress(state),
- tokenExchangeRates: state.metamask.tokenExchangeRates,
+ contractExchangeRates: state.metamask.contractExchangeRates,
conversionRate: state.metamask.conversionRate,
sidebarOpen: state.appState.sidebarOpen,
}
@@ -25,7 +25,6 @@ function mapStateToProps (state) {
function mapDispatchToProps (dispatch) {
return {
setSelectedToken: address => dispatch(actions.setSelectedToken(address)),
- updateTokenExchangeRate: token => dispatch(actions.updateTokenExchangeRate(token)),
hideSidebar: () => dispatch(actions.hideSidebar()),
}
}
@@ -41,15 +40,6 @@ function TokenCell () {
}
}
-TokenCell.prototype.componentWillMount = function () {
- const {
- updateTokenExchangeRate,
- symbol,
- } = this.props
-
- updateTokenExchangeRate(symbol)
-}
-
TokenCell.prototype.render = function () {
const { tokenMenuOpen } = this.state
const props = this.props
@@ -60,7 +50,7 @@ TokenCell.prototype.render = function () {
network,
setSelectedToken,
selectedTokenAddress,
- tokenExchangeRates,
+ contractExchangeRates,
conversionRate,
hideSidebar,
sidebarOpen,
@@ -68,15 +58,13 @@ TokenCell.prototype.render = function () {
// userAddress,
} = props
- const pair = `${symbol.toLowerCase()}_eth`
-
let currentTokenToFiatRate
let currentTokenInFiat
let formattedFiat = ''
- if (tokenExchangeRates[pair]) {
+ if (contractExchangeRates[address]) {
currentTokenToFiatRate = multiplyCurrencies(
- tokenExchangeRates[pair].rate,
+ contractExchangeRates[address],
conversionRate
)
currentTokenInFiat = conversionUtil(string, {
diff --git a/ui/app/components/token-list.js b/ui/app/components/token-list.js
index 150a3762d..4189cf801 100644
--- a/ui/app/components/token-list.js
+++ b/ui/app/components/token-list.js
@@ -6,6 +6,7 @@ const TokenTracker = require('eth-token-tracker')
const TokenCell = require('./token-cell.js')
const connect = require('react-redux').connect
const selectors = require('../selectors')
+const log = require('loglevel')
function mapStateToProps (state) {
return {
diff --git a/ui/app/components/tx-list-item.js b/ui/app/components/tx-list-item.js
index 42c008798..403f83ab9 100644
--- a/ui/app/components/tx-list-item.js
+++ b/ui/app/components/tx-list-item.js
@@ -27,7 +27,7 @@ function mapStateToProps (state) {
return {
tokens: state.metamask.tokens,
currentCurrency: getCurrentCurrency(state),
- tokenExchangeRates: state.metamask.tokenExchangeRates,
+ contractExchangeRates: state.metamask.contractExchangeRates,
selectedAddressTxList: state.metamask.selectedAddressTxList,
}
}
@@ -142,31 +142,29 @@ TxListItem.prototype.getTokenInfo = async function () {
({ decimals, symbol } = await tokenInfoGetter(toAddress))
}
- return { decimals, symbol }
+ return { decimals, symbol, address: toAddress }
}
TxListItem.prototype.getSendTokenTotal = async function () {
const {
txParams = {},
conversionRate,
- tokenExchangeRates,
+ contractExchangeRates,
currentCurrency,
} = this.props
const decodedData = txParams.data && abiDecoder.decodeMethod(txParams.data)
const { params = [] } = decodedData || {}
const { value } = params[1] || {}
- const { decimals, symbol } = await this.getTokenInfo()
+ const { decimals, symbol, address } = await this.getTokenInfo()
const total = calcTokenAmount(value, decimals)
- const pair = symbol && `${symbol.toLowerCase()}_eth`
-
let tokenToFiatRate
let totalInFiat
- if (tokenExchangeRates[pair]) {
+ if (contractExchangeRates[address]) {
tokenToFiatRate = multiplyCurrencies(
- tokenExchangeRates[pair].rate,
+ contractExchangeRates[address],
conversionRate
)
@@ -220,7 +218,6 @@ TxListItem.prototype.resubmit = function () {
TxListItem.prototype.render = function () {
const {
transactionStatus,
- transactionAmount,
onClick,
transactionId,
dateString,
@@ -229,7 +226,6 @@ TxListItem.prototype.render = function () {
txParams,
} = this.props
const { total, fiatTotal, isTokenTx } = this.state
- const showFiatTotal = transactionAmount !== '0x0' && fiatTotal
return h(`div${className || ''}`, {
key: transactionId,
@@ -288,7 +284,7 @@ TxListItem.prototype.render = function () {
h('span.tx-list-value', total),
- showFiatTotal && h('span.tx-list-fiat-value', fiatTotal),
+ fiatTotal && h('span.tx-list-fiat-value', fiatTotal),
]),
]),
diff --git a/ui/app/conf-tx.js b/ui/app/conf-tx.js
index fee7cd36f..b71538e31 100644
--- a/ui/app/conf-tx.js
+++ b/ui/app/conf-tx.js
@@ -6,6 +6,7 @@ const { withRouter } = require('react-router-dom')
const { compose } = require('recompose')
const actions = require('./actions')
const txHelper = require('../lib/tx-helper')
+const log = require('loglevel')
const PendingTx = require('./components/pending-tx')
const SignatureRequest = require('./components/signature-request')
diff --git a/ui/app/first-time/init-menu.js b/ui/app/first-time/init-menu.js
index 692b01c8c..3df040922 100644
--- a/ui/app/first-time/init-menu.js
+++ b/ui/app/first-time/init-menu.js
@@ -8,7 +8,8 @@ const actions = require('../actions')
const Tooltip = require('../components/tooltip')
const getCaretCoordinates = require('textarea-caret')
const { RESTORE_VAULT_ROUTE, DEFAULT_ROUTE } = require('../routes')
-const environmentType = require('../../../app/scripts/lib/environment-type')
+const { getEnvironmentType } = require('../../../app/scripts/lib/util')
+const { ENVIRONMENT_TYPE_POPUP } = require('../../../app/scripts/lib/enums')
const { OLD_UI_NETWORK_TYPE } = require('../../../app/scripts/config').enums
class InitializeMenuScreen extends Component {
@@ -180,7 +181,7 @@ class InitializeMenuScreen extends Component {
showRestoreVault () {
this.props.markPasswordForgotten()
- if (environmentType() === 'popup') {
+ if (getEnvironmentType(window.location.href) === ENVIRONMENT_TYPE_POPUP) {
global.platform.openExtensionInBrowser()
}
diff --git a/ui/app/keychains/hd/restore-vault.js b/ui/app/keychains/hd/restore-vault.js
index 38ad14adb..913d20505 100644
--- a/ui/app/keychains/hd/restore-vault.js
+++ b/ui/app/keychains/hd/restore-vault.js
@@ -4,6 +4,7 @@ const PersistentForm = require('../../../lib/persistent-form')
const connect = require('react-redux').connect
const h = require('react-hyperscript')
const actions = require('../../actions')
+const log = require('loglevel')
RestoreVaultScreen.contextTypes = {
t: PropTypes.func,
diff --git a/ui/app/main-container.js b/ui/app/main-container.js
index 6dd4ff151..c305687ea 100644
--- a/ui/app/main-container.js
+++ b/ui/app/main-container.js
@@ -4,6 +4,7 @@ const inherits = require('util').inherits
const AccountAndTransactionDetails = require('./account-and-transaction-details')
const Settings = require('./components/pages/settings')
const UnlockScreen = require('./components/pages/unlock')
+const log = require('loglevel')
module.exports = MainContainer
diff --git a/ui/app/reducers/app.js b/ui/app/reducers/app.js
index 74a0f9299..2b39eb8db 100644
--- a/ui/app/reducers/app.js
+++ b/ui/app/reducers/app.js
@@ -1,6 +1,7 @@
const extend = require('xtend')
const actions = require('../actions')
const txHelper = require('../../lib/tx-helper')
+const log = require('loglevel')
module.exports = reduceApp
diff --git a/ui/app/reducers/metamask.js b/ui/app/reducers/metamask.js
index 6d0a5bb10..5f965fbe0 100644
--- a/ui/app/reducers/metamask.js
+++ b/ui/app/reducers/metamask.js
@@ -1,7 +1,8 @@
const extend = require('xtend')
const actions = require('../actions')
const MetamascaraPlatform = require('../../../app/scripts/platforms/window')
-const environmentType = require('../../../app/scripts/lib/environment-type')
+const { getEnvironmentType } = require('../../../app/scripts/lib/util')
+const { ENVIRONMENT_TYPE_POPUP } = require('../../../app/scripts/lib/enums')
const { OLD_UI_NETWORK_TYPE } = require('../../../app/scripts/config').enums
module.exports = reduceMetamask
@@ -15,7 +16,7 @@ function reduceMetamask (state, action) {
isUnlocked: false,
isAccountMenuOpen: false,
isMascara: window.platform instanceof MetamascaraPlatform,
- isPopup: environmentType() === 'popup',
+ isPopup: getEnvironmentType(window.location.href) === ENVIRONMENT_TYPE_POPUP,
rpcTarget: 'https://rawtestrpc.metamask.io/',
identities: {},
unapprovedTxs: {},
@@ -24,6 +25,7 @@ function reduceMetamask (state, action) {
frequentRpcList: [],
addressBook: [],
selectedTokenAddress: null,
+ contractExchangeRates: {},
tokenExchangeRates: {},
tokens: [],
send: {
@@ -176,15 +178,6 @@ function reduceMetamask (state, action) {
conversionDate: action.value.conversionDate,
})
- case actions.UPDATE_TOKEN_EXCHANGE_RATE:
- const { payload: { pair, marketinfo } } = action
- return extend(metamaskState, {
- tokenExchangeRates: {
- ...metamaskState.tokenExchangeRates,
- [pair]: marketinfo,
- },
- })
-
case actions.UPDATE_TOKENS:
return extend(metamaskState, {
tokens: action.newTokens,
@@ -358,7 +351,7 @@ function reduceMetamask (state, action) {
welcomeScreenSeen: true,
})
- case action.SET_CURRENT_LOCALE:
+ case actions.SET_CURRENT_LOCALE:
return extend(metamaskState, {
currentLocale: action.value,
})
diff --git a/ui/app/selectors.js b/ui/app/selectors.js
index 2bdc39004..60cc264da 100644
--- a/ui/app/selectors.js
+++ b/ui/app/selectors.js
@@ -62,22 +62,15 @@ function getSelectedToken (state) {
}
function getSelectedTokenExchangeRate (state) {
- const tokenExchangeRates = state.metamask.tokenExchangeRates
+ const contractExchangeRates = state.metamask.contractExchangeRates
const selectedToken = getSelectedToken(state) || {}
- const { symbol = '' } = selectedToken
-
- const pair = `${symbol.toLowerCase()}_eth`
- const { rate: tokenExchangeRate = 0 } = tokenExchangeRates[pair] || {}
-
- return tokenExchangeRate
+ const { address } = selectedToken
+ return contractExchangeRates[address] || 0
}
-function getTokenExchangeRate (state, tokenSymbol) {
- const pair = `${tokenSymbol.toLowerCase()}_eth`
- const tokenExchangeRates = state.metamask.tokenExchangeRates
- const { rate: tokenExchangeRate = 0 } = tokenExchangeRates[pair] || {}
-
- return tokenExchangeRate
+function getTokenExchangeRate (state, address) {
+ const contractExchangeRates = state.metamask.contractExchangeRates
+ return contractExchangeRates[address] || 0
}
function conversionRateSelector (state) {
diff --git a/ui/app/send-v2.js b/ui/app/send-v2.js
index d0c28caa2..30d3d3152 100644
--- a/ui/app/send-v2.js
+++ b/ui/app/send-v2.js
@@ -88,17 +88,6 @@ SendTransactionScreen.prototype.updateSendTokenBalance = function (usersToken) {
}
SendTransactionScreen.prototype.componentWillMount = function () {
- const {
- updateTokenExchangeRate,
- selectedToken = {},
- } = this.props
-
- const { symbol } = selectedToken || {}
-
- if (symbol) {
- updateTokenExchangeRate(symbol)
- }
-
this.updateGas()
}
diff --git a/ui/app/unlock.js b/ui/app/unlock.js
index 84d8b7e7c..1325656af 100644
--- a/ui/app/unlock.js
+++ b/ui/app/unlock.js
@@ -7,7 +7,8 @@ const actions = require('./actions')
const getCaretCoordinates = require('textarea-caret')
const EventEmitter = require('events').EventEmitter
const { OLD_UI_NETWORK_TYPE } = require('../../app/scripts/config').enums
-const environmentType = require('../../app/scripts/lib/environment-type')
+const { getEnvironmentType } = require('../../app/scripts/lib/util')
+const { ENVIRONMENT_TYPE_POPUP } = require('../../app/scripts/lib/enums')
const Mascot = require('./components/mascot')
@@ -77,7 +78,7 @@ UnlockScreen.prototype.render = function () {
h('p.pointer', {
onClick: () => {
this.props.dispatch(actions.markPasswordForgotten())
- if (environmentType() === 'popup') {
+ if (getEnvironmentType(window.location.href) === ENVIRONMENT_TYPE_POPUP) {
global.platform.openExtensionInBrowser()
}
},
diff --git a/ui/index.js b/ui/index.js
index 746e28eab..7b6faac76 100644
--- a/ui/index.js
+++ b/ui/index.js
@@ -6,8 +6,7 @@ const configureStore = require('./app/store')
const txHelper = require('./lib/tx-helper')
const { fetchLocale } = require('./i18n-helper')
const { OLD_UI_NETWORK_TYPE, BETA_UI_NETWORK_TYPE } = require('../app/scripts/config').enums
-
-global.log = require('loglevel')
+const log = require('loglevel')
module.exports = launchMetamaskUi
diff --git a/ui/lib/tx-helper.js b/ui/lib/tx-helper.js
index de3f00d2d..0a6f55a63 100644
--- a/ui/lib/tx-helper.js
+++ b/ui/lib/tx-helper.js
@@ -1,4 +1,5 @@
const valuesFor = require('../app/util').valuesFor
+const log = require('loglevel')
module.exports = function (unapprovedTxs, unapprovedMsgs, personalMsgs, typedMessages, network) {
log.debug('tx-helper called with params:')