fix conflicts

feature/default_network_editable
brunobar79 6 years ago
commit 13820b6cc1
  1. 37
      .circleci/config.yml
  2. 2
      .circleci/scripts/firefox-install
  3. 2
      .eslintignore
  4. 1
      .gitignore
  5. 33
      CHANGELOG.md
  6. 8
      README.md
  7. 266
      app/_locales/en/messages.json
  8. 14
      app/_locales/es/messages.json
  9. 747
      app/_locales/fr/messages.json
  10. 1237
      app/_locales/ht/messages.json
  11. 1
      app/_locales/index.json
  12. 485
      app/_locales/it/messages.json
  13. 3
      app/_locales/ja/messages.json
  14. 575
      app/_locales/ko/messages.json
  15. 9
      app/_locales/zh_CN/messages.json
  16. 3
      app/images/arrow-popout.svg
  17. 13
      app/images/expand.svg
  18. 16
      app/images/hide.svg
  19. 17
      app/images/info.svg
  20. 34
      app/images/ledger-logo.svg
  21. 12
      app/images/open-etherscan.svg
  22. 2
      app/manifest.json
  23. 17
      app/phishing.html
  24. 10
      app/scripts/background.js
  25. 25
      app/scripts/contentscript.js
  26. 22
      app/scripts/controllers/blacklist.js
  27. 10
      app/scripts/controllers/network/createInfuraClient.js
  28. 4
      app/scripts/controllers/network/createJsonRpcClient.js
  29. 4
      app/scripts/controllers/network/createLocalhostClient.js
  30. 2
      app/scripts/controllers/network/createMetamaskMiddleware.js
  31. 31
      app/scripts/controllers/preferences.js
  32. 12
      app/scripts/controllers/transactions/enums.js
  33. 90
      app/scripts/controllers/transactions/index.js
  34. 6
      app/scripts/controllers/transactions/tx-state-manager.js
  35. 36
      app/scripts/inpage.js
  36. 3
      app/scripts/lib/account-tracker.js
  37. 6
      app/scripts/lib/auto-reload.js
  38. 254
      app/scripts/lib/config-manager.js
  39. 4
      app/scripts/lib/ipfsContent.js
  40. 2
      app/scripts/lib/message-manager.js
  41. 2
      app/scripts/lib/personal-message-manager.js
  42. 58
      app/scripts/lib/typed-message-manager.js
  43. 204
      app/scripts/metamask-controller.js
  44. 50
      app/scripts/migrations/_multi-keyring.js
  45. 2
      app/scripts/notice-controller.js
  46. 59
      app/scripts/phishing-detect.js
  47. 20
      development/sentry-publish.js
  48. 5
      development/verify-locale-strings.js
  49. 1
      docs/adding-new-networks.md
  50. 2
      docs/porting_to_new_environment.md
  51. 2
      docs/state_dump.md
  52. 2
      docs/translating-guide.md
  53. 11
      gulpfile.js
  54. 8
      mascara/src/app/first-time/create-password-screen.js
  55. 6
      mascara/src/app/first-time/index.css
  56. 3
      old-ui/app/components/pending-typed-msg-details.js
  57. 27
      old-ui/app/components/typed-message-renderer.js
  58. 52624
      package-lock.json
  59. 33
      package.json
  60. 70
      test/data/2-state.json
  61. 1251
      test/data/mock-state.json
  62. 286
      test/e2e/beta/drizzle.spec.js
  63. 2
      test/e2e/beta/from-import-beta-ui.spec.js
  64. 5
      test/e2e/beta/helpers.js
  65. 39
      test/e2e/beta/metamask-beta-ui.spec.js
  66. 4
      test/e2e/beta/run-all.sh
  67. 20
      test/e2e/beta/run-drizzle.sh
  68. 2
      test/e2e/metamask.spec.js
  69. 2
      test/integration/lib/confirm-sig-requests.js
  70. 2
      test/integration/lib/send-new-ui.js
  71. 10
      test/integration/lib/tx-list-items.js
  72. 9
      test/lib/mock-config-manager.js
  73. 2
      test/lib/mock-encryptor.js
  74. 42
      test/lib/render-helpers.js
  75. 20
      test/lib/shallow-with-store.js
  76. 33
      test/unit/app/cleanErrorStack.spec.js
  77. 15
      test/unit/app/controllers/blacklist-controller-test.js
  78. 36
      test/unit/app/controllers/metamask-controller-test.js
  79. 7
      test/unit/app/controllers/notice-controller-test.js
  80. 30
      test/unit/app/controllers/preferences-controller-test.js
  81. 30
      test/unit/app/controllers/transactions/tx-controller-test.js
  82. 3
      test/unit/components/balance-component-test.js
  83. 2
      test/unit/components/binary-renderer-test.js
  84. 112
      test/unit/config-manager-test.js
  85. 4
      test/unit/development/sample-changelog.md
  86. 2
      test/unit/responsive/components/dropdown-test.js
  87. 2
      test/unit/ui/add-token.spec.js
  88. 1468
      test/unit/ui/app/actions.spec.js
  89. 36
      test/unit/ui/app/components/identicon.spec.js
  90. 69
      test/unit/ui/app/components/token-cell.spec.js
  91. 175
      test/unit/ui/app/selectors.spec.js
  92. 26
      test/unit/ui/etherscan-prefix-for-network.spec.js
  93. 103
      ui/app/actions.js
  94. 4
      ui/app/app.js
  95. 5
      ui/app/components/button/button.component.js
  96. 25
      ui/app/components/card/card.component.js
  97. 1
      ui/app/components/card/index.js
  98. 11
      ui/app/components/card/index.scss
  99. 25
      ui/app/components/card/tests/card.component.test.js
  100. 9
      ui/app/components/confirm-page-container/confirm-page-container-content/confirm-page-container-content.component.js
  101. Some files were not shown because too many files have changed in this diff Show More

@ -36,9 +36,17 @@ workflows:
requires:
- prep-deps-npm
- prep-build
- test-e2e-beta-drizzle:
requires:
- prep-deps-npm
- prep-build
- test-unit:
requires:
- prep-deps-npm
- test-mozilla-lint:
requires:
- prep-deps-npm
- prep-build
- test-integration-mascara-chrome:
requires:
- prep-deps-npm
@ -59,10 +67,12 @@ workflows:
requires:
- test-lint
- test-unit
- test-mozilla-lint
- test-e2e-chrome
- test-e2e-firefox
- test-e2e-beta-chrome
- test-e2e-beta-firefox
- test-e2e-beta-drizzle
- test-integration-mascara-chrome
- test-integration-mascara-firefox
- test-integration-flat-chrome
@ -217,6 +227,19 @@ jobs:
path: test-artifacts
destination: test-artifacts
test-e2e-beta-drizzle:
docker:
- image: circleci/node:8.11.3-browsers
steps:
- checkout
- attach_workspace:
at: .
- run:
name: test:e2e:drizzle:beta
command: npm run test:e2e:drizzle:beta
- store_artifacts:
path: test-artifacts
destination: test-artifacts
test-e2e-beta-chrome:
docker:
- image: circleci/node:8.11.3-browsers
@ -299,8 +322,8 @@ jobs:
- run:
name: github gh-pages docs publish
command: >
git config user.name metamaskbot
git config user.email admin@metamask.io
git config user.name metamaskbot &&
git config user.email admin@metamask.io &&
gh-pages -d docs/jsdocs
test-unit:
@ -313,6 +336,16 @@ jobs:
- run:
name: test:coverage
command: npm run test:coverage
test-mozilla-lint:
docker:
- image: circleci/node:8.11.3-browsers
steps:
- checkout
- attach_workspace:
at: .
- run:
name: test:mozilla-lint
command: npm run mozilla-lint
test-integration-flat-firefox:
docker:

@ -4,7 +4,7 @@ set -e
set -u
set -o pipefail
FIREFOX_VERSION='61.0.2'
FIREFOX_VERSION='62.0'
FIREFOX_BINARY="firefox-${FIREFOX_VERSION}.tar.bz2"
FIREFOX_BINARY_URL="https://ftp.mozilla.org/pub/firefox/releases/${FIREFOX_VERSION}/linux-x86_64/en-US/${FIREFOX_BINARY}"
FIREFOX_PATH='/opt/firefox'

@ -3,6 +3,7 @@ dist/**
builds/**
test-builds/**
docs/**
coverage/
development/bundle.js
development/states.js
@ -20,3 +21,4 @@ test/integration/bundle.js
test/integration/jquery-3.1.0.min.js
test/integration/helpers.js
test/integration/lib/first-time.js

1
.gitignore vendored

@ -21,6 +21,7 @@ temp
.DS_Store
app/.DS_Store
coverage/
dist
builds/
disc/

@ -2,8 +2,41 @@
## Current Develop Branch
## 4.14.0 Thursday October 11 2018
- Update transaction statuses when switching networks.
- [#5470](https://github.com/MetaMask/metamask-extension/pull/5470) 100% coverage in French locale, fixed the procedure to verify proposed locale.
- Added rudimentary support for the subscription API to support web3 1.0 and Truffle's Drizzle.
- [#5502](https://github.com/MetaMask/metamask-extension/pull/5502) Update Italian translation.
## 4.12.0 Thursday September 27 2018
- Reintroduces changes from 4.10.0
## 4.13.0
- A rollback release, equivalent to `v4.11.1` to be deployed in the case that `v4.12.0` is found to have bugs.
## 4.11.1 Tuesday September 25 2018
- Adds Ledger support.
## 4.11.0 Monday September 24 2018
- Identical to 4.9.3. A rollback version to give time to fix bugs in the 4.10.x branch.
## 4.10.0 Mon Sep 17 2018
- [#4803](https://github.com/MetaMask/metamask-extension/pull/4803): Implement EIP-712: Sign typed data, but continue to support v1.
- [#4898](https://github.com/MetaMask/metamask-extension/pull/4898): Restore multiple consecutive accounts with balances.
- [#4279](https://github.com/MetaMask/metamask-extension/pull/4279): New BlockTracker and Json-Rpc-Engine based Provider.
- [#5050](https://github.com/MetaMask/metamask-extension/pull/5050): Add Ledger hardware wallet support.
- [#4919](https://github.com/MetaMask/metamask-extension/pull/4919): Refactor and Redesign Transaction List.
- [#5182](https://github.com/MetaMask/metamask-extension/pull/5182): Add Transaction Details to the Transaction List view.
- [#5229](https://github.com/MetaMask/metamask-extension/pull/5229): Clear old seed words when importing new seed words.
- [#5264](https://github.com/MetaMask/metamask-extension/pull/5264): Improve click area for adjustment arrows buttons.
- [#4606](https://github.com/MetaMask/metamask-extension/pull/4606): Add new metamask_watchAsset method.
- [#5189](https://github.com/MetaMask/metamask-extension/pull/5189): Fix bug where Ropsten loading message is shown when connecting to Kovan.
- [#5256](https://github.com/MetaMask/metamask-extension/pull/5256): Add mock EIP-1102 support
## 4.9.3 Wed Aug 15 2018

@ -1,5 +1,5 @@
# 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)
[![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) [![Stories in Ready](https://badge.waffle.io/MetaMask/metamask-extension.png?label=in%20progress&title=waffle.io)](https://waffle.io/MetaMask/metamask-extension)
## Support
@ -37,6 +37,12 @@ If you're a web dapp developer, we've got two types of guides for you:
Uncompressed builds can be found in `/dist`, compressed builds can be found in `/builds` once they're built.
## Contributing
You can read [our internal docs here](https://metamask.github.io/metamask-extension/).
You can re-generate the docs locally by running `npm run doc`, and contributors can update the hosted docs by running `npm run publish-docs`.
### Running Tests
Requires `mocha` installed. Run `npm install -g mocha`.

@ -3,7 +3,7 @@
"message": "Accept"
},
"accessingYourCamera": {
"message": "Accesing your camera..."
"message": "Accessing your camera..."
},
"account": {
"message": "Account"
@ -14,9 +14,15 @@
"accountName": {
"message": "Account Name"
},
"accountOptions": {
"message": "Account Options"
},
"accountSelectionRequired": {
"message": "You need to select an account!"
},
"activityLog": {
"message": "activity log"
},
"address": {
"message": "Address"
},
@ -58,6 +64,12 @@
"attemptingConnect": {
"message": "Attempting to connect to blockchain."
},
"attemptToCancel": {
"message": "Attempt to Cancel?"
},
"attemptToCancelDescription": {
"message": "Submitting this attempt does not guarantee your original transaction will be cancelled. If the cancellation attempt is successful, you will be charged the transaction fee above."
},
"attributions": {
"message": "Attributions"
},
@ -113,6 +125,15 @@
"cancel": {
"message": "Cancel"
},
"cancelAttempt": {
"message": "Cancel Attempt"
},
"cancellationGasFee": {
"message": "Cancellation Gas Fee"
},
"cancelN": {
"message": "Cancel all $1 transactions"
},
"classicInterface": {
"message": "Use classic interface"
},
@ -149,6 +170,21 @@
"connecting": {
"message": "Connecting..."
},
"connectingToKovan": {
"message": "Connecting to Kovan Test Network"
},
"connectingToMainnet": {
"message": "Connecting to Main Ethereum Network"
},
"connectingToRopsten": {
"message": "Connecting to Ropsten Test Network"
},
"connectingToRinkeby": {
"message": "Connecting to Rinkeby Test Network"
},
"connectingToUnknown": {
"message": "Connecting to Unknown Network"
},
"connectToLedger": {
"message": "Connect to Ledger"
},
@ -182,9 +218,6 @@
"copy": {
"message": "Copy"
},
"copyContractAddress": {
"message": "Copy Contract Address"
},
"copyAddress": {
"message": "Copy address to clipboard"
},
@ -213,9 +246,15 @@
"currentConversion": {
"message": "Current Conversion"
},
"currentLanguage": {
"message": "Current Language"
},
"currentNetwork": {
"message": "Current Network"
},
"currentRpc": {
"message": "Current RPC"
},
"customGas": {
"message": "Customize Gas"
},
@ -322,21 +361,15 @@
"enterPasswordContinue": {
"message": "Enter password to continue"
},
"parameters": {
"message": "Parameters"
},
"passwordNotLongEnough": {
"message": "Password not long enough"
},
"passwordsDontMatch": {
"message": "Passwords Don't Match"
},
"etherscanView": {
"message": "View account on Etherscan"
},
"exchangeRate": {
"message": "Exchange Rate"
},
"expandView": {
"message": "Expand View"
},
"exportPrivateKey": {
"message": "Export Private Key"
},
@ -391,6 +424,9 @@
"gasLimitTooLow": {
"message": "Gas limit must be at least 21000"
},
"gasUsed": {
"message": "Gas Used"
},
"generatingSeed": {
"message": "Generating Seed..."
},
@ -486,6 +522,9 @@
"importUsingSeed": {
"message": "Import using account seed phrase"
},
"info": {
"message": "Info"
},
"infoHelp": {
"message": "Info & Help"
},
@ -514,7 +553,7 @@
"message": "Invalid Request"
},
"invalidRPC": {
"message": "Invalid RPC URI"
"message": "Invalid RPC URL"
},
"invalidSeedPhrase": {
"message": "Invalid seed phrase"
@ -593,6 +632,9 @@
"metamaskSeedWords": {
"message": "MetaMask Seed Words"
},
"metamaskVersion": {
"message": "MetaMask Version"
},
"min": {
"message": "Minimum"
},
@ -635,6 +677,9 @@
"newPassword": {
"message": "New Password (min 8 chars)"
},
"newPassword8Chars": {
"message": "New Password (min 8 chars)"
},
"newRecipient": {
"message": "New Recipient"
},
@ -677,6 +722,13 @@
"oldUIMessage": {
"message": "You have returned to the old UI. You can switch back to the New UI through the option in the top right dropdown menu."
},
"onlySendToEtherAddress": {
"message": "Only send ETH to an Ethereum address."
},
"onlySendTokensToAccountAddress": {
"message": "Only send $1 to an Ethereum account address.",
"description": "displays token symbol"
},
"openInTab": {
"message": "Open in tab"
},
@ -684,19 +736,34 @@
"message": "or",
"description": "choice between creating or importing a new account"
},
"orderOneHere": {
"message": "Order a Trezor or Ledger and keep your funds in cold storage"
},
"origin": {
"message": "Origin"
},
"outgoing": {
"message": "Outgoing"
},
"parameters": {
"message": "Parameters"
},
"password": {
"message": "Password"
},
"passwordCorrect": {
"message": "Please make sure your password is correct."
},
"passwordsDontMatch": {
"message": "Passwords Don't Match"
},
"passwordMismatch": {
"message": "passwords don't match",
"description": "in password creation process, the two new password fields did not match"
},
"passwordNotLongEnough": {
"message": "Password not long enough"
},
"passwordShort": {
"message": "password not long enough",
"description": "in password creation process, the password is not long enough to be secure"
@ -760,6 +827,18 @@
"refundAddress": {
"message": "Your Refund Address"
},
"reject": {
"message": "Reject"
},
"rejectAll": {
"message": "Reject All"
},
"rejectTxsN": {
"message": "Reject $1 transactions"
},
"rejectTxsDescription": {
"message": "You are about to batch reject $1 transactions."
},
"rejected": {
"message": "Rejected"
},
@ -787,9 +866,6 @@
"retryWithMoreGas": {
"message": "Retry with a higher gas price here"
},
"walletSeed": {
"message": "Wallet Seed"
},
"restore": {
"message": "Restore"
},
@ -832,24 +908,6 @@
"rpc": {
"message": "Custom RPC"
},
"currentRpc": {
"message": "Current RPC"
},
"connectingToMainnet": {
"message": "Connecting to Main Ethereum Network"
},
"connectingToRopsten": {
"message": "Connecting to Ropsten Test Network"
},
"connectingToKovan": {
"message": "Connecting to Kovan Test Network"
},
"connectingToRinkeby": {
"message": "Connecting to Rinkeby Test Network"
},
"connectingToUnknown": {
"message": "Connecting to Unknown Network"
},
"sampleAccountName": {
"message": "E.g. My new account",
"description": "Help user understand concept of adding a human-readable name to their account"
@ -857,12 +915,6 @@
"save": {
"message": "Save"
},
"speedUpTitle": {
"message": "Speed Up Transaction"
},
"speedUpSubtitle": {
"message": "Increase your gas price to attempt to overwrite and speed up your transaction"
},
"saveAsCsvFile": {
"message": "Save as CSV File"
},
@ -873,6 +925,12 @@
"saveSeedAsFile": {
"message": "Save Seed Words As File"
},
"scanInstructions": {
"message": "Place the QR code in front of your camera"
},
"scanQrCode": {
"message": "Scan QR Code"
},
"search": {
"message": "Search"
},
@ -882,15 +940,6 @@
"secretPhrase": {
"message": "Enter your secret twelve word phrase here to restore your vault."
},
"showHexData": {
"message": "Show Hex Data"
},
"showHexDataDescription": {
"message": "Select this to show the hex data field on the send screen"
},
"newPassword8Chars": {
"message": "New Password (min 8 chars)"
},
"seedPhraseReq": {
"message": "Seed phrases are 12 words long"
},
@ -900,6 +949,9 @@
"selectCurrency": {
"message": "Select Currency"
},
"selectLocale": {
"message": "Select Locale"
},
"selectService": {
"message": "Select Service"
},
@ -924,19 +976,6 @@
"separateEachWord": {
"message": "Separate each word with a single space"
},
"onlySendToEtherAddress": {
"message": "Only send ETH to an Ethereum address."
},
"onlySendTokensToAccountAddress": {
"message": "Only send $1 to an Ethereum account address.",
"description": "displays token symbol"
},
"orderOneHere": {
"message": "Order a Trezor or Ledger and keep your funds in cold storage"
},
"outgoing": {
"message": "Outgoing"
},
"searchTokens": {
"message": "Search Tokens"
},
@ -961,33 +1000,6 @@
"settings": {
"message": "Settings"
},
"step1HardwareWallet": {
"message": "1. Connect Hardware Wallet"
},
"step1HardwareWalletMsg": {
"message": "Connect your hardware wallet directly to your computer."
},
"step2HardwareWallet": {
"message": "2. Select an Account"
},
"step2HardwareWalletMsg": {
"message": "Select the account you want to view. You can only choose one at a time."
},
"step3HardwareWallet": {
"message": "3. Start using dApps and more!"
},
"step3HardwareWalletMsg": {
"message": "Use your hardware account like you would with any Ethereum account. Log in to dApps, send Eth, buy and store ERC20 tokens and Non-Fungible tokens like CryptoKitties."
},
"info": {
"message": "Info"
},
"scanInstructions": {
"message": "Place the QR code in front of your camera"
},
"scanQrCode": {
"message": "Scan QR Code"
},
"shapeshiftBuy": {
"message": "Buy with Shapeshift"
},
@ -997,6 +1009,12 @@
"showQRCode": {
"message": "Show QR Code"
},
"showHexData": {
"message": "Show Hex Data"
},
"showHexDataDescription": {
"message": "Select this to show the hex data field on the send screen"
},
"sign": {
"message": "Sign"
},
@ -1021,6 +1039,15 @@
"spaceBetween": {
"message": "there can only be a space between words"
},
"speedUp": {
"message": "speed up"
},
"speedUpTitle": {
"message": "Speed Up Transaction"
},
"speedUpSubtitle": {
"message": "Increase your gas price to attempt to overwrite and speed up your transaction"
},
"status": {
"message": "Status"
},
@ -1033,6 +1060,24 @@
"stateLogError": {
"message": "Error in retrieving state logs."
},
"step1HardwareWallet": {
"message": "1. Connect Hardware Wallet"
},
"step1HardwareWalletMsg": {
"message": "Connect your hardware wallet directly to your computer."
},
"step2HardwareWallet": {
"message": "2. Select an Account"
},
"step2HardwareWalletMsg": {
"message": "Select the account you want to view. You can only choose one at a time."
},
"step3HardwareWallet": {
"message": "3. Start using dApps and more!"
},
"step3HardwareWalletMsg": {
"message": "Use your hardware account like you would with any Ethereum account. Log in to dApps, send Eth, buy and store ERC20 tokens and Non-Fungible tokens like CryptoKitties."
},
"submit": {
"message": "Submit"
},
@ -1085,6 +1130,30 @@
"total": {
"message": "Total"
},
"transaction": {
"message": "transaction"
},
"transactionConfirmed": {
"message": "Transaction confirmed on $2."
},
"transactionCreated": {
"message": "Transaction created with a value of $1 on $2."
},
"transactionWithNonce": {
"message": "Transaction $1"
},
"transactionDropped": {
"message": "Transaction dropped on $2."
},
"transactionSubmitted": {
"message": "Transaction submitted on $2."
},
"transactionUpdated": {
"message": "Transaction updated on $2."
},
"transactionUpdatedGas": {
"message": "Transaction updated with a gas price of $1 on $2."
},
"transactions": {
"message": "transactions"
},
@ -1100,6 +1169,9 @@
"transfer": {
"message": "Transfer"
},
"transferFrom": {
"message": "Transfer From"
},
"transfers": {
"message": "Transfers"
},
@ -1131,6 +1203,9 @@
"unavailable": {
"message": "Unavailable"
},
"units": {
"message": "units"
},
"unknown": {
"message": "Unknown"
},
@ -1150,7 +1225,7 @@
"message": "Ooops! Something went wrong...."
},
"unknownCameraError": {
"message": "There was an error while trying to access you camera. Please try again..."
"message": "There was an error while trying to access your camera. Please try again..."
},
"unlock": {
"message": "Unlock"
@ -1158,6 +1233,9 @@
"unlockMessage": {
"message": "The decentralized web awaits"
},
"updatedWithDate": {
"message": "Updated $1"
},
"uriErrorMsg": {
"message": "URIs require the appropriate HTTP/HTTPS prefix."
},
@ -1186,6 +1264,9 @@
"visitWebSite": {
"message": "Visit our web site"
},
"walletSeed": {
"message": "Wallet Seed"
},
"warning": {
"message": "Warning"
},
@ -1198,6 +1279,9 @@
"whatsThis": {
"message": "What's this?"
},
"yesLetsTry": {
"message": "Yes, let's try"
},
"youNeedToAllowCameraAccess": {
"message": "You need to allow camera access to use this feature."
},

@ -156,7 +156,7 @@
"message": " Copiar "
},
"copyPrivateKey": {
"message": "Ésta es tu llave privada (haz click para copiar)"
"message": "Ésta es tu clave privada (haz click para copiar)"
},
"copyToClipboard": {
"message": "Copiar al portapapeles"
@ -278,10 +278,10 @@
"message": "Tipo de cambio"
},
"exportPrivateKey": {
"message": "Exportar llave privada"
"message": "Exportar clave privada"
},
"exportPrivateKeyWarning": {
"message": "Exportar llaves privadas bajo TU PROPIO riesgo"
"message": "Exportar claves privadas bajo TU PROPIO riesgo"
},
"failed": {
"message": "Fallo"
@ -579,8 +579,8 @@
"description": "En el proceso de creación de contraseña, esta no es lo suficientemente larga para ser segura"
},
"pastePrivateKey": {
"message": "Pega tu llave privada aqui",
"description": "Para importar una cuenta desde una llave privada"
"message": "Pega tu clave privada aqui",
"description": "Para importar una cuenta desde una clave privada"
},
"pasteSeed": {
"message": "¡Pega tu frase semilla aquí!"
@ -595,7 +595,7 @@
"message": "Política de privacidad"
},
"privateKey": {
"message": "Llave privada",
"message": "Clave privada",
"description": "Selecciona este tupo de archivo para importar una cuenta"
},
"privateKeyWarning": {
@ -712,7 +712,7 @@
"message": "Comprar con ShapeShift"
},
"showPrivateKeys": {
"message": "Mostrar llaves privadas"
"message": "Mostrar claves privadas"
},
"showQRCode": {
"message": "Mostrar codigo QR"

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -4,6 +4,7 @@
{ "code": "en", "name": "English" },
{ "code": "es", "name": "Spanish" },
{ "code": "fr", "name": "French" },
{ "code": "ht", "name": "Haitian Creole" },
{ "code": "hn", "name": "Hindi" },
{ "code": "it", "name": "Italian" },
{ "code": "ja", "name": "Japanese" },

@ -2,6 +2,9 @@
"accept": {
"message": "Accetta"
},
"accessingYourCamera": {
"message": "Accesso alla fotocamera..."
},
"account": {
"message": "Account"
},
@ -11,6 +14,15 @@
"accountName": {
"message": "Nome Account"
},
"accountOptions": {
"message": "Account Options"
},
"accountSelectionRequired": {
"message": "Devi selezionare un account!"
},
"activityLog": {
"message": "log attività"
},
"address": {
"message": "Indirizzo"
},
@ -23,6 +35,12 @@
"addTokens": {
"message": "Aggiungi più token"
},
"addSuggestedTokens": {
"message": "Aggiungi Token Suggeriti"
},
"addAcquiredTokens": {
"message": "Aggiungi i token che hai acquistato usando MetaMask"
},
"amount": {
"message": "Importo"
},
@ -37,9 +55,21 @@
"message": "MetaMask",
"description": "Il nome dell'applicazione"
},
"approve": {
"message": "Approva"
},
"approved": {
"message": "Approvato"
},
"attemptingConnect": {
"message": "Tentativo di connessione alla blockchain."
},
"attemptToCancel": {
"message": "Tentativo di Annullamento?"
},
"attemptToCancelDescription": {
"message": "Tentare di annullare non garantisce che la transazione verrà annullata. Se annullata, è comunque richiesto pagare alla rete la commissione sulla transazione."
},
"attributions": {
"message": "Attribuzioni"
},
@ -71,8 +101,11 @@
"borrowDharma": {
"message": "Prendi in presisito con Dharma (Beta)"
},
"browserNotSupported": {
"message": "Il tuo Browser non è supportato..."
},
"builtInCalifornia": {
"message": "MetaMask è progettato e costruito in California."
"message": "MetaMask è progettato e realizzato in California."
},
"buy": {
"message": "Compra"
@ -83,8 +116,23 @@
"buyCoinbaseExplainer": {
"message": "Coinbase è il servizio più popolare al mondo per comprare e vendere Bitcoin, Ethereum e Litecoin."
},
"bytes": {
"message": "Bytes"
},
"ok": {
"message": "Ok"
},
"cancel": {
"message": "Cancella"
"message": "Annulla"
},
"cancelAttempt": {
"message": "Tentativo di Annullamento"
},
"cancellationGasFee": {
"message": "Commissione di Annullamento in Gas"
},
"cancelN": {
"message": "Cancel all $1 transactions"
},
"classicInterface": {
"message": "Usa l'interfaccia classica"
@ -92,9 +140,18 @@
"clickCopy": {
"message": "Clicca per Copiare"
},
"close": {
"message": "Chiudi"
},
"chromeRequiredForHardwareWallets": {
"message": "Devi usare MetaMask con Google Chrome per connettere il tuo Portafoglio Hardware"
},
"confirm": {
"message": "Conferma"
},
"confirmed": {
"message": "Confermata"
},
"confirmContract": {
"message": "Conferma Contratto"
},
@ -104,6 +161,36 @@
"confirmTransaction": {
"message": "Conferma Transazione"
},
"connectHardwareWallet": {
"message": "Connetti Portafoglio Hardware"
},
"connect": {
"message": "Connetti"
},
"connecting": {
"message": "Connessione..."
},
"connectingToKovan": {
"message": "Connessione alla Rete di test Kovan"
},
"connectingToMainnet": {
"message": "Connessione alla Rete Ethereum Principale"
},
"connectingToRopsten": {
"message": "Connessione alla Rete di test Ropsten"
},
"connectingToRinkeby": {
"message": "Connessione alla Rete di test Rinkeby"
},
"connectingToUnknown": {
"message": "Connessione ad una Rete Sconosciuta"
},
"connectToLedger": {
"message": "Connettersi al Ledger"
},
"connectToTrezor": {
"message": "Connettersi al Trezor"
},
"continue": {
"message": "Continua"
},
@ -131,6 +218,9 @@
"copy": {
"message": "Copia"
},
"copyAddress": {
"message": "Copia l'indirizzo"
},
"copyToClipboard": {
"message": "Copia negli appunti"
},
@ -156,12 +246,21 @@
"currentConversion": {
"message": "Cambio Corrente"
},
"currentLanguage": {
"message": "Lingua Corrente"
},
"currentNetwork": {
"message": "Rete Corrente"
},
"currentRpc": {
"message": "RPC Corrente"
},
"customGas": {
"message": "Personalizza Gas"
},
"customToken": {
"message": "Token Personalizzato"
},
"customize": {
"message": "Personalizza"
},
@ -223,33 +322,54 @@
"done": {
"message": "Finito"
},
"downloadGoogleChrome": {
"message": "Scarica Google Chrome"
},
"downloadStateLogs": {
"message": "Scarica i log di Stato"
},
"dontHaveAHardwareWallet": {
"message": "Non hai un portafoglio hardware?"
},
"dropped": {
"message": "Abbandonata"
},
"edit": {
"message": "Modifica"
},
"editAccountName": {
"message": "Modifica Nome Account"
},
"editingTransaction": {
"message": "Modifica la transazione"
},
"emailUs": {
"message": "Mandaci una mail!"
},
"encryptNewDen": {
"message": "Cripta il tuo nuovo DEN"
},
"ensNameNotFound": {
"message": "Nome ENS non trovato"
},
"enterPassword": {
"message": "Inserisci password"
},
"enterPasswordConfirm": {
"message": "Inserisci la tua password per confermare"
},
"enterPasswordContinue": {
"message": "Inserisci la tua password per continuare"
},
"etherscanView": {
"message": "Vedi account su Etherscan"
},
"exchangeRate": {
"message": "Tasso di cambio"
},
"expandView": {
"message": "Expand View"
},
"exportPrivateKey": {
"message": "Esporta Chiave Privata"
},
@ -257,7 +377,7 @@
"message": "Esporta chiave privata a tuo rischio."
},
"failed": {
"message": "Fallito"
"message": "Fallita"
},
"fiat": {
"message": "FIAT",
@ -270,6 +390,9 @@
"followTwitter": {
"message": "Seguici su Twitter"
},
"forgetDevice": {
"message": "Dimentica questo dispositivo"
},
"from": {
"message": "Da"
},
@ -279,6 +402,9 @@
"fromShapeShift": {
"message": "Da ShapeShift"
},
"functionType": {
"message": "Tipo della Funzione"
},
"gas": {
"message": "Gas",
"description": "Piccola indicazione del costo del gas"
@ -310,6 +436,9 @@
"gasPriceRequired": {
"message": "Prezzo Gas Richiesto"
},
"generatingTransaction": {
"message": "Generando la transazione"
},
"getEther": {
"message": "Ottieni Ether"
},
@ -317,10 +446,28 @@
"message": "Ottieni Get Ether da un faucet per $1",
"description": "Visualizza il nome della rete per il faucet Ether"
},
"getHelp": {
"message": "Aiuto."
},
"greaterThanMin": {
"message": "deve essere maggiore o uguale a $1.",
"description": "aiuto per inserire un input esadecimale come decimale"
},
"hardware": {
"message": "hardware"
},
"hardwareWalletConnected": {
"message": "Portafoglio hardware connesso"
},
"hardwareWallets": {
"message": "Connetti portafoglio hardware"
},
"hardwareWalletsMsg": {
"message": "Selezione un portafoglio hardware che vuoi utilizzare con MetaMask"
},
"havingTroubleConnecting": {
"message": "Problemi di connessione?"
},
"here": {
"message": "qui",
"description": "per intendere -clicca qui- per maggiori informazioni (va con troubleTokenBalances)"
@ -328,6 +475,9 @@
"hereList": {
"message": "Questa è una lista!!!!"
},
"hexData": {
"message": "Dati Hex"
},
"hide": {
"message": "Nascondi"
},
@ -337,6 +487,9 @@
"hideTokenPrompt": {
"message": "Nascondi Token?"
},
"history": {
"message": "Storico"
},
"howToDeposit": {
"message": "Come vuoi depositare Ether?"
},
@ -363,9 +516,18 @@
"message": "Importato",
"description": "stato che conferma che un account è stato totalmente caricato nel portachiavi"
},
"importUsingSeed": {
"message": "Importa account con frase seed"
},
"info": {
"message": "Informazioni"
},
"infoHelp": {
"message": "Informazioni & Aiuto"
},
"initialTransactionConfirmed": {
"message": "La transazione iniziale è stata confermata dalla rete. Clicca OK per tornare indietro."
},
"insufficientFunds": {
"message": "Fondi non sufficienti."
},
@ -390,6 +552,9 @@
"invalidRPC": {
"message": "URI RPC invalido"
},
"invalidSeedPhrase": {
"message": "Frase seed non valida"
},
"jsonFail": {
"message": "Qualcosa è andato storto. Assicurati che il file JSON sia formattato correttamente."
},
@ -397,12 +562,24 @@
"message": "File JSON",
"description": "formato per importare un account"
},
"keepTrackTokens": {
"message": "Tieni traccia dei tokens che hai acquistato con il tuo account MetaMask."
},
"kovan": {
"message": "Rete di test Kovan"
},
"knowledgeDataBase": {
"message": "Visita la nostra Knowledge Base"
},
"max": {
"message": "Massimo"
},
"learnMore": {
"message": "Scopri di più"
},
"ledgerAccountRestriction": {
"message": "E' necessario utilizzare l'ultimo account prima di poterne aggiungere uno nuovo."
},
"lessThanMax": {
"message": "deve essere minore o uguale a $1.",
"description": "aiuto per inserire un input esadecimale come decimale"
@ -410,6 +587,9 @@
"likeToAddTokens": {
"message": "Vorresti aggiungere questi token?"
},
"links": {
"message": "Collegamenti"
},
"limit": {
"message": "Limite"
},
@ -437,17 +617,26 @@
"mainnet": {
"message": "Rete Ethereum Principale"
},
"menu": {
"message": "Menu"
},
"message": {
"message": "Messaggio"
},
"metamaskDescription": {
"message": "MetaMask è una cassaforte sicura per identità su Ethereum."
},
"metamaskSeedWords": {
"message": "Parole Seed di MetaMask"
},
"metamaskVersion": {
"message": "versione di MetaMask"
},
"min": {
"message": "Minimo"
},
"myAccounts": {
"message": "Account Miei"
"message": "Miei Account"
},
"mustSelectOne": {
"message": "Devi selezionare almeno un token."
@ -469,6 +658,9 @@
"networks": {
"message": "Reti"
},
"nevermind": {
"message": "Non importa"
},
"newAccount": {
"message": "Nuovo Account"
},
@ -482,6 +674,9 @@
"newPassword": {
"message": "Nuova Password (minimo 8 caratteri)"
},
"newPassword8Chars": {
"message": "Nuova Password (minimo 8 caratteri)"
},
"newRecipient": {
"message": "Nuovo Destinatario"
},
@ -489,7 +684,7 @@
"message": "Nuovo URL RPC"
},
"next": {
"message": "Prossimo"
"message": "Avanti"
},
"noAddressForName": {
"message": "Nessun indirizzo è stato impostato per questo nome."
@ -497,32 +692,75 @@
"noDeposits": {
"message": "Nessun deposito ricevuto"
},
"noConversionRateAvailable": {
"message": "Tasso di Conversione non Disponibile"
},
"noTransactionHistory": {
"message": "Nessuna cronologia delle transazioni."
},
"noTransactions": {
"message": "Nessuna Transazione"
},
"notFound": {
"message": "Non Trovata"
},
"notStarted": {
"message": "Non Iniziato"
},
"noWebcamFoundTitle": {
"message": "Webcam non trovata"
},
"noWebcamFound": {
"message": "La webcam del tuo computer non è stata trovata. Per favore riprovaci."
},
"oldUI": {
"message": "Vecchia interfaccia"
},
"oldUIMessage": {
"message": "Sei ritornato alla vecchia interfaccia. Puoi ritornare alla nuova interfaccia tramite l'opzione nel menu a discesa in alto a destra."
},
"onlySendToEtherAddress": {
"message": "Invia ETH solamente ad un account Ethereum."
},
"onlySendTokensToAccountAddress": {
"message": "Invia solamente $1 ad un account Ethereum.",
"description": "mostra il simbolo del token"
},
"openInTab": {
"message": "Apri in una scheda"
},
"or": {
"message": "o",
"description": "scelta tra creare o importare un nuovo account"
},
"orderOneHere": {
"message": "Compra un Trezor o un Ledger e tieni i tuoi soldi al sicuro"
},
"origin": {
"message": "Origine"
},
"outgoing": {
"message": "In Uscita"
},
"parameters": {
"message": "Parametri"
},
"password": {
"message": "Password"
},
"passwordCorrect": {
"message": "Assicurati che la password sia corretta."
},
"passwordsDontMatch": {
"message": "Le Password Non Corrispondonos"
},
"passwordMismatch": {
"message": "le password non corrispondono",
"description": "nella creazione della password, le due password all'interno dei campi non corrispondono"
},
"passwordNotLongEnough": {
"message": "Password non abbastanza lunga"
},
"passwordShort": {
"message": "password non sufficientemente lunga",
"description": "nella creazione della password, la password non è lunga abbastanza"
@ -534,12 +772,21 @@
"pasteSeed": {
"message": "Incolla la tua frase seed qui!"
},
"pending": {
"message": "in corso"
},
"personalAddressDetected": {
"message": "Rilevato indirizzo personale. Inserisci l'indirizzo del contratto del token."
},
"pleaseReviewTransaction": {
"message": "Ricontrolla la tua transazione."
},
"popularTokens": {
"message": "Tokens Popolari"
},
"prev": {
"message": "Precedente"
},
"privacyMsg": {
"message": "Politica sulla Privacy"
},
@ -556,6 +803,9 @@
"qrCode": {
"message": "Mostra Codice QR"
},
"queue": {
"message": "Coda"
},
"readdToken": {
"message": "Puoi aggiungere nuovamente questo token in futuro andando in “Aggiungi token” nel menu delle opzioni del tuo account."
},
@ -574,36 +824,87 @@
"refundAddress": {
"message": "Indirizzo di Rimborso"
},
"reject": {
"message": "Reject"
},
"rejectAll": {
"message": "Reject All"
},
"rejectTxsN": {
"message": "Reject $1 transactions"
},
"rejectTxsDescription": {
"message": "You are about to batch reject $1 transactions."
},
"rejected": {
"message": "Respinta"
},
"reset": {
"message": "Reset"
},
"resetAccount": {
"message": "Resetta Account"
},
"resetAccountDescription": {
"message": "Resettare il tuo account cancellerà lo storico delle transazioni."
},
"restoreFromSeed": {
"message": "Ripristina da una frase seed"
},
"restoreVault": {
"message": "Ripristina Cassaforte"
},
"restoreAccountWithSeed": {
"message": "Ripristina Account con la Frase Seed"
},
"required": {
"message": "Richiesto"
},
"retryWithMoreGas": {
"message": "Riprova con un prezzo del Gas maggiore qui"
},
"restore": {
"message": "Ripristina"
},
"revealSeedWords": {
"message": "Rivela Frase Seed"
},
"revealSeedWordsTitle": {
"message": "Frase Seed"
},
"revealSeedWordsDescription": {
"message": "Se cambierai browser o computer, ti servirà questa frase seed per accedere ai tuoi account. Salvala in un posto sicuro e segreto."
},
"revealSeedWordsWarningTitle": {
"message": "NON CONDIVIDERE questa frase con nessuno!"
},
"revealSeedWordsWarning": {
"message": "Non ripristinare la tua frase seed in pubblico!. Queste parole possono essere usate per rubare il tuo account."
},
"revert": {
"message": "Annulla"
},
"remove": {
"message": "rimuovi"
},
"removeAccount": {
"message": "Rimuovi account"
},
"removeAccountDescription": {
"message": "Questo account sarà rimosso dal tuo portafoglio. Per favore assicurati che hai la frase seed originale o la chiave privata per questo account importato prima di continuare. Puoi nuovamente importare o creare un account dal menù a tendina. "
},
"readyToConnect": {
"message": "Pronto a Connetterti?"
},
"rinkeby": {
"message": "Rete di test Rinkeby"
},
"ropsten": {
"message": "Rete di test Ropsten"
},
"rpc": {
"message": "RPC Personalizzata"
},
"sampleAccountName": {
"message": "Es: Il mio nuovo account",
"description": "Aiuta l'utente a capire il concetto di aggiungere un nome leggibile al loro account"
@ -611,6 +912,9 @@
"save": {
"message": "Salva"
},
"saveAsCsvFile": {
"message": "Salva Come File CSV"
},
"saveAsFile": {
"message": "Salva come File",
"description": "Processo per esportare un account"
@ -618,9 +922,18 @@
"saveSeedAsFile": {
"message": "Salva la Frase Seed come File"
},
"scanInstructions": {
"message": "Posizione il codice QR davanti alla fotocamera"
},
"scanQrCode": {
"message": "Scansiona Codice QR"
},
"search": {
"message": "Cerca"
},
"searchResults": {
"message": "Risultati Ricerca"
},
"secretPhrase": {
"message": "Inserisci la tua frase segreta di dodici parole per ripristinare la cassaforte."
},
@ -633,6 +946,9 @@
"selectCurrency": {
"message": "Seleziona Moneta"
},
"selectLocale": {
"message": "Selezione Lingua"
},
"selectService": {
"message": "Seleziona Servizio"
},
@ -648,6 +964,33 @@
"sendTokens": {
"message": "Invia Tokens"
},
"sentEther": {
"message": "ether inviati"
},
"sentTokens": {
"message": "tokens inviati"
},
"separateEachWord": {
"message": "Separa ogni parola con un solo spazio"
},
"searchTokens": {
"message": "Cerca Tokens"
},
"selectAnAddress": {
"message": "Seleziona un Indirizzo"
},
"selectAnAccount": {
"message": "Seleziona un Account"
},
"selectAnAccountHelp": {
"message": "Selezione l'account da visualizzare in MetaMask"
},
"selectHdPath": {
"message": "Seleziona Percorso HD"
},
"selectPathHelp": {
"message": "Se non vedi il tuo account Ledger esistente di seguito, prova a cambiare il percorso in \"Legacy (MEW / MyCrypto)\""
},
"sendTokensAnywhere": {
"message": "Invia Tokens a chiunque abbia un account Ethereum"
},
@ -663,9 +1006,21 @@
"showQRCode": {
"message": "Mostra Codie QR"
},
"showHexData": {
"message": "Mostra Dati Hex"
},
"showHexDataDescription": {
"message": "Seleziona per mostrare il campo dei dati hex nella schermata di invio"
},
"sign": {
"message": "Firma"
},
"signatureRequest": {
"message": "Firma Richiesta"
},
"signed": {
"message": "Firmata"
},
"signMessage": {
"message": "Firma Messaggio"
},
@ -681,6 +1036,15 @@
"spaceBetween": {
"message": "ci può essere solo uno spazio tra le parole"
},
"speedUp": {
"message": "velocizza"
},
"speedUpTitle": {
"message": "Velocizza Transazione"
},
"speedUpSubtitle": {
"message": "Aumenta il prezzo del gas per tentare di sovrascrivere e velocizzare la transazione"
},
"status": {
"message": "Stato"
},
@ -690,9 +1054,33 @@
"stateLogsDescription": {
"message": "I log di stato contengono i tuoi indirizzi pubblici e le transazioni effettuate."
},
"stateLogError": {
"message": "Errore nel recupero dei log di stato."
},
"step1HardwareWallet": {
"message": "1. Connetti Portafoglio Hardware"
},
"step1HardwareWalletMsg": {
"message": "Connetti il tuo portafoglio hardware al tuo computer."
},
"step2HardwareWallet": {
"message": "2. Seleziona un Account"
},
"step2HardwareWalletMsg": {
"message": "Selezione l'account che vuoi vedere. Puoi selezionarne solo uno alla volta."
},
"step3HardwareWallet": {
"message": "3. Inizia a usare dApps e molto altro ancora!"
},
"step3HardwareWalletMsg": {
"message": "Usa il tuo account hardware come utilizzeresti qualsiasi account Ethereum. Accedi alle dApps, invia Eth, compra e conserva token ERC20 e token non fungibili come CryptoKitties"
},
"submit": {
"message": "Invia"
},
"submitted": {
"message": "Inviata"
},
"supportCenter": {
"message": "Visita il nostro Centro di Supporto"
},
@ -715,6 +1103,9 @@
"message": "$1 a ETH via ShapeShift",
"description": "il sistema riempirà il tipo di deposito all'inizio del messaggio"
},
"token": {
"message": "Token"
},
"tokenAddress": {
"message": "Indirizzo Token"
},
@ -736,22 +1127,61 @@
"total": {
"message": "Totale"
},
"transaction": {
"message": "transazione"
},
"transactionConfirmed": {
"message": "Transazione confermata il $2."
},
"transactionCreated": {
"message": "Transazione di valore $1 creata il $2."
},
"transactionWithNonce": {
"message": "Transazione $1"
},
"transactionDropped": {
"message": "Transazione abbandonata il $2."
},
"transactionSubmitted": {
"message": "Transazione inviata il $2."
},
"transactionUpdated": {
"message": "Transazione aggiornata il $2."
},
"transactionUpdatedGas": {
"message": "Transazione aggiornata con un prezzo del gas di $1 il $2."
},
"transactions": {
"message": "transazioni"
},
"transactionError": {
"message": "Errore Transazione. Eccceziona generata nel codice del contratto."
},
"transactionMemo": {
"message": "Promemoria Transazione (opzionale)"
},
"transactionNumber": {
"message": "Numero Transazione"
},
"transfer": {
"message": "Trasferisci"
},
"transferFrom": {
"message": "Transfer From"
},
"transfers": {
"message": "Trasferimenti"
},
"trezorHardwareWallet": {
"message": "TREZOR Portafoglio Hardware"
},
"troubleTokenBalances": {
"message": "Abbiamo avuto un problema a caricare il bilancio dei tuoi token. Puoi vederlo ",
"description": "Seguito da un link (qui) per vedere il bilancio dei token"
},
"tryAgain": {
"message": "Prova di nuovo"
},
"twelveWords": {
"message": "Queste 12 parole sono l'unico modo per ripristinare i tuoi account MetaMask. \nSalvale in un posto sicuro e segreto."
},
@ -764,18 +1194,45 @@
"uiWelcomeMessage": {
"message": "Stai utilizzanto la nuova interfaccia di MetaMask. Guarda in giro, prova nuove funzionalità come inviare token, e facci sapere se hai dei problemi."
},
"unapproved": {
"message": "Non approvata"
},
"unavailable": {
"message": "Non Disponibile"
},
"units": {
"message": "unità"
},
"unknown": {
"message": "Sconosciuto"
},
"unknownFunction": {
"message": "Funzione Sconosciuta"
},
"unknownNetwork": {
"message": "Rete Privata Sconosciuta"
},
"unknownNetworkId": {
"message": "ID rete sconosciuto"
},
"unknownQrCode": {
"message": "Errore: Non siamo riusciti a identificare il codice QR"
},
"unknownCameraErrorTitle": {
"message": "Ooops! Qualcosa è andato storto...."
},
"unknownCameraError": {
"message": "C'è stato un errore nel tentativo di accedere alla fotocamera. Per favore prova di nuovo..."
},
"unlock": {
"message": "Sblocca"
},
"unlockMessage": {
"message": "Il web decentralizzato ti attende"
},
"updatedWithDate": {
"message": "Aggiornata $1"
},
"uriErrorMsg": {
"message": "Gli URI richiedono un prefisso HTTP/HTTPS."
},
@ -798,22 +1255,40 @@
"viewAccount": {
"message": "Vedi Account"
},
"viewOnEtherscan": {
"message": "Vedi su Etherscan"
},
"visitWebSite": {
"message": "Visita il nostro sito web"
},
"walletSeed": {
"message": "Seed del Portafoglio"
},
"warning": {
"message": "Attenzione"
},
"welcomeBack": {
"message": "Bentornato!"
},
"welcomeBeta": {
"message": "Benvenuto nella Beta di MetaMask"
},
"whatsThis": {
"message": "Cos'è questo?"
},
"yesLetsTry": {
"message": "Si, proviamo"
},
"youNeedToAllowCameraAccess": {
"message": "Devi consentire l'accesso alla fotocamera per usare questa funzionalità."
},
"yourSigRequested": {
"message": "E' richiesta la tua firma"
},
"youSign": {
"message": "Ti stai connettendo"
},
"yourPrivateSeedPhrase": {
"message": "La tua frase seed privata"
}
}

@ -122,9 +122,6 @@
"copy": {
"message": "コピー"
},
"copyContractAddress": {
"message": "コントラクトアドレスをコピー"
},
"copyToClipboard": {
"message": "クリップボードへコピー"
},

@ -2,6 +2,9 @@
"accept": {
"message": "수락"
},
"accessingYourCamera": {
"message": "카메라에 접근 중..."
},
"account": {
"message": "계정"
},
@ -11,6 +14,15 @@
"accountName": {
"message": "계정 이름"
},
"accountOptions": {
"message": "계정 옵션"
},
"accountSelectionRequired": {
"message": "계정을 선택하셔야 합니다!"
},
"activityLog": {
"message": "활동 로그"
},
"address": {
"message": "주소"
},
@ -23,6 +35,9 @@
"addTokens": {
"message": "토큰 추가"
},
"addSuggestedTokens": {
"message": "제안된 토큰 추가"
},
"addAcquiredTokens": {
"message": "메타마스크를 통해 획득한 토큰 추가"
},
@ -34,11 +49,14 @@
},
"appDescription": {
"message": "이더리움 브라우저 확장 프로그램",
"description": "플리케이션 설명"
"description": "플리케이션 설명"
},
"appName": {
"message": "메타마스크",
"description": "어플리케이션 이름"
"description": "애플리케이션 이름"
},
"approve": {
"message": "수락"
},
"approved": {
"message": "수락"
@ -46,6 +64,9 @@
"attemptingConnect": {
"message": "블록체인에 접속을 시도하는 중입니다."
},
"attemptToCancel": {
"message": "취소 하시겠습니까?"
},
"attributions": {
"message": "속성"
},
@ -75,7 +96,10 @@
"message": "Blockies 아이덴티콘 사용"
},
"borrowDharma": {
"message": "Dharma에서 대여하기(Beta)"
"message": "Dharma에서 대출받기 (Beta)"
},
"browserNotSupported": {
"message": "브라우저를 지원하지 않습니다..."
},
"builtInCalifornia": {
"message": "메타마스크는 캘리포니아에서 디자인되고 만들어졌습니다."
@ -89,12 +113,24 @@
"buyCoinbaseExplainer": {
"message": "코인베이스는 비트코인, 이더리움, 라이트코인을 거래할 수 있는 유명한 거래소입니다."
},
"bytes": {
"message": "바이트"
},
"ok": {
"message": "확인"
},
"cancel": {
"message": "취소"
},
"cancelAttempt": {
"message": "취소 시도"
},
"cancellationGasFee": {
"message": "취소 가스 수수료"
},
"cancelN": {
"message": "모든 $1 트랜잭션 취소"
},
"classicInterface": {
"message": "예전 인터페이스"
},
@ -104,6 +140,9 @@
"close": {
"message": "닫기"
},
"chromeRequiredForHardwareWallets": {
"message": "하드웨어 지갑을 연결하기 위해서는 구글 크롬에서 메타마스크를 사용하셔야 합니다."
},
"confirm": {
"message": "승인"
},
@ -119,6 +158,36 @@
"confirmTransaction": {
"message": "트랜잭션 승인"
},
"connectHardwareWallet": {
"message": "하드웨어 지갑 연결"
},
"connect": {
"message": "연결"
},
"connecting": {
"message": "연결 중..."
},
"connectingToMainnet": {
"message": "이더리움 메인넷 접속 중"
},
"connectingToRopsten": {
"message": "Ropsten 테스트넷 접속 중"
},
"connectingToKovan": {
"message": "Kovan 테스트넷 접속 중"
},
"connectingToRinkeby": {
"message": "Rinkeby 테스트넷 접속 중"
},
"connectingToUnknown": {
"message": "알 수 없는 네트워크 접속 중"
},
"connectToLedger": {
"message": "Ledger 연결"
},
"connectToTrezor": {
"message": "Trezor 연결"
},
"continue": {
"message": "계속"
},
@ -146,8 +215,8 @@
"copy": {
"message": "복사"
},
"copyContractAddress": {
"message": "컨트랙트 주소 복사"
"copyAddress": {
"message": "클립보드로 주소 복사"
},
"copyToClipboard": {
"message": "클립보드로 복사"
@ -172,11 +241,17 @@
"description": "거래 유형 (암호화폐)"
},
"currentConversion": {
"message": "선택된 단위"
"message": "현재 통화"
},
"currentLanguage": {
"message": "현재 언어"
},
"currentNetwork": {
"message": "현재 네트워크"
},
"currentRpc": {
"message": "현재 RPC"
},
"customGas": {
"message": "가스 설정"
},
@ -205,7 +280,7 @@
"message": "입금"
},
"depositBTC": {
"message": "다음 주소로 BTC를 입해주세요."
"message": "다음 주소로 BTC를 입해주세요."
},
"depositCoin": {
"message": "다음 주소로 $1 만큼 입금해주세요.",
@ -244,9 +319,15 @@
"done": {
"message": "완료"
},
"downloadGoogleChrome": {
"message": "구글 크롬 다운로드"
},
"downloadStateLogs": {
"message": "상태 로그 다운로드"
},
"dontHaveAHardwareWallet": {
"message": "하드웨어 지갑이 없나요?"
},
"dropped": {
"message": "중단됨"
},
@ -257,7 +338,7 @@
"message": "계정 이름 수정"
},
"editingTransaction": {
"message": "트랜션을 변경합니다"
"message": "트랜션을 변경합니다"
},
"emailUs": {
"message": "저자에게 메일 보내기!"
@ -265,6 +346,9 @@
"encryptNewDen": {
"message": "새로운 DEN을 암호화"
},
"ensNameNotFound": {
"message": "ENS 이름을 찾을 수 없습니다"
},
"enterPassword": {
"message": "비밀번호를 입력해주세요"
},
@ -274,18 +358,15 @@
"enterPasswordContinue": {
"message": "계속하기 위해 비밀번호 입력"
},
"passwordNotLongEnough": {
"message": "비밀번호가 충분히 길지 않습니다"
},
"passwordsDontMatch": {
"message": "비밀번호가 맞지 않습니다"
},
"etherscanView": {
"message": "이더스캔에서 계정보기"
},
"exchangeRate": {
"message": "환율"
},
"expandView": {
"message": "큰 화면으로 보기"
},
"exportPrivateKey": {
"message": "개인키 내보내기"
},
@ -306,15 +387,21 @@
"followTwitter": {
"message": "트위터에서 팔로우하세요"
},
"forgetDevice": {
"message": "장치 연결 해제"
},
"from": {
"message": "보내는 이"
},
"fromToSame": {
"message": "보내고 받는 주소는 동일할 수 없습니다"
"message": "보내고 받는 주소는 같을 수 없습니다"
},
"fromShapeShift": {
"message": "ShapeShift로부터"
},
"functionType": {
"message": "함수 유형"
},
"gas": {
"message": "가스",
"description": "가스 가격의 줄임"
@ -332,7 +419,7 @@
"message": "가스 한도가 필요합니다."
},
"gasLimitTooLow": {
"message": "가스 한도는 최소 21000 이상이야 합니다."
"message": "가스 한도는 최소 21000 이상이야 합니다."
},
"generatingSeed": {
"message": "시드 생성 중..."
@ -356,10 +443,28 @@
"message": "파우셋에서 $1에 달하는 이더를 얻으세요.",
"description": "이더 파우셋에 대한 네트워크 이름을 표시합니다"
},
"getHelp": {
"message": "도움말"
},
"greaterThanMin": {
"message": "$1 이상이어야 합니다.",
"description": "10진수 입력으로 hex값 입력을 도와줍니다"
},
"hardware": {
"message": "하드웨어"
},
"hardwareWalletConnected": {
"message": "하드웨어 지갑이 연결됨"
},
"hardwareWallets": {
"message": "하드웨어 지갑 연결"
},
"hardwareWalletsMsg": {
"message": "메타마스크에서 사용할 하드웨어 지갑을 선택해주세요"
},
"havingTroubleConnecting": {
"message": "연결에 문제가 있나요?"
},
"here": {
"message": "여기",
"description": "as in -click here- for more information (goes with troubleTokenBalances)"
@ -367,6 +472,9 @@
"hereList": {
"message": "리스트가 있습니다!!!!"
},
"hexData": {
"message": "Hex 데이터"
},
"hide": {
"message": "숨기기"
},
@ -376,6 +484,9 @@
"hideTokenPrompt": {
"message": "토큰 숨기기?"
},
"history": {
"message": "히스토리"
},
"howToDeposit": {
"message": "어떤 방법으로 이더를 입금하시겠습니까?"
},
@ -400,16 +511,19 @@
},
"imported": {
"message": "가져온 계정",
"description": "이 상태는 해당 계정이 keyring으로 완전히 적재된 상태임을 표시합니다"
"description": "이 상태는 해당 계정이 keyring으로 완전히 로드된 상태임을 표시합니다"
},
"importUsingSeed": {
"message": "계정 시드 구문으로 가져오기"
},
"info": {
"message": "정보"
},
"infoHelp": {
"message": "정보 및 도움말"
},
"initialTransactionConfirmed": {
"message": "초기 트랜잭션이 네트워크를 통해 확정되었습니다. 확인을 누르 이전으로 돌아갑니다."
"message": "초기 트랜잭션이 네트워크를 통해 확정되었습니다. 확인을 누르 이전으로 돌아갑니다."
},
"insufficientFunds": {
"message": "충분하지 않은 자금."
@ -435,6 +549,9 @@
"invalidRPC": {
"message": "올바르지 않은 RPC URI"
},
"invalidSeedPhrase": {
"message": "잘못된 시드 구문"
},
"jsonFail": {
"message": "이상이 있습니다. JSON 파일이 올바른 파일인지 확인해주세요."
},
@ -455,7 +572,10 @@
"message": "최대"
},
"learnMore": {
"message": "더 배우기."
"message": "더 알아보기."
},
"ledgerAccountRestriction": {
"message": "새 계정을 추가하려면 최소 마지막 계정을 사용해야 합니다."
},
"lessThanMax": {
"message": "$1 이하여야합니다.",
@ -494,12 +614,18 @@
"mainnet": {
"message": "이더리움 메인넷"
},
"menu": {
"message": "메뉴"
},
"message": {
"message": "메시지"
},
"metamaskDescription": {
"message": "메타마스크는 이더리움을 위한 안전한 신분 저장소입니다."
},
"metamaskVersion": {
"message": "메타마스크 버전"
},
"metamaskSeedWords": {
"message": "메타마스크 시드 단어"
},
@ -560,35 +686,72 @@
"noDeposits": {
"message": "입금 내역이 없습니다."
},
"noConversionRateAvailable": {
"message": "변환 비율을 찾을 수 없습니다"
},
"noTransactionHistory": {
"message": "트랜잭션 기록이 없습니다."
},
"noTransactions": {
"message": "트랜잭션이 없습니다"
},
"notFound": {
"message": "찾을 수 없음"
},
"notStarted": {
"message": "시작 안 됨"
},
"noWebcamFound": {
"message": "컴퓨터의 웹캠을 찾을 수 없습니다. 다시 시도해보세요."
},
"noWebcamFoundTitle": {
"message": "웹캠이 없습니다"
},
"oldUI": {
"message": "구버전 UI"
},
"oldUIMessage": {
"message": "구버전 UI로 변경하셨습니다. 우 상단 드롭다운 메뉴에서 새 UI로 변경하실 수 있습니다."
"message": "구버전 UI로 변경하셨습니다. 오른쪽 위 드롭다운 메뉴에서 새 UI로 변경하실 수 있습니다."
},
"onlySendToEtherAddress": {
"message": "이더리움 주소로 ETH만 송금하세요."
},
"onlySendTokensToAccountAddress": {
"message": "이더리움 계정 주소로 $1만 보내기.",
"description": "토큰 심볼 보이기"
},
"openInTab": {
"message": "탭으로 열기"
},
"or": {
"message": "또는",
"description": "새 계정을 만들거나 가져오기 중에 선택하기"
},
"orderOneHere": {
"message": "Trezor 혹은 Ledger를 구입하고 자금을 콜드 스토리지에 저장합니다"
},
"origin": {
"message": "Origin"
},
"parameters": {
"message": "매개변수"
},
"password": {
"message": "비밀번호"
},
"passwordCorrect": {
"message": "비밀번호가 맞는지 확인해주세요."
},
"passwordsDontMatch": {
"message": "비밀번호가 맞지 않습니다"
},
"passwordMismatch": {
"message": "비밀번호가 일치하지 않습니다.",
"description": "비밀번호를 생성하는 과정에서 두 개의 새 비밀번호 입력란이 일치하지 않습니다"
},
"passwordNotLongEnough": {
"message": "비밀번호가 충분히 길지 않습니다"
},
"passwordShort": {
"message": "비밀번호가 짧습니다.",
"description": "비밀번호를 생성하는 과정에서 비밀번호가 짧으면 안전하지 않습니다"
@ -600,6 +763,9 @@
"pasteSeed": {
"message": "시드 구문을 이곳에 붙여넣어 주세요!"
},
"pending": {
"message": "펜딩 중"
},
"personalAddressDetected": {
"message": "개인 주소가 탐지됨. 토큰 컨트랙트 주소를 입력하세요."
},
@ -609,6 +775,9 @@
"popularTokens": {
"message": "인기있는 토큰"
},
"prev": {
"message": "이전"
},
"privacyMsg": {
"message": "개인정보 보호 정책"
},
@ -625,6 +794,9 @@
"qrCode": {
"message": "QR 코드 보기"
},
"queue": {
"message": "큐"
},
"readdToken": {
"message": "옵션 메뉴에서 “토큰 추가”를 눌러서 추후에 다시 이 토큰을 추가하실 수 있습니다."
},
@ -643,6 +815,18 @@
"refundAddress": {
"message": "환불받을 주소"
},
"reject": {
"message": "거부"
},
"rejectAll": {
"message": "모두 거부"
},
"rejectTxsN": {
"message": "$1 트랜잭션 거부"
},
"rejectTxsDescription": {
"message": "$1 트랜잭션을 거부합니다."
},
"rejected": {
"message": "거부됨"
},
@ -661,14 +845,17 @@
"restoreVault": {
"message": "저장소 복구"
},
"restoreAccountWithSeed": {
"message": "시드 구문으로 계정 복구하기"
},
"required": {
"message": "필요함"
},
"retryWithMoreGas": {
"message": "더 높은 가스 가격으로 다시 시도해주세요"
},
"walletSeed": {
"message": "지갑 시드값"
"restore": {
"message": "복구"
},
"revealSeedWords": {
"message": "시드 단어 보이기"
@ -688,6 +875,18 @@
"revert": {
"message": "되돌림"
},
"remove": {
"message": "제거"
},
"removeAccount": {
"message": "계정 제거"
},
"removeAccountDescription": {
"message": "이 계정은 지갑에서 삭제될 것입니다. 지우기 전에 이 계정에 대한 개인 키 혹은 시드 구문을 가지고 있는지 확인하세요. 계정 드롭다운 메뉴를 통해서 계정을 가져오거나 생성할 수 있습니다."
},
"readyToConnect": {
"message": "접속 준비되었나요?"
},
"rinkeby": {
"message": "Rinkeby 테스트넷"
},
@ -697,24 +896,6 @@
"rpc": {
"message": "사용자 정의 RPC"
},
"currentRpc": {
"message": "현재 RPC"
},
"connectingToMainnet": {
"message": "이더리움 메인넷 접속중"
},
"connectingToRopsten": {
"message": "Ropsten 테스트넷 접속중"
},
"connectingToKovan": {
"message": "Kovan 테스트넷 접속중"
},
"connectingToRinkeby": {
"message": "Rinkeby 테스트넷 접속중"
},
"connectingToUnknown": {
"message": "알려지지 않은 네트워크 접속중"
},
"sampleAccountName": {
"message": "예) 나의 새 계정",
"description": "각 계정에 대해서 구별하기 쉬운 이름을 지정하여 사용자가 쉽게 이해할 수 있게 합니다"
@ -726,7 +907,7 @@
"message": "트랜잭션 속도 향상하기"
},
"speedUpSubtitle": {
"message": "트랜잭션 가스 가격을 늘려서 해당 트랙잭션에 덮어쓰기 하여 속도를 빠르게 합니다"
"message": "트랜잭션 가스 가격을 올려서 해당 트랜잭션에 덮어쓰고 속도를 빠르게 합니다"
},
"saveAsCsvFile": {
"message": "CSV 파일로 저장"
@ -738,6 +919,12 @@
"saveSeedAsFile": {
"message": "시드 단어를 파일로 저장하기"
},
"scanInstructions": {
"message": "QR 코드를 카메라 앞에 가져다 놓아주세요"
},
"scanQrCode": {
"message": "QR 코드 스캔"
},
"search": {
"message": "검색"
},
@ -759,6 +946,9 @@
"selectCurrency": {
"message": "통화 선택"
},
"selectLocale": {
"message": "언어 선택"
},
"selectService": {
"message": "서비스 선택"
},
@ -774,25 +964,39 @@
"sendTokens": {
"message": "토큰 전송"
},
"onlySendToEtherAddress": {
"message": "이더리움 주소로 ETH만 송금하세요."
"sentEther": {
"message": "전송된 이더"
},
"onlySendTokensToAccountAddress": {
"message": "이더리움계정 주소로 $1 만 보내기.",
"description": "토큰 심볼 보이기"
"sentTokens": {
"message": "전송된 토큰"
},
"separateEachWord": {
"message": "각 단어는 공백 한칸으로 분리합니다"
},
"searchTokens": {
"message": "토큰 검색"
},
"selectAnAddress": {
"message": "주소 선택"
},
"selectAnAccount": {
"message": "계정 선택"
},
"selectAnAccountHelp": {
"message": "메타마스크에서 보기 위한 계정 선택"
},
"selectHdPath": {
"message": "HD 경로 지정"
},
"selectPathHelp": {
"message": "하단에서 Ledger 지갑 계정을 찾지 못하겠으면 \"Legacy (MEW / MyCrypto)\" 경로로 바꿔보세요"
},
"sendTokensAnywhere": {
"message": "이더 계정로 토큰 전송"
"message": "이더 계정로 토큰 전송"
},
"settings": {
"message": "설정"
},
"info": {
"message": "정보"
},
"shapeshiftBuy": {
"message": "Shapeshift를 통해서 구매하기"
},
@ -802,12 +1006,21 @@
"showQRCode": {
"message": "QR 코드 보기"
},
"showHexData": {
"message": "Hex 데이터 보기"
},
"showHexDataDescription": {
"message": "선택하면 전송화면의 hex 데이터 필드 값을 보여줍니다."
},
"sign": {
"message": "서명"
},
"signed": {
"message": "서명됨"
},
"signatureRequest": {
"message": "서명 요청"
},
"signMessage": {
"message": "메시지 서명"
},
@ -835,6 +1048,24 @@
"stateLogError": {
"message": "상태 로그 받기 실패."
},
"step1HardwareWallet": {
"message": "1. 하드웨어 지갑 연결"
},
"step1HardwareWalletMsg": {
"message": "하드웨어 지갑을 컴퓨터에 연결해주세요."
},
"step2HardwareWallet": {
"message": "2. 계정 선택"
},
"step2HardwareWalletMsg": {
"message": "보고 싶은 계정을 선택합니다. 한 번에 하나의 계정만 선택할 수 있습니다."
},
"step3HardwareWallet": {
"message": "3. dApps을 사용하거나 다른 것을 합니다!"
},
"step3HardwareWalletMsg": {
"message": "다른 이더리움 계정을 사용하듯 하드웨어 계정을 사용합니다. dApps을 로그인하거나, 이더를 보내거나, ERC20 토큰 혹은 대체 가능하지 않은 토큰 (예를 들어 CryptoKitties)을 사거나 저장하거나 합니다."
},
"submit": {
"message": "제출"
},
@ -882,11 +1113,14 @@
"message": "토큰 기호"
},
"tokenWarning1": {
"message": "메타마스크 계좌를 통해 구입한 토큰을 추적합니다. 다른 계정으로 토큰을 구입한 경우 이곳에 나타나지 않습니다."
"message": "메타마스크 계정을 통해 구입한 토큰을 추적합니다. 다른 계정으로 토큰을 구입한 경우 이곳에 나타나지 않습니다."
},
"total": {
"message": "합계"
},
"transaction": {
"message": "트랜잭션"
},
"transactions": {
"message": "트랜잭션"
},
@ -899,13 +1133,22 @@
"transactionNumber": {
"message": "트랜잭션 번호"
},
"transfer": {
"message": "전송"
},
"transfers": {
"message": "전송"
},
"trezorHardwareWallet": {
"message": "TREZOR 하드웨어 지갑"
},
"troubleTokenBalances": {
"message": "토큰 잔액을 가져오는 데에 문제가 생겼습니다. 링크에서 상세내용을 볼 수 있습니다.",
"description": "토큰 잔액을 보려면 (here) 링크를 따라가세요"
},
"tryAgain": {
"message": "다시 시도하세요"
},
"twelveWords": {
"message": "12개의 단어는 메타마스크 계정을 복구하기 위한 유일한 방법입니다.\n안전한 장소에 보관하시기 바랍니다."
},
@ -922,16 +1165,34 @@
"message": "허가 안 됨"
},
"unavailable": {
"message": "유효하지 않음"
"message": "이용할 수 없음"
},
"units": {
"message": "단위"
},
"unknown": {
"message": "알려지지 않음"
"message": "알 수 없음"
},
"unknownFunction": {
"message": "알 수 없는 함수"
},
"unknownNetwork": {
"message": "알려지지 않은 프라이빗 네트워크"
"message": "알 수 없는 프라이빗 네트워크"
},
"unknownNetworkId": {
"message": "알려지지 않은 네트워크 ID"
"message": "알 수 없는 네트워크 ID"
},
"unknownQrCode": {
"message": "오류: QR 코드를 확인할 수 없습니다"
},
"unknownCameraErrorTitle": {
"message": "이런! 뭔가 잘못되었습니다...."
},
"unknownCameraError": {
"message": "카메라에 접근하는 중 오류가 발생했습니다. 다시 시도해 주세요..."
},
"unlock": {
"message": "잠금 해제"
},
"unlockMessage": {
"message": "우리가 기다리던 분권형 웹입니다"
@ -959,11 +1220,14 @@
"message": "계정 보기"
},
"viewOnEtherscan": {
"message": "이스캔에서 보기"
"message": "이스캔에서 보기"
},
"visitWebSite": {
"message": "웹사이트 방문"
},
"walletSeed": {
"message": "지갑 시드값"
},
"warning": {
"message": "경고"
},
@ -976,207 +1240,18 @@
"whatsThis": {
"message": "이것은 무엇인가요?"
},
"yesLetsTry": {
"message": "네, 시도해보겠습니다."
},
"yourSigRequested": {
"message": "서명이 요청되고 있습니다."
"message": "서명을 요청 중입니다."
},
"youSign": {
"message": "서명 중입니다"
"message": "서명니다"
},
"yourPrivateSeedPhrase": {
"message": "개인 시드 구문"
},
"accessingYourCamera": {
"message": "카메라 접근중..."
},
"accountSelectionRequired": {
"message": "계정을 선택하셔야 합니다!"
},
"approve": {
"message": "수락"
},
"browserNotSupported": {
"message": "브라우저가 지원하지 않습니다..."
},
"bytes": {
"message": "바이트"
},
"chromeRequiredForHardwareWallets": {
"message": "하드웨어 지갑을 연결하기 위해서는 구글 크롬에서 메타마스크를 사용하셔야 합니다."
},
"connectHardwareWallet": {
"message": "하드웨어 지갑 연결"
},
"connect": {
"message": "연결"
},
"connecting": {
"message": "연결중..."
},
"connectToLedger": {
"message": "Ledger 연결"
},
"connectToTrezor": {
"message": "Trezor 연결"
},
"copyAddress": {
"message": "클립보드로 주소 복사"
},
"downloadGoogleChrome": {
"message": "구글 크롬 다운로드"
},
"dontHaveAHardwareWallet": {
"message": "하드웨어 지갑이 없나요?"
},
"ensNameNotFound": {
"message": "ENS 이름을 찾을 수 없습니다"
},
"parameters": {
"message": "매개변수"
},
"forgetDevice": {
"message": "장치 연결 해제"
},
"functionType": {
"message": "함수 유형"
},
"getHelp": {
"message": "도움말"
},
"hardware": {
"message": "하드웨어"
},
"hardwareWalletConnected": {
"message": "하드웨어 지갑이 연결됨"
},
"hardwareWallets": {
"message": "하드웨어 지갑 연결"
},
"hardwareWalletsMsg": {
"message": "메타마스크에서 사용할 하드웨어 지갑을 선택해주세요"
},
"havingTroubleConnecting": {
"message": "연결에 문제가 있나요?"
},
"hexData": {
"message": "Hex 데이터"
},
"invalidSeedPhrase": {
"message": "잘못된 시드 구문"
},
"ledgerAccountRestriction": {
"message": "새 계정을 추가하려면 최소 마지막 계정을 사용해야 합니다."
},
"menu": {
"message": "메뉴"
},
"noConversionRateAvailable": {
"message": "변환 비율을 찾을 수 없습니다"
},
"notFound": {
"message": "찾을 수 없음"
},
"noWebcamFoundTitle": {
"message": "웹캠이 없습니다"
},
"noWebcamFound": {
"message": "컴퓨터의 웹캠을 찾을 수 없습니다. 다시 시도해보세요."
},
"openInTab": {
"message": "탭으로 열기"
},
"origin": {
"message": "Origin"
},
"prev": {
"message": "이전"
},
"restoreAccountWithSeed": {
"message": "시드 구문으로 계정 복구하기"
},
"restore": {
"message": "복구"
},
"remove": {
"message": "제거"
},
"removeAccount": {
"message": "계정 제거"
},
"removeAccountDescription": {
"message": "이 계정한 지갑에서 삭제될 것입니다. 지우기 전에 이 계정에 대한 개인 키 혹은 시드 구문을 가지고 있는지 확인하세요. 계정 드랍다운 메뉴를 통해서 계정을 가져오거나 생성할 수 있습니다."
},
"readyToConnect": {
"message": "접속 준비되었나요?"
},
"separateEachWord": {
"message": "각 단어는 공백 한칸으로 분리합니다"
},
"orderOneHere": {
"message": "Trezor 혹은 Ledger를 구입하고 자금을 콜드 스토리지에 저장합니다"
},
"selectAnAddress": {
"message": "주소 선택"
},
"selectAnAccount": {
"message": "계정 선택"
},
"selectAnAccountHelp": {
"message": "메타마스크에서 보기위한 계정 선택"
},
"selectHdPath": {
"message": "HD 경로 지정"
},
"selectPathHelp": {
"message": "하단에서 Ledger지갑 계정을 찾지 못하겠으면 \"Legacy (MEW / MyCrypto)\" 경로로 바꿔보세요"
},
"step1HardwareWallet": {
"message": "1. 하드웨어 지갑 연결"
},
"step1HardwareWalletMsg": {
"message": "하드웨어 지갑을 컴퓨터에 연결해주세요."
},
"step2HardwareWallet": {
"message": "2. 계정 선택"
},
"step2HardwareWalletMsg": {
"message": "보고 싶은 계정을 선택합니다. 한번에 하나의 계정만 선택할 수 있습니다."
},
"step3HardwareWallet": {
"message": "3. dApps을 사용하거나 다른 것을 합니다!"
},
"step3HardwareWalletMsg": {
"message": "다른 이더리움 계정을 사용하듯 하드웨어 계정을 사용합니다. dApps을 로그인하거나, 이더를 보내거나, ERC20토큰 혹은 대체가능하지 않은 토큰 (예를 들어 CryptoKitties)을 사거나 저장하거나 합니다."
},
"scanInstructions": {
"message": "QR 코드를 카메라 앞에 가져다 놓아주세요"
},
"scanQrCode": {
"message": "QR 코드 스캔"
},
"transfer": {
"message": "전송"
},
"trezorHardwareWallet": {
"message": "TREZOR 하드웨어 지갑"
},
"tryAgain": {
"message": "다시 시도하세요"
},
"unknownFunction": {
"message": "알 수 없는 함수"
},
"unknownQrCode": {
"message": "오류: QR 코드를 확인할 수 없습니다"
},
"unknownCameraErrorTitle": {
"message": "이런! 뭔가 잘못되었습니다...."
},
"unknownCameraError": {
"message": "카메라를 접근하는 중 오류가 발생했습니다. 다시 시도해 주세요..."
},
"unlock": {
"message": "잠금 해제"
},
"youNeedToAllowCameraAccess": {
"message": "이 기능을 사용하려면 카메라 접근을 허용해야 합니다."
}

@ -23,6 +23,9 @@
"addTokens": {
"message": "添加代币"
},
"addAcquiredTokens": {
"message": "在Metamask上添加已用的代币"
},
"amount": {
"message": "数量"
},
@ -116,6 +119,9 @@
"confirmTransaction": {
"message": "确认交易"
},
"connectHardwareWallet": {
"message": "链接硬件钱包"
},
"continue": {
"message": "继续"
},
@ -714,6 +720,9 @@
"search": {
"message": "搜索"
},
"searchResults": {
"message": "搜索结果"
},
"secretPhrase": {
"message": "输入12位助记词以恢复金库."
},

@ -0,0 +1,3 @@
<svg width="10" height="10" viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.67589 0.641872C8.65169 0.642635 8.62756 0.644749 8.6036 0.648202H4.79279C4.55863 0.644896 4.34082 0.767704 4.22278 0.969601C4.10473 1.1715 4.10473 1.4212 4.22278 1.6231C4.34082 1.825 4.55863 1.9478 4.79279 1.9445H7.12113L0.437932 8.61587C0.268309 8.77843 0.19998 9.01984 0.259298 9.24697C0.318616 9.47411 0.496311 9.65149 0.723852 9.71071C0.951393 9.76992 1.19322 9.70171 1.35608 9.53239L8.03927 2.86102V5.18524C8.03596 5.41898 8.15899 5.6364 8.36124 5.75424C8.56349 5.87208 8.81364 5.87208 9.0159 5.75424C9.21815 5.6364 9.34118 5.41898 9.33787 5.18524V1.37863C9.36404 1.18976 9.30558 0.998955 9.17804 0.857009C9.0505 0.715062 8.86682 0.636369 8.67589 0.641872Z" fill="#359BDD"/>
</svg>

After

Width:  |  Height:  |  Size: 795 B

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 50.2 (55047) - http://www.bohemiancoding.com/sketch -->
<title>expand</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Extension-(mobile-form-factor)" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="expand" fill="#FFFFFF" fill-rule="nonzero">
<path d="M15.4802977,0.0647543729 C15.3777028,0.0227060788 15.2692212,0 15.1590578,0 L10.1134046,0 C9.64920448,0 9.27246238,0.376752715 9.27246238,0.840965882 C9.27246238,1.30602001 9.64920448,1.68193176 10.1134046,1.68193176 L13.1290233,1.68193176 L8.77993963,6.03113791 C8.45113123,6.35995557 8.45113123,6.89228697 8.77993963,7.22026366 C8.94392336,7.38509298 9.15920457,7.46666667 9.37448577,7.46666667 C9.58976698,7.46666667 9.80504818,7.38509298 9.96903191,7.22026366 L14.3181156,2.87105752 L14.3181156,5.88676117 C14.3181156,6.35181531 14.6948577,6.72772705 15.1590578,6.72772705 C15.6232579,6.72772705 16,6.35181531 16,5.88676117 L16,0.840965882 C16,0.731640317 15.9781355,0.623155718 15.9352475,0.519716915 C15.8494713,0.31452124 15.6863286,0.150532893 15.4802977,0.0647543729 Z" id="Fill-1" transform="translate(12.266667, 3.733333) rotate(-360.000000) translate(-12.266667, -3.733333) "></path>
<path d="M6.94696439,8.59808771 C6.84436944,8.55603941 6.73588789,8.53333333 6.62572446,8.53333333 L1.58007124,8.53333333 C1.11587115,8.53333333 0.739129042,8.91008605 0.739129042,9.37429922 C0.739129042,9.83935335 1.11587115,10.2152651 1.58007124,10.2152651 L4.59568999,10.2152651 L0.246606301,14.5644712 C-0.0822021004,14.8932889 -0.0822021004,15.4256203 0.246606301,15.753597 C0.410590031,15.9184263 0.625871235,16 0.841152439,16 C1.05643364,16 1.27171485,15.9184263 1.43569858,15.753597 L5.78478226,11.4043909 L5.78478226,14.4200945 C5.78478226,14.8851486 6.16152437,15.2610604 6.62572446,15.2610604 C7.08992456,15.2610604 7.46666667,14.8851486 7.46666667,14.4200945 L7.46666667,9.37429922 C7.46666667,9.26497365 7.44480217,9.15648905 7.40191412,9.05305025 C7.31613801,8.84785457 7.15299522,8.68386623 6.94696439,8.59808771 Z" id="Fill-1-Copy" transform="translate(3.733333, 12.266667) rotate(-180.000000) translate(-3.733333, -12.266667) "></path>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="12px" viewBox="0 0 16 12" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 50.2 (55047) - http://www.bohemiancoding.com/sketch -->
<title>hide</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Extension-(mobile-form-factor)" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="hide" fill="#FFFFFF">
<polygon id="Fill-1" points="12.7924541 0.685525644 2.5021101 11.2520394 3.21020258 11.978038 13.4995465 1.41152421"></polygon>
<path d="M4.53087507,6.33167913 C4.53087507,4.36419221 6.08507807,2.76945986 8.00132835,2.76945986 C8.40338086,2.76945986 8.78243037,2.85366337 9.14147727,2.98202238 L10.0225923,2.07734807 C9.36950705,1.93153223 8.69641914,1.83808687 8.00132835,1.83808687 C4.64088944,1.83808687 1.68850383,3.6289518 0.000283333333,6.33167913 C0.757382218,7.54338819 1.77751546,8.55691095 2.96667077,9.32295752 L4.73890224,7.50334018 C4.61288579,7.1346931 4.53087507,6.74448171 4.53087507,6.33167913" id="Fill-2"></path>
<path d="M13.0194751,3.34029805 L11.2513649,5.16094226 C11.3770883,5.52958934 11.4589083,5.91980074 11.4589083,6.33157644 C11.4589083,8.30009024 9.90831963,9.89482259 7.99652557,9.89482259 C7.59441023,9.89482259 7.21724,9.81061908 6.85803026,9.68123319 L5.97896421,10.5859075 C6.63152857,10.7317233 7.30305122,10.8261956 7.99652557,10.8261956 C11.348152,10.8261956 14.2936719,9.03430378 15.9789642,6.33157644 C15.223626,5.11986738 14.2048672,4.10634463 13.0194751,3.34029805" id="Fill-4"></path>
<path d="M8.00102831,8.7499629 C9.30219826,8.7499629 10.3553358,7.66763972 10.3553358,6.33167913 C10.3553358,6.25774434 10.341334,6.18689017 10.3343331,6.11398225 L7.78900062,8.72839858 C7.86000989,8.73455982 7.92901891,8.7499629 8.00102831,8.7499629" id="Fill-7"></path>
<path d="M8.00102831,3.9139088 C6.69985837,3.9139088 5.64672082,4.99623198 5.64672082,6.33116569 C5.64672082,6.40612736 5.66072264,6.47698153 5.66772356,6.54886258 L8.213056,3.93547311 C8.14204673,3.92931188 8.07303772,3.9139088 8.00102831,3.9139088" id="Fill-9"></path>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 50.2 (55047) - http://www.bohemiancoding.com/sketch -->
<title>info</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Extension-(mobile-form-factor)" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="info" fill="#FFFFFF">
<path d="M13.2142857,0.642857143 L13.2142857,0.5 L0.5,0.5 L0.5,13.2142857 L0.642857143,13.2142857 L0.642857143,0.642857143 L13.2142857,0.642857143 Z" id="Combined-Shape" stroke="#FFFFFF"></path>
<path d="M3.42857143,3.42857143 L3.42857143,14.8571429 L14.8571429,14.8571429 L14.8571429,3.42857143 L3.42857143,3.42857143 Z M2.28571429,2.28571429 L16,2.28571429 L16,16 L2.28571429,16 L2.28571429,2.28571429 Z" id="Rectangle-18-Copy" fill-rule="nonzero"></path>
<g id="Group-44" transform="translate(8.000000, 5.000000)">
<rect id="Rectangle-39" x="0" y="3" width="2" height="5" rx="1"></rect>
<rect id="Rectangle-39-Copy" x="0" y="0" width="2" height="2" rx="1"></rect>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

@ -1 +1,33 @@
<svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1916.3 516.8" width="2500" height="674"><style>.st0{fill:#333745}</style><g id="squares_1_"><path class="st0" d="M578.2 392.7V24.3h25.6v344.1h175.3v24.3H578.2zm327.5 5.1c-39.7 0-70.4-12.8-93.4-37.1-21.7-24.3-33.3-58.8-33.3-103.6 0-43.5 10.2-79.3 32-104.9 21.7-26.9 49.9-39.7 87-39.7 32 0 57.6 11.5 76.8 33.3 19.2 23 28.1 53.7 28.1 92.1v20.5H804.6c0 37.1 9 66.5 26.9 85.7 16.6 20.5 42.2 29.4 74.2 29.4 15.3 0 29.4-1.3 40.9-3.8 11.5-2.6 26.9-6.4 44.8-14.1v24.3c-15.3 6.4-29.4 11.5-42.2 14.1-14.3 2.6-28.9 3.9-43.5 3.8zM898 135.6c-26.9 0-47.3 9-64 25.6-15.3 17.9-25.6 42.2-28.1 75.5h168.9c0-32-6.4-56.3-20.5-74.2-12.8-18-32-26.9-56.3-26.9zm238-21.8c19.2 0 37.1 3.8 51.2 10.2 14.1 7.7 26.9 19.2 38.4 37.1h1.3c-1.3-21.7-1.3-42.2-1.3-62.7V0h24.3v392.7h-16.6l-6.4-42.2c-20.5 30.7-51.2 47.3-89.6 47.3s-66.5-11.5-87-35.8c-20.5-23-29.4-57.6-29.4-102.3 0-47.3 10.2-83.2 29.4-108.7 19.2-25.6 48.6-37.2 85.7-37.2zm0 21.8c-29.4 0-52.4 10.2-67.8 32-15.3 20.5-23 51.2-23 92.1 0 78 30.7 116.4 90.8 116.4 30.7 0 53.7-9 67.8-26.9 14.1-17.9 21.7-47.3 21.7-89.6v-3.8c0-42.2-7.7-72.9-21.7-90.8-12.8-20.5-35.8-29.4-67.8-29.4zm379.9-16.6v17.9l-56.3 3.8c15.3 19.2 23 39.7 23 61.4 0 26.9-9 47.3-26.9 64-17.9 16.6-40.9 24.3-70.4 24.3-12.8 0-21.7 0-25.6-1.3-10.2 5.1-17.9 11.5-23 17.9-5.1 7.7-7.7 14.1-7.7 23s3.8 15.3 10.2 19.2c6.4 3.8 17.9 6.4 33.3 6.4h47.3c29.4 0 52.4 6.4 67.8 17.9s24.3 29.4 24.3 53.7c0 29.4-11.5 51.2-34.5 66.5-23 15.3-56.3 23-99.8 23-34.5 0-61.4-6.4-80.6-20.5-19.2-12.8-28.1-32-28.1-55 0-19.2 6.4-34.5 17.9-47.3s28.1-20.5 47.3-25.6c-7.7-3.8-15.3-9-19.2-15.3-5-6.2-7.7-13.8-7.7-21.7 0-17.9 11.5-34.5 34.5-48.6-15.3-6.4-28.1-16.6-37.1-30.7-9-14.1-12.8-30.7-12.8-48.6 0-26.9 9-49.9 25.6-66.5 17.9-16.6 40.9-24.3 70.4-24.3 17.9 0 32 1.3 42.2 5.1h85.7v1.3h.2zm-222.6 319.8c0 37.1 28.1 56.3 84.4 56.3 71.6 0 107.5-23 107.5-69.1 0-16.6-5.1-28.1-16.6-35.8-11.5-7.7-29.4-11.5-55-11.5h-44.8c-49.9 1.2-75.5 20.4-75.5 60.1zm21.8-235.4c0 21.7 6.4 37.1 19.2 49.9 12.8 11.5 29.4 17.9 51.2 17.9 23 0 40.9-6.4 52.4-17.9 12.8-11.5 17.9-28.1 17.9-49.9 0-23-6.4-40.9-19.2-52.4-12.8-11.5-29.4-17.9-52.4-17.9-21.7 0-39.7 6.4-51.2 19.2-12.8 11.4-17.9 29.3-17.9 51.1z"/><path class="st0" d="M1640 397.8c-39.7 0-70.4-12.8-93.4-37.1-21.7-24.3-33.3-58.8-33.3-103.6 0-43.5 10.2-79.3 32-104.9 21.7-26.9 49.9-39.7 87-39.7 32 0 57.6 11.5 76.8 33.3 19.2 23 28.1 53.7 28.1 92.1v20.5h-197c0 37.1 9 66.5 26.9 85.7 16.6 20.5 42.2 29.4 74.2 29.4 15.3 0 29.4-1.3 40.9-3.8 11.5-2.6 26.9-6.4 44.8-14.1v24.3c-15.3 6.4-29.4 11.5-42.2 14.1-14.1 2.6-28.2 3.8-44.8 3.8zm-6.4-262.2c-26.9 0-47.3 9-64 25.6-15.3 17.9-25.6 42.2-28.1 75.5h168.9c0-32-6.4-56.3-20.5-74.2-12.8-18-32-26.9-56.3-26.9zm245.6-21.8c11.5 0 24.3 1.3 37.1 3.8l-5.1 24.3c-11.8-2.6-23.8-3.9-35.8-3.8-23 0-42.2 10.2-57.6 29.4-15.3 20.5-23 44.8-23 75.5v149.7h-25.6V119h21.7l2.6 49.9h1.3c11.5-20.5 23-34.5 35.8-42.2 15.4-9 30.7-12.9 48.6-12.9zM333.9 12.8h-183v245.6h245.6V76.7c.1-34.5-28.1-63.9-62.6-63.9zm-239.2 0H64c-34.5 0-64 28.1-64 64v30.7h94.7V12.8zM0 165h94.7v94.7H0V165zm301.9 245.6h30.7c34.5 0 64-28.1 64-64V316h-94.7v94.6zm-151-94.6h94.7v94.7h-94.7V316zM0 316v30.7c0 34.5 28.1 64 64 64h30.7V316H0z"/></g></svg>
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 197.4 48.6" style="enable-background:new 0 0 197.4 48.6;" xml:space="preserve">
<style type="text/css">
.st0{fill:#1D2028;}
</style>
<title>Fichier 8</title>
<g>
<path class="st0" d="M34.1,0H15.5v25.1h25.1V6.5C40.6,2.9,37.7,0,34.1,0z"/>
<path class="st0" d="M9.7,0H6.5C2.9,0,0,2.9,0,6.5v3.2h9.7V0z"/>
<rect y="15.5" class="st0" width="9.7" height="9.7"/>
<path class="st0" d="M31,40.6h3.2c3.6,0,6.5-2.9,6.5-6.5V31H31V40.6z"/>
<rect x="15.5" y="31" class="st0" width="9.7" height="9.7"/>
<path class="st0" d="M0,31v3.2c0,3.6,2.9,6.5,6.5,6.5h3.2V31H0z"/>
<g>
<polygon class="st0" points="65.4,2.6 61.6,2.6 61.6,38.1 81.7,38.1 81.7,34.7 65.4,34.7 "/>
<path class="st0" d="M93.9,12c-7.4,0-12.6,5.5-12.6,13.4c0,0.3,0,0.6,0,0.9c0.1,3.4,1.6,6.6,4.1,9c2.4,2.2,5.5,3.5,8.8,3.5
c0.2,0,0.3,0,0.5,0c3.5,0,6.8-1.3,9.4-3.5l0.1-0.1l-1.7-2.8l-0.2,0.1c-2.1,1.9-4.7,3-7.5,3c-4.6,0-9.3-3-9.6-9.7h19.2v-0.2
c0,0,0.1-1.2,0.1-1.8C104.5,16.6,100.3,12,93.9,12z M85.3,22.6c0.8-4.5,4.1-7.4,8.4-7.4c3.2,0,6.7,1.9,7,7.4H85.3z"/>
<path class="st0" d="M126.5,15c0,0.4,0,0.9,0,1.3c-1.6-2.7-4.6-4.4-7.7-4.4c-0.1,0-0.2,0-0.3,0c-6.8,0-11.5,5.4-11.5,13.3
c0,8,4.5,13.4,11.2,13.4c5.3,0,7.7-3.2,8.5-4.6c0,0.4,0,0.8,0,1.1v2.8h3.6V2.6h-3.7V15H126.5z M118.7,35.3c-4.7,0-7.8-4-7.8-10
c0-5.8,3.3-9.9,7.9-9.9c3.9,0,7.8,3.1,7.8,9.9C126.6,32.7,122.5,35.3,118.7,35.3z"/>
<path class="st0" d="M152.2,15.5c0,0.1,0,0.2,0,0.2c-0.7-1.2-2.9-3.8-8.2-3.8c-6.7,0-11.1,5.1-11.1,12.9s4.6,13.1,11.4,13.1
c3.7,0,6.2-1.3,7.9-4c0,0.4,0,0.8,0,1.2v2.3c0,4.9-3.1,7.7-8.6,7.7c-2.3,0-4.7-0.6-6.8-1.7l-0.2-0.1l-1.4,3.1l0.2,0.1
c2.6,1.3,5.5,2,8.3,2c5.9,0,12.2-3,12.2-11.3V12.6h-3.7L152.2,15.5L152.2,15.5z M144.8,34.6c-4.9,0-8.1-3.8-8.1-9.7
c0-6,2.8-9.4,7.6-9.4c5.3,0,7.8,3.1,7.8,9.4C152.2,31.1,149.6,34.6,144.8,34.6z"/>
<path class="st0" d="M171,12c-7.4,0-12.5,5.5-12.5,13.3c0,0.3,0,0.6,0,0.9c0.1,3.4,1.6,6.6,4.1,9c2.4,2.2,5.5,3.5,8.8,3.5
c0.2,0,0.3,0,0.5,0c3.5,0,6.8-1.3,9.4-3.5l0.1-0.1l-1.8-2.8l-0.2,0.1c-2.1,1.9-4.7,3-7.5,3c-4.6,0-9.3-3-9.6-9.7h19.3v-0.2
c0,0,0.1-1.2,0.1-1.8C181.7,16.6,177.5,12,171,12z M162.5,22.6c0.8-4.5,4.1-7.4,8.4-7.4c3.2,0,6.7,1.9,7,7.4H162.5z"/>
<path class="st0" d="M197.3,12.5c-0.5-0.1-0.9-0.1-1.4-0.2c-3.5,0-6.4,2.2-7.9,5.9c0-0.3,0-0.7,0-1.1v-4.6h-3.7l0.1,25.3V38h3.8
V27.3c0-1.6,0.2-3.3,0.7-4.8c1.2-3.9,3.9-6.4,7.1-6.4c0.4,0,0.8,0,1.2,0.1h0.2v-3.7L197.3,12.5z"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 50.2 (55047) - http://www.bohemiancoding.com/sketch -->
<title>open-etherscan</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Extension-(mobile-form-factor)" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="open-etherscan" fill="#FFFFFF">
<path d="M3.00020312,0 C2.44821175,0 2.00021875,0.447993 2.00021875,0.999984375 C2.00021875,1.55197575 2.44821175,1.99996875 3.00020312,1.99996875 L12.5860533,1.99996875 L0.293245418,14.2927767 C-0.0977484727,14.6837706 -0.0977484727,15.3157607 0.293245418,15.7067546 C0.488242371,15.9017515 0.744238371,15.99975 1.00023437,15.99975 C1.25623037,15.99975 1.51222637,15.9017515 1.70722332,15.7067546 L14.0000312,3.41394666 L14.0000312,12.9997969 C14.0000312,13.5517883 14.4480242,13.9997813 15.0000156,13.9997813 C15.552007,13.9997813 16,13.5517883 16,12.9997969 L16,0 L3.00020312,0 Z"></path>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

@ -1,7 +1,7 @@
{
"name": "__MSG_appName__",
"short_name": "__MSG_appName__",
"version": "4.9.3",
"version": "4.14.0",
"manifest_version": 2,
"author": "https://metamask.io",
"description": "__MSG_appDescription__",

@ -3,7 +3,7 @@
<html>
<head>
<title>Dangerous Website Warning</title>
<title>Ethereum Phishing Detection - MetMask</title>
<style>
body {
@ -24,10 +24,12 @@
a {
color: white;
cursor: pointer;
text-decoration: underline;
}
</style>
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
@ -43,9 +45,9 @@
ga('create', 'UA-68598031-1', 'auto' {'allowLinker':true});
ga('send', 'pageview');
ga('require', 'linker');
ga('linker:autoLink', ['harrydenley.com', 'metamask.io'], false, true);
ga('linker:autoLink', ['metamask.io'], false, true);
</script>
<script src="phishing-detect.js"></script>
</head>
<body>
@ -55,8 +57,13 @@
<h3>ATTENTION</h3>
<p>MetaMask believes this domain could currently compromise your security and has prevented you from interacting with it.</p>
<p>This is because the site tested positive on the <a href="https://github.com/metamask/eth-phishing-detect">Ethereum Phishing Detector</a>. This includes outright malicious websites and legitimate websites that have been compromised by a malicious actor.</p>
<p id="esdbLink"></p>
<p>You can turn MetaMask off to interact with this site, but it is advised not to.</p>
<p>If you think this domain is incorrectly flagged or if a blocked legitimate website has resolved its security issues, <a href="https://github.com/metamask/eth-phishing-detect/issues/new">please file an issue</a>.</p>
<p>
If you think this domain is incorrectly flagged or if a blocked legitimate website has resolved its security issues,
<a href="https://github.com/metamask/eth-phishing-detect/issues/new">please file an issue</a>. If you believe this website
is safe and understand the risks involved, you can <a id="unsafe-continue">visit this unsafe website at your own risk</a>.
</p>
</div>
</body>

@ -38,7 +38,6 @@ const {
const firstTimeState = Object.assign({}, rawFirstTimeState, global.METAMASK_TEST_CONFIG)
const STORAGE_KEY = 'metamask-config'
const METAMASK_DEBUG = process.env.METAMASK_DEBUG
log.setDefaultLevel(process.env.METAMASK_DEBUG ? 'debug' : 'warn')
@ -414,6 +413,7 @@ function setupController (initState, initLangCode) {
controller.txController.on('update:badge', updateBadge)
controller.messageManager.on('updateBadge', updateBadge)
controller.personalMessageManager.on('updateBadge', updateBadge)
controller.typedMessageManager.on('updateBadge', updateBadge)
/**
* Updates the Web Extension's "badge" number, on the little fox in the toolbar.
@ -469,11 +469,3 @@ function showWatchAssetUi () {
}
)
}
// On first install, open a window to MetaMask website to how-it-works.
extension.runtime.onInstalled.addListener(function (details) {
if ((details.reason === 'install') && (!METAMASK_DEBUG)) {
extension.tabs.create({url: 'https://metamask.io/#how-it-works'})
}
})

@ -1,6 +1,7 @@
const fs = require('fs')
const path = require('path')
const pump = require('pump')
const querystring = require('querystring')
const LocalMessageDuplexStream = require('post-message-stream')
const PongStream = require('ping-pong-stream/pong')
const ObjectMultiplex = require('obj-multiplex')
@ -134,17 +135,22 @@ function doctypeCheck () {
}
/**
* Checks the current document extension
* Returns whether or not the extension (suffix) of the current document is prohibited
*
* @returns {boolean} {@code true} if the current extension is not prohibited
* This checks {@code window.location.pathname} against a set of file extensions
* that should not have web3 injected into them. This check is indifferent of query parameters
* in the location.
*
* @returns {boolean} whether or not the extension of the current document is prohibited
*/
function suffixCheck () {
var prohibitedTypes = ['xml', 'pdf']
var currentUrl = window.location.href
var currentRegex
const prohibitedTypes = [
/\.xml$/,
/\.pdf$/,
]
const currentUrl = window.location.pathname
for (let i = 0; i < prohibitedTypes.length; i++) {
currentRegex = new RegExp(`\\.${prohibitedTypes[i]}$`)
if (currentRegex.test(currentUrl)) {
if (prohibitedTypes[i].test(currentUrl)) {
return false
}
}
@ -199,5 +205,8 @@ function blacklistedDomainCheck () {
function redirectToPhishingWarning () {
console.log('MetaMask - routing to Phishing Warning component')
const extensionURL = extension.runtime.getURL('phishing.html')
window.location.href = extensionURL
window.location.href = `${extensionURL}#${querystring.stringify({
hostname: window.location.hostname,
href: window.location.href,
})}`
}

@ -29,6 +29,7 @@ class BlacklistController {
constructor (opts = {}) {
const initState = extend({
phishing: PHISHING_DETECTION_CONFIG,
whitelist: [],
}, opts.initState)
this.store = new ObservableStore(initState)
// phishing detector
@ -38,6 +39,21 @@ class BlacklistController {
this._phishingUpdateIntervalRef = null
}
/**
* Adds the given hostname to the runtime whitelist
* @param {string} hostname the hostname to whitelist
*/
whitelistDomain (hostname) {
if (!hostname) {
return
}
const { whitelist } = this.store.getState()
this.store.updateState({
whitelist: [...new Set([hostname, ...whitelist])],
})
}
/**
* Given a url, returns the result of checking if that url is in the store.phishing blacklist
*
@ -48,6 +64,12 @@ class BlacklistController {
*/
checkForPhishing (hostname) {
if (!hostname) return false
const { whitelist } = this.store.getState()
if (whitelist.some((e) => e === hostname)) {
return false
}
const { result } = this._phishingDetector.check(hostname)
return result
}

@ -1,5 +1,6 @@
const mergeMiddleware = require('json-rpc-engine/src/mergeMiddleware')
const createBlockReEmitMiddleware = require('eth-json-rpc-middleware/block-reemit')
const createBlockReRefMiddleware = require('eth-json-rpc-middleware/block-ref')
const createRetryOnEmptyMiddleware = require('eth-json-rpc-middleware/retryOnEmpty')
const createBlockCacheMiddleware = require('eth-json-rpc-middleware/block-cache')
const createInflightMiddleware = require('eth-json-rpc-middleware/inflight-cache')
const createBlockTrackerInspectorMiddleware = require('eth-json-rpc-middleware/block-tracker-inspector')
@ -11,13 +12,14 @@ module.exports = createInfuraClient
function createInfuraClient ({ network }) {
const infuraMiddleware = createInfuraMiddleware({ network })
const blockProvider = providerFromMiddleware(infuraMiddleware)
const blockTracker = new BlockTracker({ provider: blockProvider })
const infuraProvider = providerFromMiddleware(infuraMiddleware)
const blockTracker = new BlockTracker({ provider: infuraProvider })
const networkMiddleware = mergeMiddleware([
createBlockCacheMiddleware({ blockTracker }),
createInflightMiddleware(),
createBlockReEmitMiddleware({ blockTracker, provider: blockProvider }),
createBlockReRefMiddleware({ blockTracker, provider: infuraProvider }),
createRetryOnEmptyMiddleware({ blockTracker, provider: infuraProvider }),
createBlockTrackerInspectorMiddleware({ blockTracker }),
infuraMiddleware,
])

@ -1,6 +1,6 @@
const mergeMiddleware = require('json-rpc-engine/src/mergeMiddleware')
const createFetchMiddleware = require('eth-json-rpc-middleware/fetch')
const createBlockRefMiddleware = require('eth-json-rpc-middleware/block-ref')
const createBlockRefRewriteMiddleware = require('eth-json-rpc-middleware/block-ref-rewrite')
const createBlockCacheMiddleware = require('eth-json-rpc-middleware/block-cache')
const createInflightMiddleware = require('eth-json-rpc-middleware/inflight-cache')
const createBlockTrackerInspectorMiddleware = require('eth-json-rpc-middleware/block-tracker-inspector')
@ -15,7 +15,7 @@ function createJsonRpcClient ({ rpcUrl }) {
const blockTracker = new BlockTracker({ provider: blockProvider })
const networkMiddleware = mergeMiddleware([
createBlockRefMiddleware({ blockTracker }),
createBlockRefRewriteMiddleware({ blockTracker }),
createBlockCacheMiddleware({ blockTracker }),
createInflightMiddleware(),
createBlockTrackerInspectorMiddleware({ blockTracker }),

@ -1,6 +1,6 @@
const mergeMiddleware = require('json-rpc-engine/src/mergeMiddleware')
const createFetchMiddleware = require('eth-json-rpc-middleware/fetch')
const createBlockRefMiddleware = require('eth-json-rpc-middleware/block-ref')
const createBlockRefRewriteMiddleware = require('eth-json-rpc-middleware/block-ref-rewrite')
const createBlockTrackerInspectorMiddleware = require('eth-json-rpc-middleware/block-tracker-inspector')
const providerFromMiddleware = require('eth-json-rpc-middleware/providerFromMiddleware')
const BlockTracker = require('eth-block-tracker')
@ -13,7 +13,7 @@ function createLocalhostClient () {
const blockTracker = new BlockTracker({ provider: blockProvider, pollingInterval: 1000 })
const networkMiddleware = mergeMiddleware([
createBlockRefMiddleware({ blockTracker }),
createBlockRefRewriteMiddleware({ blockTracker }),
createBlockTrackerInspectorMiddleware({ blockTracker }),
fetchMiddleware,
])

@ -38,6 +38,6 @@ function createPendingNonceMiddleware ({ getPendingNonce }) {
const address = req.params[0]
const blockRef = req.params[1]
if (blockRef !== 'pending') return next()
req.result = await getPendingNonce(address)
res.result = await getPendingNonce(address)
})
}

@ -36,6 +36,8 @@ class PreferencesController {
currentLocale: opts.initLangCode,
identities: {},
lostIdentities: {},
seedWords: null,
forgottenPassword: false,
}, opts.initState)
this.diagnostics = opts.diagnostics
@ -46,6 +48,22 @@ class PreferencesController {
}
// PUBLIC METHODS
/**
* Sets the {@code forgottenPassword} state property
* @param {boolean} forgottenPassword whether or not the user has forgotten their password
*/
setPasswordForgotten (forgottenPassword) {
this.store.updateState({ forgottenPassword })
}
/**
* Sets the {@code seedWords} seed words
* @param {string|null} seedWords the seed words
*/
setSeedWords (seedWords) {
this.store.updateState({ seedWords })
}
/**
* Setter for the `useBlockie` property
*
@ -357,11 +375,12 @@ class PreferencesController {
* Gets an updated rpc list from this.addToFrequentRpcList() and sets the `frequentRpcList` to this update list.
*
* @param {string} _url The the new rpc url to add to the updated list
* @param {bool} remove Remove selected url
* @returns {Promise<void>} Promise resolves with undefined
*
*/
updateFrequentRpcList (_url) {
return this.addToFrequentRpcList(_url)
updateFrequentRpcList (_url, remove = false) {
return this.addToFrequentRpcList(_url, remove)
.then((rpcList) => {
this.store.updateState({ frequentRpcList: rpcList })
return Promise.resolve()
@ -388,21 +407,19 @@ class PreferencesController {
* end of the list. The current list is modified and returned as a promise.
*
* @param {string} _url The rpc url to add to the frequentRpcList.
* @param {bool} remove Remove selected url
* @returns {Promise<array>} The updated frequentRpcList.
*
*/
addToFrequentRpcList (_url) {
addToFrequentRpcList (_url, remove = false) {
const rpcList = this.getFrequentRpcList()
const index = rpcList.findIndex((element) => { return element === _url })
if (index !== -1) {
rpcList.splice(index, 1)
}
if (_url !== 'http://localhost:8545') {
if (!remove && _url !== 'http://localhost:8545') {
rpcList.push(_url)
}
if (rpcList.length > 3) {
rpcList.shift()
}
return Promise.resolve(rpcList)
}

@ -0,0 +1,12 @@
const TRANSACTION_TYPE_CANCEL = 'cancel'
const TRANSACTION_TYPE_RETRY = 'retry'
const TRANSACTION_TYPE_STANDARD = 'standard'
const TRANSACTION_STATUS_APPROVED = 'approved'
module.exports = {
TRANSACTION_TYPE_CANCEL,
TRANSACTION_TYPE_RETRY,
TRANSACTION_TYPE_STANDARD,
TRANSACTION_STATUS_APPROVED,
}

@ -11,6 +11,14 @@ const txUtils = require('./lib/util')
const cleanErrorStack = require('../../lib/cleanErrorStack')
const log = require('loglevel')
const recipientBlacklistChecker = require('./lib/recipient-blacklist-checker')
const {
TRANSACTION_TYPE_CANCEL,
TRANSACTION_TYPE_RETRY,
TRANSACTION_TYPE_STANDARD,
TRANSACTION_STATUS_APPROVED,
} = require('./enums')
const { hexToBn, bnToHex, BnMultiplyByFraction } = require('../../lib/util')
/**
Transaction Controller is an aggregate of sub-controllers and trackers
@ -158,9 +166,16 @@ class TransactionController extends EventEmitter {
async addUnapprovedTransaction (txParams) {
// validate
const normalizedTxParams = txUtils.normalizeTxParams(txParams)
// Assert the from address is the selected address
if (normalizedTxParams.from !== this.getSelectedAddress()) {
throw new Error(`Transaction from address isn't valid for this account`)
}
txUtils.validateTxParams(normalizedTxParams)
// construct txMeta
let txMeta = this.txStateManager.generateTxMeta({ txParams: normalizedTxParams })
let txMeta = this.txStateManager.generateTxMeta({
txParams: normalizedTxParams,
type: TRANSACTION_TYPE_STANDARD,
})
this.addTx(txMeta)
this.emit('newUnapprovedTx', txMeta)
@ -214,12 +229,47 @@ class TransactionController extends EventEmitter {
txParams: originalTxMeta.txParams,
lastGasPrice,
loadingDefaults: false,
type: TRANSACTION_TYPE_RETRY,
})
this.addTx(txMeta)
this.emit('newUnapprovedTx', txMeta)
return txMeta
}
/**
* Creates a new approved transaction to attempt to cancel a previously submitted transaction. The
* new transaction contains the same nonce as the previous, is a basic ETH transfer of 0x value to
* the sender's address, and has a higher gasPrice than that of the previous transaction.
* @param {number} originalTxId - the id of the txMeta that you want to attempt to cancel
* @param {string=} customGasPrice - the hex value to use for the cancel transaction
* @returns {txMeta}
*/
async createCancelTransaction (originalTxId, customGasPrice) {
const originalTxMeta = this.txStateManager.getTx(originalTxId)
const { txParams } = originalTxMeta
const { gasPrice: lastGasPrice, from, nonce } = txParams
const newGasPrice = customGasPrice || bnToHex(BnMultiplyByFraction(hexToBn(lastGasPrice), 11, 10))
const newTxMeta = this.txStateManager.generateTxMeta({
txParams: {
from,
to: from,
nonce,
gas: '0x5208',
value: '0x0',
gasPrice: newGasPrice,
},
lastGasPrice,
loadingDefaults: false,
status: TRANSACTION_STATUS_APPROVED,
type: TRANSACTION_TYPE_CANCEL,
})
this.addTx(newTxMeta)
await this.approveTransaction(newTxMeta.id)
return newTxMeta
}
/**
updates the txMeta in the txStateManager
@param txMeta {Object} - the updated txMeta
@ -316,7 +366,40 @@ class TransactionController extends EventEmitter {
this.txStateManager.setTxStatusSubmitted(txId)
}
confirmTransaction (txId) {
/**
* Sets the status of the transaction to confirmed and sets the status of nonce duplicates as
* dropped if the txParams have data it will fetch the txReceipt
* @param {number} txId - The tx's ID
* @returns {Promise<void>}
*/
async confirmTransaction (txId) {
// get the txReceipt before marking the transaction confirmed
// to ensure the receipt is gotten before the ui revives the tx
const txMeta = this.txStateManager.getTx(txId)
if (!txMeta) {
return
}
try {
const txReceipt = await this.query.getTransactionReceipt(txMeta.hash)
// It seems that sometimes the numerical values being returned from
// this.query.getTransactionReceipt are BN instances and not strings.
const gasUsed = typeof txReceipt.gasUsed !== 'string'
? txReceipt.gasUsed.toString(16)
: txReceipt.gasUsed
txMeta.txReceipt = {
...txReceipt,
gasUsed,
}
this.txStateManager.updateTx(txMeta, 'transactions#confirmTransaction - add txReceipt')
} catch (err) {
log.error(err)
}
this.txStateManager.setTxStatusConfirmed(txId)
this._markNonceDuplicatesDropped(txId)
}
@ -393,7 +476,7 @@ class TransactionController extends EventEmitter {
})
this.txStateManager.getFilteredTxList({
status: 'approved',
status: TRANSACTION_STATUS_APPROVED,
}).forEach((txMeta) => {
const txSignError = new Error('Transaction found as "approved" during boot - possibly stuck during signing')
this.txStateManager.setTxStatusFailed(txMeta.id, txSignError)
@ -484,6 +567,7 @@ class TransactionController extends EventEmitter {
Updates the memStore in transaction controller
*/
_updateMemstore () {
this.pendingTxTracker.updatePendingTxs()
const unapprovedTxs = this.txStateManager.getUnapprovedTxList()
const selectedAddressTxList = this.txStateManager.getFilteredTxList({
from: this.getSelectedAddress(),

@ -353,6 +353,7 @@ class TransactionStateManager extends EventEmitter {
const txMeta = this.getTx(txId)
txMeta.err = {
message: err.toString(),
rpc: err.value,
stack: err.stack,
}
this.updateTx(txMeta)
@ -399,6 +400,11 @@ class TransactionStateManager extends EventEmitter {
*/
_setTxStatus (txId, status) {
const txMeta = this.getTx(txId)
if (!txMeta) {
return
}
txMeta.status = status
setTimeout(() => {
try {

@ -5,10 +5,16 @@ const log = require('loglevel')
const LocalMessageDuplexStream = require('post-message-stream')
const setupDappAutoReload = require('./lib/auto-reload.js')
const MetamaskInpageProvider = require('metamask-inpage-provider')
restoreContextAfterImports()
log.setDefaultLevel(process.env.METAMASK_DEBUG ? 'debug' : 'warn')
console.warn('ATTENTION: In an effort to improve user privacy, MetaMask will ' +
'stop exposing user accounts to dapps by default beginning November 2nd, 2018. ' +
'Dapps should call provider.enable() in order to view and use accounts. Please see ' +
'https://bit.ly/2QQHXvF for complete information and up-to-date example code.')
//
// setup plugin communication
//
@ -22,6 +28,33 @@ var metamaskStream = new LocalMessageDuplexStream({
// compose the inpage provider
var inpageProvider = new MetamaskInpageProvider(metamaskStream)
// Augment the provider with its enable method
inpageProvider.enable = function (options = {}) {
return new Promise((resolve, reject) => {
if (options.mockRejection) {
reject('User rejected account access')
} else {
inpageProvider.sendAsync({ method: 'eth_accounts', params: [] }, (error, response) => {
if (error) {
reject(error)
} else {
resolve(response.result)
}
})
}
})
}
// Work around for web3@1.0 deleting the bound `sendAsync` but not the unbound
// `sendAsync` method on the prototype, causing `this` reference issues with drizzle
const proxiedInpageProvider = new Proxy(inpageProvider, {
// straight up lie that we deleted the property so that it doesnt
// throw an error in strict mode
deleteProperty: () => true,
})
window.ethereum = proxiedInpageProvider
//
// setup web3
//
@ -33,7 +66,8 @@ if (typeof window.web3 !== 'undefined') {
or MetaMask and another web3 extension. Please remove one
and try again.`)
}
var web3 = new Web3(inpageProvider)
var web3 = new Web3(proxiedInpageProvider)
web3.setProvider = function () {
log.debug('MetaMask - overrode web3.setProvider')
}

@ -45,6 +45,9 @@ class AccountTracker {
this._blockTracker = opts.blockTracker
// blockTracker.currentBlock may be null
this._currentBlockNumber = this._blockTracker.getCurrentBlock()
this._blockTracker.once('latest', blockNumber => {
this._currentBlockNumber = blockNumber
})
// bind function for easier listener syntax
this._updateForBlock = this._updateForBlock.bind(this)
}

@ -2,18 +2,12 @@ module.exports = setupDappAutoReload
function setupDappAutoReload (web3, observable) {
// export web3 as a global, checking for usage
let hasBeenWarned = false
let reloadInProgress = false
let lastTimeUsed
let lastSeenNetwork
global.web3 = new Proxy(web3, {
get: (_web3, key) => {
// show warning once on web3 access
if (!hasBeenWarned && key !== 'currentProvider') {
console.warn('MetaMask: web3 will be deprecated in the near future in favor of the ethereumProvider \nhttps://github.com/MetaMask/faq/blob/master/detecting_metamask.md#web3-deprecation')
hasBeenWarned = true
}
// get the time of use
lastTimeUsed = Date.now()
// return value normally

@ -1,254 +0,0 @@
const ethUtil = require('ethereumjs-util')
const normalize = require('eth-sig-util').normalize
const {
MAINNET_RPC_URL,
ROPSTEN_RPC_URL,
KOVAN_RPC_URL,
RINKEBY_RPC_URL,
} = require('../controllers/network/enums')
/* The config-manager is a convenience object
* wrapping a pojo-migrator.
*
* It exists mostly to allow the creation of
* convenience methods to access and persist
* particular portions of the state.
*/
module.exports = ConfigManager
function ConfigManager (opts) {
// ConfigManager is observable and will emit updates
this._subs = []
this.store = opts.store
}
ConfigManager.prototype.setConfig = function (config) {
var data = this.getData()
data.config = config
this.setData(data)
this._emitUpdates(config)
}
ConfigManager.prototype.getConfig = function () {
var data = this.getData()
return data.config
}
ConfigManager.prototype.setData = function (data) {
this.store.putState(data)
}
ConfigManager.prototype.getData = function () {
return this.store.getState()
}
ConfigManager.prototype.setPasswordForgotten = function (passwordForgottenState) {
const data = this.getData()
data.forgottenPassword = passwordForgottenState
this.setData(data)
}
ConfigManager.prototype.getPasswordForgotten = function (passwordForgottenState) {
const data = this.getData()
return data.forgottenPassword
}
ConfigManager.prototype.setWallet = function (wallet) {
var data = this.getData()
data.wallet = wallet
this.setData(data)
}
ConfigManager.prototype.setVault = function (encryptedString) {
var data = this.getData()
data.vault = encryptedString
this.setData(data)
}
ConfigManager.prototype.getVault = function () {
var data = this.getData()
return data.vault
}
ConfigManager.prototype.getKeychains = function () {
return this.getData().keychains || []
}
ConfigManager.prototype.setKeychains = function (keychains) {
var data = this.getData()
data.keychains = keychains
this.setData(data)
}
ConfigManager.prototype.getSelectedAccount = function () {
var config = this.getConfig()
return config.selectedAccount
}
ConfigManager.prototype.setSelectedAccount = function (address) {
var config = this.getConfig()
config.selectedAccount = ethUtil.addHexPrefix(address)
this.setConfig(config)
}
ConfigManager.prototype.getWallet = function () {
return this.getData().wallet
}
// Takes a boolean
ConfigManager.prototype.setShowSeedWords = function (should) {
var data = this.getData()
data.showSeedWords = should
this.setData(data)
}
ConfigManager.prototype.getShouldShowSeedWords = function () {
var data = this.getData()
return data.showSeedWords
}
ConfigManager.prototype.setSeedWords = function (words) {
var data = this.getData()
data.seedWords = words
this.setData(data)
}
ConfigManager.prototype.getSeedWords = function () {
var data = this.getData()
return data.seedWords
}
ConfigManager.prototype.setRpcTarget = function (rpcUrl) {
var config = this.getConfig()
config.provider = {
type: 'rpc',
rpcTarget: rpcUrl,
}
this.setConfig(config)
}
ConfigManager.prototype.setProviderType = function (type) {
var config = this.getConfig()
config.provider = {
type: type,
}
this.setConfig(config)
}
ConfigManager.prototype.useEtherscanProvider = function () {
var config = this.getConfig()
config.provider = {
type: 'etherscan',
}
this.setConfig(config)
}
ConfigManager.prototype.getProvider = function () {
var config = this.getConfig()
return config.provider
}
ConfigManager.prototype.getCurrentRpcAddress = function () {
var provider = this.getProvider()
if (!provider) return null
switch (provider.type) {
case 'mainnet':
return MAINNET_RPC_URL
case 'ropsten':
return ROPSTEN_RPC_URL
case 'kovan':
return KOVAN_RPC_URL
case 'rinkeby':
return RINKEBY_RPC_URL
default:
return provider && provider.rpcTarget ? provider.rpcTarget : RINKEBY_RPC_URL
}
}
//
// Tx
//
ConfigManager.prototype.getTxList = function () {
var data = this.getData()
if (data.transactions !== undefined) {
return data.transactions
} else {
return []
}
}
ConfigManager.prototype.setTxList = function (txList) {
var data = this.getData()
data.transactions = txList
this.setData(data)
}
// wallet nickname methods
ConfigManager.prototype.getWalletNicknames = function () {
var data = this.getData()
const nicknames = ('walletNicknames' in data) ? data.walletNicknames : {}
return nicknames
}
ConfigManager.prototype.nicknameForWallet = function (account) {
const address = normalize(account)
const nicknames = this.getWalletNicknames()
return nicknames[address]
}
ConfigManager.prototype.setNicknameForWallet = function (account, nickname) {
const address = normalize(account)
const nicknames = this.getWalletNicknames()
nicknames[address] = nickname
var data = this.getData()
data.walletNicknames = nicknames
this.setData(data)
}
// observable
ConfigManager.prototype.getSalt = function () {
var data = this.getData()
return data.salt
}
ConfigManager.prototype.setSalt = function (salt) {
var data = this.getData()
data.salt = salt
this.setData(data)
}
ConfigManager.prototype.subscribe = function (fn) {
this._subs.push(fn)
var unsubscribe = this.unsubscribe.bind(this, fn)
return unsubscribe
}
ConfigManager.prototype.unsubscribe = function (fn) {
var index = this._subs.indexOf(fn)
if (index !== -1) this._subs.splice(index, 1)
}
ConfigManager.prototype._emitUpdates = function (state) {
this._subs.forEach(function (handler) {
handler(state)
})
}
ConfigManager.prototype.setLostAccounts = function (lostAccounts) {
var data = this.getData()
data.lostAccounts = lostAccounts
this.setData(data)
}
ConfigManager.prototype.getLostAccounts = function () {
var data = this.getData()
return data.lostAccounts || []
}

@ -5,6 +5,8 @@ module.exports = function (provider) {
function ipfsContent (details) {
const name = details.url.substring(7, details.url.length - 1)
let clearTime = null
if (/^.+\.eth$/.test(name) === false) return
extension.tabs.query({active: true}, tab => {
extension.tabs.update(tab.id, { url: 'loading.html' })
@ -34,7 +36,7 @@ module.exports = function (provider) {
return { cancel: true }
}
extension.webRequest.onErrorOccurred.addListener(ipfsContent, {urls: ['*://*.eth/']})
extension.webRequest.onErrorOccurred.addListener(ipfsContent, {urls: ['*://*.eth/'], types: ['main_frame']})
return {
remove () {

@ -272,6 +272,6 @@ function normalizeMsgData (data) {
return data
} else {
// data is unicode, convert to hex
return ethUtil.bufferToHex(new Buffer(data, 'utf8'))
return ethUtil.bufferToHex(Buffer.from(data, 'utf8'))
}
}

@ -285,7 +285,7 @@ module.exports = class PersonalMessageManager extends EventEmitter {
log.debug(`Message was not hex encoded, interpreting as utf8.`)
}
return ethUtil.bufferToHex(new Buffer(data, 'utf8'))
return ethUtil.bufferToHex(Buffer.from(data, 'utf8'))
}
}

@ -4,6 +4,7 @@ const createId = require('./random-id')
const assert = require('assert')
const sigUtil = require('eth-sig-util')
const log = require('loglevel')
const jsonschema = require('jsonschema')
/**
* Represents, and contains data about, an 'eth_signTypedData' type signature request. These are created when a
@ -17,7 +18,7 @@ const log = require('loglevel')
* @property {Object} msgParams.from The address that is making the signature request.
* @property {string} msgParams.data A hex string conversion of the raw buffer data of the signature request
* @property {number} time The epoch time at which the this message was created
* @property {string} status Indicates whether the signature request is 'unapproved', 'approved', 'signed' or 'rejected'
* @property {string} status Indicates whether the signature request is 'unapproved', 'approved', 'signed', 'rejected', or 'errored'
* @property {string} type The json-prc signing method for which a signature request has been made. A 'Message' will
* always have a 'eth_signTypedData' type.
*
@ -26,17 +27,10 @@ const log = require('loglevel')
module.exports = class TypedMessageManager extends EventEmitter {
/**
* Controller in charge of managing - storing, adding, removing, updating - TypedMessage.
*
* @typedef {Object} TypedMessage
* @param {Object} opts @deprecated
* @property {Object} memStore The observable store where TypedMessage are saved.
* @property {Object} memStore.unapprovedTypedMessages A collection of all TypedMessages in the 'unapproved' state
* @property {number} memStore.unapprovedTypedMessagesCount The count of all TypedMessages in this.memStore.unapprobedMsgs
* @property {array} messages Holds all messages that have been created by this TypedMessage
*
*/
constructor (opts) {
constructor ({ networkController }) {
super()
this.networkController = networkController
this.memStore = new ObservableStore({
unapprovedTypedMessages: {},
unapprovedTypedMessagesCount: 0,
@ -76,15 +70,17 @@ module.exports = class TypedMessageManager extends EventEmitter {
* @returns {promise} When the message has been signed or rejected
*
*/
addUnapprovedMessageAsync (msgParams, req) {
addUnapprovedMessageAsync (msgParams, req, version) {
return new Promise((resolve, reject) => {
const msgId = this.addUnapprovedMessage(msgParams, req)
const msgId = this.addUnapprovedMessage(msgParams, req, version)
this.once(`${msgId}:finished`, (data) => {
switch (data.status) {
case 'signed':
return resolve(data.rawSig)
case 'rejected':
return reject(new Error('MetaMask Message Signature: User denied message signature.'))
case 'errored':
return reject(new Error(`MetaMask Message Signature: ${data.error}`))
default:
return reject(new Error(`MetaMask Message Signature: Unknown problem: ${JSON.stringify(msgParams)}`))
}
@ -102,7 +98,8 @@ module.exports = class TypedMessageManager extends EventEmitter {
* @returns {number} The id of the newly created TypedMessage.
*
*/
addUnapprovedMessage (msgParams, req) {
addUnapprovedMessage (msgParams, req, version) {
msgParams.version = version
this.validateParams(msgParams)
// add origin from request
if (req) msgParams.origin = req.origin
@ -132,6 +129,8 @@ module.exports = class TypedMessageManager extends EventEmitter {
*
*/
validateParams (params) {
switch (params.version) {
case 'V1':
assert.equal(typeof params, 'object', 'Params should ben an object.')
assert.ok('data' in params, 'Params must include a data field.')
assert.ok('from' in params, 'Params must include a from field.')
@ -140,6 +139,23 @@ module.exports = class TypedMessageManager extends EventEmitter {
assert.doesNotThrow(() => {
sigUtil.typedSignatureHash(params.data)
}, 'Expected EIP712 typed data')
break
case 'V3':
let data
assert.equal(typeof params, 'object', 'Params should be an object.')
assert.ok('data' in params, 'Params must include a data field.')
assert.ok('from' in params, 'Params must include a from field.')
assert.equal(typeof params.from, 'string', 'From field must be a string.')
assert.equal(typeof params.data, 'string', 'Data must be passed as a valid JSON string.')
assert.doesNotThrow(() => { data = JSON.parse(params.data) }, 'Data must be passed as a valid JSON string.')
const validation = jsonschema.validate(data, sigUtil.TYPED_MESSAGE_SCHEMA)
assert.ok(data.primaryType in data.types, `Primary type of "${data.primaryType}" has no type definition.`)
assert.equal(validation.errors.length, 0, 'Data must conform to EIP-712 schema. See https://git.io/fNtcx.')
const chainId = data.domain.chainId
const activeChainId = parseInt(this.networkController.getNetworkState())
chainId && assert.equal(chainId, activeChainId, `Provided chainId (${chainId}) must match the active chainId (${activeChainId})`)
break
}
}
/**
@ -214,6 +230,7 @@ module.exports = class TypedMessageManager extends EventEmitter {
*/
prepMsgForSigning (msgParams) {
delete msgParams.metamaskId
delete msgParams.version
return Promise.resolve(msgParams)
}
@ -227,6 +244,19 @@ module.exports = class TypedMessageManager extends EventEmitter {
this._setMsgStatus(msgId, 'rejected')
}
/**
* Sets a TypedMessage status to 'errored' via a call to this._setMsgStatus.
*
* @param {number} msgId The id of the TypedMessage to error
*
*/
errorMessage (msgId, error) {
const msg = this.getMsg(msgId)
msg.error = error
this._updateMsg(msg)
this._setMsgStatus(msgId, 'errored')
}
//
// PRIVATE METHODS
//
@ -250,7 +280,7 @@ module.exports = class TypedMessageManager extends EventEmitter {
msg.status = status
this._updateMsg(msg)
this.emit(`${msgId}:${status}`, msg)
if (status === 'rejected' || status === 'signed') {
if (status === 'rejected' || status === 'signed' || status === 'errored') {
this.emit(`${msgId}:finished`, msg)
}
}

@ -15,6 +15,7 @@ const RpcEngine = require('json-rpc-engine')
const debounce = require('debounce')
const createEngineStream = require('json-rpc-middleware-stream/engineStream')
const createFilterMiddleware = require('eth-json-rpc-filters')
const createSubscriptionManager = require('eth-json-rpc-filters/subscriptionManager')
const createOriginMiddleware = require('./lib/createOriginMiddleware')
const createLoggerMiddleware = require('./lib/createLoggerMiddleware')
const createProviderMiddleware = require('./lib/createProviderMiddleware')
@ -36,7 +37,6 @@ const TransactionController = require('./controllers/transactions')
const BalancesController = require('./controllers/computed-balances')
const TokenRatesController = require('./controllers/token-rates')
const DetectTokensController = require('./controllers/detect-tokens')
const ConfigManager = require('./lib/config-manager')
const nodeify = require('./lib/nodeify')
const accountImporter = require('./account-import-strategies')
const getBuyEthUrl = require('./lib/buy-eth-url')
@ -50,6 +50,8 @@ const log = require('loglevel')
const TrezorKeyring = require('eth-trezor-keyring')
const LedgerBridgeKeyring = require('eth-ledger-bridge-keyring')
const EthQuery = require('eth-query')
const ethUtil = require('ethereumjs-util')
const sigUtil = require('eth-sig-util')
module.exports = class MetamaskController extends EventEmitter {
@ -83,11 +85,6 @@ module.exports = class MetamaskController extends EventEmitter {
// network store
this.networkController = new NetworkController(initState.NetworkController)
// config manager
this.configManager = new ConfigManager({
store: this.store,
})
// preferences controller
this.preferencesController = new PreferencesController({
initState: initState.PreferencesController,
@ -177,7 +174,7 @@ module.exports = class MetamaskController extends EventEmitter {
blockTracker: this.blockTracker,
getGasPrice: this.getGasPrice.bind(this),
})
this.txController.on('newUnapprovedTx', opts.showUnapprovedTx.bind(opts))
this.txController.on('newUnapprovedTx', () => opts.showUnapprovedTx())
this.txController.on(`tx:status-update`, (txId, status) => {
if (status === 'confirmed' || status === 'failed') {
@ -211,7 +208,7 @@ module.exports = class MetamaskController extends EventEmitter {
this.networkController.lookupNetwork()
this.messageManager = new MessageManager()
this.personalMessageManager = new PersonalMessageManager()
this.typedMessageManager = new TypedMessageManager()
this.typedMessageManager = new TypedMessageManager({ networkController: this.networkController })
this.publicConfigStore = this.initPublicConfigStore()
this.store.updateStructure({
@ -256,6 +253,7 @@ module.exports = class MetamaskController extends EventEmitter {
eth_syncing: false,
web3_clientVersion: `MetaMask/v${version}`,
},
version,
// account mgmt
getAccounts: async () => {
const isUnlocked = this.keyringController.memStore.getState().isUnlocked
@ -272,7 +270,7 @@ module.exports = class MetamaskController extends EventEmitter {
// msg signing
processEthSignMessage: this.newUnsignedMessage.bind(this),
processPersonalMessage: this.newUnsignedPersonalMessage.bind(this),
processTypedMessage: this.newUnsignedTypedMessage.bind(this),
getPendingNonce: this.getPendingNonce.bind(this),
}
const providerProxy = this.networkController.initializeProvider(providerOpts)
return providerProxy
@ -314,18 +312,15 @@ module.exports = class MetamaskController extends EventEmitter {
* @returns {Object} status
*/
getState () {
const wallet = this.configManager.getWallet()
const vault = this.keyringController.store.getState().vault
const isInitialized = (!!wallet || !!vault)
const isInitialized = !!vault
return {
...{ isInitialized },
...this.memStore.getFlatState(),
...this.configManager.getConfig(),
...{
lostAccounts: this.configManager.getLostAccounts(),
seedWords: this.configManager.getSeedWords(),
forgottenPassword: this.configManager.getPasswordForgotten(),
// TODO: Remove usages of lost accounts
lostAccounts: [],
},
}
}
@ -382,6 +377,7 @@ module.exports = class MetamaskController extends EventEmitter {
// network management
setProviderType: nodeify(networkController.setProviderType, networkController),
setCustomRpc: nodeify(this.setCustomRpc, this),
delCustomRpc: nodeify(this.delCustomRpc, this),
// PreferencesController
setSelectedAddress: nodeify(preferencesController.setSelectedAddress, preferencesController),
@ -392,6 +388,9 @@ module.exports = class MetamaskController extends EventEmitter {
setAccountLabel: nodeify(preferencesController.setAccountLabel, preferencesController),
setFeatureFlag: nodeify(preferencesController.setFeatureFlag, preferencesController),
// BlacklistController
whitelistPhishingDomain: this.whitelistPhishingDomain.bind(this),
// AddressController
setAddressBook: nodeify(addressBookController.setAddressBook, addressBookController),
@ -407,6 +406,7 @@ module.exports = class MetamaskController extends EventEmitter {
updateTransaction: nodeify(txController.updateTransaction, txController),
updateAndApproveTransaction: nodeify(txController.updateAndApproveTransaction, txController),
retryTransaction: nodeify(this.retryTransaction, this),
createCancelTransaction: nodeify(this.createCancelTransaction, this),
getFilteredTxList: nodeify(txController.getFilteredTxList, txController),
isNonceTaken: nodeify(txController.isNonceTaken, txController),
estimateGas: nodeify(this.estimateGas, this),
@ -556,6 +556,8 @@ module.exports = class MetamaskController extends EventEmitter {
}
await this.preferencesController.syncAddresses(accounts)
await this.balancesController.updateAllBalances()
await this.txController.pendingTxTracker.updatePendingTxs()
return this.keyringController.fullUpdate()
}
@ -726,7 +728,7 @@ module.exports = class MetamaskController extends EventEmitter {
this.verifySeedPhrase()
.then((seedWords) => {
this.configManager.setSeedWords(seedWords)
this.preferencesController.setSeedWords(seedWords)
return cb(null, seedWords)
})
.catch((err) => {
@ -775,7 +777,7 @@ module.exports = class MetamaskController extends EventEmitter {
* @param {function} cb Callback function called with the current address.
*/
clearSeedWordCache (cb) {
this.configManager.setSeedWords(null)
this.preferencesController.setSeedWords(null)
cb(null, this.preferencesController.getSelectedAddress())
}
@ -983,22 +985,31 @@ module.exports = class MetamaskController extends EventEmitter {
* @param {Object} msgParams - The params passed to eth_signTypedData.
* @returns {Object} Full state update.
*/
signTypedMessage (msgParams) {
log.info('MetaMaskController - signTypedMessage')
async signTypedMessage (msgParams) {
log.info('MetaMaskController - eth_signTypedData')
const msgId = msgParams.metamaskId
// sets the status op the message to 'approved'
// and removes the metamaskId for signing
return this.typedMessageManager.approveMessage(msgParams)
.then((cleanMsgParams) => {
// signs the message
return this.keyringController.signTypedMessage(cleanMsgParams)
})
.then((rawSig) => {
// tells the listener that the message has been signed
// and can be returned to the dapp
this.typedMessageManager.setMsgStatusSigned(msgId, rawSig)
const version = msgParams.version
try {
const cleanMsgParams = await this.typedMessageManager.approveMessage(msgParams)
const address = sigUtil.normalize(cleanMsgParams.from)
const keyring = await this.keyringController.getKeyringForAccount(address)
const wallet = keyring._getWalletForAccount(address)
const privKey = ethUtil.toBuffer(wallet.getPrivateKey())
let signature
switch (version) {
case 'V1':
signature = sigUtil.signTypedDataLegacy(privKey, { data: cleanMsgParams.data })
break
case 'V3':
signature = sigUtil.signTypedData(privKey, { data: JSON.parse(cleanMsgParams.data) })
break
}
this.typedMessageManager.setMsgStatusSigned(msgId, signature)
return this.getState()
})
} catch (error) {
log.info('MetaMaskController - eth_signTypedData failed.', error)
this.typedMessageManager.errorMessage(msgId, error)
}
}
/**
@ -1036,35 +1047,16 @@ module.exports = class MetamaskController extends EventEmitter {
/**
* A legacy method used to record user confirmation that they understand
* that some of their accounts have been recovered but should be backed up.
* This function no longer does anything and will be removed.
*
* @deprecated
* @param {Function} cb - A callback function called with a full state update.
*/
markAccountsFound (cb) {
this.configManager.setLostAccounts([])
this.sendUpdate()
// TODO Remove me
cb(null, this.getState())
}
/**
* A legacy method (probably dead code) that was used when we swapped out our
* key management library that we depended on.
*
* Described in:
* https://medium.com/metamask/metamask-3-migration-guide-914b79533cdd
*
* @deprecated
* @param {} migratorOutput
*/
restoreOldLostAccounts (migratorOutput) {
const { lostAccounts } = migratorOutput
if (lostAccounts) {
this.configManager.setLostAccounts(lostAccounts.map(acct => acct.address))
return this.importLostAccounts(migratorOutput)
}
return Promise.resolve(migratorOutput)
}
/**
* An account object
* @typedef Account
@ -1109,6 +1101,19 @@ module.exports = class MetamaskController extends EventEmitter {
return state
}
/**
* Allows a user to attempt to cancel a previously submitted transaction by creating a new
* transaction.
* @param {number} originalTxId - the id of the txMeta that you want to attempt to cancel
* @param {string=} customGasPrice - the hex value to use for the cancel transaction
* @returns {object} MetaMask state
*/
async createCancelTransaction (originalTxId, customGasPrice, cb) {
await this.txController.createCancelTransaction(originalTxId, customGasPrice)
const state = await this.getState()
return state
}
estimateGas (estimateGasParams) {
return new Promise((resolve, reject) => {
return this.txController.txGasUtil.query.estimateGas(estimateGasParams, (err, res) => {
@ -1130,7 +1135,7 @@ module.exports = class MetamaskController extends EventEmitter {
* @param {Function} cb - A callback function called when complete.
*/
markPasswordForgotten (cb) {
this.configManager.setPasswordForgotten(true)
this.preferencesController.setPasswordForgotten(true)
this.sendUpdate()
cb()
}
@ -1140,7 +1145,7 @@ module.exports = class MetamaskController extends EventEmitter {
* @param {Function} cb - A callback function called when complete.
*/
unMarkPasswordForgotten (cb) {
this.configManager.setPasswordForgotten(false)
this.preferencesController.setPasswordForgotten(false)
this.sendUpdate()
cb()
}
@ -1229,8 +1234,10 @@ module.exports = class MetamaskController extends EventEmitter {
)
dnode.on('remote', (remote) => {
// push updates to popup
const sendUpdate = remote.sendUpdate.bind(remote)
const sendUpdate = (update) => remote.sendUpdate(update)
this.on('update', sendUpdate)
// remove update listener once the connection ends
dnode.on('end', () => this.removeListener('update', sendUpdate))
})
}
@ -1242,21 +1249,33 @@ module.exports = class MetamaskController extends EventEmitter {
setupProviderConnection (outStream, origin) {
// setup json rpc engine stack
const engine = new RpcEngine()
const provider = this.provider
const blockTracker = this.blockTracker
// create filter polyfill middleware
const filterMiddleware = createFilterMiddleware({
provider: this.provider,
blockTracker: this.blockTracker,
})
const filterMiddleware = createFilterMiddleware({ provider, blockTracker })
// create subscription polyfill middleware
const subscriptionManager = createSubscriptionManager({ provider, blockTracker })
subscriptionManager.events.on('notification', (message) => engine.emit('notification', message))
// metadata
engine.push(createOriginMiddleware({ origin }))
engine.push(createLoggerMiddleware({ origin }))
// filter and subscription polyfills
engine.push(filterMiddleware)
engine.push(subscriptionManager.middleware)
// watch asset
engine.push(this.preferencesController.requestWatchAsset.bind(this.preferencesController))
engine.push(createProviderMiddleware({ provider: this.provider }))
// sign typed data middleware
engine.push(this.createTypedDataMiddleware('eth_signTypedData', 'V1').bind(this))
engine.push(this.createTypedDataMiddleware('eth_signTypedData_v1', 'V1').bind(this))
engine.push(this.createTypedDataMiddleware('eth_signTypedData_v3', 'V3', true).bind(this))
// forward to metamask primary provider
engine.push(createProviderMiddleware({ provider }))
// setup connection
const providerStream = createEngineStream({ engine })
pump(
outStream,
providerStream,
@ -1280,10 +1299,12 @@ module.exports = class MetamaskController extends EventEmitter {
* @param {*} outStream - The stream to provide public config over.
*/
setupPublicConfig (outStream) {
const configStream = asStream(this.publicConfigStore)
pump(
asStream(this.publicConfigStore),
configStream,
outStream,
(err) => {
configStream.destroy()
if (err) log.error(err)
}
)
@ -1354,11 +1375,24 @@ module.exports = class MetamaskController extends EventEmitter {
})
.map(number => number.div(GWEI_BN).toNumber())
const percentileNum = percentile(50, lowestPrices)
const percentileNum = percentile(65, lowestPrices)
const percentileNumBn = new BN(percentileNum)
return '0x' + percentileNumBn.mul(GWEI_BN).toString(16)
}
/**
* Returns the nonce that will be associated with a transaction once approved
* @param address {string} - The hex string address for the transaction
* @returns Promise<number>
*/
async getPendingNonce (address) {
const { nonceDetails, releaseLock} = await this.txController.nonceTracker.getNonceLock(address)
const pendingNonce = nonceDetails.params.highestSuggested
releaseLock()
return pendingNonce
}
//=============================================================================
// CONFIG
//=============================================================================
@ -1421,6 +1455,14 @@ module.exports = class MetamaskController extends EventEmitter {
return rpcTarget
}
/**
* A method for deleting a selected custom URL.
* @param {string} rpcTarget - A RPC URL to delete.
*/
async delCustomRpc (rpcTarget) {
await this.preferencesController.updateFrequentRpcList(rpcTarget, true)
}
/**
* Sets whether or not to use the blockie identicon format.
* @param {boolean} val - True for bockie, false for jazzicon.
@ -1484,4 +1526,42 @@ module.exports = class MetamaskController extends EventEmitter {
set isClientOpenAndUnlocked (active) {
this.tokenRatesController.isActive = active
}
/**
* Creates RPC engine middleware for processing eth_signTypedData requests
*
* @param {Object} req - request object
* @param {Object} res - response object
* @param {Function} - next
* @param {Function} - end
*/
createTypedDataMiddleware (methodName, version, reverse) {
return async (req, res, next, end) => {
const { method, params } = req
if (method === methodName) {
const promise = this.typedMessageManager.addUnapprovedMessageAsync({
data: reverse ? params[1] : params[0],
from: reverse ? params[0] : params[1],
}, req, version)
this.sendUpdate()
this.opts.showUnconfirmedMessage()
try {
res.result = await promise
end()
} catch (error) {
end(error)
}
} else {
next()
}
}
}
/**
* Adds a domain to the {@link BlacklistController} whitelist
* @param {string} hostname the domain to whitelist
*/
whitelistPhishingDomain (hostname) {
return this.blacklistController.whitelistDomain(hostname)
}
}

@ -1,50 +0,0 @@
const version = 5
/*
This is an incomplete migration bc it requires post-decrypted data
which we dont have access to at the time of this writing.
*/
const ObservableStore = require('obs-store')
const ConfigManager = require('../../app/scripts/lib/config-manager')
const IdentityStoreMigrator = require('../../app/scripts/lib/idStore-migrator')
const KeyringController = require('eth-keyring-controller')
const password = 'obviously not correct'
module.exports = {
version,
migrate: function (versionedData) {
versionedData.meta.version = version
const store = new ObservableStore(versionedData.data)
const configManager = new ConfigManager({ store })
const idStoreMigrator = new IdentityStoreMigrator({ configManager })
const keyringController = new KeyringController({
configManager: configManager,
})
// attempt to migrate to multiVault
return idStoreMigrator.migratedVaultForPassword(password)
.then((result) => {
// skip if nothing to migrate
if (!result) return Promise.resolve(versionedData)
delete versionedData.data.wallet
// create new keyrings
const privKeys = result.lostAccounts.map(acct => acct.privateKey)
return Promise.all([
keyringController.restoreKeyring(result.serialized),
keyringController.restoreKeyring({ type: 'Simple Key Pair', data: privKeys }),
]).then(() => {
return keyringController.persistAllKeyrings(password)
}).then(() => {
// copy result on to state object
versionedData.data = store.get()
return Promise.resolve(versionedData)
})
})
},
}

@ -7,7 +7,7 @@ const uniqBy = require('lodash.uniqby')
module.exports = class NoticeController extends EventEmitter {
constructor (opts) {
constructor (opts = {}) {
super()
this.noticePoller = null
this.firstVersion = opts.firstVersion

@ -0,0 +1,59 @@
window.onload = function () {
if (window.location.pathname === '/phishing.html') {
const {hostname} = parseHash()
document.getElementById('esdbLink').innerHTML = '<b>To read more about this scam, navigate to: <a href="https://etherscamdb.info/domain/' + hostname + '"> https://etherscamdb.info/domain/' + hostname + '</a></b>'
}
}
const querystring = require('querystring')
const dnode = require('dnode')
const { EventEmitter } = require('events')
const PortStream = require('extension-port-stream')
const extension = require('extensionizer')
const setupMultiplex = require('./lib/stream-utils.js').setupMultiplex
const { getEnvironmentType } = require('./lib/util')
const ExtensionPlatform = require('./platforms/extension')
document.addEventListener('DOMContentLoaded', start)
function start () {
const windowType = getEnvironmentType(window.location.href)
global.platform = new ExtensionPlatform()
global.METAMASK_UI_TYPE = windowType
const extensionPort = extension.runtime.connect({ name: windowType })
const connectionStream = new PortStream(extensionPort)
const mx = setupMultiplex(connectionStream)
setupControllerConnection(mx.createStream('controller'), (err, metaMaskController) => {
if (err) {
return
}
const suspect = parseHash()
const unsafeContinue = () => {
window.location.href = suspect.href
}
const continueLink = document.getElementById('unsafe-continue')
continueLink.addEventListener('click', () => {
metaMaskController.whitelistPhishingDomain(suspect.hostname)
unsafeContinue()
})
})
}
function setupControllerConnection (connectionStream, cb) {
const eventEmitter = new EventEmitter()
const accountManagerDnode = dnode({
sendUpdate (state) {
eventEmitter.emit('update', state)
},
})
connectionStream.pipe(accountManagerDnode).pipe(connectionStream)
accountManagerDnode.once('remote', (accountManager) => cb(null, accountManager))
}
function parseHash () {
const hash = window.location.hash.substring(1)
return querystring.parse(hash)
}

@ -14,21 +14,27 @@ async function start () {
const versionAlreadyExists = await checkIfVersionExists()
// abort if versions exists
if (versionAlreadyExists) {
console.log(`Version "${VERSION}" already exists on Sentry, aborting sourcemap upload.`)
return
}
console.log(`Version "${VERSION}" already exists on Sentry, skipping version creation`)
} else {
// create sentry release
console.log(`creating Sentry release for "${VERSION}"...`)
await exec(`sentry-cli releases --org 'metamask' --project 'metamask' new ${VERSION}`)
console.log(`removing any existing files from Sentry release "${VERSION}"...`)
await exec(`sentry-cli releases --org 'metamask' --project 'metamask' files ${VERSION} delete --all`)
}
// check if version has artifacts or not
const versionHasArtifacts = versionAlreadyExists && await checkIfVersionHasArtifacts()
if (!versionHasArtifacts) {
// upload sentry source and sourcemaps
console.log(`uploading source files Sentry release "${VERSION}"...`)
await exec(`for FILEPATH in ./dist/chrome/*.js; do [ -e $FILEPATH ] || continue; export FILE=\`basename $FILEPATH\` && echo uploading $FILE && sentry-cli releases --org 'metamask' --project 'metamask' files ${VERSION} upload $FILEPATH metamask/$FILE; done;`)
console.log(`uploading sourcemaps Sentry release "${VERSION}"...`)
await exec(`sentry-cli releases --org 'metamask' --project 'metamask' files ${VERSION} upload-sourcemaps ./dist/sourcemaps/ --url-prefix 'sourcemaps'`)
console.log('all done!')
} else {
console.log(`Version "${VERSION}" already has artifacts on Sentry, skipping sourcemap upload`)
}
}
async function checkIfAuthWorks () {
@ -45,6 +51,12 @@ async function checkIfVersionExists () {
return versionAlreadyExists
}
async function checkIfVersionHasArtifacts () {
const artifacts = await exec(`sentry-cli releases --org 'metamask' --project 'metamask' files ${VERSION} list`)
// When there's no artifacts, we get a response from the shell like this ['', '']
return artifacts[0] && artifacts[0].length > 0
}
async function doesNotFail (asyncFn) {
try {
await asyncFn()

@ -20,7 +20,7 @@ const specifiedLocale = process.argv[2]
if (specifiedLocale) {
console.log(`Verifying selected locale "${specifiedLocale}":\n\n`)
const locale = localeIndex.find(localeMeta => localeMeta.code === specifiedLocale)
verifyLocale({ locale })
verifyLocale(locale)
} else {
console.log('Verifying all locales:\n\n')
localeIndex.forEach(localeMeta => {
@ -30,11 +30,10 @@ if (specifiedLocale) {
}
function verifyLocale ({ localeMeta }) {
function verifyLocale (localeMeta) {
const localeCode = localeMeta.code
const localeName = localeMeta.name
let targetLocale, englishLocale
try {
const localeFilePath = path.join(process.cwd(), 'app', '_locales', localeCode, 'messages.json')
targetLocale = JSON.parse(fs.readFileSync(localeFilePath, 'utf8'))

@ -5,7 +5,6 @@ To add another network to our dropdown menu, make sure the following files are a
```
app/scripts/config.js
app/scripts/lib/buy-eth-url.js
app/scripts/lib/config-manager.js
ui/app/app.js
ui/app/components/buy-button-subview.js
ui/app/components/drop-menu-item.js

@ -97,5 +97,5 @@ If streams seem new and confusing to you, that's ok, they can seem strange at fi
## Conclusion
I hope this has been helpful to you! If you have any other questionsm, or points you think need clarification in this guide, please [open an issue on our GitHub](https://github.com/MetaMask/metamask-plugin/issues/new)!
I hope this has been helpful to you! If you have any other questions, or points you think need clarification in this guide, please [open an issue on our GitHub](https://github.com/MetaMask/metamask-plugin/issues/new)!

@ -11,5 +11,5 @@ To take a state dump, follow these steps:
3. In case it isn't already selected, click the "Console" tab in the new Developer Tools window.
4. In the console, type this command exactly: `logState()`. This should print a bunch of JSON text into your console.
5. Copy that printed JSON text
6. *Optional*: Annonymize that text if you'd like (you may change all instances of an account address to another valid account address, for example) We may automate the anonymization in the future.
6. *Optional*: Anonymize that text if you'd like (you may change all instances of an account address to another valid account address, for example) We may automate the anonymization in the future.
7. Send that JSON text to the developer, ideally pasting it in the issue regarding the bug.

@ -8,7 +8,7 @@ The MetaMask browser extension supports new translations added in the form of ne
- Each supported language is represented by a folder in `app/_locales` whose name is that language's subtag (example: `app/_locales/es/`). (look up a language subtag using the [r12a "Find" tool](https://r12a.github.io/app-subtags/) or this [wikipedia list](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes)).
- Inside that folder there should be a `messages.json`.
- An easy way to start your translation is to first **make a copy** of `app/_locales/en/messages.json` (the english translation), and then **translate the `message` key** for each in-app message.
- An easy way to start your translation is to first **make a copy** of `app/_locales/en/messages.json` (the English translation), and then **translate the `message` key** for each in-app message.
- **The `description` key** is just to add context for what the translation is about, it **does not need to be translated**.
- Add the language to the [locales index](https://github.com/MetaMask/metamask-extension/blob/master/app/_locales/index.json) `app/_locales/index.json`

@ -233,11 +233,12 @@ function createScssBuildTask ({ src, dest, devMode, pattern }) {
await endOfStream(stream)
livereload.changed(event.path)
})
return buildScssWithSourceMaps()
}
return buildScss()
}
function buildScss () {
function buildScssWithSourceMaps () {
return gulp.src(src)
.pipe(sourcemaps.init())
.pipe(sass().on('error', sass.logError))
@ -245,6 +246,13 @@ function createScssBuildTask ({ src, dest, devMode, pattern }) {
.pipe(autoprefixer())
.pipe(gulp.dest(dest))
}
function buildScss () {
return gulp.src(src)
.pipe(sass().on('error', sass.logError))
.pipe(autoprefixer())
.pipe(gulp.dest(dest))
}
}
gulp.task('lint-scss', function () {
@ -271,6 +279,7 @@ const buildJsFiles = [
'contentscript',
'background',
'ui',
'phishing-detect',
]
// bundle tasks

@ -63,7 +63,9 @@ class CreatePasswordScreen extends Component {
return password === confirmPassword
}
createAccount = () => {
createAccount = (event) => {
event.preventDefault()
if (!this.isValid()) {
return
}
@ -127,7 +129,7 @@ class CreatePasswordScreen extends Component {
It allows you to hold ether & tokens, and interact with decentralized applications.
</div>
</div>}
<div className="create-password">
<form className="create-password">
<div className="create-password__title">
Create Password
</div>
@ -188,7 +190,7 @@ class CreatePasswordScreen extends Component {
</a>
{ */ }
<Breadcrumbs total={3} currentIndex={0} />
</div>
</form>
</div>
</div>
)

@ -17,6 +17,12 @@
font-family: Roboto;
}
@media screen and (min-height: 601px) {
.first-time-flow {
height: 100vh;
}
}
.alpha-warning__container {
display: flex;
justify-content: center;

@ -21,7 +21,7 @@ PendingMsgDetails.prototype.render = function () {
var identity = state.identities[address] || { address: address }
var account = state.accounts[address] || { address: address }
var { data } = msgParams
var { data, version } = msgParams
return (
h('div', {
@ -48,6 +48,7 @@ PendingMsgDetails.prototype.render = function () {
h('label.font-small', { style: { display: 'block' } }, 'YOU ARE SIGNING'),
h(TypedMessageRenderer, {
value: data,
version,
style: {
height: '215px',
},

@ -2,6 +2,7 @@ const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const extend = require('xtend')
const { ObjectInspector } = require('react-inspector')
module.exports = TypedMessageRenderer
@ -12,8 +13,16 @@ function TypedMessageRenderer () {
TypedMessageRenderer.prototype.render = function () {
const props = this.props
const { value, style } = props
const text = renderTypedData(value)
const { value, version, style } = props
let text
switch (version) {
case 'V1':
text = renderTypedData(value)
break
case 'V3':
text = renderTypedDataV3(value)
break
}
const defaultStyle = extend({
width: '315px',
@ -44,3 +53,17 @@ function renderTypedData (values) {
])
})
}
function renderTypedDataV3 (values) {
const { domain, message } = JSON.parse(values)
return [
domain ? h('div', [
h('h1', 'Domain'),
h(ObjectInspector, { data: domain, expandLevel: 1, name: 'domain' }),
]) : '',
message ? h('div', [
h('h1', 'Message'),
h(ObjectInspector, { data: message, expandLevel: 1, name: 'message' }),
]) : '',
]
}

52624
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -8,13 +8,15 @@
"mascara": "gulp dev:mascara & node ./mascara/example/server",
"dist": "gulp dist",
"doc": "jsdoc -c development/tools/.jsdoc.json",
"publish-docs": "gh-pages -d docs/jsdocs",
"test": "npm run test:unit && npm run test:integration && npm run lint",
"watch:test:unit": "nodemon --exec \"npm run test:unit\" ./test ./app ./ui",
"test:unit": "cross-env METAMASK_ENV=test mocha --exit --require test/setup.js --recursive \"test/unit/**/*.js\" \"ui/app/**/*.test.js\" && dot-only-hunter",
"test:unit": "cross-env METAMASK_ENV=test mocha --exit --require test/setup.js --recursive \"test/unit/**/*.js\" \"ui/app/**/*.test.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",
"test:e2e:chrome": "shell-parallel -s 'npm run ganache:start' -x 'sleep 3 && npm run test:e2e:run:chrome'",
"test:e2e:drizzle:beta": "SELENIUM_BROWSER=chrome test/e2e/beta/run-drizzle.sh",
"test:e2e:chrome:beta": "SELENIUM_BROWSER=chrome test/e2e/beta/run-all.sh",
"test:e2e:firefox": "shell-parallel -s 'npm run ganache:start' -x 'sleep 3 && npm run test:e2e:run:firefox'",
"test:e2e:firefox:beta": "SELENIUM_BROWSER=firefox test/e2e/beta/run-all.sh",
@ -22,7 +24,7 @@
"test:e2e:run:firefox": "SELENIUM_BROWSER=firefox mocha test/e2e/metamask.spec --bail --recursive",
"test:screens": "shell-parallel -s 'npm run ganache:start' -x 'sleep 3 && npm run test:screens:run'",
"test:screens:run": "node test/screens/new-ui.js",
"test:coverage": "nyc npm run test:unit && npm run test:coveralls-upload",
"test:coverage": "nyc --reporter=text --reporter=html npm run test:unit && npm run test:coveralls-upload",
"test:coveralls-upload": "if [ $COVERALLS_REPO_TOKEN ]; then nyc report --reporter=text-lcov | coveralls; fi",
"test:flat": "npm run test:flat:build && karma start test/flat.conf.js",
"test:flat:build": "npm run test:flat:build:ui && npm run test:flat:build:tests && npm run test:flat:build:locales",
@ -40,6 +42,7 @@
"sentry:publish": "node ./development/sentry-publish.js",
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"mozilla-lint": "addons-linter dist/firefox",
"ui": "npm run test:flat:build:states && beefy development/ui-dev.js:bundle.js --live --open --index=./development/index.html --cwd ./",
"mock": "beefy development/mock-dev.js:bundle.js --live --open --index=./development/index.html --cwd ./",
"watch": "mocha watch --recursive \"test/unit/**/*.js\"",
@ -98,7 +101,7 @@
"copy-to-clipboard": "^3.0.8",
"css-loader": "^0.28.11",
"currency-formatter": "^1.4.2",
"debounce": "^1.0.0",
"debounce": "1.1.0",
"debounce-stream": "^2.0.0",
"deep-extend": "^0.5.1",
"detect-node": "^2.0.3",
@ -109,21 +112,20 @@
"ensnare": "^1.0.0",
"eslint-plugin-react": "^7.4.0",
"eth-bin-to-ops": "^1.0.1",
"eth-block-tracker": "^4.0.1",
"eth-block-tracker": "^4.0.3",
"eth-contract-metadata": "github:MetaMask/eth-contract-metadata#master",
"eth-ens-namehash": "^2.0.8",
"eth-hd-keyring": "^1.2.2",
"eth-json-rpc-filters": "^2.1.1",
"eth-json-rpc-filters": "^3.0.1",
"eth-json-rpc-infura": "^3.0.0",
"eth-json-rpc-middleware": "^2.4.0",
"eth-keyring-controller": "^3.1.4",
"eth-ledger-bridge-keyring": "^0.1.0",
"eth-method-registry": "^1.0.0",
"eth-phishing-detect": "^1.1.4",
"eth-query": "^2.1.2",
"eth-sig-util": "^1.4.2",
"eth-token-tracker": "^1.1.4",
"eth-trezor-keyring": "github:MetaMask/eth-trezor-keyring#trezor-v5",
"eth-sig-util": "^2.0.2",
"eth-token-tracker": "^1.1.5",
"ethereumjs-abi": "^0.6.4",
"ethereumjs-tx": "^1.3.0",
"ethereumjs-util": "github:ethereumjs/ethereumjs-util#ac5d0908536b447083ea422b435da27f26615de9",
@ -154,22 +156,23 @@
"iframe-stream": "^3.0.0",
"inject-css": "^0.1.1",
"jazzicon": "^1.2.0",
"json-rpc-engine": "^3.7.3",
"json-rpc-middleware-stream": "^1.0.1",
"json-rpc-engine": "^3.8.0",
"json-rpc-middleware-stream": "^2.1.0",
"jsonschema": "^1.2.4",
"lodash.debounce": "^4.0.8",
"lodash.memoize": "^4.1.2",
"lodash.shuffle": "^4.2.0",
"lodash.uniqby": "^4.7.0",
"loglevel": "^1.4.1",
"metamascara": "^2.0.0",
"metamask-inpage-provider": "^1.0.0",
"metamask-inpage-provider": "^1.2.2",
"metamask-logo": "^2.1.4",
"mkdirp": "^0.5.1",
"multihashes": "^0.4.12",
"multiplex": "^6.7.0",
"number-to-bn": "^1.7.0",
"obj-multiplex": "^1.0.0",
"obs-store": "^3.0.0",
"obs-store": "^3.0.2",
"percentile": "^1.2.0",
"pify": "^3.0.0",
"ping-pong-stream": "^1.0.0",
@ -188,6 +191,7 @@
"react-addons-css-transition-group": "^15.6.0",
"react-dom": "^15.6.2",
"react-hyperscript": "^3.0.0",
"react-inspector": "^2.3.0",
"react-markdown": "^3.0.0",
"react-media": "^1.8.0",
"react-redux": "^5.0.5",
@ -227,6 +231,7 @@
"@storybook/addon-info": "^3.4.2",
"@storybook/addon-knobs": "^3.4.2",
"@storybook/react": "^3.4.2",
"addons-linter": "^1.3.4",
"babel-core": "^6.24.1",
"babel-eslint": "^8.0.0",
"babel-plugin-transform-async-to-generator": "^6.24.1",
@ -249,7 +254,6 @@
"css-loader": "^0.28.11",
"deep-freeze-strict": "^1.1.1",
"del": "^3.0.0",
"dot-only-hunter": "^1.0.3",
"envify": "^4.0.0",
"enzyme": "^3.4.4",
"enzyme-adapter-react-15": "^1.0.6",
@ -257,8 +261,9 @@
"eslint-plugin-json": "^1.2.0",
"eslint-plugin-mocha": "^5.0.0",
"eslint-plugin-react": "^7.4.0",
"eth-json-rpc-middleware": "^1.6.0",
"eth-json-rpc-middleware": "^3.1.3",
"eth-keyring-controller": "^3.3.1",
"fetch-mock": "^6.5.2",
"file-loader": "^1.1.11",
"fs-extra": "^6.0.1",
"fs-promise": "^2.0.3",

@ -0,0 +1,70 @@
{ "isInitialized": true,
"provider": { "type": "rpc", "rpcTarget": "http://localhost:8545" },
"network": "loading",
"accounts": {
"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc": {
"address": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc",
"balance": "0x0"
},
"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b": {
"address": "0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b",
"balance": "0x0"
}
},
"currentBlockGasLimit": "",
"unapprovedTxs": {},
"selectedAddressTxList": [],
"computedBalances": {},
"unapprovedMsgs": {},
"unapprovedMsgCount": 0,
"unapprovedPersonalMsgs": {},
"unapprovedPersonalMsgCount": 0,
"unapprovedTypedMessages": {},
"unapprovedTypedMessagesCount": 0,
"isUnlocked": true,
"keyringTypes": [ "Simple Key Pair", "HD Key Tree" ],
"keyrings":[
{ "type": "HD Key Tree",
"accounts": [
"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc"
]
},
{
"type": "Simple Key Pair",
"accounts": [
"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b"
]
}
],
"frequentRpcList": [],
"currentAccountTab": "history",
"tokens": [],
"useBlockie": false,
"featureFlags": {},
"currentLocale": null,
"identities": {
"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc": {
"name": "Account 1",
"address": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc"
},
"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b": {
"name": "Account 2",
"address": "0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b"
}
},
"lostIdentities": {},
"selectedAddress": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc",
"recentBlocks": [],
"addressBook": [],
"currentCurrency": "usd",
"conversionRate": 288.45,
"conversionDate": 1506444677,
"nextUnreadNotice": null,
"noActiveNotices": true,
"shapeShiftTxList": [],
"infuraNetworkStatus": {},
"lostAccounts": [],
"seedWords": "debris dizzy just program just float decrease vacant alarm reduce speak stadium",
"forgottenPassword": null
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,286 @@
const path = require('path')
const assert = require('assert')
const webdriver = require('selenium-webdriver')
const { By, until } = webdriver
const {
delay,
buildChromeWebDriver,
buildFirefoxWebdriver,
installWebExt,
getExtensionIdChrome,
getExtensionIdFirefox,
} = require('../func')
const {
checkBrowserForConsoleErrors,
closeAllWindowHandlesExcept,
findElement,
findElements,
loadExtension,
openNewPage,
verboseReportOnFailure,
waitUntilXWindowHandles,
} = require('./helpers')
describe('MetaMask', function () {
let extensionId
let driver
const tinyDelayMs = 200
const regularDelayMs = tinyDelayMs * 2
const largeDelayMs = regularDelayMs * 2
this.timeout(0)
this.bail(true)
before(async function () {
switch (process.env.SELENIUM_BROWSER) {
case 'chrome': {
const extPath = path.resolve('dist/chrome')
driver = buildChromeWebDriver(extPath)
extensionId = await getExtensionIdChrome(driver)
await driver.get(`chrome-extension://${extensionId}/popup.html`)
break
}
case 'firefox': {
const extPath = path.resolve('dist/firefox')
driver = buildFirefoxWebdriver()
await installWebExt(driver, extPath)
await delay(700)
extensionId = await getExtensionIdFirefox(driver)
await driver.get(`moz-extension://${extensionId}/popup.html`)
}
}
})
afterEach(async function () {
if (process.env.SELENIUM_BROWSER === 'chrome') {
const errors = await checkBrowserForConsoleErrors(driver)
if (errors.length) {
const errorReports = errors.map(err => err.message)
const errorMessage = `Errors found in browser console:\n${errorReports.join('\n')}`
console.error(new Error(errorMessage))
}
}
if (this.currentTest.state === 'failed') {
await verboseReportOnFailure(driver, this.currentTest)
}
})
after(async function () {
await driver.quit()
})
describe('New UI setup', async function () {
it('switches to first tab', async function () {
await delay(tinyDelayMs)
const [firstTab] = await driver.getAllWindowHandles()
await driver.switchTo().window(firstTab)
await delay(regularDelayMs)
})
it('selects the new UI option', async () => {
try {
const overlay = await findElement(driver, By.css('.full-flex-height'))
await driver.wait(until.stalenessOf(overlay))
} catch (e) {}
let button
try {
button = await findElement(driver, By.xpath("//button[contains(text(), 'Try it now')]"))
} catch (e) {
await loadExtension(driver, extensionId)
await delay(largeDelayMs)
button = await findElement(driver, By.xpath("//button[contains(text(), 'Try it now')]"))
}
await button.click()
await delay(regularDelayMs)
// Close all other tabs
const [tab0, tab1, tab2] = await driver.getAllWindowHandles()
await driver.switchTo().window(tab0)
await delay(tinyDelayMs)
let selectedUrl = await driver.getCurrentUrl()
await delay(tinyDelayMs)
if (tab0 && selectedUrl.match(/popup.html/)) {
await closeAllWindowHandlesExcept(driver, tab0)
} else if (tab1) {
await driver.switchTo().window(tab1)
selectedUrl = await driver.getCurrentUrl()
await delay(tinyDelayMs)
if (selectedUrl.match(/popup.html/)) {
await closeAllWindowHandlesExcept(driver, tab1)
} else if (tab2) {
await driver.switchTo().window(tab2)
selectedUrl = await driver.getCurrentUrl()
selectedUrl.match(/popup.html/) && await closeAllWindowHandlesExcept(driver, tab2)
}
} else {
throw new Error('popup.html not found')
}
await delay(regularDelayMs)
const [appTab] = await driver.getAllWindowHandles()
await driver.switchTo().window(appTab)
await delay(tinyDelayMs)
await loadExtension(driver, extensionId)
await delay(regularDelayMs)
const continueBtn = await findElement(driver, By.css('.welcome-screen__button'))
await continueBtn.click()
await delay(regularDelayMs)
})
})
describe('Going through the first time flow', () => {
it('accepts a secure password', async () => {
const passwordBox = await findElement(driver, By.css('.create-password #create-password'))
const passwordBoxConfirm = await findElement(driver, By.css('.create-password #confirm-password'))
const button = await findElement(driver, By.css('.create-password button'))
await passwordBox.sendKeys('correct horse battery staple')
await passwordBoxConfirm.sendKeys('correct horse battery staple')
await button.click()
await delay(regularDelayMs)
})
it('clicks through the unique image screen', async () => {
const nextScreen = await findElement(driver, By.css('.unique-image button'))
await nextScreen.click()
await delay(regularDelayMs)
})
it('clicks through the ToS', async () => {
// terms of use
const canClickThrough = await driver.findElement(By.css('.tou button')).isEnabled()
assert.equal(canClickThrough, false, 'disabled continue button')
const bottomOfTos = await findElement(driver, By.linkText('Attributions'))
await driver.executeScript('arguments[0].scrollIntoView(true)', bottomOfTos)
await delay(regularDelayMs)
const acceptTos = await findElement(driver, By.css('.tou button'))
driver.wait(until.elementIsEnabled(acceptTos))
await acceptTos.click()
await delay(regularDelayMs)
})
it('clicks through the privacy notice', async () => {
// privacy notice
const nextScreen = await findElement(driver, By.css('.tou button'))
await nextScreen.click()
await delay(regularDelayMs)
})
it('clicks through the phishing notice', async () => {
// phishing notice
const noticeElement = await driver.findElement(By.css('.markdown'))
await driver.executeScript('arguments[0].scrollTop = arguments[0].scrollHeight', noticeElement)
await delay(regularDelayMs)
const nextScreen = await findElement(driver, By.css('.tou button'))
await nextScreen.click()
await delay(regularDelayMs)
})
let seedPhrase
it('reveals the seed phrase', async () => {
const byRevealButton = By.css('.backup-phrase__secret-blocker .backup-phrase__reveal-button')
await driver.wait(until.elementLocated(byRevealButton, 10000))
const revealSeedPhraseButton = await findElement(driver, byRevealButton, 10000)
await revealSeedPhraseButton.click()
await delay(regularDelayMs)
seedPhrase = await driver.findElement(By.css('.backup-phrase__secret-words')).getText()
assert.equal(seedPhrase.split(' ').length, 12)
await delay(regularDelayMs)
const nextScreen = await findElement(driver, By.css('.backup-phrase button'))
await nextScreen.click()
await delay(regularDelayMs)
})
async function clickWordAndWait (word) {
const xpathClass = 'backup-phrase__confirm-seed-option backup-phrase__confirm-seed-option--unselected'
const xpath = `//button[@class='${xpathClass}' and contains(text(), '${word}')]`
const word0 = await findElement(driver, By.xpath(xpath), 10000)
await word0.click()
await delay(tinyDelayMs)
}
async function retypeSeedPhrase (words, wasReloaded, count = 0) {
try {
if (wasReloaded) {
const byRevealButton = By.css('.backup-phrase__secret-blocker .backup-phrase__reveal-button')
await driver.wait(until.elementLocated(byRevealButton, 10000))
const revealSeedPhraseButton = await findElement(driver, byRevealButton, 10000)
await revealSeedPhraseButton.click()
await delay(regularDelayMs)
const nextScreen = await findElement(driver, By.css('.backup-phrase button'))
await nextScreen.click()
await delay(regularDelayMs)
}
for (let i = 0; i < 12; i++) {
await clickWordAndWait(words[i])
}
} catch (e) {
if (count > 2) {
throw e
} else {
await loadExtension(driver, extensionId)
await retypeSeedPhrase(words, true, count + 1)
}
}
}
it('can retype the seed phrase', async () => {
const words = seedPhrase.split(' ')
await retypeSeedPhrase(words)
const confirm = await findElement(driver, By.xpath(`//button[contains(text(), 'Confirm')]`))
await confirm.click()
await delay(regularDelayMs)
})
it('clicks through the deposit modal', async () => {
const byBuyModal = By.css('span .modal')
const buyModal = await driver.wait(until.elementLocated(byBuyModal))
const closeModal = await findElement(driver, By.css('.page-container__header-close'))
await closeModal.click()
await driver.wait(until.stalenessOf(buyModal))
await delay(regularDelayMs)
})
it('switches to localhost', async () => {
const networkDropdown = await findElement(driver, By.css('.network-name'))
await networkDropdown.click()
await delay(regularDelayMs)
const [localhost] = await findElements(driver, By.xpath(`//span[contains(text(), 'Localhost')]`))
await localhost.click()
await delay(largeDelayMs * 2)
})
})
describe('Drizzle', () => {
it('should be able to detect our eth address', async () => {
await openNewPage(driver, 'http://127.0.0.1:3000/')
await delay(regularDelayMs)
await waitUntilXWindowHandles(driver, 2)
const windowHandles = await driver.getAllWindowHandles()
const dapp = windowHandles[1]
await driver.switchTo().window(dapp)
await delay(regularDelayMs)
const addressElement = await findElement(driver, By.css(`.pure-u-1-1 h4`))
const addressText = await addressElement.getText()
assert(addressText.match(/^0x[a-fA-F0-9]{40}$/))
})
})
})

@ -317,7 +317,7 @@ describe('Using MetaMask with an existing account', function () {
const transactions = await findElements(driver, By.css('.transaction-list-item'))
assert.equal(transactions.length, 1)
const txValues = await findElements(driver, By.css('.transaction-list-item__amount--secondary'))
const txValues = await findElements(driver, By.css('.transaction-list-item__amount--primary'))
assert.equal(txValues.length, 1)
assert.equal(await txValues[0].getText(), '-1 ETH')
})

@ -126,10 +126,7 @@ async function assertElementNotPresent (webdriver, driver, by) {
try {
dataTab = await findElement(driver, by, 4000)
} catch (err) {
console.log(err)
assert(err instanceof webdriver.error.NoSuchElementError || err instanceof webdriver.error.TimeoutError)
}
if (dataTab) {
assert(false, 'Data tab should not be present')
}
assert.ok(!dataTab, 'Found element that should not be present')
}

@ -271,6 +271,17 @@ describe('MetaMask', function () {
await driver.wait(until.stalenessOf(accountModal))
await delay(regularDelayMs)
})
it('show account details dropdown menu', async () => {
const {width, height} = await driver.manage().window().getSize()
driver.manage().window().setSize(320, 480)
await driver.findElement(By.css('div.menu-bar__open-in-browser')).click()
const options = await driver.findElements(By.css('div.menu.account-details-dropdown div.menu__item'))
assert.equal(options.length, 3) // HD Wallet type does not have to show the Remove Account option
await delay(regularDelayMs)
driver.manage().window().setSize(width, height)
})
})
describe('Log out an log back in', () => {
@ -408,7 +419,7 @@ describe('MetaMask', function () {
assert.equal(transactions.length, 1)
if (process.env.SELENIUM_BROWSER !== 'firefox') {
const txValues = await findElement(driver, By.css('.transaction-list-item__amount--secondary'))
const txValues = await findElement(driver, By.css('.transaction-list-item__amount--primary'))
await driver.wait(until.elementTextMatches(txValues, /-1\sETH/), 10000)
}
})
@ -450,7 +461,7 @@ describe('MetaMask', function () {
const transactions = await findElements(driver, By.css('.transaction-list-item'))
assert.equal(transactions.length, 2)
const txValues = await findElement(driver, By.css('.transaction-list-item__amount--secondary'))
const txValues = await findElement(driver, By.css('.transaction-list-item__amount--primary'))
await driver.wait(until.elementTextMatches(txValues, /-3\sETH/), 10000)
})
})
@ -528,7 +539,7 @@ describe('MetaMask', function () {
await delay(largeDelayMs)
await findElements(driver, By.css('.transaction-list-item'))
const [txListValue] = await findElements(driver, By.css('.transaction-list-item__amount--secondary'))
const [txListValue] = await findElements(driver, By.css('.transaction-list-item__amount--primary'))
await driver.wait(until.elementTextMatches(txListValue, /-4\sETH/), 10000)
await txListValue.click()
await delay(regularDelayMs)
@ -562,7 +573,7 @@ describe('MetaMask', function () {
return confirmedTxes.length === 4
}, 10000)
const txValues = await findElements(driver, By.css('.transaction-list-item__amount--secondary'))
const txValues = await findElements(driver, By.css('.transaction-list-item__amount--primary'))
await driver.wait(until.elementTextMatches(txValues[0], /-4\sETH/), 10000)
// const txAccounts = await findElements(driver, By.css('.tx-list-account'))
@ -594,7 +605,7 @@ describe('MetaMask', function () {
return confirmedTxes.length === 5
}, 10000)
const txValues = await findElement(driver, By.css('.transaction-list-item__amount--secondary'))
const txValues = await findElement(driver, By.css('.transaction-list-item__amount--primary'))
await driver.wait(until.elementTextMatches(txValues, /-0\sETH/), 10000)
await closeAllWindowHandlesExcept(driver, [extension, dapp])
@ -1030,7 +1041,7 @@ describe('MetaMask', function () {
]
customRpcUrls.forEach(customRpcUrl => {
it('creates custom RPC: ' + customRpcUrl, async () => {
it(`creates custom RPC: ${customRpcUrl}`, async () => {
const networkDropdown = await findElement(driver, By.css('.network-name'))
await networkDropdown.click()
await delay(regularDelayMs)
@ -1043,7 +1054,7 @@ describe('MetaMask', function () {
await customRpcInput.clear()
await customRpcInput.sendKeys(customRpcUrl)
const customRpcSave = await findElement(driver, By.css('.settings__rpc-save-button'))
const customRpcSave = await findElement(driver, By.css('.settings-tab__rpc-save-button'))
await customRpcSave.click()
await delay(largeDelayMs * 2)
})
@ -1059,25 +1070,15 @@ describe('MetaMask', function () {
await delay(largeDelayMs * 2)
})
it('finds 3 recent RPCs in history', async () => {
it('finds all recent RPCs in history', async () => {
const networkDropdown = await findElement(driver, By.css('.network-name'))
await networkDropdown.click()
await delay(regularDelayMs)
// oldest selected RPC is not found
await assertElementNotPresent(webdriver, driver, By.xpath(`//span[contains(text(), '${customRpcUrls[0]}')]`))
// only recent 3 are found and in correct order (most recent at the top)
const customRpcs = await findElements(driver, By.xpath(`//span[contains(text(), 'https://mainnet.infura.io/')]`))
assert.equal(customRpcs.length, 3)
for (let i = 0; i < customRpcs.length; i++) {
const linkText = await customRpcs[i].getText()
const rpcUrl = customRpcUrls[customRpcUrls.length - i - 1]
assert.notEqual(linkText.indexOf(rpcUrl), -1)
}
assert.equal(customRpcs.length, customRpcUrls.length)
})
})
})

@ -6,5 +6,5 @@ set -o pipefail
export PATH="$PATH:./node_modules/.bin"
shell-parallel -s 'npm run ganache:start -- -b 2' -x 'sleep 5 && static-server test/e2e/beta/contract-test/ --port 8080' -x 'sleep 5 && mocha test/e2e/beta/metamask-beta-ui.spec'
shell-parallel -s 'npm run ganache:start -- -d -b 2' -x 'sleep 5 && static-server test/e2e/beta/contract-test/ --port 8080' -x 'sleep 5 && mocha test/e2e/beta/from-import-beta-ui.spec'
shell-parallel -s 'npm run ganache:start -- -b 2' -x 'sleep 5 && static-server test/e2e/beta/contract-test --port 8080' -x 'sleep 5 && mocha test/e2e/beta/metamask-beta-ui.spec'
shell-parallel -s 'npm run ganache:start -- -d -b 2' -x 'sleep 5 && mocha test/e2e/beta/from-import-beta-ui.spec'

@ -0,0 +1,20 @@
#!/usr/bin/env bash
set -e
set -u
set -o pipefail
export PATH="$PATH:./node_modules/.bin"
npm run ganache:start -- -b 2 >> /dev/null 2>&1 &
sleep 5
cd test/e2e/beta/
rm -rf drizzle-test
mkdir drizzle-test && cd drizzle-test
npm install truffle
truffle unbox drizzle
echo "Deploying contracts for Drizzle test..."
truffle compile && truffle migrate
BROWSER=none npm start >> /dev/null 2>&1 &
cd ../../../../
mocha test/e2e/beta/drizzle.spec

@ -201,7 +201,7 @@ describe('Metamask popup page', function () {
})
it('balance renders', async function () {
await delay(200)
await delay(500)
const balance = await driver.findElement(By.css('#app-content > div > div.app-primary.from-right > div > div > div.flex-row > div.ether-balance.ether-balance-amount > div > div > div:nth-child(1) > div:nth-child(1)'))
assert.equal(await balance.getText(), '100.000')
await delay(200)

@ -19,7 +19,7 @@ async function runConfirmSigRequestsTest (assert, done) {
selectState.val('confirm sig requests')
reactTriggerChange(selectState[0])
const pendingRequestItem = $.find('.transaction-list-item')
const pendingRequestItem = $.find('.transaction-list-item .transaction-list-item__grid')
if (pendingRequestItem[0]) {
pendingRequestItem[0].click()

@ -94,7 +94,7 @@ async function runSendFlowTest (assert, done) {
sendToDropdownList.children()[2].click()
const sendToAccountAddress = sendToFieldInput.val()
assert.equal(sendToAccountAddress, '0x2f8d4a878cfa04a6e60d46362f5644deab66572d', 'send to dropdown selects the correct address')
assert.equal(sendToAccountAddress, '0x2f8D4a878cFA04A6E60D46362f5644DeAb66572D', 'send to dropdown selects the correct address')
const sendAmountField = await queryAsync($, '.send-v2__form-row:eq(2)')
sendAmountField.find('.currency-display')[0].click()

@ -32,15 +32,17 @@ async function runTxListItemsTest (assert, done) {
const txListItems = await queryAsync($, '.transaction-list-item')
assert.equal(txListItems.length, 8, 'all tx list items are rendered')
const retryTx = txListItems[1]
const retryTxLink = await findAsync($(retryTx), '.transaction-list-item__retry')
assert.equal(retryTxLink[0].textContent, 'Taking too long? Increase the gas price on your transaction', 'retryTx has expected link')
const retryTxGrid = await findAsync($(txListItems[2]), '.transaction-list-item__grid')
retryTxGrid[0].click()
const retryTxDetails = await findAsync($, '.transaction-list-item-details')
const headerButtons = await findAsync($(retryTxDetails[0]), '.transaction-list-item-details__header-button')
assert.equal(headerButtons[0].textContent, 'speed up')
const approvedTx = txListItems[2]
const approvedTxRenderedStatus = await findAsync($(approvedTx), '.transaction-list-item__status')
assert.equal(approvedTxRenderedStatus[0].textContent, 'pending', 'approvedTx has correct label')
const unapprovedMsg = txListItems[3]
const unapprovedMsg = txListItems[0]
const unapprovedMsgDescription = await findAsync($(unapprovedMsg), '.transaction-list-item__action')
assert.equal(unapprovedMsgDescription[0].textContent, 'Signature Request', 'unapprovedMsg has correct description')

@ -1,9 +0,0 @@
const ObservableStore = require('obs-store')
const clone = require('clone')
const ConfigManager = require('../../app/scripts/lib/config-manager')
const firstTimeState = require('../../app/scripts/first-time-state')
module.exports = function () {
const store = new ObservableStore(clone(firstTimeState))
return new ConfigManager({ store })
}

@ -1,5 +1,5 @@
var mockHex = '0xabcdef0123456789'
var mockKey = new Buffer(32)
var mockKey = Buffer.alloc(32)
let cacheVal
module.exports = {

@ -0,0 +1,42 @@
const { shallow, mount } = require('enzyme')
import { BrowserRouter } from 'react-router-dom'
import { shape } from 'prop-types'
module.exports = {
shallowWithStore,
mountWithStore,
mountWithRouter,
}
function shallowWithStore (component, store) {
const context = {
store,
}
return shallow(component, {context})
}
function mountWithStore (component, store) {
const context = {
store,
}
return mount(component, {context})
}
function mountWithRouter (node) {
// Instantiate router context
const router = {
history: new BrowserRouter().history,
route: {
location: {},
match: {},
},
}
const createContext = () => ({
context: { router, t: () => {} },
childContextTypes: { router: shape({}), t: () => {} },
})
return mount(node, createContext())
}

@ -1,20 +0,0 @@
const { shallow, mount } = require('enzyme')
module.exports = {
shallowWithStore,
mountWithStore,
}
function shallowWithStore (component, store) {
const context = {
store,
}
return shallow(component, {context})
}
function mountWithStore (component, store) {
const context = {
store,
}
return mount(component, {context})
}

@ -0,0 +1,33 @@
const assert = require('assert')
const cleanErrorStack = require('../../../app/scripts/lib/cleanErrorStack')
describe('Clean Error Stack', () => {
const testMessage = 'Test Message'
const testError = new Error(testMessage)
const undefinedErrorName = new Error(testMessage)
const blankErrorName = new Error(testMessage)
const blankMsgError = new Error()
beforeEach(() => {
undefinedErrorName.name = undefined
blankErrorName.name = ''
})
it('tests error with message', () => {
assert.equal(cleanErrorStack(testError), 'Error: Test Message')
})
it('tests error with undefined name', () => {
assert.equal(cleanErrorStack(undefinedErrorName).toString(), 'Error: Test Message')
})
it('tests error with blank name', () => {
assert.equal(cleanErrorStack(blankErrorName).toString(), 'Test Message')
})
it('tests error with blank message', () => {
assert.equal(cleanErrorStack(blankMsgError), 'Error')
})
})

@ -8,6 +8,16 @@ describe('blacklist controller', function () {
blacklistController = new BlacklistController()
})
describe('whitelistDomain', function () {
it('should add hostname to the runtime whitelist', function () {
blacklistController.whitelistDomain('foo.com')
assert.deepEqual(blacklistController.store.getState().whitelist, ['foo.com'])
blacklistController.whitelistDomain('bar.com')
assert.deepEqual(blacklistController.store.getState().whitelist, ['bar.com', 'foo.com'])
})
})
describe('checkForPhishing', function () {
it('should not flag whitelisted values', function () {
const result = blacklistController.checkForPhishing('www.metamask.io')
@ -37,5 +47,10 @@ describe('blacklist controller', function () {
const result = blacklistController.checkForPhishing('zero-faucet.metamask.io')
assert.equal(result, false)
})
it('should not flag whitelisted domain', function () {
blacklistController.whitelistDomain('metamask.com')
const result = blacklistController.checkForPhishing('metamask.com')
assert.equal(result, false)
})
})
})

@ -116,7 +116,7 @@ describe('MetaMaskController', function () {
}
const gasPrice = metamaskController.getGasPrice()
assert.equal(gasPrice, '0x3b9aca00', 'accurately estimates 50th percentile accepted gas price')
assert.equal(gasPrice, '0x174876e800', 'accurately estimates 65th percentile accepted gas price')
metamaskController.recentBlocksController = realRecentBlocksController
})
@ -584,22 +584,18 @@ describe('MetaMaskController', function () {
})
describe('#clearSeedWordCache', function () {
it('should set seed words to null', function (done) {
sandbox.stub(metamaskController.preferencesController, 'setSeedWords')
metamaskController.clearSeedWordCache((err) => {
if (err) {
done(err)
}
it('should have set seed words', function () {
metamaskController.configManager.setSeedWords('test words')
const getConfigSeed = metamaskController.configManager.getSeedWords()
assert.equal(getConfigSeed, 'test words')
})
it('should clear config seed phrase', function () {
metamaskController.configManager.setSeedWords('test words')
metamaskController.clearSeedWordCache((err, result) => {
if (err) console.log(err)
assert.ok(metamaskController.preferencesController.setSeedWords.calledOnce)
assert.deepEqual(metamaskController.preferencesController.setSeedWords.args, [[null]])
done()
})
const getConfigSeed = metamaskController.configManager.getSeedWords()
assert.equal(getConfigSeed, null)
})
})
describe('#setCurrentLocale', function () {
@ -793,24 +789,24 @@ describe('MetaMaskController', function () {
describe('#markAccountsFound', function () {
it('adds lost accounts to config manager data', function () {
metamaskController.markAccountsFound(noop)
const configManagerData = metamaskController.configManager.getData()
assert.deepEqual(configManagerData.lostAccounts, [])
const state = metamaskController.getState()
assert.deepEqual(state.lostAccounts, [])
})
})
describe('#markPasswordForgotten', function () {
it('adds and sets forgottenPassword to config data to true', function () {
metamaskController.markPasswordForgotten(noop)
const configManagerData = metamaskController.configManager.getData()
assert.equal(configManagerData.forgottenPassword, true)
const state = metamaskController.getState()
assert.equal(state.forgottenPassword, true)
})
})
describe('#unMarkPasswordForgotten', function () {
it('adds and sets forgottenPassword to config data to false', function () {
metamaskController.unMarkPasswordForgotten(noop)
const configManagerData = metamaskController.configManager.getData()
assert.equal(configManagerData.forgottenPassword, false)
const state = metamaskController.getState()
assert.equal(state.forgottenPassword, false)
})
})

@ -1,16 +1,11 @@
const assert = require('assert')
const configManagerGen = require('../../../lib/mock-config-manager')
const NoticeController = require('../../../../app/scripts/notice-controller')
describe('notice-controller', function () {
var noticeController
beforeEach(function () {
// simple localStorage polyfill
const configManager = configManagerGen()
noticeController = new NoticeController({
configManager: configManager,
})
noticeController = new NoticeController()
})
describe('notices', function () {

@ -449,5 +449,35 @@ describe('preferences controller', function () {
assert.ok(assetImages[address], `set image correctly`)
})
})
describe('setPasswordForgotten', function () {
it('should default to false', function () {
const state = preferencesController.store.getState()
assert.equal(state.forgottenPassword, false)
})
it('should set the forgottenPassword property in state', function () {
assert.equal(preferencesController.store.getState().forgottenPassword, false)
preferencesController.setPasswordForgotten(true)
assert.equal(preferencesController.store.getState().forgottenPassword, true)
})
})
describe('setSeedWords', function () {
it('should default to null', function () {
const state = preferencesController.store.getState()
assert.equal(state.seedWords, null)
})
it('should set the seedWords property in state', function () {
assert.equal(preferencesController.store.getState().seedWords, null)
preferencesController.setSeedWords('foo bar baz')
assert.equal(preferencesController.store.getState().seedWords, 'foo bar baz')
})
})
})

@ -158,9 +158,19 @@ describe('Transaction Controller', function () {
})
describe('#addUnapprovedTransaction', function () {
const selectedAddress = '0x1678a085c290ebd122dc42cba69373b5953b831d'
let getSelectedAddress
beforeEach(function () {
getSelectedAddress = sinon.stub(txController, 'getSelectedAddress').returns(selectedAddress)
})
afterEach(function () {
getSelectedAddress.restore()
})
it('should add an unapproved transaction and return a valid txMeta', function (done) {
txController.addUnapprovedTransaction({ from: '0x1678a085c290ebd122dc42cba69373b5953b831d' })
txController.addUnapprovedTransaction({ from: selectedAddress })
.then((txMeta) => {
assert(('id' in txMeta), 'should have a id')
assert(('time' in txMeta), 'should have a time stamp')
@ -180,25 +190,37 @@ describe('Transaction Controller', function () {
assert(txMetaFromEmit, 'txMeta is falsey')
done()
})
txController.addUnapprovedTransaction({ from: '0x1678a085c290ebd122dc42cba69373b5953b831d' })
txController.addUnapprovedTransaction({ from: selectedAddress })
.catch(done)
})
it('should fail if recipient is public', function (done) {
txController.networkStore = new ObservableStore(1)
txController.addUnapprovedTransaction({ from: '0x1678a085c290ebd122dc42cba69373b5953b831d', to: '0x0d1d4e623D10F9FBA5Db95830F7d3839406C6AF2' })
txController.addUnapprovedTransaction({ from: selectedAddress, to: '0x0d1d4e623D10F9FBA5Db95830F7d3839406C6AF2' })
.catch((err) => {
if (err.message === 'Recipient is a public account') done()
else done(err)
})
})
it('should fail if the from address isn\'t the selected address', function (done) {
txController.addUnapprovedTransaction({from: '0x0d1d4e623D10F9FBA5Db95830F7d3839406C6AF2'})
.then(function () {
assert.fail('transaction should not have been added')
done()
})
.catch(function () {
assert.ok('pass')
done()
})
})
it('should not fail if recipient is public but not on mainnet', function (done) {
txController.once('newUnapprovedTx', (txMetaFromEmit) => {
assert(txMetaFromEmit, 'txMeta is falsey')
done()
})
txController.addUnapprovedTransaction({ from: '0x1678a085c290ebd122dc42cba69373b5953b831d', to: '0x0d1d4e623D10F9FBA5Db95830F7d3839406C6AF2' })
txController.addUnapprovedTransaction({ from: selectedAddress, to: '0x0d1d4e623D10F9FBA5Db95830F7d3839406C6AF2' })
.catch(done)
})
})

@ -1,7 +1,7 @@
const assert = require('assert')
const h = require('react-hyperscript')
const { createMockStore } = require('redux-test-utils')
const { shallowWithStore } = require('../../lib/shallow-with-store')
const { shallowWithStore } = require('../../lib/render-helpers')
const BalanceComponent = require('../../../ui/app/components/balance-component')
const mockState = {
metamask: {
@ -42,4 +42,3 @@ describe('BalanceComponent', function () {
})
})

@ -4,7 +4,7 @@ var BinaryRenderer = require('../../../old-ui/app/components/binary-renderer')
describe('BinaryRenderer', function () {
let binaryRenderer
const message = 'Hello, world!'
const buffer = new Buffer(message, 'utf8')
const buffer = Buffer.from(message, 'utf8')
const hex = buffer.toString('hex')
beforeEach(function () {

@ -1,112 +0,0 @@
const assert = require('assert')
const configManagerGen = require('../lib/mock-config-manager')
describe('config-manager', function () {
var configManager
beforeEach(function () {
configManager = configManagerGen()
})
describe('#setConfig', function () {
it('should set the config key', function () {
var testConfig = {
provider: {
type: 'rpc',
rpcTarget: 'foobar',
},
}
configManager.setConfig(testConfig)
var result = configManager.getData()
assert.equal(result.config.provider.type, testConfig.provider.type)
assert.equal(result.config.provider.rpcTarget, testConfig.provider.rpcTarget)
})
it('setting wallet should not overwrite config', function () {
var testConfig = {
provider: {
type: 'rpc',
rpcTarget: 'foobar',
},
}
configManager.setConfig(testConfig)
var testWallet = {
name: 'this is my fake wallet',
}
configManager.setWallet(testWallet)
var result = configManager.getData()
assert.equal(result.wallet.name, testWallet.name, 'wallet name is set')
assert.equal(result.config.provider.rpcTarget, testConfig.provider.rpcTarget)
testConfig.provider.type = 'something else!'
configManager.setConfig(testConfig)
result = configManager.getData()
assert.equal(result.wallet.name, testWallet.name, 'wallet name is set')
assert.equal(result.config.provider.rpcTarget, testConfig.provider.rpcTarget)
assert.equal(result.config.provider.type, testConfig.provider.type)
})
})
describe('wallet nicknames', function () {
it('should return null when no nicknames are saved', function () {
var nick = configManager.nicknameForWallet('0x0')
assert.equal(nick, null, 'no nickname returned')
})
it('should persist nicknames', function () {
var account = '0x0'
var nick1 = 'foo'
var nick2 = 'bar'
configManager.setNicknameForWallet(account, nick1)
var result1 = configManager.nicknameForWallet(account)
assert.equal(result1, nick1)
configManager.setNicknameForWallet(account, nick2)
var result2 = configManager.nicknameForWallet(account)
assert.equal(result2, nick2)
})
})
describe('rpc manipulations', function () {
it('changing rpc should return a different rpc', function () {
var firstRpc = 'first'
var secondRpc = 'second'
configManager.setRpcTarget(firstRpc)
var firstResult = configManager.getCurrentRpcAddress()
assert.equal(firstResult, firstRpc)
configManager.setRpcTarget(secondRpc)
var secondResult = configManager.getCurrentRpcAddress()
assert.equal(secondResult, secondRpc)
})
})
describe('transactions', function () {
beforeEach(function () {
configManager.setTxList([])
})
describe('#getTxList', function () {
it('when new should return empty array', function () {
var result = configManager.getTxList()
assert.ok(Array.isArray(result))
assert.equal(result.length, 0)
})
})
describe('#setTxList', function () {
it('saves the submitted data to the tx list', function () {
var target = [{ foo: 'bar' }]
configManager.setTxList(target)
var result = configManager.getTxList()
assert.equal(result[0].foo, 'bar')
})
})
})
})

@ -67,7 +67,7 @@
- Estimating gas limit for simple ether sends now faster & cheaper, by avoiding VM usage on recipients with no code.
- Add an extra px to address for Firefox clipping.
- Fix Firefox scrollbar.
- Open metamask popup for transaction confirmation before gas estimation finishes and add a loading screen over transaction confirmation.
- Open MetaMask popup for transaction confirmation before gas estimation finishes and add a loading screen over transaction confirmation.
- Fix bug that prevented eth_signTypedData from signing bytes.
- Further improve gas price estimation.
@ -330,7 +330,7 @@ rollback to 3.10.0 due to bug
## 3.7.7 2017-6-8
- Fix bug where metamask would show old data after computer being asleep or disconnected from the internet.
- Fix bug where MetaMask would show old data after computer being asleep or disconnected from the internet.
## 3.7.6 2017-6-5

@ -6,7 +6,7 @@ const path = require('path')
const Dropdown = require(path.join(__dirname, '..', '..', '..', '..', 'ui', 'app', 'components', 'dropdowns', 'index.js')).Dropdown
const { createMockStore } = require('redux-test-utils')
const { mountWithStore } = require('../../../lib/shallow-with-store')
const { mountWithStore } = require('../../../lib/render-helpers')
const mockState = {
metamask: {

@ -1,7 +1,7 @@
const assert = require('assert')
const { createMockStore } = require('redux-test-utils')
const h = require('react-hyperscript')
const { shallowWithStore } = require('../../lib/shallow-with-store')
const { shallowWithStore } = require('../../lib/render-helpers')
const AddTokenScreen = require('../../../old-ui/app/add-token')
describe('Add Token Screen', function () {

File diff suppressed because it is too large Load Diff

@ -0,0 +1,36 @@
import React from 'react'
import assert from 'assert'
import thunk from 'redux-thunk'
import configureMockStore from 'redux-mock-store'
import { mount } from 'enzyme'
import IdenticonComponent from '../../../../../ui/app/components/identicon'
describe('Identicon Component', () => {
const state = {
metamask: {
useBlockie: false,
},
}
const middlewares = [thunk]
const mockStore = configureMockStore(middlewares)
const store = mockStore(state)
it('renders default eth_logo identicon with no props', () => {
const wrapper = mount(<IdenticonComponent store={store}/>)
assert.equal(wrapper.find('img.balance-icon').prop('src'), './images/eth_logo.svg')
})
it('renders custom image and add className props', () => {
const wrapper = mount(<IdenticonComponent store={store} className={'test-image'} image={'test-image'} />)
assert.equal(wrapper.find('img.test-image').prop('className'), 'test-image identicon')
assert.equal(wrapper.find('img.test-image').prop('src'), 'test-image')
})
it('renders div with address prop', () => {
const wrapper = mount(<IdenticonComponent store={store} className={'test-address'} address={'0xTest'} />)
assert.equal(wrapper.find('div.test-address').prop('className'), 'test-address identicon')
})
})

@ -0,0 +1,69 @@
import React from 'react'
import assert from 'assert'
import thunk from 'redux-thunk'
import { Provider } from 'react-redux'
import configureMockStore from 'redux-mock-store'
import { mount } from 'enzyme'
import TokenCell from '../../../../../ui/app/components/token-cell'
import Identicon from '../../../../../ui/app/components/identicon'
describe('Token Cell', () => {
let wrapper
const state = {
metamask: {
network: 'test',
currentCurrency: 'usd',
selectedTokenAddress: '0xToken',
selectedAddress: '0xAddress',
contractExchangeRates: {
'0xAnotherToken': 0.015,
},
conversionRate: 7.00,
},
appState: {
sidebar: {
isOpen: true,
},
},
}
const middlewares = [thunk]
const mockStore = configureMockStore(middlewares)
const store = mockStore(state)
beforeEach(() => {
wrapper = mount(
<Provider store={store}>
<TokenCell
address={'0xAnotherToken'}
symbol={'TEST'}
string={'5.000'}
network={22}
currentCurrency={'usd'}
image={'./test-image'}
/>
</Provider>
)
})
it('renders Identicon with props from token cell', () => {
assert.equal(wrapper.find(Identicon).prop('address'), '0xAnotherToken')
assert.equal(wrapper.find(Identicon).prop('network'), 'test')
assert.equal(wrapper.find(Identicon).prop('image'), './test-image')
})
it('renders token balance', () => {
assert.equal(wrapper.find('.token-list-item__token-balance').text(), '5.000')
})
it('renders token symbol', () => {
assert.equal(wrapper.find('.token-list-item__token-symbol').text(), 'TEST')
})
it('renders converted fiat amount', () => {
assert.equal(wrapper.find('.token-list-item__fiat-amount').text(), '0.52 USD')
})
})

@ -0,0 +1,175 @@
const assert = require('assert')
const selectors = require('../../../../ui/app/selectors')
const mockState = require('../../../data/mock-state.json')
const Eth = require('ethjs')
const { createTestProviderTools } = require('../../../stub/provider')
const provider = createTestProviderTools({ scaffold: {}}).provider
describe('Selectors', function () {
describe('#getSelectedAddress', function () {
let state
beforeEach(function () {
state = {
metamask: {
accounts: {
'0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc': {
'balance': '0x0',
'address': '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc',
},
},
},
}
})
it('returns first account if selectedAddress is undefined', function () {
assert.equal(selectors.getSelectedAddress(state), '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc')
})
it('returns selectedAddress', function () {
assert.equal(selectors.getSelectedAddress(mockState), '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc')
})
})
it('returns selected identity', function () {
const identity = selectors.getSelectedIdentity(mockState)
assert.equal(identity.address, '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc')
assert.equal(identity.name, 'Test Account')
})
it('returns selected account', function () {
const account = selectors.getSelectedAccount(mockState)
assert.equal(account.balance, '0x0')
assert.equal(account.address, '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc')
})
it('returns selected token from first token list', function () {
const token = selectors.getSelectedToken(mockState)
assert.equal(token.address, '0x108cf70c7d384c552f42c07c41c0e1e46d77ea0d')
assert.equal(token.symbol, 'TEST')
assert.equal(token.decimals, '0')
})
describe('#getSelectedTokenExchangeRate', function () {
it('returns token exchange rate for first token', function () {
const tokenRate = selectors.getSelectedTokenExchangeRate(mockState)
assert.equal(tokenRate, '0.00039345803819379796')
})
})
describe('#getTokenExchangeRate', function () {
let missingTokenRate
beforeEach(function () {
missingTokenRate = {
metamask: {
'contractExchangeRates': {},
},
}
})
it('returns 0 token exchange rate for a token not in state', function () {
const tokenRate = selectors.getTokenExchangeRate(missingTokenRate, '0xd8f6a2ffb0fc5952d16c9768b71cfd35b6399aa5')
assert.equal(tokenRate, 0)
})
it('returns token exchange rate for specified token in state', function () {
const tokenRate = selectors.getTokenExchangeRate(mockState, '0xd8f6a2ffb0fc5952d16c9768b71cfd35b6399aa5')
assert.equal(tokenRate, 0.00008189274407698049)
})
})
it('returns conversionRate from state', function () {
assert.equal(selectors.conversionRateSelector(mockState), 556.12)
})
it('returns address book from state', function () {
const addressBook = selectors.getAddressBook(mockState)
assert.equal(addressBook[0].address, '0xc42edfcc21ed14dda456aa0756c153f7985d8813')
assert.equal(addressBook[0].name, '')
})
it('returns accounts with balance, address, and name from identity and accounts in state', function () {
const accountsWithSendEther = selectors.accountsWithSendEtherInfoSelector(mockState)
assert.equal(accountsWithSendEther.length, 2)
assert.equal(accountsWithSendEther[0].balance, '0x0')
assert.equal(accountsWithSendEther[0].address, '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc')
assert.equal(accountsWithSendEther[0].name, 'Test Account')
})
it('returns selected account with balance, address, and name from accountsWithSendEtherInfoSelector', function () {
const currentAccountwithSendEther = selectors.getCurrentAccountWithSendEtherInfo(mockState)
assert.equal(currentAccountwithSendEther.balance, '0x0')
assert.equal(currentAccountwithSendEther.address, '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc')
assert.equal(currentAccountwithSendEther.name, 'Test Account')
})
describe('#transactionSelector', function () {
it('returns transactions from state', function () {
selectors.transactionsSelector(mockState)
})
})
it('#getGasIsLoading', () => {
const gasIsLoading = selectors.getGasIsLoading(mockState)
assert.equal(gasIsLoading, false)
})
describe('Send From', () => {
it('#getSendFrom', () => {
const sendFrom = selectors.getSendFrom(mockState)
assert.equal(sendFrom, '0xc42edfcc21ed14dda456aa0756c153f7985d8813')
})
it('#getForceGasMin', () => {
const forceGasMin = selectors.getForceGasMin(mockState)
assert.equal(forceGasMin, null)
})
it('#getSendAmount', () => {
const sendAmount = selectors.getSendAmount(mockState)
assert.equal(sendAmount, '1bc16d674ec80000')
})
it('#getSendMaxModeState', () => {
const sendMaxModeState = selectors.getSendMaxModeState(mockState)
assert.equal(sendMaxModeState, false)
})
})
it('#getCurrentCurrency', () => {
const currentCurrency = selectors.getCurrentCurrency(mockState)
assert.equal(currentCurrency, 'usd')
})
it('#getSelectedTokenToFiatRate', () => {
const selectedTokenToFiatRate = selectors.getSelectedTokenToFiatRate(mockState)
assert.equal(selectedTokenToFiatRate, '0.21880988420033493')
})
describe('#getSelectedTokenContract', () => {
beforeEach(() => {
global.eth = new Eth(provider)
})
it('', () => {
const selectedTokenContract = selectors.getSelectedTokenContract(mockState)
assert(selectedTokenContract.abi)
})
})
it('#getCurrentViewContext', () => {
const currentViewContext = selectors.getCurrentViewContext(mockState)
assert.equal(currentViewContext, '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc')
})
it('#getTotalUnapprovedCount', () => {
const totalUnapprovedCount = selectors.getTotalUnapprovedCount(mockState)
assert.equal(totalUnapprovedCount, 1)
})
})

@ -0,0 +1,26 @@
const assert = require('assert')
const etherscanNetworkPrefix = require('../../../ui/lib/etherscan-prefix-for-network')
describe('Etherscan Network Prefix', () => {
it('returns empy string as default value', () => {
assert.equal(etherscanNetworkPrefix(), '')
})
it('returns empty string as a prefix for networkId of 1', () => {
assert.equal(etherscanNetworkPrefix(1), '')
})
it('returns ropsten as prefix for networkId of 3', () => {
assert.equal(etherscanNetworkPrefix(3), 'ropsten.')
})
it('returns rinkeby as prefix for networkId of 4', () => {
assert.equal(etherscanNetworkPrefix(4), 'rinkeby.')
})
it('returs kovan as prefix for networkId of 42', () => {
assert.equal(etherscanNetworkPrefix(42), 'kovan.')
})
})

@ -167,6 +167,7 @@ var actions = {
updateTransaction,
updateAndApproveTx,
cancelTx: cancelTx,
cancelTxs,
completedTx: completedTx,
txError: txError,
nextTx: nextTx,
@ -237,6 +238,7 @@ var actions = {
removeSuggestedTokens,
UPDATE_TOKENS: 'UPDATE_TOKENS',
setRpcTarget: setRpcTarget,
delRpcTarget: delRpcTarget,
setProviderType: setProviderType,
SET_HARDWARE_WALLET_DEFAULT_HD_PATH: 'SET_HARDWARE_WALLET_DEFAULT_HD_PATH',
setHardwareWalletDefaultHdPath,
@ -315,6 +317,8 @@ var actions = {
CLEAR_PENDING_TOKENS: 'CLEAR_PENDING_TOKENS',
setPendingTokens,
clearPendingTokens,
createCancelTransaction,
}
module.exports = actions
@ -413,7 +417,12 @@ function createNewVaultAndRestore (password, seed) {
log.debug(`background.createNewVaultAndRestore`)
return new Promise((resolve, reject) => {
background.createNewVaultAndRestore(password, seed, err => {
background.clearSeedWordCache((err) => {
if (err) {
return reject(err)
}
background.createNewVaultAndRestore(password, seed, (err) => {
if (err) {
return reject(err)
}
@ -421,6 +430,7 @@ function createNewVaultAndRestore (password, seed) {
resolve()
})
})
})
.then(() => dispatch(actions.unMarkPasswordForgotten()))
.then(() => {
dispatch(actions.showAccountsPage())
@ -908,6 +918,7 @@ function updateGasData ({
selectedToken,
to,
value,
data,
}) {
return (dispatch) => {
dispatch(actions.gasLoadingStarted())
@ -928,6 +939,7 @@ function updateGasData ({
to,
value,
estimateGasPrice,
data,
}),
])
})
@ -1291,6 +1303,47 @@ function cancelTx (txData) {
}
}
/**
* Cancels all of the given transactions
* @param {Array<object>} txDataList a list of tx data objects
* @return {function(*): Promise<void>}
*/
function cancelTxs (txDataList) {
return async (dispatch, getState) => {
dispatch(actions.showLoadingIndication())
const txIds = txDataList.map(({id}) => id)
const cancellations = txIds.map((id) => new Promise((resolve, reject) => {
background.cancelTransaction(id, (err) => {
if (err) {
return reject(err)
}
resolve()
})
}))
await Promise.all(cancellations)
const newState = await updateMetamaskStateFromBackground()
dispatch(actions.updateMetamaskState(newState))
dispatch(actions.clearSend())
txIds.forEach((id) => {
dispatch(actions.completedTx(id))
})
dispatch(actions.hideLoadingIndication())
if (global.METAMASK_UI_TYPE === ENVIRONMENT_TYPE_NOTIFICATION) {
return global.platform.closeCurrentWindow()
}
}
}
/**
* @deprecated
* @param {Array<object>} txsData
* @return {Function}
*/
function cancelAllTx (txsData) {
return (dispatch) => {
txsData.forEach((txData, i) => {
@ -1709,7 +1762,7 @@ function markNoticeRead (notice) {
background.markNoticeRead(notice, (err, notice) => {
dispatch(actions.hideLoadingIndication())
if (err) {
dispatch(actions.displayWarning(err))
dispatch(actions.displayWarning(err.message))
return reject(err)
}
@ -1766,6 +1819,29 @@ function retryTransaction (txId) {
}
}
function createCancelTransaction (txId, customGasPrice) {
log.debug('background.cancelTransaction')
let newTxId
return dispatch => {
return new Promise((resolve, reject) => {
background.createCancelTransaction(txId, customGasPrice, (err, newState) => {
if (err) {
dispatch(actions.displayWarning(err.message))
reject(err)
}
const { selectedAddressTxList } = newState
const { id } = selectedAddressTxList[selectedAddressTxList.length - 1]
newTxId = id
resolve(newState)
})
})
.then(newState => dispatch(actions.updateMetamaskState(newState)))
.then(() => newTxId)
}
}
//
// config
//
@ -1776,7 +1852,7 @@ function setProviderType (type) {
background.setProviderType(type, (err, result) => {
if (err) {
log.error(err)
return dispatch(self.displayWarning('Had a problem changing networks!'))
return dispatch(actions.displayWarning('Had a problem changing networks!'))
}
dispatch(actions.updateProviderType(type))
dispatch(actions.setSelectedToken())
@ -1798,7 +1874,20 @@ function setRpcTarget (newRpc) {
background.setCustomRpc(newRpc, (err, result) => {
if (err) {
log.error(err)
return dispatch(self.displayWarning('Had a problem changing networks!'))
return dispatch(actions.displayWarning('Had a problem changing networks!'))
}
dispatch(actions.setSelectedToken())
})
}
}
function delRpcTarget (oldRpc) {
return (dispatch) => {
log.debug(`background.delRpcTarget: ${oldRpc}`)
background.delCustomRpc(oldRpc, (err, result) => {
if (err) {
log.error(err)
return dispatch(self.displayWarning('Had a problem removing network!'))
}
dispatch(actions.setSelectedToken())
})
@ -2220,6 +2309,10 @@ function updateNetworkNonce (address) {
return (dispatch) => {
return new Promise((resolve, reject) => {
global.ethQuery.getTransactionCount(address, (err, data) => {
if (err) {
dispatch(actions.displayWarning(err.message))
return reject(err)
}
dispatch(setNetworkNonce(data))
resolve(data)
})
@ -2307,7 +2400,7 @@ function setUseBlockie (val) {
function updateCurrentLocale (key) {
return (dispatch) => {
dispatch(actions.showLoadingIndication())
fetchLocale(key)
return fetchLocale(key)
.then((localeMessages) => {
log.debug(`background.setCurrentLocale`)
background.setCurrentLocale(key, (err) => {

@ -19,9 +19,9 @@ const Sidebar = require('./components/sidebars').default
// other views
import Home from './components/pages/home'
import Settings from './components/pages/settings'
const Authenticated = require('./components/pages/authenticated')
const Initialized = require('./components/pages/initialized')
const Settings = require('./components/pages/settings')
const RestoreVaultPage = require('./components/pages/keychains/restore-vault').default
const RevealSeedConfirmation = require('./components/pages/keychains/reveal-seed')
const AddTokenPage = require('./components/pages/add-token')
@ -152,12 +152,14 @@ class App extends Component {
h(AccountMenu),
h('div.main-container-wrapper', [
(isLoading || isLoadingNetwork) && h(Loading, {
loadingMessage: loadMessage,
}),
// content
this.renderRoutes(),
]),
])
)
}

@ -6,6 +6,7 @@ const CLASSNAME_DEFAULT = 'btn-default'
const CLASSNAME_PRIMARY = 'btn-primary'
const CLASSNAME_SECONDARY = 'btn-secondary'
const CLASSNAME_CONFIRM = 'btn-confirm'
const CLASSNAME_RAISED = 'btn-raised'
const CLASSNAME_LARGE = 'btn--large'
const typeHash = {
@ -13,6 +14,7 @@ const typeHash = {
primary: CLASSNAME_PRIMARY,
secondary: CLASSNAME_SECONDARY,
confirm: CLASSNAME_CONFIRM,
raised: CLASSNAME_RAISED,
}
export default class Button extends Component {
@ -20,7 +22,7 @@ export default class Button extends Component {
type: PropTypes.string,
large: PropTypes.bool,
className: PropTypes.string,
children: PropTypes.string,
children: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
}
render () {
@ -29,6 +31,7 @@ export default class Button extends Component {
return (
<button
className={classnames(
'button',
typeHash[type],
large && CLASSNAME_LARGE,
className

@ -0,0 +1,25 @@
import React, { PureComponent } from 'react'
import PropTypes from 'prop-types'
import classnames from 'classnames'
export default class Card extends PureComponent {
static propTypes = {
className: PropTypes.string,
overrideClassName: PropTypes.bool,
title: PropTypes.string,
children: PropTypes.node,
}
render () {
const { className, overrideClassName, title } = this.props
return (
<div className={classnames({ 'card': !overrideClassName }, className)}>
<div className="card__title">
{ title }
</div>
{ this.props.children }
</div>
)
}
}

@ -0,0 +1 @@
export { default } from './card.component'

@ -0,0 +1,11 @@
.card {
border-radius: 4px;
box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.08);
padding: 8px;
&__title {
border-bottom: 1px solid #d8d8d8;
padding-bottom: 4px;
text-transform: capitalize;
}
}

@ -0,0 +1,25 @@
import React from 'react'
import assert from 'assert'
import { shallow } from 'enzyme'
import Card from '../card.component'
describe('Card Component', () => {
it('should render a card with a title and child element', () => {
const wrapper = shallow(
<Card
title="Test"
className="card-test-class"
>
<div className="child-test-class">Child</div>
</Card>
)
assert.ok(wrapper.hasClass('card-test-class'))
const title = wrapper.find('.card__title')
assert.ok(title)
assert.equal(title.text(), 'Test')
const child = wrapper.find('.child-test-class')
assert.ok(child)
assert.equal(child.text(), 'Child')
})
})

@ -2,11 +2,8 @@ import React, { Component } from 'react'
import PropTypes from 'prop-types'
import classnames from 'classnames'
import { Tabs, Tab } from '../../tabs'
import {
ConfirmPageContainerSummary,
ConfirmPageContainerError,
ConfirmPageContainerWarning,
} from './'
import { ConfirmPageContainerSummary, ConfirmPageContainerWarning } from './'
import ErrorMessage from '../../error-message'
export default class ConfirmPageContainerContent extends Component {
static propTypes = {
@ -95,7 +92,7 @@ export default class ConfirmPageContainerContent extends Component {
{
(errorKey || errorMessage) && (
<div className="confirm-page-container-content__error-container">
<ConfirmPageContainerError
<ErrorMessage
errorMessage={errorMessage}
errorKey={errorKey}
/>

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save