Merge remote-tracking branch 'upstream/develop' into HEAD
This commit is contained in:
commit
595447ccac
|
@ -30,6 +30,15 @@ workflows:
|
|||
- prep-deps-npm
|
||||
- prep-deps-firefox
|
||||
- prep-build
|
||||
- test-e2e-beta-chrome:
|
||||
requires:
|
||||
- prep-deps-npm
|
||||
- prep-build
|
||||
- test-e2e-beta-firefox:
|
||||
requires:
|
||||
- prep-deps-npm
|
||||
- prep-deps-firefox
|
||||
- prep-build
|
||||
- test-unit:
|
||||
requires:
|
||||
- prep-deps-npm
|
||||
|
@ -57,6 +66,8 @@ workflows:
|
|||
- test-unit
|
||||
- test-e2e-chrome
|
||||
- test-e2e-firefox
|
||||
- test-e2e-beta-chrome
|
||||
- test-e2e-beta-firefox
|
||||
- test-integration-mascara-chrome
|
||||
- test-integration-mascara-firefox
|
||||
- test-integration-flat-chrome
|
||||
|
@ -86,7 +97,7 @@ workflows:
|
|||
jobs:
|
||||
prep-deps-npm:
|
||||
docker:
|
||||
- image: circleci/node:8-browsers
|
||||
- image: circleci/node:8.11.3-browsers
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
|
@ -105,14 +116,12 @@ jobs:
|
|||
|
||||
prep-deps-firefox:
|
||||
docker:
|
||||
- image: circleci/node:8-browsers
|
||||
- image: circleci/node:8.11.3-browsers
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Download Firefox
|
||||
command: >
|
||||
wget https://ftp.mozilla.org/pub/firefox/releases/58.0/linux-x86_64/en-US/firefox-58.0.tar.bz2
|
||||
&& tar xjf firefox-58.0.tar.bz2
|
||||
command: ./.circleci/scripts/firefox-download.sh
|
||||
- save_cache:
|
||||
key: dependency-cache-firefox-{{ .Revision }}
|
||||
paths:
|
||||
|
@ -120,7 +129,7 @@ jobs:
|
|||
|
||||
prep-build:
|
||||
docker:
|
||||
- image: circleci/node:8-browsers
|
||||
- image: circleci/node:8.11.3-browsers
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
|
@ -139,7 +148,7 @@ jobs:
|
|||
|
||||
prep-docs:
|
||||
docker:
|
||||
- image: circleci/node:8-browsers
|
||||
- image: circleci/node:8.11.3-browsers
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
|
@ -154,7 +163,7 @@ jobs:
|
|||
|
||||
prep-scss:
|
||||
docker:
|
||||
- image: circleci/node:8-browsers
|
||||
- image: circleci/node:8.11.3-browsers
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
|
@ -173,7 +182,7 @@ jobs:
|
|||
|
||||
test-lint:
|
||||
docker:
|
||||
- image: circleci/node:8-browsers
|
||||
- image: circleci/node:8.11.3-browsers
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
|
@ -184,7 +193,7 @@ jobs:
|
|||
|
||||
test-deps:
|
||||
docker:
|
||||
- image: circleci/node:8-browsers
|
||||
- image: circleci/node:8.11.3-browsers
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
|
@ -195,7 +204,7 @@ jobs:
|
|||
|
||||
test-e2e-chrome:
|
||||
docker:
|
||||
- image: circleci/node:8-browsers
|
||||
- image: circleci/node:8.11.3-browsers
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
|
@ -203,28 +212,22 @@ jobs:
|
|||
- restore_cache:
|
||||
key: build-cache-{{ .Revision }}
|
||||
- run:
|
||||
name: Test
|
||||
name: test:e2e:chrome
|
||||
command: npm run test:e2e:chrome
|
||||
- store_artifacts:
|
||||
path: test-artifacts
|
||||
destination: test-artifacts
|
||||
|
||||
test-e2e-firefox:
|
||||
environment:
|
||||
browsers: '["Firefox"]'
|
||||
docker:
|
||||
- image: circleci/node:8-browsers
|
||||
- image: circleci/node:8.11.3-browsers
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
key: dependency-cache-firefox-{{ .Revision }}
|
||||
- run:
|
||||
name: Install firefox
|
||||
command: >
|
||||
sudo rm -r /opt/firefox
|
||||
&& sudo mv firefox /opt/firefox58
|
||||
&& sudo mv /usr/bin/firefox /usr/bin/firefox-old
|
||||
&& sudo ln -s /opt/firefox58/firefox /usr/bin/firefox
|
||||
command: ./.circleci/scripts/firefox-install.sh
|
||||
- restore_cache:
|
||||
key: dependency-cache-{{ .Revision }}
|
||||
- restore_cache:
|
||||
|
@ -236,9 +239,46 @@ jobs:
|
|||
path: test-artifacts
|
||||
destination: test-artifacts
|
||||
|
||||
test-e2e-beta-chrome:
|
||||
docker:
|
||||
- image: circleci/node:8.11.3-browsers
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
key: dependency-cache-{{ .Revision }}
|
||||
- restore_cache:
|
||||
key: build-cache-{{ .Revision }}
|
||||
- run:
|
||||
name: test:e2e:chrome:beta
|
||||
command: npm run test:e2e:chrome:beta
|
||||
- store_artifacts:
|
||||
path: test-artifacts
|
||||
destination: test-artifacts
|
||||
|
||||
test-e2e-beta-firefox:
|
||||
docker:
|
||||
- image: circleci/node:8.11.3-browsers
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
key: dependency-cache-firefox-{{ .Revision }}
|
||||
- run:
|
||||
name: Install firefox
|
||||
command: ./.circleci/scripts/firefox-install.sh
|
||||
- restore_cache:
|
||||
key: dependency-cache-{{ .Revision }}
|
||||
- restore_cache:
|
||||
key: build-cache-{{ .Revision }}
|
||||
- run:
|
||||
name: test:e2e:firefox:beta
|
||||
command: npm run test:e2e:firefox:beta
|
||||
- store_artifacts:
|
||||
path: test-artifacts
|
||||
destination: test-artifacts
|
||||
|
||||
job-screens:
|
||||
docker:
|
||||
- image: circleci/node:8-browsers
|
||||
- image: circleci/node:8.11.3-browsers
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
|
@ -255,7 +295,7 @@ jobs:
|
|||
|
||||
job-publish-prerelease:
|
||||
docker:
|
||||
- image: circleci/node:8-browsers
|
||||
- image: circleci/node:8.11.3-browsers
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
|
@ -282,7 +322,7 @@ jobs:
|
|||
|
||||
job-publish-release:
|
||||
docker:
|
||||
- image: circleci/node:8-browsers
|
||||
- image: circleci/node:8.11.3-browsers
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
|
@ -305,7 +345,7 @@ jobs:
|
|||
|
||||
test-unit:
|
||||
docker:
|
||||
- image: circleci/node:8-browsers
|
||||
- image: circleci/node:8.11.3-browsers
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
|
@ -318,18 +358,14 @@ jobs:
|
|||
environment:
|
||||
browsers: '["Firefox"]'
|
||||
docker:
|
||||
- image: circleci/node:8-browsers
|
||||
- image: circleci/node:8.11.3-browsers
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
key: dependency-cache-firefox-{{ .Revision }}
|
||||
- run:
|
||||
name: Install firefox
|
||||
command: >
|
||||
sudo rm -r /opt/firefox
|
||||
&& sudo mv firefox /opt/firefox58
|
||||
&& sudo mv /usr/bin/firefox /usr/bin/firefox-old
|
||||
&& sudo ln -s /opt/firefox58/firefox /usr/bin/firefox
|
||||
command: ./.circleci/scripts/firefox-install.sh
|
||||
- restore_cache:
|
||||
key: dependency-cache-{{ .Revision }}
|
||||
- run:
|
||||
|
@ -346,7 +382,7 @@ jobs:
|
|||
environment:
|
||||
browsers: '["Chrome"]'
|
||||
docker:
|
||||
- image: circleci/node:8-browsers
|
||||
- image: circleci/node:8.11.3-browsers
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
|
@ -365,18 +401,14 @@ jobs:
|
|||
environment:
|
||||
browsers: '["Firefox"]'
|
||||
docker:
|
||||
- image: circleci/node:8-browsers
|
||||
- image: circleci/node:8.11.3-browsers
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
key: dependency-cache-firefox-{{ .Revision }}
|
||||
- run:
|
||||
name: Install firefox
|
||||
command: >
|
||||
sudo rm -r /opt/firefox
|
||||
&& sudo mv firefox /opt/firefox58
|
||||
&& sudo mv /usr/bin/firefox /usr/bin/firefox-old
|
||||
&& sudo ln -s /opt/firefox58/firefox /usr/bin/firefox
|
||||
command: ./.circleci/scripts/firefox-install.sh
|
||||
- restore_cache:
|
||||
key: dependency-cache-{{ .Revision }}
|
||||
- run:
|
||||
|
@ -393,7 +425,7 @@ jobs:
|
|||
environment:
|
||||
browsers: '["Chrome"]'
|
||||
docker:
|
||||
- image: circleci/node:8-browsers
|
||||
- image: circleci/node:8.11.3-browsers
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
|
@ -410,9 +442,8 @@ jobs:
|
|||
|
||||
all-tests-pass:
|
||||
docker:
|
||||
- image: circleci/node:8-browsers
|
||||
- image: circleci/node:8.11.3-browsers
|
||||
steps:
|
||||
- run:
|
||||
name: All Tests Passed
|
||||
command: echo 'weew - everything passed!'
|
||||
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
echo "Downloading firefox..."
|
||||
wget https://ftp.mozilla.org/pub/firefox/releases/58.0/linux-x86_64/en-US/firefox-58.0.tar.bz2 \
|
||||
&& tar xjf firefox-58.0.tar.bz2
|
||||
echo "firefox download complete"
|
|
@ -0,0 +1,8 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
echo "Installing firefox..."
|
||||
sudo rm -r /opt/firefox
|
||||
sudo mv firefox /opt/firefox58
|
||||
sudo mv /usr/bin/firefox /usr/bin/firefox-old
|
||||
sudo ln -s /opt/firefox58/firefox /usr/bin/firefox
|
||||
echo "Firefox installed."
|
|
@ -1,6 +1,20 @@
|
|||
node_modules/**
|
||||
dist/**
|
||||
builds/**
|
||||
docs/**
|
||||
|
||||
development/bundle.js
|
||||
development/states.js
|
||||
|
||||
app/scripts/lib/extension-instance.js
|
||||
app/scripts/chromereload.js
|
||||
|
||||
ui/lib/blockies.js
|
||||
|
||||
mascara/src/app/first-time/spinner.js
|
||||
mascara/test/jquery-3.1.0.min.js
|
||||
|
||||
test/integration/bundle.js
|
||||
test/integration/jquery-3.1.0.min.js
|
||||
test/integration/helpers.js
|
||||
test/integration/lib/first-time.js
|
||||
ui/lib/blockies.js
|
|
@ -37,7 +37,9 @@
|
|||
"document": false,
|
||||
"navigator": false,
|
||||
"web3": true,
|
||||
"window": false
|
||||
"window": false,
|
||||
"$": false,
|
||||
"QUnit": false
|
||||
},
|
||||
|
||||
"rules": {
|
||||
|
@ -142,6 +144,7 @@
|
|||
"operator-linebreak": [1, "after", { "overrides": { "?": "ignore", ":": "ignore" } }],
|
||||
"padded-blocks": "off",
|
||||
"quotes": [2, "single", {"avoidEscape": true, "allowTemplateLiterals": true}],
|
||||
"react/no-deprecated": 0,
|
||||
"semi": [2, "never"],
|
||||
"semi-spacing": [2, { "before": false, "after": true }],
|
||||
"space-before-blocks": [1, "always"],
|
||||
|
@ -158,5 +161,6 @@
|
|||
"yield-star-spacing": [2, "both"],
|
||||
"yoda": [2, "never"],
|
||||
"prefer-const": 1,
|
||||
"mocha/no-exclusive-tests": "error"
|
||||
}
|
||||
}
|
||||
|
|
36
CHANGELOG.md
36
CHANGELOG.md
|
@ -2,6 +2,42 @@
|
|||
|
||||
## Current Master
|
||||
|
||||
## 4.8.0 Thur Jun 14 2018
|
||||
|
||||
- [#4513](https://github.com/MetaMask/metamask-extension/pull/4513): Attempting to import an empty private key will now show a clear error.
|
||||
- [#4570](https://github.com/MetaMask/metamask-extension/pull/4570): Fix bug where metamask data would stop being written to disk after prolonged use.
|
||||
- [#4523](https://github.com/MetaMask/metamask-extension/pull/4523): Fix bug where account reset did not work with custom RPC providers.
|
||||
- [#4524](https://github.com/MetaMask/metamask-extension/pull/4524): Fix for Brave i18n getAcceptLanguages.
|
||||
- [#4557](https://github.com/MetaMask/metamask-extension/pull/4557): Fix bug where nonce mutex was never released.
|
||||
- [#4566](https://github.com/MetaMask/metamask-extension/pull/4566): Add phishing notice.
|
||||
- [#4591](https://github.com/MetaMask/metamask-extension/pull/4591): Allow Copying Token Addresses and link to Token on Etherscan.
|
||||
|
||||
## 4.7.4 Tue Jun 05 2018
|
||||
|
||||
- Add diagnostic reporting for users with multiple HD keyrings
|
||||
- Throw explicit error when selected account is unset
|
||||
|
||||
## 4.7.3 Mon Jun 04 2018
|
||||
|
||||
- Hide token now uses new modal
|
||||
- Indicate the current selected account on the popup account view
|
||||
- Reduce height of notice container in onboarding
|
||||
- Fixes issue where old nicknames were kept around causing errors
|
||||
|
||||
## 4.7.2 Sun Jun 03 2018
|
||||
|
||||
- Fix bug preventing users from logging in. Internally accounts and identities were out of sync.
|
||||
- Fix support links to point to new support system (Zendesk)
|
||||
- Fix bug in migration #26 ( moving account nicknames to preferences )
|
||||
- Clears account nicknames on restore from seedPhrase
|
||||
|
||||
## 4.7.1 Fri Jun 01 2018
|
||||
|
||||
- Fix bug where errors were not returned to Dapps.
|
||||
|
||||
## 4.7.0 Wed May 30 2018
|
||||
|
||||
- Fix Brave support
|
||||
- Adds error messages when passwords don't match in onboarding flow.
|
||||
- Adds modal notification if a retry in the process of being confirmed is dropped.
|
||||
- New unlock screen design.
|
||||
|
|
1
LICENSE
1
LICENSE
|
@ -18,3 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
|
|
@ -27,7 +27,9 @@ If you're a web dapp developer, we've got two types of guides for you:
|
|||
## Building locally
|
||||
|
||||
- Install [Node.js](https://nodejs.org/en/) version 6.3.1 or later.
|
||||
- Install local dependencies with `npm install`.
|
||||
- Install dependencies:
|
||||
- For node versions up to and including 9, install local dependencies with `npm install`.
|
||||
- For node versions 10 and later, install [Yarn](https://yarnpkg.com/lang/en/docs/install/) and use `yarn install`.
|
||||
- Install gulp globally with `npm install -g gulp-cli`.
|
||||
- Build the project to the `./dist/` folder with `gulp build`.
|
||||
- Optionally, to rebuild on file changes, run `gulp dev`.
|
||||
|
|
|
@ -146,6 +146,9 @@
|
|||
"copy": {
|
||||
"message": "Copy"
|
||||
},
|
||||
"copyContractAddress": {
|
||||
"message": "Copy Contract Address"
|
||||
},
|
||||
"copyToClipboard": {
|
||||
"message": "Copy to clipboard"
|
||||
},
|
||||
|
@ -253,12 +256,18 @@
|
|||
"editAccountName": {
|
||||
"message": "Edit Account Name"
|
||||
},
|
||||
"editingTransaction": {
|
||||
"message": "Make changes to your transaction"
|
||||
},
|
||||
"emailUs": {
|
||||
"message": "Email us!"
|
||||
},
|
||||
"encryptNewDen": {
|
||||
"message": "Encrypt your new DEN"
|
||||
},
|
||||
"ensNameNotFound": {
|
||||
"message": "ENS name not found"
|
||||
},
|
||||
"enterPassword": {
|
||||
"message": "Enter password"
|
||||
},
|
||||
|
@ -771,6 +780,10 @@
|
|||
"onlySendToEtherAddress": {
|
||||
"message": "Only send ETH to an Ethereum address."
|
||||
},
|
||||
"onlySendTokensToAccountAddress": {
|
||||
"message": "Only send $1 to an Ethereum account address.",
|
||||
"description": "displays token symbol"
|
||||
},
|
||||
"searchTokens": {
|
||||
"message": "Search Tokens"
|
||||
},
|
||||
|
@ -948,6 +961,9 @@
|
|||
"viewAccount": {
|
||||
"message": "View Account"
|
||||
},
|
||||
"viewOnEtherscan": {
|
||||
"message": "View on Etherscan"
|
||||
},
|
||||
"visitWebSite": {
|
||||
"message": "Visit our web site"
|
||||
},
|
||||
|
|
|
@ -62,6 +62,9 @@
|
|||
"message": " $1以上 $2以下にして下さい。",
|
||||
"description": "helper for inputting hex as decimal input"
|
||||
},
|
||||
"blockiesIdenticon": {
|
||||
"message": "Blockies Identicon を使用"
|
||||
},
|
||||
"borrowDharma": {
|
||||
"message": "Dharmaで借りる(ベータ版)"
|
||||
},
|
||||
|
@ -95,6 +98,9 @@
|
|||
"confirmTransaction": {
|
||||
"message": "トランザクションの確認"
|
||||
},
|
||||
"continue": {
|
||||
"message": "続行"
|
||||
},
|
||||
"continueToCoinbase": {
|
||||
"message": "Coinbaseを開く"
|
||||
},
|
||||
|
@ -359,6 +365,9 @@
|
|||
"likeToAddTokens": {
|
||||
"message": "トークンを追加しますか?"
|
||||
},
|
||||
"links": {
|
||||
"message": "リンク"
|
||||
},
|
||||
"limit": {
|
||||
"message": "リミット"
|
||||
},
|
||||
|
@ -371,12 +380,18 @@
|
|||
"localhost": {
|
||||
"message": "Localhost 8545"
|
||||
},
|
||||
"login": {
|
||||
"message": "ログイン"
|
||||
},
|
||||
"logout": {
|
||||
"message": "ログアウト"
|
||||
},
|
||||
"loose": {
|
||||
"message": "外部秘密鍵"
|
||||
},
|
||||
"max": {
|
||||
"message": "最大"
|
||||
},
|
||||
"mainnet": {
|
||||
"message": "Ethereumメインネットワーク"
|
||||
},
|
||||
|
@ -417,7 +432,7 @@
|
|||
"message": "新規コントラクト"
|
||||
},
|
||||
"newPassword": {
|
||||
"message": "新規パスワード(最低8文字)"
|
||||
"message": "新規パスワード(最低8文字)"
|
||||
},
|
||||
"newRecipient": {
|
||||
"message": "新規受取人"
|
||||
|
@ -453,6 +468,9 @@
|
|||
"message": "または",
|
||||
"description": "choice between creating or importing a new account"
|
||||
},
|
||||
"password": {
|
||||
"message": "パスワード"
|
||||
},
|
||||
"passwordMismatch": {
|
||||
"message": "パスワードが一致しません。",
|
||||
"description": "in password creation process, the two new password fields did not match"
|
||||
|
@ -474,6 +492,9 @@
|
|||
"popularTokens": {
|
||||
"message": "人気のトークン"
|
||||
},
|
||||
"privacyMsg": {
|
||||
"message": "プライバシーポリシー"
|
||||
},
|
||||
"privateKey": {
|
||||
"message": "秘密鍵",
|
||||
"description": "select this type of file to use to import an account"
|
||||
|
@ -546,6 +567,12 @@
|
|||
"message": "ファイルとして保存",
|
||||
"description": "Account export process"
|
||||
},
|
||||
"search": {
|
||||
"message": "検索"
|
||||
},
|
||||
"searchResults": {
|
||||
"message": "検索結果"
|
||||
},
|
||||
"selectService": {
|
||||
"message": "サービスを選択"
|
||||
},
|
||||
|
@ -575,7 +602,7 @@
|
|||
},
|
||||
"info": {
|
||||
"message": "情報"
|
||||
},
|
||||
},
|
||||
"shapeshiftBuy": {
|
||||
"message": "Shapeshiftで交換"
|
||||
},
|
||||
|
@ -609,6 +636,9 @@
|
|||
"takesTooLong": {
|
||||
"message": "送信に時間がかかりますか?"
|
||||
},
|
||||
"terms": {
|
||||
"message": "利用規約"
|
||||
},
|
||||
"testFaucet": {
|
||||
"message": "Faucetをテスト"
|
||||
},
|
||||
|
@ -619,6 +649,9 @@
|
|||
"message": "ShapeShiftで $1をETHにする",
|
||||
"description": "system will fill in deposit type in start of message"
|
||||
},
|
||||
"token": {
|
||||
"message": "トークン"
|
||||
},
|
||||
"tokenAddress": {
|
||||
"message": "トークンアドレス"
|
||||
},
|
||||
|
@ -690,6 +723,12 @@
|
|||
"warning": {
|
||||
"message": "警告"
|
||||
},
|
||||
"welcomeBack": {
|
||||
"message": "おかえりなさい!"
|
||||
},
|
||||
"welcomeBeta": {
|
||||
"message": "MetaMask ベータ版へようこそ!"
|
||||
},
|
||||
"whatsThis": {
|
||||
"message": "この機能について"
|
||||
},
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "__MSG_appName__",
|
||||
"short_name": "__MSG_appName__",
|
||||
"version": "4.6.1",
|
||||
"version": "4.8.0",
|
||||
"manifest_version": 2,
|
||||
"author": "https://metamask.io",
|
||||
"description": "__MSG_appDescription__",
|
||||
|
@ -71,6 +71,8 @@
|
|||
"matches": [
|
||||
"https://metamask.io/*"
|
||||
],
|
||||
"ids": ["*"]
|
||||
"ids": [
|
||||
"*"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,18 @@ const accountImporter = {
|
|||
|
||||
strategies: {
|
||||
'Private Key': (privateKey) => {
|
||||
const stripped = ethUtil.stripHexPrefix(privateKey)
|
||||
if (!privateKey) {
|
||||
throw new Error('Cannot import an empty key.')
|
||||
}
|
||||
|
||||
const prefixed = ethUtil.addHexPrefix(privateKey)
|
||||
const buffer = ethUtil.toBuffer(prefixed)
|
||||
|
||||
if (!ethUtil.isValidPrivate(buffer)) {
|
||||
throw new Error('Cannot import invalid private key.')
|
||||
}
|
||||
|
||||
const stripped = ethUtil.stripHexPrefix(prefixed)
|
||||
return stripped
|
||||
},
|
||||
'JSON File': (input, password) => {
|
||||
|
|
|
@ -16,6 +16,7 @@ const ExtensionPlatform = require('./platforms/extension')
|
|||
const Migrator = require('./lib/migrator/')
|
||||
const migrations = require('./migrations/')
|
||||
const PortStream = require('./lib/port-stream.js')
|
||||
const createStreamSink = require('./lib/createStreamSink')
|
||||
const NotificationManager = require('./lib/notification-manager.js')
|
||||
const MetamaskController = require('./metamask-controller')
|
||||
const firstTimeState = require('./first-time-state')
|
||||
|
@ -279,7 +280,7 @@ function setupController (initState, initLangCode) {
|
|||
asStream(controller.store),
|
||||
debounce(1000),
|
||||
storeTransform(versionifyData),
|
||||
storeTransform(persistData),
|
||||
createStreamSink(persistData),
|
||||
(error) => {
|
||||
log.error('MetaMask - Persistence pipeline failed', error)
|
||||
}
|
||||
|
@ -295,7 +296,7 @@ function setupController (initState, initLangCode) {
|
|||
return versionedData
|
||||
}
|
||||
|
||||
function persistData (state) {
|
||||
async function persistData (state) {
|
||||
if (!state) {
|
||||
throw new Error('MetaMask - updated state is missing', state)
|
||||
}
|
||||
|
@ -303,12 +304,13 @@ function setupController (initState, initLangCode) {
|
|||
throw new Error('MetaMask - updated state does not have data', state)
|
||||
}
|
||||
if (localStore.isSupported) {
|
||||
localStore.set(state)
|
||||
.catch((err) => {
|
||||
try {
|
||||
await localStore.set(state)
|
||||
} catch (err) {
|
||||
// log error so we dont break the pipeline
|
||||
log.error('error setting state in local store:', err)
|
||||
})
|
||||
}
|
||||
}
|
||||
return state
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -382,7 +384,7 @@ function setupController (initState, initLangCode) {
|
|||
}
|
||||
|
||||
// communication with page or other extension
|
||||
function connectExternal(remotePort) {
|
||||
function connectExternal (remotePort) {
|
||||
const originDomain = urlUtil.parse(remotePort.sender.url).hostname
|
||||
const portStream = new PortStream(remotePort)
|
||||
controller.setupUntrustedCommunication(portStream, originDomain)
|
||||
|
|
|
@ -115,8 +115,8 @@ function logStreamDisconnectWarning (remoteLabel, err) {
|
|||
* @returns {boolean} {@code true} if Web3 should be injected
|
||||
*/
|
||||
function shouldInjectWeb3 () {
|
||||
return doctypeCheck() && suffixCheck()
|
||||
&& documentElementCheck() && !blacklistedDomainCheck()
|
||||
return doctypeCheck() && suffixCheck() &&
|
||||
documentElementCheck() && !blacklistedDomainCheck()
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -176,6 +176,7 @@ function blacklistedDomainCheck () {
|
|||
'webbyawards.com',
|
||||
'cdn.shopify.com/s/javascripts/tricorder/xtld-read-only-frame.html',
|
||||
'adyen.com',
|
||||
'gravityforms.com',
|
||||
]
|
||||
var currentUrl = window.location.href
|
||||
var currentRegex
|
||||
|
|
|
@ -60,7 +60,7 @@ class BalanceController {
|
|||
* Sets up listeners and subscriptions which should trigger an update of ethBalance. These updates include:
|
||||
* - when a transaction changes state to 'submitted', 'confirmed' or 'failed'
|
||||
* - when the current account changes (i.e. a new account is selected)
|
||||
* - when there is a block update
|
||||
* - when there is a block update
|
||||
*
|
||||
* @private
|
||||
*
|
||||
|
@ -100,7 +100,7 @@ class BalanceController {
|
|||
|
||||
/**
|
||||
* Gets the pending transactions (i.e. those with a 'submitted' status). These are accessed from the
|
||||
* TransactionController passed to this BalanceController during construction.
|
||||
* TransactionController passed to this BalanceController during construction.
|
||||
*
|
||||
* @private
|
||||
* @returns {Promise<array>} Promises an array of transaction objects.
|
||||
|
|
|
@ -87,7 +87,7 @@ class BlacklistController {
|
|||
*
|
||||
* @private
|
||||
* @param {object} config A config object like that found at {@link https://github.com/MetaMask/eth-phishing-detect/blob/master/src/config.json}
|
||||
*
|
||||
*
|
||||
*/
|
||||
_setupPhishingDetector (config) {
|
||||
this._phishingDetector = new PhishingDetector(config)
|
||||
|
|
|
@ -18,7 +18,7 @@ class ComputedbalancesController {
|
|||
/**
|
||||
* Creates a new controller instance
|
||||
*
|
||||
* @param {ComputedBalancesOptions} [opts] Controller configuration parameters
|
||||
* @param {ComputedBalancesOptions} [opts] Controller configuration parameters
|
||||
*/
|
||||
constructor (opts = {}) {
|
||||
const { accountTracker, txController, blockTracker } = opts
|
||||
|
|
|
@ -16,9 +16,9 @@ class CurrencyController {
|
|||
* currentCurrency, conversionRate and conversionDate properties
|
||||
* @property {string} currentCurrency A 2-4 character shorthand that describes a specific currency, currently
|
||||
* selected by the user
|
||||
* @property {number} conversionRate The conversion rate from ETH to the selected currency.
|
||||
* @property {number} conversionRate The conversion rate from ETH to the selected currency.
|
||||
* @property {string} conversionDate The date at which the conversion rate was set. Expressed in in milliseconds
|
||||
* since midnight of January 1, 1970
|
||||
* since midnight of January 1, 1970
|
||||
* @property {number} conversionInterval The id of the interval created by the scheduleConversionInterval method.
|
||||
* Used to clear an existing interval on subsequent calls of that method.
|
||||
*
|
||||
|
@ -59,7 +59,7 @@ class CurrencyController {
|
|||
/**
|
||||
* A getter for the conversionRate property
|
||||
*
|
||||
* @returns {string} The conversion rate from ETH to the selected currency.
|
||||
* @returns {string} The conversion rate from ETH to the selected currency.
|
||||
*
|
||||
*/
|
||||
getConversionRate () {
|
||||
|
@ -80,7 +80,7 @@ class CurrencyController {
|
|||
* A getter for the conversionDate property
|
||||
*
|
||||
* @returns {string} The date at which the conversion rate was set. Expressed in milliseconds since midnight of
|
||||
* January 1, 1970
|
||||
* January 1, 1970
|
||||
*
|
||||
*/
|
||||
getConversionDate () {
|
||||
|
|
|
@ -89,14 +89,21 @@ module.exports = class NetworkController extends EventEmitter {
|
|||
type: 'rpc',
|
||||
rpcTarget,
|
||||
}
|
||||
this.providerStore.updateState(providerConfig)
|
||||
this._switchNetwork(providerConfig)
|
||||
this.providerConfig = providerConfig
|
||||
}
|
||||
|
||||
async setProviderType (type) {
|
||||
assert.notEqual(type, 'rpc', `NetworkController - cannot call "setProviderType" with type 'rpc'. use "setRpcTarget"`)
|
||||
assert(INFURA_PROVIDER_TYPES.includes(type) || type === LOCALHOST, `NetworkController - Unknown rpc type "${type}"`)
|
||||
const providerConfig = { type }
|
||||
this.providerConfig = providerConfig
|
||||
}
|
||||
|
||||
resetConnection () {
|
||||
this.providerConfig = this.getProviderConfig()
|
||||
}
|
||||
|
||||
set providerConfig (providerConfig) {
|
||||
this.providerStore.updateState(providerConfig)
|
||||
this._switchNetwork(providerConfig)
|
||||
}
|
||||
|
@ -125,7 +132,7 @@ module.exports = class NetworkController extends EventEmitter {
|
|||
} else if (type === LOCALHOST) {
|
||||
this._configureStandardProvider({ rpcUrl: LOCALHOST_RPC_URL })
|
||||
// url-based rpc endpoints
|
||||
} else if (type === 'rpc'){
|
||||
} else if (type === 'rpc') {
|
||||
this._configureStandardProvider({ rpcUrl: rpcTarget })
|
||||
} else {
|
||||
throw new Error(`NetworkController - _configureProvider - unknown type "${type}"`)
|
||||
|
|
|
@ -2,6 +2,7 @@ const ObservableStore = require('obs-store')
|
|||
const normalizeAddress = require('eth-sig-util').normalize
|
||||
const extend = require('xtend')
|
||||
|
||||
|
||||
class PreferencesController {
|
||||
|
||||
/**
|
||||
|
@ -28,7 +29,11 @@ class PreferencesController {
|
|||
featureFlags: {},
|
||||
currentLocale: opts.initLangCode,
|
||||
identities: {},
|
||||
lostIdentities: {},
|
||||
}, opts.initState)
|
||||
|
||||
this.diagnostics = opts.diagnostics
|
||||
|
||||
this.store = new ObservableStore(initState)
|
||||
}
|
||||
// PUBLIC METHODS
|
||||
|
@ -63,6 +68,13 @@ class PreferencesController {
|
|||
this.store.updateState({ currentLocale: key })
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates identities to only include specified addresses. Removes identities
|
||||
* not included in addresses array
|
||||
*
|
||||
* @param {string[]} addresses An array of hex addresses
|
||||
*
|
||||
*/
|
||||
setAddresses (addresses) {
|
||||
const oldIdentities = this.store.getState().identities
|
||||
const identities = addresses.reduce((ids, address, index) => {
|
||||
|
@ -73,6 +85,68 @@ class PreferencesController {
|
|||
this.store.updateState({ identities })
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds addresses to the identities object without removing identities
|
||||
*
|
||||
* @param {string[]} addresses An array of hex addresses
|
||||
*
|
||||
*/
|
||||
addAddresses (addresses) {
|
||||
const identities = this.store.getState().identities
|
||||
addresses.forEach((address) => {
|
||||
// skip if already exists
|
||||
if (identities[address]) return
|
||||
// add missing identity
|
||||
const identityCount = Object.keys(identities).length
|
||||
identities[address] = { name: `Account ${identityCount + 1}`, address }
|
||||
})
|
||||
this.store.updateState({ identities })
|
||||
}
|
||||
|
||||
/*
|
||||
* Synchronizes identity entries with known accounts.
|
||||
* Removes any unknown identities, and returns the resulting selected address.
|
||||
*
|
||||
* @param {Array<string>} addresses known to the vault.
|
||||
* @returns {Promise<string>} selectedAddress the selected address.
|
||||
*/
|
||||
syncAddresses (addresses) {
|
||||
const { identities, lostIdentities } = this.store.getState()
|
||||
|
||||
const newlyLost = {}
|
||||
Object.keys(identities).forEach((identity) => {
|
||||
if (!addresses.includes(identity)) {
|
||||
newlyLost[identity] = identities[identity]
|
||||
delete identities[identity]
|
||||
}
|
||||
})
|
||||
|
||||
// Identities are no longer present.
|
||||
if (Object.keys(newlyLost).length > 0) {
|
||||
|
||||
// Notify our servers:
|
||||
if (this.diagnostics) this.diagnostics.reportOrphans(newlyLost)
|
||||
|
||||
// store lost accounts
|
||||
for (const key in newlyLost) {
|
||||
lostIdentities[key] = newlyLost[key]
|
||||
}
|
||||
}
|
||||
|
||||
this.store.updateState({ identities, lostIdentities })
|
||||
this.addAddresses(addresses)
|
||||
|
||||
// If the selected account is no longer valid,
|
||||
// select an arbitrary other account:
|
||||
let selected = this.getSelectedAddress()
|
||||
if (!addresses.includes(selected)) {
|
||||
selected = addresses[0]
|
||||
this.setSelectedAddress(selected)
|
||||
}
|
||||
|
||||
return selected
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for the `selectedAddress` property
|
||||
*
|
||||
|
@ -111,7 +185,7 @@ class PreferencesController {
|
|||
/**
|
||||
* Adds a new token to the token array, or updates the token if passed an address that already exists.
|
||||
* Modifies the existing tokens array from the store. All objects in the tokens array array AddedToken objects.
|
||||
* @see AddedToken {@link AddedToken}
|
||||
* @see AddedToken {@link AddedToken}
|
||||
*
|
||||
* @param {string} rawAddress Hex address of the token contract. May or may not be a checksum address.
|
||||
* @param {string} symbol The symbol of the token
|
||||
|
@ -173,6 +247,7 @@ class PreferencesController {
|
|||
* @return {Promise<string>}
|
||||
*/
|
||||
setAccountLabel (account, label) {
|
||||
if (!account) throw new Error('setAccountLabel requires a valid address, got ' + String(account))
|
||||
const address = normalizeAddress(account)
|
||||
const {identities} = this.store.getState()
|
||||
identities[address] = identities[address] || {}
|
||||
|
@ -197,7 +272,7 @@ class PreferencesController {
|
|||
}
|
||||
|
||||
/**
|
||||
* Setter for the `currentAccountTab` property
|
||||
* Setter for the `currentAccountTab` property
|
||||
*
|
||||
* @param {string} currentAccountTab Specifies the new tab to be marked as current
|
||||
* @returns {Promise<void>} Promise resolves with undefined
|
||||
|
@ -215,7 +290,7 @@ class PreferencesController {
|
|||
* The returned list will have a max length of 2. If the _url currently exists it the list, it will be moved to the
|
||||
* 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 {string} _url The rpc url to add to the frequentRpcList.
|
||||
* @returns {Promise<array>} The updated frequentRpcList.
|
||||
*
|
||||
*/
|
||||
|
|
|
@ -117,7 +117,7 @@ class RecentBlocksController {
|
|||
*
|
||||
* @returns {Promise<void>} Promises undefined
|
||||
*/
|
||||
async backfill() {
|
||||
async backfill () {
|
||||
this.blockTracker.once('block', async (block) => {
|
||||
const currentBlockNumber = Number.parseInt(block.number, 16)
|
||||
const blocksToFetch = Math.min(currentBlockNumber, this.historyLength)
|
||||
|
|
|
@ -10,6 +10,7 @@ const NonceTracker = require('./nonce-tracker')
|
|||
const txUtils = require('./lib/util')
|
||||
const cleanErrorStack = require('../../lib/cleanErrorStack')
|
||||
const log = require('loglevel')
|
||||
const recipientBlacklistChecker = require('./lib/recipient-blacklist-checker')
|
||||
|
||||
/**
|
||||
Transaction Controller is an aggregate of sub-controllers and trackers
|
||||
|
@ -157,11 +158,14 @@ class TransactionController extends EventEmitter {
|
|||
let txMeta = this.txStateManager.generateTxMeta({ txParams: normalizedTxParams })
|
||||
this.addTx(txMeta)
|
||||
this.emit('newUnapprovedTx', txMeta)
|
||||
// add default tx params
|
||||
|
||||
try {
|
||||
// check whether recipient account is blacklisted
|
||||
recipientBlacklistChecker.checkAccount(txMeta.metamaskNetworkId, normalizedTxParams.to)
|
||||
// add default tx params
|
||||
txMeta = await this.addTxGasDefaults(txMeta)
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
log.warn(error)
|
||||
this.txStateManager.setTxStatusFailed(txMeta.id, error)
|
||||
throw error
|
||||
}
|
||||
|
@ -260,7 +264,12 @@ class TransactionController extends EventEmitter {
|
|||
// must set transaction to submitted/failed before releasing lock
|
||||
nonceLock.releaseLock()
|
||||
} catch (err) {
|
||||
this.txStateManager.setTxStatusFailed(txId, err)
|
||||
// this is try-catch wrapped so that we can guarantee that the nonceLock is released
|
||||
try {
|
||||
this.txStateManager.setTxStatusFailed(txId, err)
|
||||
} catch (err) {
|
||||
log.error(err)
|
||||
}
|
||||
// must set transaction to submitted/failed before releasing lock
|
||||
if (nonceLock) nonceLock.releaseLock()
|
||||
// continue with error chain
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
const Config = require('./recipient-blacklist.js')
|
||||
|
||||
/** @module*/
|
||||
module.exports = {
|
||||
checkAccount,
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a specified account on a specified network is blacklisted.
|
||||
@param networkId {number}
|
||||
@param account {string}
|
||||
*/
|
||||
function checkAccount (networkId, account) {
|
||||
|
||||
const mainnetId = 1
|
||||
if (networkId !== mainnetId) {
|
||||
return
|
||||
}
|
||||
|
||||
const accountToCheck = account.toLowerCase()
|
||||
if (Config.blacklist.includes(accountToCheck)) {
|
||||
throw new Error('Recipient is a public account')
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
module.exports = {
|
||||
'blacklist': [
|
||||
// IDEX phisher
|
||||
'0x9bcb0A9d99d815Bb87ee3191b1399b1Bcc46dc77',
|
||||
// Ganache default seed phrases
|
||||
'0x627306090abab3a6e1400e9345bc60c78a8bef57',
|
||||
'0xf17f52151ebef6c7334fad080c5704d77216b732',
|
||||
'0xc5fdf4076b8f3a5357c5e395ab970b5b54098fef',
|
||||
'0x821aea9a577a9b44299b9c15c88cf3087f3b5544',
|
||||
'0x0d1d4e623d10f9fba5db95830f7d3839406c6af2',
|
||||
'0x2932b7a2355d6fecc4b5c0b6bd44cc31df247a2e',
|
||||
'0x2191ef87e392377ec08e7c08eb105ef5448eced5',
|
||||
'0x0f4f2ac550a1b4e2280d04c21cea7ebd822934b5',
|
||||
'0x6330a553fc93768f612722bb8c2ec78ac90b3bbc',
|
||||
'0x5aeda56215b167893e80b4fe645ba6d5bab767de',
|
||||
],
|
||||
}
|
|
@ -49,29 +49,35 @@ class NonceTracker {
|
|||
await this._globalMutexFree()
|
||||
// await lock free, then take lock
|
||||
const releaseLock = await this._takeMutex(address)
|
||||
// evaluate multiple nextNonce strategies
|
||||
const nonceDetails = {}
|
||||
const networkNonceResult = await this._getNetworkNextNonce(address)
|
||||
const highestLocallyConfirmed = this._getHighestLocallyConfirmed(address)
|
||||
const nextNetworkNonce = networkNonceResult.nonce
|
||||
const highestSuggested = Math.max(nextNetworkNonce, highestLocallyConfirmed)
|
||||
try {
|
||||
// evaluate multiple nextNonce strategies
|
||||
const nonceDetails = {}
|
||||
const networkNonceResult = await this._getNetworkNextNonce(address)
|
||||
const highestLocallyConfirmed = this._getHighestLocallyConfirmed(address)
|
||||
const nextNetworkNonce = networkNonceResult.nonce
|
||||
const highestSuggested = Math.max(nextNetworkNonce, highestLocallyConfirmed)
|
||||
|
||||
const pendingTxs = this.getPendingTransactions(address)
|
||||
const localNonceResult = this._getHighestContinuousFrom(pendingTxs, highestSuggested) || 0
|
||||
const pendingTxs = this.getPendingTransactions(address)
|
||||
const localNonceResult = this._getHighestContinuousFrom(pendingTxs, highestSuggested) || 0
|
||||
|
||||
nonceDetails.params = {
|
||||
highestLocallyConfirmed,
|
||||
highestSuggested,
|
||||
nextNetworkNonce,
|
||||
nonceDetails.params = {
|
||||
highestLocallyConfirmed,
|
||||
highestSuggested,
|
||||
nextNetworkNonce,
|
||||
}
|
||||
nonceDetails.local = localNonceResult
|
||||
nonceDetails.network = networkNonceResult
|
||||
|
||||
const nextNonce = Math.max(networkNonceResult.nonce, localNonceResult.nonce)
|
||||
assert(Number.isInteger(nextNonce), `nonce-tracker - nextNonce is not an integer - got: (${typeof nextNonce}) "${nextNonce}"`)
|
||||
|
||||
// return nonce and release cb
|
||||
return { nextNonce, nonceDetails, releaseLock }
|
||||
} catch (err) {
|
||||
// release lock if we encounter an error
|
||||
releaseLock()
|
||||
throw err
|
||||
}
|
||||
nonceDetails.local = localNonceResult
|
||||
nonceDetails.network = networkNonceResult
|
||||
|
||||
const nextNonce = Math.max(networkNonceResult.nonce, localNonceResult.nonce)
|
||||
assert(Number.isInteger(nextNonce), `nonce-tracker - nextNonce is not an integer - got: (${typeof nextNonce}) "${nextNonce}"`)
|
||||
|
||||
// return nonce and release cb
|
||||
return { nextNonce, nonceDetails, releaseLock }
|
||||
}
|
||||
|
||||
async _getCurrentBlock () {
|
||||
|
@ -85,8 +91,8 @@ class NonceTracker {
|
|||
|
||||
async _globalMutexFree () {
|
||||
const globalMutex = this._lookupMutex('global')
|
||||
const release = await globalMutex.acquire()
|
||||
release()
|
||||
const releaseLock = await globalMutex.acquire()
|
||||
releaseLock()
|
||||
}
|
||||
|
||||
async _takeMutex (lockId) {
|
||||
|
|
|
@ -196,14 +196,14 @@ class PendingTransactionTracker extends EventEmitter {
|
|||
async _checkPendingTxs () {
|
||||
const signedTxList = this.getPendingTransactions()
|
||||
// in order to keep the nonceTracker accurate we block it while updating pending transactions
|
||||
const nonceGlobalLock = await this.nonceTracker.getGlobalLock()
|
||||
const { releaseLock } = await this.nonceTracker.getGlobalLock()
|
||||
try {
|
||||
await Promise.all(signedTxList.map((txMeta) => this._checkPendingTx(txMeta)))
|
||||
} catch (err) {
|
||||
log.error('PendingTransactionWatcher - Error updating pending transactions')
|
||||
log.error(err)
|
||||
}
|
||||
nonceGlobalLock.releaseLock()
|
||||
releaseLock()
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -126,4 +126,4 @@ class TxGasUtil {
|
|||
}
|
||||
}
|
||||
|
||||
module.exports = TxGasUtil
|
||||
module.exports = TxGasUtil
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
const MessageManager = require('./lib/message-manager')
|
||||
const PersonalMessageManager = require('./lib/personal-message-manager')
|
||||
const TypedMessageManager = require('./lib/typed-message-manager')
|
||||
|
||||
class UserActionController {
|
||||
|
||||
constructor (opts = {}) {
|
||||
|
||||
this.messageManager = new MessageManager()
|
||||
this.personalMessageManager = new PersonalMessageManager()
|
||||
this.typedMessageManager = new TypedMessageManager()
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = UserActionController
|
|
@ -38,9 +38,30 @@ web3.setProvider = function () {
|
|||
log.debug('MetaMask - overrode web3.setProvider')
|
||||
}
|
||||
log.debug('MetaMask - injected web3')
|
||||
// export global web3, with usage-detection
|
||||
|
||||
setupDappAutoReload(web3, inpageProvider.publicConfigStore)
|
||||
|
||||
// export global web3, with usage-detection and deprecation warning
|
||||
|
||||
/* TODO: Uncomment this area once auto-reload.js has been deprecated:
|
||||
let hasBeenWarned = false
|
||||
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
|
||||
}
|
||||
// return value normally
|
||||
return _web3[key]
|
||||
},
|
||||
set: (_web3, key, value) => {
|
||||
// set value normally
|
||||
_web3[key] = value
|
||||
},
|
||||
})
|
||||
*/
|
||||
|
||||
// set web3 defaultAccount
|
||||
inpageProvider.publicConfigStore.subscribe(function (state) {
|
||||
web3.eth.defaultAccount = state.selectedAddress
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* @param {Error} err - error
|
||||
* @returns {Error} Error with clean stack trace.
|
||||
*/
|
||||
function cleanErrorStack(err){
|
||||
function cleanErrorStack (err) {
|
||||
var name = err.name
|
||||
name = (name === undefined) ? 'Error' : String(name)
|
||||
|
||||
|
|
|
@ -59,8 +59,9 @@ function createErrorMiddleware ({ override = true } = {}) {
|
|||
if (!error) { return done() }
|
||||
sanitizeRPCError(error)
|
||||
log.error(`MetaMask - RPC Error: ${error.message}`, error)
|
||||
done()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = createErrorMiddleware
|
||||
module.exports = createErrorMiddleware
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
const WritableStream = require('readable-stream').Writable
|
||||
const promiseToCallback = require('promise-to-callback')
|
||||
|
||||
module.exports = createStreamSink
|
||||
|
||||
|
||||
function createStreamSink (asyncWriteFn, _opts) {
|
||||
return new AsyncWritableStream(asyncWriteFn, _opts)
|
||||
}
|
||||
|
||||
class AsyncWritableStream extends WritableStream {
|
||||
|
||||
constructor (asyncWriteFn, _opts) {
|
||||
const opts = Object.assign({ objectMode: true }, _opts)
|
||||
super(opts)
|
||||
this._asyncWriteFn = asyncWriteFn
|
||||
}
|
||||
|
||||
// write from incomming stream to state
|
||||
_write (chunk, encoding, callback) {
|
||||
promiseToCallback(this._asyncWriteFn(chunk, encoding))(callback)
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
class DiagnosticsReporter {
|
||||
|
||||
constructor ({ firstTimeInfo, version }) {
|
||||
this.firstTimeInfo = firstTimeInfo
|
||||
this.version = version
|
||||
}
|
||||
|
||||
async reportOrphans (orphans) {
|
||||
try {
|
||||
return await this.submit({
|
||||
accounts: Object.keys(orphans),
|
||||
metadata: {
|
||||
type: 'orphans',
|
||||
},
|
||||
})
|
||||
} catch (err) {
|
||||
console.error('DiagnosticsReporter - "reportOrphans" encountered an error:')
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
|
||||
async reportMultipleKeyrings (rawKeyrings) {
|
||||
try {
|
||||
const keyrings = await Promise.all(rawKeyrings.map(async (keyring, index) => {
|
||||
return {
|
||||
index,
|
||||
type: keyring.type,
|
||||
accounts: await keyring.getAccounts(),
|
||||
}
|
||||
}))
|
||||
return await this.submit({
|
||||
accounts: [],
|
||||
metadata: {
|
||||
type: 'keyrings',
|
||||
keyrings,
|
||||
},
|
||||
})
|
||||
} catch (err) {
|
||||
console.error('DiagnosticsReporter - "reportMultipleKeyrings" encountered an error:')
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
|
||||
async submit (message) {
|
||||
try {
|
||||
// add metadata
|
||||
message.metadata.version = this.version
|
||||
message.metadata.firstTimeInfo = this.firstTimeInfo
|
||||
return await postData(message)
|
||||
} catch (err) {
|
||||
console.error('DiagnosticsReporter - "submit" encountered an error:')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function postData (data) {
|
||||
const uri = 'https://diagnostics.metamask.io/v1/orphanedAccounts'
|
||||
return fetch(uri, {
|
||||
body: JSON.stringify(data), // must match 'Content-Type' header
|
||||
credentials: 'same-origin', // include, same-origin, *omit
|
||||
headers: {
|
||||
'content-type': 'application/json',
|
||||
},
|
||||
method: 'POST', // *GET, POST, PUT, DELETE, etc.
|
||||
mode: 'cors', // no-cors, cors, *same-origin
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = DiagnosticsReporter
|
|
@ -10,13 +10,13 @@ module.exports = extractEthjsErrorMessage
|
|||
*
|
||||
* @param {string} errorMessage The error message to parse
|
||||
* @returns {string} Returns an error message, either the same as was passed, or the ending message portion of an isEthjsRpcError
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* // returns 'Transaction Failed: replacement transaction underpriced'
|
||||
* extractEthjsErrorMessage(`Error: [ethjs-rpc] rpc error with payload {"id":3947817945380,"jsonrpc":"2.0","params":["0xf8eb8208708477359400830398539406012c8cf97bead5deae237070f9587f8e7a266d80b8843d7d3f5a0000000000000000000000000000000000000000000000000000000000081d1a000000000000000000000000000000000000000000000000001ff973cafa800000000000000000000000000000000000000000000000000000038d7ea4c68000000000000000000000000000000000000000000000000000000000000003f48025a04c32a9b630e0d9e7ff361562d850c86b7a884908135956a7e4a336fa0300d19ca06830776423f25218e8d19b267161db526e66895567147015b1f3fc47aef9a3c7"],"method":"eth_sendRawTransaction"} Error: replacement transaction underpriced`)
|
||||
*
|
||||
*/
|
||||
function extractEthjsErrorMessage(errorMessage) {
|
||||
function extractEthjsErrorMessage (errorMessage) {
|
||||
const isEthjsRpcError = errorMessage.includes(ethJsRpcSlug)
|
||||
if (isEthjsRpcError) {
|
||||
const payloadAndError = errorMessage.slice(ethJsRpcSlug.length)
|
||||
|
|
|
@ -2,8 +2,7 @@ const extension = require('extensionizer')
|
|||
const promisify = require('pify')
|
||||
const allLocales = require('../../_locales/index.json')
|
||||
|
||||
const isSupported = extension.i18n && extension.i18n.getAcceptLanguages
|
||||
const getPreferredLocales = isSupported ? promisify(
|
||||
const getPreferredLocales = extension.i18n ? promisify(
|
||||
extension.i18n.getAcceptLanguages,
|
||||
{ errorFirst: false }
|
||||
) : async () => []
|
||||
|
@ -18,7 +17,21 @@ const existingLocaleCodes = allLocales.map(locale => locale.code.toLowerCase().r
|
|||
*
|
||||
*/
|
||||
async function getFirstPreferredLangCode () {
|
||||
const userPreferredLocaleCodes = await getPreferredLocales()
|
||||
let userPreferredLocaleCodes
|
||||
|
||||
try {
|
||||
userPreferredLocaleCodes = await getPreferredLocales()
|
||||
} catch (e) {
|
||||
// Brave currently throws when calling getAcceptLanguages, so this handles that.
|
||||
userPreferredLocaleCodes = []
|
||||
}
|
||||
|
||||
// safeguard for Brave Browser until they implement chrome.i18n.getAcceptLanguages
|
||||
// https://github.com/MetaMask/metamask-extension/issues/4270
|
||||
if (!userPreferredLocaleCodes) {
|
||||
userPreferredLocaleCodes = []
|
||||
}
|
||||
|
||||
const firstPreferredLangCode = userPreferredLocaleCodes
|
||||
.map(code => code.toLowerCase())
|
||||
.find(code => existingLocaleCodes.includes(code))
|
||||
|
@ -26,3 +39,4 @@ async function getFirstPreferredLangCode () {
|
|||
}
|
||||
|
||||
module.exports = getFirstPreferredLangCode
|
||||
|
||||
|
|
|
@ -18,12 +18,12 @@ module.exports = getObjStructure
|
|||
* Creates an object that represents the structure of the given object. It replaces all values with the result of their
|
||||
* type.
|
||||
*
|
||||
* @param {object} obj The object for which a 'structure' will be returned. Usually a plain object and not a class.
|
||||
* @param {object} obj The object for which a 'structure' will be returned. Usually a plain object and not a class.
|
||||
* @returns {object} The "mapped" version of a deep clone of the passed object, with each non-object property value
|
||||
* replaced with the javascript type of that value.
|
||||
*
|
||||
*/
|
||||
function getObjStructure(obj) {
|
||||
function getObjStructure (obj) {
|
||||
const structure = clone(obj)
|
||||
return deepMap(structure, (value) => {
|
||||
return value === null ? 'null' : typeof value
|
||||
|
@ -38,7 +38,7 @@ function getObjStructure(obj) {
|
|||
* @param {Function} visit The modifier to apply to each non-object property value
|
||||
* @returns {object} The modified object
|
||||
*/
|
||||
function deepMap(target = {}, visit) {
|
||||
function deepMap (target = {}, visit) {
|
||||
Object.entries(target).forEach(([key, value]) => {
|
||||
if (typeof value === 'object' && value !== null) {
|
||||
target[key] = deepMap(value, visit)
|
||||
|
|
|
@ -8,7 +8,7 @@ module.exports = class ExtensionStore {
|
|||
/**
|
||||
* @constructor
|
||||
*/
|
||||
constructor() {
|
||||
constructor () {
|
||||
this.isSupported = !!(extension.storage.local)
|
||||
if (!this.isSupported) {
|
||||
log.error('Storage local API not available.')
|
||||
|
@ -19,7 +19,7 @@ module.exports = class ExtensionStore {
|
|||
* Returns all of the keys currently saved
|
||||
* @return {Promise<*>}
|
||||
*/
|
||||
async get() {
|
||||
async get () {
|
||||
if (!this.isSupported) return undefined
|
||||
const result = await this._get()
|
||||
// extension.storage.local always returns an obj
|
||||
|
@ -36,7 +36,7 @@ module.exports = class ExtensionStore {
|
|||
* @param {object} state - The state to set
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
async set(state) {
|
||||
async set (state) {
|
||||
return this._set(state)
|
||||
}
|
||||
|
||||
|
@ -45,7 +45,7 @@ module.exports = class ExtensionStore {
|
|||
* @private
|
||||
* @return {object} the key-value map from local storage
|
||||
*/
|
||||
_get() {
|
||||
_get () {
|
||||
const local = extension.storage.local
|
||||
return new Promise((resolve, reject) => {
|
||||
local.get(null, (/** @type {any} */ result) => {
|
||||
|
@ -65,7 +65,7 @@ module.exports = class ExtensionStore {
|
|||
* @return {Promise<void>}
|
||||
* @private
|
||||
*/
|
||||
_set(obj) {
|
||||
_set (obj) {
|
||||
const local = extension.storage.local
|
||||
return new Promise((resolve, reject) => {
|
||||
local.set(obj, () => {
|
||||
|
@ -85,6 +85,6 @@ module.exports = class ExtensionStore {
|
|||
* @param {object} obj - The object to check
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function isEmpty(obj) {
|
||||
function isEmpty (obj) {
|
||||
return Object.keys(obj).length === 0
|
||||
}
|
||||
|
|
|
@ -26,13 +26,15 @@ class NotificationManager {
|
|||
// bring focus to existing chrome popup
|
||||
extension.windows.update(popup.id, { focused: true })
|
||||
} else {
|
||||
const cb = (currentPopup) => { this._popupId = currentPopup.id }
|
||||
// create new notification popup
|
||||
extension.windows.create({
|
||||
const creation = extension.windows.create({
|
||||
url: 'notification.html',
|
||||
type: 'popup',
|
||||
width,
|
||||
height,
|
||||
})
|
||||
}, cb)
|
||||
creation && creation.then && creation.then(cb)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -84,7 +86,7 @@ class NotificationManager {
|
|||
}
|
||||
|
||||
/**
|
||||
* Given an array of windows, returns the first that has a 'popup' type, or null if no such window exists.
|
||||
* Given an array of windows, returns the 'popup' that has been opened by MetaMask, or null if no such window exists.
|
||||
*
|
||||
* @private
|
||||
* @param {array} windows An array of objects containing data about the open MetaMask extension windows.
|
||||
|
@ -93,7 +95,7 @@ class NotificationManager {
|
|||
_getPopupIn (windows) {
|
||||
return windows ? windows.find((win) => {
|
||||
// Returns notification popup
|
||||
return (win && win.type === 'popup')
|
||||
return (win && win.type === 'popup' && win.id === this._popupId)
|
||||
}) : null
|
||||
}
|
||||
|
||||
|
|
|
@ -58,7 +58,7 @@ PortDuplexStream.prototype._read = noop
|
|||
/**
|
||||
* Called internally when data should be written to
|
||||
* this writable stream.
|
||||
*
|
||||
*
|
||||
* @private
|
||||
* @param {*} msg Arbitrary object to write
|
||||
* @param {string} encoding Encoding to use when writing payload
|
||||
|
|
|
@ -7,7 +7,7 @@ module.exports = reportFailedTxToSentry
|
|||
// for sending to sentry
|
||||
//
|
||||
|
||||
function reportFailedTxToSentry({ raven, txMeta }) {
|
||||
function reportFailedTxToSentry ({ raven, txMeta }) {
|
||||
const errorMessage = 'Transaction Failed: ' + extractEthjsErrorMessage(txMeta.err.message)
|
||||
raven.captureMessage(errorMessage, {
|
||||
// "extra" key is required by Sentry
|
||||
|
|
|
@ -4,7 +4,7 @@ module.exports = setupMetamaskMeshMetrics
|
|||
/**
|
||||
* Injects an iframe into the current document for testing
|
||||
*/
|
||||
function setupMetamaskMeshMetrics() {
|
||||
function setupMetamaskMeshMetrics () {
|
||||
const testingContainer = document.createElement('iframe')
|
||||
testingContainer.src = 'https://metamask.github.io/mesh-testing/'
|
||||
console.log('Injecting MetaMask Mesh testing client')
|
||||
|
|
|
@ -7,7 +7,7 @@ const DEV = 'https://f59f3dd640d2429d9d0e2445a87ea8e1@sentry.io/273496'
|
|||
module.exports = setupRaven
|
||||
|
||||
// Setup raven / sentry remote error reporting
|
||||
function setupRaven(opts) {
|
||||
function setupRaven (opts) {
|
||||
const { release } = opts
|
||||
let ravenTarget
|
||||
|
||||
|
@ -21,7 +21,7 @@ function setupRaven(opts) {
|
|||
|
||||
const client = Raven.config(ravenTarget, {
|
||||
release,
|
||||
transport: function(opts) {
|
||||
transport: function (opts) {
|
||||
const report = opts.data
|
||||
try {
|
||||
// handle error-like non-error exceptions
|
||||
|
@ -42,7 +42,7 @@ function setupRaven(opts) {
|
|||
return Raven
|
||||
}
|
||||
|
||||
function rewriteErrorLikeExceptions(report) {
|
||||
function rewriteErrorLikeExceptions (report) {
|
||||
// handle errors that lost their error-ness in serialization (e.g. dnode)
|
||||
rewriteErrorMessages(report, (errorMessage) => {
|
||||
if (!errorMessage.includes('Non-Error exception captured with keys:')) return errorMessage
|
||||
|
@ -51,7 +51,7 @@ function rewriteErrorLikeExceptions(report) {
|
|||
})
|
||||
}
|
||||
|
||||
function simplifyErrorMessages(report) {
|
||||
function simplifyErrorMessages (report) {
|
||||
rewriteErrorMessages(report, (errorMessage) => {
|
||||
// simplify ethjs error messages
|
||||
errorMessage = extractEthjsErrorMessage(errorMessage)
|
||||
|
@ -64,9 +64,9 @@ function simplifyErrorMessages(report) {
|
|||
})
|
||||
}
|
||||
|
||||
function rewriteErrorMessages(report, rewriteFn) {
|
||||
function rewriteErrorMessages (report, rewriteFn) {
|
||||
// rewrite top level message
|
||||
report.message = rewriteFn(report.message)
|
||||
if (report.message) report.message = rewriteFn(report.message)
|
||||
// rewrite each exception message
|
||||
if (report.exception && report.exception.values) {
|
||||
report.exception.values.forEach(item => {
|
||||
|
@ -75,7 +75,7 @@ function rewriteErrorMessages(report, rewriteFn) {
|
|||
}
|
||||
}
|
||||
|
||||
function rewriteReportUrls(report) {
|
||||
function rewriteReportUrls (report) {
|
||||
// update request url
|
||||
report.request.url = toMetamaskUrl(report.request.url)
|
||||
// update exception stack trace
|
||||
|
@ -88,7 +88,7 @@ function rewriteReportUrls(report) {
|
|||
}
|
||||
}
|
||||
|
||||
function toMetamaskUrl(origUrl) {
|
||||
function toMetamaskUrl (origUrl) {
|
||||
const filePath = origUrl.split(location.origin)[1]
|
||||
if (!filePath) return origUrl
|
||||
const metamaskUrl = `metamask${filePath}`
|
||||
|
|
|
@ -139,6 +139,8 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||
const address = addresses[0]
|
||||
this.preferencesController.setSelectedAddress(address)
|
||||
}
|
||||
// ensure preferences + identities controller know about all addresses
|
||||
this.preferencesController.addAddresses(addresses)
|
||||
this.accountTracker.syncWithAddresses(addresses)
|
||||
})
|
||||
|
||||
|
@ -179,9 +181,6 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||
version,
|
||||
firstVersion: initState.firstTimeInfo.version,
|
||||
})
|
||||
this.noticeController.updateNoticesList()
|
||||
// to be uncommented when retrieving notices from a remote server.
|
||||
// this.noticeController.startPolling()
|
||||
|
||||
this.shapeshiftController = new ShapeShiftController({
|
||||
initState: initState.ShapeShiftController,
|
||||
|
@ -354,7 +353,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||
importAccountWithStrategy: nodeify(this.importAccountWithStrategy, this),
|
||||
|
||||
// vault management
|
||||
submitPassword: nodeify(keyringController.submitPassword, keyringController),
|
||||
submitPassword: nodeify(this.submitPassword, this),
|
||||
|
||||
// network management
|
||||
setProviderType: nodeify(networkController.setProviderType, networkController),
|
||||
|
@ -384,6 +383,8 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||
updateAndApproveTransaction: nodeify(txController.updateAndApproveTransaction, txController),
|
||||
retryTransaction: nodeify(this.retryTransaction, this),
|
||||
getFilteredTxList: nodeify(txController.getFilteredTxList, txController),
|
||||
isNonceTaken: nodeify(txController.isNonceTaken, txController),
|
||||
estimateGas: nodeify(this.estimateGas, this),
|
||||
|
||||
// messageManager
|
||||
signMessage: nodeify(this.signMessage, this),
|
||||
|
@ -404,7 +405,6 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||
}
|
||||
|
||||
|
||||
|
||||
//=============================================================================
|
||||
// VAULT / KEYRING RELATED METHODS
|
||||
//=============================================================================
|
||||
|
@ -424,28 +424,24 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||
* @returns {Object} vault
|
||||
*/
|
||||
async createNewVaultAndKeychain (password) {
|
||||
const release = await this.createVaultMutex.acquire()
|
||||
let vault
|
||||
|
||||
const releaseLock = await this.createVaultMutex.acquire()
|
||||
try {
|
||||
let vault
|
||||
const accounts = await this.keyringController.getAccounts()
|
||||
|
||||
if (accounts.length > 0) {
|
||||
vault = await this.keyringController.fullUpdate()
|
||||
|
||||
} else {
|
||||
vault = await this.keyringController.createNewVaultAndKeychain(password)
|
||||
const accounts = await this.keyringController.getAccounts()
|
||||
this.preferencesController.setAddresses(accounts)
|
||||
this.selectFirstIdentity()
|
||||
}
|
||||
release()
|
||||
releaseLock()
|
||||
return vault
|
||||
} catch (err) {
|
||||
release()
|
||||
releaseLock()
|
||||
throw err
|
||||
}
|
||||
|
||||
return vault
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -454,20 +450,46 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||
* @param {} seed
|
||||
*/
|
||||
async createNewVaultAndRestore (password, seed) {
|
||||
const release = await this.createVaultMutex.acquire()
|
||||
const releaseLock = await this.createVaultMutex.acquire()
|
||||
try {
|
||||
// clear known identities
|
||||
this.preferencesController.setAddresses([])
|
||||
// create new vault
|
||||
const vault = await this.keyringController.createNewVaultAndRestore(password, seed)
|
||||
// set new identities
|
||||
const accounts = await this.keyringController.getAccounts()
|
||||
this.preferencesController.setAddresses(accounts)
|
||||
this.selectFirstIdentity()
|
||||
release()
|
||||
releaseLock()
|
||||
return vault
|
||||
} catch (err) {
|
||||
release()
|
||||
releaseLock()
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Submits the user's password and attempts to unlock the vault.
|
||||
* Also synchronizes the preferencesController, to ensure its schema
|
||||
* is up to date with known accounts once the vault is decrypted.
|
||||
*
|
||||
* @param {string} password - The user's password
|
||||
* @returns {Promise<object>} - The keyringController update.
|
||||
*/
|
||||
async submitPassword (password) {
|
||||
await this.keyringController.submitPassword(password)
|
||||
const accounts = await this.keyringController.getAccounts()
|
||||
|
||||
// verify keyrings
|
||||
const nonSimpleKeyrings = this.keyringController.keyrings.filter(keyring => keyring.type !== 'Simple Key Pair')
|
||||
if (nonSimpleKeyrings.length > 1 && this.diagnostics) {
|
||||
await this.diagnostics.reportMultipleKeyrings(nonSimpleKeyrings)
|
||||
}
|
||||
|
||||
await this.preferencesController.syncAddresses(accounts)
|
||||
return this.keyringController.fullUpdate()
|
||||
}
|
||||
|
||||
/**
|
||||
* @type Identity
|
||||
* @property {string} name - The account nickname.
|
||||
|
@ -592,10 +614,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||
async resetAccount () {
|
||||
const selectedAddress = this.preferencesController.getSelectedAddress()
|
||||
this.txController.wipeTransactions(selectedAddress)
|
||||
|
||||
const networkController = this.networkController
|
||||
const oldType = networkController.getProviderConfig().type
|
||||
await networkController.setProviderType(oldType, true)
|
||||
this.networkController.resetConnection()
|
||||
|
||||
return selectedAddress
|
||||
}
|
||||
|
@ -922,6 +941,18 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||
return state
|
||||
}
|
||||
|
||||
estimateGas (estimateGasParams) {
|
||||
return new Promise((resolve, reject) => {
|
||||
return this.txController.txGasUtil.query.estimateGas(estimateGasParams, (err, res) => {
|
||||
if (err) {
|
||||
return reject(err)
|
||||
}
|
||||
|
||||
return resolve(res)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// PASSWORD MANAGEMENT
|
||||
//=============================================================================
|
||||
|
@ -930,7 +961,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||
* Allows a user to begin the seed phrase recovery process.
|
||||
* @param {Function} cb - A callback function called when complete.
|
||||
*/
|
||||
markPasswordForgotten(cb) {
|
||||
markPasswordForgotten (cb) {
|
||||
this.configManager.setPasswordForgotten(true)
|
||||
this.sendUpdate()
|
||||
cb()
|
||||
|
@ -940,7 +971,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||
* Allows a user to end the seed phrase recovery process.
|
||||
* @param {Function} cb - A callback function called when complete.
|
||||
*/
|
||||
unMarkPasswordForgotten(cb) {
|
||||
unMarkPasswordForgotten (cb) {
|
||||
this.configManager.setPasswordForgotten(false)
|
||||
this.sendUpdate()
|
||||
cb()
|
||||
|
|
|
@ -28,7 +28,7 @@ module.exports = {
|
|||
function transformState (state) {
|
||||
const newState = state
|
||||
const { config } = newState
|
||||
if ( config && config.provider ) {
|
||||
if (config && config.provider) {
|
||||
if (config.provider.type === 'testnet') {
|
||||
newState.config.provider.type = 'ropsten'
|
||||
}
|
||||
|
|
|
@ -35,10 +35,10 @@ function transformState (state) {
|
|||
|
||||
if (transactions.length <= 40) return newState
|
||||
|
||||
let reverseTxList = transactions.reverse()
|
||||
const reverseTxList = transactions.reverse()
|
||||
let stripping = true
|
||||
while (reverseTxList.length > 40 && stripping) {
|
||||
let txIndex = reverseTxList.findIndex((txMeta) => {
|
||||
const txIndex = reverseTxList.findIndex((txMeta) => {
|
||||
return (txMeta.status === 'failed' ||
|
||||
txMeta.status === 'rejected' ||
|
||||
txMeta.status === 'confirmed' ||
|
||||
|
|
|
@ -27,7 +27,7 @@ module.exports = {
|
|||
|
||||
function transformState (state) {
|
||||
if (!state.KeyringController || !state.PreferencesController) {
|
||||
return
|
||||
return state
|
||||
}
|
||||
|
||||
if (!state.KeyringController.walletNicknames) {
|
||||
|
|
|
@ -2,7 +2,7 @@ const EventEmitter = require('events').EventEmitter
|
|||
const semver = require('semver')
|
||||
const extend = require('xtend')
|
||||
const ObservableStore = require('obs-store')
|
||||
const hardCodedNotices = require('../../notices/notices.json')
|
||||
const hardCodedNotices = require('../../notices/notices.js')
|
||||
const uniqBy = require('lodash.uniqby')
|
||||
|
||||
module.exports = class NoticeController extends EventEmitter {
|
||||
|
@ -16,8 +16,12 @@ module.exports = class NoticeController extends EventEmitter {
|
|||
noticesList: [],
|
||||
}, opts.initState)
|
||||
this.store = new ObservableStore(initState)
|
||||
// setup memStore
|
||||
this.memStore = new ObservableStore({})
|
||||
this.store.subscribe(() => this._updateMemstore())
|
||||
this._updateMemstore()
|
||||
// pull in latest notices
|
||||
this.updateNoticesList()
|
||||
}
|
||||
|
||||
getNoticesList () {
|
||||
|
@ -29,9 +33,9 @@ module.exports = class NoticeController extends EventEmitter {
|
|||
return notices.filter((notice) => notice.read === false)
|
||||
}
|
||||
|
||||
getLatestUnreadNotice () {
|
||||
getNextUnreadNotice () {
|
||||
const unreadNotices = this.getUnreadNotices()
|
||||
return unreadNotices[unreadNotices.length - 1]
|
||||
return unreadNotices[0]
|
||||
}
|
||||
|
||||
async setNoticesList (noticesList) {
|
||||
|
@ -47,7 +51,7 @@ module.exports = class NoticeController extends EventEmitter {
|
|||
notices[index].read = true
|
||||
notices[index].body = ''
|
||||
this.setNoticesList(notices)
|
||||
const latestNotice = this.getLatestUnreadNotice()
|
||||
const latestNotice = this.getNextUnreadNotice()
|
||||
cb(null, latestNotice)
|
||||
} catch (err) {
|
||||
cb(err)
|
||||
|
@ -64,15 +68,6 @@ module.exports = class NoticeController extends EventEmitter {
|
|||
return result
|
||||
}
|
||||
|
||||
startPolling () {
|
||||
if (this.noticePoller) {
|
||||
clearInterval(this.noticePoller)
|
||||
}
|
||||
this.noticePoller = setInterval(() => {
|
||||
this.noticeController.updateNoticesList()
|
||||
}, 300000)
|
||||
}
|
||||
|
||||
_mergeNotices (oldNotices, newNotices) {
|
||||
return uniqBy(oldNotices.concat(newNotices), 'id')
|
||||
}
|
||||
|
@ -91,19 +86,15 @@ module.exports = class NoticeController extends EventEmitter {
|
|||
})
|
||||
}
|
||||
|
||||
_mapNoticeIds (notices) {
|
||||
return notices.map((notice) => notice.id)
|
||||
}
|
||||
|
||||
async _retrieveNoticeData () {
|
||||
// Placeholder for the API.
|
||||
// Placeholder for remote notice API.
|
||||
return hardCodedNotices
|
||||
}
|
||||
|
||||
_updateMemstore () {
|
||||
const lastUnreadNotice = this.getLatestUnreadNotice()
|
||||
const noActiveNotices = !lastUnreadNotice
|
||||
this.memStore.updateState({ lastUnreadNotice, noActiveNotices })
|
||||
const nextUnreadNotice = this.getNextUnreadNotice()
|
||||
const noActiveNotices = !nextUnreadNotice
|
||||
this.memStore.updateState({ nextUnreadNotice, noActiveNotices })
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ module.exports = initializePopup
|
|||
/**
|
||||
* Asynchronously initializes the MetaMask popup UI
|
||||
*
|
||||
* @param {{ container: Element, connectionStream: * }} config Popup configuration object
|
||||
* @param {{ container: Element, connectionStream: * }} config Popup configuration object
|
||||
* @param {Function} cb Called when initialization is complete
|
||||
*/
|
||||
function initializePopup ({ container, connectionStream }, cb) {
|
||||
|
|
|
@ -14,7 +14,7 @@ const log = require('loglevel')
|
|||
|
||||
start().catch(log.error)
|
||||
|
||||
async function start() {
|
||||
async function start () {
|
||||
|
||||
// create platform global
|
||||
global.platform = new ExtensionPlatform()
|
||||
|
|
|
@ -7,6 +7,6 @@ var changelog = fs.readFileSync(path.join(__dirname, '..', 'CHANGELOG.md')).toSt
|
|||
|
||||
var log = changelog.split(version)[1].split('##')[0].trim()
|
||||
|
||||
let msg = `*MetaMask ${version}* now published! It should auto-update soon!\n${log}`
|
||||
const msg = `*MetaMask ${version}* now published! It should auto-update soon!\n${log}`
|
||||
|
||||
console.log(msg)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
module.exports = {
|
||||
"confirm sig requests": {
|
||||
'confirm sig requests': {
|
||||
signMessage: (msgData, cb) => {
|
||||
const stateUpdate = {
|
||||
unapprovedMsgs: {},
|
||||
|
|
|
@ -1,17 +1,14 @@
|
|||
const beefy = require('beefy')
|
||||
const http = require('http')
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
|
||||
const port = 8124
|
||||
|
||||
const handler = beefy({
|
||||
entries: {'mocker.js': 'bundle.js'}
|
||||
, cwd: __dirname
|
||||
, live: true
|
||||
, open: true
|
||||
, quiet: false
|
||||
, bundlerFlags: ['-t', 'brfs']
|
||||
entries: {'mocker.js': 'bundle.js'},
|
||||
cwd: __dirname,
|
||||
live: true,
|
||||
open: true,
|
||||
quiet: false,
|
||||
bundlerFlags: ['-t', 'brfs'],
|
||||
})
|
||||
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ const VERSION = require('../dist/chrome/manifest.json').version
|
|||
|
||||
start().catch(console.error)
|
||||
|
||||
async function start() {
|
||||
async function start () {
|
||||
|
||||
const GITHUB_COMMENT_TOKEN = process.env.GITHUB_COMMENT_TOKEN
|
||||
const CIRCLE_PULL_REQUEST = process.env.CIRCLE_PULL_REQUEST
|
||||
|
@ -20,7 +20,7 @@ async function start() {
|
|||
}
|
||||
|
||||
const CIRCLE_PR_NUMBER = CIRCLE_PULL_REQUEST.split('/').pop()
|
||||
const SHORT_SHA1 = CIRCLE_SHA1.slice(0,7)
|
||||
const SHORT_SHA1 = CIRCLE_SHA1.slice(0, 7)
|
||||
const BUILD_LINK_BASE = `https://${CIRCLE_BUILD_NUM}-42009758-gh.circle-artifacts.com/0`
|
||||
|
||||
const MASCARA = `${BUILD_LINK_BASE}/builds/mascara/home.html`
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
* To use, run `npm run mock`.
|
||||
*/
|
||||
|
||||
const extend = require('xtend')
|
||||
const render = require('react-dom').render
|
||||
const h = require('react-hyperscript')
|
||||
const Root = require('../ui/app/root')
|
||||
|
@ -24,7 +23,6 @@ const Selector = require('./selector')
|
|||
const MetamaskController = require('../app/scripts/metamask-controller')
|
||||
const firstTimeState = require('../app/scripts/first-time-state')
|
||||
const ExtensionPlatform = require('../app/scripts/platforms/extension')
|
||||
const extension = require('./mockExtension')
|
||||
const noop = function () {}
|
||||
|
||||
const log = require('loglevel')
|
||||
|
@ -81,14 +79,14 @@ const controller = new MetamaskController({
|
|||
initState: firstTimeState,
|
||||
})
|
||||
global.metamaskController = controller
|
||||
global.platform = new ExtensionPlatform
|
||||
global.platform = new ExtensionPlatform()
|
||||
|
||||
//
|
||||
// User Interface
|
||||
//
|
||||
|
||||
actions._setBackgroundConnection(controller.getApi())
|
||||
actions.update = function(stateName) {
|
||||
actions.update = function (stateName) {
|
||||
selectedView = stateName
|
||||
updateQueryParams(stateName)
|
||||
const newState = states[selectedView]
|
||||
|
@ -98,7 +96,7 @@ actions.update = function(stateName) {
|
|||
}
|
||||
}
|
||||
|
||||
function modifyBackgroundConnection(backgroundConnectionModifier) {
|
||||
function modifyBackgroundConnection (backgroundConnectionModifier) {
|
||||
const modifiedBackgroundConnection = Object.assign({}, controller.getApi(), backgroundConnectionModifier)
|
||||
actions._setBackgroundConnection(modifiedBackgroundConnection)
|
||||
}
|
||||
|
@ -112,7 +110,7 @@ var store = configureStore(firstState)
|
|||
// start app
|
||||
startApp()
|
||||
|
||||
function startApp(){
|
||||
function startApp () {
|
||||
const body = document.body
|
||||
const container = document.createElement('div')
|
||||
container.id = 'test-container'
|
||||
|
|
|
@ -39,6 +39,6 @@ extension.runtime.reload = noop
|
|||
extension.tabs.create = noop
|
||||
extension.runtime.getManifest = function () {
|
||||
return {
|
||||
version: 'development'
|
||||
version: 'development',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ const bumpType = normalizeType(process.argv[2])
|
|||
|
||||
start().catch(console.error)
|
||||
|
||||
async function start() {
|
||||
async function start () {
|
||||
|
||||
const changeBuffer = await readFile(changelogPath)
|
||||
const changelog = changeBuffer.toString()
|
||||
|
|
|
@ -11,7 +11,7 @@ function NewComponent () {
|
|||
|
||||
NewComponent.prototype.render = function () {
|
||||
const props = this.props
|
||||
let {
|
||||
const {
|
||||
states,
|
||||
selectedKey,
|
||||
actions,
|
||||
|
@ -28,7 +28,7 @@ NewComponent.prototype.render = function () {
|
|||
margin: '20px 20px 0px',
|
||||
},
|
||||
value: selected,
|
||||
onChange:(event) => {
|
||||
onChange: (event) => {
|
||||
const selectedKey = event.target.value
|
||||
const backgroundConnectionModifier = backGroundConnectionModifiers[selectedKey]
|
||||
modifyBackgroundConnection(backgroundConnectionModifier || {})
|
||||
|
|
|
@ -5,7 +5,7 @@ const VERSION = require('../dist/chrome/manifest.json').version
|
|||
|
||||
start().catch(console.error)
|
||||
|
||||
async function start(){
|
||||
async function start () {
|
||||
const authWorked = await checkIfAuthWorks()
|
||||
if (!authWorked) {
|
||||
console.log(`Sentry auth failed...`)
|
||||
|
@ -31,21 +31,21 @@ async function start(){
|
|||
console.log('all done!')
|
||||
}
|
||||
|
||||
async function checkIfAuthWorks() {
|
||||
async function checkIfAuthWorks () {
|
||||
const itWorked = await doesNotFail(async () => {
|
||||
await exec(`sentry-cli releases --org 'metamask' --project 'metamask' list`)
|
||||
})
|
||||
return itWorked
|
||||
}
|
||||
|
||||
async function checkIfVersionExists() {
|
||||
async function checkIfVersionExists () {
|
||||
const versionAlreadyExists = await doesNotFail(async () => {
|
||||
await exec(`sentry-cli releases --org 'metamask' --project 'metamask' info ${VERSION}`)
|
||||
})
|
||||
return versionAlreadyExists
|
||||
}
|
||||
|
||||
async function doesNotFail(asyncFn) {
|
||||
async function doesNotFail (asyncFn) {
|
||||
try {
|
||||
await asyncFn()
|
||||
return true
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
const fs = require('fs')
|
||||
const { SourceMapConsumer } = require('source-map')
|
||||
|
||||
const path = require('path')
|
||||
//
|
||||
// Utility to help check if sourcemaps are working
|
||||
//
|
||||
|
@ -11,9 +11,11 @@ const { SourceMapConsumer } = require('source-map')
|
|||
|
||||
start()
|
||||
|
||||
async function start() {
|
||||
const rawBuild = fs.readFileSync(__dirname + '/../dist/chrome/inpage.js', 'utf8')
|
||||
const rawSourceMap = fs.readFileSync(__dirname + '/../dist/sourcemaps/inpage.js.map', 'utf8')
|
||||
|
||||
async function start () {
|
||||
const rawBuild = fs.readFileSync(path.join(__dirname, '/../dist/chrome/', 'inpage.js')
|
||||
, 'utf8')
|
||||
const rawSourceMap = fs.readFileSync(path.join(__dirname, '/../dist/sourcemaps/', 'inpage.js.map'), 'utf8')
|
||||
const consumer = await new SourceMapConsumer(rawSourceMap)
|
||||
|
||||
console.log('hasContentsOfAllSources:', consumer.hasContentsOfAllSources(), '\n')
|
||||
|
@ -34,7 +36,7 @@ async function start() {
|
|||
if (result.source === 'node_modules/web3/dist/web3.min.js') return // minified mess
|
||||
const sourceContent = consumer.sourceContentFor(result.source)
|
||||
const sourceLines = sourceContent.split('\n')
|
||||
const line = sourceLines[result.line-1]
|
||||
const line = sourceLines[result.line - 1]
|
||||
console.log(`\n========================== ${result.source} ====================================\n`)
|
||||
console.log(line)
|
||||
console.log(`\n==============================================================================\n`)
|
||||
|
@ -42,8 +44,9 @@ async function start() {
|
|||
})
|
||||
}
|
||||
|
||||
function indicesOf(substring, string) {
|
||||
var a=[],i=-1;
|
||||
while((i=string.indexOf(substring,i+1)) >= 0) a.push(i);
|
||||
return a;
|
||||
function indicesOf (substring, string) {
|
||||
var a = []
|
||||
var i = -1
|
||||
while ((i = string.indexOf(substring, i + 1)) >= 0) a.push(i)
|
||||
return a
|
||||
}
|
||||
|
|
|
@ -75,9 +75,9 @@
|
|||
{
|
||||
"type": "HD Key Tree",
|
||||
"accounts": [
|
||||
"fdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||
"c5b8dbac4c1d3f152cdeb400e2313f309c410acb",
|
||||
"2f8d4a878cfa04a6e60d46362f5644deab66572d"
|
||||
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||
"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb",
|
||||
"0x2f8d4a878cfa04a6e60d46362f5644deab66572d"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
|
@ -52,7 +52,7 @@
|
|||
"conversionRate": 12.7200827,
|
||||
"conversionDate": 1487363041,
|
||||
"noActiveNotices": true,
|
||||
"lastUnreadNotice": {
|
||||
"nextUnreadNotice": {
|
||||
"read": true,
|
||||
"date": "Thu Feb 09 2017",
|
||||
"title": "Terms of Use",
|
||||
|
|
|
@ -151,5 +151,10 @@
|
|||
"scrollToBottom": false,
|
||||
"forgottenPassword": null
|
||||
},
|
||||
"identities": {}
|
||||
"identities": {},
|
||||
"send": {
|
||||
"fromDropdownOpen": false,
|
||||
"toDropdownOpen": false,
|
||||
"errors": {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -115,9 +115,9 @@
|
|||
{
|
||||
"type": "HD Key Tree",
|
||||
"accounts": [
|
||||
"fdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||
"c5b8dbac4c1d3f152cdeb400e2313f309c410acb",
|
||||
"2f8d4a878cfa04a6e60d46362f5644deab66572d"
|
||||
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||
"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb",
|
||||
"0x2f8d4a878cfa04a6e60d46362f5644deab66572d"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
|
@ -76,9 +76,9 @@
|
|||
{
|
||||
"type": "HD Key Tree",
|
||||
"accounts": [
|
||||
"fdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||
"c5b8dbac4c1d3f152cdeb400e2313f309c410acb",
|
||||
"2f8d4a878cfa04a6e60d46362f5644deab66572d"
|
||||
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||
"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb",
|
||||
"0x2f8d4a878cfa04a6e60d46362f5644deab66572d"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
"conversionRate": 12.7527416,
|
||||
"conversionDate": 1487624341,
|
||||
"noActiveNotices": false,
|
||||
"lastUnreadNotice": {
|
||||
"nextUnreadNotice": {
|
||||
"read": false,
|
||||
"date": "Thu Feb 09 2017",
|
||||
"title": "Terms of Use",
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
"conversionRate": 8.3533002,
|
||||
"conversionDate": 1481671082,
|
||||
"noActiveNotices": false,
|
||||
"lastUnreadNotice": {
|
||||
"nextUnreadNotice": {
|
||||
"read": false,
|
||||
"date": "Tue Dec 13 2016",
|
||||
"title": "MultiVault Support",
|
||||
|
|
|
@ -94,9 +94,9 @@
|
|||
{
|
||||
"type": "HD Key Tree",
|
||||
"accounts": [
|
||||
"fdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||
"c5b8dbac4c1d3f152cdeb400e2313f309c410acb",
|
||||
"2f8d4a878cfa04a6e60d46362f5644deab66572d"
|
||||
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||
"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb",
|
||||
"0x2f8d4a878cfa04a6e60d46362f5644deab66572d"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
@ -151,5 +151,10 @@
|
|||
"scrollToBottom": false,
|
||||
"forgottenPassword": null
|
||||
},
|
||||
"identities": {}
|
||||
"identities": {},
|
||||
"send": {
|
||||
"fromDropdownOpen": false,
|
||||
"toDropdownOpen": false,
|
||||
"errors": {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,9 +76,9 @@
|
|||
{
|
||||
"type": "HD Key Tree",
|
||||
"accounts": [
|
||||
"fdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||
"c5b8dbac4c1d3f152cdeb400e2313f309c410acb",
|
||||
"2f8d4a878cfa04a6e60d46362f5644deab66572d"
|
||||
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||
"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb",
|
||||
"0x2f8d4a878cfa04a6e60d46362f5644deab66572d"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
@ -130,5 +130,10 @@
|
|||
"scrollToBottom": false,
|
||||
"forgottenPassword": null
|
||||
},
|
||||
"identities": {}
|
||||
"identities": {},
|
||||
"send": {
|
||||
"fromDropdownOpen": false,
|
||||
"toDropdownOpen": false,
|
||||
"errors": {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -83,9 +83,9 @@
|
|||
{
|
||||
"type": "HD Key Tree",
|
||||
"accounts": [
|
||||
"fdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||
"c5b8dbac4c1d3f152cdeb400e2313f309c410acb",
|
||||
"2f8d4a878cfa04a6e60d46362f5644deab66572d"
|
||||
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||
"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb",
|
||||
"0x2f8d4a878cfa04a6e60d46362f5644deab66572d"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
@ -124,5 +124,10 @@
|
|||
"scrollToBottom": false,
|
||||
"forgottenPassword": null
|
||||
},
|
||||
"identities": {}
|
||||
"identities": {},
|
||||
"send": {
|
||||
"fromDropdownOpen": false,
|
||||
"toDropdownOpen": false,
|
||||
"errors": {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,9 +29,8 @@ log.setDefaultLevel(1)
|
|||
|
||||
// Query String
|
||||
const qs = require('qs')
|
||||
let queryString = qs.parse(window.location.href.split('#')[1])
|
||||
const queryString = qs.parse(window.location.href.split('#')[1])
|
||||
let selectedView = queryString.view || 'first time'
|
||||
const firstState = states[selectedView]
|
||||
updateQueryParams(selectedView)
|
||||
|
||||
// CSS
|
||||
|
@ -39,15 +38,15 @@ const MetaMaskUiCss = require('../ui/css')
|
|||
const injectCss = require('inject-css')
|
||||
|
||||
|
||||
function updateQueryParams(newView) {
|
||||
function updateQueryParams (newView) {
|
||||
queryString.view = newView
|
||||
const params = qs.stringify(queryString)
|
||||
window.location.href = window.location.href.split('#')[0] + `#${params}`
|
||||
}
|
||||
|
||||
const actions = {
|
||||
_setBackgroundConnection(){},
|
||||
update: function(stateName) {
|
||||
_setBackgroundConnection () {},
|
||||
update: function (stateName) {
|
||||
selectedView = stateName
|
||||
updateQueryParams(stateName)
|
||||
const newState = states[selectedView]
|
||||
|
@ -67,7 +66,7 @@ var store = configureStore(states[selectedView])
|
|||
// start app
|
||||
startApp()
|
||||
|
||||
function startApp(){
|
||||
function startApp () {
|
||||
const body = document.body
|
||||
const container = document.createElement('div')
|
||||
container.id = 'test-container'
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Locale verification script
|
||||
//
|
||||
|
@ -8,7 +8,7 @@
|
|||
//
|
||||
// will check the given locale against the strings in english
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
|
@ -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({ localeMeta })
|
||||
verifyLocale({ locale })
|
||||
} else {
|
||||
console.log('Verifying all locales:\n\n')
|
||||
localeIndex.forEach(localeMeta => {
|
||||
|
@ -30,16 +30,16 @@ 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'));
|
||||
targetLocale = JSON.parse(fs.readFileSync(localeFilePath, 'utf8'))
|
||||
} catch (e) {
|
||||
if (e.code == 'ENOENT') {
|
||||
if (e.code === 'ENOENT') {
|
||||
console.log('Locale file not found')
|
||||
} else {
|
||||
console.log(`Error opening your locale ("${localeCode}") file: `, e)
|
||||
|
@ -49,9 +49,9 @@ function verifyLocale({ localeMeta }) {
|
|||
|
||||
try {
|
||||
const englishFilePath = path.join(process.cwd(), 'app', '_locales', 'en', 'messages.json')
|
||||
englishLocale = JSON.parse(fs.readFileSync(englishFilePath, 'utf8'));
|
||||
englishLocale = JSON.parse(fs.readFileSync(englishFilePath, 'utf8'))
|
||||
} catch (e) {
|
||||
if(e.code == 'ENOENT') {
|
||||
if (e.code === 'ENOENT') {
|
||||
console.log('English File not found')
|
||||
} else {
|
||||
console.log('Error opening english locale file: ', e)
|
||||
|
@ -71,7 +71,7 @@ function verifyLocale({ localeMeta }) {
|
|||
|
||||
if (extraItems.length) {
|
||||
console.log('\nMissing from english locale:')
|
||||
extraItems.forEach(function(key) {
|
||||
extraItems.forEach(function (key) {
|
||||
console.log(` - [ ] ${key}`)
|
||||
})
|
||||
} else {
|
||||
|
@ -80,7 +80,7 @@ function verifyLocale({ localeMeta }) {
|
|||
|
||||
if (missingItems.length) {
|
||||
console.log(`\nMissing:`)
|
||||
missingItems.forEach(function(key) {
|
||||
missingItems.forEach(function (key) {
|
||||
console.log(` - [ ] ${key}`)
|
||||
})
|
||||
} else {
|
||||
|
@ -92,6 +92,6 @@ function verifyLocale({ localeMeta }) {
|
|||
}
|
||||
}
|
||||
|
||||
function compareLocalesForMissingItems({ base, subject }) {
|
||||
function compareLocalesForMissingItems ({ base, subject }) {
|
||||
return Object.keys(base).filter((key) => !subject[key])
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
const clone = require('clone')
|
||||
|
||||
async function versionBump(bumpType, changelog, oldManifest) {
|
||||
async function versionBump (bumpType, changelog, oldManifest) {
|
||||
const manifest = clone(oldManifest)
|
||||
const newVersion = newVersionFrom(manifest, bumpType)
|
||||
|
||||
|
@ -19,13 +19,13 @@ async function versionBump(bumpType, changelog, oldManifest) {
|
|||
return {
|
||||
version: newVersion,
|
||||
manifest: manifest,
|
||||
changelog: logLines.join('\n')
|
||||
changelog: logLines.join('\n'),
|
||||
}
|
||||
}
|
||||
|
||||
function newVersionFrom (manifest, bumpType) {
|
||||
const string = manifest.version
|
||||
let segments = string.split('.').map((str) => parseInt(str))
|
||||
const segments = string.split('.').map((str) => parseInt(str))
|
||||
|
||||
switch (bumpType) {
|
||||
case 'major':
|
||||
|
@ -45,8 +45,4 @@ function newVersionFrom (manifest, bumpType) {
|
|||
return segments.map(String).join('.')
|
||||
}
|
||||
|
||||
function bumpManifest (manifest, bumpType) {
|
||||
|
||||
}
|
||||
|
||||
module.exports = versionBump
|
||||
|
|
|
@ -2,18 +2,32 @@
|
|||
|
||||
When publishing a new version of MetaMask, we follow this procedure:
|
||||
|
||||
## Preparation
|
||||
|
||||
We try to ensure certain criteria are met before deploying:
|
||||
|
||||
- Deploy early in the week, to give time for emergency responses to unforeseen bugs.
|
||||
- Deploy early in the day, for the same reason.
|
||||
- Make sure at least one member of the support team is "on duty" to watch for new user issues coming through the support system.
|
||||
- Roll out incrementally when possible, to a small number of users first, and gradually to more users.
|
||||
|
||||
## Incrementing Version & Changelog
|
||||
|
||||
Version can be automatically incremented [using our bump script](./bumping-version.md).
|
||||
|
||||
npm run version:bump $BUMP_TYPE` where `$BUMP_TYPE` is one of `major`, `minor`, or `patch`.
|
||||
|
||||
## Building
|
||||
|
||||
While we develop on the main `develop` branch, our production version is maintained on the `master` branch.
|
||||
|
||||
With each pull request, the @MetaMaskBot will comment with a build of that new pull request, so after bumping the version on `develop`, open a pull request against `master`, and once the pull request is reviewed and merged, you can download those builds for publication.
|
||||
|
||||
## Publishing
|
||||
|
||||
1. `npm run dist` to generate the latest build.
|
||||
2. Publish to chrome store.
|
||||
- Visit [the chrome developer dashboard](https://chrome.google.com/webstore/developer/dashboard?authuser=2).
|
||||
3. Publish to firefox addon marketplace.
|
||||
4. Post on Github releases page.
|
||||
5. `npm run announce`, post that announcement in our public places.
|
||||
|
||||
1. Publish to chrome store.
|
||||
2. Visit [the chrome developer dashboard](https://chrome.google.com/webstore/developer/dashboard?authuser=2).
|
||||
3. Publish to [firefox addon marketplace](http://addons.mozilla.org/en-us/firefox/addon/ether-metamask).
|
||||
4. Publish to [Opera store](https://addons.opera.com/en/extensions/details/metamask/).
|
||||
5. Post on [Github releases](https://github.com/MetaMask/metamask-extension/releases) page.
|
||||
6. Run the `npm run announce` script, and post that announcement in our public places.
|
||||
|
|
|
@ -0,0 +1,241 @@
|
|||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const async = require('async')
|
||||
const promisify = require('pify')
|
||||
|
||||
// start(/\.selectors.js/, generateSelectorTest).catch(console.error)
|
||||
// start(/\.utils.js/, generateUtilTest).catch(console.error)
|
||||
startContainer(/\.container.js/, generateContainerTest).catch(console.error)
|
||||
|
||||
async function getAllFileNames (dirName) {
|
||||
const allNames = (await promisify(fs.readdir)(dirName))
|
||||
const fileNames = allNames.filter(name => name.match(/^.+\./))
|
||||
const dirNames = allNames.filter(name => name.match(/^[^.]+$/))
|
||||
|
||||
const fullPathDirNames = dirNames.map(d => `${dirName}/${d}`)
|
||||
const subNameArrays = await promisify(async.map)(fullPathDirNames, getAllFileNames)
|
||||
let subNames = []
|
||||
subNameArrays.forEach(subNameArray => { subNames = [...subNames, ...subNameArray] })
|
||||
|
||||
return [
|
||||
...fileNames.map(name => dirName + '/' + name),
|
||||
...subNames,
|
||||
]
|
||||
}
|
||||
|
||||
/*
|
||||
async function start (fileRegEx, testGenerator) {
|
||||
const fileNames = await getAllFileNames('./ui/app')
|
||||
const sFiles = fileNames.filter(name => name.match(fileRegEx))
|
||||
|
||||
let sFileMethodNames
|
||||
let testFilePath
|
||||
async.each(sFiles, async (sFile, cb) => {
|
||||
const [, sRootPath, sPath] = sFile.match(/^(.+\/)([^/]+)$/)
|
||||
sFileMethodNames = Object.keys(require(__dirname + '/' + sFile))
|
||||
|
||||
testFilePath = sPath.replace('.', '-').replace('.', '.test.')
|
||||
|
||||
await promisify(fs.writeFile)(
|
||||
`${__dirname}/${sRootPath}tests/${testFilePath}`,
|
||||
testGenerator(sPath, sFileMethodNames),
|
||||
'utf8'
|
||||
)
|
||||
}, (err) => {
|
||||
console.log(err)
|
||||
})
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
async function startContainer (fileRegEx, testGenerator) {
|
||||
const fileNames = await getAllFileNames('./ui/app')
|
||||
const sFiles = fileNames.filter(name => name.match(fileRegEx))
|
||||
|
||||
async.each(sFiles, async (sFile, cb) => {
|
||||
console.log(`sFile`, sFile)
|
||||
const [, sRootPath, sPath] = sFile.match(/^(.+\/)([^/]+)$/)
|
||||
|
||||
const testFilePath = sPath.replace('.', '-').replace('.', '.test.')
|
||||
|
||||
await promisify(fs.readFile)(
|
||||
path.join(__dirname, sFile),
|
||||
'utf8',
|
||||
async (err, result) => {
|
||||
if (err) {
|
||||
console.log('Error: ', err)
|
||||
} else {
|
||||
console.log(`result`, result.length)
|
||||
const returnObjectStrings = result
|
||||
.match(/return\s(\{[\s\S]+?})\n}/g)
|
||||
.map(str => {
|
||||
return str
|
||||
.slice(0, str.length - 1)
|
||||
.slice(7)
|
||||
.replace(/\n/g, '')
|
||||
.replace(/\s\s+/g, ' ')
|
||||
|
||||
})
|
||||
const mapStateToPropsAssertionObject = returnObjectStrings[0]
|
||||
.replace(/\w+:\s\w+\([\w,\s]+\),/g, str => {
|
||||
const strKey = str.match(/^\w+/)[0]
|
||||
return strKey + ': \'mock' + str.match(/^\w+/)[0].replace(/^./, c => c.toUpperCase()) + ':mockState\',\n'
|
||||
})
|
||||
.replace(/{\s\w.+/, firstLinePair => `{\n ${firstLinePair.slice(2)}`)
|
||||
.replace(/\w+:.+,/g, s => ` ${s}`)
|
||||
.replace(/}/g, s => ` ${s}`)
|
||||
let mapDispatchToPropsMethodNames
|
||||
if (returnObjectStrings[1]) {
|
||||
mapDispatchToPropsMethodNames = returnObjectStrings[1].match(/\s\w+:\s/g).map(str => str.match(/\w+/)[0])
|
||||
}
|
||||
const proxyquireObject = ('{\n ' + result
|
||||
.match(/import\s{[\s\S]+?}\sfrom\s.+/g)
|
||||
.map(s => s.replace(/\n/g, ''))
|
||||
.map((s, i) => {
|
||||
const proxyKeys = s.match(/{.+}/)[0].match(/\w+/g)
|
||||
return '\'' + s.match(/'(.+)'/)[1] + '\': { ' + (proxyKeys.length > 1
|
||||
? '\n ' + proxyKeys.join(': () => {},\n ') + ': () => {},\n '
|
||||
: proxyKeys[0] + ': () => {},') + ' }'
|
||||
})
|
||||
.join(',\n ') + '\n}')
|
||||
.replace('{ connect: () => {}, },', `{
|
||||
connect: (ms, md) => {
|
||||
mapStateToProps = ms
|
||||
mapDispatchToProps = md
|
||||
return () => ({})
|
||||
},
|
||||
},`)
|
||||
// console.log(`proxyquireObject`, proxyquireObject);
|
||||
// console.log(`mapStateToPropsAssertionObject`, mapStateToPropsAssertionObject);
|
||||
// console.log(`mapDispatchToPropsMethodNames`, mapDispatchToPropsMethodNames);
|
||||
|
||||
const containerTest = generateContainerTest(sPath, {
|
||||
mapStateToPropsAssertionObject,
|
||||
mapDispatchToPropsMethodNames,
|
||||
proxyquireObject,
|
||||
})
|
||||
// console.log(`containerTest`, `${__dirname}/${sRootPath}tests/${testFilePath}`, containerTest);
|
||||
console.log('----')
|
||||
console.log(`sRootPath`, sRootPath)
|
||||
console.log(`testFilePath`, testFilePath)
|
||||
await promisify(fs.writeFile)(
|
||||
`${__dirname}/${sRootPath}tests/${testFilePath}`,
|
||||
containerTest,
|
||||
'utf8'
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}, (err) => {
|
||||
console.log('123', err)
|
||||
})
|
||||
|
||||
}
|
||||
/*
|
||||
function generateMethodList (methodArray) {
|
||||
return methodArray.map(n => ' ' + n).join(',\n') + ','
|
||||
}
|
||||
|
||||
function generateMethodDescribeBlock (methodName, index) {
|
||||
const describeBlock =
|
||||
`${index ? ' ' : ''}describe('${methodName}()', () => {
|
||||
it('should', () => {
|
||||
const state = {}
|
||||
|
||||
assert.equal(${methodName}(state), )
|
||||
})
|
||||
})`
|
||||
return describeBlock
|
||||
}
|
||||
*/
|
||||
function generateDispatchMethodDescribeBlock (methodName, index) {
|
||||
const describeBlock =
|
||||
`${index ? ' ' : ''}describe('${methodName}()', () => {
|
||||
it('should dispatch an action', () => {
|
||||
mapDispatchToPropsObject.${methodName}()
|
||||
assert(dispatchSpy.calledOnce)
|
||||
})
|
||||
})`
|
||||
return describeBlock
|
||||
}
|
||||
/*
|
||||
function generateMethodDescribeBlocks (methodArray) {
|
||||
return methodArray
|
||||
.map((methodName, index) => generateMethodDescribeBlock(methodName, index))
|
||||
.join('\n\n')
|
||||
}
|
||||
*/
|
||||
|
||||
function generateDispatchMethodDescribeBlocks (methodArray) {
|
||||
return methodArray
|
||||
.map((methodName, index) => generateDispatchMethodDescribeBlock(methodName, index))
|
||||
.join('\n\n')
|
||||
}
|
||||
|
||||
/*
|
||||
function generateSelectorTest (name, methodArray) {
|
||||
return `import assert from 'assert'
|
||||
import {
|
||||
${generateMethodList(methodArray)}
|
||||
} from '../${name}'
|
||||
|
||||
describe('${name.match(/^[^.]+/)} selectors', () => {
|
||||
|
||||
${generateMethodDescribeBlocks(methodArray)}
|
||||
|
||||
})`
|
||||
}
|
||||
|
||||
function generateUtilTest (name, methodArray) {
|
||||
return `import assert from 'assert'
|
||||
import {
|
||||
${generateMethodList(methodArray)}
|
||||
} from '../${name}'
|
||||
|
||||
describe('${name.match(/^[^.]+/)} utils', () => {
|
||||
|
||||
${generateMethodDescribeBlocks(methodArray)}
|
||||
|
||||
})`
|
||||
}
|
||||
*/
|
||||
|
||||
function generateContainerTest (sPath, {
|
||||
mapStateToPropsAssertionObject,
|
||||
mapDispatchToPropsMethodNames,
|
||||
proxyquireObject,
|
||||
}) {
|
||||
return `import assert from 'assert'
|
||||
import proxyquire from 'proxyquire'
|
||||
import sinon from 'sinon'
|
||||
|
||||
let mapStateToProps
|
||||
let mapDispatchToProps
|
||||
|
||||
proxyquire('../${sPath}', ${proxyquireObject})
|
||||
|
||||
describe('${sPath.match(/^[^.]+/)} container', () => {
|
||||
|
||||
describe('mapStateToProps()', () => {
|
||||
|
||||
it('should map the correct properties to props', () => {
|
||||
assert.deepEqual(mapStateToProps('mockState'), ${mapStateToPropsAssertionObject})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('mapDispatchToProps()', () => {
|
||||
let dispatchSpy
|
||||
let mapDispatchToPropsObject
|
||||
|
||||
beforeEach(() => {
|
||||
dispatchSpy = sinon.spy()
|
||||
mapDispatchToPropsObject = mapDispatchToProps(dispatchSpy)
|
||||
})
|
||||
|
||||
${mapDispatchToPropsMethodNames ? generateDispatchMethodDescribeBlocks(mapDispatchToPropsMethodNames) : 'delete'}
|
||||
|
||||
})
|
||||
|
||||
})`
|
||||
}
|
107
gulpfile.js
107
gulpfile.js
|
@ -13,27 +13,21 @@ const zip = require('gulp-zip')
|
|||
const assign = require('lodash.assign')
|
||||
const livereload = require('gulp-livereload')
|
||||
const del = require('del')
|
||||
const eslint = require('gulp-eslint')
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const manifest = require('./app/manifest.json')
|
||||
const replace = require('gulp-replace')
|
||||
const mkdirp = require('mkdirp')
|
||||
const asyncEach = require('async/each')
|
||||
const exec = require('child_process').exec
|
||||
const sass = require('gulp-sass')
|
||||
const autoprefixer = require('gulp-autoprefixer')
|
||||
const gulpStylelint = require('gulp-stylelint')
|
||||
const stylefmt = require('gulp-stylefmt')
|
||||
const uglify = require('gulp-uglify-es').default
|
||||
const babel = require('gulp-babel')
|
||||
const debug = require('gulp-debug')
|
||||
const pify = require('pify')
|
||||
const gulpMultiProcess = require('gulp-multi-process')
|
||||
const endOfStream = pify(require('end-of-stream'))
|
||||
|
||||
function gulpParallel (...args) {
|
||||
return function spawnGulpChildProcess(cb) {
|
||||
return function spawnGulpChildProcess (cb) {
|
||||
return gulpMultiProcess(args, cb, true)
|
||||
}
|
||||
}
|
||||
|
@ -48,12 +42,12 @@ const commonPlatforms = [
|
|||
// browser webapp
|
||||
'mascara',
|
||||
// browser extensions
|
||||
...browserPlatforms
|
||||
...browserPlatforms,
|
||||
]
|
||||
|
||||
// browser reload
|
||||
|
||||
gulp.task('dev:reload', function() {
|
||||
gulp.task('dev:reload', function () {
|
||||
livereload.listen({
|
||||
port: 35729,
|
||||
})
|
||||
|
@ -108,7 +102,7 @@ createCopyTasks('html:mascara', {
|
|||
destinations: [`./dist/mascara/`],
|
||||
})
|
||||
|
||||
function createCopyTasks(label, opts) {
|
||||
function createCopyTasks (label, opts) {
|
||||
if (!opts.devOnly) {
|
||||
const copyTaskName = `copy:${label}`
|
||||
copyTask(copyTaskName, opts)
|
||||
|
@ -119,7 +113,7 @@ function createCopyTasks(label, opts) {
|
|||
copyDevTaskNames.push(copyDevTaskName)
|
||||
}
|
||||
|
||||
function copyTask(taskName, opts){
|
||||
function copyTask (taskName, opts) {
|
||||
const source = opts.source
|
||||
const destination = opts.destination
|
||||
const destinations = opts.destinations || [destination]
|
||||
|
@ -137,12 +131,12 @@ function copyTask(taskName, opts){
|
|||
return performCopy()
|
||||
})
|
||||
|
||||
function performCopy() {
|
||||
function performCopy () {
|
||||
// stream from source
|
||||
let stream = gulp.src(source + pattern, { base: source })
|
||||
|
||||
// copy to destinations
|
||||
destinations.forEach(function(destination) {
|
||||
destinations.forEach(function (destination) {
|
||||
stream = stream.pipe(gulp.dest(destination))
|
||||
})
|
||||
|
||||
|
@ -152,40 +146,40 @@ function copyTask(taskName, opts){
|
|||
|
||||
// manifest tinkering
|
||||
|
||||
gulp.task('manifest:chrome', function() {
|
||||
gulp.task('manifest:chrome', function () {
|
||||
return gulp.src('./dist/chrome/manifest.json')
|
||||
.pipe(jsoneditor(function(json) {
|
||||
.pipe(jsoneditor(function (json) {
|
||||
delete json.applications
|
||||
return json
|
||||
}))
|
||||
.pipe(gulp.dest('./dist/chrome', { overwrite: true }))
|
||||
})
|
||||
|
||||
gulp.task('manifest:opera', function() {
|
||||
gulp.task('manifest:opera', function () {
|
||||
return gulp.src('./dist/opera/manifest.json')
|
||||
.pipe(jsoneditor(function(json) {
|
||||
.pipe(jsoneditor(function (json) {
|
||||
json.permissions = [
|
||||
"storage",
|
||||
"tabs",
|
||||
"clipboardWrite",
|
||||
"clipboardRead",
|
||||
"http://localhost:8545/"
|
||||
'storage',
|
||||
'tabs',
|
||||
'clipboardWrite',
|
||||
'clipboardRead',
|
||||
'http://localhost:8545/',
|
||||
]
|
||||
return json
|
||||
}))
|
||||
.pipe(gulp.dest('./dist/opera', { overwrite: true }))
|
||||
})
|
||||
|
||||
gulp.task('manifest:production', function() {
|
||||
gulp.task('manifest:production', function () {
|
||||
return gulp.src([
|
||||
'./dist/firefox/manifest.json',
|
||||
'./dist/chrome/manifest.json',
|
||||
'./dist/edge/manifest.json',
|
||||
'./dist/opera/manifest.json',
|
||||
],{base: './dist/'})
|
||||
], {base: './dist/'})
|
||||
|
||||
// Exclude chromereload script in production:
|
||||
.pipe(jsoneditor(function(json) {
|
||||
.pipe(jsoneditor(function (json) {
|
||||
json.background.scripts = json.background.scripts.filter((script) => {
|
||||
return !script.includes('chromereload')
|
||||
})
|
||||
|
@ -212,29 +206,6 @@ gulp.task('dev:copy',
|
|||
)
|
||||
)
|
||||
|
||||
// lint js
|
||||
|
||||
const lintTargets = ['app/**/*.json', 'app/**/*.js', '!app/scripts/vendor/**/*.js', 'ui/**/*.js', 'old-ui/**/*.js', 'mascara/src/*.js', 'mascara/server/*.js', '!node_modules/**', '!dist/firefox/**', '!docs/**', '!app/scripts/chromereload.js', '!mascara/test/jquery-3.1.0.min.js']
|
||||
|
||||
gulp.task('lint', function () {
|
||||
// Ignoring node_modules, dist/firefox, and docs folders:
|
||||
return gulp.src(lintTargets)
|
||||
.pipe(eslint(fs.readFileSync(path.join(__dirname, '.eslintrc'))))
|
||||
// eslint.format() outputs the lint results to the console.
|
||||
// Alternatively use eslint.formatEach() (see Docs).
|
||||
.pipe(eslint.format())
|
||||
// To have the process exit with an error code (1) on
|
||||
// lint error, return the stream and pipe to failAfterError last.
|
||||
.pipe(eslint.failAfterError())
|
||||
});
|
||||
|
||||
gulp.task('lint:fix', function () {
|
||||
return gulp.src(lintTargets)
|
||||
.pipe(eslint(Object.assign(fs.readFileSync(path.join(__dirname, '.eslintrc')), {fix: true})))
|
||||
.pipe(eslint.format())
|
||||
.pipe(eslint.failAfterError())
|
||||
});
|
||||
|
||||
// scss compilation and autoprefixing tasks
|
||||
|
||||
gulp.task('build:scss', createScssBuildTask({
|
||||
|
@ -250,7 +221,7 @@ gulp.task('dev:scss', createScssBuildTask({
|
|||
pattern: 'ui/app/**/*.scss',
|
||||
}))
|
||||
|
||||
function createScssBuildTask({ src, dest, devMode, pattern }) {
|
||||
function createScssBuildTask ({ src, dest, devMode, pattern }) {
|
||||
return function () {
|
||||
if (devMode) {
|
||||
watch(pattern, async (event) => {
|
||||
|
@ -262,7 +233,7 @@ function createScssBuildTask({ src, dest, devMode, pattern }) {
|
|||
return buildScss()
|
||||
}
|
||||
|
||||
function buildScss() {
|
||||
function buildScss () {
|
||||
return gulp.src(src)
|
||||
.pipe(sourcemaps.init())
|
||||
.pipe(sass().on('error', sass.logError))
|
||||
|
@ -272,22 +243,22 @@ function createScssBuildTask({ src, dest, devMode, pattern }) {
|
|||
}
|
||||
}
|
||||
|
||||
gulp.task('lint-scss', function() {
|
||||
gulp.task('lint-scss', function () {
|
||||
return gulp
|
||||
.src('ui/app/css/itcss/**/*.scss')
|
||||
.pipe(gulpStylelint({
|
||||
reporters: [
|
||||
{ formatter: 'string', console: true }
|
||||
{ formatter: 'string', console: true },
|
||||
],
|
||||
fix: true,
|
||||
}));
|
||||
});
|
||||
}))
|
||||
})
|
||||
|
||||
gulp.task('fmt-scss', function () {
|
||||
return gulp.src('ui/app/css/itcss/**/*.scss')
|
||||
.pipe(stylefmt())
|
||||
.pipe(gulp.dest('ui/app/css/itcss'));
|
||||
});
|
||||
.pipe(gulp.dest('ui/app/css/itcss'))
|
||||
})
|
||||
|
||||
// build js
|
||||
|
||||
|
@ -300,11 +271,11 @@ const buildJsFiles = [
|
|||
|
||||
// bundle tasks
|
||||
createTasksForBuildJsExtension({ buildJsFiles, taskPrefix: 'dev:extension:js', devMode: true })
|
||||
createTasksForBuildJsExtension({ buildJsFiles, taskPrefix: 'build:extension:js' })
|
||||
createTasksForBuildJsExtension({ buildJsFiles, taskPrefix: 'build:extension:js' })
|
||||
createTasksForBuildJsMascara({ taskPrefix: 'build:mascara:js' })
|
||||
createTasksForBuildJsMascara({ taskPrefix: 'dev:mascara:js', devMode: true })
|
||||
|
||||
function createTasksForBuildJsExtension({ buildJsFiles, taskPrefix, devMode, bundleTaskOpts = {} }) {
|
||||
function createTasksForBuildJsExtension ({ buildJsFiles, taskPrefix, devMode, bundleTaskOpts = {} }) {
|
||||
// inpage must be built before all other scripts:
|
||||
const rootDir = './app/scripts'
|
||||
const nonInpageFiles = buildJsFiles.filter(file => file !== 'inpage')
|
||||
|
@ -322,7 +293,7 @@ function createTasksForBuildJsExtension({ buildJsFiles, taskPrefix, devMode, bun
|
|||
createTasksForBuildJs({ rootDir, taskPrefix, bundleTaskOpts, destinations, buildPhase1, buildPhase2 })
|
||||
}
|
||||
|
||||
function createTasksForBuildJsMascara({ taskPrefix, devMode, bundleTaskOpts = {} }) {
|
||||
function createTasksForBuildJsMascara ({ taskPrefix, devMode, bundleTaskOpts = {} }) {
|
||||
// inpage must be built before all other scripts:
|
||||
const rootDir = './mascara/src/'
|
||||
const buildPhase1 = ['ui', 'proxy', 'background', 'metamascara']
|
||||
|
@ -338,7 +309,7 @@ function createTasksForBuildJsMascara({ taskPrefix, devMode, bundleTaskOpts = {}
|
|||
createTasksForBuildJs({ rootDir, taskPrefix, bundleTaskOpts, destinations, buildPhase1 })
|
||||
}
|
||||
|
||||
function createTasksForBuildJs({ rootDir, taskPrefix, bundleTaskOpts, destinations, buildPhase1 = [], buildPhase2 = [] }) {
|
||||
function createTasksForBuildJs ({ rootDir, taskPrefix, bundleTaskOpts, destinations, buildPhase1 = [], buildPhase2 = [] }) {
|
||||
// bundle task for each file
|
||||
const jsFiles = [].concat(buildPhase1, buildPhase2)
|
||||
jsFiles.forEach((jsFile) => {
|
||||
|
@ -367,7 +338,7 @@ gulp.task('disc', gulp.parallel(buildJsFiles.map(jsFile => `disc:${jsFile}`)))
|
|||
|
||||
// clean dist
|
||||
|
||||
gulp.task('clean', function clean() {
|
||||
gulp.task('clean', function clean () {
|
||||
return del(['./dist/*'])
|
||||
})
|
||||
|
||||
|
@ -460,7 +431,7 @@ gulp.task('dist',
|
|||
|
||||
// task generators
|
||||
|
||||
function zipTask(target) {
|
||||
function zipTask (target) {
|
||||
return () => {
|
||||
return gulp.src(`dist/${target}/**`)
|
||||
.pipe(zip(`metamask-${target}-${manifest.version}.zip`))
|
||||
|
@ -468,7 +439,7 @@ function zipTask(target) {
|
|||
}
|
||||
}
|
||||
|
||||
function generateBundler(opts, performBundle) {
|
||||
function generateBundler (opts, performBundle) {
|
||||
const browserifyOpts = assign({}, watchify.args, {
|
||||
entries: [opts.filepath],
|
||||
plugin: 'browserify-derequire',
|
||||
|
@ -497,7 +468,7 @@ function generateBundler(opts, performBundle) {
|
|||
return bundler
|
||||
}
|
||||
|
||||
function discTask(opts) {
|
||||
function discTask (opts) {
|
||||
opts = Object.assign({
|
||||
buildWithFullPaths: true,
|
||||
}, opts)
|
||||
|
@ -508,7 +479,7 @@ function discTask(opts) {
|
|||
|
||||
return performBundle
|
||||
|
||||
function performBundle(){
|
||||
function performBundle () {
|
||||
// start "disc" build
|
||||
const discDir = path.join(__dirname, 'disc')
|
||||
mkdirp.sync(discDir)
|
||||
|
@ -523,14 +494,14 @@ function discTask(opts) {
|
|||
}
|
||||
|
||||
|
||||
function bundleTask(opts) {
|
||||
function bundleTask (opts) {
|
||||
const bundler = generateBundler(opts, performBundle)
|
||||
// output build logs to terminal
|
||||
bundler.on('log', gutil.log)
|
||||
|
||||
return performBundle
|
||||
|
||||
function performBundle(){
|
||||
function performBundle () {
|
||||
let buildStream = bundler.bundle()
|
||||
|
||||
// handle errors
|
||||
|
@ -562,7 +533,7 @@ function bundleTask(opts) {
|
|||
buildStream = buildStream
|
||||
.pipe(uglify({
|
||||
mangle: {
|
||||
reserved: [ 'MetamaskInpageProvider' ]
|
||||
reserved: [ 'MetamaskInpageProvider' ],
|
||||
},
|
||||
}))
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ const EthQuery = require('ethjs-query')
|
|||
window.addEventListener('load', loadProvider)
|
||||
window.addEventListener('message', console.warn)
|
||||
|
||||
async function loadProvider() {
|
||||
async function loadProvider () {
|
||||
const ethereumProvider = window.metamask.createDefaultProvider({ host: 'http://localhost:9001' })
|
||||
const ethQuery = new EthQuery(ethereumProvider)
|
||||
const accounts = await ethQuery.accounts()
|
||||
|
@ -13,7 +13,7 @@ async function loadProvider() {
|
|||
}
|
||||
|
||||
|
||||
function logToDom(message, context){
|
||||
function logToDom (message, context) {
|
||||
document.getElementById(context).innerText = message
|
||||
console.log(message)
|
||||
}
|
||||
|
@ -35,4 +35,4 @@ function setupButtons (ethQuery) {
|
|||
})
|
||||
logToDom(txHash, 'cb-value')
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
const express = require('express')
|
||||
const path = require('path')
|
||||
const createMetamascaraServer = require('../server/')
|
||||
const createBundle = require('../server/util').createBundle
|
||||
const serveBundle = require('../server/util').serveBundle
|
||||
|
||||
//
|
||||
// Iframe Server
|
||||
//
|
||||
|
@ -23,7 +23,7 @@ const dappServer = express()
|
|||
|
||||
// serve dapp bundle
|
||||
serveBundle(dappServer, '/app.js', createBundle(require.resolve('./app.js')))
|
||||
dappServer.use(express.static(__dirname + '/app/'))
|
||||
dappServer.use(express.static(path.join(__dirname, '/app/')))
|
||||
|
||||
// start the server
|
||||
const dappPort = '9002'
|
||||
|
|
|
@ -8,7 +8,7 @@ export default class Breadcrumbs extends Component {
|
|||
currentIndex: PropTypes.number,
|
||||
};
|
||||
|
||||
render() {
|
||||
render () {
|
||||
const {total, currentIndex} = this.props
|
||||
return (
|
||||
<div className="breadcrumbs">
|
||||
|
@ -20,7 +20,7 @@ export default class Breadcrumbs extends Component {
|
|||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -54,7 +54,7 @@ class BuyEtherScreen extends Component {
|
|||
|
||||
return (
|
||||
<div
|
||||
className='buy-ether__do-it-later'
|
||||
className="buy-ether__do-it-later"
|
||||
onClick={() => showAccountDetail(address)}
|
||||
>
|
||||
Do it later
|
||||
|
@ -64,17 +64,17 @@ class BuyEtherScreen extends Component {
|
|||
|
||||
renderCoinbaseLogo () {
|
||||
return (
|
||||
<svg width='140px' height='49px' viewBox='0 0 579 126' version='1.1'>
|
||||
<g id='Page-1' stroke='none' strokeWidth={1} fill='none' fillRule='evenodd'>
|
||||
<g id='Imported-Layers' fill='#0081C9'>
|
||||
<path d='M37.752,125.873 C18.824,125.873 0.369,112.307 0.369,81.549 C0.369,50.79 18.824,37.382 37.752,37.382 C47.059,37.382 54.315,39.749 59.52,43.219 L53.841,55.68 C50.371,53.156 45.166,51.579 39.961,51.579 C28.604,51.579 18.193,60.57 18.193,81.391 C18.193,102.212 28.919,111.361 39.961,111.361 C45.166,111.361 50.371,109.783 53.841,107.26 L59.52,120.036 C54.157,123.664 47.059,125.873 37.752,125.873' id='Fill-1' />
|
||||
<path d='M102.898,125.873 C78.765,125.873 65.515,106.786 65.515,81.549 C65.515,56.311 78.765,37.382 102.898,37.382 C127.032,37.382 140.282,56.311 140.282,81.549 C140.282,106.786 127.032,125.873 102.898,125.873 L102.898,125.873 Z M102.898,51.105 C89.491,51.105 82.866,63.093 82.866,81.391 C82.866,99.688 89.491,111.834 102.898,111.834 C116.306,111.834 122.931,99.688 122.931,81.391 C122.931,63.093 116.306,51.105 102.898,51.105 L102.898,51.105 Z' id='Fill-2' />
|
||||
<path d='M163.468,23.659 C157.79,23.659 153.215,19.243 153.215,13.88 C153.215,8.517 157.79,4.1 163.468,4.1 C169.146,4.1 173.721,8.517 173.721,13.88 C173.721,19.243 169.146,23.659 163.468,23.659 L163.468,23.659 Z M154.793,39.118 L172.144,39.118 L172.144,124.138 L154.793,124.138 L154.793,39.118 Z' id='Fill-3' />
|
||||
<path d='M240.443,124.137 L240.443,67.352 C240.443,57.415 234.449,51.263 222.619,51.263 C216.31,51.263 210.473,52.367 207.003,53.787 L207.003,124.137 L189.81,124.137 L189.81,43.376 C198.328,39.906 209.212,37.382 222.461,37.382 C246.28,37.382 257.794,47.793 257.794,65.775 L257.794,124.137 L240.443,124.137' id='Fill-4' />
|
||||
<path d='M303.536,125.873 C292.494,125.873 281.611,123.191 274.986,119.879 L274.986,0.314 L292.179,0.314 L292.179,41.326 C296.28,39.433 302.905,37.856 308.741,37.856 C330.667,37.856 345.494,53.629 345.494,79.656 C345.494,111.676 328.931,125.873 303.536,125.873 L303.536,125.873 Z M305.744,51.263 C301.012,51.263 295.491,52.367 292.179,54.103 L292.179,109.941 C294.703,111.045 299.593,112.149 304.482,112.149 C318.205,112.149 328.301,102.685 328.301,80.918 C328.301,62.305 319.467,51.263 305.744,51.263 L305.744,51.263 Z' id='Fill-5' />
|
||||
<path d='M392.341,125.873 C367.892,125.873 355.589,115.935 355.589,99.215 C355.589,75.555 380.826,71.296 406.537,69.876 L406.537,64.513 C406.537,53.787 399.439,50.001 388.555,50.001 C380.511,50.001 370.731,52.525 365.053,55.207 L360.636,43.376 C367.419,40.379 378.933,37.382 390.29,37.382 C410.638,37.382 422.942,45.269 422.942,66.248 L422.942,119.879 C416.79,123.191 404.329,125.873 392.341,125.873 L392.341,125.873 Z M406.537,81.391 C389.186,82.337 371.835,83.757 371.835,98.9 C371.835,107.89 378.776,113.411 391.868,113.411 C397.389,113.411 403.856,112.465 406.537,111.203 L406.537,81.391 L406.537,81.391 Z' id='Fill-6' />
|
||||
<path d='M461.743,125.873 C451.806,125.873 441.395,123.191 435.244,119.879 L441.08,106.629 C445.496,109.31 454.803,112.149 461.27,112.149 C470.576,112.149 476.728,107.575 476.728,100.477 C476.728,92.748 470.261,89.751 461.586,86.596 C450.228,82.337 437.452,77.132 437.452,61.201 C437.452,47.162 448.336,37.382 467.264,37.382 C477.517,37.382 486.035,39.906 492.029,43.376 L486.665,55.364 C482.88,52.998 475.309,50.317 469.157,50.317 C460.166,50.317 455.118,55.049 455.118,61.201 C455.118,68.93 461.428,71.611 469.788,74.766 C481.618,79.183 494.71,84.072 494.71,100.635 C494.71,115.935 483.038,125.873 461.743,125.873' id='Fill-7' />
|
||||
<path d='M578.625,81.233 L522.155,89.12 C523.89,104.42 533.828,112.149 548.182,112.149 C556.699,112.149 565.848,110.099 571.684,106.944 L576.732,119.879 C570.107,123.349 558.75,125.873 547.078,125.873 C520.262,125.873 505.277,108.679 505.277,81.549 C505.277,55.522 519.789,37.382 543.607,37.382 C565.69,37.382 578.782,51.894 578.782,74.766 C578.782,76.816 578.782,79.025 578.625,81.233 L578.625,81.233 Z M543.292,50.001 C530.042,50.001 521.367,60.097 521.051,77.763 L562.22,72.084 C562.062,57.257 554.649,50.001 543.292,50.001 L543.292,50.001 Z' id='Fill-8' />
|
||||
<svg width="140px" height="49px" viewBox="0 0 579 126" version="1.1">
|
||||
<g id="Page-1" stroke="none" strokeWidth={1} fill="none" fillRule="evenodd">
|
||||
<g id="Imported-Layers" fill="#0081C9">
|
||||
<path d="M37.752,125.873 C18.824,125.873 0.369,112.307 0.369,81.549 C0.369,50.79 18.824,37.382 37.752,37.382 C47.059,37.382 54.315,39.749 59.52,43.219 L53.841,55.68 C50.371,53.156 45.166,51.579 39.961,51.579 C28.604,51.579 18.193,60.57 18.193,81.391 C18.193,102.212 28.919,111.361 39.961,111.361 C45.166,111.361 50.371,109.783 53.841,107.26 L59.52,120.036 C54.157,123.664 47.059,125.873 37.752,125.873" id="Fill-1" />
|
||||
<path d="M102.898,125.873 C78.765,125.873 65.515,106.786 65.515,81.549 C65.515,56.311 78.765,37.382 102.898,37.382 C127.032,37.382 140.282,56.311 140.282,81.549 C140.282,106.786 127.032,125.873 102.898,125.873 L102.898,125.873 Z M102.898,51.105 C89.491,51.105 82.866,63.093 82.866,81.391 C82.866,99.688 89.491,111.834 102.898,111.834 C116.306,111.834 122.931,99.688 122.931,81.391 C122.931,63.093 116.306,51.105 102.898,51.105 L102.898,51.105 Z" id="Fill-2" />
|
||||
<path d="M163.468,23.659 C157.79,23.659 153.215,19.243 153.215,13.88 C153.215,8.517 157.79,4.1 163.468,4.1 C169.146,4.1 173.721,8.517 173.721,13.88 C173.721,19.243 169.146,23.659 163.468,23.659 L163.468,23.659 Z M154.793,39.118 L172.144,39.118 L172.144,124.138 L154.793,124.138 L154.793,39.118 Z" id="Fill-3" />
|
||||
<path d="M240.443,124.137 L240.443,67.352 C240.443,57.415 234.449,51.263 222.619,51.263 C216.31,51.263 210.473,52.367 207.003,53.787 L207.003,124.137 L189.81,124.137 L189.81,43.376 C198.328,39.906 209.212,37.382 222.461,37.382 C246.28,37.382 257.794,47.793 257.794,65.775 L257.794,124.137 L240.443,124.137" id="Fill-4" />
|
||||
<path d="M303.536,125.873 C292.494,125.873 281.611,123.191 274.986,119.879 L274.986,0.314 L292.179,0.314 L292.179,41.326 C296.28,39.433 302.905,37.856 308.741,37.856 C330.667,37.856 345.494,53.629 345.494,79.656 C345.494,111.676 328.931,125.873 303.536,125.873 L303.536,125.873 Z M305.744,51.263 C301.012,51.263 295.491,52.367 292.179,54.103 L292.179,109.941 C294.703,111.045 299.593,112.149 304.482,112.149 C318.205,112.149 328.301,102.685 328.301,80.918 C328.301,62.305 319.467,51.263 305.744,51.263 L305.744,51.263 Z" id="Fill-5" />
|
||||
<path d="M392.341,125.873 C367.892,125.873 355.589,115.935 355.589,99.215 C355.589,75.555 380.826,71.296 406.537,69.876 L406.537,64.513 C406.537,53.787 399.439,50.001 388.555,50.001 C380.511,50.001 370.731,52.525 365.053,55.207 L360.636,43.376 C367.419,40.379 378.933,37.382 390.29,37.382 C410.638,37.382 422.942,45.269 422.942,66.248 L422.942,119.879 C416.79,123.191 404.329,125.873 392.341,125.873 L392.341,125.873 Z M406.537,81.391 C389.186,82.337 371.835,83.757 371.835,98.9 C371.835,107.89 378.776,113.411 391.868,113.411 C397.389,113.411 403.856,112.465 406.537,111.203 L406.537,81.391 L406.537,81.391 Z" id="Fill-6" />
|
||||
<path d="M461.743,125.873 C451.806,125.873 441.395,123.191 435.244,119.879 L441.08,106.629 C445.496,109.31 454.803,112.149 461.27,112.149 C470.576,112.149 476.728,107.575 476.728,100.477 C476.728,92.748 470.261,89.751 461.586,86.596 C450.228,82.337 437.452,77.132 437.452,61.201 C437.452,47.162 448.336,37.382 467.264,37.382 C477.517,37.382 486.035,39.906 492.029,43.376 L486.665,55.364 C482.88,52.998 475.309,50.317 469.157,50.317 C460.166,50.317 455.118,55.049 455.118,61.201 C455.118,68.93 461.428,71.611 469.788,74.766 C481.618,79.183 494.71,84.072 494.71,100.635 C494.71,115.935 483.038,125.873 461.743,125.873" id="Fill-7" />
|
||||
<path d="M578.625,81.233 L522.155,89.12 C523.89,104.42 533.828,112.149 548.182,112.149 C556.699,112.149 565.848,110.099 571.684,106.944 L576.732,119.879 C570.107,123.349 558.75,125.873 547.078,125.873 C520.262,125.873 505.277,108.679 505.277,81.549 C505.277,55.522 519.789,37.382 543.607,37.382 C565.69,37.382 578.782,51.894 578.782,74.766 C578.782,76.816 578.782,79.025 578.625,81.233 L578.625,81.233 Z M543.292,50.001 C530.042,50.001 521.367,60.097 521.051,77.763 L562.22,72.084 C562.062,57.257 554.649,50.001 543.292,50.001 L543.292,50.001 Z" id="Fill-8" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
@ -85,13 +85,13 @@ class BuyEtherScreen extends Component {
|
|||
const {goToCoinbase, address} = this.props
|
||||
|
||||
return (
|
||||
<div className='buy-ether__action-content-wrapper'>
|
||||
<div className="buy-ether__action-content-wrapper">
|
||||
<div>{this.renderCoinbaseLogo()}</div>
|
||||
<div className='buy-ether__body-text'>Coinbase is the world’s most popular way to buy and sell bitcoin, ethereum, and litecoin.</div>
|
||||
<a className='first-time-flow__link buy-ether__faq-link'>What is Ethereum?</a>
|
||||
<div className='buy-ether__buttons'>
|
||||
<div className="buy-ether__body-text">Coinbase is the world’s most popular way to buy and sell bitcoin, ethereum, and litecoin.</div>
|
||||
<a className="first-time-flow__link buy-ether__faq-link">What is Ethereum?</a>
|
||||
<div className="buy-ether__buttons">
|
||||
<button
|
||||
className='first-time-flow__button'
|
||||
className="first-time-flow__button"
|
||||
onClick={() => goToCoinbase(address)}
|
||||
>
|
||||
Buy
|
||||
|
@ -114,23 +114,23 @@ class BuyEtherScreen extends Component {
|
|||
return this.renderCoinbaseForm()
|
||||
case OPTION_VALUES.SHAPESHIFT:
|
||||
return (
|
||||
<div className='buy-ether__action-content-wrapper'>
|
||||
<div className='shapeshift-logo' />
|
||||
<div className='buy-ether__body-text'>
|
||||
<div className="buy-ether__action-content-wrapper">
|
||||
<div className="shapeshift-logo" />
|
||||
<div className="buy-ether__body-text">
|
||||
Trade any leading blockchain asset for any other. Protection by Design. No Account Needed.
|
||||
</div>
|
||||
<ShapeShiftForm btnClass='first-time-flow__button' />
|
||||
<ShapeShiftForm btnClass="first-time-flow__button" />
|
||||
</div>
|
||||
)
|
||||
case OPTION_VALUES.QR_CODE:
|
||||
return (
|
||||
<div className='buy-ether__action-content-wrapper'>
|
||||
<div className="buy-ether__action-content-wrapper">
|
||||
<div dangerouslySetInnerHTML={{ __html: qrImage.createTableTag(4) }} />
|
||||
<div className='buy-ether__body-text'>Deposit Ether directly into your account.</div>
|
||||
<div className='buy-ether__small-body-text'>(This is the account address that MetaMask created for you to recieve funds.)</div>
|
||||
<div className='buy-ether__buttons'>
|
||||
<div className="buy-ether__body-text">Deposit Ether directly into your account.</div>
|
||||
<div className="buy-ether__small-body-text">(This is the account address that MetaMask created for you to recieve funds.)</div>
|
||||
<div className="buy-ether__buttons">
|
||||
<button
|
||||
className='first-time-flow__button'
|
||||
className="first-time-flow__button"
|
||||
onClick={this.copyToClipboard}
|
||||
disabled={justCopied}
|
||||
>
|
||||
|
@ -149,19 +149,19 @@ class BuyEtherScreen extends Component {
|
|||
const { selectedOption } = this.state
|
||||
|
||||
return (
|
||||
<div className='buy-ether'>
|
||||
<div className="buy-ether">
|
||||
<Identicon address={this.props.address} diameter={70} />
|
||||
<div className='buy-ether__title'>Deposit Ether</div>
|
||||
<div className='buy-ether__body-text'>
|
||||
<div className="buy-ether__title">Deposit Ether</div>
|
||||
<div className="buy-ether__body-text">
|
||||
MetaMask works best if you have Ether in your account to pay for transaction gas fees and more. To get Ether, choose from one of these methods.
|
||||
</div>
|
||||
<div className='buy-ether__content-wrapper'>
|
||||
<div className='buy-ether__content-headline-wrapper'>
|
||||
<div className='buy-ether__content-headline'>Deposit Options</div>
|
||||
<div className="buy-ether__content-wrapper">
|
||||
<div className="buy-ether__content-headline-wrapper">
|
||||
<div className="buy-ether__content-headline">Deposit Options</div>
|
||||
{this.renderSkip()}
|
||||
</div>
|
||||
<div className='buy-ether__content'>
|
||||
<div className='buy-ether__side-panel'>
|
||||
<div className="buy-ether__content">
|
||||
<div className="buy-ether__side-panel">
|
||||
{OPTIONS.map(({ name, value }) => (
|
||||
<div
|
||||
key={value}
|
||||
|
@ -170,16 +170,16 @@ class BuyEtherScreen extends Component {
|
|||
})}
|
||||
onClick={() => this.setState({ selectedOption: value })}
|
||||
>
|
||||
<div className='buy-ether__side-panel-item-name'>{name}</div>
|
||||
<div className="buy-ether__side-panel-item-name">{name}</div>
|
||||
{value === selectedOption && (
|
||||
<svg viewBox='0 0 574 1024' id='si-ant-right' width='15px' height='15px'>
|
||||
<path d='M10 9Q0 19 0 32t10 23l482 457L10 969Q0 979 0 992t10 23q10 9 24 9t24-9l506-480q10-10 10-23t-10-23L58 9Q48 0 34 0T10 9z' />
|
||||
<svg viewBox="0 0 574 1024" id="si-ant-right" width="15px" height="15px">
|
||||
<path d="M10 9Q0 19 0 32t10 23l482 457L10 969Q0 979 0 992t10 23q10 9 24 9t24-9l506-480q10-10 10-23t-10-23L58 9Q48 0 34 0T10 9z" />
|
||||
</svg>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className='buy-ether__action-content'>
|
||||
<div className="buy-ether__action-content">
|
||||
{this.renderContent()}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -123,10 +123,6 @@
|
|||
width: calc(100vw - 80px);
|
||||
}
|
||||
|
||||
.unique-image {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.create-password__title,
|
||||
.unique-image__title,
|
||||
.tou__title,
|
||||
|
@ -148,7 +144,7 @@
|
|||
height: 100%;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: space-evenly;
|
||||
justify-content: flex-start;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
|
@ -181,7 +177,6 @@
|
|||
margin: 0 !important;
|
||||
padding: 16px 20px !important;
|
||||
height: 30vh !important;
|
||||
width: calc(100% - 48px) !important;
|
||||
}
|
||||
|
||||
.backup-phrase__content-wrapper {
|
||||
|
@ -280,6 +275,12 @@
|
|||
width: 335px;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 575px) {
|
||||
.unique-image__body-text {
|
||||
width: initial;
|
||||
}
|
||||
}
|
||||
|
||||
.unique-image__body-text +
|
||||
.unique-image__body-text,
|
||||
.backup-phrase__body-text +
|
||||
|
@ -294,7 +295,7 @@
|
|||
border-radius: 8px;
|
||||
background-color: #FFFFFF;
|
||||
margin: 0 142px 0 0;
|
||||
height: 334px;
|
||||
height: 200px;
|
||||
overflow-y: auto;
|
||||
color: #757575;
|
||||
font-family: Roboto;
|
||||
|
@ -679,7 +680,7 @@ button.backup-phrase__confirm-seed-option:hover {
|
|||
}
|
||||
|
||||
.first-time-flow__input {
|
||||
width: 350px;
|
||||
max-width: 350px;
|
||||
}
|
||||
|
||||
.first-time-flow__button {
|
||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react'
|
|||
import PropTypes from 'prop-types'
|
||||
import Spinner from './spinner'
|
||||
|
||||
export default function LoadingScreen({ className = '', loadingMessage }) {
|
||||
export default function LoadingScreen ({ className = '', loadingMessage }) {
|
||||
return (
|
||||
<div className={`${className} loading-screen`}>
|
||||
<Spinner color="#1B344D" />
|
||||
|
|
|
@ -14,7 +14,7 @@ import LoadingScreen from './loading-screen'
|
|||
class NoticeScreen extends Component {
|
||||
static propTypes = {
|
||||
address: PropTypes.string.isRequired,
|
||||
lastUnreadNotice: PropTypes.shape({
|
||||
nextUnreadNotice: PropTypes.shape({
|
||||
title: PropTypes.string,
|
||||
date: PropTypes.string,
|
||||
body: PropTypes.string,
|
||||
|
@ -31,7 +31,7 @@ class NoticeScreen extends Component {
|
|||
};
|
||||
|
||||
static defaultProps = {
|
||||
lastUnreadNotice: {},
|
||||
nextUnreadNotice: {},
|
||||
};
|
||||
|
||||
state = {
|
||||
|
@ -47,8 +47,8 @@ class NoticeScreen extends Component {
|
|||
}
|
||||
|
||||
acceptTerms = () => {
|
||||
const { markNoticeRead, lastUnreadNotice, history } = this.props
|
||||
markNoticeRead(lastUnreadNotice)
|
||||
const { markNoticeRead, nextUnreadNotice, history } = this.props
|
||||
markNoticeRead(nextUnreadNotice)
|
||||
.then(hasActiveNotices => {
|
||||
if (!hasActiveNotices) {
|
||||
history.push(INITIALIZE_BACKUP_PHRASE_ROUTE)
|
||||
|
@ -72,7 +72,7 @@ class NoticeScreen extends Component {
|
|||
render () {
|
||||
const {
|
||||
address,
|
||||
lastUnreadNotice: { title, body },
|
||||
nextUnreadNotice: { title, body },
|
||||
isLoading,
|
||||
} = this.props
|
||||
const { atBottom } = this.state
|
||||
|
@ -113,12 +113,12 @@ class NoticeScreen extends Component {
|
|||
}
|
||||
|
||||
const mapStateToProps = ({ metamask, appState }) => {
|
||||
const { selectedAddress, lastUnreadNotice, noActiveNotices } = metamask
|
||||
const { selectedAddress, nextUnreadNotice, noActiveNotices } = metamask
|
||||
const { isLoading } = appState
|
||||
|
||||
return {
|
||||
address: selectedAddress,
|
||||
lastUnreadNotice,
|
||||
nextUnreadNotice,
|
||||
noActiveNotices,
|
||||
isLoading,
|
||||
}
|
||||
|
|
|
@ -79,11 +79,11 @@ export class ShapeShiftForm extends Component {
|
|||
|
||||
renderMetadata (label, value) {
|
||||
return (
|
||||
<div className='shapeshift-form__metadata-wrapper'>
|
||||
<div className='shapeshift-form__metadata-label'>
|
||||
<div className="shapeshift-form__metadata-wrapper">
|
||||
<div className="shapeshift-form__metadata-label">
|
||||
{label}:
|
||||
</div>
|
||||
<div className='shapeshift-form__metadata-value'>
|
||||
<div className="shapeshift-form__metadata-value">
|
||||
{value}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -101,7 +101,7 @@ export class ShapeShiftForm extends Component {
|
|||
} = tokenExchangeRates[coinPair] || {}
|
||||
|
||||
return (
|
||||
<div className='shapeshift-form__metadata'>
|
||||
<div className="shapeshift-form__metadata">
|
||||
{this.renderMetadata('Status', limit ? 'Available' : 'Unavailable')}
|
||||
{this.renderMetadata('Limit', limit)}
|
||||
{this.renderMetadata('Exchange Rate', rate)}
|
||||
|
@ -117,13 +117,13 @@ export class ShapeShiftForm extends Component {
|
|||
qrImage.make()
|
||||
|
||||
return (
|
||||
<div className='shapeshift-form'>
|
||||
<div className='shapeshift-form__deposit-instruction'>
|
||||
<div className="shapeshift-form">
|
||||
<div className="shapeshift-form__deposit-instruction">
|
||||
Deposit your BTC to the address bellow:
|
||||
</div>
|
||||
<div className='shapeshift-form__qr-code'>
|
||||
<div className="shapeshift-form__qr-code">
|
||||
{isLoading
|
||||
? <img src='images/loading.svg' style={{ width: '60px' }} />
|
||||
? <img src="images/loading.svg" style={{ width: '60px' }} />
|
||||
: <div dangerouslySetInnerHTML={{ __html: qrImage.createTableTag(4) }} />
|
||||
}
|
||||
</div>
|
||||
|
@ -141,14 +141,14 @@ export class ShapeShiftForm extends Component {
|
|||
|
||||
return showQrCode ? this.renderQrCode() : (
|
||||
<div>
|
||||
<div className='shapeshift-form'>
|
||||
<div className='shapeshift-form__selectors'>
|
||||
<div className='shapeshift-form__selector'>
|
||||
<div className='shapeshift-form__selector-label'>
|
||||
<div className="shapeshift-form">
|
||||
<div className="shapeshift-form__selectors">
|
||||
<div className="shapeshift-form__selector">
|
||||
<div className="shapeshift-form__selector-label">
|
||||
Deposit
|
||||
</div>
|
||||
<select
|
||||
className='shapeshift-form__selector-input'
|
||||
className="shapeshift-form__selector-input"
|
||||
value={this.state.depositCoin}
|
||||
onChange={this.onCoinChange}
|
||||
>
|
||||
|
@ -160,14 +160,14 @@ export class ShapeShiftForm extends Component {
|
|||
</select>
|
||||
</div>
|
||||
<div
|
||||
className='icon shapeshift-form__caret'
|
||||
className="icon shapeshift-form__caret"
|
||||
style={{ backgroundImage: 'url(images/caret-right.svg)'}}
|
||||
/>
|
||||
<div className='shapeshift-form__selector'>
|
||||
<div className='shapeshift-form__selector-label'>
|
||||
<div className="shapeshift-form__selector">
|
||||
<div className="shapeshift-form__selector-label">
|
||||
Receive
|
||||
</div>
|
||||
<div className='shapeshift-form__selector-input'>
|
||||
<div className="shapeshift-form__selector-input">
|
||||
ETH
|
||||
</div>
|
||||
</div>
|
||||
|
@ -177,18 +177,18 @@ export class ShapeShiftForm extends Component {
|
|||
'shapeshift-form__address-input-wrapper--error': errorMessage,
|
||||
})}
|
||||
>
|
||||
<div className='shapeshift-form__address-input-label'>
|
||||
<div className="shapeshift-form__address-input-label">
|
||||
Your Refund Address
|
||||
</div>
|
||||
<input
|
||||
type='text'
|
||||
className='shapeshift-form__address-input'
|
||||
type="text"
|
||||
className="shapeshift-form__address-input"
|
||||
onChange={e => this.setState({
|
||||
refundAddress: e.target.value,
|
||||
errorMessage: '',
|
||||
})}
|
||||
/>
|
||||
<div className='shapeshift-form__address-input-error-message'>
|
||||
<div className="shapeshift-form__address-input-error-message">
|
||||
{errorMessage}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -37,7 +37,7 @@ const dbController = new DbController({
|
|||
|
||||
start().catch(log.error)
|
||||
|
||||
async function start() {
|
||||
async function start () {
|
||||
log.debug('MetaMask initializing...')
|
||||
const initState = await loadStateFromPersistence()
|
||||
await setupController(initState)
|
||||
|
|
|
@ -43,7 +43,7 @@ console.log('starting service worker')
|
|||
swController.startWorker()
|
||||
|
||||
// Setup listener for when the service worker is read
|
||||
function connectApp() {
|
||||
function connectApp () {
|
||||
const connectionStream = SwStream({
|
||||
serviceWorker: swController.getWorker(),
|
||||
context: name,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
function wait(time) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
setTimeout(function() {
|
||||
export default function wait (time) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
setTimeout(function () {
|
||||
resolve()
|
||||
}, time * 3 || 1500)
|
||||
})
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
var fs = require('fs')
|
||||
var path = require('path')
|
||||
var browserify = require('browserify');
|
||||
var browserify = require('browserify')
|
||||
var tests = fs.readdirSync(path.join(__dirname, 'lib'))
|
||||
var bundlePath = path.join(__dirname, 'test-bundle.js')
|
||||
var b = browserify();
|
||||
var b = browserify()
|
||||
|
||||
// Remove old bundle
|
||||
try {
|
||||
|
@ -14,9 +14,9 @@ try {
|
|||
|
||||
var writeStream = fs.createWriteStream(bundlePath)
|
||||
|
||||
tests.forEach(function(fileName) {
|
||||
tests.forEach(function (fileName) {
|
||||
b.add(path.join(__dirname, 'lib', fileName))
|
||||
})
|
||||
|
||||
b.bundle().pipe(writeStream);
|
||||
b.bundle().pipe(writeStream)
|
||||
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
const Helper = require('./util/mascara-test-helper.js')
|
||||
|
||||
window.addEventListener('load', () => {
|
||||
window.METAMASK_SKIP_RELOAD = true
|
||||
// inject app container
|
||||
|
|
|
@ -2,27 +2,29 @@ const EventEmitter = require('events')
|
|||
const IDB = require('idb-global')
|
||||
const KEY = 'metamask-test-config'
|
||||
module.exports = class Helper extends EventEmitter {
|
||||
constructor () {
|
||||
super()
|
||||
}
|
||||
|
||||
tryToCleanContext () {
|
||||
this.unregister()
|
||||
.then(() => this.clearDb())
|
||||
.then(() => super.emit('complete'))
|
||||
.catch((err) => super.emit('complete'))
|
||||
.catch((err) => {
|
||||
if (err) {
|
||||
super.emit('complete')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
unregister () {
|
||||
return global.navigator.serviceWorker.getRegistration()
|
||||
.then((registration) => {
|
||||
if (registration) return registration.unregister()
|
||||
if (registration) {
|
||||
return registration.unregister()
|
||||
.then((b) => b ? Promise.resolve() : Promise.reject())
|
||||
else return Promise.resolve()
|
||||
} else return Promise.resolve()
|
||||
})
|
||||
}
|
||||
clearDb () {
|
||||
return new Promise ((resolve, reject) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const deleteRequest = global.indexDB.deleteDatabase(KEY)
|
||||
deleteRequest.addEventListener('success', resolve)
|
||||
deleteRequest.addEventListener('error', reject)
|
||||
|
@ -33,7 +35,7 @@ module.exports = class Helper extends EventEmitter {
|
|||
const db = new IDB({
|
||||
version: 2,
|
||||
key: KEY,
|
||||
initialState: state
|
||||
initialState: state,
|
||||
})
|
||||
return db.open()
|
||||
}
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
Dear MetaMask Users,
|
||||
|
||||
There have been several instances of high-profile legitimate websites such as BTC Manager and Games Workshop that have had their websites temporarily compromised. This involves showing a fake MetaMask window on the page asking for user's seed phrases. MetaMask will never open itself in this way and users are encouraged to report these instances immediately to either [our phishing blacklist](https://github.com/MetaMask/eth-phishing-detect/issues) or our support email at [support@metamask.io](mailto:support@metamask.io).
|
||||
|
||||
Please read our full article on this ongoing issue at [https://medium.com/metamask/new-phishing-strategy-becoming-common-1b1123837168](https://medium.com/metamask/new-phishing-strategy-becoming-common-1b1123837168).
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
var fs = require('fs')
|
||||
var path = require('path')
|
||||
var prompt = require('prompt')
|
||||
var open = require('open')
|
||||
var extend = require('extend')
|
||||
var notices = require('./notices.json')
|
||||
|
||||
|
||||
console.log('List of Notices')
|
||||
console.log(`ID \t DATE \t\t\t TITLE`)
|
||||
notices.forEach((notice) => {
|
||||
console.log(`${(' ' + notice.id).slice(-2)} \t ${notice.date} \t ${notice.title}`)
|
||||
})
|
||||
prompt.get(['id'], (error, res) => {
|
||||
prompt.start()
|
||||
if (error) {
|
||||
console.log("Exiting...")
|
||||
process.exit()
|
||||
}
|
||||
var index = notices.findIndex((notice) => { return notice.id == res.id})
|
||||
if (index === -1) {
|
||||
console.log('Notice not found. Exiting...')
|
||||
}
|
||||
notices.splice(index, 1)
|
||||
fs.unlink(`notices/archive/notice_${res.id}.md`)
|
||||
fs.writeFile(`notices/notices.json`, JSON.stringify(notices))
|
||||
})
|
|
@ -1,33 +0,0 @@
|
|||
var fsp = require('fs-promise')
|
||||
var path = require('path')
|
||||
var prompt = require('prompt')
|
||||
var open = require('open')
|
||||
var extend = require('extend')
|
||||
var notices = require('./notices.json')
|
||||
var id = Number(require('./notice-nonce.json'))
|
||||
|
||||
var date = new Date().toDateString()
|
||||
|
||||
var notice = {
|
||||
read: false,
|
||||
date: date,
|
||||
}
|
||||
|
||||
fsp.writeFile(`notices/archive/notice_${id}.md`,'Message goes here. Please write out your notice and save before proceeding at the command line.')
|
||||
.then(() => {
|
||||
open(`notices/archive/notice_${id}.md`)
|
||||
prompt.start()
|
||||
prompt.get(['title'], (err, result) => {
|
||||
notice.title = result.title
|
||||
fsp.readFile(`notices/archive/notice_${id}.md`)
|
||||
.then((body) => {
|
||||
notice.body = body.toString()
|
||||
notice.id = id
|
||||
notices.push(notice)
|
||||
return fsp.writeFile(`notices/notices.json`, JSON.stringify(notices))
|
||||
}).then((completion) => {
|
||||
id += 1
|
||||
return fsp.writeFile(`notices/notice-nonce.json`, id)
|
||||
})
|
||||
})
|
||||
})
|
|
@ -1 +0,0 @@
|
|||
4
|
|
@ -0,0 +1,35 @@
|
|||
// fs.readFileSync is inlined by browserify transform "brfs"
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
id: 0,
|
||||
read: false,
|
||||
date: 'Thu Feb 09 2017',
|
||||
title: 'Terms of Use',
|
||||
body: fs.readFileSync(path.join(__dirname, '/archive', 'notice_0.md'), 'utf8'),
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
read: false,
|
||||
date: 'Mon May 08 2017',
|
||||
title: 'Privacy Notice',
|
||||
body: fs.readFileSync(path.join(__dirname, '/archive', 'notice_2.md'), 'utf8'),
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
read: false,
|
||||
date: 'Tue Nov 28 2017',
|
||||
title: 'Seed Phrase Alert',
|
||||
firstVersion: '<=3.12.0',
|
||||
body: fs.readFileSync(path.join(__dirname, '/archive', 'notice_3.md'), 'utf8'),
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
read: false,
|
||||
date: 'Wed Jun 13 2018',
|
||||
title: 'Phishing Warning',
|
||||
body: fs.readFileSync(path.join(__dirname, '/archive', 'notice_4.md'), 'utf8'),
|
||||
},
|
||||
]
|
File diff suppressed because one or more lines are too long
|
@ -73,7 +73,7 @@ function mapStateToProps (state) {
|
|||
network: state.metamask.network,
|
||||
provider: state.metamask.provider,
|
||||
forgottenPassword: state.appState.forgottenPassword,
|
||||
lastUnreadNotice: state.metamask.lastUnreadNotice,
|
||||
nextUnreadNotice: state.metamask.nextUnreadNotice,
|
||||
lostAccounts: state.metamask.lostAccounts,
|
||||
frequentRpcList: state.metamask.frequentRpcList || [],
|
||||
featureFlags,
|
||||
|
@ -461,9 +461,9 @@ App.prototype.renderPrimary = function () {
|
|||
}, [
|
||||
|
||||
h(NoticeScreen, {
|
||||
notice: props.lastUnreadNotice,
|
||||
notice: props.nextUnreadNotice,
|
||||
key: 'NoticeScreen',
|
||||
onConfirm: () => props.dispatch(actions.markNoticeRead(props.lastUnreadNotice)),
|
||||
onConfirm: () => props.dispatch(actions.markNoticeRead(props.nextUnreadNotice)),
|
||||
}),
|
||||
|
||||
!props.isInitialized && h('.flex-row.flex-center.flex-grow', [
|
||||
|
|
|
@ -23,9 +23,10 @@ class AccountDropdowns extends Component {
|
|||
|
||||
renderAccounts () {
|
||||
const { identities, selected, keyrings } = this.props
|
||||
const accountOrder = keyrings.reduce((list, keyring) => list.concat(keyring.accounts), [])
|
||||
|
||||
return Object.keys(identities).map((key, index) => {
|
||||
const identity = identities[key]
|
||||
return accountOrder.map((address, index) => {
|
||||
const identity = identities[address]
|
||||
const isSelected = identity.address === selected
|
||||
|
||||
const simpleAddress = identity.address.substring(2).toLowerCase()
|
||||
|
|
|
@ -20,7 +20,7 @@ function EnsInput () {
|
|||
EnsInput.prototype.render = function () {
|
||||
const props = this.props
|
||||
|
||||
function onInputChange() {
|
||||
function onInputChange () {
|
||||
const network = this.props.network
|
||||
const networkHasEnsSupport = getNetworkEnsSupport(network)
|
||||
if (!networkHasEnsSupport) return
|
||||
|
|
|
@ -28,7 +28,7 @@ LoadingIndicator.prototype.render = function () {
|
|||
background: 'rgba(255, 255, 255, 0.8)',
|
||||
},
|
||||
}, [
|
||||
canBypass ? h( 'i.fa.fa-close.cursor-pointer.close-loading', {
|
||||
canBypass ? h('i.fa.fa-close.cursor-pointer.close-loading', {
|
||||
style: {
|
||||
position: 'absolute',
|
||||
top: '1px',
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue