Merge branch 'master' into dm-docs-1

feature/default_network_editable
Dan 7 years ago
commit 649d2f8b22
  1. 1
      .eslintrc
  2. 27
      .jshintrc
  3. 3
      CHANGELOG.md
  4. 2
      README.md
  5. 912
      app/_locales/cs/messages.json
  6. 20
      app/_locales/hn/messages.json
  7. 2
      app/_locales/index.json
  8. 912
      app/_locales/tml/messages.json
  9. 68
      app/scripts/background.js
  10. 1
      app/scripts/controllers/blacklist.js
  11. 1
      app/scripts/controllers/currency.js
  12. 1
      app/scripts/controllers/infura.js
  13. 1
      app/scripts/controllers/network.js
  14. 1
      app/scripts/controllers/recent-blocks.js
  15. 1
      app/scripts/controllers/shapeshift.js
  16. 77
      app/scripts/controllers/token-rates.js
  17. 1
      app/scripts/controllers/transactions.js
  18. 7
      app/scripts/inpage.js
  19. 49
      app/scripts/lib/ComposableObservableStore.js
  20. 2
      app/scripts/lib/createLoggerMiddleware.js
  21. 9
      app/scripts/lib/enums.js
  22. 19
      app/scripts/lib/environment-type.js
  23. 18
      app/scripts/lib/is-popup-or-notification.js
  24. 1
      app/scripts/lib/local-store.js
  25. 1
      app/scripts/lib/personal-message-manager.js
  26. 1
      app/scripts/lib/seed-phrase-verifier.js
  27. 2
      app/scripts/lib/typed-message-manager.js
  28. 41
      app/scripts/lib/util.js
  29. 121
      app/scripts/metamask-controller.js
  30. 8
      app/scripts/ui.js
  31. 134
      development/states/currency-localization.json
  32. 25
      development/tools/.jsdoc.json
  33. 15
      development/tools/README.md
  34. 21
      development/tools/appveyor.txt
  35. 471
      docs/jsdocs/controllers_transactions.js.html
  36. 486
      docs/jsdocs/global.html
  37. 275
      docs/jsdocs/index.html
  38. 1144
      docs/jsdocs/metamask-controller.js.html
  39. 229
      docs/jsdocs/module.exports_module.exports.html
  40. 117
      docs/jsdocs/platforms_extension.js.html
  41. 25
      docs/jsdocs/scripts/linenumber.js
  42. 202
      docs/jsdocs/scripts/prettify/Apache-License-2.0.txt
  43. 2
      docs/jsdocs/scripts/prettify/lang-css.js
  44. 28
      docs/jsdocs/scripts/prettify/prettify.js
  45. 19
      docs/jsdocs/scripts/semantic.min.js
  46. 626
      docs/jsdocs/styles/jsdoc.css
  47. 3
      docs/jsdocs/styles/override.css
  48. 79
      docs/jsdocs/styles/prettify.css
  49. 364
      docs/jsdocs/styles/semantic.min.css
  50. 78
      docs/team.md
  51. 1
      old-ui/app/app.js
  52. 1
      old-ui/app/components/ens-input.js
  53. 1
      old-ui/app/components/pending-tx.js
  54. 1
      old-ui/app/components/token-list.js
  55. 6
      old-ui/app/conf-tx.js
  56. 1
      old-ui/lib/tx-helper.js
  57. 816
      package-lock.json
  58. 8
      package.json
  59. 28
      test/integration/lib/currency-localization.js
  60. 8
      test/integration/lib/send-new-ui.js
  61. 5
      test/setup.js
  62. 35
      test/unit/ComposableObservableStore.js
  63. 27
      test/unit/balance-formatter-test.js
  64. 29
      test/unit/token-rates-controller.js
  65. 25
      ui/app/actions.js
  66. 1
      ui/app/app.js
  67. 18
      ui/app/components/balance-component.js
  68. 1
      ui/app/components/ens-input.js
  69. 11
      ui/app/components/modals/modal.js
  70. 1
      ui/app/components/pages/home.js
  71. 1
      ui/app/components/pages/keychains/restore-vault.js
  72. 5
      ui/app/components/pages/unlock.js
  73. 19
      ui/app/components/pending-tx/confirm-send-ether.js
  74. 29
      ui/app/components/pending-tx/confirm-send-token.js
  75. 35
      ui/app/components/send/currency-display.js
  76. 1
      ui/app/components/send/send-v2-container.js
  77. 1
      ui/app/components/token-balance.js
  78. 20
      ui/app/components/token-cell.js
  79. 1
      ui/app/components/token-list.js
  80. 18
      ui/app/components/tx-list-item.js
  81. 1
      ui/app/conf-tx.js
  82. 5
      ui/app/first-time/init-menu.js
  83. 1
      ui/app/keychains/hd/restore-vault.js
  84. 1
      ui/app/main-container.js
  85. 1
      ui/app/reducers/app.js
  86. 17
      ui/app/reducers/metamask.js
  87. 19
      ui/app/selectors.js
  88. 11
      ui/app/send-v2.js
  89. 5
      ui/app/unlock.js
  90. 3
      ui/index.js
  91. 1
      ui/lib/tx-helper.js
  92. 2221
      yarn.lock

@ -35,7 +35,6 @@
"globals": {
"document": false,
"log": true,
"navigator": false,
"web3": true,
"window": false

@ -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
}
}

@ -2,7 +2,10 @@
## Current Master
- Correctly format currency conversion for locally selected preferred currency.
- 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

@ -1,6 +1,7 @@
# MetaMask Browser Extension
[![Build Status](https://circleci.com/gh/MetaMask/metamask-extension.svg?style=shield&circle-token=a1ddcf3cd38e29267f254c9c59d556d513e3a1fd)](https://circleci.com/gh/MetaMask/metamask-extension) [![Coverage Status](https://coveralls.io/repos/github/MetaMask/metamask-extension/badge.svg?branch=master)](https://coveralls.io/github/MetaMask/metamask-extension?branch=master) [![Greenkeeper badge](https://badges.greenkeeper.io/MetaMask/metamask-extension.svg)](https://greenkeeper.io/) [![Stories in Ready](https://badge.waffle.io/MetaMask/metamask-extension.png?label=in%20progress&title=waffle.io)](https://waffle.io/MetaMask/metamask-extension)
[Internal documentation](./docs/jsdocs)
## Support
@ -68,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)

@ -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"
}
}

@ -9,7 +9,7 @@
"message": "खिवरण"
},
"accountName": {
"message": "खम"
"message": "खम"
},
"address": {
"message": "ख पत"
@ -21,7 +21,7 @@
"message": "टकन ज"
},
"addTokens": {
"message": "टकन"
"message": "टकन"
},
"amount": {
"message": "रि"
@ -30,7 +30,7 @@
"message": "रि + गस"
},
"appDescription": {
"message": "एथरम बउजर एकसटशन",
"message": "इथिम बउजर एकसटशन",
"description": "आवदन किवरण"
},
"appName": {
@ -53,7 +53,7 @@
"message": "उपलबध बस।"
},
"balances": {
"message": "पक उपलबध बस"
"message": "पक उपलबध बस"
},
"balanceIsInsufficientGas": {
"message": "वरतमन गस कल किए अपरत शष"
@ -78,10 +78,10 @@
"message": "खर"
},
"buyCoinbase": {
"message": "कनबस पर खर"
"message": "कनबस पर खर"
},
"buyCoinbaseExplainer": {
"message": "बिटकइन, एथरम और लइटकइन खरदन और बचनिए दि सबसकपिय तर Coinbase।"
"message": "बिटकइन, इथिम और लइटकइन खरदन और बचनिए दि सबसकपिय तर इनब।"
},
"cancel": {
"message": "रदद कर"
@ -108,7 +108,7 @@
"message": "ज रख"
},
"continueToCoinbase": {
"message": "कनबस कजन रख"
"message": "कनबस कजन रख"
},
"contractDeployment": {
"message": "अनध परििजन व त"
@ -435,13 +435,13 @@
"message": "बज शबद मवल लअरकस वरण ह"
},
"mainnet": {
"message": "मईथरम नटवरक"
"message": "मइथिम नटवरक"
},
"message": {
"message": "सश"
},
"metamaskDescription": {
"message": "मएथरम किए एक सरकित पहचन वट ह।"
"message": "मइथिम किए एक सरकित पहचन वट ह।"
},
"min": {
"message": "ननतम"
@ -649,7 +649,7 @@
"message": "भकन"
},
"sendTokensAnywhere": {
"message": "इम खिकन भ"
"message": "इिम खिकन भ"
},
"settings": {
"message": "सिस"

@ -1,4 +1,5 @@
[
{ "code": "cs", "name": "Czech" },
{ "code": "de", "name": "German" },
{ "code": "en", "name": "English" },
{ "code": "es", "name": "Spanish" },
@ -13,6 +14,7 @@
{ "code": "ru", "name": "Russian" },
{ "code": "sl", "name": "Slovenian" },
{ "code": "th", "name": "Thai" },
{ "code": "tml", "name": "Tamil" },
{ "code": "tr", "name": "Turkish" },
{ "code": "vi", "name": "Vietnamese" },
{ "code": "zh_CN", "name": "Mandarin" },

@ -0,0 +1,912 @@
{
"accept": {
"message": "ஏறகவ"
},
"account": {
"message": "கணக"
},
"accountDetails": {
"message": "கணகிவரஙகள"
},
"accountName": {
"message": "கணகியர"
},
"address": {
"message": "மகவரி"
},
"addCustomToken": {
"message": "தனிபயனகனகவ"
},
"addToken": {
"message": "டகன"
},
"addTokens": {
"message": "டகனகள"
},
"amount": {
"message": "த"
},
"amountPlusGas": {
"message": "த + எரி"
},
"appDescription": {
"message": "எதிிசரி",
"description": "பயனிிளககம"
},
"appName": {
"message": "மடமஸ ",
"description": "பயனியர"
},
"approved": {
"message": "அஙகரிகபபடட"
},
"attemptingConnect": {
"message": "இணக மயறிக ப"
},
"attributions": {
"message": "பணகள"
},
"available": {
"message": "கி"
},
"back": {
"message": "ம"
},
"balance": {
"message": "இர:"
},
"balances": {
"message": "உஙகள இர"
},
"balanceIsInsufficientGas": {
"message": "நடபதமன சமநி"
},
"beta": {
"message": "ப"
},
"betweenMinAndMax": {
"message": "$ 1 க அதிகமகவ அலலத $ 2 க சமமகவ இரக வ.",
"description": "ஹ உள தசம உள என உதவி"
},
"blockiesIdenticon": {
"message": "பி ஐடி பயன"
},
"borrowDharma": {
"message": "தரமதடன கடனகள (ப)"
},
"builtInCalifornia": {
"message": "மடமஸ வடிவமகபபட கலிிி கடடபபடளத."
},
"buy": {
"message": "வக"
},
"buyCoinbase": {
"message": "கஇனபசகவ"
},
"buyCoinbaseExplainer": {
"message": "கஇனபசிறக , எதி மறிடசக மறிக உலகிிகவிரபலமன வழி"
},
"ok": {
"message": "சரி"
},
"cancel": {
"message": "ரத"
},
"classicInterface": {
"message": "கிி இடகத பயனபடதவ"
},
"clickCopy": {
"message": "நகலக கிியவ"
},
"confirm": {
"message": "உறிபடதவ"
},
"confirmed": {
"message": "உறி"
},
"confirmContract": {
"message": "ஒபபநதத உறிபடக"
},
"confirmPassword": {
"message": "கடவ உறிபடக"
},
"confirmTransaction": {
"message": "பரிவரதன உறிபடதவ"
},
"continue": {
"message": "தடர"
},
"continueToCoinbase": {
"message": "கஇனபச ஐதடரவ"
},
"contractDeployment": {
"message": "ஒபபநத வரிபடதல"
},
"conversionProgress": {
"message": "மறமறம"
},
"copiedButton": {
"message": "நகலகபபடடன"
},
"copiedClipboard": {
"message": "கிி நகலகபபடடத"
},
"copiedExclamation": {
"message": "நகலகபபடடன!"
},
"copiedSafe": {
"message": "ந எஙவதக நகலிி"
},
"copy": {
"message": "நகல"
},
"copyToClipboard": {
"message": "கிி நகலகபபடடத"
},
"copyButton": {
"message": " நகல "
},
"copyPrivateKey": {
"message": "இத உஙகள தனிபடட வி (நகலக கிியவ)"
},
"create": {
"message": "உரகவ"
},
"createAccount": {
"message": "உஙகள கணகவஙகள"
},
"createDen": {
"message": "உரகவ"
},
"crypto": {
"message": "கிி",
"description": "பரிற வக (கிிி)"
},
"currentConversion": {
"message": "தறய மறம"
},
"currentNetwork": {
"message": "தறய ந"
},
"customGas": {
"message": "எரி தனிபயனகள"
},
"customToken": {
"message": "தனிபயனகன"
},
"customize": {
"message": "தனிபயனகல"
},
"customRPC": {
"message": "விப RPC ஐ"
},
"decimalsMustZerotoTen": {
"message": "தசமஙகளதபடசம 0, மற 36 க இரக வ."
},
"decimal": {
"message": "தியதிி"
},
"defaultNetwork": {
"message": "எதி பரிவரதனகளன மி வலயமதனிகரம."
},
"denExplainer": {
"message": "உஙகள DEN எனபத உஙகள கடவ-மறிகபபடட சிபகமி."
},
"deposit": {
"message": "வ"
},
"depositBTC": {
"message": "க உஙகளகவரி உஙகள BTC வ:"
},
"depositCoin": {
"message": "உஙகளகவரி $ 1 ஐ க உளிடவ",
"description": "சபஷிி உடனகபபடட நணயத பயனரிடமி"
},
"depositEth": {
"message": "வ எத "
},
"depositEther": {
"message": "வ எதி "
},
"depositFiat": {
"message": "ஃபியட உடன"
},
"depositFromAccount": {
"message": "மற கணகிி"
},
"depositShapeShift": {
"message": "ShapeShift உடன"
},
"depositShapeShiftExplainer": {
"message": "நகள மறற கிிிரனகளதமக வி, உஙகள பணபிரடிக ஈதர வரதகமயல மறயல. கணகி."
},
"details": {
"message": "விவரஙகள"
},
"directDeposit": {
"message": "நரடி"
},
"directDepositEther": {
"message": "நரடிக வ"
},
"directDepositEtherExplainer": {
"message": "நகள ஏறகனவ ஏத இர, நரடிலம உஙகளிய பணபி ஈததரற வின வழி."
},
"done": {
"message": "மிதத"
},
"downloadStateLogs": {
"message": "மில பதிகள பதிிறகக"
},
"dropped": {
"message": "நகபபட"
},
"edit": {
"message": "த"
},
"editAccountName": {
"message": "கணகயரக"
},
"emailUs": {
"message": "எஙகளினஞசல!"
},
"encryptNewDen": {
"message": "உஙகளிய DEN ஐ கிக"
},
"enterPassword": {
"message": "கடவ உளிடவ"
},
"enterPasswordConfirm": {
"message": "உறிபடத உஙகள கடவ உளிடவ"
},
"passwordNotLongEnough": {
"message": "கடவ"
},
"passwordsDontMatch": {
"message": "கடவகள"
},
"etherscanView": {
"message": "Etherscan கணககவ"
},
"exchangeRate": {
"message": "மிிதம"
},
"exportPrivateKey": {
"message": "தனிி ஐ ஏறமதிக"
},
"exportPrivateKeyWarning": {
"message": "தனிபடட விகள உஙகளத ஆபதி ஏறமதிகள."
},
"failed": {
"message": "தி"
},
"fiat": {
"message": "FIAT",
"description": "பரிற வக"
},
"fileImportFail": {
"message": "க இறகமதியவி? இஙிியவ!",
"description": "JSON கி பயனர கணக தஙகள கணக இறகமதிய உதவிறத"
},
"followTwitter": {
"message": "Twitter இல எஙகளிடரவ"
},
"from": {
"message": "இர"
},
"fromToSame": {
"message": "இர மறகவரி அத இரக மி"
},
"fromShapeShift": {
"message": "ShapeShift இலி"
},
"gas": {
"message": "எரி",
"description": "எரிி"
},
"gasFee": {
"message": "எரி கடடணம"
},
"gasLimit": {
"message": "எரி வரம"
},
"gasLimitCalculation": {
"message": "நிிிதஙகளி அடிபடி பரிகபபடட எரி வரமகள கணகிி."
},
"gasLimitRequired": {
"message": "எரி வரம"
},
"gasLimitTooLow": {
"message": "எரி வரமதத 21000 ஆக இரக வ"
},
"generatingSeed": {
"message": "வி உரிறத ..."
},
"gasPrice": {
"message": "எரிி (GWEI)"
},
"gasPriceCalculation": {
"message": "நிிிதஙகளி அடிபடி பரிகபபடட எரிிகளகள கணகிி."
},
"gasPriceRequired": {
"message": "எரிிபடிறத"
},
"getEther": {
"message": "ஈததரி"
},
"getEtherFromFaucet": {
"message": "$ 1 க ஒர இர ஈதரி$1",
"description": "ஈததர ஐநிய பயரிறத"
},
"greaterThanMin": {
"message": "$ 1 க அதிகமகவ அலலத சமமகவ இரக வ",
"description": "ஹ உள தசம உள என உதவி"
},
"here": {
"message": "இங",
"description": "இங-கிியவ- ம தகவல (troubleTokenBalances சிறத)"
},
"hereList": {
"message": "இங ஒர படியல !!!!"
},
"hide": {
"message": "மற"
},
"hideToken": {
"message": "டகன மற"
},
"hideTokenPrompt": {
"message": "டகன மற?"
},
"howToDeposit": {
"message": "எபபடி ஈததரபத?"
},
"holdEther": {
"message": "இதகள ஈததர மறகனகளிக உதவிறத, மற பரவலகபபடட பயனகள உஙகளலமக சயலபடிறத."
},
"import": {
"message": "இறகமதி",
"description": "தகபபடட கிி ஒர கணக இறகமதிய ப அழதவ"
},
"importAccount": {
"message": "கணக இறகமதிக"
},
"importAccountMsg": {
"message":" இறகமதியபபடட கணக உஙகளதலி உரகபபடட ம கணகிலமடரயதக இர. இறகமதியபபடட கணககள பறி அறிக "
},
"importAnAccount": {
"message": "ஒர கணக இறகமதிக"
},
"importDen": {
"message": "இறகமதி DEN இறகமதி"
},
"imported": {
"message": "இறகமதி",
"description": "ஒர கணகக விபலகி ஏறறபபடடதிறத"
},
"infoHelp": {
"message": "தகவல மற உதவி"
},
"insufficientFunds": {
"message": "பன பணம இல."
},
"insufficientTokens": {
"message": "பன டகனகள."
},
"invalidAddress": {
"message": "தவறன மகவரி"
},
"invalidAddressRecipient": {
"message": "பநரகவரி தவறனத"
},
"invalidGasParams": {
"message": "தவறன எரி அளவகள"
},
"invalidInput": {
"message": "தவறன உள.."
},
"invalidRequest": {
"message": "தவறன கி"
},
"invalidRPC": {
"message": "தவறன RPC URI"
},
"jsonFail": {
"message": "ஏத தவற நடநிடத. உஙகள JSON க ஒழக வடிவமகபபடளத எனபத உறிபடதவ"
},
"jsonFile": {
"message": "JSON க",
"description": "ஒர கணக இறகமதிய வடிவமகபபடளத"
},
"keepTrackTokens": {
"message": "உஙகளடமஸ கணகடனகளிய டகனகள கணிகள."
},
"kovan": {
"message": "கவன"
},
"knowledgeDataBase": {
"message": "எஙகள அறி தளதிடவ"
},
"max": {
"message": "ம"
},
"learnMore": {
"message": "ம அறிக"
},
"lessThanMax": {
"message": "$ 1 ககவ அலலத சமமகவ இரக வ.",
"description": "ஹ உள தசம உள என உதவி"
},
"likeToAddTokens": {
"message": "இநத டகனகளக விிகள?"
},
"links": {
"message": "இணகள"
},
"limit": {
"message": "அளவ"
},
"loading": {
"message": "ஏறதல ..."
},
"loadingTokens": {
"message": "டகனகள ஏறிறத ..."
},
"localhost": {
"message": "லகல 8545"
},
"login": {
"message": "உள"
},
"logout": {
"message": "வி"
},
"loose": {
"message": "ல"
},
"loweCaseWords": {
"message": "விகள எழகள மட"
},
"mainnet": {
"message": "மதன எதி"
},
"message": {
"message": "சி"
},
"metamaskDescription": {
"message": "மடமஸ எனபத ஒரன அடள வ எதி"
},
"min": {
"message": "கதபடச"
},
"myAccounts": {
"message": "எனத கணககள"
},
"mustSelectOne": {
"message": "கதத 1 டகனக வ."
},
"needEtherInWallet": {
"message": "மடமஸ ஐ பயனபடி பரவலகபபடட பயனகளடனடரள, உஙகள பணபபரிறதி ஈதர."
},
"needImportFile": {
"message": "இறகமதிய ஒரகளக வ.",
"description": "பயனர ஒர கணகியம மறடர ஒரக வ"
},
"needImportPassword": {
"message": "நகளத க ஒர கடவ உளிட வ.",
"description": "ஒர கணக இறகமதிய கடவ மற"
},
"negativeETH": {
"message": "ETH எதிமற அளவகள அனப மி."
},
"networks": {
"message": "ந"
},
"newAccount": {
"message": "பிய கணக"
},
"newAccountNumberName": {
"message": "கணக $ 1",
"description": "கணக கணக உரவதற அடத கணகி இயலியர உரகபபட"
},
"newContract": {
"message": "பிய ஒபபநதம"
},
"newPassword": {
"message": "பிய கடவ (min 8 எழகள)"
},
"newRecipient": {
"message": "பிய பநர"
},
"newRPC": {
"message": "பிய RPC URL"
},
"next": {
"message": "அடத"
},
"noAddressForName": {
"message": "இநத பயரன மகவரி அமகபபடவி."
},
"noDeposits": {
"message": "எநத வகளிகவி"
},
"noTransactionHistory": {
"message": "பரிவரதன வரல இல."
},
"noTransactions": {
"message": "பரிவரதனகள இல"
},
"notStarted": {
"message": "தவஙகவி"
},
"oldUI": {
"message": "பழய UI"
},
"oldUIMessage": {
"message": "நகள பழய UI கிிகள. ம வலதி உளள விபதிலமிய UI ஐ மறல."
},
"or": {
"message": "அலலத",
"description": "ஒரிய கணக உரக அலலத இறகமதிவதற இட"
},
"passwordCorrect": {
"message": "தயவ உஙகள கடவ சரினத என உறிபடதவ."
},
"passwordMismatch": {
"message": "கடவகளதவி",
"description": "கடவ உரகதி, இரணிய கடவலஙகளதவி"
},
"passwordShort": {
"message": "கடவட கலமக இல",
"description": "கடவ உரகதி, பனதக இர கடவ"
},
"pastePrivateKey": {
"message": "இங உஙகள தனிபடட வி சரத ஒடக:",
"description": "ஒர தனிபடட வி ஒர கணக இறகமதிய"
},
"pasteSeed": {
"message": "இங உஙகளிடர ஒடடவ!"
},
"personalAddressDetected": {
"message": "தனிபடட மகவரி கணடறியபபடடத. டகன ஒபபநத மகவரி உளிடவ."
},
"pleaseReviewTransaction": {
"message": "உஙகள பரிவரதன மதியவ."
},
"popularTokens": {
"message": "பிரபலமன டகனகள"
},
"privacyMsg": {
"message": "தனிி"
},
"privateKey": {
"message": "தனிபடட வி",
"description": "ஒர கணக இறகமதிய பயனபடத இநத வககவ"
},
"privateKeyWarning": {
"message": "எசசரி: இநத வி எபிிட வ. உஙகள தனிபடட விகளட எவர உஙகள கணகி உளள எநத சகளிடல."
},
"privateNetwork": {
"message": "தனி"
},
"qrCode": {
"message": "QR கி"
},
"readdToken": {
"message": "உஙகள கணகிபஙகளி \"டகன\" எனபதனலமகள எதிலதி இநத டகனகல."
},
"readMore": {
"message": "மிக இங."
},
"readMore2": {
"message": "மிக."
},
"receive": {
"message": "பக"
},
"recipientAddress": {
"message": "பநரகவரி"
},
"refundAddress": {
"message": "உஙகள பணதிி அனகவரி"
},
"rejected": {
"message": "நிகரிகபபடடத"
},
"resetAccount": {
"message": "கணகடம"
},
"restoreFromSeed": {
"message": "விியதிிகவ"
},
"restoreVault": {
"message": "வகவ"
},
"required": {
"message": "தன"
},
"retryWithMoreGas": {
"message": "இங அதிக எரிியறிகவ"
},
"walletSeed": {
"message": "வி"
},
"revealSeedWords": {
"message": "விகளிபடத"
},
"revealSeedWordsWarning": {
"message": "உஙகளிகள ஒர இடதிக வ! உஙகள எல கணககளிட இநத வகள பயனபடதபபடல."
},
"revert": {
"message": "மியம"
},
"rinkeby": {
"message": "ரிய ட"
},
"ropsten": {
"message": "ர"
},
"currentRpc": {
"message": "தறய RPC"
},
"connectingToMainnet": {
"message": "மிய எதி இண"
},
"connectingToRopsten": {
"message": "ரடன இணிறத"
},
"connectingToKovan": {
"message": "கவனடன இணதல"
},
"connectingToRinkeby": {
"message": "ரிய டடன இணிறத"
},
"connectingToUnknown": {
"message": "தித நடன இணிறத"
},
"sampleAccountName": {
"message": "உதரணமக எனதிய கணக",
"description": "தஙகள கணகி மனிதர படிககிய பயர கர பயனரிள உதவகள"
},
"save": {
"message": "சி"
},
"reprice_title": {
"message": "ரி பரிவரதன"
},
"reprice_subtitle": {
"message": "உஙகள பரிவரதனகளயறி அதிகரிக உஙகள எரிி அதிகரிகவ"
},
"saveAsFile": {
"message": "கக சிகவ",
"description": "கணக ஏறமதியல"
},
"saveSeedAsFile": {
"message": "க என விகளிகவ"
},
"search": {
"message": "தடல"
},
"secretPhrase": {
"message": "உஙகளடகதபதறக இங உஙகள ரகசிய பனிரணடர உளிடவ."
},
"newPassword8Chars": {
"message": "பிய கடவ (கதபடசம 8 எழகள)"
},
"seedPhraseReq": {
"message": "விியஙகள 12 வகளடவ"
},
"select": {
"message": "த"
},
"selectCurrency": {
"message": "நணயத"
},
"selectService": {
"message": "சகவ"
},
"selectType": {
"message": "வக"
},
"send": {
"message": "அன"
},
"sendETH": {
"message": "ETH ஐ அன"
},
"sendTokens": {
"message": "டகனகள அனபவ"
},
"onlySendToEtherAddress": {
"message": "ETH ஐ ஒர எதரிகவரி மட அனபவ."
},
"searchTokens": {
"message": "தடலகன"
},
"sendTokensAnywhere": {
"message": "யடனகனகள அனபவ எதி கணக"
},
"settings": {
"message": "அமகள"
},
"info": {
"message": "தகவல"
},
"shapeshiftBuy": {
"message": "Shapeshift உடனகவ"
},
"showPrivateKeys": {
"message": "தனிபடட விகளி"
},
"showQRCode": {
"message": "QR கி"
},
"sign": {
"message": "உள"
},
"signed": {
"message": "கபமிடபபடட"
},
"signMessage": {
"message": "சி பதிக"
},
"signNotice": {
"message": "இநத சிிபமிடல \nஆபதன பகக விகள இரகல. \n உஙகளத கணகிக நமபகிய தளஙகளிிகள மடபமிகள. \n இநத ஆபதன ம எதில பதிி அகறறபபட."
},
"sigRequest": {
"message": "கபமி"
},
"sigRequested": {
"message": "கபமரபபடடத"
},
"spaceBetween": {
"message": "வகள இட இடி மட இரக மி"
},
"status": {
"message": "நி"
},
"stateLogs": {
"message": "மில பதிகள"
},
"stateLogsDescription": {
"message": "மில பதிகள உஙகள கணககவரிகள மற பரிறஙகள அனிளன."
},
"stateLogError": {
"message": "மில பதிகளபதிி."
},
"submit": {
"message": "சமரி"
},
"submitted": {
"message": "சமரிகபபடடத"
},
"supportCenter": {
"message": "எஙகள ஆதரவயதிடவ"
},
"symbolBetweenZeroTen": {
"message": "கி 0 மற 10 எழகள இடி இரக வ."
},
"takesTooLong": {
"message": "நட நரம எடிறத?"
},
"terms": {
"message": "பயனிிகள"
},
"testFaucet": {
"message": "சதன"
},
"to": {
"message": "பநர: "
},
"toETHviaShapeShift": {
"message": "$ 1 மதல ETH வர வடிவம",
"description": "சிடககதி வககளிிரபபபபட"
},
"tokenAddress": {
"message": "டகனகவரி"
},
"tokenAlreadyAdded": {
"message": "டகன ஏறகனவகபபடடத."
},
"tokenBalance": {
"message": "உஙகளகன இர:"
},
"tokenSelection": {
"message": "டகனகள அலலதிரபல டகனகளி படியலிிகவ."
},
"tokenSymbol": {
"message": "டகனினம"
},
"tokenWarning1": {
"message": "உஙகளடமஸ கணகடனகளிய டகனகள கணிகள. வ கணக பயனபடிகனகளிி, அநத டகனகள இங."
},
"total": {
"message": "மத"
},
"transactions": {
"message": "பரிவரதனகள"
},
"transactionError": {
"message": "பரிவரதனி. விி ஒபபநததிிிிலக."
},
"transactionMemo": {
"message": "பரிவரதனி (விபம)"
},
"transactionNumber": {
"message": "பரிவரதன எண"
},
"transfers": {
"message": "இடமறஙகள"
},
"troubleTokenBalances": {
"message": "உஙகளகனிகள ஏறவதிிகல ஏறபடடத. நகள அவரகளக மி.",
"description": "டகனிகளண ஒர இண (இங) தடர"
},
"twelveWords": {
"message": "இநத 12 வகள உஙகள கணகக ஒர வழி. \n அவற எஙவதகவ ரகசியமகவிகவ."
},
"typePassword": {
"message": "உஙகள கடவ தடடசயவ"
},
"uiWelcome": {
"message": "பிய UI (ப) க வரவி"
},
"uiWelcomeMessage": {
"message": "இபகளிய ம UI ஐ பயனபடிகள. சிகள, டகனகள அனிய அமசஙகளயறிகவ, உஙகளிடம ஏதிகல இர எஙகளியபபடதவ."
},
"unapproved": {
"message": "அஙகரிகபபடத"
},
"unavailable": {
"message": "கிகவி"
},
"unknown": {
"message": "தித"
},
"unknownNetwork": {
"message": "அறியபபடத தனி"
},
"unknownNetworkId": {
"message": "தித ந ஐடி"
},
"uriErrorMsg": {
"message": "URI கள சரின HTTP / HTTPS ம."
},
"usaOnly": {
"message": "அமி மட",
"description": "இநத பரிறத பயனபடி அமிி உளளவரகள மட இத வரயறகபபடிறத"
},
"usedByClients": {
"message": "பலிளரகள பல பயனபடிய"
},
"useOldUI": {
"message": "உஸ ஓல உய "
},
"validFileImport": {
"message": "இறகமதிய சரின கக வ."
},
"vaultCreated": {
"message": "வ உரகபபடடத"
},
"viewAccount": {
"message": "கணக"
},
"visitWebSite": {
"message": "எஙகள வலதளதிடவ"
},
"warning": {
"message": "எசசரி"
},
"welcomeBeta": {
"message": "ம வரக"
},
"whatsThis": {
"message": "இத எனன?"
},
"yourSigRequested": {
"message": "உஙகளபமரபபடிறத"
},
"youSign": {
"message": "நகளிிகள"
}
}

@ -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()
}
})
}

@ -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')

@ -1,5 +1,6 @@
const ObservableStore = require('obs-store')
const extend = require('xtend')
const log = require('loglevel')
// every ten minutes
const POLLING_INTERVAL = 600000

@ -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

@ -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']

@ -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 {

@ -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

@ -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

@ -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

@ -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

@ -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

@ -1,3 +1,5 @@
const log = require('loglevel')
// log rpc activity
module.exports = createLoggerMiddleware

@ -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,
}

@ -1,19 +0,0 @@
/**
* Used to determine the window type through which the app is being viewed.
* - 'popup' refers to the extension opened through the browser app icon (in top right corner in chrome and firefox)
* - 'responsive' refers to the main browser window
* - 'notification' refers to the popup that appears in its own window when taking action outside of metamask
*
* @returns {string} A single word label that represents the type of window through which the app is being viewed
*
*/
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'
}
}

@ -1,18 +0,0 @@
/**
* Indicates whether the user is viewing the app through an extension like window or through a notification.
* Used to make some style decisions on the frontend, and when deciding whether to close the popup in the backend.
*
* @returns {string} Returns 'popup' if the user is viewing through the browser ('home.html') or popup extension
* ('popup.html'). Otherwise it returns 'notification'.
*
*/
module.exports = function isPopupOrNotification () {
const url = window.location.href
if (url.match(/popup.html(?:\?.+)*$/) ||
url.match(/home.html(?:\?.+)*$/) || url.match(/home.html(?:#.*)*$/)) {
return 'popup'
} else {
return 'notification'
}
}

@ -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() {

@ -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 {

@ -1,4 +1,5 @@
const KeyringController = require('eth-keyring-controller')
const log = require('loglevel')
const seedPhraseVerifier = {

@ -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) {

@ -1,14 +1,11 @@
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')
/**
* Generates an example stack trace
@ -21,6 +18,25 @@ function getStack () {
return stack
}
/**
* Used to determine the window type through which the app is being viewed.
* - 'popup' refers to the extension opened through the browser app icon (in top right corner in chrome and firefox)
* - 'responsive' refers to the main browser window
* - 'notification' refers to the popup that appears in its own window when taking action outside of metamask
*
* @returns {string} A single word label that represents the type of window through which the app is being viewed
*
*/
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
}
}
/**
* Checks whether a given balance of ETH, represented as a hex string, is sufficient to pay a value plus a gas fee
*
@ -82,3 +98,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,
}

@ -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
}
}

@ -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()
}

@ -0,0 +1,134 @@
{
"metamask": {
"isInitialized": true,
"isUnlocked": true,
"featureFlags": {"betaUI": true},
"rpcTarget": "https://rawtestrpc.metamask.io/",
"identities": {
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825": {
"address": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
"name": "Send Account 1"
},
"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb": {
"address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb",
"name": "Send Account 2"
},
"0x2f8d4a878cfa04a6e60d46362f5644deab66572d": {
"address": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d",
"name": "Send Account 3"
},
"0xd85a4b6a394794842887b8284293d69163007bbb": {
"address": "0xd85a4b6a394794842887b8284293d69163007bbb",
"name": "Send Account 4"
}
},
"unapprovedTxs": {},
"currentCurrency": "USD",
"conversionRate": 19855,
"conversionDate": 1489013762,
"noActiveNotices": true,
"frequentRpcList": [],
"network": "3",
"accounts": {
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825": {
"code": "0x",
"balance": "0x47c9d71831c76efe",
"nonce": "0x1b",
"address": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"
},
"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb": {
"code": "0x",
"balance": "0x37452b1315889f80",
"nonce": "0xa",
"address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb"
},
"0x2f8d4a878cfa04a6e60d46362f5644deab66572d": {
"code": "0x",
"balance": "0x30c9d71831c76efe",
"nonce": "0x1c",
"address": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d"
},
"0xd85a4b6a394794842887b8284293d69163007bbb": {
"code": "0x",
"balance": "0x0",
"nonce": "0x0",
"address": "0xd85a4b6a394794842887b8284293d69163007bbb"
}
},
"addressBook": [
{
"address": "0x06195827297c7a80a443b6894d3bdb8824b43896",
"name": "Address Book Account 1"
}
],
"tokens": [],
"transactions": {},
"selectedAddressTxList": [],
"unapprovedMsgs": {},
"unapprovedMsgCount": 0,
"unapprovedPersonalMsgs": {},
"unapprovedPersonalMsgCount": 0,
"keyringTypes": [
"Simple Key Pair",
"HD Key Tree"
],
"keyrings": [
{
"type": "HD Key Tree",
"accounts": [
"fdea65c8e26263f6d9a1b5de9555d2931a33b825",
"c5b8dbac4c1d3f152cdeb400e2313f309c410acb",
"2f8d4a878cfa04a6e60d46362f5644deab66572d"
]
},
{
"type": "Simple Key Pair",
"accounts": [
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"
]
}
],
"selectedAddress": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
"currentCurrency": "PHP",
"provider": {
"type": "testnet"
},
"shapeShiftTxList": [],
"lostAccounts": [],
"send": {
"gasLimit": null,
"gasPrice": null,
"gasTotal": "0xb451dc41b578",
"tokenBalance": null,
"from": "",
"to": "",
"amount": "0x0",
"memo": "",
"errors": {},
"maxModeOn": false,
"editingTransactionId": null
},
"currentLocale": "en"
},
"appState": {
"menuOpen": false,
"currentView": {
"name": "accountDetail",
"detailView": null,
"context": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc"
},
"accountDetail": {
"subview": "transactions"
},
"modal": {
"modalState": {},
"previousModalState": {}
},
"transForward": true,
"isLoading": false,
"warning": null,
"scrollToBottom": false,
"forgottenPassword": null
},
"identities": {}
}

@ -0,0 +1,25 @@
{
"tags": {
"allowUnknownTags": false
},
"source": {
"include": "app/scripts/",
"includePattern": ".js$",
"excludePattern": "(node_modules/|docs)"
},
"plugins": [
"plugins/markdown"
],
"opts": {
"template": "node_modules/radgrad-jsdoc-template/",
"encoding": "utf8",
"destination": "docs/jsdocs",
"recurse": true,
"verbose": true
},
"templates": {
"cleverLinks": false,
"monospaceLinks": false
}
}

@ -0,0 +1,15 @@
# Development Tools & Configurations
This folder contains configuration files which are used by the the different
development-tools, like e.g. JsDoc.
## Appveyor
https://www.appveyor.com/docs/build-configuration/#alternative-yaml-file-location
Withtin the configuration, point to a weblocation of a txt config file:
https://ci.appveyor.com/project/lazaridiscom/mm-vault/settings
https://raw.githubusercontent.com/lazaridiscom/mm-vault/master/dev/tools/appveyor.txt

@ -0,0 +1,21 @@
# Test against the latest version of this Node.js version
environment:
nodejs_version: "8"
# Install scripts. (runs after repo cloning)
install:
# Get the latest stable version of Node.js or io.js
- ps: Install-Product node $env:nodejs_version
# install modules
- npm install
# Post-install test scripts.
test_script:
# Output useful info for debugging.
- node --version
- npm --version
# run tests
- npm test
# Don't actually build.
build: off

@ -0,0 +1,471 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="google" content="notranslate">
<meta http-equiv="Content-Language" content="en">
<title>controllers/transactions.js - Documentation</title>
<script src="scripts/prettify/prettify.js"></script>
<script src="scripts/prettify/lang-css.js"></script>
<script
src="https://code.jquery.com/jquery-3.1.1.min.js"
integrity="sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8="
crossorigin="anonymous"></script>
<script src="scripts/semantic.min.js"></script>
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link type="text/css" rel="stylesheet" href="https://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css">
<link type="text/css" rel="stylesheet" href="styles/prettify.css">
<link type="text/css" rel="stylesheet" href="styles/jsdoc.css">
<link type="text/css" rel="stylesheet" href="styles/semantic.min.css">
<link type="text/css" rel="stylesheet" href="styles/override.css">
</head>
<body>
<input type="checkbox" id="nav-trigger" class="nav-trigger" />
<label for="nav-trigger" class="navicon-button x">
<div class="navicon"></div>
</label>
<label for="nav-trigger" class="overlay"></label>
<nav>
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><div class="ui vertical accordion"><div class="title"><div class="ui list"><div class="item"><i class="inverted dropdown icon"></i><a href="module.exports_module.exports.html">exports</a></div></div></div></li></div></ul><h3><a href="global.html">Global</a></h3>
</nav>
<div id="main">
<h1 class="page-title">controllers/transactions.js</h1>
<section>
<article>
<pre class="prettyprint source linenums"><code>const EventEmitter = require('events')
const ObservableStore = require('obs-store')
const ethUtil = require('ethereumjs-util')
/**
* @file The transaction controller. Receives incoming transactions, and emits events for various states of their processing.
* @copyright Copyright (c) 2018 MetaMask
* @license MIT
*/
const Transaction = require('ethereumjs-tx')
const EthQuery = require('ethjs-query')
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')
/*
Transaction Controller is an aggregate of sub-controllers and trackers
composing them in a way to be exposed to the metamask controller
- txStateManager
responsible for the state of a transaction and
storing the transaction
- pendingTxTracker
watching blocks for transactions to be include
and emitting confirmed events
- txGasUtil
gas calculations and safety buffering
- nonceTracker
calculating nonces
*/
module.exports = class TransactionController extends EventEmitter {
constructor (opts) {
super()
this.networkStore = opts.networkStore || new ObservableStore({})
this.preferencesStore = opts.preferencesStore || new ObservableStore({})
this.provider = opts.provider
this.blockTracker = opts.blockTracker
this.signEthTx = opts.signTransaction
this.getGasPrice = opts.getGasPrice
this.memStore = new ObservableStore({})
this.query = new EthQuery(this.provider)
this.txGasUtil = new TxGasUtil(this.provider)
this.txStateManager = new TransactionStateManager({
initState: opts.initState,
txHistoryLimit: opts.txHistoryLimit,
getNetwork: this.getNetwork.bind(this),
})
this.txStateManager.getFilteredTxList({
status: 'unapproved',
loadingDefaults: true,
}).forEach((tx) => {
this.addTxDefaults(tx)
.then((txMeta) => {
txMeta.loadingDefaults = false
this.txStateManager.updateTx(txMeta, 'transactions: gas estimation for tx on boot')
}).catch((error) => {
this.txStateManager.setTxStatusFailed(tx.id, error)
})
})
this.txStateManager.getFilteredTxList({
status: 'approved',
}).forEach((txMeta) => {
const txSignError = new Error('Transaction found as "approved" during boot - possibly stuck during signing')
this.txStateManager.setTxStatusFailed(txMeta.id, txSignError)
})
this.store = this.txStateManager.store
this.txStateManager.on('tx:status-update', this.emit.bind(this, 'tx:status-update'))
this.nonceTracker = new NonceTracker({
provider: this.provider,
getPendingTransactions: this.txStateManager.getPendingTransactions.bind(this.txStateManager),
getConfirmedTransactions: (address) => {
return this.txStateManager.getFilteredTxList({
from: address,
status: 'confirmed',
err: undefined,
})
},
})
this.pendingTxTracker = new PendingTransactionTracker({
provider: this.provider,
nonceTracker: this.nonceTracker,
publishTransaction: (rawTx) => this.query.sendRawTransaction(rawTx),
getPendingTransactions: this.txStateManager.getPendingTransactions.bind(this.txStateManager),
getCompletedTransactions: this.txStateManager.getConfirmedTransactions.bind(this.txStateManager),
})
this.txStateManager.store.subscribe(() => this.emit('update:badge'))
this.pendingTxTracker.on('tx:warning', (txMeta) => {
this.txStateManager.updateTx(txMeta, 'transactions/pending-tx-tracker#event: tx:warning')
})
this.pendingTxTracker.on('tx:confirmed', (txId) => this._markNonceDuplicatesDropped(txId))
this.pendingTxTracker.on('tx:failed', this.txStateManager.setTxStatusFailed.bind(this.txStateManager))
this.pendingTxTracker.on('tx:block-update', (txMeta, latestBlockNumber) => {
if (!txMeta.firstRetryBlockNumber) {
txMeta.firstRetryBlockNumber = latestBlockNumber
this.txStateManager.updateTx(txMeta, 'transactions/pending-tx-tracker#event: tx:block-update')
}
})
this.pendingTxTracker.on('tx:retry', (txMeta) => {
if (!('retryCount' in txMeta)) txMeta.retryCount = 0
txMeta.retryCount++
this.txStateManager.updateTx(txMeta, 'transactions/pending-tx-tracker#event: tx:retry')
})
this.blockTracker.on('block', this.pendingTxTracker.checkForTxInBlock.bind(this.pendingTxTracker))
// this is a little messy but until ethstore has been either
// removed or redone this is to guard against the race condition
this.blockTracker.on('latest', this.pendingTxTracker.resubmitPendingTxs.bind(this.pendingTxTracker))
this.blockTracker.on('sync', this.pendingTxTracker.queryPendingTxs.bind(this.pendingTxTracker))
// memstore is computed from a few different stores
this._updateMemstore()
this.txStateManager.store.subscribe(() => this._updateMemstore())
this.networkStore.subscribe(() => this._updateMemstore())
this.preferencesStore.subscribe(() => this._updateMemstore())
}
getState () {
return this.memStore.getState()
}
getNetwork () {
return this.networkStore.getState()
}
getSelectedAddress () {
return this.preferencesStore.getState().selectedAddress
}
getUnapprovedTxCount () {
return Object.keys(this.txStateManager.getUnapprovedTxList()).length
}
getPendingTxCount (account) {
return this.txStateManager.getPendingTransactions(account).length
}
getFilteredTxList (opts) {
return this.txStateManager.getFilteredTxList(opts)
}
getChainId () {
const networkState = this.networkStore.getState()
const getChainId = parseInt(networkState)
if (Number.isNaN(getChainId)) {
return 0
} else {
return getChainId
}
}
wipeTransactions (address) {
this.txStateManager.wipeTransactions(address)
}
// Adds a tx to the txlist
addTx (txMeta) {
this.txStateManager.addTx(txMeta)
this.emit(`${txMeta.id}:unapproved`, txMeta)
}
async newUnapprovedTransaction (txParams, opts = {}) {
log.debug(`MetaMaskController newUnapprovedTransaction ${JSON.stringify(txParams)}`)
const initialTxMeta = await this.addUnapprovedTransaction(txParams)
initialTxMeta.origin = opts.origin
this.txStateManager.updateTx(initialTxMeta, '#newUnapprovedTransaction - adding the origin')
// listen for tx completion (success, fail)
return new Promise((resolve, reject) => {
this.txStateManager.once(`${initialTxMeta.id}:finished`, (finishedTxMeta) => {
switch (finishedTxMeta.status) {
case 'submitted':
return resolve(finishedTxMeta.hash)
case 'rejected':
return reject(new Error('MetaMask Tx Signature: User denied transaction signature.'))
case 'failed':
return reject(new Error(finishedTxMeta.err.message))
default:
return reject(new Error(`MetaMask Tx Signature: Unknown problem: ${JSON.stringify(finishedTxMeta.txParams)}`))
}
})
})
}
async addUnapprovedTransaction (txParams) {
// validate
const normalizedTxParams = this._normalizeTxParams(txParams)
this._validateTxParams(normalizedTxParams)
// construct txMeta
let txMeta = this.txStateManager.generateTxMeta({ txParams: normalizedTxParams })
this.addTx(txMeta)
this.emit('newUnapprovedTx', txMeta)
// add default tx params
try {
txMeta = await this.addTxDefaults(txMeta)
} catch (error) {
console.log(error)
this.txStateManager.setTxStatusFailed(txMeta.id, error)
throw error
}
txMeta.loadingDefaults = false
// save txMeta
this.txStateManager.updateTx(txMeta)
return txMeta
}
async addTxDefaults (txMeta) {
const txParams = txMeta.txParams
// ensure value
txMeta.gasPriceSpecified = Boolean(txParams.gasPrice)
let gasPrice = txParams.gasPrice
if (!gasPrice) {
gasPrice = this.getGasPrice ? this.getGasPrice() : await this.query.gasPrice()
}
txParams.gasPrice = ethUtil.addHexPrefix(gasPrice.toString(16))
txParams.value = txParams.value || '0x0'
// set gasLimit
return await this.txGasUtil.analyzeGasUsage(txMeta)
}
async retryTransaction (originalTxId) {
const originalTxMeta = this.txStateManager.getTx(originalTxId)
const lastGasPrice = originalTxMeta.txParams.gasPrice
const txMeta = this.txStateManager.generateTxMeta({
txParams: originalTxMeta.txParams,
lastGasPrice,
loadingDefaults: false,
})
this.addTx(txMeta)
this.emit('newUnapprovedTx', txMeta)
return txMeta
}
async updateTransaction (txMeta) {
this.txStateManager.updateTx(txMeta, 'confTx: user updated transaction')
}
async updateAndApproveTransaction (txMeta) {
this.txStateManager.updateTx(txMeta, 'confTx: user approved transaction')
await this.approveTransaction(txMeta.id)
}
async approveTransaction (txId) {
let nonceLock
try {
// approve
this.txStateManager.setTxStatusApproved(txId)
// get next nonce
const txMeta = this.txStateManager.getTx(txId)
const fromAddress = txMeta.txParams.from
// wait for a nonce
nonceLock = await this.nonceTracker.getNonceLock(fromAddress)
// add nonce to txParams
// if txMeta has lastGasPrice then it is a retry at same nonce with higher
// gas price transaction and their for the nonce should not be calculated
const nonce = txMeta.lastGasPrice ? txMeta.txParams.nonce : nonceLock.nextNonce
txMeta.txParams.nonce = ethUtil.addHexPrefix(nonce.toString(16))
// add nonce debugging information to txMeta
txMeta.nonceDetails = nonceLock.nonceDetails
this.txStateManager.updateTx(txMeta, 'transactions#approveTransaction')
// sign transaction
const rawTx = await this.signTransaction(txId)
await this.publishTransaction(txId, rawTx)
// must set transaction to submitted/failed before releasing lock
nonceLock.releaseLock()
} catch (err) {
this.txStateManager.setTxStatusFailed(txId, err)
// must set transaction to submitted/failed before releasing lock
if (nonceLock) nonceLock.releaseLock()
// continue with error chain
throw err
}
}
async signTransaction (txId) {
const txMeta = this.txStateManager.getTx(txId)
// add network/chain id
const chainId = this.getChainId()
const txParams = Object.assign({}, txMeta.txParams, { chainId })
// sign tx
const fromAddress = txParams.from
const ethTx = new Transaction(txParams)
await this.signEthTx(ethTx, fromAddress)
// set state to signed
this.txStateManager.setTxStatusSigned(txMeta.id)
const rawTx = ethUtil.bufferToHex(ethTx.serialize())
return rawTx
}
async publishTransaction (txId, rawTx) {
const txMeta = this.txStateManager.getTx(txId)
txMeta.rawTx = rawTx
this.txStateManager.updateTx(txMeta, 'transactions#publishTransaction')
const txHash = await this.query.sendRawTransaction(rawTx)
this.setTxHash(txId, txHash)
this.txStateManager.setTxStatusSubmitted(txId)
}
async cancelTransaction (txId) {
this.txStateManager.setTxStatusRejected(txId)
}
// receives a txHash records the tx as signed
setTxHash (txId, txHash) {
// Add the tx hash to the persisted meta-tx object
const txMeta = this.txStateManager.getTx(txId)
txMeta.hash = txHash
this.txStateManager.updateTx(txMeta, 'transactions#setTxHash')
}
//
// PRIVATE METHODS
//
_normalizeTxParams (txParams) {
// functions that handle normalizing of that key in txParams
const whiteList = {
from: from => ethUtil.addHexPrefix(from).toLowerCase(),
to: to => ethUtil.addHexPrefix(txParams.to).toLowerCase(),
nonce: nonce => ethUtil.addHexPrefix(nonce),
value: value => ethUtil.addHexPrefix(value),
data: data => ethUtil.addHexPrefix(data),
gas: gas => ethUtil.addHexPrefix(gas),
gasPrice: gasPrice => ethUtil.addHexPrefix(gasPrice),
}
// apply only keys in the whiteList
const normalizedTxParams = {}
Object.keys(whiteList).forEach((key) => {
if (txParams[key]) normalizedTxParams[key] = whiteList[key](txParams[key])
})
return normalizedTxParams
}
_validateTxParams (txParams) {
this._validateFrom(txParams)
this._validateRecipient(txParams)
if ('value' in txParams) {
const value = txParams.value.toString()
if (value.includes('-')) {
throw new Error(`Invalid transaction value of ${txParams.value} not a positive number.`)
}
if (value.includes('.')) {
throw new Error(`Invalid transaction value of ${txParams.value} number must be in wei`)
}
}
}
_validateFrom (txParams) {
if ( !(typeof txParams.from === 'string') ) throw new Error(`Invalid from address ${txParams.from} not a string`)
if (!ethUtil.isValidAddress(txParams.from)) throw new Error('Invalid from address')
}
_validateRecipient (txParams) {
if (txParams.to === '0x' || txParams.to === null ) {
if (txParams.data) {
delete txParams.to
} else {
throw new Error('Invalid recipient address')
}
} else if ( txParams.to !== undefined &amp;&amp; !ethUtil.isValidAddress(txParams.to) ) {
throw new Error('Invalid recipient address')
}
return txParams
}
_markNonceDuplicatesDropped (txId) {
this.txStateManager.setTxStatusConfirmed(txId)
// get the confirmed transactions nonce and from address
const txMeta = this.txStateManager.getTx(txId)
const { nonce, from } = txMeta.txParams
const sameNonceTxs = this.txStateManager.getFilteredTxList({nonce, from})
if (!sameNonceTxs.length) return
// mark all same nonce transactions as dropped and give i a replacedBy hash
sameNonceTxs.forEach((otherTxMeta) => {
if (otherTxMeta.id === txId) return
otherTxMeta.replacedBy = txMeta.hash
this.txStateManager.updateTx(txMeta, 'transactions/pending-tx-tracker#event: tx:confirmed reference to confirmed txHash with same nonce')
this.txStateManager.setTxStatusDropped(otherTxMeta.id)
})
}
_updateMemstore () {
const unapprovedTxs = this.txStateManager.getUnapprovedTxList()
const selectedAddressTxList = this.txStateManager.getFilteredTxList({
from: this.getSelectedAddress(),
metamaskNetworkId: this.getNetwork(),
})
this.memStore.updateState({ unapprovedTxs, selectedAddressTxList })
}
}
</code></pre>
</article>
</section>
</div>
<br class="clear">
<footer>
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.5.5</a> on Thu Apr 12 2018 14:37:39 GMT-0700 (PDT) using the radgrad jsdoc theme. Derived from docdash.
</footer>
<script>prettyPrint();</script>
<script src="scripts/linenumber.js"></script>
<script>$('.ui.accordion').accordion();</script>
</body>
</html>

@ -0,0 +1,486 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="google" content="notranslate">
<meta http-equiv="Content-Language" content="en">
<title>Global - Documentation</title>
<script src="scripts/prettify/prettify.js"></script>
<script src="scripts/prettify/lang-css.js"></script>
<script
src="https://code.jquery.com/jquery-3.1.1.min.js"
integrity="sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8="
crossorigin="anonymous"></script>
<script src="scripts/semantic.min.js"></script>
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link type="text/css" rel="stylesheet" href="https://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css">
<link type="text/css" rel="stylesheet" href="styles/prettify.css">
<link type="text/css" rel="stylesheet" href="styles/jsdoc.css">
<link type="text/css" rel="stylesheet" href="styles/semantic.min.css">
<link type="text/css" rel="stylesheet" href="styles/override.css">
</head>
<body>
<input type="checkbox" id="nav-trigger" class="nav-trigger" />
<label for="nav-trigger" class="navicon-button x">
<div class="navicon"></div>
</label>
<label for="nav-trigger" class="overlay"></label>
<nav>
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><div class="ui vertical accordion"><div class="title"><div class="ui list"><div class="item"><i class="inverted dropdown icon"></i><a href="module.exports_module.exports.html">exports</a></div></div></div></li></div></ul><h3><a href="global.html">Global</a></h3>
</nav>
<div id="main">
<h1 class="page-title">Global</h1>
<section>
<header>
<h2>
</h2>
</header>
<article>
<div class="container-overview">
<dl class="details">
</dl>
</div>
<h3 class="subsection-title">Type Definitions</h3>
<h4 class="name" id="MetaMaskOptions">MetaMaskOptions</h4>
<dl class="details">
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="metamask-controller.js.html">metamask-controller.js</a>, <a href="metamask-controller.js.html#line48">line 48</a>
</li></ul></dd>
</dl>
<h5 class="subsection-title">Properties:</h5>
<table class="props">
<thead>
<tr>
<th>Name</th>
<th>Type</th>
<th class="last">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td class="name"><code>platform</code></td>
<td class="type">
<span class="param-type"><a href="global.html#Platform">Platform</a></span>
</td>
<td class="description last"><p>An object including platform-specific functions.</p></td>
</tr>
</tbody>
</table>
<h5>Type:</h5>
<ul>
<li>
<span class="param-type">object</span>
</li>
</ul>
<h4 class="name" id="Platform">Platform</h4>
<dl class="details">
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="platforms_extension.js.html">platforms/extension.js</a>, <a href="platforms_extension.js.html#line3">line 3</a>
</li></ul></dd>
</dl>
<h5 class="subsection-title">Properties:</h5>
<table class="props">
<thead>
<tr>
<th>Name</th>
<th>Type</th>
<th class="last">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td class="name"><code>reload</code></td>
<td class="type">
<span class="param-type">function</span>
</td>
<td class="description last"><p>A function to reload the application.</p></td>
</tr>
<tr>
<td class="name"><code>openWindow</code></td>
<td class="type">
<span class="param-type">function</span>
</td>
<td class="description last"><p>Opens a URL in the web browser.</p></td>
</tr>
<tr>
<td class="name"><code>getVersion</code></td>
<td class="type">
<span class="param-type">function</span>
</td>
<td class="description last"><p>Gets the current version of MetaMask.</p></td>
</tr>
<tr>
<td class="name"><code>openExtensionInBrowser</code></td>
<td class="type">
<span class="param-type">function</span>
</td>
<td class="description last"><p>Opens the MetaMask UI in a full window.</p></td>
</tr>
<tr>
<td class="name"><code>getPlatformInfo</code></td>
<td class="type">
<span class="param-type">function</span>
</td>
<td class="description last"><p>Callback function that returns info about the current platform.</p></td>
</tr>
</tbody>
</table>
<div class="description">
<p>An object that provides a variety of platform-specific functions.</p>
</div>
<h5>Type:</h5>
<ul>
<li>
<span class="param-type">object</span>
</li>
</ul>
</article>
</section>
</div>
<br class="clear">
<footer>
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.5.5</a> on Thu Apr 12 2018 14:37:39 GMT-0700 (PDT) using the radgrad jsdoc theme. Derived from docdash.
</footer>
<script>prettyPrint();</script>
<script src="scripts/linenumber.js"></script>
<script>$('.ui.accordion').accordion();</script>
</body>
</html>

@ -0,0 +1,275 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="google" content="notranslate">
<meta http-equiv="Content-Language" content="en">
<title>Home - Documentation</title>
<script src="scripts/prettify/prettify.js"></script>
<script src="scripts/prettify/lang-css.js"></script>
<script
src="https://code.jquery.com/jquery-3.1.1.min.js"
integrity="sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8="
crossorigin="anonymous"></script>
<script src="scripts/semantic.min.js"></script>
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link type="text/css" rel="stylesheet" href="https://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css">
<link type="text/css" rel="stylesheet" href="styles/prettify.css">
<link type="text/css" rel="stylesheet" href="styles/jsdoc.css">
<link type="text/css" rel="stylesheet" href="styles/semantic.min.css">
<link type="text/css" rel="stylesheet" href="styles/override.css">
</head>
<body>
<input type="checkbox" id="nav-trigger" class="nav-trigger" />
<label for="nav-trigger" class="navicon-button x">
<div class="navicon"></div>
</label>
<label for="nav-trigger" class="overlay"></label>
<nav>
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><div class="ui vertical accordion"><div class="title"><div class="ui list"><div class="item"><i class="inverted dropdown icon"></i><a href="module.exports_module.exports.html">exports</a></div></div></div></li></div></ul><h3><a href="global.html">Global</a></h3>
</nav>
<div id="main">
<section>
<header>
<h2>
controllers/transactions.js
</h2>
</header>
<article>
<div class="container-overview">
<dl class="details">
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="controllers_transactions.js.html">controllers/transactions.js</a>, <a href="controllers_transactions.js.html#line4">line 4</a>
</li></ul></dd>
<dt class="tag-copyright">Copyright:</dt>
<dd class="tag-copyright"><ul class="dummy"><li>Copyright (c) 2018 MetaMask</li></ul></dd>
<dt class="tag-license">License:</dt>
<dd class="tag-license"><ul class="dummy"><li>MIT</li></ul></dd>
</dl>
<div class="description"><p>The transaction controller. Receives incoming transactions, and emits events for various states of their processing.</p></div>
</div>
</article>
</section>
<section>
<header>
<h2>
metamask-controller.js
</h2>
</header>
<article>
<div class="container-overview">
<dl class="details">
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="metamask-controller.js.html">metamask-controller.js</a>, <a href="metamask-controller.js.html#line1">line 1</a>
</li></ul></dd>
<dt class="tag-copyright">Copyright:</dt>
<dd class="tag-copyright"><ul class="dummy"><li>Copyright (c) 2018 MetaMask</li></ul></dd>
<dt class="tag-license">License:</dt>
<dd class="tag-license"><ul class="dummy"><li>MIT</li></ul></dd>
</dl>
<div class="description"><p>The central metamask controller. Aggregates other controllers and exports an api.</p></div>
</div>
</article>
</section>
</div>
<br class="clear">
<footer>
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.5.5</a> on Thu Apr 12 2018 14:37:39 GMT-0700 (PDT) using the radgrad jsdoc theme. Derived from docdash.
</footer>
<script>prettyPrint();</script>
<script src="scripts/linenumber.js"></script>
<script>$('.ui.accordion').accordion();</script>
</body>
</html>

File diff suppressed because it is too large Load Diff

@ -0,0 +1,229 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="google" content="notranslate">
<meta http-equiv="Content-Language" content="en">
<title>exports - Documentation</title>
<script src="scripts/prettify/prettify.js"></script>
<script src="scripts/prettify/lang-css.js"></script>
<script
src="https://code.jquery.com/jquery-3.1.1.min.js"
integrity="sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8="
crossorigin="anonymous"></script>
<script src="scripts/semantic.min.js"></script>
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link type="text/css" rel="stylesheet" href="https://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css">
<link type="text/css" rel="stylesheet" href="styles/prettify.css">
<link type="text/css" rel="stylesheet" href="styles/jsdoc.css">
<link type="text/css" rel="stylesheet" href="styles/semantic.min.css">
<link type="text/css" rel="stylesheet" href="styles/override.css">
</head>
<body>
<input type="checkbox" id="nav-trigger" class="nav-trigger" />
<label for="nav-trigger" class="navicon-button x">
<div class="navicon"></div>
</label>
<label for="nav-trigger" class="overlay"></label>
<nav>
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><div class="ui vertical accordion"><div class="title"><div class="ui list"><div class="item"><i class="inverted dropdown icon"></i><a href="module.exports_module.exports.html">exports</a></div></div></div></li></div></ul><h3><a href="global.html">Global</a></h3>
</nav>
<div id="main">
<h1 class="page-title">exports</h1>
<section>
<header>
<h2>
exports
</h2>
</header>
<article>
<div class="container-overview">
<h4 class="name" id="exports"><span class="type-signature"></span>new exports<span class="signature">(opts)</span><span class="type-signature"></span></h4>
<dl class="details">
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="metamask-controller.js.html">metamask-controller.js</a>, <a href="metamask-controller.js.html#line59">line 59</a>
</li></ul></dd>
</dl>
<h5>Parameters:</h5>
<table class="params">
<thead>
<tr>
<th>Name</th>
<th>Type</th>
<th class="last">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td class="name"><code>opts</code></td>
<td class="type">
<span class="param-type">Object</span>
</td>
<td class="description last"></td>
</tr>
</tbody>
</table>
</div>
</article>
</section>
</div>
<br class="clear">
<footer>
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.5.5</a> on Thu Apr 12 2018 14:37:39 GMT-0700 (PDT) using the radgrad jsdoc theme. Derived from docdash.
</footer>
<script>prettyPrint();</script>
<script src="scripts/linenumber.js"></script>
<script>$('.ui.accordion').accordion();</script>
</body>
</html>

@ -0,0 +1,117 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="google" content="notranslate">
<meta http-equiv="Content-Language" content="en">
<title>platforms/extension.js - Documentation</title>
<script src="scripts/prettify/prettify.js"></script>
<script src="scripts/prettify/lang-css.js"></script>
<script
src="https://code.jquery.com/jquery-3.1.1.min.js"
integrity="sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8="
crossorigin="anonymous"></script>
<script src="scripts/semantic.min.js"></script>
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link type="text/css" rel="stylesheet" href="https://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css">
<link type="text/css" rel="stylesheet" href="styles/prettify.css">
<link type="text/css" rel="stylesheet" href="styles/jsdoc.css">
<link type="text/css" rel="stylesheet" href="styles/semantic.min.css">
<link type="text/css" rel="stylesheet" href="styles/override.css">
</head>
<body>
<input type="checkbox" id="nav-trigger" class="nav-trigger" />
<label for="nav-trigger" class="navicon-button x">
<div class="navicon"></div>
</label>
<label for="nav-trigger" class="overlay"></label>
<nav>
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><div class="ui vertical accordion"><div class="title"><div class="ui list"><div class="item"><i class="inverted dropdown icon"></i><a href="module.exports_module.exports.html">exports</a></div></div></div></li></div></ul><h3><a href="global.html">Global</a></h3>
</nav>
<div id="main">
<h1 class="page-title">platforms/extension.js</h1>
<section>
<article>
<pre class="prettyprint source linenums"><code>const extension = require('extensionizer')
/**
* An object that provides a variety of platform-specific functions.
*
* @typedef {object} Platform
*
* @property {Function} reload - A function to reload the application.
* @property {Function} openWindow - Opens a URL in the web browser.
* @property {Function} getVersion - Gets the current version of MetaMask.
* @property {Function} openExtensionInBrowser - Opens the MetaMask UI in a full window.
* @property {Function} getPlatformInfo - Callback function that returns info about the current platform.
*/
class ExtensionPlatform {
//
// Public
//
reload () {
extension.runtime.reload()
}
openWindow ({ url }) {
extension.tabs.create({ url })
}
getVersion () {
return extension.runtime.getManifest().version
}
openExtensionInBrowser () {
const extensionURL = extension.runtime.getURL('home.html')
this.openWindow({ url: extensionURL })
}
getPlatformInfo (cb) {
try {
extension.runtime.getPlatformInfo((platform) => {
cb(null, platform)
})
} catch (e) {
cb(e)
}
}
}
module.exports = ExtensionPlatform
</code></pre>
</article>
</section>
</div>
<br class="clear">
<footer>
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.5.5</a> on Thu Apr 12 2018 14:37:39 GMT-0700 (PDT) using the radgrad jsdoc theme. Derived from docdash.
</footer>
<script>prettyPrint();</script>
<script src="scripts/linenumber.js"></script>
<script>$('.ui.accordion').accordion();</script>
</body>
</html>

@ -0,0 +1,25 @@
/*global document */
(function() {
var source = document.getElementsByClassName('prettyprint source linenums');
var i = 0;
var lineNumber = 0;
var lineId;
var lines;
var totalLines;
var anchorHash;
if (source && source[0]) {
anchorHash = document.location.hash.substring(1);
lines = source[0].getElementsByTagName('li');
totalLines = lines.length;
for (; i < totalLines; i++) {
lineNumber++;
lineId = 'line' + lineNumber;
lines[i].id = lineId;
if (lineId === anchorHash) {
lines[i].className += ' selected';
}
}
}
})();

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

@ -0,0 +1,2 @@
PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\f\r ]+/,null," \t\r\n "]],[["str",/^"(?:[^\n\f\r"\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*"/,null],["str",/^'(?:[^\n\f\r'\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*'/,null],["lang-css-str",/^url\(([^"')]*)\)/i],["kwd",/^(?:url|rgb|!important|@import|@page|@media|@charset|inherit)(?=[^\w-]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*)\s*:/i],["com",/^\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//],["com",
/^(?:<\!--|--\>)/],["lit",/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],["lit",/^#[\da-f]{3,6}/i],["pln",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i],["pun",/^[^\s\w"']+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[["kwd",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[["str",/^[^"')]+/]]),["css-str"]);

@ -0,0 +1,28 @@
var q=null;window.PR_SHOULD_USE_CONTINUATION=!0;
(function(){function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92)return f;var b=a.charAt(1);return(f=r[b])?f:"0"<=b&&b<="7"?parseInt(a.substring(1),8):b==="u"||b==="x"?parseInt(a.substring(2),16):a.charCodeAt(1)}function e(a){if(a<32)return(a<16?"\\x0":"\\x")+a.toString(16);a=String.fromCharCode(a);if(a==="\\"||a==="-"||a==="["||a==="]")a="\\"+a;return a}function h(a){for(var f=a.substring(1,a.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),a=
[],b=[],o=f[0]==="^",c=o?1:0,i=f.length;c<i;++c){var j=f[c];if(/\\[bdsw]/i.test(j))a.push(j);else{var j=m(j),d;c+2<i&&"-"===f[c+1]?(d=m(f[c+2]),c+=2):d=j;b.push([j,d]);d<65||j>122||(d<65||j>90||b.push([Math.max(65,j)|32,Math.min(d,90)|32]),d<97||j>122||b.push([Math.max(97,j)&-33,Math.min(d,122)&-33]))}}b.sort(function(a,f){return a[0]-f[0]||f[1]-a[1]});f=[];j=[NaN,NaN];for(c=0;c<b.length;++c)i=b[c],i[0]<=j[1]+1?j[1]=Math.max(j[1],i[1]):f.push(j=i);b=["["];o&&b.push("^");b.push.apply(b,a);for(c=0;c<
f.length;++c)i=f[c],b.push(e(i[0])),i[1]>i[0]&&(i[1]+1>i[0]&&b.push("-"),b.push(e(i[1])));b.push("]");return b.join("")}function y(a){for(var f=a.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=f.length,d=[],c=0,i=0;c<b;++c){var j=f[c];j==="("?++i:"\\"===j.charAt(0)&&(j=+j.substring(1))&&j<=i&&(d[j]=-1)}for(c=1;c<d.length;++c)-1===d[c]&&(d[c]=++t);for(i=c=0;c<b;++c)j=f[c],j==="("?(++i,d[i]===void 0&&(f[c]="(?:")):"\\"===j.charAt(0)&&
(j=+j.substring(1))&&j<=i&&(f[c]="\\"+d[i]);for(i=c=0;c<b;++c)"^"===f[c]&&"^"!==f[c+1]&&(f[c]="");if(a.ignoreCase&&s)for(c=0;c<b;++c)j=f[c],a=j.charAt(0),j.length>=2&&a==="["?f[c]=h(j):a!=="\\"&&(f[c]=j.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return f.join("")}for(var t=0,s=!1,l=!1,p=0,d=a.length;p<d;++p){var g=a[p];if(g.ignoreCase)l=!0;else if(/[a-z]/i.test(g.source.replace(/\\u[\da-f]{4}|\\x[\da-f]{2}|\\[^UXux]/gi,""))){s=!0;l=!1;break}}for(var r=
{b:8,t:9,n:10,v:11,f:12,r:13},n=[],p=0,d=a.length;p<d;++p){g=a[p];if(g.global||g.multiline)throw Error(""+g);n.push("(?:"+y(g)+")")}return RegExp(n.join("|"),l?"gi":"g")}function M(a){function m(a){switch(a.nodeType){case 1:if(e.test(a.className))break;for(var g=a.firstChild;g;g=g.nextSibling)m(g);g=a.nodeName;if("BR"===g||"LI"===g)h[s]="\n",t[s<<1]=y++,t[s++<<1|1]=a;break;case 3:case 4:g=a.nodeValue,g.length&&(g=p?g.replace(/\r\n?/g,"\n"):g.replace(/[\t\n\r ]+/g," "),h[s]=g,t[s<<1]=y,y+=g.length,
t[s++<<1|1]=a)}}var e=/(?:^|\s)nocode(?:\s|$)/,h=[],y=0,t=[],s=0,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=document.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);m(a);return{a:h.join("").replace(/\n$/,""),c:t}}function B(a,m,e,h){m&&(a={a:m,d:a},e(a),h.push.apply(h,a.e))}function x(a,m){function e(a){for(var l=a.d,p=[l,"pln"],d=0,g=a.a.match(y)||[],r={},n=0,z=g.length;n<z;++n){var f=g[n],b=r[f],o=void 0,c;if(typeof b===
"string")c=!1;else{var i=h[f.charAt(0)];if(i)o=f.match(i[1]),b=i[0];else{for(c=0;c<t;++c)if(i=m[c],o=f.match(i[1])){b=i[0];break}o||(b="pln")}if((c=b.length>=5&&"lang-"===b.substring(0,5))&&!(o&&typeof o[1]==="string"))c=!1,b="src";c||(r[f]=b)}i=d;d+=f.length;if(c){c=o[1];var j=f.indexOf(c),k=j+c.length;o[2]&&(k=f.length-o[2].length,j=k-c.length);b=b.substring(5);B(l+i,f.substring(0,j),e,p);B(l+i+j,c,C(b,c),p);B(l+i+k,f.substring(k),e,p)}else p.push(l+i,b)}a.e=p}var h={},y;(function(){for(var e=a.concat(m),
l=[],p={},d=0,g=e.length;d<g;++d){var r=e[d],n=r[3];if(n)for(var k=n.length;--k>=0;)h[n.charAt(k)]=r;r=r[1];n=""+r;p.hasOwnProperty(n)||(l.push(r),p[n]=q)}l.push(/[\S\s]/);y=L(l)})();var t=m.length;return e}function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?m.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/,
q,"'\"`"]):m.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&e.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var h=a.hashComments;h&&(a.cStyleComments?(h>1?m.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):m.push(["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),e.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,q])):m.push(["com",/^#[^\n\r]*/,
q,"#"]));a.cStyleComments&&(e.push(["com",/^\/\/[^\n\r]*/,q]),e.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));a.regexLiterals&&e.push(["lang-regex",/^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(h=a.types)&&e.push(["typ",h]);a=(""+a.keywords).replace(/^ | $/g,
"");a.length&&e.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),q]);m.push(["pln",/^\s+/,q," \r\n\t\xa0"]);e.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,q],["pun",/^.[^\s\w"-$'./@\\`]*/,q]);return x(m,e)}function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.className))break;if("BR"===a.nodeName)h(a),
a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)e(a);break;case 3:case 4:if(p){var b=a.nodeValue,d=b.match(t);if(d){var c=b.substring(0,d.index);a.nodeValue=c;(b=b.substring(d.index+d[0].length))&&a.parentNode.insertBefore(s.createTextNode(b),a.nextSibling);h(a);c||a.parentNode.removeChild(a)}}}}function h(a){function b(a,d){var e=d?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),g=a.nextSibling;f.appendChild(e);for(var h=g;h;h=g)g=h.nextSibling,f.appendChild(h)}return e}
for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),e;(e=a.parentNode)&&e.nodeType===1;)a=e;d.push(a)}var k=/(?:^|\s)nocode(?:\s|$)/,t=/\r\n?|\n/,s=a.ownerDocument,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=s.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);for(l=s.createElement("LI");a.firstChild;)l.appendChild(a.firstChild);for(var d=[l],g=0;g<d.length;++g)e(d[g]);m===(m|0)&&d[0].setAttribute("value",
m);var r=s.createElement("OL");r.className="linenums";for(var n=Math.max(0,m-1|0)||0,g=0,z=d.length;g<z;++g)l=d[g],l.className="L"+(g+n)%10,l.firstChild||l.appendChild(s.createTextNode("\xa0")),r.appendChild(l);a.appendChild(r)}function k(a,m){for(var e=m.length;--e>=0;){var h=m[e];A.hasOwnProperty(h)?window.console&&console.warn("cannot override language handler %s",h):A[h]=a}}function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*</.test(m)?"default-markup":"default-code";return A[a]}function E(a){var m=
a.g;try{var e=M(a.h),h=e.a;a.a=h;a.c=e.c;a.d=0;C(m,h)(a);var k=/\bMSIE\b/.test(navigator.userAgent),m=/\n/g,t=a.a,s=t.length,e=0,l=a.c,p=l.length,h=0,d=a.e,g=d.length,a=0;d[g]=s;var r,n;for(n=r=0;n<g;)d[n]!==d[n+2]?(d[r++]=d[n++],d[r++]=d[n++]):n+=2;g=r;for(n=r=0;n<g;){for(var z=d[n],f=d[n+1],b=n+2;b+2<=g&&d[b+1]===f;)b+=2;d[r++]=z;d[r++]=f;n=b}for(d.length=r;h<p;){var o=l[h+2]||s,c=d[a+2]||s,b=Math.min(o,c),i=l[h+1],j;if(i.nodeType!==1&&(j=t.substring(e,b))){k&&(j=j.replace(m,"\r"));i.nodeValue=
j;var u=i.ownerDocument,v=u.createElement("SPAN");v.className=d[a+1];var x=i.parentNode;x.replaceChild(v,i);v.appendChild(i);e<o&&(l[h+1]=i=u.createTextNode(t.substring(b,o)),x.insertBefore(i,v.nextSibling))}e=b;e>=o&&(h+=2);e>=c&&(a+=2)}}catch(w){"console"in window&&console.log(w&&w.stack?w.stack:w)}}var v=["break,continue,do,else,for,if,return,while"],w=[[v,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"],
"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],F=[w,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],G=[w,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"],
H=[G,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"],w=[w,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],I=[v,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"],
J=[v,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],v=[v,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],K=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/,N=/\S/,O=u({keywords:[F,H,w,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+
I,J,v],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};k(O,["default-code"]);k(x([],[["pln",/^[^<?]+/],["dec",/^<!\w[^>]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^<xmp\b[^>]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^<script\b[^>]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^<style\b[^>]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),
["default-markup","htm","html","mxml","xhtml","xml","xsl"]);k(x([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css",
/^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);k(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);k(u({keywords:F,hashComments:!0,cStyleComments:!0,types:K}),["c","cc","cpp","cxx","cyc","m"]);k(u({keywords:"null,true,false"}),["json"]);k(u({keywords:H,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:K}),["cs"]);k(u({keywords:G,cStyleComments:!0}),["java"]);k(u({keywords:v,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);k(u({keywords:I,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}),
["cv","py"]);k(u({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);k(u({keywords:J,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb"]);k(u({keywords:w,cStyleComments:!0,regexLiterals:!0}),["js"]);k(u({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes",
hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);k(x([],[["str",/^[\S\s]+/]]),["regex"]);window.prettyPrintOne=function(a,m,e){var h=document.createElement("PRE");h.innerHTML=a;e&&D(h,e);E({g:m,i:e,h:h});return h.innerHTML};window.prettyPrint=function(a){function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;p<h.length&&l.now()<e;p++){var n=h[p],k=n.className;if(k.indexOf("prettyprint")>=0){var k=k.match(g),f,b;if(b=
!k){b=n;for(var o=void 0,c=b.firstChild;c;c=c.nextSibling)var i=c.nodeType,o=i===1?o?b:c:i===3?N.test(c.nodeValue)?b:o:o;b=(f=o===b?void 0:o)&&"CODE"===f.tagName}b&&(k=f.className.match(g));k&&(k=k[1]);b=!1;for(o=n.parentNode;o;o=o.parentNode)if((o.tagName==="pre"||o.tagName==="code"||o.tagName==="xmp")&&o.className&&o.className.indexOf("prettyprint")>=0){b=!0;break}b||((b=(b=n.className.match(/\blinenums\b(?::(\d+))?/))?b[1]&&b[1].length?+b[1]:!0:!1)&&D(n,b),d={g:k,h:n,i:b},E(d))}}p<h.length?setTimeout(m,
250):a&&a()}for(var e=[document.getElementsByTagName("pre"),document.getElementsByTagName("code"),document.getElementsByTagName("xmp")],h=[],k=0;k<e.length;++k)for(var t=0,s=e[k].length;t<s;++t)h.push(e[k][t]);var e=q,l=Date;l.now||(l={now:function(){return+new Date}});var p=0,d,g=/\blang(?:uage)?-([\w.]+)(?!\S)/;m()};window.PR={createSimpleLexer:x,registerLangHandler:k,sourceDecorator:u,PR_ATTRIB_NAME:"atn",PR_ATTRIB_VALUE:"atv",PR_COMMENT:"com",PR_DECLARATION:"dec",PR_KEYWORD:"kwd",PR_LITERAL:"lit",
PR_NOCODE:"nocode",PR_PLAIN:"pln",PR_PUNCTUATION:"pun",PR_SOURCE:"src",PR_STRING:"str",PR_TAG:"tag",PR_TYPE:"typ"}})();

File diff suppressed because one or more lines are too long

@ -0,0 +1,626 @@
@import url(https://fonts.googleapis.com/css?family=Montserrat:400,700);
* {
box-sizing: border-box
}
html, body {
height: 100%;
width: 100%;
}
body {
color: #4d4e53;
background-color: white;
margin: 0 auto;
padding: 0 20px;
font-family: 'Helvetica Neue', Helvetica, sans-serif;
font-size: 16px;
line-height: 160%;
}
a,
a:active {
color: #606;
text-decoration: none;
}
a:hover {
text-decoration: none;
}
article a {
border-bottom: 1px solid #ddd;
}
article a:hover, article a:active {
border-bottom-color: #222;
}
p, ul, ol, blockquote {
margin-bottom: 1em;
}
h1, h2, h3, h4, h5, h6 {
font-family: 'Montserrat', sans-serif;
}
h1, h2, h3, h4, h5, h6 {
color: #000;
font-weight: 400;
margin: 0;
}
h1 {
font-weight: 300;
font-size: 48px;
margin: 1em 0 .5em;
}
h1.page-title {
font-size: 48px;
margin: 1em 30px;
}
h2 {
font-size: 24px;
margin: 1.5em 0 .3em;
}
h3 {
font-size: 24px;
margin: 1.2em 0 .3em;
}
h4 {
font-size: 18px;
margin: 1em 0 .2em;
color: #4d4e53;
}
h4.name {
color: #fff;
background: #6d426d;
box-shadow: 0 .25em .5em #d3d3d3;
border-top: 1px solid #d3d3d3;
border-bottom: 1px solid #d3d3d3;
margin: 1.5em 0 0.5em;
padding: .75em 0 .75em 10px;
}
h5, .container-overview .subsection-title {
font-size: 120%;
letter-spacing: -0.01em;
margin: 8px 0 3px 0;
}
h6 {
font-size: 100%;
letter-spacing: -0.01em;
margin: 6px 0 3px 0;
font-style: italic;
}
tt, code, kbd, samp {
font-family: Consolas, Monaco, 'Andale Mono', monospace;
background: #f4f4f4;
padding: 1px 5px;
}
.class-description {
font-size: 130%;
line-height: 140%;
margin-bottom: 1em;
margin-top: 1em;
}
.class-description:empty {
margin: 0
}
#main {
float: right;
min-width: 360px;
width: calc(100% - 240px);
}
header {
display: block
}
section {
display: block;
background-color: #fff;
padding: 5px 20px;
}
.variation {
display: none
}
.signature-attributes {
font-size: 60%;
color: #eee;
font-style: italic;
font-weight: lighter;
}
nav {
float: left;
display: block;
width: 250px;
background: #fff;
overflow: auto;
position: fixed;
height: 100%;
padding: 5px 20px;
}
nav h3 {
margin-top: 12px;
font-size: 13px;
text-transform: uppercase;
letter-spacing: 1px;
font-weight: 700;
line-height: 24px;
margin: 15px 0 10px;
padding: 0;
color: #000;
}
nav ul {
/* font-family: 'Lucida Grande', 'Lucida Sans Unicode', arial, sans-serif; */
font-size: 100%;
line-height: 17px;
padding: 0;
margin: 0;
list-style-type: none;
}
nav ul a,
nav ul a:active {
/* font-family: 'Montserrat', sans-serif; */
line-height: 18px;
padding: 0;
/* display: block; */
font-size: 12px;
}
nav a:hover,
nav a:active {
color: #606;
}
nav > ul {
padding: 0 10px;
}
nav > ul > li > a {
color: #606;
}
nav ul ul {
margin-bottom: 10px
}
nav ul ul a {
color: hsl(207, 1%, 60%);
border-left: 1px solid hsl(207, 10%, 86%);
}
nav ul ul a,
nav ul ul a:active {
padding-left: 20px
}
nav h2 {
font-size: 12px;
margin: 0;
padding: 0;
}
nav > h2 > a {
display: block;
margin: 10px 0 -10px;
color: #606 !important;
}
footer {
color: hsl(0, 0%, 28%);
margin-left: 250px;
display: block;
padding: 15px;
font-style: italic;
font-size: 90%;
}
.ancestors {
color: #999
}
.ancestors a {
color: #999 !important;
}
.clear {
clear: both
}
.important {
font-weight: bold;
color: #950B02;
}
.yes-def {
text-indent: -1000px
}
.type-signature {
color: #CA79CA
}
.type-signature:last-child {
color: #eee;
}
.name, .signature {
font-family: Consolas, Monaco, 'Andale Mono', monospace
}
.signature {
color: #fc83ff;
}
.details {
margin-top: 6px;
border-left: 2px solid #DDD;
line-height: 20px;
font-size: 14px;
}
.details dt {
width: 120px;
float: left;
padding-left: 10px;
}
.details dd {
margin-left: 70px;
margin-top: 6px;
margin-bottom: 6px;
}
.details ul {
margin: 0
}
.details ul {
list-style-type: none
}
.details pre.prettyprint {
margin: 0
}
.details .object-value {
padding-top: 0
}
.description {
margin-bottom: 1em;
margin-top: 1em;
}
.code-caption {
font-style: italic;
font-size: 107%;
margin: 0;
}
.prettyprint {
font-size: 14px;
overflow: auto;
}
.prettyprint.source {
width: inherit;
line-height: 18px;
display: block;
background-color: #0d152a;
color: #aeaeae;
}
.prettyprint code {
line-height: 18px;
display: block;
background-color: #0d152a;
color: #4D4E53;
}
.prettyprint > code {
padding: 15px;
}
.prettyprint .linenums code {
padding: 0 15px
}
.prettyprint .linenums li:first-of-type code {
padding-top: 15px
}
.prettyprint code span.line {
display: inline-block
}
.prettyprint.linenums {
padding-left: 70px;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.prettyprint.linenums ol {
padding-left: 0
}
.prettyprint.linenums li {
border-left: 3px #34446B solid;
}
.prettyprint.linenums li.selected, .prettyprint.linenums li.selected * {
background-color: #34446B;
}
.prettyprint.linenums li * {
-webkit-user-select: text;
-moz-user-select: text;
-ms-user-select: text;
user-select: text;
}
.params, .props {
border-spacing: 0;
border: 1px solid #ddd;
border-collapse: collapse;
border-radius: 3px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
width: 100%;
font-size: 14px;
margin: 1em 0;
}
.params .type {
white-space: nowrap;
}
.params code {
white-space: pre;
}
.params td, .params .name, .props .name, .name code {
color: #4D4E53;
font-family: Consolas, Monaco, 'Andale Mono', monospace;
font-size: 100%;
}
.params td, .params th, .props td, .props th {
margin: 0px;
text-align: left;
vertical-align: top;
padding: 10px;
display: table-cell;
}
.params td {
border-top: 1px solid #eee
}
.params thead tr, .props thead tr {
background-color: #fff;
font-weight: bold;
}
.params .params thead tr, .props .props thead tr {
background-color: #fff;
font-weight: bold;
}
.params td.description > p:first-child, .props td.description > p:first-child {
margin-top: 0;
padding-top: 0;
}
.params td.description > p:last-child, .props td.description > p:last-child {
margin-bottom: 0;
padding-bottom: 0;
}
span.param-type, .params td .param-type, .param-type dd {
color: #606;
font-family: Consolas, Monaco, 'Andale Mono', monospace
}
.param-type dt, .param-type dd {
display: inline-block
}
.param-type {
margin: 14px 0;
}
.disabled {
color: #454545
}
/* navicon button */
.navicon-button {
display: none;
position: relative;
padding: 2.0625rem 1.5rem;
transition: 0.25s;
cursor: pointer;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
opacity: .8;
}
.navicon-button .navicon:before, .navicon-button .navicon:after {
transition: 0.25s;
}
.navicon-button:hover {
transition: 0.5s;
opacity: 1;
}
.navicon-button:hover .navicon:before, .navicon-button:hover .navicon:after {
transition: 0.25s;
}
.navicon-button:hover .navicon:before {
top: .825rem;
}
.navicon-button:hover .navicon:after {
top: -.825rem;
}
/* navicon */
.navicon {
position: relative;
width: 2.5em;
height: .3125rem;
background: #000;
transition: 0.3s;
border-radius: 2.5rem;
}
.navicon:before, .navicon:after {
display: block;
content: "";
height: .3125rem;
width: 2.5rem;
background: #000;
position: absolute;
z-index: -1;
transition: 0.3s 0.25s;
border-radius: 1rem;
}
.navicon:before {
top: .625rem;
}
.navicon:after {
top: -.625rem;
}
/* open */
.nav-trigger:checked + label:not(.steps) .navicon:before,
.nav-trigger:checked + label:not(.steps) .navicon:after {
top: 0 !important;
}
.nav-trigger:checked + label .navicon:before,
.nav-trigger:checked + label .navicon:after {
transition: 0.5s;
}
/* Minus */
.nav-trigger:checked + label {
-webkit-transform: scale(0.75);
transform: scale(0.75);
}
/* × and + */
.nav-trigger:checked + label.plus .navicon,
.nav-trigger:checked + label.x .navicon {
background: transparent;
}
.nav-trigger:checked + label.plus .navicon:before,
.nav-trigger:checked + label.x .navicon:before {
-webkit-transform: rotate(-45deg);
transform: rotate(-45deg);
background: #FFF;
}
.nav-trigger:checked + label.plus .navicon:after,
.nav-trigger:checked + label.x .navicon:after {
-webkit-transform: rotate(45deg);
transform: rotate(45deg);
background: #FFF;
}
.nav-trigger:checked + label.plus {
-webkit-transform: scale(0.75) rotate(45deg);
transform: scale(0.75) rotate(45deg);
}
.nav-trigger:checked ~ nav {
left: 0 !important;
}
.nav-trigger:checked ~ .overlay {
display: block;
}
.nav-trigger {
position: fixed;
top: 0;
clip: rect(0, 0, 0, 0);
}
.overlay {
display: none;
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
width: 100%;
height: 100%;
background: hsla(0, 0%, 0%, 0.5);
z-index: 1;
}
@media only screen and (min-width: 320px) and (max-width: 680px) {
body {
overflow-x: hidden;
}
nav {
background: #FFF;
width: 250px;
height: 100%;
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: -250px;
z-index: 3;
padding: 0 10px;
transition: left 0.2s;
}
.navicon-button {
display: inline-block;
position: fixed;
top: 1.5em;
right: 0;
z-index: 2;
}
#main {
width: 100%;
min-width: 360px;
}
#main h1.page-title {
margin: 1em 0;
}
#main section {
padding: 0;
}
footer {
margin-left: 0;
}
}

@ -0,0 +1,3 @@
.ui.accordion .title:not(.ui) {
padding: 0px;
}

@ -0,0 +1,79 @@
.pln {
color: #ddd;
}
/* string content */
.str {
color: #61ce3c;
}
/* a keyword */
.kwd {
color: #fbde2d;
}
/* a comment */
.com {
color: #aeaeae;
}
/* a type name */
.typ {
color: #8da6ce;
}
/* a literal value */
.lit {
color: #fbde2d;
}
/* punctuation */
.pun {
color: #ddd;
}
/* lisp open bracket */
.opn {
color: #000000;
}
/* lisp close bracket */
.clo {
color: #000000;
}
/* a markup tag name */
.tag {
color: #8da6ce;
}
/* a markup attribute name */
.atn {
color: #fbde2d;
}
/* a markup attribute value */
.atv {
color: #ddd;
}
/* a declaration */
.dec {
color: #EF5050;
}
/* a variable name */
.var {
color: #c82829;
}
/* a function name */
.fun {
color: #4271ae;
}
/* Specify class=linenums on a pre to get line numbering */
ol.linenums {
margin-top: 0;
margin-bottom: 0;
}

File diff suppressed because one or more lines are too long

@ -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)

@ -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

@ -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

@ -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

@ -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

@ -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 })

@ -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:')

816
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -7,8 +7,9 @@
"start": "gulp dev:extension",
"mascara": "gulp dev:mascara & node ./mascara/example/server",
"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",
@ -75,6 +76,7 @@
"classnames": "^2.2.5",
"clone": "^2.1.1",
"copy-to-clipboard": "^3.0.8",
"currency-formatter": "^1.4.2",
"debounce": "^1.0.0",
"debounce-stream": "^2.0.0",
"deep-extend": "^0.5.0",
@ -90,7 +92,7 @@
"eth-hd-keyring": "^1.2.1",
"eth-json-rpc-filters": "^1.2.5",
"eth-json-rpc-infura": "^3.0.0",
"eth-keyring-controller": "^2.1.4",
"eth-keyring-controller": "^2.2.0",
"eth-phishing-detect": "^1.1.4",
"eth-query": "^2.1.2",
"eth-sig-util": "^1.4.2",
@ -236,6 +238,7 @@
"gulp-zip": "^4.0.0",
"image-size": "^0.6.2",
"isomorphic-fetch": "^2.2.1",
"jsdoc": "^3.5.5",
"jsdom": "^11.2.0",
"jsdom-global": "^3.0.2",
"jshint-stylish": "~2.2.1",
@ -257,6 +260,7 @@
"prompt": "^1.0.0",
"qs": "^6.2.0",
"qunitjs": "^2.4.1",
"radgrad-jsdoc-template": "^1.1.3",
"react-addons-test-utils": "^15.5.1",
"react-test-renderer": "^15.6.2",
"react-testutils-additions": "^15.2.0",

@ -0,0 +1,28 @@
const reactTriggerChange = require('../../lib/react-trigger-change')
const {
timeout,
queryAsync,
findAsync,
} = require('../../lib/util')
QUnit.module('currency localization')
QUnit.test('renders localized currency', (assert) => {
const done = assert.async()
runCurrencyLocalizationTest(assert).then(done).catch((err) => {
assert.notOk(err, `Error was thrown: ${err.stack}`)
done()
})
})
async function runCurrencyLocalizationTest(assert, done) {
console.log('*** start runCurrencyLocalizationTest')
const selectState = await queryAsync($, 'select')
selectState.val('currency localization')
reactTriggerChange(selectState[0])
await timeout(1000)
const txView = await queryAsync($, '.tx-view')
const heroBalance = await findAsync($(txView), '.hero-balance')
const fiatAmount = await findAsync($(heroBalance), '.fiat-amount')
assert.equal(fiatAmount[0].textContent, '₱102,707.97')
}

@ -91,7 +91,7 @@ async function runSendFlowTest(assert, done) {
)
assert.equal(
sendGasField.find('.currency-display__converted-value')[0].textContent,
'0.24 USD',
'$0.24 USD',
'send gas field should show estimated gas total converted to USD'
)
@ -118,7 +118,7 @@ async function runSendFlowTest(assert, done) {
)
assert.equal(
(await findAsync(sendGasField, '.currency-display__converted-value'))[0].textContent,
'3.60 USD',
'$3.60 USD',
'send gas field should show customized gas total converted to USD'
)
@ -138,9 +138,9 @@ async function runSendFlowTest(assert, done) {
const confirmScreenRows = await queryAsync($, '.confirm-screen-rows')
const confirmScreenGas = confirmScreenRows.find('.currency-display__converted-value')[0]
assert.equal(confirmScreenGas.textContent, '3.60 USD', 'confirm screen should show correct gas')
assert.equal(confirmScreenGas.textContent, '$3.60 USD', 'confirm screen should show correct gas')
const confirmScreenTotal = confirmScreenRows.find('.confirm-screen-row-info')[2]
assert.equal(confirmScreenTotal.textContent, '2405.36 USD', 'confirm screen should show correct total')
assert.equal(confirmScreenTotal.textContent, '$2,405.36 USD', 'confirm screen should show correct total')
const confirmScreenBackButton = await queryAsync($, '.page-container__back-button')
confirmScreenBackButton[0].click()

@ -0,0 +1,5 @@
require('babel-register')({
ignore: name => name.includes('node_modules') && !name.includes('obs-store'),
})
require('./helper')

@ -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' })
})
})

@ -0,0 +1,27 @@
const assert = require('assert')
const currencyFormatter = require('currency-formatter')
const infuraConversion = require('../../ui/app/infura-conversion.json')
describe('currencyFormatting', function () {
it('be able to format any infura currency', function (done) {
const number = 10000
infuraConversion.objects.forEach((conversion) => {
const code = conversion.quote.code.toUpperCase()
const result = currencyFormatter.format(number, { code })
switch (code) {
case 'USD':
assert.equal(result, '$10,000.00')
break
case 'JPY':
assert.equal(result, '¥10,000')
break
default:
assert.ok(result, `Currency ${code} formatted as ${result}`)
}
})
done()
})
})

@ -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' })
})
})

@ -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())

@ -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

@ -4,6 +4,8 @@ const h = require('react-hyperscript')
const inherits = require('util').inherits
const TokenBalance = require('./token-balance')
const Identicon = require('./identicon')
const currencyFormatter = require('currency-formatter')
const currencies = require('currency-formatter/currencies')
const { formatBalance, generateBalanceObject } = require('../util')
@ -97,9 +99,17 @@ BalanceComponent.prototype.renderFiatAmount = function (fiatDisplayNumber, fiatS
const shouldNotRenderFiat = fiatDisplayNumber === 'N/A' || Number(fiatDisplayNumber) === 0
if (shouldNotRenderFiat) return null
const upperCaseFiatSuffix = fiatSuffix.toUpperCase()
const display = currencies.find(currency => currency.code === upperCaseFiatSuffix)
? currencyFormatter.format(Number(fiatDisplayNumber), {
code: upperCaseFiatSuffix,
})
: `${fiatPrefix}${fiatDisplayNumber} ${upperCaseFiatSuffix}`
return h('div.fiat-amount', {
style: {},
}, `${fiatPrefix}${fiatDisplayNumber} ${fiatSuffix}`)
}, display)
}
BalanceComponent.prototype.getTokenBalance = function (formattedBalance, shorten) {
@ -117,5 +127,9 @@ BalanceComponent.prototype.getFiatDisplayNumber = function (formattedBalance, co
const splitBalance = formattedBalance.split(' ')
return (Number(splitBalance[0]) * conversionRate).toFixed(2)
const convertedNumber = (Number(splitBalance[0]) * conversionRate)
const wholePart = Math.floor(convertedNumber)
const decimalPart = convertedNumber - wholePart
return wholePart + Number(decimalPart.toPrecision(2))
}

@ -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,

@ -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',

@ -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')

@ -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) {

@ -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()
}
},

@ -23,6 +23,8 @@ const {
const GasFeeDisplay = require('../send/gas-fee-display-v2')
const SenderToRecipient = require('../sender-to-recipient')
const NetworkDisplay = require('../network-display')
const currencyFormatter = require('currency-formatter')
const currencies = require('currency-formatter/currencies')
const { MIN_GAS_PRICE_HEX } = require('../send/send-constants')
const { SEND_ROUTE, DEFAULT_ROUTE } = require('../../routes')
@ -275,6 +277,16 @@ ConfirmSendEther.prototype.getData = function () {
}
}
ConfirmSendEther.prototype.convertToRenderableCurrency = function (value, currencyCode) {
const upperCaseCurrencyCode = currencyCode.toUpperCase()
return currencies.find(currency => currency.code === upperCaseCurrencyCode)
? currencyFormatter.format(Number(value), {
code: upperCaseCurrencyCode,
})
: value
}
ConfirmSendEther.prototype.editTransaction = function (txMeta) {
const { editTransaction, history } = this.props
editTransaction(txMeta)
@ -319,6 +331,9 @@ ConfirmSendEther.prototype.render = function () {
? 'Increase your gas fee to attempt to overwrite and speed up your transaction'
: 'Please review your transaction.'
const convertedAmountInFiat = this.convertToRenderableCurrency(amountInFIAT, currentCurrency)
const convertedTotalInFiat = this.convertToRenderableCurrency(totalInFIAT, currentCurrency)
// This is from the latest master
// It handles some of the errors that we are not currently handling
// Leaving as comments fo reference
@ -365,7 +380,7 @@ ConfirmSendEther.prototype.render = function () {
// `You're sending to Recipient ...${toAddress.slice(toAddress.length - 4)}`,
// ]),
h('h3.flex-center.confirm-screen-send-amount', [`${amountInFIAT}`]),
h('h3.flex-center.confirm-screen-send-amount', [`${convertedAmountInFiat}`]),
h('h3.flex-center.confirm-screen-send-amount-currency', [ currentCurrency.toUpperCase() ]),
h('div.flex-center.confirm-memo-wrapper', [
h('h3.confirm-screen-send-memo', [ memo ? `"${memo}"` : '' ]),
@ -412,7 +427,7 @@ ConfirmSendEther.prototype.render = function () {
]),
h('div.confirm-screen-section-column', [
h('div.confirm-screen-row-info', `${totalInFIAT} ${currentCurrency.toUpperCase()}`),
h('div.confirm-screen-row-info', `${convertedTotalInFiat} ${currentCurrency.toUpperCase()}`),
h('div.confirm-screen-row-detail', `${totalInETH} ETH`),
]),

@ -27,6 +27,8 @@ const {
calcTokenAmount,
} = require('../../token-util')
const classnames = require('classnames')
const currencyFormatter = require('currency-formatter')
const currencies = require('currency-formatter/currencies')
const { MIN_GAS_PRICE_HEX } = require('../send/send-constants')
@ -48,7 +50,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 +61,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 +77,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 +202,6 @@ ConfirmSendToken.prototype.componentWillMount = function () {
.balanceOf(selectedAddress)
.then(usersToken => {
})
this.props.updateTokenExchangeRate()
this.updateComponentSendErrors({})
}
@ -322,10 +320,12 @@ ConfirmSendToken.prototype.renderHeroAmount = function () {
const txParams = txMeta.txParams || {}
const { memo = '' } = txParams
const convertedAmountInFiat = this.convertToRenderableCurrency(fiatAmount, currentCurrency)
return fiatAmount
? (
h('div.confirm-send-token__hero-amount-wrapper', [
h('h3.flex-center.confirm-screen-send-amount', `${fiatAmount}`),
h('h3.flex-center.confirm-screen-send-amount', `${convertedAmountInFiat}`),
h('h3.flex-center.confirm-screen-send-amount-currency', currentCurrency),
h('div.flex-center.confirm-memo-wrapper', [
h('h3.confirm-screen-send-memo', [ memo ? `"${memo}"` : '' ]),
@ -373,6 +373,9 @@ ConfirmSendToken.prototype.renderTotalPlusGas = function () {
const { fiat: fiatAmount, token: tokenAmount } = this.getAmount()
const { fiat: fiatGas, token: tokenGas } = this.getGasFee()
const totalInFIAT = fiatAmount && fiatGas && addCurrencies(fiatAmount, fiatGas)
const convertedTotalInFiat = this.convertToRenderableCurrency(totalInFIAT, currentCurrency)
return fiatAmount && fiatGas
? (
h('section.flex-row.flex-center.confirm-screen-row.confirm-screen-total-box ', [
@ -382,7 +385,7 @@ ConfirmSendToken.prototype.renderTotalPlusGas = function () {
]),
h('div.confirm-screen-section-column', [
h('div.confirm-screen-row-info', `${addCurrencies(fiatAmount, fiatGas)} ${currentCurrency}`),
h('div.confirm-screen-row-info', `${convertedTotalInFiat} ${currentCurrency}`),
h('div.confirm-screen-row-detail', `${addCurrencies(tokenAmount, tokenGas || '0')} ${symbol}`),
]),
])
@ -417,6 +420,16 @@ ConfirmSendToken.prototype.renderErrorMessage = function (message) {
: null
}
ConfirmSendToken.prototype.convertToRenderableCurrency = function (value, currencyCode) {
const upperCaseCurrencyCode = currencyCode.toUpperCase()
return currencies.find(currency => currency.code === upperCaseCurrencyCode)
? currencyFormatter.format(Number(value), {
code: upperCaseCurrencyCode,
})
: value
}
ConfirmSendToken.prototype.render = function () {
const txMeta = this.gatherTxMeta()
const {

@ -3,6 +3,8 @@ const h = require('react-hyperscript')
const inherits = require('util').inherits
const CurrencyInput = require('../currency-input')
const { conversionUtil, multiplyCurrencies } = require('../../conversion-util')
const currencyFormatter = require('currency-formatter')
const currencies = require('currency-formatter/currencies')
module.exports = CurrencyDisplay
@ -53,12 +55,32 @@ CurrencyDisplay.prototype.getValueToRender = function () {
})
}
CurrencyDisplay.prototype.getConvertedValueToRender = function (nonFormattedValue) {
const { primaryCurrency, convertedCurrency, conversionRate } = this.props
let convertedValue = conversionUtil(nonFormattedValue, {
fromNumericBase: 'dec',
fromCurrency: primaryCurrency,
toCurrency: convertedCurrency,
numberOfDecimals: 2,
conversionRate,
})
convertedValue = Number(convertedValue).toFixed(2)
const upperCaseCurrencyCode = convertedCurrency.toUpperCase()
return currencies.find(currency => currency.code === upperCaseCurrencyCode)
? currencyFormatter.format(Number(convertedValue), {
code: upperCaseCurrencyCode,
})
: convertedValue
}
CurrencyDisplay.prototype.render = function () {
const {
className = 'currency-display',
primaryBalanceClassName = 'currency-display__input',
convertedBalanceClassName = 'currency-display__converted-value',
conversionRate,
primaryCurrency,
convertedCurrency,
readOnly = false,
@ -68,14 +90,7 @@ CurrencyDisplay.prototype.render = function () {
const valueToRender = this.getValueToRender()
let convertedValue = conversionUtil(valueToRender, {
fromNumericBase: 'dec',
fromCurrency: primaryCurrency,
toCurrency: convertedCurrency,
numberOfDecimals: 2,
conversionRate,
})
convertedValue = Number(convertedValue).toFixed(2)
const convertedValueToRender = this.getConvertedValueToRender(valueToRender)
return h('div', {
className,
@ -108,7 +123,7 @@ CurrencyDisplay.prototype.render = function () {
h('div', {
className: convertedBalanceClassName,
}, `${convertedValue} ${convertedCurrency.toUpperCase()}`),
}, `${convertedValueToRender} ${convertedCurrency.toUpperCase()}`),
])

@ -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))
),

@ -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 {

@ -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, {

@ -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 {

@ -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),
]),
]),

@ -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')

@ -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()
}

@ -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,

@ -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

@ -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

@ -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,
})

@ -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) {

@ -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()
}

@ -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()
}
},

@ -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

@ -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:')

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save