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-npm
|
||||||
- prep-deps-firefox
|
- prep-deps-firefox
|
||||||
- prep-build
|
- 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:
|
- test-unit:
|
||||||
requires:
|
requires:
|
||||||
- prep-deps-npm
|
- prep-deps-npm
|
||||||
|
@ -57,6 +66,8 @@ workflows:
|
||||||
- test-unit
|
- test-unit
|
||||||
- test-e2e-chrome
|
- test-e2e-chrome
|
||||||
- test-e2e-firefox
|
- test-e2e-firefox
|
||||||
|
- test-e2e-beta-chrome
|
||||||
|
- test-e2e-beta-firefox
|
||||||
- test-integration-mascara-chrome
|
- test-integration-mascara-chrome
|
||||||
- test-integration-mascara-firefox
|
- test-integration-mascara-firefox
|
||||||
- test-integration-flat-chrome
|
- test-integration-flat-chrome
|
||||||
|
@ -86,7 +97,7 @@ workflows:
|
||||||
jobs:
|
jobs:
|
||||||
prep-deps-npm:
|
prep-deps-npm:
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/node:8-browsers
|
- image: circleci/node:8.11.3-browsers
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
|
@ -105,14 +116,12 @@ jobs:
|
||||||
|
|
||||||
prep-deps-firefox:
|
prep-deps-firefox:
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/node:8-browsers
|
- image: circleci/node:8.11.3-browsers
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- run:
|
- run:
|
||||||
name: Download Firefox
|
name: Download Firefox
|
||||||
command: >
|
command: ./.circleci/scripts/firefox-download.sh
|
||||||
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
|
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: dependency-cache-firefox-{{ .Revision }}
|
key: dependency-cache-firefox-{{ .Revision }}
|
||||||
paths:
|
paths:
|
||||||
|
@ -120,7 +129,7 @@ jobs:
|
||||||
|
|
||||||
prep-build:
|
prep-build:
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/node:8-browsers
|
- image: circleci/node:8.11.3-browsers
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
|
@ -139,7 +148,7 @@ jobs:
|
||||||
|
|
||||||
prep-docs:
|
prep-docs:
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/node:8-browsers
|
- image: circleci/node:8.11.3-browsers
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
|
@ -154,7 +163,7 @@ jobs:
|
||||||
|
|
||||||
prep-scss:
|
prep-scss:
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/node:8-browsers
|
- image: circleci/node:8.11.3-browsers
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
|
@ -173,7 +182,7 @@ jobs:
|
||||||
|
|
||||||
test-lint:
|
test-lint:
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/node:8-browsers
|
- image: circleci/node:8.11.3-browsers
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
|
@ -184,7 +193,7 @@ jobs:
|
||||||
|
|
||||||
test-deps:
|
test-deps:
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/node:8-browsers
|
- image: circleci/node:8.11.3-browsers
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
|
@ -195,7 +204,7 @@ jobs:
|
||||||
|
|
||||||
test-e2e-chrome:
|
test-e2e-chrome:
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/node:8-browsers
|
- image: circleci/node:8.11.3-browsers
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
|
@ -203,28 +212,22 @@ jobs:
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
key: build-cache-{{ .Revision }}
|
key: build-cache-{{ .Revision }}
|
||||||
- run:
|
- run:
|
||||||
name: Test
|
name: test:e2e:chrome
|
||||||
command: npm run test:e2e:chrome
|
command: npm run test:e2e:chrome
|
||||||
- store_artifacts:
|
- store_artifacts:
|
||||||
path: test-artifacts
|
path: test-artifacts
|
||||||
destination: test-artifacts
|
destination: test-artifacts
|
||||||
|
|
||||||
test-e2e-firefox:
|
test-e2e-firefox:
|
||||||
environment:
|
|
||||||
browsers: '["Firefox"]'
|
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/node:8-browsers
|
- image: circleci/node:8.11.3-browsers
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
key: dependency-cache-firefox-{{ .Revision }}
|
key: dependency-cache-firefox-{{ .Revision }}
|
||||||
- run:
|
- run:
|
||||||
name: Install firefox
|
name: Install firefox
|
||||||
command: >
|
command: ./.circleci/scripts/firefox-install.sh
|
||||||
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
|
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
key: dependency-cache-{{ .Revision }}
|
key: dependency-cache-{{ .Revision }}
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
|
@ -236,9 +239,46 @@ jobs:
|
||||||
path: test-artifacts
|
path: test-artifacts
|
||||||
destination: 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:
|
job-screens:
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/node:8-browsers
|
- image: circleci/node:8.11.3-browsers
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
|
@ -255,7 +295,7 @@ jobs:
|
||||||
|
|
||||||
job-publish-prerelease:
|
job-publish-prerelease:
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/node:8-browsers
|
- image: circleci/node:8.11.3-browsers
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
|
@ -282,7 +322,7 @@ jobs:
|
||||||
|
|
||||||
job-publish-release:
|
job-publish-release:
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/node:8-browsers
|
- image: circleci/node:8.11.3-browsers
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
|
@ -305,7 +345,7 @@ jobs:
|
||||||
|
|
||||||
test-unit:
|
test-unit:
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/node:8-browsers
|
- image: circleci/node:8.11.3-browsers
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
|
@ -318,18 +358,14 @@ jobs:
|
||||||
environment:
|
environment:
|
||||||
browsers: '["Firefox"]'
|
browsers: '["Firefox"]'
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/node:8-browsers
|
- image: circleci/node:8.11.3-browsers
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
key: dependency-cache-firefox-{{ .Revision }}
|
key: dependency-cache-firefox-{{ .Revision }}
|
||||||
- run:
|
- run:
|
||||||
name: Install firefox
|
name: Install firefox
|
||||||
command: >
|
command: ./.circleci/scripts/firefox-install.sh
|
||||||
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
|
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
key: dependency-cache-{{ .Revision }}
|
key: dependency-cache-{{ .Revision }}
|
||||||
- run:
|
- run:
|
||||||
|
@ -346,7 +382,7 @@ jobs:
|
||||||
environment:
|
environment:
|
||||||
browsers: '["Chrome"]'
|
browsers: '["Chrome"]'
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/node:8-browsers
|
- image: circleci/node:8.11.3-browsers
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
|
@ -365,18 +401,14 @@ jobs:
|
||||||
environment:
|
environment:
|
||||||
browsers: '["Firefox"]'
|
browsers: '["Firefox"]'
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/node:8-browsers
|
- image: circleci/node:8.11.3-browsers
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
key: dependency-cache-firefox-{{ .Revision }}
|
key: dependency-cache-firefox-{{ .Revision }}
|
||||||
- run:
|
- run:
|
||||||
name: Install firefox
|
name: Install firefox
|
||||||
command: >
|
command: ./.circleci/scripts/firefox-install.sh
|
||||||
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
|
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
key: dependency-cache-{{ .Revision }}
|
key: dependency-cache-{{ .Revision }}
|
||||||
- run:
|
- run:
|
||||||
|
@ -393,7 +425,7 @@ jobs:
|
||||||
environment:
|
environment:
|
||||||
browsers: '["Chrome"]'
|
browsers: '["Chrome"]'
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/node:8-browsers
|
- image: circleci/node:8.11.3-browsers
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
|
@ -410,9 +442,8 @@ jobs:
|
||||||
|
|
||||||
all-tests-pass:
|
all-tests-pass:
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/node:8-browsers
|
- image: circleci/node:8.11.3-browsers
|
||||||
steps:
|
steps:
|
||||||
- run:
|
- run:
|
||||||
name: All Tests Passed
|
name: All Tests Passed
|
||||||
command: echo 'weew - everything 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/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/bundle.js
|
||||||
test/integration/jquery-3.1.0.min.js
|
test/integration/jquery-3.1.0.min.js
|
||||||
test/integration/helpers.js
|
test/integration/helpers.js
|
||||||
test/integration/lib/first-time.js
|
test/integration/lib/first-time.js
|
||||||
ui/lib/blockies.js
|
|
|
@ -37,7 +37,9 @@
|
||||||
"document": false,
|
"document": false,
|
||||||
"navigator": false,
|
"navigator": false,
|
||||||
"web3": true,
|
"web3": true,
|
||||||
"window": false
|
"window": false,
|
||||||
|
"$": false,
|
||||||
|
"QUnit": false
|
||||||
},
|
},
|
||||||
|
|
||||||
"rules": {
|
"rules": {
|
||||||
|
@ -142,6 +144,7 @@
|
||||||
"operator-linebreak": [1, "after", { "overrides": { "?": "ignore", ":": "ignore" } }],
|
"operator-linebreak": [1, "after", { "overrides": { "?": "ignore", ":": "ignore" } }],
|
||||||
"padded-blocks": "off",
|
"padded-blocks": "off",
|
||||||
"quotes": [2, "single", {"avoidEscape": true, "allowTemplateLiterals": true}],
|
"quotes": [2, "single", {"avoidEscape": true, "allowTemplateLiterals": true}],
|
||||||
|
"react/no-deprecated": 0,
|
||||||
"semi": [2, "never"],
|
"semi": [2, "never"],
|
||||||
"semi-spacing": [2, { "before": false, "after": true }],
|
"semi-spacing": [2, { "before": false, "after": true }],
|
||||||
"space-before-blocks": [1, "always"],
|
"space-before-blocks": [1, "always"],
|
||||||
|
@ -158,5 +161,6 @@
|
||||||
"yield-star-spacing": [2, "both"],
|
"yield-star-spacing": [2, "both"],
|
||||||
"yoda": [2, "never"],
|
"yoda": [2, "never"],
|
||||||
"prefer-const": 1,
|
"prefer-const": 1,
|
||||||
|
"mocha/no-exclusive-tests": "error"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
36
CHANGELOG.md
36
CHANGELOG.md
|
@ -2,6 +2,42 @@
|
||||||
|
|
||||||
## Current Master
|
## 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 error messages when passwords don't match in onboarding flow.
|
||||||
- Adds modal notification if a retry in the process of being confirmed is dropped.
|
- Adds modal notification if a retry in the process of being confirmed is dropped.
|
||||||
- New unlock screen design.
|
- 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
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
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
|
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
|
## Building locally
|
||||||
|
|
||||||
- Install [Node.js](https://nodejs.org/en/) version 6.3.1 or later.
|
- 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`.
|
- Install gulp globally with `npm install -g gulp-cli`.
|
||||||
- Build the project to the `./dist/` folder with `gulp build`.
|
- Build the project to the `./dist/` folder with `gulp build`.
|
||||||
- Optionally, to rebuild on file changes, run `gulp dev`.
|
- Optionally, to rebuild on file changes, run `gulp dev`.
|
||||||
|
|
|
@ -146,6 +146,9 @@
|
||||||
"copy": {
|
"copy": {
|
||||||
"message": "Copy"
|
"message": "Copy"
|
||||||
},
|
},
|
||||||
|
"copyContractAddress": {
|
||||||
|
"message": "Copy Contract Address"
|
||||||
|
},
|
||||||
"copyToClipboard": {
|
"copyToClipboard": {
|
||||||
"message": "Copy to clipboard"
|
"message": "Copy to clipboard"
|
||||||
},
|
},
|
||||||
|
@ -253,12 +256,18 @@
|
||||||
"editAccountName": {
|
"editAccountName": {
|
||||||
"message": "Edit Account Name"
|
"message": "Edit Account Name"
|
||||||
},
|
},
|
||||||
|
"editingTransaction": {
|
||||||
|
"message": "Make changes to your transaction"
|
||||||
|
},
|
||||||
"emailUs": {
|
"emailUs": {
|
||||||
"message": "Email us!"
|
"message": "Email us!"
|
||||||
},
|
},
|
||||||
"encryptNewDen": {
|
"encryptNewDen": {
|
||||||
"message": "Encrypt your new DEN"
|
"message": "Encrypt your new DEN"
|
||||||
},
|
},
|
||||||
|
"ensNameNotFound": {
|
||||||
|
"message": "ENS name not found"
|
||||||
|
},
|
||||||
"enterPassword": {
|
"enterPassword": {
|
||||||
"message": "Enter password"
|
"message": "Enter password"
|
||||||
},
|
},
|
||||||
|
@ -771,6 +780,10 @@
|
||||||
"onlySendToEtherAddress": {
|
"onlySendToEtherAddress": {
|
||||||
"message": "Only send ETH to an Ethereum address."
|
"message": "Only send ETH to an Ethereum address."
|
||||||
},
|
},
|
||||||
|
"onlySendTokensToAccountAddress": {
|
||||||
|
"message": "Only send $1 to an Ethereum account address.",
|
||||||
|
"description": "displays token symbol"
|
||||||
|
},
|
||||||
"searchTokens": {
|
"searchTokens": {
|
||||||
"message": "Search Tokens"
|
"message": "Search Tokens"
|
||||||
},
|
},
|
||||||
|
@ -948,6 +961,9 @@
|
||||||
"viewAccount": {
|
"viewAccount": {
|
||||||
"message": "View Account"
|
"message": "View Account"
|
||||||
},
|
},
|
||||||
|
"viewOnEtherscan": {
|
||||||
|
"message": "View on Etherscan"
|
||||||
|
},
|
||||||
"visitWebSite": {
|
"visitWebSite": {
|
||||||
"message": "Visit our web site"
|
"message": "Visit our web site"
|
||||||
},
|
},
|
||||||
|
|
|
@ -62,6 +62,9 @@
|
||||||
"message": " $1以上 $2以下にして下さい。",
|
"message": " $1以上 $2以下にして下さい。",
|
||||||
"description": "helper for inputting hex as decimal input"
|
"description": "helper for inputting hex as decimal input"
|
||||||
},
|
},
|
||||||
|
"blockiesIdenticon": {
|
||||||
|
"message": "Blockies Identicon を使用"
|
||||||
|
},
|
||||||
"borrowDharma": {
|
"borrowDharma": {
|
||||||
"message": "Dharmaで借りる(ベータ版)"
|
"message": "Dharmaで借りる(ベータ版)"
|
||||||
},
|
},
|
||||||
|
@ -95,6 +98,9 @@
|
||||||
"confirmTransaction": {
|
"confirmTransaction": {
|
||||||
"message": "トランザクションの確認"
|
"message": "トランザクションの確認"
|
||||||
},
|
},
|
||||||
|
"continue": {
|
||||||
|
"message": "続行"
|
||||||
|
},
|
||||||
"continueToCoinbase": {
|
"continueToCoinbase": {
|
||||||
"message": "Coinbaseを開く"
|
"message": "Coinbaseを開く"
|
||||||
},
|
},
|
||||||
|
@ -359,6 +365,9 @@
|
||||||
"likeToAddTokens": {
|
"likeToAddTokens": {
|
||||||
"message": "トークンを追加しますか?"
|
"message": "トークンを追加しますか?"
|
||||||
},
|
},
|
||||||
|
"links": {
|
||||||
|
"message": "リンク"
|
||||||
|
},
|
||||||
"limit": {
|
"limit": {
|
||||||
"message": "リミット"
|
"message": "リミット"
|
||||||
},
|
},
|
||||||
|
@ -371,12 +380,18 @@
|
||||||
"localhost": {
|
"localhost": {
|
||||||
"message": "Localhost 8545"
|
"message": "Localhost 8545"
|
||||||
},
|
},
|
||||||
|
"login": {
|
||||||
|
"message": "ログイン"
|
||||||
|
},
|
||||||
"logout": {
|
"logout": {
|
||||||
"message": "ログアウト"
|
"message": "ログアウト"
|
||||||
},
|
},
|
||||||
"loose": {
|
"loose": {
|
||||||
"message": "外部秘密鍵"
|
"message": "外部秘密鍵"
|
||||||
},
|
},
|
||||||
|
"max": {
|
||||||
|
"message": "最大"
|
||||||
|
},
|
||||||
"mainnet": {
|
"mainnet": {
|
||||||
"message": "Ethereumメインネットワーク"
|
"message": "Ethereumメインネットワーク"
|
||||||
},
|
},
|
||||||
|
@ -417,7 +432,7 @@
|
||||||
"message": "新規コントラクト"
|
"message": "新規コントラクト"
|
||||||
},
|
},
|
||||||
"newPassword": {
|
"newPassword": {
|
||||||
"message": "新規パスワード(最低8文字)"
|
"message": "新規パスワード(最低8文字)"
|
||||||
},
|
},
|
||||||
"newRecipient": {
|
"newRecipient": {
|
||||||
"message": "新規受取人"
|
"message": "新規受取人"
|
||||||
|
@ -453,6 +468,9 @@
|
||||||
"message": "または",
|
"message": "または",
|
||||||
"description": "choice between creating or importing a new account"
|
"description": "choice between creating or importing a new account"
|
||||||
},
|
},
|
||||||
|
"password": {
|
||||||
|
"message": "パスワード"
|
||||||
|
},
|
||||||
"passwordMismatch": {
|
"passwordMismatch": {
|
||||||
"message": "パスワードが一致しません。",
|
"message": "パスワードが一致しません。",
|
||||||
"description": "in password creation process, the two new password fields did not match"
|
"description": "in password creation process, the two new password fields did not match"
|
||||||
|
@ -474,6 +492,9 @@
|
||||||
"popularTokens": {
|
"popularTokens": {
|
||||||
"message": "人気のトークン"
|
"message": "人気のトークン"
|
||||||
},
|
},
|
||||||
|
"privacyMsg": {
|
||||||
|
"message": "プライバシーポリシー"
|
||||||
|
},
|
||||||
"privateKey": {
|
"privateKey": {
|
||||||
"message": "秘密鍵",
|
"message": "秘密鍵",
|
||||||
"description": "select this type of file to use to import an account"
|
"description": "select this type of file to use to import an account"
|
||||||
|
@ -546,6 +567,12 @@
|
||||||
"message": "ファイルとして保存",
|
"message": "ファイルとして保存",
|
||||||
"description": "Account export process"
|
"description": "Account export process"
|
||||||
},
|
},
|
||||||
|
"search": {
|
||||||
|
"message": "検索"
|
||||||
|
},
|
||||||
|
"searchResults": {
|
||||||
|
"message": "検索結果"
|
||||||
|
},
|
||||||
"selectService": {
|
"selectService": {
|
||||||
"message": "サービスを選択"
|
"message": "サービスを選択"
|
||||||
},
|
},
|
||||||
|
@ -609,6 +636,9 @@
|
||||||
"takesTooLong": {
|
"takesTooLong": {
|
||||||
"message": "送信に時間がかかりますか?"
|
"message": "送信に時間がかかりますか?"
|
||||||
},
|
},
|
||||||
|
"terms": {
|
||||||
|
"message": "利用規約"
|
||||||
|
},
|
||||||
"testFaucet": {
|
"testFaucet": {
|
||||||
"message": "Faucetをテスト"
|
"message": "Faucetをテスト"
|
||||||
},
|
},
|
||||||
|
@ -619,6 +649,9 @@
|
||||||
"message": "ShapeShiftで $1をETHにする",
|
"message": "ShapeShiftで $1をETHにする",
|
||||||
"description": "system will fill in deposit type in start of message"
|
"description": "system will fill in deposit type in start of message"
|
||||||
},
|
},
|
||||||
|
"token": {
|
||||||
|
"message": "トークン"
|
||||||
|
},
|
||||||
"tokenAddress": {
|
"tokenAddress": {
|
||||||
"message": "トークンアドレス"
|
"message": "トークンアドレス"
|
||||||
},
|
},
|
||||||
|
@ -690,6 +723,12 @@
|
||||||
"warning": {
|
"warning": {
|
||||||
"message": "警告"
|
"message": "警告"
|
||||||
},
|
},
|
||||||
|
"welcomeBack": {
|
||||||
|
"message": "おかえりなさい!"
|
||||||
|
},
|
||||||
|
"welcomeBeta": {
|
||||||
|
"message": "MetaMask ベータ版へようこそ!"
|
||||||
|
},
|
||||||
"whatsThis": {
|
"whatsThis": {
|
||||||
"message": "この機能について"
|
"message": "この機能について"
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "__MSG_appName__",
|
"name": "__MSG_appName__",
|
||||||
"short_name": "__MSG_appName__",
|
"short_name": "__MSG_appName__",
|
||||||
"version": "4.6.1",
|
"version": "4.8.0",
|
||||||
"manifest_version": 2,
|
"manifest_version": 2,
|
||||||
"author": "https://metamask.io",
|
"author": "https://metamask.io",
|
||||||
"description": "__MSG_appDescription__",
|
"description": "__MSG_appDescription__",
|
||||||
|
@ -71,6 +71,8 @@
|
||||||
"matches": [
|
"matches": [
|
||||||
"https://metamask.io/*"
|
"https://metamask.io/*"
|
||||||
],
|
],
|
||||||
"ids": ["*"]
|
"ids": [
|
||||||
|
"*"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,18 @@ const accountImporter = {
|
||||||
|
|
||||||
strategies: {
|
strategies: {
|
||||||
'Private Key': (privateKey) => {
|
'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
|
return stripped
|
||||||
},
|
},
|
||||||
'JSON File': (input, password) => {
|
'JSON File': (input, password) => {
|
||||||
|
|
|
@ -16,6 +16,7 @@ const ExtensionPlatform = require('./platforms/extension')
|
||||||
const Migrator = require('./lib/migrator/')
|
const Migrator = require('./lib/migrator/')
|
||||||
const migrations = require('./migrations/')
|
const migrations = require('./migrations/')
|
||||||
const PortStream = require('./lib/port-stream.js')
|
const PortStream = require('./lib/port-stream.js')
|
||||||
|
const createStreamSink = require('./lib/createStreamSink')
|
||||||
const NotificationManager = require('./lib/notification-manager.js')
|
const NotificationManager = require('./lib/notification-manager.js')
|
||||||
const MetamaskController = require('./metamask-controller')
|
const MetamaskController = require('./metamask-controller')
|
||||||
const firstTimeState = require('./first-time-state')
|
const firstTimeState = require('./first-time-state')
|
||||||
|
@ -279,7 +280,7 @@ function setupController (initState, initLangCode) {
|
||||||
asStream(controller.store),
|
asStream(controller.store),
|
||||||
debounce(1000),
|
debounce(1000),
|
||||||
storeTransform(versionifyData),
|
storeTransform(versionifyData),
|
||||||
storeTransform(persistData),
|
createStreamSink(persistData),
|
||||||
(error) => {
|
(error) => {
|
||||||
log.error('MetaMask - Persistence pipeline failed', error)
|
log.error('MetaMask - Persistence pipeline failed', error)
|
||||||
}
|
}
|
||||||
|
@ -295,7 +296,7 @@ function setupController (initState, initLangCode) {
|
||||||
return versionedData
|
return versionedData
|
||||||
}
|
}
|
||||||
|
|
||||||
function persistData (state) {
|
async function persistData (state) {
|
||||||
if (!state) {
|
if (!state) {
|
||||||
throw new Error('MetaMask - updated state is missing', 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)
|
throw new Error('MetaMask - updated state does not have data', state)
|
||||||
}
|
}
|
||||||
if (localStore.isSupported) {
|
if (localStore.isSupported) {
|
||||||
localStore.set(state)
|
try {
|
||||||
.catch((err) => {
|
await localStore.set(state)
|
||||||
|
} catch (err) {
|
||||||
|
// log error so we dont break the pipeline
|
||||||
log.error('error setting state in local store:', err)
|
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
|
// communication with page or other extension
|
||||||
function connectExternal(remotePort) {
|
function connectExternal (remotePort) {
|
||||||
const originDomain = urlUtil.parse(remotePort.sender.url).hostname
|
const originDomain = urlUtil.parse(remotePort.sender.url).hostname
|
||||||
const portStream = new PortStream(remotePort)
|
const portStream = new PortStream(remotePort)
|
||||||
controller.setupUntrustedCommunication(portStream, originDomain)
|
controller.setupUntrustedCommunication(portStream, originDomain)
|
||||||
|
|
|
@ -115,8 +115,8 @@ function logStreamDisconnectWarning (remoteLabel, err) {
|
||||||
* @returns {boolean} {@code true} if Web3 should be injected
|
* @returns {boolean} {@code true} if Web3 should be injected
|
||||||
*/
|
*/
|
||||||
function shouldInjectWeb3 () {
|
function shouldInjectWeb3 () {
|
||||||
return doctypeCheck() && suffixCheck()
|
return doctypeCheck() && suffixCheck() &&
|
||||||
&& documentElementCheck() && !blacklistedDomainCheck()
|
documentElementCheck() && !blacklistedDomainCheck()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -176,6 +176,7 @@ function blacklistedDomainCheck () {
|
||||||
'webbyawards.com',
|
'webbyawards.com',
|
||||||
'cdn.shopify.com/s/javascripts/tricorder/xtld-read-only-frame.html',
|
'cdn.shopify.com/s/javascripts/tricorder/xtld-read-only-frame.html',
|
||||||
'adyen.com',
|
'adyen.com',
|
||||||
|
'gravityforms.com',
|
||||||
]
|
]
|
||||||
var currentUrl = window.location.href
|
var currentUrl = window.location.href
|
||||||
var currentRegex
|
var currentRegex
|
||||||
|
|
|
@ -89,14 +89,21 @@ module.exports = class NetworkController extends EventEmitter {
|
||||||
type: 'rpc',
|
type: 'rpc',
|
||||||
rpcTarget,
|
rpcTarget,
|
||||||
}
|
}
|
||||||
this.providerStore.updateState(providerConfig)
|
this.providerConfig = providerConfig
|
||||||
this._switchNetwork(providerConfig)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async setProviderType (type) {
|
async setProviderType (type) {
|
||||||
assert.notEqual(type, 'rpc', `NetworkController - cannot call "setProviderType" with type 'rpc'. use "setRpcTarget"`)
|
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}"`)
|
assert(INFURA_PROVIDER_TYPES.includes(type) || type === LOCALHOST, `NetworkController - Unknown rpc type "${type}"`)
|
||||||
const providerConfig = { type }
|
const providerConfig = { type }
|
||||||
|
this.providerConfig = providerConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
resetConnection () {
|
||||||
|
this.providerConfig = this.getProviderConfig()
|
||||||
|
}
|
||||||
|
|
||||||
|
set providerConfig (providerConfig) {
|
||||||
this.providerStore.updateState(providerConfig)
|
this.providerStore.updateState(providerConfig)
|
||||||
this._switchNetwork(providerConfig)
|
this._switchNetwork(providerConfig)
|
||||||
}
|
}
|
||||||
|
@ -125,7 +132,7 @@ module.exports = class NetworkController extends EventEmitter {
|
||||||
} else if (type === LOCALHOST) {
|
} else if (type === LOCALHOST) {
|
||||||
this._configureStandardProvider({ rpcUrl: LOCALHOST_RPC_URL })
|
this._configureStandardProvider({ rpcUrl: LOCALHOST_RPC_URL })
|
||||||
// url-based rpc endpoints
|
// url-based rpc endpoints
|
||||||
} else if (type === 'rpc'){
|
} else if (type === 'rpc') {
|
||||||
this._configureStandardProvider({ rpcUrl: rpcTarget })
|
this._configureStandardProvider({ rpcUrl: rpcTarget })
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`NetworkController - _configureProvider - unknown type "${type}"`)
|
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 normalizeAddress = require('eth-sig-util').normalize
|
||||||
const extend = require('xtend')
|
const extend = require('xtend')
|
||||||
|
|
||||||
|
|
||||||
class PreferencesController {
|
class PreferencesController {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -28,7 +29,11 @@ class PreferencesController {
|
||||||
featureFlags: {},
|
featureFlags: {},
|
||||||
currentLocale: opts.initLangCode,
|
currentLocale: opts.initLangCode,
|
||||||
identities: {},
|
identities: {},
|
||||||
|
lostIdentities: {},
|
||||||
}, opts.initState)
|
}, opts.initState)
|
||||||
|
|
||||||
|
this.diagnostics = opts.diagnostics
|
||||||
|
|
||||||
this.store = new ObservableStore(initState)
|
this.store = new ObservableStore(initState)
|
||||||
}
|
}
|
||||||
// PUBLIC METHODS
|
// PUBLIC METHODS
|
||||||
|
@ -63,6 +68,13 @@ class PreferencesController {
|
||||||
this.store.updateState({ currentLocale: key })
|
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) {
|
setAddresses (addresses) {
|
||||||
const oldIdentities = this.store.getState().identities
|
const oldIdentities = this.store.getState().identities
|
||||||
const identities = addresses.reduce((ids, address, index) => {
|
const identities = addresses.reduce((ids, address, index) => {
|
||||||
|
@ -73,6 +85,68 @@ class PreferencesController {
|
||||||
this.store.updateState({ identities })
|
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
|
* Setter for the `selectedAddress` property
|
||||||
*
|
*
|
||||||
|
@ -173,6 +247,7 @@ class PreferencesController {
|
||||||
* @return {Promise<string>}
|
* @return {Promise<string>}
|
||||||
*/
|
*/
|
||||||
setAccountLabel (account, label) {
|
setAccountLabel (account, label) {
|
||||||
|
if (!account) throw new Error('setAccountLabel requires a valid address, got ' + String(account))
|
||||||
const address = normalizeAddress(account)
|
const address = normalizeAddress(account)
|
||||||
const {identities} = this.store.getState()
|
const {identities} = this.store.getState()
|
||||||
identities[address] = identities[address] || {}
|
identities[address] = identities[address] || {}
|
||||||
|
|
|
@ -117,7 +117,7 @@ class RecentBlocksController {
|
||||||
*
|
*
|
||||||
* @returns {Promise<void>} Promises undefined
|
* @returns {Promise<void>} Promises undefined
|
||||||
*/
|
*/
|
||||||
async backfill() {
|
async backfill () {
|
||||||
this.blockTracker.once('block', async (block) => {
|
this.blockTracker.once('block', async (block) => {
|
||||||
const currentBlockNumber = Number.parseInt(block.number, 16)
|
const currentBlockNumber = Number.parseInt(block.number, 16)
|
||||||
const blocksToFetch = Math.min(currentBlockNumber, this.historyLength)
|
const blocksToFetch = Math.min(currentBlockNumber, this.historyLength)
|
||||||
|
|
|
@ -10,6 +10,7 @@ const NonceTracker = require('./nonce-tracker')
|
||||||
const txUtils = require('./lib/util')
|
const txUtils = require('./lib/util')
|
||||||
const cleanErrorStack = require('../../lib/cleanErrorStack')
|
const cleanErrorStack = require('../../lib/cleanErrorStack')
|
||||||
const log = require('loglevel')
|
const log = require('loglevel')
|
||||||
|
const recipientBlacklistChecker = require('./lib/recipient-blacklist-checker')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Transaction Controller is an aggregate of sub-controllers and trackers
|
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 })
|
let txMeta = this.txStateManager.generateTxMeta({ txParams: normalizedTxParams })
|
||||||
this.addTx(txMeta)
|
this.addTx(txMeta)
|
||||||
this.emit('newUnapprovedTx', txMeta)
|
this.emit('newUnapprovedTx', txMeta)
|
||||||
// add default tx params
|
|
||||||
try {
|
try {
|
||||||
|
// check whether recipient account is blacklisted
|
||||||
|
recipientBlacklistChecker.checkAccount(txMeta.metamaskNetworkId, normalizedTxParams.to)
|
||||||
|
// add default tx params
|
||||||
txMeta = await this.addTxGasDefaults(txMeta)
|
txMeta = await this.addTxGasDefaults(txMeta)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
log.warn(error)
|
||||||
this.txStateManager.setTxStatusFailed(txMeta.id, error)
|
this.txStateManager.setTxStatusFailed(txMeta.id, error)
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
|
@ -260,7 +264,12 @@ class TransactionController extends EventEmitter {
|
||||||
// must set transaction to submitted/failed before releasing lock
|
// must set transaction to submitted/failed before releasing lock
|
||||||
nonceLock.releaseLock()
|
nonceLock.releaseLock()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
// this is try-catch wrapped so that we can guarantee that the nonceLock is released
|
||||||
|
try {
|
||||||
this.txStateManager.setTxStatusFailed(txId, err)
|
this.txStateManager.setTxStatusFailed(txId, err)
|
||||||
|
} catch (err) {
|
||||||
|
log.error(err)
|
||||||
|
}
|
||||||
// must set transaction to submitted/failed before releasing lock
|
// must set transaction to submitted/failed before releasing lock
|
||||||
if (nonceLock) nonceLock.releaseLock()
|
if (nonceLock) nonceLock.releaseLock()
|
||||||
// continue with error chain
|
// 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,6 +49,7 @@ class NonceTracker {
|
||||||
await this._globalMutexFree()
|
await this._globalMutexFree()
|
||||||
// await lock free, then take lock
|
// await lock free, then take lock
|
||||||
const releaseLock = await this._takeMutex(address)
|
const releaseLock = await this._takeMutex(address)
|
||||||
|
try {
|
||||||
// evaluate multiple nextNonce strategies
|
// evaluate multiple nextNonce strategies
|
||||||
const nonceDetails = {}
|
const nonceDetails = {}
|
||||||
const networkNonceResult = await this._getNetworkNextNonce(address)
|
const networkNonceResult = await this._getNetworkNextNonce(address)
|
||||||
|
@ -72,6 +73,11 @@ class NonceTracker {
|
||||||
|
|
||||||
// return nonce and release cb
|
// return nonce and release cb
|
||||||
return { nextNonce, nonceDetails, releaseLock }
|
return { nextNonce, nonceDetails, releaseLock }
|
||||||
|
} catch (err) {
|
||||||
|
// release lock if we encounter an error
|
||||||
|
releaseLock()
|
||||||
|
throw err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async _getCurrentBlock () {
|
async _getCurrentBlock () {
|
||||||
|
@ -85,8 +91,8 @@ class NonceTracker {
|
||||||
|
|
||||||
async _globalMutexFree () {
|
async _globalMutexFree () {
|
||||||
const globalMutex = this._lookupMutex('global')
|
const globalMutex = this._lookupMutex('global')
|
||||||
const release = await globalMutex.acquire()
|
const releaseLock = await globalMutex.acquire()
|
||||||
release()
|
releaseLock()
|
||||||
}
|
}
|
||||||
|
|
||||||
async _takeMutex (lockId) {
|
async _takeMutex (lockId) {
|
||||||
|
|
|
@ -196,14 +196,14 @@ class PendingTransactionTracker extends EventEmitter {
|
||||||
async _checkPendingTxs () {
|
async _checkPendingTxs () {
|
||||||
const signedTxList = this.getPendingTransactions()
|
const signedTxList = this.getPendingTransactions()
|
||||||
// in order to keep the nonceTracker accurate we block it while updating pending transactions
|
// 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 {
|
try {
|
||||||
await Promise.all(signedTxList.map((txMeta) => this._checkPendingTx(txMeta)))
|
await Promise.all(signedTxList.map((txMeta) => this._checkPendingTx(txMeta)))
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
log.error('PendingTransactionWatcher - Error updating pending transactions')
|
log.error('PendingTransactionWatcher - Error updating pending transactions')
|
||||||
log.error(err)
|
log.error(err)
|
||||||
}
|
}
|
||||||
nonceGlobalLock.releaseLock()
|
releaseLock()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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 - overrode web3.setProvider')
|
||||||
}
|
}
|
||||||
log.debug('MetaMask - injected web3')
|
log.debug('MetaMask - injected web3')
|
||||||
// export global web3, with usage-detection
|
|
||||||
setupDappAutoReload(web3, inpageProvider.publicConfigStore)
|
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
|
// set web3 defaultAccount
|
||||||
inpageProvider.publicConfigStore.subscribe(function (state) {
|
inpageProvider.publicConfigStore.subscribe(function (state) {
|
||||||
web3.eth.defaultAccount = state.selectedAddress
|
web3.eth.defaultAccount = state.selectedAddress
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
* @param {Error} err - error
|
* @param {Error} err - error
|
||||||
* @returns {Error} Error with clean stack trace.
|
* @returns {Error} Error with clean stack trace.
|
||||||
*/
|
*/
|
||||||
function cleanErrorStack(err){
|
function cleanErrorStack (err) {
|
||||||
var name = err.name
|
var name = err.name
|
||||||
name = (name === undefined) ? 'Error' : String(name)
|
name = (name === undefined) ? 'Error' : String(name)
|
||||||
|
|
||||||
|
|
|
@ -59,6 +59,7 @@ function createErrorMiddleware ({ override = true } = {}) {
|
||||||
if (!error) { return done() }
|
if (!error) { return done() }
|
||||||
sanitizeRPCError(error)
|
sanitizeRPCError(error)
|
||||||
log.error(`MetaMask - RPC Error: ${error.message}`, error)
|
log.error(`MetaMask - RPC Error: ${error.message}`, error)
|
||||||
|
done()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
@ -16,7 +16,7 @@ module.exports = extractEthjsErrorMessage
|
||||||
* extractEthjsErrorMessage(`Error: [ethjs-rpc] rpc error with payload {"id":3947817945380,"jsonrpc":"2.0","params":["0xf8eb8208708477359400830398539406012c8cf97bead5deae237070f9587f8e7a266d80b8843d7d3f5a0000000000000000000000000000000000000000000000000000000000081d1a000000000000000000000000000000000000000000000000001ff973cafa800000000000000000000000000000000000000000000000000000038d7ea4c68000000000000000000000000000000000000000000000000000000000000003f48025a04c32a9b630e0d9e7ff361562d850c86b7a884908135956a7e4a336fa0300d19ca06830776423f25218e8d19b267161db526e66895567147015b1f3fc47aef9a3c7"],"method":"eth_sendRawTransaction"} Error: 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)
|
const isEthjsRpcError = errorMessage.includes(ethJsRpcSlug)
|
||||||
if (isEthjsRpcError) {
|
if (isEthjsRpcError) {
|
||||||
const payloadAndError = errorMessage.slice(ethJsRpcSlug.length)
|
const payloadAndError = errorMessage.slice(ethJsRpcSlug.length)
|
||||||
|
|
|
@ -2,8 +2,7 @@ const extension = require('extensionizer')
|
||||||
const promisify = require('pify')
|
const promisify = require('pify')
|
||||||
const allLocales = require('../../_locales/index.json')
|
const allLocales = require('../../_locales/index.json')
|
||||||
|
|
||||||
const isSupported = extension.i18n && extension.i18n.getAcceptLanguages
|
const getPreferredLocales = extension.i18n ? promisify(
|
||||||
const getPreferredLocales = isSupported ? promisify(
|
|
||||||
extension.i18n.getAcceptLanguages,
|
extension.i18n.getAcceptLanguages,
|
||||||
{ errorFirst: false }
|
{ errorFirst: false }
|
||||||
) : async () => []
|
) : async () => []
|
||||||
|
@ -18,7 +17,21 @@ const existingLocaleCodes = allLocales.map(locale => locale.code.toLowerCase().r
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
async function getFirstPreferredLangCode () {
|
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
|
const firstPreferredLangCode = userPreferredLocaleCodes
|
||||||
.map(code => code.toLowerCase())
|
.map(code => code.toLowerCase())
|
||||||
.find(code => existingLocaleCodes.includes(code))
|
.find(code => existingLocaleCodes.includes(code))
|
||||||
|
@ -26,3 +39,4 @@ async function getFirstPreferredLangCode () {
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = getFirstPreferredLangCode
|
module.exports = getFirstPreferredLangCode
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ module.exports = getObjStructure
|
||||||
* replaced with the javascript type of that value.
|
* replaced with the javascript type of that value.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
function getObjStructure(obj) {
|
function getObjStructure (obj) {
|
||||||
const structure = clone(obj)
|
const structure = clone(obj)
|
||||||
return deepMap(structure, (value) => {
|
return deepMap(structure, (value) => {
|
||||||
return value === null ? 'null' : typeof 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
|
* @param {Function} visit The modifier to apply to each non-object property value
|
||||||
* @returns {object} The modified object
|
* @returns {object} The modified object
|
||||||
*/
|
*/
|
||||||
function deepMap(target = {}, visit) {
|
function deepMap (target = {}, visit) {
|
||||||
Object.entries(target).forEach(([key, value]) => {
|
Object.entries(target).forEach(([key, value]) => {
|
||||||
if (typeof value === 'object' && value !== null) {
|
if (typeof value === 'object' && value !== null) {
|
||||||
target[key] = deepMap(value, visit)
|
target[key] = deepMap(value, visit)
|
||||||
|
|
|
@ -8,7 +8,7 @@ module.exports = class ExtensionStore {
|
||||||
/**
|
/**
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
constructor() {
|
constructor () {
|
||||||
this.isSupported = !!(extension.storage.local)
|
this.isSupported = !!(extension.storage.local)
|
||||||
if (!this.isSupported) {
|
if (!this.isSupported) {
|
||||||
log.error('Storage local API not available.')
|
log.error('Storage local API not available.')
|
||||||
|
@ -19,7 +19,7 @@ module.exports = class ExtensionStore {
|
||||||
* Returns all of the keys currently saved
|
* Returns all of the keys currently saved
|
||||||
* @return {Promise<*>}
|
* @return {Promise<*>}
|
||||||
*/
|
*/
|
||||||
async get() {
|
async get () {
|
||||||
if (!this.isSupported) return undefined
|
if (!this.isSupported) return undefined
|
||||||
const result = await this._get()
|
const result = await this._get()
|
||||||
// extension.storage.local always returns an obj
|
// extension.storage.local always returns an obj
|
||||||
|
@ -36,7 +36,7 @@ module.exports = class ExtensionStore {
|
||||||
* @param {object} state - The state to set
|
* @param {object} state - The state to set
|
||||||
* @return {Promise<void>}
|
* @return {Promise<void>}
|
||||||
*/
|
*/
|
||||||
async set(state) {
|
async set (state) {
|
||||||
return this._set(state)
|
return this._set(state)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ module.exports = class ExtensionStore {
|
||||||
* @private
|
* @private
|
||||||
* @return {object} the key-value map from local storage
|
* @return {object} the key-value map from local storage
|
||||||
*/
|
*/
|
||||||
_get() {
|
_get () {
|
||||||
const local = extension.storage.local
|
const local = extension.storage.local
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
local.get(null, (/** @type {any} */ result) => {
|
local.get(null, (/** @type {any} */ result) => {
|
||||||
|
@ -65,7 +65,7 @@ module.exports = class ExtensionStore {
|
||||||
* @return {Promise<void>}
|
* @return {Promise<void>}
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_set(obj) {
|
_set (obj) {
|
||||||
const local = extension.storage.local
|
const local = extension.storage.local
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
local.set(obj, () => {
|
local.set(obj, () => {
|
||||||
|
@ -85,6 +85,6 @@ module.exports = class ExtensionStore {
|
||||||
* @param {object} obj - The object to check
|
* @param {object} obj - The object to check
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
function isEmpty(obj) {
|
function isEmpty (obj) {
|
||||||
return Object.keys(obj).length === 0
|
return Object.keys(obj).length === 0
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,13 +26,15 @@ class NotificationManager {
|
||||||
// bring focus to existing chrome popup
|
// bring focus to existing chrome popup
|
||||||
extension.windows.update(popup.id, { focused: true })
|
extension.windows.update(popup.id, { focused: true })
|
||||||
} else {
|
} else {
|
||||||
|
const cb = (currentPopup) => { this._popupId = currentPopup.id }
|
||||||
// create new notification popup
|
// create new notification popup
|
||||||
extension.windows.create({
|
const creation = extension.windows.create({
|
||||||
url: 'notification.html',
|
url: 'notification.html',
|
||||||
type: 'popup',
|
type: 'popup',
|
||||||
width,
|
width,
|
||||||
height,
|
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
|
* @private
|
||||||
* @param {array} windows An array of objects containing data about the open MetaMask extension windows.
|
* @param {array} windows An array of objects containing data about the open MetaMask extension windows.
|
||||||
|
@ -93,7 +95,7 @@ class NotificationManager {
|
||||||
_getPopupIn (windows) {
|
_getPopupIn (windows) {
|
||||||
return windows ? windows.find((win) => {
|
return windows ? windows.find((win) => {
|
||||||
// Returns notification popup
|
// Returns notification popup
|
||||||
return (win && win.type === 'popup')
|
return (win && win.type === 'popup' && win.id === this._popupId)
|
||||||
}) : null
|
}) : null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ module.exports = reportFailedTxToSentry
|
||||||
// for sending to sentry
|
// for sending to sentry
|
||||||
//
|
//
|
||||||
|
|
||||||
function reportFailedTxToSentry({ raven, txMeta }) {
|
function reportFailedTxToSentry ({ raven, txMeta }) {
|
||||||
const errorMessage = 'Transaction Failed: ' + extractEthjsErrorMessage(txMeta.err.message)
|
const errorMessage = 'Transaction Failed: ' + extractEthjsErrorMessage(txMeta.err.message)
|
||||||
raven.captureMessage(errorMessage, {
|
raven.captureMessage(errorMessage, {
|
||||||
// "extra" key is required by Sentry
|
// "extra" key is required by Sentry
|
||||||
|
|
|
@ -4,7 +4,7 @@ module.exports = setupMetamaskMeshMetrics
|
||||||
/**
|
/**
|
||||||
* Injects an iframe into the current document for testing
|
* Injects an iframe into the current document for testing
|
||||||
*/
|
*/
|
||||||
function setupMetamaskMeshMetrics() {
|
function setupMetamaskMeshMetrics () {
|
||||||
const testingContainer = document.createElement('iframe')
|
const testingContainer = document.createElement('iframe')
|
||||||
testingContainer.src = 'https://metamask.github.io/mesh-testing/'
|
testingContainer.src = 'https://metamask.github.io/mesh-testing/'
|
||||||
console.log('Injecting MetaMask Mesh testing client')
|
console.log('Injecting MetaMask Mesh testing client')
|
||||||
|
|
|
@ -7,7 +7,7 @@ const DEV = 'https://f59f3dd640d2429d9d0e2445a87ea8e1@sentry.io/273496'
|
||||||
module.exports = setupRaven
|
module.exports = setupRaven
|
||||||
|
|
||||||
// Setup raven / sentry remote error reporting
|
// Setup raven / sentry remote error reporting
|
||||||
function setupRaven(opts) {
|
function setupRaven (opts) {
|
||||||
const { release } = opts
|
const { release } = opts
|
||||||
let ravenTarget
|
let ravenTarget
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ function setupRaven(opts) {
|
||||||
|
|
||||||
const client = Raven.config(ravenTarget, {
|
const client = Raven.config(ravenTarget, {
|
||||||
release,
|
release,
|
||||||
transport: function(opts) {
|
transport: function (opts) {
|
||||||
const report = opts.data
|
const report = opts.data
|
||||||
try {
|
try {
|
||||||
// handle error-like non-error exceptions
|
// handle error-like non-error exceptions
|
||||||
|
@ -42,7 +42,7 @@ function setupRaven(opts) {
|
||||||
return Raven
|
return Raven
|
||||||
}
|
}
|
||||||
|
|
||||||
function rewriteErrorLikeExceptions(report) {
|
function rewriteErrorLikeExceptions (report) {
|
||||||
// handle errors that lost their error-ness in serialization (e.g. dnode)
|
// handle errors that lost their error-ness in serialization (e.g. dnode)
|
||||||
rewriteErrorMessages(report, (errorMessage) => {
|
rewriteErrorMessages(report, (errorMessage) => {
|
||||||
if (!errorMessage.includes('Non-Error exception captured with keys:')) return 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) => {
|
rewriteErrorMessages(report, (errorMessage) => {
|
||||||
// simplify ethjs error messages
|
// simplify ethjs error messages
|
||||||
errorMessage = extractEthjsErrorMessage(errorMessage)
|
errorMessage = extractEthjsErrorMessage(errorMessage)
|
||||||
|
@ -64,9 +64,9 @@ function simplifyErrorMessages(report) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function rewriteErrorMessages(report, rewriteFn) {
|
function rewriteErrorMessages (report, rewriteFn) {
|
||||||
// rewrite top level message
|
// rewrite top level message
|
||||||
report.message = rewriteFn(report.message)
|
if (report.message) report.message = rewriteFn(report.message)
|
||||||
// rewrite each exception message
|
// rewrite each exception message
|
||||||
if (report.exception && report.exception.values) {
|
if (report.exception && report.exception.values) {
|
||||||
report.exception.values.forEach(item => {
|
report.exception.values.forEach(item => {
|
||||||
|
@ -75,7 +75,7 @@ function rewriteErrorMessages(report, rewriteFn) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function rewriteReportUrls(report) {
|
function rewriteReportUrls (report) {
|
||||||
// update request url
|
// update request url
|
||||||
report.request.url = toMetamaskUrl(report.request.url)
|
report.request.url = toMetamaskUrl(report.request.url)
|
||||||
// update exception stack trace
|
// update exception stack trace
|
||||||
|
@ -88,7 +88,7 @@ function rewriteReportUrls(report) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function toMetamaskUrl(origUrl) {
|
function toMetamaskUrl (origUrl) {
|
||||||
const filePath = origUrl.split(location.origin)[1]
|
const filePath = origUrl.split(location.origin)[1]
|
||||||
if (!filePath) return origUrl
|
if (!filePath) return origUrl
|
||||||
const metamaskUrl = `metamask${filePath}`
|
const metamaskUrl = `metamask${filePath}`
|
||||||
|
|
|
@ -139,6 +139,8 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||||
const address = addresses[0]
|
const address = addresses[0]
|
||||||
this.preferencesController.setSelectedAddress(address)
|
this.preferencesController.setSelectedAddress(address)
|
||||||
}
|
}
|
||||||
|
// ensure preferences + identities controller know about all addresses
|
||||||
|
this.preferencesController.addAddresses(addresses)
|
||||||
this.accountTracker.syncWithAddresses(addresses)
|
this.accountTracker.syncWithAddresses(addresses)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -179,9 +181,6 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||||
version,
|
version,
|
||||||
firstVersion: initState.firstTimeInfo.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({
|
this.shapeshiftController = new ShapeShiftController({
|
||||||
initState: initState.ShapeShiftController,
|
initState: initState.ShapeShiftController,
|
||||||
|
@ -354,7 +353,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||||
importAccountWithStrategy: nodeify(this.importAccountWithStrategy, this),
|
importAccountWithStrategy: nodeify(this.importAccountWithStrategy, this),
|
||||||
|
|
||||||
// vault management
|
// vault management
|
||||||
submitPassword: nodeify(keyringController.submitPassword, keyringController),
|
submitPassword: nodeify(this.submitPassword, this),
|
||||||
|
|
||||||
// network management
|
// network management
|
||||||
setProviderType: nodeify(networkController.setProviderType, networkController),
|
setProviderType: nodeify(networkController.setProviderType, networkController),
|
||||||
|
@ -384,6 +383,8 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||||
updateAndApproveTransaction: nodeify(txController.updateAndApproveTransaction, txController),
|
updateAndApproveTransaction: nodeify(txController.updateAndApproveTransaction, txController),
|
||||||
retryTransaction: nodeify(this.retryTransaction, this),
|
retryTransaction: nodeify(this.retryTransaction, this),
|
||||||
getFilteredTxList: nodeify(txController.getFilteredTxList, txController),
|
getFilteredTxList: nodeify(txController.getFilteredTxList, txController),
|
||||||
|
isNonceTaken: nodeify(txController.isNonceTaken, txController),
|
||||||
|
estimateGas: nodeify(this.estimateGas, this),
|
||||||
|
|
||||||
// messageManager
|
// messageManager
|
||||||
signMessage: nodeify(this.signMessage, this),
|
signMessage: nodeify(this.signMessage, this),
|
||||||
|
@ -404,7 +405,6 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=============================================================================
|
//=============================================================================
|
||||||
// VAULT / KEYRING RELATED METHODS
|
// VAULT / KEYRING RELATED METHODS
|
||||||
//=============================================================================
|
//=============================================================================
|
||||||
|
@ -424,28 +424,24 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||||
* @returns {Object} vault
|
* @returns {Object} vault
|
||||||
*/
|
*/
|
||||||
async createNewVaultAndKeychain (password) {
|
async createNewVaultAndKeychain (password) {
|
||||||
const release = await this.createVaultMutex.acquire()
|
const releaseLock = await this.createVaultMutex.acquire()
|
||||||
let vault
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
let vault
|
||||||
const accounts = await this.keyringController.getAccounts()
|
const accounts = await this.keyringController.getAccounts()
|
||||||
|
|
||||||
if (accounts.length > 0) {
|
if (accounts.length > 0) {
|
||||||
vault = await this.keyringController.fullUpdate()
|
vault = await this.keyringController.fullUpdate()
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
vault = await this.keyringController.createNewVaultAndKeychain(password)
|
vault = await this.keyringController.createNewVaultAndKeychain(password)
|
||||||
const accounts = await this.keyringController.getAccounts()
|
const accounts = await this.keyringController.getAccounts()
|
||||||
this.preferencesController.setAddresses(accounts)
|
this.preferencesController.setAddresses(accounts)
|
||||||
this.selectFirstIdentity()
|
this.selectFirstIdentity()
|
||||||
}
|
}
|
||||||
release()
|
releaseLock()
|
||||||
|
return vault
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
release()
|
releaseLock()
|
||||||
throw err
|
throw err
|
||||||
}
|
}
|
||||||
|
|
||||||
return vault
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -454,20 +450,46 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||||
* @param {} seed
|
* @param {} seed
|
||||||
*/
|
*/
|
||||||
async createNewVaultAndRestore (password, seed) {
|
async createNewVaultAndRestore (password, seed) {
|
||||||
const release = await this.createVaultMutex.acquire()
|
const releaseLock = await this.createVaultMutex.acquire()
|
||||||
try {
|
try {
|
||||||
|
// clear known identities
|
||||||
|
this.preferencesController.setAddresses([])
|
||||||
|
// create new vault
|
||||||
const vault = await this.keyringController.createNewVaultAndRestore(password, seed)
|
const vault = await this.keyringController.createNewVaultAndRestore(password, seed)
|
||||||
|
// set new identities
|
||||||
const accounts = await this.keyringController.getAccounts()
|
const accounts = await this.keyringController.getAccounts()
|
||||||
this.preferencesController.setAddresses(accounts)
|
this.preferencesController.setAddresses(accounts)
|
||||||
this.selectFirstIdentity()
|
this.selectFirstIdentity()
|
||||||
release()
|
releaseLock()
|
||||||
return vault
|
return vault
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
release()
|
releaseLock()
|
||||||
throw err
|
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
|
* @type Identity
|
||||||
* @property {string} name - The account nickname.
|
* @property {string} name - The account nickname.
|
||||||
|
@ -592,10 +614,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||||
async resetAccount () {
|
async resetAccount () {
|
||||||
const selectedAddress = this.preferencesController.getSelectedAddress()
|
const selectedAddress = this.preferencesController.getSelectedAddress()
|
||||||
this.txController.wipeTransactions(selectedAddress)
|
this.txController.wipeTransactions(selectedAddress)
|
||||||
|
this.networkController.resetConnection()
|
||||||
const networkController = this.networkController
|
|
||||||
const oldType = networkController.getProviderConfig().type
|
|
||||||
await networkController.setProviderType(oldType, true)
|
|
||||||
|
|
||||||
return selectedAddress
|
return selectedAddress
|
||||||
}
|
}
|
||||||
|
@ -922,6 +941,18 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||||
return state
|
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
|
// PASSWORD MANAGEMENT
|
||||||
//=============================================================================
|
//=============================================================================
|
||||||
|
@ -930,7 +961,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||||
* Allows a user to begin the seed phrase recovery process.
|
* Allows a user to begin the seed phrase recovery process.
|
||||||
* @param {Function} cb - A callback function called when complete.
|
* @param {Function} cb - A callback function called when complete.
|
||||||
*/
|
*/
|
||||||
markPasswordForgotten(cb) {
|
markPasswordForgotten (cb) {
|
||||||
this.configManager.setPasswordForgotten(true)
|
this.configManager.setPasswordForgotten(true)
|
||||||
this.sendUpdate()
|
this.sendUpdate()
|
||||||
cb()
|
cb()
|
||||||
|
@ -940,7 +971,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||||
* Allows a user to end the seed phrase recovery process.
|
* Allows a user to end the seed phrase recovery process.
|
||||||
* @param {Function} cb - A callback function called when complete.
|
* @param {Function} cb - A callback function called when complete.
|
||||||
*/
|
*/
|
||||||
unMarkPasswordForgotten(cb) {
|
unMarkPasswordForgotten (cb) {
|
||||||
this.configManager.setPasswordForgotten(false)
|
this.configManager.setPasswordForgotten(false)
|
||||||
this.sendUpdate()
|
this.sendUpdate()
|
||||||
cb()
|
cb()
|
||||||
|
|
|
@ -28,7 +28,7 @@ module.exports = {
|
||||||
function transformState (state) {
|
function transformState (state) {
|
||||||
const newState = state
|
const newState = state
|
||||||
const { config } = newState
|
const { config } = newState
|
||||||
if ( config && config.provider ) {
|
if (config && config.provider) {
|
||||||
if (config.provider.type === 'testnet') {
|
if (config.provider.type === 'testnet') {
|
||||||
newState.config.provider.type = 'ropsten'
|
newState.config.provider.type = 'ropsten'
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,10 +35,10 @@ function transformState (state) {
|
||||||
|
|
||||||
if (transactions.length <= 40) return newState
|
if (transactions.length <= 40) return newState
|
||||||
|
|
||||||
let reverseTxList = transactions.reverse()
|
const reverseTxList = transactions.reverse()
|
||||||
let stripping = true
|
let stripping = true
|
||||||
while (reverseTxList.length > 40 && stripping) {
|
while (reverseTxList.length > 40 && stripping) {
|
||||||
let txIndex = reverseTxList.findIndex((txMeta) => {
|
const txIndex = reverseTxList.findIndex((txMeta) => {
|
||||||
return (txMeta.status === 'failed' ||
|
return (txMeta.status === 'failed' ||
|
||||||
txMeta.status === 'rejected' ||
|
txMeta.status === 'rejected' ||
|
||||||
txMeta.status === 'confirmed' ||
|
txMeta.status === 'confirmed' ||
|
||||||
|
|
|
@ -27,7 +27,7 @@ module.exports = {
|
||||||
|
|
||||||
function transformState (state) {
|
function transformState (state) {
|
||||||
if (!state.KeyringController || !state.PreferencesController) {
|
if (!state.KeyringController || !state.PreferencesController) {
|
||||||
return
|
return state
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!state.KeyringController.walletNicknames) {
|
if (!state.KeyringController.walletNicknames) {
|
||||||
|
|
|
@ -2,7 +2,7 @@ const EventEmitter = require('events').EventEmitter
|
||||||
const semver = require('semver')
|
const semver = require('semver')
|
||||||
const extend = require('xtend')
|
const extend = require('xtend')
|
||||||
const ObservableStore = require('obs-store')
|
const ObservableStore = require('obs-store')
|
||||||
const hardCodedNotices = require('../../notices/notices.json')
|
const hardCodedNotices = require('../../notices/notices.js')
|
||||||
const uniqBy = require('lodash.uniqby')
|
const uniqBy = require('lodash.uniqby')
|
||||||
|
|
||||||
module.exports = class NoticeController extends EventEmitter {
|
module.exports = class NoticeController extends EventEmitter {
|
||||||
|
@ -16,8 +16,12 @@ module.exports = class NoticeController extends EventEmitter {
|
||||||
noticesList: [],
|
noticesList: [],
|
||||||
}, opts.initState)
|
}, opts.initState)
|
||||||
this.store = new ObservableStore(initState)
|
this.store = new ObservableStore(initState)
|
||||||
|
// setup memStore
|
||||||
this.memStore = new ObservableStore({})
|
this.memStore = new ObservableStore({})
|
||||||
this.store.subscribe(() => this._updateMemstore())
|
this.store.subscribe(() => this._updateMemstore())
|
||||||
|
this._updateMemstore()
|
||||||
|
// pull in latest notices
|
||||||
|
this.updateNoticesList()
|
||||||
}
|
}
|
||||||
|
|
||||||
getNoticesList () {
|
getNoticesList () {
|
||||||
|
@ -29,9 +33,9 @@ module.exports = class NoticeController extends EventEmitter {
|
||||||
return notices.filter((notice) => notice.read === false)
|
return notices.filter((notice) => notice.read === false)
|
||||||
}
|
}
|
||||||
|
|
||||||
getLatestUnreadNotice () {
|
getNextUnreadNotice () {
|
||||||
const unreadNotices = this.getUnreadNotices()
|
const unreadNotices = this.getUnreadNotices()
|
||||||
return unreadNotices[unreadNotices.length - 1]
|
return unreadNotices[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
async setNoticesList (noticesList) {
|
async setNoticesList (noticesList) {
|
||||||
|
@ -47,7 +51,7 @@ module.exports = class NoticeController extends EventEmitter {
|
||||||
notices[index].read = true
|
notices[index].read = true
|
||||||
notices[index].body = ''
|
notices[index].body = ''
|
||||||
this.setNoticesList(notices)
|
this.setNoticesList(notices)
|
||||||
const latestNotice = this.getLatestUnreadNotice()
|
const latestNotice = this.getNextUnreadNotice()
|
||||||
cb(null, latestNotice)
|
cb(null, latestNotice)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
cb(err)
|
cb(err)
|
||||||
|
@ -64,15 +68,6 @@ module.exports = class NoticeController extends EventEmitter {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
startPolling () {
|
|
||||||
if (this.noticePoller) {
|
|
||||||
clearInterval(this.noticePoller)
|
|
||||||
}
|
|
||||||
this.noticePoller = setInterval(() => {
|
|
||||||
this.noticeController.updateNoticesList()
|
|
||||||
}, 300000)
|
|
||||||
}
|
|
||||||
|
|
||||||
_mergeNotices (oldNotices, newNotices) {
|
_mergeNotices (oldNotices, newNotices) {
|
||||||
return uniqBy(oldNotices.concat(newNotices), 'id')
|
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 () {
|
async _retrieveNoticeData () {
|
||||||
// Placeholder for the API.
|
// Placeholder for remote notice API.
|
||||||
return hardCodedNotices
|
return hardCodedNotices
|
||||||
}
|
}
|
||||||
|
|
||||||
_updateMemstore () {
|
_updateMemstore () {
|
||||||
const lastUnreadNotice = this.getLatestUnreadNotice()
|
const nextUnreadNotice = this.getNextUnreadNotice()
|
||||||
const noActiveNotices = !lastUnreadNotice
|
const noActiveNotices = !nextUnreadNotice
|
||||||
this.memStore.updateState({ lastUnreadNotice, noActiveNotices })
|
this.memStore.updateState({ nextUnreadNotice, noActiveNotices })
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ const log = require('loglevel')
|
||||||
|
|
||||||
start().catch(log.error)
|
start().catch(log.error)
|
||||||
|
|
||||||
async function start() {
|
async function start () {
|
||||||
|
|
||||||
// create platform global
|
// create platform global
|
||||||
global.platform = new ExtensionPlatform()
|
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()
|
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)
|
console.log(msg)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
"confirm sig requests": {
|
'confirm sig requests': {
|
||||||
signMessage: (msgData, cb) => {
|
signMessage: (msgData, cb) => {
|
||||||
const stateUpdate = {
|
const stateUpdate = {
|
||||||
unapprovedMsgs: {},
|
unapprovedMsgs: {},
|
||||||
|
|
|
@ -1,17 +1,14 @@
|
||||||
const beefy = require('beefy')
|
const beefy = require('beefy')
|
||||||
const http = require('http')
|
const http = require('http')
|
||||||
const fs = require('fs')
|
|
||||||
const path = require('path')
|
|
||||||
|
|
||||||
const port = 8124
|
const port = 8124
|
||||||
|
|
||||||
const handler = beefy({
|
const handler = beefy({
|
||||||
entries: {'mocker.js': 'bundle.js'}
|
entries: {'mocker.js': 'bundle.js'},
|
||||||
, cwd: __dirname
|
cwd: __dirname,
|
||||||
, live: true
|
live: true,
|
||||||
, open: true
|
open: true,
|
||||||
, quiet: false
|
quiet: false,
|
||||||
, bundlerFlags: ['-t', 'brfs']
|
bundlerFlags: ['-t', 'brfs'],
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ const VERSION = require('../dist/chrome/manifest.json').version
|
||||||
|
|
||||||
start().catch(console.error)
|
start().catch(console.error)
|
||||||
|
|
||||||
async function start() {
|
async function start () {
|
||||||
|
|
||||||
const GITHUB_COMMENT_TOKEN = process.env.GITHUB_COMMENT_TOKEN
|
const GITHUB_COMMENT_TOKEN = process.env.GITHUB_COMMENT_TOKEN
|
||||||
const CIRCLE_PULL_REQUEST = process.env.CIRCLE_PULL_REQUEST
|
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 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 BUILD_LINK_BASE = `https://${CIRCLE_BUILD_NUM}-42009758-gh.circle-artifacts.com/0`
|
||||||
|
|
||||||
const MASCARA = `${BUILD_LINK_BASE}/builds/mascara/home.html`
|
const MASCARA = `${BUILD_LINK_BASE}/builds/mascara/home.html`
|
||||||
|
|
|
@ -12,7 +12,6 @@
|
||||||
* To use, run `npm run mock`.
|
* To use, run `npm run mock`.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const extend = require('xtend')
|
|
||||||
const render = require('react-dom').render
|
const render = require('react-dom').render
|
||||||
const h = require('react-hyperscript')
|
const h = require('react-hyperscript')
|
||||||
const Root = require('../ui/app/root')
|
const Root = require('../ui/app/root')
|
||||||
|
@ -24,7 +23,6 @@ const Selector = require('./selector')
|
||||||
const MetamaskController = require('../app/scripts/metamask-controller')
|
const MetamaskController = require('../app/scripts/metamask-controller')
|
||||||
const firstTimeState = require('../app/scripts/first-time-state')
|
const firstTimeState = require('../app/scripts/first-time-state')
|
||||||
const ExtensionPlatform = require('../app/scripts/platforms/extension')
|
const ExtensionPlatform = require('../app/scripts/platforms/extension')
|
||||||
const extension = require('./mockExtension')
|
|
||||||
const noop = function () {}
|
const noop = function () {}
|
||||||
|
|
||||||
const log = require('loglevel')
|
const log = require('loglevel')
|
||||||
|
@ -81,14 +79,14 @@ const controller = new MetamaskController({
|
||||||
initState: firstTimeState,
|
initState: firstTimeState,
|
||||||
})
|
})
|
||||||
global.metamaskController = controller
|
global.metamaskController = controller
|
||||||
global.platform = new ExtensionPlatform
|
global.platform = new ExtensionPlatform()
|
||||||
|
|
||||||
//
|
//
|
||||||
// User Interface
|
// User Interface
|
||||||
//
|
//
|
||||||
|
|
||||||
actions._setBackgroundConnection(controller.getApi())
|
actions._setBackgroundConnection(controller.getApi())
|
||||||
actions.update = function(stateName) {
|
actions.update = function (stateName) {
|
||||||
selectedView = stateName
|
selectedView = stateName
|
||||||
updateQueryParams(stateName)
|
updateQueryParams(stateName)
|
||||||
const newState = states[selectedView]
|
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)
|
const modifiedBackgroundConnection = Object.assign({}, controller.getApi(), backgroundConnectionModifier)
|
||||||
actions._setBackgroundConnection(modifiedBackgroundConnection)
|
actions._setBackgroundConnection(modifiedBackgroundConnection)
|
||||||
}
|
}
|
||||||
|
@ -112,7 +110,7 @@ var store = configureStore(firstState)
|
||||||
// start app
|
// start app
|
||||||
startApp()
|
startApp()
|
||||||
|
|
||||||
function startApp(){
|
function startApp () {
|
||||||
const body = document.body
|
const body = document.body
|
||||||
const container = document.createElement('div')
|
const container = document.createElement('div')
|
||||||
container.id = 'test-container'
|
container.id = 'test-container'
|
||||||
|
|
|
@ -39,6 +39,6 @@ extension.runtime.reload = noop
|
||||||
extension.tabs.create = noop
|
extension.tabs.create = noop
|
||||||
extension.runtime.getManifest = function () {
|
extension.runtime.getManifest = function () {
|
||||||
return {
|
return {
|
||||||
version: 'development'
|
version: 'development',
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -11,7 +11,7 @@ const bumpType = normalizeType(process.argv[2])
|
||||||
|
|
||||||
start().catch(console.error)
|
start().catch(console.error)
|
||||||
|
|
||||||
async function start() {
|
async function start () {
|
||||||
|
|
||||||
const changeBuffer = await readFile(changelogPath)
|
const changeBuffer = await readFile(changelogPath)
|
||||||
const changelog = changeBuffer.toString()
|
const changelog = changeBuffer.toString()
|
||||||
|
|
|
@ -11,7 +11,7 @@ function NewComponent () {
|
||||||
|
|
||||||
NewComponent.prototype.render = function () {
|
NewComponent.prototype.render = function () {
|
||||||
const props = this.props
|
const props = this.props
|
||||||
let {
|
const {
|
||||||
states,
|
states,
|
||||||
selectedKey,
|
selectedKey,
|
||||||
actions,
|
actions,
|
||||||
|
@ -28,7 +28,7 @@ NewComponent.prototype.render = function () {
|
||||||
margin: '20px 20px 0px',
|
margin: '20px 20px 0px',
|
||||||
},
|
},
|
||||||
value: selected,
|
value: selected,
|
||||||
onChange:(event) => {
|
onChange: (event) => {
|
||||||
const selectedKey = event.target.value
|
const selectedKey = event.target.value
|
||||||
const backgroundConnectionModifier = backGroundConnectionModifiers[selectedKey]
|
const backgroundConnectionModifier = backGroundConnectionModifiers[selectedKey]
|
||||||
modifyBackgroundConnection(backgroundConnectionModifier || {})
|
modifyBackgroundConnection(backgroundConnectionModifier || {})
|
||||||
|
|
|
@ -5,7 +5,7 @@ const VERSION = require('../dist/chrome/manifest.json').version
|
||||||
|
|
||||||
start().catch(console.error)
|
start().catch(console.error)
|
||||||
|
|
||||||
async function start(){
|
async function start () {
|
||||||
const authWorked = await checkIfAuthWorks()
|
const authWorked = await checkIfAuthWorks()
|
||||||
if (!authWorked) {
|
if (!authWorked) {
|
||||||
console.log(`Sentry auth failed...`)
|
console.log(`Sentry auth failed...`)
|
||||||
|
@ -31,21 +31,21 @@ async function start(){
|
||||||
console.log('all done!')
|
console.log('all done!')
|
||||||
}
|
}
|
||||||
|
|
||||||
async function checkIfAuthWorks() {
|
async function checkIfAuthWorks () {
|
||||||
const itWorked = await doesNotFail(async () => {
|
const itWorked = await doesNotFail(async () => {
|
||||||
await exec(`sentry-cli releases --org 'metamask' --project 'metamask' list`)
|
await exec(`sentry-cli releases --org 'metamask' --project 'metamask' list`)
|
||||||
})
|
})
|
||||||
return itWorked
|
return itWorked
|
||||||
}
|
}
|
||||||
|
|
||||||
async function checkIfVersionExists() {
|
async function checkIfVersionExists () {
|
||||||
const versionAlreadyExists = await doesNotFail(async () => {
|
const versionAlreadyExists = await doesNotFail(async () => {
|
||||||
await exec(`sentry-cli releases --org 'metamask' --project 'metamask' info ${VERSION}`)
|
await exec(`sentry-cli releases --org 'metamask' --project 'metamask' info ${VERSION}`)
|
||||||
})
|
})
|
||||||
return versionAlreadyExists
|
return versionAlreadyExists
|
||||||
}
|
}
|
||||||
|
|
||||||
async function doesNotFail(asyncFn) {
|
async function doesNotFail (asyncFn) {
|
||||||
try {
|
try {
|
||||||
await asyncFn()
|
await asyncFn()
|
||||||
return true
|
return true
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const { SourceMapConsumer } = require('source-map')
|
const { SourceMapConsumer } = require('source-map')
|
||||||
|
const path = require('path')
|
||||||
//
|
//
|
||||||
// Utility to help check if sourcemaps are working
|
// Utility to help check if sourcemaps are working
|
||||||
//
|
//
|
||||||
|
@ -11,9 +11,11 @@ const { SourceMapConsumer } = require('source-map')
|
||||||
|
|
||||||
start()
|
start()
|
||||||
|
|
||||||
async function start() {
|
|
||||||
const rawBuild = fs.readFileSync(__dirname + '/../dist/chrome/inpage.js', 'utf8')
|
async function start () {
|
||||||
const rawSourceMap = fs.readFileSync(__dirname + '/../dist/sourcemaps/inpage.js.map', 'utf8')
|
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)
|
const consumer = await new SourceMapConsumer(rawSourceMap)
|
||||||
|
|
||||||
console.log('hasContentsOfAllSources:', consumer.hasContentsOfAllSources(), '\n')
|
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
|
if (result.source === 'node_modules/web3/dist/web3.min.js') return // minified mess
|
||||||
const sourceContent = consumer.sourceContentFor(result.source)
|
const sourceContent = consumer.sourceContentFor(result.source)
|
||||||
const sourceLines = sourceContent.split('\n')
|
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(`\n========================== ${result.source} ====================================\n`)
|
||||||
console.log(line)
|
console.log(line)
|
||||||
console.log(`\n==============================================================================\n`)
|
console.log(`\n==============================================================================\n`)
|
||||||
|
@ -42,8 +44,9 @@ async function start() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function indicesOf(substring, string) {
|
function indicesOf (substring, string) {
|
||||||
var a=[],i=-1;
|
var a = []
|
||||||
while((i=string.indexOf(substring,i+1)) >= 0) a.push(i);
|
var i = -1
|
||||||
return a;
|
while ((i = string.indexOf(substring, i + 1)) >= 0) a.push(i)
|
||||||
|
return a
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,9 +75,9 @@
|
||||||
{
|
{
|
||||||
"type": "HD Key Tree",
|
"type": "HD Key Tree",
|
||||||
"accounts": [
|
"accounts": [
|
||||||
"fdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
"c5b8dbac4c1d3f152cdeb400e2313f309c410acb",
|
"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb",
|
||||||
"2f8d4a878cfa04a6e60d46362f5644deab66572d"
|
"0x2f8d4a878cfa04a6e60d46362f5644deab66572d"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -52,7 +52,7 @@
|
||||||
"conversionRate": 12.7200827,
|
"conversionRate": 12.7200827,
|
||||||
"conversionDate": 1487363041,
|
"conversionDate": 1487363041,
|
||||||
"noActiveNotices": true,
|
"noActiveNotices": true,
|
||||||
"lastUnreadNotice": {
|
"nextUnreadNotice": {
|
||||||
"read": true,
|
"read": true,
|
||||||
"date": "Thu Feb 09 2017",
|
"date": "Thu Feb 09 2017",
|
||||||
"title": "Terms of Use",
|
"title": "Terms of Use",
|
||||||
|
|
|
@ -151,5 +151,10 @@
|
||||||
"scrollToBottom": false,
|
"scrollToBottom": false,
|
||||||
"forgottenPassword": null
|
"forgottenPassword": null
|
||||||
},
|
},
|
||||||
"identities": {}
|
"identities": {},
|
||||||
|
"send": {
|
||||||
|
"fromDropdownOpen": false,
|
||||||
|
"toDropdownOpen": false,
|
||||||
|
"errors": {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -115,9 +115,9 @@
|
||||||
{
|
{
|
||||||
"type": "HD Key Tree",
|
"type": "HD Key Tree",
|
||||||
"accounts": [
|
"accounts": [
|
||||||
"fdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
"c5b8dbac4c1d3f152cdeb400e2313f309c410acb",
|
"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb",
|
||||||
"2f8d4a878cfa04a6e60d46362f5644deab66572d"
|
"0x2f8d4a878cfa04a6e60d46362f5644deab66572d"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -76,9 +76,9 @@
|
||||||
{
|
{
|
||||||
"type": "HD Key Tree",
|
"type": "HD Key Tree",
|
||||||
"accounts": [
|
"accounts": [
|
||||||
"fdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
"c5b8dbac4c1d3f152cdeb400e2313f309c410acb",
|
"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb",
|
||||||
"2f8d4a878cfa04a6e60d46362f5644deab66572d"
|
"0x2f8d4a878cfa04a6e60d46362f5644deab66572d"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
"conversionRate": 12.7527416,
|
"conversionRate": 12.7527416,
|
||||||
"conversionDate": 1487624341,
|
"conversionDate": 1487624341,
|
||||||
"noActiveNotices": false,
|
"noActiveNotices": false,
|
||||||
"lastUnreadNotice": {
|
"nextUnreadNotice": {
|
||||||
"read": false,
|
"read": false,
|
||||||
"date": "Thu Feb 09 2017",
|
"date": "Thu Feb 09 2017",
|
||||||
"title": "Terms of Use",
|
"title": "Terms of Use",
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
"conversionRate": 8.3533002,
|
"conversionRate": 8.3533002,
|
||||||
"conversionDate": 1481671082,
|
"conversionDate": 1481671082,
|
||||||
"noActiveNotices": false,
|
"noActiveNotices": false,
|
||||||
"lastUnreadNotice": {
|
"nextUnreadNotice": {
|
||||||
"read": false,
|
"read": false,
|
||||||
"date": "Tue Dec 13 2016",
|
"date": "Tue Dec 13 2016",
|
||||||
"title": "MultiVault Support",
|
"title": "MultiVault Support",
|
||||||
|
|
|
@ -94,9 +94,9 @@
|
||||||
{
|
{
|
||||||
"type": "HD Key Tree",
|
"type": "HD Key Tree",
|
||||||
"accounts": [
|
"accounts": [
|
||||||
"fdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
"c5b8dbac4c1d3f152cdeb400e2313f309c410acb",
|
"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb",
|
||||||
"2f8d4a878cfa04a6e60d46362f5644deab66572d"
|
"0x2f8d4a878cfa04a6e60d46362f5644deab66572d"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -151,5 +151,10 @@
|
||||||
"scrollToBottom": false,
|
"scrollToBottom": false,
|
||||||
"forgottenPassword": null
|
"forgottenPassword": null
|
||||||
},
|
},
|
||||||
"identities": {}
|
"identities": {},
|
||||||
|
"send": {
|
||||||
|
"fromDropdownOpen": false,
|
||||||
|
"toDropdownOpen": false,
|
||||||
|
"errors": {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,9 +76,9 @@
|
||||||
{
|
{
|
||||||
"type": "HD Key Tree",
|
"type": "HD Key Tree",
|
||||||
"accounts": [
|
"accounts": [
|
||||||
"fdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
"c5b8dbac4c1d3f152cdeb400e2313f309c410acb",
|
"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb",
|
||||||
"2f8d4a878cfa04a6e60d46362f5644deab66572d"
|
"0x2f8d4a878cfa04a6e60d46362f5644deab66572d"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -130,5 +130,10 @@
|
||||||
"scrollToBottom": false,
|
"scrollToBottom": false,
|
||||||
"forgottenPassword": null
|
"forgottenPassword": null
|
||||||
},
|
},
|
||||||
"identities": {}
|
"identities": {},
|
||||||
|
"send": {
|
||||||
|
"fromDropdownOpen": false,
|
||||||
|
"toDropdownOpen": false,
|
||||||
|
"errors": {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,9 +83,9 @@
|
||||||
{
|
{
|
||||||
"type": "HD Key Tree",
|
"type": "HD Key Tree",
|
||||||
"accounts": [
|
"accounts": [
|
||||||
"fdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
"c5b8dbac4c1d3f152cdeb400e2313f309c410acb",
|
"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb",
|
||||||
"2f8d4a878cfa04a6e60d46362f5644deab66572d"
|
"0x2f8d4a878cfa04a6e60d46362f5644deab66572d"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -124,5 +124,10 @@
|
||||||
"scrollToBottom": false,
|
"scrollToBottom": false,
|
||||||
"forgottenPassword": null
|
"forgottenPassword": null
|
||||||
},
|
},
|
||||||
"identities": {}
|
"identities": {},
|
||||||
|
"send": {
|
||||||
|
"fromDropdownOpen": false,
|
||||||
|
"toDropdownOpen": false,
|
||||||
|
"errors": {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,9 +29,8 @@ log.setDefaultLevel(1)
|
||||||
|
|
||||||
// Query String
|
// Query String
|
||||||
const qs = require('qs')
|
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'
|
let selectedView = queryString.view || 'first time'
|
||||||
const firstState = states[selectedView]
|
|
||||||
updateQueryParams(selectedView)
|
updateQueryParams(selectedView)
|
||||||
|
|
||||||
// CSS
|
// CSS
|
||||||
|
@ -39,15 +38,15 @@ const MetaMaskUiCss = require('../ui/css')
|
||||||
const injectCss = require('inject-css')
|
const injectCss = require('inject-css')
|
||||||
|
|
||||||
|
|
||||||
function updateQueryParams(newView) {
|
function updateQueryParams (newView) {
|
||||||
queryString.view = newView
|
queryString.view = newView
|
||||||
const params = qs.stringify(queryString)
|
const params = qs.stringify(queryString)
|
||||||
window.location.href = window.location.href.split('#')[0] + `#${params}`
|
window.location.href = window.location.href.split('#')[0] + `#${params}`
|
||||||
}
|
}
|
||||||
|
|
||||||
const actions = {
|
const actions = {
|
||||||
_setBackgroundConnection(){},
|
_setBackgroundConnection () {},
|
||||||
update: function(stateName) {
|
update: function (stateName) {
|
||||||
selectedView = stateName
|
selectedView = stateName
|
||||||
updateQueryParams(stateName)
|
updateQueryParams(stateName)
|
||||||
const newState = states[selectedView]
|
const newState = states[selectedView]
|
||||||
|
@ -67,7 +66,7 @@ var store = configureStore(states[selectedView])
|
||||||
// start app
|
// start app
|
||||||
startApp()
|
startApp()
|
||||||
|
|
||||||
function startApp(){
|
function startApp () {
|
||||||
const body = document.body
|
const body = document.body
|
||||||
const container = document.createElement('div')
|
const container = document.createElement('div')
|
||||||
container.id = 'test-container'
|
container.id = 'test-container'
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
// //////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Locale verification script
|
// Locale verification script
|
||||||
//
|
//
|
||||||
|
@ -8,7 +8,7 @@
|
||||||
//
|
//
|
||||||
// will check the given locale against the strings in english
|
// will check the given locale against the strings in english
|
||||||
//
|
//
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
// //////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
|
@ -20,7 +20,7 @@ const specifiedLocale = process.argv[2]
|
||||||
if (specifiedLocale) {
|
if (specifiedLocale) {
|
||||||
console.log(`Verifying selected locale "${specifiedLocale}":\n\n`)
|
console.log(`Verifying selected locale "${specifiedLocale}":\n\n`)
|
||||||
const locale = localeIndex.find(localeMeta => localeMeta.code === specifiedLocale)
|
const locale = localeIndex.find(localeMeta => localeMeta.code === specifiedLocale)
|
||||||
verifyLocale({ localeMeta })
|
verifyLocale({ locale })
|
||||||
} else {
|
} else {
|
||||||
console.log('Verifying all locales:\n\n')
|
console.log('Verifying all locales:\n\n')
|
||||||
localeIndex.forEach(localeMeta => {
|
localeIndex.forEach(localeMeta => {
|
||||||
|
@ -30,16 +30,16 @@ if (specifiedLocale) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function verifyLocale ({ localeMeta }) {
|
||||||
function verifyLocale({ localeMeta }) {
|
|
||||||
const localeCode = localeMeta.code
|
const localeCode = localeMeta.code
|
||||||
const localeName = localeMeta.name
|
const localeName = localeMeta.name
|
||||||
|
let targetLocale, englishLocale
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const localeFilePath = path.join(process.cwd(), 'app', '_locales', localeCode, 'messages.json')
|
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) {
|
} catch (e) {
|
||||||
if (e.code == 'ENOENT') {
|
if (e.code === 'ENOENT') {
|
||||||
console.log('Locale file not found')
|
console.log('Locale file not found')
|
||||||
} else {
|
} else {
|
||||||
console.log(`Error opening your locale ("${localeCode}") file: `, e)
|
console.log(`Error opening your locale ("${localeCode}") file: `, e)
|
||||||
|
@ -49,9 +49,9 @@ function verifyLocale({ localeMeta }) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const englishFilePath = path.join(process.cwd(), 'app', '_locales', 'en', 'messages.json')
|
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) {
|
} catch (e) {
|
||||||
if(e.code == 'ENOENT') {
|
if (e.code === 'ENOENT') {
|
||||||
console.log('English File not found')
|
console.log('English File not found')
|
||||||
} else {
|
} else {
|
||||||
console.log('Error opening english locale file: ', e)
|
console.log('Error opening english locale file: ', e)
|
||||||
|
@ -71,7 +71,7 @@ function verifyLocale({ localeMeta }) {
|
||||||
|
|
||||||
if (extraItems.length) {
|
if (extraItems.length) {
|
||||||
console.log('\nMissing from english locale:')
|
console.log('\nMissing from english locale:')
|
||||||
extraItems.forEach(function(key) {
|
extraItems.forEach(function (key) {
|
||||||
console.log(` - [ ] ${key}`)
|
console.log(` - [ ] ${key}`)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
@ -80,7 +80,7 @@ function verifyLocale({ localeMeta }) {
|
||||||
|
|
||||||
if (missingItems.length) {
|
if (missingItems.length) {
|
||||||
console.log(`\nMissing:`)
|
console.log(`\nMissing:`)
|
||||||
missingItems.forEach(function(key) {
|
missingItems.forEach(function (key) {
|
||||||
console.log(` - [ ] ${key}`)
|
console.log(` - [ ] ${key}`)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
@ -92,6 +92,6 @@ function verifyLocale({ localeMeta }) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function compareLocalesForMissingItems({ base, subject }) {
|
function compareLocalesForMissingItems ({ base, subject }) {
|
||||||
return Object.keys(base).filter((key) => !subject[key])
|
return Object.keys(base).filter((key) => !subject[key])
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
const clone = require('clone')
|
const clone = require('clone')
|
||||||
|
|
||||||
async function versionBump(bumpType, changelog, oldManifest) {
|
async function versionBump (bumpType, changelog, oldManifest) {
|
||||||
const manifest = clone(oldManifest)
|
const manifest = clone(oldManifest)
|
||||||
const newVersion = newVersionFrom(manifest, bumpType)
|
const newVersion = newVersionFrom(manifest, bumpType)
|
||||||
|
|
||||||
|
@ -19,13 +19,13 @@ async function versionBump(bumpType, changelog, oldManifest) {
|
||||||
return {
|
return {
|
||||||
version: newVersion,
|
version: newVersion,
|
||||||
manifest: manifest,
|
manifest: manifest,
|
||||||
changelog: logLines.join('\n')
|
changelog: logLines.join('\n'),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function newVersionFrom (manifest, bumpType) {
|
function newVersionFrom (manifest, bumpType) {
|
||||||
const string = manifest.version
|
const string = manifest.version
|
||||||
let segments = string.split('.').map((str) => parseInt(str))
|
const segments = string.split('.').map((str) => parseInt(str))
|
||||||
|
|
||||||
switch (bumpType) {
|
switch (bumpType) {
|
||||||
case 'major':
|
case 'major':
|
||||||
|
@ -45,8 +45,4 @@ function newVersionFrom (manifest, bumpType) {
|
||||||
return segments.map(String).join('.')
|
return segments.map(String).join('.')
|
||||||
}
|
}
|
||||||
|
|
||||||
function bumpManifest (manifest, bumpType) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = versionBump
|
module.exports = versionBump
|
||||||
|
|
|
@ -2,18 +2,32 @@
|
||||||
|
|
||||||
When publishing a new version of MetaMask, we follow this procedure:
|
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
|
## Incrementing Version & Changelog
|
||||||
|
|
||||||
Version can be automatically incremented [using our bump script](./bumping-version.md).
|
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`.
|
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
|
## Publishing
|
||||||
|
|
||||||
1. `npm run dist` to generate the latest build.
|
1. Publish to chrome store.
|
||||||
2. Publish to chrome store.
|
2. Visit [the chrome developer dashboard](https://chrome.google.com/webstore/developer/dashboard?authuser=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).
|
||||||
3. Publish to firefox addon marketplace.
|
4. Publish to [Opera store](https://addons.opera.com/en/extensions/details/metamask/).
|
||||||
4. Post on Github releases page.
|
5. Post on [Github releases](https://github.com/MetaMask/metamask-extension/releases) page.
|
||||||
5. `npm run announce`, post that announcement in our public places.
|
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'}
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
})`
|
||||||
|
}
|
105
gulpfile.js
105
gulpfile.js
|
@ -13,27 +13,21 @@ const zip = require('gulp-zip')
|
||||||
const assign = require('lodash.assign')
|
const assign = require('lodash.assign')
|
||||||
const livereload = require('gulp-livereload')
|
const livereload = require('gulp-livereload')
|
||||||
const del = require('del')
|
const del = require('del')
|
||||||
const eslint = require('gulp-eslint')
|
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const manifest = require('./app/manifest.json')
|
const manifest = require('./app/manifest.json')
|
||||||
const replace = require('gulp-replace')
|
|
||||||
const mkdirp = require('mkdirp')
|
const mkdirp = require('mkdirp')
|
||||||
const asyncEach = require('async/each')
|
|
||||||
const exec = require('child_process').exec
|
|
||||||
const sass = require('gulp-sass')
|
const sass = require('gulp-sass')
|
||||||
const autoprefixer = require('gulp-autoprefixer')
|
const autoprefixer = require('gulp-autoprefixer')
|
||||||
const gulpStylelint = require('gulp-stylelint')
|
const gulpStylelint = require('gulp-stylelint')
|
||||||
const stylefmt = require('gulp-stylefmt')
|
const stylefmt = require('gulp-stylefmt')
|
||||||
const uglify = require('gulp-uglify-es').default
|
const uglify = require('gulp-uglify-es').default
|
||||||
const babel = require('gulp-babel')
|
|
||||||
const debug = require('gulp-debug')
|
|
||||||
const pify = require('pify')
|
const pify = require('pify')
|
||||||
const gulpMultiProcess = require('gulp-multi-process')
|
const gulpMultiProcess = require('gulp-multi-process')
|
||||||
const endOfStream = pify(require('end-of-stream'))
|
const endOfStream = pify(require('end-of-stream'))
|
||||||
|
|
||||||
function gulpParallel (...args) {
|
function gulpParallel (...args) {
|
||||||
return function spawnGulpChildProcess(cb) {
|
return function spawnGulpChildProcess (cb) {
|
||||||
return gulpMultiProcess(args, cb, true)
|
return gulpMultiProcess(args, cb, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,12 +42,12 @@ const commonPlatforms = [
|
||||||
// browser webapp
|
// browser webapp
|
||||||
'mascara',
|
'mascara',
|
||||||
// browser extensions
|
// browser extensions
|
||||||
...browserPlatforms
|
...browserPlatforms,
|
||||||
]
|
]
|
||||||
|
|
||||||
// browser reload
|
// browser reload
|
||||||
|
|
||||||
gulp.task('dev:reload', function() {
|
gulp.task('dev:reload', function () {
|
||||||
livereload.listen({
|
livereload.listen({
|
||||||
port: 35729,
|
port: 35729,
|
||||||
})
|
})
|
||||||
|
@ -108,7 +102,7 @@ createCopyTasks('html:mascara', {
|
||||||
destinations: [`./dist/mascara/`],
|
destinations: [`./dist/mascara/`],
|
||||||
})
|
})
|
||||||
|
|
||||||
function createCopyTasks(label, opts) {
|
function createCopyTasks (label, opts) {
|
||||||
if (!opts.devOnly) {
|
if (!opts.devOnly) {
|
||||||
const copyTaskName = `copy:${label}`
|
const copyTaskName = `copy:${label}`
|
||||||
copyTask(copyTaskName, opts)
|
copyTask(copyTaskName, opts)
|
||||||
|
@ -119,7 +113,7 @@ function createCopyTasks(label, opts) {
|
||||||
copyDevTaskNames.push(copyDevTaskName)
|
copyDevTaskNames.push(copyDevTaskName)
|
||||||
}
|
}
|
||||||
|
|
||||||
function copyTask(taskName, opts){
|
function copyTask (taskName, opts) {
|
||||||
const source = opts.source
|
const source = opts.source
|
||||||
const destination = opts.destination
|
const destination = opts.destination
|
||||||
const destinations = opts.destinations || [destination]
|
const destinations = opts.destinations || [destination]
|
||||||
|
@ -137,12 +131,12 @@ function copyTask(taskName, opts){
|
||||||
return performCopy()
|
return performCopy()
|
||||||
})
|
})
|
||||||
|
|
||||||
function performCopy() {
|
function performCopy () {
|
||||||
// stream from source
|
// stream from source
|
||||||
let stream = gulp.src(source + pattern, { base: source })
|
let stream = gulp.src(source + pattern, { base: source })
|
||||||
|
|
||||||
// copy to destinations
|
// copy to destinations
|
||||||
destinations.forEach(function(destination) {
|
destinations.forEach(function (destination) {
|
||||||
stream = stream.pipe(gulp.dest(destination))
|
stream = stream.pipe(gulp.dest(destination))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -152,40 +146,40 @@ function copyTask(taskName, opts){
|
||||||
|
|
||||||
// manifest tinkering
|
// manifest tinkering
|
||||||
|
|
||||||
gulp.task('manifest:chrome', function() {
|
gulp.task('manifest:chrome', function () {
|
||||||
return gulp.src('./dist/chrome/manifest.json')
|
return gulp.src('./dist/chrome/manifest.json')
|
||||||
.pipe(jsoneditor(function(json) {
|
.pipe(jsoneditor(function (json) {
|
||||||
delete json.applications
|
delete json.applications
|
||||||
return json
|
return json
|
||||||
}))
|
}))
|
||||||
.pipe(gulp.dest('./dist/chrome', { overwrite: true }))
|
.pipe(gulp.dest('./dist/chrome', { overwrite: true }))
|
||||||
})
|
})
|
||||||
|
|
||||||
gulp.task('manifest:opera', function() {
|
gulp.task('manifest:opera', function () {
|
||||||
return gulp.src('./dist/opera/manifest.json')
|
return gulp.src('./dist/opera/manifest.json')
|
||||||
.pipe(jsoneditor(function(json) {
|
.pipe(jsoneditor(function (json) {
|
||||||
json.permissions = [
|
json.permissions = [
|
||||||
"storage",
|
'storage',
|
||||||
"tabs",
|
'tabs',
|
||||||
"clipboardWrite",
|
'clipboardWrite',
|
||||||
"clipboardRead",
|
'clipboardRead',
|
||||||
"http://localhost:8545/"
|
'http://localhost:8545/',
|
||||||
]
|
]
|
||||||
return json
|
return json
|
||||||
}))
|
}))
|
||||||
.pipe(gulp.dest('./dist/opera', { overwrite: true }))
|
.pipe(gulp.dest('./dist/opera', { overwrite: true }))
|
||||||
})
|
})
|
||||||
|
|
||||||
gulp.task('manifest:production', function() {
|
gulp.task('manifest:production', function () {
|
||||||
return gulp.src([
|
return gulp.src([
|
||||||
'./dist/firefox/manifest.json',
|
'./dist/firefox/manifest.json',
|
||||||
'./dist/chrome/manifest.json',
|
'./dist/chrome/manifest.json',
|
||||||
'./dist/edge/manifest.json',
|
'./dist/edge/manifest.json',
|
||||||
'./dist/opera/manifest.json',
|
'./dist/opera/manifest.json',
|
||||||
],{base: './dist/'})
|
], {base: './dist/'})
|
||||||
|
|
||||||
// Exclude chromereload script in production:
|
// Exclude chromereload script in production:
|
||||||
.pipe(jsoneditor(function(json) {
|
.pipe(jsoneditor(function (json) {
|
||||||
json.background.scripts = json.background.scripts.filter((script) => {
|
json.background.scripts = json.background.scripts.filter((script) => {
|
||||||
return !script.includes('chromereload')
|
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
|
// scss compilation and autoprefixing tasks
|
||||||
|
|
||||||
gulp.task('build:scss', createScssBuildTask({
|
gulp.task('build:scss', createScssBuildTask({
|
||||||
|
@ -250,7 +221,7 @@ gulp.task('dev:scss', createScssBuildTask({
|
||||||
pattern: 'ui/app/**/*.scss',
|
pattern: 'ui/app/**/*.scss',
|
||||||
}))
|
}))
|
||||||
|
|
||||||
function createScssBuildTask({ src, dest, devMode, pattern }) {
|
function createScssBuildTask ({ src, dest, devMode, pattern }) {
|
||||||
return function () {
|
return function () {
|
||||||
if (devMode) {
|
if (devMode) {
|
||||||
watch(pattern, async (event) => {
|
watch(pattern, async (event) => {
|
||||||
|
@ -262,7 +233,7 @@ function createScssBuildTask({ src, dest, devMode, pattern }) {
|
||||||
return buildScss()
|
return buildScss()
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildScss() {
|
function buildScss () {
|
||||||
return gulp.src(src)
|
return gulp.src(src)
|
||||||
.pipe(sourcemaps.init())
|
.pipe(sourcemaps.init())
|
||||||
.pipe(sass().on('error', sass.logError))
|
.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
|
return gulp
|
||||||
.src('ui/app/css/itcss/**/*.scss')
|
.src('ui/app/css/itcss/**/*.scss')
|
||||||
.pipe(gulpStylelint({
|
.pipe(gulpStylelint({
|
||||||
reporters: [
|
reporters: [
|
||||||
{ formatter: 'string', console: true }
|
{ formatter: 'string', console: true },
|
||||||
],
|
],
|
||||||
fix: true,
|
fix: true,
|
||||||
}));
|
}))
|
||||||
});
|
})
|
||||||
|
|
||||||
gulp.task('fmt-scss', function () {
|
gulp.task('fmt-scss', function () {
|
||||||
return gulp.src('ui/app/css/itcss/**/*.scss')
|
return gulp.src('ui/app/css/itcss/**/*.scss')
|
||||||
.pipe(stylefmt())
|
.pipe(stylefmt())
|
||||||
.pipe(gulp.dest('ui/app/css/itcss'));
|
.pipe(gulp.dest('ui/app/css/itcss'))
|
||||||
});
|
})
|
||||||
|
|
||||||
// build js
|
// build js
|
||||||
|
|
||||||
|
@ -304,7 +275,7 @@ createTasksForBuildJsExtension({ buildJsFiles, taskPrefix: 'build:extension:js'
|
||||||
createTasksForBuildJsMascara({ taskPrefix: 'build:mascara:js' })
|
createTasksForBuildJsMascara({ taskPrefix: 'build:mascara:js' })
|
||||||
createTasksForBuildJsMascara({ taskPrefix: 'dev:mascara:js', devMode: true })
|
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:
|
// inpage must be built before all other scripts:
|
||||||
const rootDir = './app/scripts'
|
const rootDir = './app/scripts'
|
||||||
const nonInpageFiles = buildJsFiles.filter(file => file !== 'inpage')
|
const nonInpageFiles = buildJsFiles.filter(file => file !== 'inpage')
|
||||||
|
@ -322,7 +293,7 @@ function createTasksForBuildJsExtension({ buildJsFiles, taskPrefix, devMode, bun
|
||||||
createTasksForBuildJs({ rootDir, taskPrefix, bundleTaskOpts, destinations, buildPhase1, buildPhase2 })
|
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:
|
// inpage must be built before all other scripts:
|
||||||
const rootDir = './mascara/src/'
|
const rootDir = './mascara/src/'
|
||||||
const buildPhase1 = ['ui', 'proxy', 'background', 'metamascara']
|
const buildPhase1 = ['ui', 'proxy', 'background', 'metamascara']
|
||||||
|
@ -338,7 +309,7 @@ function createTasksForBuildJsMascara({ taskPrefix, devMode, bundleTaskOpts = {}
|
||||||
createTasksForBuildJs({ rootDir, taskPrefix, bundleTaskOpts, destinations, buildPhase1 })
|
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
|
// bundle task for each file
|
||||||
const jsFiles = [].concat(buildPhase1, buildPhase2)
|
const jsFiles = [].concat(buildPhase1, buildPhase2)
|
||||||
jsFiles.forEach((jsFile) => {
|
jsFiles.forEach((jsFile) => {
|
||||||
|
@ -367,7 +338,7 @@ gulp.task('disc', gulp.parallel(buildJsFiles.map(jsFile => `disc:${jsFile}`)))
|
||||||
|
|
||||||
// clean dist
|
// clean dist
|
||||||
|
|
||||||
gulp.task('clean', function clean() {
|
gulp.task('clean', function clean () {
|
||||||
return del(['./dist/*'])
|
return del(['./dist/*'])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -460,7 +431,7 @@ gulp.task('dist',
|
||||||
|
|
||||||
// task generators
|
// task generators
|
||||||
|
|
||||||
function zipTask(target) {
|
function zipTask (target) {
|
||||||
return () => {
|
return () => {
|
||||||
return gulp.src(`dist/${target}/**`)
|
return gulp.src(`dist/${target}/**`)
|
||||||
.pipe(zip(`metamask-${target}-${manifest.version}.zip`))
|
.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, {
|
const browserifyOpts = assign({}, watchify.args, {
|
||||||
entries: [opts.filepath],
|
entries: [opts.filepath],
|
||||||
plugin: 'browserify-derequire',
|
plugin: 'browserify-derequire',
|
||||||
|
@ -497,7 +468,7 @@ function generateBundler(opts, performBundle) {
|
||||||
return bundler
|
return bundler
|
||||||
}
|
}
|
||||||
|
|
||||||
function discTask(opts) {
|
function discTask (opts) {
|
||||||
opts = Object.assign({
|
opts = Object.assign({
|
||||||
buildWithFullPaths: true,
|
buildWithFullPaths: true,
|
||||||
}, opts)
|
}, opts)
|
||||||
|
@ -508,7 +479,7 @@ function discTask(opts) {
|
||||||
|
|
||||||
return performBundle
|
return performBundle
|
||||||
|
|
||||||
function performBundle(){
|
function performBundle () {
|
||||||
// start "disc" build
|
// start "disc" build
|
||||||
const discDir = path.join(__dirname, 'disc')
|
const discDir = path.join(__dirname, 'disc')
|
||||||
mkdirp.sync(discDir)
|
mkdirp.sync(discDir)
|
||||||
|
@ -523,14 +494,14 @@ function discTask(opts) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function bundleTask(opts) {
|
function bundleTask (opts) {
|
||||||
const bundler = generateBundler(opts, performBundle)
|
const bundler = generateBundler(opts, performBundle)
|
||||||
// output build logs to terminal
|
// output build logs to terminal
|
||||||
bundler.on('log', gutil.log)
|
bundler.on('log', gutil.log)
|
||||||
|
|
||||||
return performBundle
|
return performBundle
|
||||||
|
|
||||||
function performBundle(){
|
function performBundle () {
|
||||||
let buildStream = bundler.bundle()
|
let buildStream = bundler.bundle()
|
||||||
|
|
||||||
// handle errors
|
// handle errors
|
||||||
|
@ -562,7 +533,7 @@ function bundleTask(opts) {
|
||||||
buildStream = buildStream
|
buildStream = buildStream
|
||||||
.pipe(uglify({
|
.pipe(uglify({
|
||||||
mangle: {
|
mangle: {
|
||||||
reserved: [ 'MetamaskInpageProvider' ]
|
reserved: [ 'MetamaskInpageProvider' ],
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ const EthQuery = require('ethjs-query')
|
||||||
window.addEventListener('load', loadProvider)
|
window.addEventListener('load', loadProvider)
|
||||||
window.addEventListener('message', console.warn)
|
window.addEventListener('message', console.warn)
|
||||||
|
|
||||||
async function loadProvider() {
|
async function loadProvider () {
|
||||||
const ethereumProvider = window.metamask.createDefaultProvider({ host: 'http://localhost:9001' })
|
const ethereumProvider = window.metamask.createDefaultProvider({ host: 'http://localhost:9001' })
|
||||||
const ethQuery = new EthQuery(ethereumProvider)
|
const ethQuery = new EthQuery(ethereumProvider)
|
||||||
const accounts = await ethQuery.accounts()
|
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
|
document.getElementById(context).innerText = message
|
||||||
console.log(message)
|
console.log(message)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
const express = require('express')
|
const express = require('express')
|
||||||
|
const path = require('path')
|
||||||
const createMetamascaraServer = require('../server/')
|
const createMetamascaraServer = require('../server/')
|
||||||
const createBundle = require('../server/util').createBundle
|
const createBundle = require('../server/util').createBundle
|
||||||
const serveBundle = require('../server/util').serveBundle
|
const serveBundle = require('../server/util').serveBundle
|
||||||
|
|
||||||
//
|
//
|
||||||
// Iframe Server
|
// Iframe Server
|
||||||
//
|
//
|
||||||
|
@ -23,7 +23,7 @@ const dappServer = express()
|
||||||
|
|
||||||
// serve dapp bundle
|
// serve dapp bundle
|
||||||
serveBundle(dappServer, '/app.js', createBundle(require.resolve('./app.js')))
|
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
|
// start the server
|
||||||
const dappPort = '9002'
|
const dappPort = '9002'
|
||||||
|
|
|
@ -8,7 +8,7 @@ export default class Breadcrumbs extends Component {
|
||||||
currentIndex: PropTypes.number,
|
currentIndex: PropTypes.number,
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render () {
|
||||||
const {total, currentIndex} = this.props
|
const {total, currentIndex} = this.props
|
||||||
return (
|
return (
|
||||||
<div className="breadcrumbs">
|
<div className="breadcrumbs">
|
||||||
|
@ -20,7 +20,7 @@ export default class Breadcrumbs extends Component {
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,7 +54,7 @@ class BuyEtherScreen extends Component {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className='buy-ether__do-it-later'
|
className="buy-ether__do-it-later"
|
||||||
onClick={() => showAccountDetail(address)}
|
onClick={() => showAccountDetail(address)}
|
||||||
>
|
>
|
||||||
Do it later
|
Do it later
|
||||||
|
@ -64,17 +64,17 @@ class BuyEtherScreen extends Component {
|
||||||
|
|
||||||
renderCoinbaseLogo () {
|
renderCoinbaseLogo () {
|
||||||
return (
|
return (
|
||||||
<svg width='140px' height='49px' viewBox='0 0 579 126' version='1.1'>
|
<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="Page-1" stroke="none" strokeWidth={1} fill="none" fillRule="evenodd">
|
||||||
<g id='Imported-Layers' fill='#0081C9'>
|
<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="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="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="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="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="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="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="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' />
|
<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>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
@ -85,13 +85,13 @@ class BuyEtherScreen extends Component {
|
||||||
const {goToCoinbase, address} = this.props
|
const {goToCoinbase, address} = this.props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='buy-ether__action-content-wrapper'>
|
<div className="buy-ether__action-content-wrapper">
|
||||||
<div>{this.renderCoinbaseLogo()}</div>
|
<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>
|
<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>
|
<a className="first-time-flow__link buy-ether__faq-link">What is Ethereum?</a>
|
||||||
<div className='buy-ether__buttons'>
|
<div className="buy-ether__buttons">
|
||||||
<button
|
<button
|
||||||
className='first-time-flow__button'
|
className="first-time-flow__button"
|
||||||
onClick={() => goToCoinbase(address)}
|
onClick={() => goToCoinbase(address)}
|
||||||
>
|
>
|
||||||
Buy
|
Buy
|
||||||
|
@ -114,23 +114,23 @@ class BuyEtherScreen extends Component {
|
||||||
return this.renderCoinbaseForm()
|
return this.renderCoinbaseForm()
|
||||||
case OPTION_VALUES.SHAPESHIFT:
|
case OPTION_VALUES.SHAPESHIFT:
|
||||||
return (
|
return (
|
||||||
<div className='buy-ether__action-content-wrapper'>
|
<div className="buy-ether__action-content-wrapper">
|
||||||
<div className='shapeshift-logo' />
|
<div className="shapeshift-logo" />
|
||||||
<div className='buy-ether__body-text'>
|
<div className="buy-ether__body-text">
|
||||||
Trade any leading blockchain asset for any other. Protection by Design. No Account Needed.
|
Trade any leading blockchain asset for any other. Protection by Design. No Account Needed.
|
||||||
</div>
|
</div>
|
||||||
<ShapeShiftForm btnClass='first-time-flow__button' />
|
<ShapeShiftForm btnClass="first-time-flow__button" />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
case OPTION_VALUES.QR_CODE:
|
case OPTION_VALUES.QR_CODE:
|
||||||
return (
|
return (
|
||||||
<div className='buy-ether__action-content-wrapper'>
|
<div className="buy-ether__action-content-wrapper">
|
||||||
<div dangerouslySetInnerHTML={{ __html: qrImage.createTableTag(4) }} />
|
<div dangerouslySetInnerHTML={{ __html: qrImage.createTableTag(4) }} />
|
||||||
<div className='buy-ether__body-text'>Deposit Ether directly into your account.</div>
|
<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__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__buttons">
|
||||||
<button
|
<button
|
||||||
className='first-time-flow__button'
|
className="first-time-flow__button"
|
||||||
onClick={this.copyToClipboard}
|
onClick={this.copyToClipboard}
|
||||||
disabled={justCopied}
|
disabled={justCopied}
|
||||||
>
|
>
|
||||||
|
@ -149,19 +149,19 @@ class BuyEtherScreen extends Component {
|
||||||
const { selectedOption } = this.state
|
const { selectedOption } = this.state
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='buy-ether'>
|
<div className="buy-ether">
|
||||||
<Identicon address={this.props.address} diameter={70} />
|
<Identicon address={this.props.address} diameter={70} />
|
||||||
<div className='buy-ether__title'>Deposit Ether</div>
|
<div className="buy-ether__title">Deposit Ether</div>
|
||||||
<div className='buy-ether__body-text'>
|
<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.
|
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>
|
||||||
<div className='buy-ether__content-wrapper'>
|
<div className="buy-ether__content-wrapper">
|
||||||
<div className='buy-ether__content-headline-wrapper'>
|
<div className="buy-ether__content-headline-wrapper">
|
||||||
<div className='buy-ether__content-headline'>Deposit Options</div>
|
<div className="buy-ether__content-headline">Deposit Options</div>
|
||||||
{this.renderSkip()}
|
{this.renderSkip()}
|
||||||
</div>
|
</div>
|
||||||
<div className='buy-ether__content'>
|
<div className="buy-ether__content">
|
||||||
<div className='buy-ether__side-panel'>
|
<div className="buy-ether__side-panel">
|
||||||
{OPTIONS.map(({ name, value }) => (
|
{OPTIONS.map(({ name, value }) => (
|
||||||
<div
|
<div
|
||||||
key={value}
|
key={value}
|
||||||
|
@ -170,16 +170,16 @@ class BuyEtherScreen extends Component {
|
||||||
})}
|
})}
|
||||||
onClick={() => this.setState({ selectedOption: value })}
|
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 && (
|
{value === selectedOption && (
|
||||||
<svg viewBox='0 0 574 1024' id='si-ant-right' width='15px' height='15px'>
|
<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' />
|
<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>
|
</svg>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div className='buy-ether__action-content'>
|
<div className="buy-ether__action-content">
|
||||||
{this.renderContent()}
|
{this.renderContent()}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -123,10 +123,6 @@
|
||||||
width: calc(100vw - 80px);
|
width: calc(100vw - 80px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.unique-image {
|
|
||||||
width: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.create-password__title,
|
.create-password__title,
|
||||||
.unique-image__title,
|
.unique-image__title,
|
||||||
.tou__title,
|
.tou__title,
|
||||||
|
@ -148,7 +144,7 @@
|
||||||
height: 100%;
|
height: 100%;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-evenly;
|
justify-content: flex-start;
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,7 +177,6 @@
|
||||||
margin: 0 !important;
|
margin: 0 !important;
|
||||||
padding: 16px 20px !important;
|
padding: 16px 20px !important;
|
||||||
height: 30vh !important;
|
height: 30vh !important;
|
||||||
width: calc(100% - 48px) !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.backup-phrase__content-wrapper {
|
.backup-phrase__content-wrapper {
|
||||||
|
@ -280,6 +275,12 @@
|
||||||
width: 335px;
|
width: 335px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: 575px) {
|
||||||
|
.unique-image__body-text {
|
||||||
|
width: initial;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.unique-image__body-text +
|
.unique-image__body-text +
|
||||||
.unique-image__body-text,
|
.unique-image__body-text,
|
||||||
.backup-phrase__body-text +
|
.backup-phrase__body-text +
|
||||||
|
@ -294,7 +295,7 @@
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
background-color: #FFFFFF;
|
background-color: #FFFFFF;
|
||||||
margin: 0 142px 0 0;
|
margin: 0 142px 0 0;
|
||||||
height: 334px;
|
height: 200px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
color: #757575;
|
color: #757575;
|
||||||
font-family: Roboto;
|
font-family: Roboto;
|
||||||
|
@ -679,7 +680,7 @@ button.backup-phrase__confirm-seed-option:hover {
|
||||||
}
|
}
|
||||||
|
|
||||||
.first-time-flow__input {
|
.first-time-flow__input {
|
||||||
width: 350px;
|
max-width: 350px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.first-time-flow__button {
|
.first-time-flow__button {
|
||||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import Spinner from './spinner'
|
import Spinner from './spinner'
|
||||||
|
|
||||||
export default function LoadingScreen({ className = '', loadingMessage }) {
|
export default function LoadingScreen ({ className = '', loadingMessage }) {
|
||||||
return (
|
return (
|
||||||
<div className={`${className} loading-screen`}>
|
<div className={`${className} loading-screen`}>
|
||||||
<Spinner color="#1B344D" />
|
<Spinner color="#1B344D" />
|
||||||
|
|
|
@ -14,7 +14,7 @@ import LoadingScreen from './loading-screen'
|
||||||
class NoticeScreen extends Component {
|
class NoticeScreen extends Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
address: PropTypes.string.isRequired,
|
address: PropTypes.string.isRequired,
|
||||||
lastUnreadNotice: PropTypes.shape({
|
nextUnreadNotice: PropTypes.shape({
|
||||||
title: PropTypes.string,
|
title: PropTypes.string,
|
||||||
date: PropTypes.string,
|
date: PropTypes.string,
|
||||||
body: PropTypes.string,
|
body: PropTypes.string,
|
||||||
|
@ -31,7 +31,7 @@ class NoticeScreen extends Component {
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
lastUnreadNotice: {},
|
nextUnreadNotice: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
|
@ -47,8 +47,8 @@ class NoticeScreen extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
acceptTerms = () => {
|
acceptTerms = () => {
|
||||||
const { markNoticeRead, lastUnreadNotice, history } = this.props
|
const { markNoticeRead, nextUnreadNotice, history } = this.props
|
||||||
markNoticeRead(lastUnreadNotice)
|
markNoticeRead(nextUnreadNotice)
|
||||||
.then(hasActiveNotices => {
|
.then(hasActiveNotices => {
|
||||||
if (!hasActiveNotices) {
|
if (!hasActiveNotices) {
|
||||||
history.push(INITIALIZE_BACKUP_PHRASE_ROUTE)
|
history.push(INITIALIZE_BACKUP_PHRASE_ROUTE)
|
||||||
|
@ -72,7 +72,7 @@ class NoticeScreen extends Component {
|
||||||
render () {
|
render () {
|
||||||
const {
|
const {
|
||||||
address,
|
address,
|
||||||
lastUnreadNotice: { title, body },
|
nextUnreadNotice: { title, body },
|
||||||
isLoading,
|
isLoading,
|
||||||
} = this.props
|
} = this.props
|
||||||
const { atBottom } = this.state
|
const { atBottom } = this.state
|
||||||
|
@ -113,12 +113,12 @@ class NoticeScreen extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = ({ metamask, appState }) => {
|
const mapStateToProps = ({ metamask, appState }) => {
|
||||||
const { selectedAddress, lastUnreadNotice, noActiveNotices } = metamask
|
const { selectedAddress, nextUnreadNotice, noActiveNotices } = metamask
|
||||||
const { isLoading } = appState
|
const { isLoading } = appState
|
||||||
|
|
||||||
return {
|
return {
|
||||||
address: selectedAddress,
|
address: selectedAddress,
|
||||||
lastUnreadNotice,
|
nextUnreadNotice,
|
||||||
noActiveNotices,
|
noActiveNotices,
|
||||||
isLoading,
|
isLoading,
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,11 +79,11 @@ export class ShapeShiftForm extends Component {
|
||||||
|
|
||||||
renderMetadata (label, value) {
|
renderMetadata (label, value) {
|
||||||
return (
|
return (
|
||||||
<div className='shapeshift-form__metadata-wrapper'>
|
<div className="shapeshift-form__metadata-wrapper">
|
||||||
<div className='shapeshift-form__metadata-label'>
|
<div className="shapeshift-form__metadata-label">
|
||||||
{label}:
|
{label}:
|
||||||
</div>
|
</div>
|
||||||
<div className='shapeshift-form__metadata-value'>
|
<div className="shapeshift-form__metadata-value">
|
||||||
{value}
|
{value}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -101,7 +101,7 @@ export class ShapeShiftForm extends Component {
|
||||||
} = tokenExchangeRates[coinPair] || {}
|
} = tokenExchangeRates[coinPair] || {}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='shapeshift-form__metadata'>
|
<div className="shapeshift-form__metadata">
|
||||||
{this.renderMetadata('Status', limit ? 'Available' : 'Unavailable')}
|
{this.renderMetadata('Status', limit ? 'Available' : 'Unavailable')}
|
||||||
{this.renderMetadata('Limit', limit)}
|
{this.renderMetadata('Limit', limit)}
|
||||||
{this.renderMetadata('Exchange Rate', rate)}
|
{this.renderMetadata('Exchange Rate', rate)}
|
||||||
|
@ -117,13 +117,13 @@ export class ShapeShiftForm extends Component {
|
||||||
qrImage.make()
|
qrImage.make()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='shapeshift-form'>
|
<div className="shapeshift-form">
|
||||||
<div className='shapeshift-form__deposit-instruction'>
|
<div className="shapeshift-form__deposit-instruction">
|
||||||
Deposit your BTC to the address bellow:
|
Deposit your BTC to the address bellow:
|
||||||
</div>
|
</div>
|
||||||
<div className='shapeshift-form__qr-code'>
|
<div className="shapeshift-form__qr-code">
|
||||||
{isLoading
|
{isLoading
|
||||||
? <img src='images/loading.svg' style={{ width: '60px' }} />
|
? <img src="images/loading.svg" style={{ width: '60px' }} />
|
||||||
: <div dangerouslySetInnerHTML={{ __html: qrImage.createTableTag(4) }} />
|
: <div dangerouslySetInnerHTML={{ __html: qrImage.createTableTag(4) }} />
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
@ -141,14 +141,14 @@ export class ShapeShiftForm extends Component {
|
||||||
|
|
||||||
return showQrCode ? this.renderQrCode() : (
|
return showQrCode ? this.renderQrCode() : (
|
||||||
<div>
|
<div>
|
||||||
<div className='shapeshift-form'>
|
<div className="shapeshift-form">
|
||||||
<div className='shapeshift-form__selectors'>
|
<div className="shapeshift-form__selectors">
|
||||||
<div className='shapeshift-form__selector'>
|
<div className="shapeshift-form__selector">
|
||||||
<div className='shapeshift-form__selector-label'>
|
<div className="shapeshift-form__selector-label">
|
||||||
Deposit
|
Deposit
|
||||||
</div>
|
</div>
|
||||||
<select
|
<select
|
||||||
className='shapeshift-form__selector-input'
|
className="shapeshift-form__selector-input"
|
||||||
value={this.state.depositCoin}
|
value={this.state.depositCoin}
|
||||||
onChange={this.onCoinChange}
|
onChange={this.onCoinChange}
|
||||||
>
|
>
|
||||||
|
@ -160,14 +160,14 @@ export class ShapeShiftForm extends Component {
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className='icon shapeshift-form__caret'
|
className="icon shapeshift-form__caret"
|
||||||
style={{ backgroundImage: 'url(images/caret-right.svg)'}}
|
style={{ backgroundImage: 'url(images/caret-right.svg)'}}
|
||||||
/>
|
/>
|
||||||
<div className='shapeshift-form__selector'>
|
<div className="shapeshift-form__selector">
|
||||||
<div className='shapeshift-form__selector-label'>
|
<div className="shapeshift-form__selector-label">
|
||||||
Receive
|
Receive
|
||||||
</div>
|
</div>
|
||||||
<div className='shapeshift-form__selector-input'>
|
<div className="shapeshift-form__selector-input">
|
||||||
ETH
|
ETH
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -177,18 +177,18 @@ export class ShapeShiftForm extends Component {
|
||||||
'shapeshift-form__address-input-wrapper--error': errorMessage,
|
'shapeshift-form__address-input-wrapper--error': errorMessage,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<div className='shapeshift-form__address-input-label'>
|
<div className="shapeshift-form__address-input-label">
|
||||||
Your Refund Address
|
Your Refund Address
|
||||||
</div>
|
</div>
|
||||||
<input
|
<input
|
||||||
type='text'
|
type="text"
|
||||||
className='shapeshift-form__address-input'
|
className="shapeshift-form__address-input"
|
||||||
onChange={e => this.setState({
|
onChange={e => this.setState({
|
||||||
refundAddress: e.target.value,
|
refundAddress: e.target.value,
|
||||||
errorMessage: '',
|
errorMessage: '',
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
<div className='shapeshift-form__address-input-error-message'>
|
<div className="shapeshift-form__address-input-error-message">
|
||||||
{errorMessage}
|
{errorMessage}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -37,7 +37,7 @@ const dbController = new DbController({
|
||||||
|
|
||||||
start().catch(log.error)
|
start().catch(log.error)
|
||||||
|
|
||||||
async function start() {
|
async function start () {
|
||||||
log.debug('MetaMask initializing...')
|
log.debug('MetaMask initializing...')
|
||||||
const initState = await loadStateFromPersistence()
|
const initState = await loadStateFromPersistence()
|
||||||
await setupController(initState)
|
await setupController(initState)
|
||||||
|
|
|
@ -43,7 +43,7 @@ console.log('starting service worker')
|
||||||
swController.startWorker()
|
swController.startWorker()
|
||||||
|
|
||||||
// Setup listener for when the service worker is read
|
// Setup listener for when the service worker is read
|
||||||
function connectApp() {
|
function connectApp () {
|
||||||
const connectionStream = SwStream({
|
const connectionStream = SwStream({
|
||||||
serviceWorker: swController.getWorker(),
|
serviceWorker: swController.getWorker(),
|
||||||
context: name,
|
context: name,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
function wait(time) {
|
export default function wait (time) {
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function (resolve, reject) {
|
||||||
setTimeout(function() {
|
setTimeout(function () {
|
||||||
resolve()
|
resolve()
|
||||||
}, time * 3 || 1500)
|
}, time * 3 || 1500)
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
var fs = require('fs')
|
var fs = require('fs')
|
||||||
var path = require('path')
|
var path = require('path')
|
||||||
var browserify = require('browserify');
|
var browserify = require('browserify')
|
||||||
var tests = fs.readdirSync(path.join(__dirname, 'lib'))
|
var tests = fs.readdirSync(path.join(__dirname, 'lib'))
|
||||||
var bundlePath = path.join(__dirname, 'test-bundle.js')
|
var bundlePath = path.join(__dirname, 'test-bundle.js')
|
||||||
var b = browserify();
|
var b = browserify()
|
||||||
|
|
||||||
// Remove old bundle
|
// Remove old bundle
|
||||||
try {
|
try {
|
||||||
|
@ -14,9 +14,9 @@ try {
|
||||||
|
|
||||||
var writeStream = fs.createWriteStream(bundlePath)
|
var writeStream = fs.createWriteStream(bundlePath)
|
||||||
|
|
||||||
tests.forEach(function(fileName) {
|
tests.forEach(function (fileName) {
|
||||||
b.add(path.join(__dirname, 'lib', 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.addEventListener('load', () => {
|
||||||
window.METAMASK_SKIP_RELOAD = true
|
window.METAMASK_SKIP_RELOAD = true
|
||||||
// inject app container
|
// inject app container
|
||||||
|
|
|
@ -2,27 +2,29 @@ const EventEmitter = require('events')
|
||||||
const IDB = require('idb-global')
|
const IDB = require('idb-global')
|
||||||
const KEY = 'metamask-test-config'
|
const KEY = 'metamask-test-config'
|
||||||
module.exports = class Helper extends EventEmitter {
|
module.exports = class Helper extends EventEmitter {
|
||||||
constructor () {
|
|
||||||
super()
|
|
||||||
}
|
|
||||||
|
|
||||||
tryToCleanContext () {
|
tryToCleanContext () {
|
||||||
this.unregister()
|
this.unregister()
|
||||||
.then(() => this.clearDb())
|
.then(() => this.clearDb())
|
||||||
.then(() => super.emit('complete'))
|
.then(() => super.emit('complete'))
|
||||||
.catch((err) => super.emit('complete'))
|
.catch((err) => {
|
||||||
|
if (err) {
|
||||||
|
super.emit('complete')
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
unregister () {
|
unregister () {
|
||||||
return global.navigator.serviceWorker.getRegistration()
|
return global.navigator.serviceWorker.getRegistration()
|
||||||
.then((registration) => {
|
.then((registration) => {
|
||||||
if (registration) return registration.unregister()
|
if (registration) {
|
||||||
|
return registration.unregister()
|
||||||
.then((b) => b ? Promise.resolve() : Promise.reject())
|
.then((b) => b ? Promise.resolve() : Promise.reject())
|
||||||
else return Promise.resolve()
|
} else return Promise.resolve()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
clearDb () {
|
clearDb () {
|
||||||
return new Promise ((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const deleteRequest = global.indexDB.deleteDatabase(KEY)
|
const deleteRequest = global.indexDB.deleteDatabase(KEY)
|
||||||
deleteRequest.addEventListener('success', resolve)
|
deleteRequest.addEventListener('success', resolve)
|
||||||
deleteRequest.addEventListener('error', reject)
|
deleteRequest.addEventListener('error', reject)
|
||||||
|
@ -33,7 +35,7 @@ module.exports = class Helper extends EventEmitter {
|
||||||
const db = new IDB({
|
const db = new IDB({
|
||||||
version: 2,
|
version: 2,
|
||||||
key: KEY,
|
key: KEY,
|
||||||
initialState: state
|
initialState: state,
|
||||||
})
|
})
|
||||||
return db.open()
|
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,
|
network: state.metamask.network,
|
||||||
provider: state.metamask.provider,
|
provider: state.metamask.provider,
|
||||||
forgottenPassword: state.appState.forgottenPassword,
|
forgottenPassword: state.appState.forgottenPassword,
|
||||||
lastUnreadNotice: state.metamask.lastUnreadNotice,
|
nextUnreadNotice: state.metamask.nextUnreadNotice,
|
||||||
lostAccounts: state.metamask.lostAccounts,
|
lostAccounts: state.metamask.lostAccounts,
|
||||||
frequentRpcList: state.metamask.frequentRpcList || [],
|
frequentRpcList: state.metamask.frequentRpcList || [],
|
||||||
featureFlags,
|
featureFlags,
|
||||||
|
@ -461,9 +461,9 @@ App.prototype.renderPrimary = function () {
|
||||||
}, [
|
}, [
|
||||||
|
|
||||||
h(NoticeScreen, {
|
h(NoticeScreen, {
|
||||||
notice: props.lastUnreadNotice,
|
notice: props.nextUnreadNotice,
|
||||||
key: 'NoticeScreen',
|
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', [
|
!props.isInitialized && h('.flex-row.flex-center.flex-grow', [
|
||||||
|
|
|
@ -23,9 +23,10 @@ class AccountDropdowns extends Component {
|
||||||
|
|
||||||
renderAccounts () {
|
renderAccounts () {
|
||||||
const { identities, selected, keyrings } = this.props
|
const { identities, selected, keyrings } = this.props
|
||||||
|
const accountOrder = keyrings.reduce((list, keyring) => list.concat(keyring.accounts), [])
|
||||||
|
|
||||||
return Object.keys(identities).map((key, index) => {
|
return accountOrder.map((address, index) => {
|
||||||
const identity = identities[key]
|
const identity = identities[address]
|
||||||
const isSelected = identity.address === selected
|
const isSelected = identity.address === selected
|
||||||
|
|
||||||
const simpleAddress = identity.address.substring(2).toLowerCase()
|
const simpleAddress = identity.address.substring(2).toLowerCase()
|
||||||
|
|
|
@ -20,7 +20,7 @@ function EnsInput () {
|
||||||
EnsInput.prototype.render = function () {
|
EnsInput.prototype.render = function () {
|
||||||
const props = this.props
|
const props = this.props
|
||||||
|
|
||||||
function onInputChange() {
|
function onInputChange () {
|
||||||
const network = this.props.network
|
const network = this.props.network
|
||||||
const networkHasEnsSupport = getNetworkEnsSupport(network)
|
const networkHasEnsSupport = getNetworkEnsSupport(network)
|
||||||
if (!networkHasEnsSupport) return
|
if (!networkHasEnsSupport) return
|
||||||
|
|
|
@ -28,7 +28,7 @@ LoadingIndicator.prototype.render = function () {
|
||||||
background: 'rgba(255, 255, 255, 0.8)',
|
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: {
|
style: {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: '1px',
|
top: '1px',
|
||||||
|
|
|
@ -40,8 +40,8 @@ TransactionListItem.prototype.showRetryButton = function () {
|
||||||
const currentNonceTxs = transactions.filter(tx => tx.txParams.nonce === currentNonce)
|
const currentNonceTxs = transactions.filter(tx => tx.txParams.nonce === currentNonce)
|
||||||
const currentNonceSubmittedTxs = currentNonceTxs.filter(tx => tx.status === 'submitted')
|
const currentNonceSubmittedTxs = currentNonceTxs.filter(tx => tx.status === 'submitted')
|
||||||
const lastSubmittedTxWithCurrentNonce = currentNonceSubmittedTxs[0]
|
const lastSubmittedTxWithCurrentNonce = currentNonceSubmittedTxs[0]
|
||||||
const currentTxIsLatestWithNonce = lastSubmittedTxWithCurrentNonce
|
const currentTxIsLatestWithNonce = lastSubmittedTxWithCurrentNonce &&
|
||||||
&& lastSubmittedTxWithCurrentNonce.id === transaction.id
|
lastSubmittedTxWithCurrentNonce.id === transaction.id
|
||||||
|
|
||||||
return currentTxIsLatestWithNonce && Date.now() - submittedTime > 30000
|
return currentTxIsLatestWithNonce && Date.now() - submittedTime > 30000
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,9 +34,13 @@ TypedMessageRenderer.prototype.render = function () {
|
||||||
|
|
||||||
function renderTypedData (values) {
|
function renderTypedData (values) {
|
||||||
return values.map(function (value) {
|
return values.map(function (value) {
|
||||||
|
let v = value.value
|
||||||
|
if (typeof v === 'boolean') {
|
||||||
|
v = v.toString()
|
||||||
|
}
|
||||||
return h('div', {}, [
|
return h('div', {}, [
|
||||||
h('strong', {style: {display: 'block', fontWeight: 'bold'}}, String(value.name) + ':'),
|
h('strong', {style: {display: 'block', fontWeight: 'bold'}}, String(value.name) + ':'),
|
||||||
h('div', {}, value.value),
|
h('div', {}, v),
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ ConfigScreen.prototype.render = function () {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
h('.flex-column.flex-grow', {
|
h('.flex-column.flex-grow', {
|
||||||
style:{
|
style: {
|
||||||
maxHeight: '585px',
|
maxHeight: '585px',
|
||||||
overflowY: 'auto',
|
overflowY: 'auto',
|
||||||
},
|
},
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
33
package.json
33
package.json
|
@ -9,7 +9,8 @@
|
||||||
"dist": "gulp dist",
|
"dist": "gulp dist",
|
||||||
"doc": "jsdoc -c development/tools/.jsdoc.json",
|
"doc": "jsdoc -c development/tools/.jsdoc.json",
|
||||||
"test": "npm run test:unit && npm run test:integration && npm run lint",
|
"test": "npm run test:unit && npm run test:integration && npm run lint",
|
||||||
"test:unit": "cross-env METAMASK_ENV=test mocha --exit --require test/setup.js --recursive \"test/unit/**/*.js\" && dot-only-hunter",
|
"watch:test:unit": "nodemon --exec \"npm run test:unit\" ./test ./app ./ui",
|
||||||
|
"test:unit": "cross-env METAMASK_ENV=test mocha --exit --require test/setup.js --recursive \"test/unit/**/*.js\" \"ui/app/**/*.test.js\" && dot-only-hunter",
|
||||||
"test:single": "cross-env METAMASK_ENV=test mocha --require test/helper.js",
|
"test:single": "cross-env METAMASK_ENV=test mocha --require test/helper.js",
|
||||||
"test:integration": "npm run test:integration:build && npm run test:flat && npm run test:mascara",
|
"test:integration": "npm run test:integration:build && npm run test:flat && npm run test:mascara",
|
||||||
"test:integration:build": "gulp build:scss",
|
"test:integration:build": "gulp build:scss",
|
||||||
|
@ -37,16 +38,14 @@
|
||||||
"test:mascara:build:tests": "browserify test/integration/lib/first-time.js -o dist/mascara/tests.js",
|
"test:mascara:build:tests": "browserify test/integration/lib/first-time.js -o dist/mascara/tests.js",
|
||||||
"ganache:start": "ganache-cli -m 'phrase upgrade clock rough situate wedding elder clever doctor stamp excess tent'",
|
"ganache:start": "ganache-cli -m 'phrase upgrade clock rough situate wedding elder clever doctor stamp excess tent'",
|
||||||
"sentry:publish": "node ./development/sentry-publish.js",
|
"sentry:publish": "node ./development/sentry-publish.js",
|
||||||
"lint": "gulp lint",
|
"lint": "eslint .",
|
||||||
"lint:fix": "gulp lint:fix",
|
"lint:fix": "eslint . --fix",
|
||||||
"ui": "npm run test:flat:build:states && beefy development/ui-dev.js:bundle.js --live --open --index=./development/index.html --cwd ./",
|
"ui": "npm run test:flat:build:states && beefy development/ui-dev.js:bundle.js --live --open --index=./development/index.html --cwd ./",
|
||||||
"mock": "beefy development/mock-dev.js:bundle.js --live --open --index=./development/index.html --cwd ./",
|
"mock": "beefy development/mock-dev.js:bundle.js --live --open --index=./development/index.html --cwd ./",
|
||||||
"watch": "mocha watch --recursive \"test/unit/**/*.js\"",
|
"watch": "mocha watch --recursive \"test/unit/**/*.js\"",
|
||||||
"disc": "gulp disc --debug",
|
"disc": "gulp disc --debug",
|
||||||
"announce": "node development/announcer.js",
|
"announce": "node development/announcer.js",
|
||||||
"version:bump": "node development/run-version-bump.js",
|
"version:bump": "node development/run-version-bump.js",
|
||||||
"generateNotice": "node notices/notice-generator.js",
|
|
||||||
"deleteNotice": "node notices/notice-delete.js",
|
|
||||||
"storybook": "start-storybook -p 6006 -c .storybook"
|
"storybook": "start-storybook -p 6006 -c .storybook"
|
||||||
},
|
},
|
||||||
"browserify": {
|
"browserify": {
|
||||||
|
@ -94,7 +93,7 @@
|
||||||
"ensnare": "^1.0.0",
|
"ensnare": "^1.0.0",
|
||||||
"eslint-plugin-react": "^7.4.0",
|
"eslint-plugin-react": "^7.4.0",
|
||||||
"eth-bin-to-ops": "^1.0.1",
|
"eth-bin-to-ops": "^1.0.1",
|
||||||
"eth-contract-metadata": "^1.1.5",
|
"eth-contract-metadata": "github:MetaMask/eth-contract-metadata#master",
|
||||||
"eth-hd-keyring": "^1.2.1",
|
"eth-hd-keyring": "^1.2.1",
|
||||||
"eth-json-rpc-filters": "^1.2.6",
|
"eth-json-rpc-filters": "^1.2.6",
|
||||||
"eth-json-rpc-infura": "^3.0.0",
|
"eth-json-rpc-infura": "^3.0.0",
|
||||||
|
@ -108,7 +107,7 @@
|
||||||
"ethereumjs-wallet": "^0.6.0",
|
"ethereumjs-wallet": "^0.6.0",
|
||||||
"etherscan-link": "^1.0.2",
|
"etherscan-link": "^1.0.2",
|
||||||
"ethjs": "^0.4.0",
|
"ethjs": "^0.4.0",
|
||||||
"ethjs-contract": "^0.2.0",
|
"ethjs-contract": "^0.2.3",
|
||||||
"ethjs-ens": "^2.0.0",
|
"ethjs-ens": "^2.0.0",
|
||||||
"ethjs-query": "^0.3.4",
|
"ethjs-query": "^0.3.4",
|
||||||
"express": "^4.15.5",
|
"express": "^4.15.5",
|
||||||
|
@ -184,6 +183,7 @@
|
||||||
"semaphore": "^1.0.5",
|
"semaphore": "^1.0.5",
|
||||||
"semver": "^5.4.1",
|
"semver": "^5.4.1",
|
||||||
"shallow-copy": "0.0.1",
|
"shallow-copy": "0.0.1",
|
||||||
|
"superstatic": "^5.0.2",
|
||||||
"sw-controller": "^1.0.3",
|
"sw-controller": "^1.0.3",
|
||||||
"sw-stream": "^2.0.2",
|
"sw-stream": "^2.0.2",
|
||||||
"textarea-caret": "^3.0.1",
|
"textarea-caret": "^3.0.1",
|
||||||
|
@ -211,7 +211,7 @@
|
||||||
"babel-register": "^6.7.2",
|
"babel-register": "^6.7.2",
|
||||||
"babelify": "^8.0.0",
|
"babelify": "^8.0.0",
|
||||||
"beefy": "^2.1.5",
|
"beefy": "^2.1.5",
|
||||||
"brfs": "^1.4.3",
|
"brfs": "^1.6.1",
|
||||||
"browserify": "^16.1.1",
|
"browserify": "^16.1.1",
|
||||||
"chai": "^4.1.0",
|
"chai": "^4.1.0",
|
||||||
"chromedriver": "2.36.0",
|
"chromedriver": "2.36.0",
|
||||||
|
@ -235,13 +235,12 @@
|
||||||
"file-loader": "^1.1.11",
|
"file-loader": "^1.1.11",
|
||||||
"fs-promise": "^2.0.3",
|
"fs-promise": "^2.0.3",
|
||||||
"ganache-cli": "^6.1.0",
|
"ganache-cli": "^6.1.0",
|
||||||
"ganache-core": "^2.1.0",
|
"ganache-core": "^2.1.3",
|
||||||
"geckodriver": "^1.11.0",
|
"geckodriver": "^1.11.0",
|
||||||
"gh-pages": "^1.1.0",
|
"gh-pages": "^1.2.0",
|
||||||
"gifencoder": "^1.1.0",
|
"gifencoder": "^1.1.0",
|
||||||
"gulp": "github:gulpjs/gulp#6d71a658c61edb3090221579d8f97dbe086ba2ed",
|
"gulp": "github:gulpjs/gulp#6d71a658c61edb3090221579d8f97dbe086ba2ed",
|
||||||
"gulp-babel": "^7.0.0",
|
"gulp-babel": "^7.0.0",
|
||||||
"gulp-eslint": "^4.0.0",
|
|
||||||
"gulp-json-editor": "^2.2.1",
|
"gulp-json-editor": "^2.2.1",
|
||||||
"gulp-livereload": "^3.8.1",
|
"gulp-livereload": "^3.8.1",
|
||||||
"gulp-multi-process": "^1.3.1",
|
"gulp-multi-process": "^1.3.1",
|
||||||
|
@ -254,13 +253,14 @@
|
||||||
"gulp-util": "^3.0.7",
|
"gulp-util": "^3.0.7",
|
||||||
"gulp-watch": "^5.0.0",
|
"gulp-watch": "^5.0.0",
|
||||||
"gulp-zip": "^4.0.0",
|
"gulp-zip": "^4.0.0",
|
||||||
|
"http-server": "^0.11.1",
|
||||||
"image-size": "^0.6.2",
|
"image-size": "^0.6.2",
|
||||||
"isomorphic-fetch": "^2.2.1",
|
"isomorphic-fetch": "^2.2.1",
|
||||||
"jsdoc": "^3.5.5",
|
"jsdoc": "^3.5.5",
|
||||||
"jsdom": "^11.2.0",
|
"jsdom": "^11.2.0",
|
||||||
"jsdom-global": "^3.0.2",
|
"jsdom-global": "^3.0.2",
|
||||||
"jshint-stylish": "~2.2.1",
|
"jshint-stylish": "~2.2.1",
|
||||||
"karma": "^2.0.0",
|
"karma": "^2.0.4",
|
||||||
"karma-chrome-launcher": "^2.2.0",
|
"karma-chrome-launcher": "^2.2.0",
|
||||||
"karma-cli": "^1.0.1",
|
"karma-cli": "^1.0.1",
|
||||||
"karma-firefox-launcher": "^1.0.1",
|
"karma-firefox-launcher": "^1.0.1",
|
||||||
|
@ -271,13 +271,14 @@
|
||||||
"mocha-jsdom": "^1.1.0",
|
"mocha-jsdom": "^1.1.0",
|
||||||
"mocha-sinon": "^2.0.0",
|
"mocha-sinon": "^2.0.0",
|
||||||
"nock": "^9.0.14",
|
"nock": "^9.0.14",
|
||||||
"node-sass": "^4.7.2",
|
"node-sass": "^4.9.0",
|
||||||
"nsp": "^3.2.1",
|
"nsp": "^3.2.1",
|
||||||
"nyc": "^11.0.3",
|
"nyc": "^13.0.0",
|
||||||
"open": "0.0.5",
|
"open": "0.0.5",
|
||||||
"path": "^0.12.7",
|
"path": "^0.12.7",
|
||||||
"png-file-stream": "^1.0.0",
|
"png-file-stream": "^1.0.0",
|
||||||
"prompt": "^1.0.0",
|
"prompt": "^1.0.0",
|
||||||
|
"proxyquire": "2.0.1",
|
||||||
"qs": "^6.2.0",
|
"qs": "^6.2.0",
|
||||||
"qunitjs": "^2.4.1",
|
"qunitjs": "^2.4.1",
|
||||||
"radgrad-jsdoc-template": "^1.1.3",
|
"radgrad-jsdoc-template": "^1.1.3",
|
||||||
|
@ -295,11 +296,11 @@
|
||||||
"style-loader": "^0.21.0",
|
"style-loader": "^0.21.0",
|
||||||
"stylelint-config-standard": "^18.2.0",
|
"stylelint-config-standard": "^18.2.0",
|
||||||
"tape": "^4.5.1",
|
"tape": "^4.5.1",
|
||||||
"testem": "^2.0.0",
|
"testem": "^2.8.0",
|
||||||
"through2": "^2.0.3",
|
"through2": "^2.0.3",
|
||||||
"vinyl-buffer": "^1.0.1",
|
"vinyl-buffer": "^1.0.1",
|
||||||
"vinyl-source-stream": "^2.0.0",
|
"vinyl-source-stream": "^2.0.0",
|
||||||
"watchify": "^3.9.0"
|
"watchify": "^3.11.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8.0.0"
|
"node": ">=8.0.0"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Karma configuration
|
// Karma configuration
|
||||||
// Generated on Mon Sep 11 2017 18:45:48 GMT-0700 (PDT)
|
// Generated on Mon Sep 11 2017 18:45:48 GMT-0700 (PDT)
|
||||||
|
|
||||||
module.exports = function(config) {
|
module.exports = function (config) {
|
||||||
return {
|
return {
|
||||||
// base path that will be used to resolve all patterns (eg. files, exclude)
|
// base path that will be used to resolve all patterns (eg. files, exclude)
|
||||||
basePath: process.cwd(),
|
basePath: process.cwd(),
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
/*
|
||||||
|
The `piggybankContract` is compiled from:
|
||||||
|
|
||||||
|
pragma solidity ^0.4.0;
|
||||||
|
contract PiggyBank {
|
||||||
|
|
||||||
|
uint private balance;
|
||||||
|
address public owner;
|
||||||
|
|
||||||
|
function PiggyBank() public {
|
||||||
|
owner = msg.sender;
|
||||||
|
balance = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function deposit() public payable returns (uint) {
|
||||||
|
balance += msg.value;
|
||||||
|
return balance;
|
||||||
|
}
|
||||||
|
|
||||||
|
function withdraw(uint withdrawAmount) public returns (uint remainingBal) {
|
||||||
|
require(msg.sender == owner);
|
||||||
|
balance -= withdrawAmount;
|
||||||
|
|
||||||
|
msg.sender.transfer(withdrawAmount);
|
||||||
|
|
||||||
|
return balance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
var piggybankContract = web3.eth.contract([{'constant': false, 'inputs': [{'name': 'withdrawAmount', 'type': 'uint256'}], 'name': 'withdraw', 'outputs': [{'name': 'remainingBal', 'type': 'uint256'}], 'payable': false, 'stateMutability': 'nonpayable', 'type': 'function'}, {'constant': true, 'inputs': [], 'name': 'owner', 'outputs': [{'name': '', 'type': 'address'}], 'payable': false, 'stateMutability': 'view', 'type': 'function'}, {'constant': false, 'inputs': [], 'name': 'deposit', 'outputs': [{'name': '', 'type': 'uint256'}], 'payable': true, 'stateMutability': 'payable', 'type': 'function'}, {'inputs': [], 'payable': false, 'stateMutability': 'nonpayable', 'type': 'constructor'}])
|
||||||
|
const deployButton = document.getElementById('deployButton')
|
||||||
|
const depositButton = document.getElementById('depositButton')
|
||||||
|
const withdrawButton = document.getElementById('withdrawButton')
|
||||||
|
|
||||||
|
deployButton.addEventListener('click', async function (event) {
|
||||||
|
|
||||||
|
var piggybank = await piggybankContract.new(
|
||||||
|
{
|
||||||
|
from: web3.eth.accounts[0],
|
||||||
|
data: '0x608060405234801561001057600080fd5b5033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000808190555061023b806100686000396000f300608060405260043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680632e1a7d4d1461005c5780638da5cb5b1461009d578063d0e30db0146100f4575b600080fd5b34801561006857600080fd5b5061008760048036038101908080359060200190929190505050610112565b6040518082815260200191505060405180910390f35b3480156100a957600080fd5b506100b26101d0565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100fc6101f6565b6040518082815260200191505060405180910390f35b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561017057600080fd5b8160008082825403925050819055503373ffffffffffffffffffffffffffffffffffffffff166108fc839081150290604051600060405180830381858888f193505050501580156101c5573d6000803e3d6000fd5b506000549050919050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60003460008082825401925050819055506000549050905600a165627a7a72305820f237db3ec816a52589d82512117bc85bc08d3537683ffeff9059108caf3e5d400029',
|
||||||
|
gas: '4700000',
|
||||||
|
}, function (e, contract) {
|
||||||
|
console.log(e, contract)
|
||||||
|
if (typeof contract.address !== 'undefined') {
|
||||||
|
console.log('Contract mined! address: ' + contract.address + ' transactionHash: ' + contract.transactionHash)
|
||||||
|
|
||||||
|
console.log(`contract`, contract)
|
||||||
|
|
||||||
|
depositButton.addEventListener('click', function (event) {
|
||||||
|
contract.deposit({ from: web3.eth.accounts[0], value: '0x29a2241af62c0000' }, function (result) {
|
||||||
|
console.log(result)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
withdrawButton.addEventListener('click', function (event) {
|
||||||
|
contract.withdraw('0xde0b6b3a7640000', { from: web3.eth.accounts[0] }, function (result) {
|
||||||
|
console.log(result)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log(piggybank)
|
||||||
|
|
||||||
|
})
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue