23
.babelrc
@ -1,23 +0,0 @@
|
||||
{
|
||||
"presets": [
|
||||
[
|
||||
"env",
|
||||
{
|
||||
"targets": {
|
||||
"browsers": [
|
||||
">0.25%",
|
||||
"not ie 11",
|
||||
"not op_mini all"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"react",
|
||||
"stage-0"
|
||||
],
|
||||
"plugins": [
|
||||
"transform-runtime",
|
||||
"transform-async-to-generator",
|
||||
"transform-class-properties"
|
||||
]
|
||||
}
|
@ -2,48 +2,54 @@ version: 2
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
full_test:
|
||||
test_and_release:
|
||||
jobs:
|
||||
- prep-deps-npm
|
||||
- create_release_pull_request:
|
||||
filters:
|
||||
branches:
|
||||
only:
|
||||
- /^Version-v(\d+)[.](\d+)[.](\d+)/
|
||||
- prep-deps
|
||||
- test-deps
|
||||
- prep-build:
|
||||
requires:
|
||||
- prep-deps-npm
|
||||
- prep-deps
|
||||
- create_github_release:
|
||||
requires:
|
||||
- prep-build
|
||||
filters:
|
||||
branches:
|
||||
only:
|
||||
- develop
|
||||
# - prep-docs:
|
||||
# requires:
|
||||
# - prep-deps-npm
|
||||
# - prep-deps
|
||||
- prep-scss:
|
||||
requires:
|
||||
- prep-deps-npm
|
||||
- prep-deps
|
||||
- test-lint:
|
||||
requires:
|
||||
- prep-deps-npm
|
||||
- test-deps:
|
||||
requires:
|
||||
- prep-deps-npm
|
||||
- prep-deps
|
||||
- test-e2e-chrome:
|
||||
requires:
|
||||
- prep-deps-npm
|
||||
- prep-deps
|
||||
- test-e2e-firefox:
|
||||
requires:
|
||||
- prep-deps-npm
|
||||
# - test-e2e-beta-drizzle:
|
||||
# requires:
|
||||
# - prep-deps-npm
|
||||
# - prep-build
|
||||
- prep-deps
|
||||
- test-unit:
|
||||
requires:
|
||||
- prep-deps-npm
|
||||
- prep-deps
|
||||
- test-mozilla-lint:
|
||||
requires:
|
||||
- prep-deps-npm
|
||||
- prep-deps
|
||||
- prep-build
|
||||
- test-integration-flat-chrome:
|
||||
requires:
|
||||
- prep-deps-npm
|
||||
- prep-deps
|
||||
- prep-scss
|
||||
- test-integration-flat-firefox:
|
||||
requires:
|
||||
- prep-deps-npm
|
||||
- prep-deps
|
||||
- prep-scss
|
||||
- all-tests-pass:
|
||||
requires:
|
||||
@ -52,41 +58,45 @@ workflows:
|
||||
- test-mozilla-lint
|
||||
- test-e2e-chrome
|
||||
- test-e2e-firefox
|
||||
# - test-e2e-beta-drizzle
|
||||
- test-integration-flat-chrome
|
||||
- test-integration-flat-firefox
|
||||
- job-screens:
|
||||
requires:
|
||||
- prep-deps-npm
|
||||
- prep-build
|
||||
- all-tests-pass
|
||||
- job-publish-prerelease:
|
||||
requires:
|
||||
- prep-deps-npm
|
||||
- prep-deps
|
||||
- prep-build
|
||||
- job-screens
|
||||
- all-tests-pass
|
||||
- job-publish-release:
|
||||
filters:
|
||||
branches:
|
||||
only: master
|
||||
requires:
|
||||
- prep-deps-npm
|
||||
- prep-deps
|
||||
- prep-build
|
||||
# - prep-docs
|
||||
- job-screens
|
||||
- all-tests-pass
|
||||
|
||||
jobs:
|
||||
prep-deps-npm:
|
||||
create_release_pull_request:
|
||||
docker:
|
||||
- image: circleci/node:8.15.1-browsers
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Create GitHub Pull Request for version
|
||||
command: |
|
||||
.circleci/scripts/release-bump-changelog-version
|
||||
.circleci/scripts/release-bump-manifest-version
|
||||
.circleci/scripts/release-create-release-pr
|
||||
|
||||
prep-deps:
|
||||
docker:
|
||||
- image: circleci/node:10.16-browsers
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Install deps via npm
|
||||
name: Install deps
|
||||
command: |
|
||||
npm ci
|
||||
yarn --frozen-lockfile
|
||||
- persist_to_workspace:
|
||||
root: .
|
||||
paths:
|
||||
@ -101,7 +111,7 @@ jobs:
|
||||
at: .
|
||||
- run:
|
||||
name: build:dist
|
||||
command: npm run dist
|
||||
command: yarn dist
|
||||
- run:
|
||||
name: build:debug
|
||||
command: find dist/ -type f -exec md5sum {} \; | sort -k 2
|
||||
@ -120,7 +130,7 @@ jobs:
|
||||
at: .
|
||||
- run:
|
||||
name: build:dist
|
||||
command: npm run doc
|
||||
command: yarn doc
|
||||
- persist_to_workspace:
|
||||
root: .
|
||||
paths:
|
||||
@ -139,7 +149,7 @@ jobs:
|
||||
command: find ui/app/css -type f -exec md5sum {} \; | sort -k 2 > scss_checksum
|
||||
- run:
|
||||
name: Build for integration tests
|
||||
command: npm run test:integration:build
|
||||
command: yarn test:integration:build
|
||||
- persist_to_workspace:
|
||||
root: .
|
||||
paths:
|
||||
@ -154,7 +164,7 @@ jobs:
|
||||
at: .
|
||||
- run:
|
||||
name: Test
|
||||
command: npm run lint
|
||||
command: yarn lint
|
||||
|
||||
test-deps:
|
||||
docker:
|
||||
@ -164,22 +174,9 @@ jobs:
|
||||
- attach_workspace:
|
||||
at: .
|
||||
- run:
|
||||
name: npm audit
|
||||
command: .circleci/scripts/npm-audit
|
||||
name: yarn audit
|
||||
command: .circleci/scripts/yarn-audit
|
||||
|
||||
# test-e2e-beta-drizzle:
|
||||
# docker:
|
||||
# - image: circleci/node:8.11.3-browsers
|
||||
# steps:
|
||||
# - checkout
|
||||
# - attach_workspace:
|
||||
# at: .
|
||||
# - run:
|
||||
# name: test:e2e:drizzle:beta
|
||||
# command: npm run test:e2e:drizzle:beta
|
||||
# - store_artifacts:
|
||||
# path: test-artifacts
|
||||
# destination: test-artifacts
|
||||
test-e2e-chrome:
|
||||
docker:
|
||||
- image: circleci/node:10.16-browsers
|
||||
@ -189,7 +186,7 @@ jobs:
|
||||
at: .
|
||||
- run:
|
||||
name: test:e2e:chrome
|
||||
command: npm run build:test && npm run test:e2e:chrome
|
||||
command: yarn build:test && yarn test:e2e:chrome
|
||||
no_output_timeout: 20m
|
||||
- store_artifacts:
|
||||
path: test-artifacts
|
||||
@ -207,27 +204,12 @@ jobs:
|
||||
at: .
|
||||
- run:
|
||||
name: test:e2e:firefox
|
||||
command: npm run build:test && npm run test:e2e:chrome
|
||||
command: yarn build:test && yarn test:e2e:chrome
|
||||
no_output_timeout: 20m
|
||||
- store_artifacts:
|
||||
path: test-artifacts
|
||||
destination: test-artifacts
|
||||
|
||||
job-screens:
|
||||
docker:
|
||||
- image: circleci/node:10.16-browsers
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: .
|
||||
- run:
|
||||
name: Test
|
||||
command: npm run test:screens
|
||||
- persist_to_workspace:
|
||||
root: .
|
||||
paths:
|
||||
- test-artifacts
|
||||
|
||||
job-publish-prerelease:
|
||||
docker:
|
||||
- image: circleci/node:10.16-browsers
|
||||
@ -257,13 +239,13 @@ jobs:
|
||||
at: .
|
||||
- run:
|
||||
name: sentry sourcemaps upload
|
||||
command: npm run sentry:publish
|
||||
command: yarn sentry:publish
|
||||
# - run:
|
||||
# name: github gh-pages docs publish
|
||||
# command: >
|
||||
# git config --global user.name "metamaskbot" &&
|
||||
# git config --global user.email "admin@metamask.io" &&
|
||||
# npm run publish-docs
|
||||
# yarn publish-docs
|
||||
|
||||
test-unit:
|
||||
docker:
|
||||
@ -274,7 +256,7 @@ jobs:
|
||||
at: .
|
||||
- run:
|
||||
name: test:coverage
|
||||
command: npm run test:coverage
|
||||
command: yarn test:coverage
|
||||
test-mozilla-lint:
|
||||
docker:
|
||||
- image: circleci/node:10.16-browsers
|
||||
@ -284,7 +266,7 @@ jobs:
|
||||
at: .
|
||||
- run:
|
||||
name: test:mozilla-lint
|
||||
command: npm run mozilla-lint
|
||||
command: NODE_OPTIONS=--max_old_space_size=3072 yarn mozilla-lint
|
||||
|
||||
test-integration-flat-firefox:
|
||||
docker:
|
||||
@ -298,7 +280,7 @@ jobs:
|
||||
command: ./.circleci/scripts/firefox-install
|
||||
- run:
|
||||
name: test:integration:flat
|
||||
command: npm run test:flat
|
||||
command: yarn test:flat
|
||||
|
||||
test-integration-flat-chrome:
|
||||
environment:
|
||||
@ -311,7 +293,7 @@ jobs:
|
||||
at: .
|
||||
- run:
|
||||
name: test:integration:flat
|
||||
command: npm run test:flat
|
||||
command: yarn test:flat
|
||||
|
||||
all-tests-pass:
|
||||
docker:
|
||||
@ -320,3 +302,15 @@ jobs:
|
||||
- run:
|
||||
name: All Tests Passed
|
||||
command: echo 'weew - everything passed!'
|
||||
|
||||
create_github_release:
|
||||
docker:
|
||||
- image: circleci/node:8.15.1-browsers
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: .
|
||||
- run:
|
||||
name: Create GitHub release
|
||||
command: |
|
||||
.circleci/scripts/release-create-gh-release
|
||||
|
@ -4,7 +4,7 @@ set -e
|
||||
set -u
|
||||
set -o pipefail
|
||||
|
||||
FIREFOX_VERSION='62.0'
|
||||
FIREFOX_VERSION='68.0'
|
||||
FIREFOX_BINARY="firefox-${FIREFOX_VERSION}.tar.bz2"
|
||||
FIREFOX_BINARY_URL="https://ftp.mozilla.org/pub/firefox/releases/${FIREFOX_VERSION}/linux-x86_64/en-US/${FIREFOX_BINARY}"
|
||||
FIREFOX_PATH='/opt/firefox'
|
||||
|
@ -1,12 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
set -u
|
||||
set -o pipefail
|
||||
|
||||
if ! npm audit
|
||||
then
|
||||
! npm audit --json > audit.json
|
||||
printf '%s\n' ''
|
||||
node .circleci/scripts/npm-audit-check.js
|
||||
fi
|
@ -1,24 +0,0 @@
|
||||
const path = require('path')
|
||||
const audit = require(path.join(__dirname, '..', '..', 'audit.json'))
|
||||
const error = audit.error
|
||||
const advisories = Object.keys(audit.advisories || []).map((k) => audit.advisories[k])
|
||||
|
||||
if (error) {
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
let count = 0
|
||||
for (const advisory of advisories) {
|
||||
if (advisory.severity === 'low') {
|
||||
continue
|
||||
}
|
||||
|
||||
count += advisory.findings.some((finding) => (!finding.dev && !finding.optional))
|
||||
}
|
||||
|
||||
if (count > 0) {
|
||||
console.log(`Audit shows ${count} moderate or high severity advisories _in the production dependencies_`)
|
||||
process.exit(1)
|
||||
} else {
|
||||
console.log(`Audit shows _zero_ moderate or high severity advisories _in the production dependencies_`)
|
||||
}
|
44
.circleci/scripts/release-bump-changelog-version
Executable file
@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
set -u
|
||||
set -o pipefail
|
||||
|
||||
if [[ "${CI:-}" != 'true' ]]
|
||||
then
|
||||
printf '%s\n' 'CI environment variable must be set to true'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "${CIRCLECI:-}" != 'true' ]]
|
||||
then
|
||||
printf '%s\n' 'CIRCLECI environment variable must be set to true'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
version="${CIRCLE_BRANCH/Version-v/}"
|
||||
|
||||
if ! grep --quiet --fixed-strings "$version" CHANGELOG.md
|
||||
then
|
||||
printf '%s\n' 'Adding this release to CHANGELOG.md'
|
||||
date_str="$(date '+%a %b %d %Y')"
|
||||
cp CHANGELOG.md{,.bak}
|
||||
|
||||
update_headers=$(cat <<END
|
||||
/## Current Develop Branch/ {
|
||||
print "## Current Develop Branch\n";
|
||||
print "## ${version} ${date_str}";
|
||||
next;
|
||||
}
|
||||
{
|
||||
print;
|
||||
}
|
||||
END
|
||||
)
|
||||
|
||||
awk "$update_headers" CHANGELOG.md.bak > CHANGELOG.md
|
||||
rm CHANGELOG.md.bak
|
||||
else
|
||||
printf '%s\n' "CHANGELOG.md already includes a header for ${version}"
|
||||
exit 0
|
||||
fi
|
38
.circleci/scripts/release-bump-manifest-version
Executable file
@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
set -u
|
||||
set -o pipefail
|
||||
|
||||
if [[ "${CI:-}" != 'true' ]]
|
||||
then
|
||||
printf '%s\n' 'CI environment variable must be set to true'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "${CIRCLECI:-}" != 'true' ]]
|
||||
then
|
||||
printf '%s\n' 'CIRCLECI environment variable must be set to true'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf '%s\n' 'Updating the manifest version if needed'
|
||||
|
||||
version="${CIRCLE_BRANCH/Version-v/}"
|
||||
updated_manifest="$(jq ".version = \"$version\"" app/manifest.json)"
|
||||
printf '%s\n' "$updated_manifest" > app/manifest.json
|
||||
|
||||
if [[ -z $(git status --porcelain) ]]
|
||||
then
|
||||
printf '%s\n' 'App manifest version already set'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
git \
|
||||
-c user.name='MetaMask Bot' \
|
||||
-c user.email='metamaskbot@users.noreply.github.com' \
|
||||
commit --message "${CIRCLE_BRANCH/-/ }" \
|
||||
CHANGELOG.md app/manifest.json
|
||||
|
||||
repo_slug="$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME"
|
||||
git push "https://$GITHUB_TOKEN_USER:$GITHUB_TOKEN@github.com/$repo_slug" "$CIRCLE_BRANCH"
|
51
.circleci/scripts/release-create-gh-release
Executable file
@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -x
|
||||
set -e
|
||||
set -u
|
||||
set -o pipefail
|
||||
|
||||
if [[ "${CI:-}" != 'true' ]]
|
||||
then
|
||||
printf '%s\n' 'CI environment variable must be set to true'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "${CIRCLECI:-}" != 'true' ]]
|
||||
then
|
||||
printf '%s\n' 'CIRCLECI environment variable must be set to true'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
function install_github_cli ()
|
||||
{
|
||||
printf '%s\n' 'Installing hub CLI'
|
||||
pushd "$(mktemp -d)"
|
||||
curl -sSL 'https://github.com/github/hub/releases/download/v2.11.2/hub-linux-amd64-2.11.2.tgz' | tar xz
|
||||
PATH="$PATH:$PWD/hub-linux-amd64-2.11.2/bin"
|
||||
popd
|
||||
}
|
||||
|
||||
current_commit_msg=$(git show -s --format='%s' HEAD)
|
||||
|
||||
if grep --quiet '^Version v' <<< "$current_commit_msg"
|
||||
then
|
||||
install_github_cli
|
||||
|
||||
printf '%s\n' 'Creating GitHub Release'
|
||||
read -ra commit_words <<< "$current_commit_msg"
|
||||
tag="${commit_words[1]}"
|
||||
release_body="$(awk -v version="${tag##v}" -f .circleci/scripts/show-changelog.awk CHANGELOG.md)"
|
||||
pushd builds
|
||||
hub release create \
|
||||
--attach metamask-chrome-*.zip \
|
||||
--attach metamask-firefox-*.zip \
|
||||
--message "${commit_words[0]} ${commit_words[1]#v}" \
|
||||
--message "$release_body" \
|
||||
--commitish "$CIRCLE_SHA1" \
|
||||
"$tag"
|
||||
popd
|
||||
else
|
||||
printf '%s\n' 'Skipping GitHub Release'
|
||||
exit 0
|
||||
fi
|
54
.circleci/scripts/release-create-release-pr
Executable file
@ -0,0 +1,54 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
set -u
|
||||
set -o pipefail
|
||||
|
||||
if [[ "${CI:-}" != 'true' ]]
|
||||
then
|
||||
printf '%s\n' 'CI environment variable must be set to true'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "${CIRCLECI:-}" != 'true' ]]
|
||||
then
|
||||
printf '%s\n' 'CIRCLECI environment variable must be set to true'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z "${GITHUB_TOKEN:-}" ]]
|
||||
then
|
||||
printf '%s\n' 'GITHUB_TOKEN environment variable must be set'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
function install_github_cli ()
|
||||
{
|
||||
printf '%s\n' 'Installing hub CLI'
|
||||
pushd "$(mktemp -d)"
|
||||
curl -sSL 'https://github.com/github/hub/releases/download/v2.11.2/hub-linux-amd64-2.11.2.tgz' | tar xz
|
||||
PATH="$PATH:$PWD/hub-linux-amd64-2.11.2/bin"
|
||||
popd
|
||||
}
|
||||
|
||||
version="${CIRCLE_BRANCH/Version-v/}"
|
||||
base_branch='develop'
|
||||
|
||||
if [[ -n "${CI_PULL_REQUEST:-}" ]]
|
||||
then
|
||||
printf '%s\n' 'CI_PULL_REQUEST is set, pull request already exists for this build'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
install_github_cli
|
||||
|
||||
printf '%s\n' "Creating a Pull Request for $version on GitHub"
|
||||
|
||||
if ! hub pull-request \
|
||||
--reviewer '@MetaMask/extension-release-team' \
|
||||
--message "${CIRCLE_BRANCH/-/ } RC" --message ':package: :rocket:' \
|
||||
--base "$CIRCLE_PROJECT_USERNAME:$base_branch" \
|
||||
--head "$CIRCLE_PROJECT_USERNAME:$CIRCLE_BRANCH";
|
||||
then
|
||||
printf '%s\n' 'Pull Request already exists'
|
||||
fi
|
52
.circleci/scripts/show-changelog.awk
Normal file
@ -0,0 +1,52 @@
|
||||
# DESCRIPTION
|
||||
#
|
||||
# This script will print out all of the CHANGELOG.md lines for a given version
|
||||
# with the assumption that the CHANGELOG.md files looks something along the
|
||||
# lines of:
|
||||
#
|
||||
# ```
|
||||
# ## 6.6.2 Fri Jun 07 2019
|
||||
#
|
||||
# - [#6690](https://github.com/MetaMask/metamask-extension/pull/6690): Some words
|
||||
# - [#6700](https://github.com/MetaMask/metamask-extension/pull/6700): some more words
|
||||
#
|
||||
# ## 6.6.1 Thu Jun 06 2019
|
||||
#
|
||||
# - [#6691](https://github.com/MetaMask/metamask-extension/pull/6691): Revert other words
|
||||
#
|
||||
# ## 6.6.0 Mon Jun 03 2019
|
||||
#
|
||||
# - [#6659](https://github.com/MetaMask/metamask-extension/pull/6659): foo
|
||||
# - [#6671](https://github.com/MetaMask/metamask-extension/pull/6671): bar
|
||||
# - [#6625](https://github.com/MetaMask/metamask-extension/pull/6625): baz
|
||||
# - [#6633](https://github.com/MetaMask/metamask-extension/pull/6633): Many many words
|
||||
#
|
||||
#
|
||||
# ```
|
||||
#
|
||||
# EXAMPLE
|
||||
#
|
||||
# Run this script like so, passing in the version:
|
||||
#
|
||||
# ```
|
||||
# awk -v version='6.6.0' -f .circleci/scripts/show-changelog.awk CHANGELOG.md
|
||||
# ```
|
||||
#
|
||||
|
||||
BEGIN {
|
||||
inside_section = 0;
|
||||
}
|
||||
|
||||
$1 == "##" && $2 == version {
|
||||
inside_section = 1;
|
||||
next;
|
||||
}
|
||||
|
||||
$1 == "##" && $2 != version {
|
||||
inside_section = 0;
|
||||
next;
|
||||
}
|
||||
|
||||
inside_section && !/^$/ {
|
||||
print $0;
|
||||
}
|
20
.circleci/scripts/yarn-audit
Executable file
@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -u
|
||||
set -o pipefail
|
||||
|
||||
yarn audit --level moderate --groups dependencies
|
||||
audit_status="$?"
|
||||
|
||||
# Use a bitmask to ignore INFO and LOW severity audit results
|
||||
# See here: https://yarnpkg.com/lang/en/docs/cli/audit/
|
||||
audit_status="$(( audit_status & 11100 ))"
|
||||
|
||||
if [[ "$audit_status" != 0 ]]
|
||||
then
|
||||
count="$(yarn audit --level moderate --groups dependencies --json | tail -1 | jq '.data.vulnerabilities.moderate + .data.vulnerabilities.high + .data.vulnerabilities.critical')"
|
||||
printf "Audit shows %s moderate or high severity advisories _in the production dependencies_\n" "$count"
|
||||
exit 1
|
||||
else
|
||||
printf "Audit shows _zero_ moderate or high severity advisories _in the production dependencies_\n"
|
||||
fi
|
@ -17,3 +17,5 @@ ui/lib/blockies.js
|
||||
test/integration/bundle.js
|
||||
test/integration/jquery-3.1.0.min.js
|
||||
test/integration/helpers.js
|
||||
|
||||
package-lock.json
|
||||
|
@ -59,7 +59,7 @@
|
||||
"eqeqeq": [2, "allow-null"],
|
||||
"generator-star-spacing": [2, { "before": true, "after": true }],
|
||||
"handle-callback-err": [2, "^(err|error)$" ],
|
||||
"indent": "off",
|
||||
"indent": [2, 2,{ "SwitchCase": 1 }],
|
||||
"jsx-quotes": [2, "prefer-double"],
|
||||
"key-spacing": 2,
|
||||
"keyword-spacing": [2, { "before": true, "after": true }],
|
||||
@ -133,7 +133,9 @@
|
||||
"no-unneeded-ternary": [2, { "defaultAssignment": false }],
|
||||
"no-unreachable": 2,
|
||||
"no-unsafe-finally": 2,
|
||||
"no-unused-expressions": ["error", { "allowShortCircuit" : true, "allowTernary": true }],
|
||||
"no-unused-vars": [2, { "vars": "all", "args": "all", "argsIgnorePattern": "[_]+" }],
|
||||
"no-use-before-define": [2, { "functions": false }],
|
||||
"no-useless-call": 2,
|
||||
"no-useless-computed-key": 2,
|
||||
"no-useless-constructor": 2,
|
||||
|
7
.gitattributes
vendored
@ -1 +1,8 @@
|
||||
CHANGELOG.md merge=union
|
||||
|
||||
# Reviewing the lockfile contents is an important step in verifying that
|
||||
# we're using the dependencies we expect to be using
|
||||
package-lock.json linguist-generated=false
|
||||
yarn.lock linguist-generated=false
|
||||
|
||||
test/e2e/send-eth-with-private-key-test/ethereumjs-tx.js linguist-vendored linguist-generated
|
||||
|
5
.github/CODEOWNERS
vendored
@ -1,7 +1,8 @@
|
||||
# Lines starting with '#' are comments.
|
||||
# Each line is a file pattern followed by one or more owners.
|
||||
|
||||
package*.json @whymarrh
|
||||
ui/ @danjm @whymarrh
|
||||
package.json @danjm @whymarrh @Gudahtt
|
||||
yarn.lock @danjm @whymarrh @Gudahtt
|
||||
ui/ @danjm @whymarrh @Gudahtt
|
||||
app/scripts/controllers/transactions @frankiebee
|
||||
|
||||
|
8
.gitignore
vendored
@ -1,6 +1,9 @@
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
node_modules
|
||||
yarn.lock
|
||||
package-lock.json
|
||||
|
||||
audit.json
|
||||
|
||||
app/bower_components
|
||||
test/bower_components
|
||||
@ -11,6 +14,8 @@ package
|
||||
.vscode
|
||||
.sublime-project
|
||||
|
||||
*.bak
|
||||
|
||||
# VIM
|
||||
*.swp
|
||||
*.swo
|
||||
@ -24,7 +29,6 @@ app/.DS_Store
|
||||
coverage/
|
||||
dist
|
||||
builds/
|
||||
disc/
|
||||
builds.zip
|
||||
docs/jsdocs
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
# Storybook
|
||||
We're currently using [Storybook](https://storybook.js.org/) as part of our design system. To run Storybook and test some of our UI components, clone the repo and run the following:
|
||||
```
|
||||
npm install
|
||||
npm run storybook
|
||||
yarn
|
||||
yarn storybook
|
||||
```
|
||||
You should then see:
|
||||
> info Storybook started on => http://localhost:6006/
|
||||
|
24
CHANGELOG.md
@ -2,20 +2,29 @@
|
||||
|
||||
## Current Develop Branch
|
||||
|
||||
## 7.0.0 Fri Aug 02 2019
|
||||
- [#6828](https://github.com/MetaMask/metamask-extension/pull/6828): Capitalized speed up label to match rest of UI
|
||||
- [#6874](https://github.com/MetaMask/metamask-extension/pull/6928): Allows skipping of seed phrase challenge during onboarding, and completing it at a later time
|
||||
- [#6900](https://github.com/MetaMask/metamask-extension/pull/6900): Prevent opening of asset dropdown if no tokens in account
|
||||
- [#6904](https://github.com/MetaMask/metamask-extension/pull/6904): Set privacy mode as default
|
||||
- [#6914](https://github.com/MetaMask/metamask-extension/pull/6914): Adds Address Book feature
|
||||
- [#6928](https://github.com/MetaMask/metamask-extension/pull/6928): Disable Copy Tx ID and block explorer link for transactions without hash
|
||||
- [#6967](https://github.com/MetaMask/metamask-extension/pull/6967): Fix mobile sync
|
||||
|
||||
## 6.7.3 Thu Jul 18 2019
|
||||
|
||||
- [#6888](https://github.com/MetaMask/metamask-extension/pull/6888): Fix bug with resubmitting unsigned transactions.
|
||||
|
||||
## 6.7.2 Mon Jul 01 2019
|
||||
|
||||
- [#6713](https://github.com/MetaMask/metamask-extension/pull/6713): * Normalize and Validate txParams in TransactionStateManager.addTx too
|
||||
- [#6713](https://github.com/MetaMask/metamask-extension/pull/6713): * Normalize and Validate txParams in TransactionStateManager.addTx too
|
||||
- [#6759](https://github.com/MetaMask/metamask-extension/pull/6759): Update to Node.js v10
|
||||
- [#Fixes #6694
](https://github.com/MetaMask/metamask-extension/pull/Fixes #6694
): Fixes #6694
|
||||
- [#6743](https://github.com/MetaMask/metamask-extension/pull/6743): * Add tests for ImportWithSeedPhrase#parseSeedPhrase
|
||||
- [#Fixes #6740](https://github.com/MetaMask/metamask-extension/pull/Fixes #6740): Fixes #6740
|
||||
- [#Fixes #6741](https://github.com/MetaMask/metamask-extension/pull/Fixes #6741): Fixes #6741
|
||||
- [#6694](https://github.com/MetaMask/metamask-extension/pull/6694): Fixes #6694
|
||||
- [#6743](https://github.com/MetaMask/metamask-extension/pull/6743): * Add tests for ImportWithSeedPhrase#parseSeedPhrase
|
||||
- [#6740](https://github.com/MetaMask/metamask-extension/pull/6740): Fixes #6740
|
||||
- [#6741](https://github.com/MetaMask/metamask-extension/pull/6741): Fixes #6741
|
||||
- [#6761](https://github.com/MetaMask/metamask-extension/pull/6761): Fixes #6760, correct PropTypes for nextRoute
|
||||
- [#6754](https://github.com/MetaMask/metamask-extension/pull/6754): There is currently a bug in chrome that prevents reading source maps
|
||||
- [#6754](https://github.com/MetaMask/metamask-extension/pull/6754): Use inline source maps in development
|
||||
- [#6589](https://github.com/MetaMask/metamask-extension/pull/6589): Document hotfix protocol
|
||||
- [#6738](https://github.com/MetaMask/metamask-extension/pull/6738): Add codeowner for package-lock-old.json package-lock.json package.json packagelock-old.json files
|
||||
- [#6648](https://github.com/MetaMask/metamask-extension/pull/6648): Add loading view to notification.html
|
||||
@ -28,11 +37,12 @@
|
||||
|
||||
- [#6623](https://github.com/MetaMask/metamask-extension/pull/6623): Improve contract method data fetching (#6623)
|
||||
- [#6551](https://github.com/MetaMask/metamask-extension/pull/6551): Adds 4byte registry fallback to getMethodData() (#6435)
|
||||
- [#6718, #6650](https://github.com/MetaMask/metamask-extension/pull/6718, #6650): Add delete to custom RPC form
|
||||
- [#6718](https://github.com/MetaMask/metamask-extension/pull/6718): Add delete to custom RPC form
|
||||
- [#6700](https://github.com/MetaMask/metamask-extension/pull/6700): Fix styles on 'import account' page, update help link
|
||||
- [#6714](https://github.com/MetaMask/metamask-extension/pull/6714): Wrap smaller custom block explorer url text
|
||||
- [#6706](https://github.com/MetaMask/metamask-extension/pull/6706): Pin ethereumjs-tx
|
||||
- [#6700](https://github.com/MetaMask/metamask-extension/pull/6700): Fix styles on 'import account' page, update help link
|
||||
- [#6775](https://github.com/MetaMask/metamask-extension/pull/6775): Started adding visual documentation of MetaMask plugin components with the account menu component first
|
||||
|
||||
## 6.6.2 Fri Jun 07 2019
|
||||
|
||||
|
@ -22,30 +22,3 @@ When you're done with your project / bugfix / feature and ready to submit a PR,
|
||||
- [ ] **Get reviewed by a core contributor**: Make sure you get a `:thumbsup`, `:+1`, or `LGTM` from a user with a `Member` badge before merging.
|
||||
|
||||
And that's it! Thanks for helping out.
|
||||
|
||||
### Developing inside a node_modules folder
|
||||
|
||||
First make sure you are comfortable with [how require works](https://github.com/maxogden/art-of-node#how-require-works) in node.
|
||||
|
||||
We recommend creating a folder somewhere manually called `node_modules`. For example in `~/code/node_modules`. Clone all of your git copies of modules that you want to work on into here, so for example:
|
||||
|
||||
- `~/code/node_modules/dat`
|
||||
- `~/code/node_modules/hyperdrive`
|
||||
|
||||
When you run `npm install` inside of `~/code/node_modules/dat`, dat will get its own copy of `hyperdrive` (one if its dependencies) inside `~/code/node_modules/dat/node_modules`. However, if you encounter a bug in hyperdrive that you need to fix, but you want to test your fix in dat, you want dat to use your git copy of hyperdrive at `~/code/node_modules/hyperdrive` and not the npm copy of hyperdrive at `~/code/node_modules/dat/node_modules/hyperdrive`.
|
||||
|
||||
How do you get dat to use the git copy of hyperdrive? Just delete the npm copy!
|
||||
|
||||
```
|
||||
rm -rf ~/code/node_modules/dat/node_modules/hyperdrive
|
||||
```
|
||||
|
||||
Now when you run dat, and it tries to `require('hyperdrive')` it first looks in its own `node_modules` folder at `~/code/node_modules/dat/node_modules` but doesnt find hyperdrive. So it goes up to `~/code/node_modules` and finds `hyperdrive` there and uses that one, your git copy.
|
||||
|
||||
If you want to switch back to an npm copy, just run `npm install` inside `~/code/node_modules/dat/` and npm will download any missing modules into `~/code/node_modules/dat/node_modules` but wont touch anything in `~/code/node_modules`.
|
||||
|
||||
This might seem a bit complicated at first, but is simple once you get the hang of it. Here are some rules to help you get started:
|
||||
|
||||
- Never make any meaningful edits to code inside an "npm-managed" node_modules folder (such as `~/code/node_modules/dat/node_modules`), because when you run `npm install` inside those folders it could inadvertently delete all of your edits when installing an updated copy of a module. This has happened to me many times, so I just always use my git copy and delete the npm copy (as described above) to make edits to a module.
|
||||
- You should never need to run any npm commands in terminal when at your "manually managed"" node_modules folder at `~/code/node_modules`. Never running npm commands at that folder also prevents npm from accidentally erasing your git copies of modules
|
||||
- The location of your "manually managed" node_modules folder should be somewhere isolated from your normal require path. E.g. if you put it at `~/node_modules`, then when you run `npm install dat` at `~/Desktop` npm might decide to erase your git copy of dat at `~/node_modules/dat` and replace it with a copy from npm, which could make you lose work. Putting your manually managed `node_modules` folder in a sub-folder like `~/code` gets it "out of the way" and prevents accidents like that from happening.
|
||||
|
36
README.md
@ -3,6 +3,8 @@
|
||||
|
||||
You can find the latest version of MetaMask on [our official website](https://metamask.io/). For help using MetaMask, visit our [User Support Site](https://metamask.zendesk.com/hc/en-us).
|
||||
|
||||
MetaMask supports Firefox, Google Chrome, and Chromium-based browsers. We recommend using the latest available browser version.
|
||||
|
||||
For up to the minute news, follow our [Twitter](https://twitter.com/metamask_io) or [Medium](https://medium.com/metamask) pages.
|
||||
|
||||
To learn how to develop MetaMask-compatible applications, visit our [Developer Docs](https://metamask.github.io/metamask-docs/).
|
||||
@ -11,14 +13,17 @@ To learn how to contribute to the MetaMask project itself, visit our [Internal D
|
||||
|
||||
## Building locally
|
||||
|
||||
- Install [Node.js](https://nodejs.org) version 10 and the latest available npm@6
|
||||
- Install [Node.js](https://nodejs.org) version 10
|
||||
- If you are using [nvm](https://github.com/creationix/nvm#installation) (recommended) running `nvm use` will automatically choose the right node version for you.
|
||||
- If you install Node.js manually, ensure you're using npm@6
|
||||
- Install npm@6 using `npm install -g npm@6`
|
||||
- Install dependencies: `npm ci`
|
||||
- If you have issues with node-sass compilation, try `npm rebuild node-sass`
|
||||
- Build the project to the `./dist/` folder with `npm run dist`.
|
||||
- Optionally, to start a development build (e.g. with logging and file watching) run `npm start` instead.
|
||||
- Install [Yarn](https://yarnpkg.com/en/docs/install)
|
||||
- Install dependencies: `yarn`
|
||||
- Build the project to the `./dist/` folder with `yarn dist`.
|
||||
- Optionally, to start a development build (e.g. with logging and file watching) run `yarn start` instead.
|
||||
- To start the [React DevTools](https://github.com/facebook/react-devtools) and [Redux DevTools Extension](http://extension.remotedev.io)
|
||||
alongside the app, use `yarn start:dev`.
|
||||
- React DevTools will open in a separate window; no browser extension is required
|
||||
- Redux DevTools will need to be installed as a browser extension. Open the Redux Remote Devtools to access Redux state logs. This can be done by either right clicking within the web browser to bring up the context menu, expanding the Redux DevTools panel and clicking Open Remote DevTools OR clicking the Redux DevTools extension icon and clicking Open Remote DevTools.
|
||||
- You will also need to check the "Use custom (local) server" checkbox in the Remote DevTools Settings, using the default server configuration (host `localhost`, port `8000`, secure connection checkbox unchecked)
|
||||
|
||||
Uncompressed builds can be found in `/dist`, compressed builds can be found in `/builds` once they're built.
|
||||
|
||||
@ -26,15 +31,15 @@ Uncompressed builds can be found in `/dist`, compressed builds can be found in `
|
||||
|
||||
You can read [our internal docs here](https://metamask.github.io/metamask-extension/).
|
||||
|
||||
You can re-generate the docs locally by running `npm run doc`, and contributors can update the hosted docs by running `npm run publish-docs`.
|
||||
You can re-generate the docs locally by running `yarn doc`, and contributors can update the hosted docs by running `yarn publish-docs`.
|
||||
|
||||
### Running Tests
|
||||
|
||||
Run tests with `npm test`.
|
||||
Run tests with `yarn test`.
|
||||
|
||||
You can also test with a continuously watching process, via `npm run watch`.
|
||||
You can also test with a continuously watching process, via `yarn watch`.
|
||||
|
||||
You can run the linter by itself with `npm run lint`.
|
||||
You can run the linter by itself with `yarn lint`.
|
||||
|
||||
## Architecture
|
||||
|
||||
@ -43,14 +48,14 @@ You can run the linter by itself with `npm run lint`.
|
||||
## Development
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npm start
|
||||
yarn
|
||||
yarn start
|
||||
```
|
||||
|
||||
## Build for Publishing
|
||||
|
||||
```bash
|
||||
npm run dist
|
||||
yarn dist
|
||||
```
|
||||
|
||||
#### Writing Browser Tests
|
||||
@ -66,9 +71,8 @@ To write tests that will be run in the browser using QUnit, add your test files
|
||||
- [The MetaMask Team](./docs/team.md)
|
||||
- [How to live reload on local dependency changes](./docs/developing-on-deps.md)
|
||||
- [How to add new networks to the Provider Menu](./docs/adding-new-networks.md)
|
||||
- [How to manage notices that appear when the app starts up](./docs/notices.md)
|
||||
- [How to port MetaMask to a new platform](./docs/porting_to_new_environment.md)
|
||||
- [How to use the TREZOR emulator](./docs/trezor-emulator.md)
|
||||
- [How to generate a visualization of this repository's development](./docs/development-visualization.md)
|
||||
|
||||
[1]: http://www.nomnoml.com/#view/%5B%3Cactor%3Euser%5D%0A%0A%5Bmetamask-ui%7C%0A%20%20%20%5Btools%7C%0A%20%20%20%20%20react%0A%20%20%20%20%20redux%0A%20%20%20%20%20thunk%0A%20%20%20%20%20ethUtils%0A%20%20%20%20%20jazzicon%0A%20%20%20%5D%0A%20%20%20%5Bcomponents%7C%0A%20%20%20%20%20app%0A%20%20%20%20%20account-detail%0A%20%20%20%20%20accounts%0A%20%20%20%20%20locked-screen%0A%20%20%20%20%20restore-vault%0A%20%20%20%20%20identicon%0A%20%20%20%20%20config%0A%20%20%20%20%20info%0A%20%20%20%5D%0A%20%20%20%5Breducers%7C%0A%20%20%20%20%20app%0A%20%20%20%20%20metamask%0A%20%20%20%20%20identities%0A%20%20%20%5D%0A%20%20%20%5Bactions%7C%0A%20%20%20%20%20%5BaccountManager%5D%0A%20%20%20%5D%0A%20%20%20%5Bcomponents%5D%3A-%3E%5Bactions%5D%0A%20%20%20%5Bactions%5D%3A-%3E%5Breducers%5D%0A%20%20%20%5Breducers%5D%3A-%3E%5Bcomponents%5D%0A%5D%0A%0A%5Bweb%20dapp%7C%0A%20%20%5Bui%20code%5D%0A%20%20%5Bweb3%5D%0A%20%20%5Bmetamask-inpage%5D%0A%20%20%0A%20%20%5B%3Cactor%3Eui%20developer%5D%0A%20%20%5Bui%20developer%5D-%3E%5Bui%20code%5D%0A%20%20%5Bui%20code%5D%3C-%3E%5Bweb3%5D%0A%20%20%5Bweb3%5D%3C-%3E%5Bmetamask-inpage%5D%0A%5D%0A%0A%5Bmetamask-background%7C%0A%20%20%5Bprovider-engine%5D%0A%20%20%5Bhooked%20wallet%20subprovider%5D%0A%20%20%5Bid%20store%5D%0A%20%20%0A%20%20%5Bprovider-engine%5D%3C-%3E%5Bhooked%20wallet%20subprovider%5D%0A%20%20%5Bhooked%20wallet%20subprovider%5D%3C-%3E%5Bid%20store%5D%0A%20%20%5Bconfig%20manager%7C%0A%20%20%20%20%5Brpc%20configuration%5D%0A%20%20%20%20%5Bencrypted%20keys%5D%0A%20%20%20%20%5Bwallet%20nicknames%5D%0A%20%20%5D%0A%20%20%0A%20%20%5Bprovider-engine%5D%3C-%5Bconfig%20manager%5D%0A%20%20%5Bid%20store%5D%3C-%3E%5Bconfig%20manager%5D%0A%5D%0A%0A%5Buser%5D%3C-%3E%5Bmetamask-ui%5D%0A%0A%5Buser%5D%3C%3A--%3A%3E%5Bweb%20dapp%5D%0A%0A%5Bmetamask-contentscript%7C%0A%20%20%5Bplugin%20restart%20detector%5D%0A%20%20%5Brpc%20passthrough%5D%0A%5D%0A%0A%5Brpc%20%7C%0A%20%20%5Bethereum%20blockchain%20%7C%0A%20%20%20%20%5Bcontracts%5D%0A%20%20%20%20%5Baccounts%5D%0A%20%20%5D%0A%5D%0A%0A%5Bweb%20dapp%5D%3C%3A--%3A%3E%5Bmetamask-contentscript%5D%0A%5Bmetamask-contentscript%5D%3C-%3E%5Bmetamask-background%5D%0A%5Bmetamask-background%5D%3C-%3E%5Bmetamask-ui%5D%0A%5Bmetamask-background%5D%3C-%3E%5Brpc%5D%0A
|
||||
[1]: http://www.nomnoml.com/#view/%5B%3Cactor%3Euser%5D%0A%0A%5Bmetamask-ui%7C%0A%20%20%20%5Btools%7C%0A%20%20%20%20%20react%0A%20%20%20%20%20redux%0A%20%20%20%20%20thunk%0A%20%20%20%20%20ethUtils%0A%20%20%20%20%20jazzicon%0A%20%20%20%5D%0A%20%20%20%5Bcomponents%7C%0A%20%20%20%20%20app%0A%20%20%20%20%20account-detail%0A%20%20%20%20%20accounts%0A%20%20%20%20%20locked-screen%0A%20%20%20%20%20restore-vault%0A%20%20%20%20%20identicon%0A%20%20%20%20%20config%0A%20%20%20%20%20info%0A%20%20%20%5D%0A%20%20%20%5Breducers%7C%0A%20%20%20%20%20app%0A%20%20%20%20%20metamask%0A%20%20%20%20%20identities%0A%20%20%20%5D%0A%20%20%20%5Bactions%7C%0A%20%20%20%20%20%5BbackgroundConnection%5D%0A%20%20%20%5D%0A%20%20%20%5Bcomponents%5D%3A-%3E%5Bactions%5D%0A%20%20%20%5Bactions%5D%3A-%3E%5Breducers%5D%0A%20%20%20%5Breducers%5D%3A-%3E%5Bcomponents%5D%0A%5D%0A%0A%5Bweb%20dapp%7C%0A%20%20%5Bui%20code%5D%0A%20%20%5Bweb3%5D%0A%20%20%5Bmetamask-inpage%5D%0A%20%20%0A%20%20%5B%3Cactor%3Eui%20developer%5D%0A%20%20%5Bui%20developer%5D-%3E%5Bui%20code%5D%0A%20%20%5Bui%20code%5D%3C-%3E%5Bweb3%5D%0A%20%20%5Bweb3%5D%3C-%3E%5Bmetamask-inpage%5D%0A%5D%0A%0A%5Bmetamask-background%7C%0A%20%20%5Bprovider-engine%5D%0A%20%20%5Bhooked%20wallet%20subprovider%5D%0A%20%20%5Bid%20store%5D%0A%20%20%0A%20%20%5Bprovider-engine%5D%3C-%3E%5Bhooked%20wallet%20subprovider%5D%0A%20%20%5Bhooked%20wallet%20subprovider%5D%3C-%3E%5Bid%20store%5D%0A%20%20%5Bconfig%20manager%7C%0A%20%20%20%20%5Brpc%20configuration%5D%0A%20%20%20%20%5Bencrypted%20keys%5D%0A%20%20%20%20%5Bwallet%20nicknames%5D%0A%20%20%5D%0A%20%20%0A%20%20%5Bprovider-engine%5D%3C-%5Bconfig%20manager%5D%0A%20%20%5Bid%20store%5D%3C-%3E%5Bconfig%20manager%5D%0A%5D%0A%0A%5Buser%5D%3C-%3E%5Bmetamask-ui%5D%0A%0A%5Buser%5D%3C%3A--%3A%3E%5Bweb%20dapp%5D%0A%0A%5Bmetamask-contentscript%7C%0A%20%20%5Bplugin%20restart%20detector%5D%0A%20%20%5Brpc%20passthrough%5D%0A%5D%0A%0A%5Brpc%20%7C%0A%20%20%5Bethereum%20blockchain%20%7C%0A%20%20%20%20%5Bcontracts%5D%0A%20%20%20%20%5Baccounts%5D%0A%20%20%5D%0A%5D%0A%0A%5Bweb%20dapp%5D%3C%3A--%3A%3E%5Bmetamask-contentscript%5D%0A%5Bmetamask-contentscript%5D%3C-%3E%5Bmetamask-background%5D%0A%5Bmetamask-background%5D%3C-%3E%5Bmetamask-ui%5D%0A%5Bmetamask-background%5D%3C-%3E%5Brpc%5D%0A
|
||||
|
@ -1,4 +1,16 @@
|
||||
{
|
||||
"shareAddress": {
|
||||
"message": "Share Address"
|
||||
},
|
||||
"shareAddressToConnect": {
|
||||
"message": "Share your address to connect to $1?"
|
||||
},
|
||||
"shareAddressInfo": {
|
||||
"message": "Sharing your address with $1 will allow you to interact with this dapp. This permission is to protect your privacy by default."
|
||||
},
|
||||
"privacyModeDefault": {
|
||||
"message": "Privacy Mode is now enabled by default"
|
||||
},
|
||||
"privacyMode": {
|
||||
"message": "Privacy Mode"
|
||||
},
|
||||
@ -80,12 +92,21 @@
|
||||
"activityLog": {
|
||||
"message": "activity log"
|
||||
},
|
||||
"add": {
|
||||
"message": "Add"
|
||||
},
|
||||
"address": {
|
||||
"message": "Address"
|
||||
},
|
||||
"addNetwork": {
|
||||
"message": "Add Network"
|
||||
},
|
||||
"addRecipient": {
|
||||
"message": "Add Recipient"
|
||||
},
|
||||
"addressBook": {
|
||||
"message": "Address Book"
|
||||
},
|
||||
"advanced": {
|
||||
"message": "Advanced"
|
||||
},
|
||||
@ -98,6 +119,18 @@
|
||||
"addCustomToken": {
|
||||
"message": "Add custom token"
|
||||
},
|
||||
"addToAddressBook": {
|
||||
"message": "Add to address book"
|
||||
},
|
||||
"addToAddressBookModalPlaceholder": {
|
||||
"message": "e.g. John D."
|
||||
},
|
||||
"addAlias": {
|
||||
"message": "Add alias"
|
||||
},
|
||||
"addEthAddress": {
|
||||
"message": "Add an Ethereum address"
|
||||
},
|
||||
"addToken": {
|
||||
"message": "Add Token"
|
||||
},
|
||||
@ -172,6 +205,18 @@
|
||||
"back": {
|
||||
"message": "Back"
|
||||
},
|
||||
"backToAll": {
|
||||
"message": "Back to All"
|
||||
},
|
||||
"backupApprovalNotice": {
|
||||
"message": "Backup your Secret Recovery code to keep your wallet and funds secure."
|
||||
},
|
||||
"backupApprovalInfo": {
|
||||
"message": "This secret code is required to recover your wallet in case you lose your device, forget your password, have to re-install MetaMask, or want to access your wallet on another device."
|
||||
},
|
||||
"backupNow": {
|
||||
"message": "Backup now"
|
||||
},
|
||||
"balance": {
|
||||
"message": "Balance"
|
||||
},
|
||||
@ -237,9 +282,15 @@
|
||||
"bytes": {
|
||||
"message": "Bytes"
|
||||
},
|
||||
"off": {
|
||||
"message": "Off"
|
||||
},
|
||||
"ok": {
|
||||
"message": "Ok"
|
||||
},
|
||||
"on": {
|
||||
"message": "On"
|
||||
},
|
||||
"optionalBlockExplorerUrl": {
|
||||
"message": "Block Explorer URL (optional)"
|
||||
},
|
||||
@ -348,6 +399,12 @@
|
||||
"connectToTrezor": {
|
||||
"message": "Connect to Trezor"
|
||||
},
|
||||
"contactList": {
|
||||
"message": "Contact List"
|
||||
},
|
||||
"contactListDescription": {
|
||||
"message": "Add, edit, remove, and manage your contacts"
|
||||
},
|
||||
"continue": {
|
||||
"message": "Continue"
|
||||
},
|
||||
@ -457,6 +514,9 @@
|
||||
"delete": {
|
||||
"message": "Delete"
|
||||
},
|
||||
"deleteAccount": {
|
||||
"message": "Delete Account"
|
||||
},
|
||||
"denExplainer": {
|
||||
"message": "Your DEN is your password-encrypted storage within MetaMask."
|
||||
},
|
||||
@ -496,6 +556,9 @@
|
||||
"directDepositEtherExplainer": {
|
||||
"message": "If you already have some Ether, the quickest way to get Ether in your new wallet by direct deposit."
|
||||
},
|
||||
"dismiss": {
|
||||
"message": "Dismiss"
|
||||
},
|
||||
"done": {
|
||||
"message": "Done"
|
||||
},
|
||||
@ -523,6 +586,9 @@
|
||||
"editAccountName": {
|
||||
"message": "Edit Account Name"
|
||||
},
|
||||
"editContact":{
|
||||
"message": "Edit Contact"
|
||||
},
|
||||
"editingTransaction": {
|
||||
"message": "Make changes to your transaction"
|
||||
},
|
||||
@ -556,9 +622,24 @@
|
||||
"endOfFlowMessage8": {
|
||||
"message": "MetaMask cannot recover your seedphrase. Learn more."
|
||||
},
|
||||
"endOfFlowMessage9": {
|
||||
"message": "Learn more."
|
||||
},
|
||||
"endOfFlowMessage10": {
|
||||
"message": "All Done"
|
||||
},
|
||||
"ensNameNotFound": {
|
||||
"message": "ENS name not found"
|
||||
},
|
||||
"ensRegistrationError": {
|
||||
"message": "Error in ENS name registration"
|
||||
},
|
||||
"ensNotFoundOnCurrentNetwork": {
|
||||
"message": "ENS name not found on the current network. Try switching to Main Ethereum Network."
|
||||
},
|
||||
"enterAnAlias": {
|
||||
"message": "Enter an alias"
|
||||
},
|
||||
"enterPassword": {
|
||||
"message": "Enter password"
|
||||
},
|
||||
@ -571,6 +652,9 @@
|
||||
"eth": {
|
||||
"message": "ETH"
|
||||
},
|
||||
"ethereumPublicAddress": {
|
||||
"message": "Ethereum Public Address"
|
||||
},
|
||||
"etherscanView": {
|
||||
"message": "View account on Etherscan"
|
||||
},
|
||||
@ -881,6 +965,9 @@
|
||||
"loadingTokens": {
|
||||
"message": "Loading Tokens..."
|
||||
},
|
||||
"loadMore": {
|
||||
"message": "Load More"
|
||||
},
|
||||
"localhost": {
|
||||
"message": "Localhost 8545"
|
||||
},
|
||||
@ -902,6 +989,9 @@
|
||||
"memorizePhrase": {
|
||||
"message": "Memorize this phrase."
|
||||
},
|
||||
"memo": {
|
||||
"message": "memo"
|
||||
},
|
||||
"menu": {
|
||||
"message": "Menu"
|
||||
},
|
||||
@ -935,6 +1025,12 @@
|
||||
"myAccounts": {
|
||||
"message": "My Accounts"
|
||||
},
|
||||
"myWalletAccounts": {
|
||||
"message": "My Wallet Accounts"
|
||||
},
|
||||
"myWalletAccountsDescription": {
|
||||
"message": "All of your MetaMask created accounts will automatically be added to this section."
|
||||
},
|
||||
"mustSelectOne": {
|
||||
"message": "Must select at least 1 token."
|
||||
},
|
||||
@ -967,10 +1063,16 @@
|
||||
"newAccount": {
|
||||
"message": "New Account"
|
||||
},
|
||||
"newAccountDetectedDialogMessage": {
|
||||
"message": "New address detected! Click here to add to your address book."
|
||||
},
|
||||
"newAccountNumberName": {
|
||||
"message": "Account $1",
|
||||
"description": "Default name of next account to be created on create account screen"
|
||||
},
|
||||
"newContact": {
|
||||
"message": "New Contact"
|
||||
},
|
||||
"newContract": {
|
||||
"message": "New Contract"
|
||||
},
|
||||
@ -1170,7 +1272,7 @@
|
||||
"message": "Queue"
|
||||
},
|
||||
"readdToken": {
|
||||
"message": "You can add this token back in the future by going go to “Add token” in your accounts options menu."
|
||||
"message": "You can add this token back in the future by going to “Add token” in your accounts options menu."
|
||||
},
|
||||
"readMore": {
|
||||
"message": "Read more here."
|
||||
@ -1181,9 +1283,15 @@
|
||||
"receive": {
|
||||
"message": "Receive"
|
||||
},
|
||||
"recents": {
|
||||
"message": "Recents"
|
||||
},
|
||||
"recipientAddress": {
|
||||
"message": "Recipient Address"
|
||||
},
|
||||
"recipientAddressPlaceholder": {
|
||||
"message": "Search, public address (0x), or ENS"
|
||||
},
|
||||
"refundAddress": {
|
||||
"message": "Your Refund Address"
|
||||
},
|
||||
@ -1208,6 +1316,15 @@
|
||||
"resetAccountDescription": {
|
||||
"message": "Resetting your account will clear your transaction history."
|
||||
},
|
||||
"deleteNetwork": {
|
||||
"message": "Delete Network?"
|
||||
},
|
||||
"deleteNetworkDescription": {
|
||||
"message": "Are you sure you want to delete this network?"
|
||||
},
|
||||
"remindMeLater": {
|
||||
"message": "Remind me later"
|
||||
},
|
||||
"restoreFromSeed": {
|
||||
"message": "Restore account?"
|
||||
},
|
||||
@ -1457,7 +1574,7 @@
|
||||
"message": "there can only be a space between words"
|
||||
},
|
||||
"speedUp": {
|
||||
"message": "speed up"
|
||||
"message": "Speed Up"
|
||||
},
|
||||
"speedUpTitle": {
|
||||
"message": "Speed Up Transaction"
|
||||
@ -1652,6 +1769,9 @@
|
||||
"transfer": {
|
||||
"message": "Transfer"
|
||||
},
|
||||
"transferBetweenAccounts": {
|
||||
"message": "Transfer between my accounts"
|
||||
},
|
||||
"transferFrom": {
|
||||
"message": "Transfer From"
|
||||
},
|
||||
@ -1732,6 +1852,9 @@
|
||||
"useOldUI": {
|
||||
"message": "Use old UI"
|
||||
},
|
||||
"userName":{
|
||||
"message": "Username"
|
||||
},
|
||||
"validFileImport": {
|
||||
"message": "You must select a valid file to import."
|
||||
},
|
||||
@ -1744,6 +1867,9 @@
|
||||
"viewinExplorer": {
|
||||
"message": "View in Explorer"
|
||||
},
|
||||
"viewContact": {
|
||||
"message": "View Contact"
|
||||
},
|
||||
"viewOnCustomBlockExplorer": {
|
||||
"message": "View at $1"
|
||||
},
|
||||
@ -1802,6 +1928,6 @@
|
||||
"message": "MetaMask will never ask for your seed phrase!"
|
||||
},
|
||||
"zeroGasPriceOnSpeedUpError": {
|
||||
"message":"Zero gas price on speed up"
|
||||
"message": "Zero gas price on speed up"
|
||||
}
|
||||
}
|
||||
|
@ -1235,7 +1235,7 @@
|
||||
},
|
||||
"pending": {
|
||||
"message": "pendiente"
|
||||
},
|
||||
},
|
||||
"popularTokens": {
|
||||
"message": "Tokens Corrientes"
|
||||
},
|
||||
@ -1504,7 +1504,7 @@
|
||||
"message": "Tu frase semilla privada"
|
||||
},
|
||||
"zeroGasPriceOnSpeedUpError": {
|
||||
"message":"No hubo precio de gas al agilizar"
|
||||
"message": "No hubo precio de gas al agilizar"
|
||||
},
|
||||
"currencyConversion": {
|
||||
"message": "Cambio de Monedas"
|
||||
|
@ -318,7 +318,7 @@
|
||||
"message": "i-click ito",
|
||||
"description": "tulad ng -i-click dito- para sa mas maraming impormasyon (kasama ng troubleTokenBalances)"
|
||||
},
|
||||
"hide": {
|
||||
"hide": {
|
||||
"message": "Itago"
|
||||
},
|
||||
"hideToken": {
|
||||
|
@ -1145,7 +1145,7 @@
|
||||
},
|
||||
"seedPhraseAlert": {
|
||||
"message": "Opozorilo o seed phrase"
|
||||
},
|
||||
},
|
||||
"select": {
|
||||
"message": "Izberi"
|
||||
},
|
||||
@ -1549,6 +1549,6 @@
|
||||
"message": "Vaš zasebni seed phrase"
|
||||
},
|
||||
"zeroGasPriceOnSpeedUpError": {
|
||||
"message":"Ničelni gas price na pospešitvi"
|
||||
"message": "Ničelni gas price na pospešitvi"
|
||||
}
|
||||
}
|
||||
|
@ -655,5 +655,5 @@
|
||||
},
|
||||
"yourSigRequested": {
|
||||
"message": "Chữ ký của bạn đang được yêu cầu"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -651,7 +651,7 @@
|
||||
"description": "status showing that an account has been fully loaded into the keyring"
|
||||
},
|
||||
"importUsingSeed": {
|
||||
"message": "利用助憶詞匯入帳戶" ,
|
||||
"message": "利用助憶詞匯入帳戶",
|
||||
"description": "登入頁面下方"
|
||||
},
|
||||
"importWithSeedPhrase": {
|
||||
@ -1584,6 +1584,6 @@
|
||||
"message": "您每一次在交易時,都會看到這個圖案。"
|
||||
},
|
||||
"zeroGasPriceOnSpeedUpError": {
|
||||
"message":"加速的 Gas 價格為 0"
|
||||
"message": "加速的 Gas 價格為 0"
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1 user-scalable=no">
|
||||
<title>MetaMask</title>
|
||||
<link rel="stylesheet" type="text/css" href="./index.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app-content"></div>
|
||||
|
4
app/images/check-green-solid.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.833374 10C0.833374 4.9374 4.93743 0.833344 10 0.833344C15.0626 0.833344 19.1667 4.9374 19.1667 10C19.1667 15.0626 15.0626 19.1667 10 19.1667C4.93743 19.1667 0.833374 15.0626 0.833374 10Z" fill="#28A745"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.4256 6.70245C14.7511 7.02789 14.7511 7.55553 14.4256 7.88097L9.25303 13.2976C8.9276 13.6231 8.39996 13.6231 8.07452 13.2976L5.57452 10.7976C5.24909 10.4722 5.24909 9.94456 5.57452 9.61912C5.89996 9.29368 6.4276 9.29368 6.75303 9.61912L8.66378 11.5299L13.2471 6.70245C13.5725 6.37702 14.1002 6.37702 14.4256 6.70245Z" fill="white"/>
|
||||
</svg>
|
After Width: | Height: | Size: 745 B |
4
app/images/close-gray.svg
Executable file
@ -0,0 +1,4 @@
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="0.14917" y="1.09723" width="1.34076" height="15.4188" rx="0.670381" transform="rotate(-45 0.14917 1.09723)" fill="#A1A5B3"/>
|
||||
<rect x="0.94812" y="11.8508" width="1.34076" height="15.4188" rx="0.670381" transform="rotate(-135 0.94812 11.8508)" fill="#A1A5B3"/>
|
||||
</svg>
|
After Width: | Height: | Size: 372 B |
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.4 KiB |
7
app/images/icons/connect.svg
Normal file
@ -0,0 +1,7 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8.00002 9.57037C8.93767 9.57037 9.69778 8.81026 9.69778 7.8726C9.69778 6.93495 8.93767 6.17484 8.00002 6.17484C7.06236 6.17484 6.30225 6.93495 6.30225 7.8726C6.30225 8.81026 7.06236 9.57037 8.00002 9.57037Z" fill="white"/>
|
||||
<path d="M11.0582 11.6586C10.872 11.6586 10.6857 11.5876 10.5437 11.4455C10.2595 11.1614 10.2595 10.7007 10.5437 10.4165C11.2232 9.73704 11.5975 8.83356 11.5975 7.87259C11.5975 6.91161 11.2232 6.00813 10.5437 5.32865C10.2595 5.04448 10.2595 4.58381 10.5437 4.29964C10.8278 4.01554 11.2886 4.01554 11.5727 4.29964C12.527 5.25398 13.0527 6.52293 13.0527 7.87259C13.0527 9.22224 12.527 10.4912 11.5727 11.4455C11.4306 11.5876 11.2444 11.6586 11.0582 11.6586Z" fill="white"/>
|
||||
<path d="M4.94175 11.6586C4.75553 11.6586 4.56929 11.5876 4.42724 11.4455C3.4729 10.4912 2.94727 9.22224 2.94727 7.87259C2.94727 6.52293 3.4729 5.25398 4.42724 4.29964C4.71135 4.01554 5.17215 4.01554 5.45626 4.29964C5.74043 4.58381 5.74043 5.04448 5.45626 5.32865C4.77672 6.00813 4.4025 6.91161 4.4025 7.87259C4.4025 8.83356 4.77672 9.73704 5.45626 10.4165C5.74043 10.7007 5.74043 11.1614 5.45626 11.4455C5.3142 11.5876 5.12798 11.6586 4.94175 11.6586Z" fill="white"/>
|
||||
<path d="M13.1451 13.7453C12.9589 13.7453 12.7727 13.6742 12.6306 13.5322C12.3464 13.248 12.3464 12.7873 12.6306 12.5031C15.1839 9.94985 15.1839 5.79538 12.6306 3.24209C12.3464 2.95792 12.3464 2.49725 12.6306 2.21308C12.9147 1.92897 13.3755 1.92897 13.6596 2.21308C16.7803 5.33374 16.7803 10.4115 13.6596 13.5322C13.5176 13.6742 13.3313 13.7453 13.1451 13.7453Z" fill="white"/>
|
||||
<path d="M2.855 13.7453C2.66878 13.7453 2.48255 13.6742 2.3405 13.5322C-0.780166 10.4115 -0.780166 5.33374 2.3405 2.21308C2.62461 1.92897 3.08541 1.92897 3.36951 2.21308C3.65368 2.49725 3.65368 2.95792 3.36951 3.24209C0.816221 5.79538 0.816221 9.94985 3.36951 12.5031C3.65368 12.7873 3.65368 13.248 3.36951 13.5322C3.22745 13.6742 3.04123 13.7453 2.855 13.7453Z" fill="white"/>
|
||||
</svg>
|
After Width: | Height: | Size: 2.0 KiB |
5
app/images/icons/info.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.99984 2.00001C4.68613 2.00001 1.99984 4.6863 1.99984 8C1.99984 11.3137 4.68613 14 7.99984 14C11.3135 14 13.9998 11.3137 13.9998 8C13.9998 4.6863 11.3135 2.00001 7.99984 2.00001ZM0.666504 8C0.666504 3.94992 3.94975 0.666672 7.99984 0.666672C12.0499 0.666672 15.3332 3.94992 15.3332 8C15.3332 12.0501 12.0499 15.3333 7.99984 15.3333C3.94975 15.3333 0.666504 12.0501 0.666504 8Z" fill="#6A737D"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.99984 7.33334C8.36803 7.33334 8.6665 7.63181 8.6665 8V10.6667C8.6665 11.0349 8.36803 11.3333 7.99984 11.3333C7.63165 11.3333 7.33317 11.0349 7.33317 10.6667V8C7.33317 7.63181 7.63165 7.33334 7.99984 7.33334Z" fill="#6A737D"/>
|
||||
<path d="M8.6665 5.33334C8.6665 5.70153 8.36803 6 7.99984 6C7.63165 6 7.33317 5.70153 7.33317 5.33334C7.33317 4.96515 7.63165 4.66667 7.99984 4.66667C8.36803 4.66667 8.6665 4.96515 8.6665 5.33334Z" fill="#6A737D"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 692 B |
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 4.6 KiB |
3
app/images/meta-shield.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg width="20" height="24" viewBox="0 0 20 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 9.71495V3.57158L10 0.872803L20 3.57158V9.71495C20 16.0442 15.7999 21.4041 10 23.2357C4.19995 21.4041 0 16.0442 0 9.71495ZM15.3879 10.7277L15.6232 10.898L15.0819 11.5366L15.9057 14.1001L15.1431 16.7158L12.4738 15.9779L11.956 16.4036L10.9014 17.1367H9.09833L8.04379 16.4036L7.52596 15.9779L4.85671 16.7158L4.09874 14.1001L4.91784 11.5366L4.37644 10.898L4.61188 10.7277L4.23524 10.3825L4.52242 10.1553L4.14578 9.8669L4.4 9.6777L3.99989 7.74783L4.59302 5.95527L8.41104 7.38838H11.5887L15.4067 5.95527L15.9999 7.74783L15.6045 9.6777L15.854 9.8669L15.4773 10.1553L15.7644 10.3825L15.3879 10.7277ZM7.38499 13.1652L8.47065 12.6641L8.92346 13.6225L7.38499 13.1652ZM10.7685 13.6225L11.2197 12.6641L12.307 13.1652L10.7685 13.6225ZM9.24343 14.9001H10.4439L10.6519 15.0662L10.7689 16.178L10.6607 16.0674H9.02677L8.92279 16.178L9.03541 15.0662L9.24343 14.9001Z" fill="white"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1018 B |
Before Width: | Height: | Size: 93 KiB After Width: | Height: | Size: 69 KiB |
7
app/images/qr-blue.svg
Normal file
@ -0,0 +1,7 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.32 8H8.63997L8.63997 16H16.32V8ZM8.63997 6C7.57958 6 6.71997 6.89543 6.71997 8V16C6.71997 17.1046 7.57958 18 8.63997 18H16.32C17.3804 18 18.24 17.1046 18.24 16V8C18.24 6.89543 17.3804 6 16.32 6H8.63997Z" fill="#037DD6"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.32 1C16.32 0.447715 16.7498 0 17.28 0H21.12C22.7106 0 24 1.34315 24 3V7C24 7.55228 23.5702 8 23.04 8C22.5098 8 22.08 7.55228 22.08 7V3C22.08 2.44772 21.6502 2 21.12 2H17.28C16.7498 2 16.32 1.55228 16.32 1Z" fill="#037DD6"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M23.04 16C23.5702 16 24 16.4477 24 17L24 21C24 22.6569 22.7106 24 21.12 24L17.28 24C16.7498 24 16.32 23.5523 16.32 23C16.32 22.4477 16.7498 22 17.28 22L21.12 22C21.6502 22 22.08 21.5523 22.08 21L22.08 17C22.08 16.4477 22.5098 16 23.04 16Z" fill="#037DD6"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.67999 23C7.67999 23.5523 7.25019 24 6.71999 24L2.87999 24C1.28941 24 -6.563e-06 22.6569 -6.42394e-06 21L-6.08824e-06 17C-6.04189e-06 16.4477 0.429801 16 0.959994 16C1.49019 16 1.91999 16.4477 1.91999 17L1.91999 21C1.91999 21.5523 2.3498 22 2.87999 22L6.71999 22C7.25019 22 7.67999 22.4477 7.67999 23Z" fill="#037DD6"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.96 8C0.429807 8 5.87108e-08 7.55228 1.31134e-07 7L6.55671e-07 3C8.72941e-07 1.34315 1.28942 1.69087e-07 2.88 3.77666e-07L6.72 8.81222e-07C7.25019 9.50748e-07 7.68 0.447716 7.68 1C7.68 1.55229 7.25019 2 6.72 2L2.88 2C2.34981 2 1.92 2.44772 1.92 3L1.92 7C1.92 7.55229 1.49019 8 0.96 8Z" fill="#037DD6"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.7 KiB |
4
app/images/search-black.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.16667 3.33341C5.94501 3.33341 3.33334 5.94509 3.33334 9.16675C3.33334 12.3884 5.94501 15.0001 9.16667 15.0001C12.3883 15.0001 15 12.3884 15 9.16675C15 5.94509 12.3883 3.33341 9.16667 3.33341ZM1.66667 9.16675C1.66667 5.02461 5.02454 1.66675 9.16667 1.66675C13.3088 1.66675 16.6667 5.02461 16.6667 9.16675C16.6667 13.3089 13.3088 16.6667 9.16667 16.6667C5.02454 16.6667 1.66667 13.3089 1.66667 9.16675Z" fill="black"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.2857 13.2858C13.6112 12.9604 14.1388 12.9604 14.4642 13.2858L18.0892 16.9108C18.4147 17.2363 18.4147 17.7639 18.0892 18.0893C17.7638 18.4148 17.2362 18.4148 16.9107 18.0893L13.2857 14.4643C12.9603 14.1389 12.9603 13.6113 13.2857 13.2858Z" fill="black"/>
|
||||
</svg>
|
After Width: | Height: | Size: 883 B |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 14 KiB |
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "__MSG_appName__",
|
||||
"short_name": "__MSG_appName__",
|
||||
"version": "6.7.3",
|
||||
"version": "7.0.0",
|
||||
"manifest_version": 2,
|
||||
"author": "https://metamask.io",
|
||||
"description": "__MSG_appDescription__",
|
||||
@ -21,7 +21,8 @@
|
||||
},
|
||||
"applications": {
|
||||
"gecko": {
|
||||
"id": "webextension@metamask.io"
|
||||
"id": "webextension@metamask.io",
|
||||
"strict_min_version": "60.0"
|
||||
}
|
||||
},
|
||||
"default_locale": "en",
|
||||
@ -85,4 +86,4 @@
|
||||
"*"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,7 @@
|
||||
margin-top: 1rem;
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" type="text/css" href="./index.css">
|
||||
</head>
|
||||
<body class="notification" style="height:600px;">
|
||||
<div id="app-content">
|
||||
|
@ -4,6 +4,7 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1 user-scalable=no">
|
||||
<title>MetaMask</title>
|
||||
<link rel="stylesheet" type="text/css" href="./index.css">
|
||||
</head>
|
||||
<body style="width:357px; height:600px;">
|
||||
<div id="app-content"></div>
|
||||
|
@ -116,7 +116,6 @@ setupMetamaskMeshMetrics()
|
||||
* @property {boolean} useBlockie - Indicates preferred user identicon format. True for blockie, false for Jazzicon.
|
||||
* @property {Object} featureFlags - An object for optional feature flags.
|
||||
* @property {string} networkEndpointType - TODO: Document
|
||||
* @property {boolean} isRevealingSeedWords - True if seed words are currently being recovered, and should be shown to user.
|
||||
* @property {boolean} welcomeScreen - True if welcome screen should be shown.
|
||||
* @property {string} currentLocale - A locale string matching the user's preferred display language.
|
||||
* @property {Object} provider - The current selected network provider.
|
||||
@ -253,7 +252,6 @@ function setupController (initState, initLangCode) {
|
||||
const controller = new MetamaskController({
|
||||
// User confirmation callbacks:
|
||||
showUnconfirmedMessage: triggerUi,
|
||||
unlockAccountMessage: triggerUi,
|
||||
showUnapprovedTx: triggerUi,
|
||||
openPopup: openPopup,
|
||||
closePopup: notificationManager.closePopup.bind(notificationManager),
|
||||
|
@ -10,7 +10,7 @@ const extension = require('extensionizer')
|
||||
const PortStream = require('extension-port-stream')
|
||||
|
||||
const inpageContent = fs.readFileSync(path.join(__dirname, '..', '..', 'dist', 'chrome', 'inpage.js')).toString()
|
||||
const inpageSuffix = '//# sourceURL=' + extension.extension.getURL('inpage.js') + '\n'
|
||||
const inpageSuffix = '//# sourceURL=' + extension.runtime.getURL('inpage.js') + '\n'
|
||||
const inpageBundle = inpageContent + inpageSuffix
|
||||
|
||||
// Eventually this streaming injection could be replaced with:
|
||||
@ -114,6 +114,7 @@ function forwardTrafficBetweenMuxers (channelName, muxA, muxB) {
|
||||
|
||||
async function setupPublicApi (outStream) {
|
||||
const api = {
|
||||
forceReloadSite: (cb) => cb(null, forceReloadSite()),
|
||||
getSiteMetadata: (cb) => cb(null, getSiteMetadata()),
|
||||
}
|
||||
const dnode = Dnode(api)
|
||||
@ -306,3 +307,10 @@ async function domIsReady () {
|
||||
// wait for load
|
||||
await new Promise(resolve => window.addEventListener('DOMContentLoaded', resolve, { once: true }))
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads the site
|
||||
*/
|
||||
function forceReloadSite () {
|
||||
window.location.reload()
|
||||
}
|
||||
|
@ -65,7 +65,7 @@ class ComputedbalancesController {
|
||||
syncAllAccountsFromStore (store) {
|
||||
const upstream = Object.keys(store.accounts)
|
||||
const balances = Object.keys(this.balances)
|
||||
.map(address => this.balances[address])
|
||||
.map(address => this.balances[address])
|
||||
|
||||
// Follow new addresses
|
||||
for (const address in balances) {
|
||||
|
@ -47,14 +47,14 @@ class DetectTokensController {
|
||||
}
|
||||
tokensToDetect.forEach((tokenAddress, index) => {
|
||||
const balance = result[index]
|
||||
if (!balance.isZero()) {
|
||||
if (balance && !balance.isZero()) {
|
||||
this._preferences.addToken(tokenAddress, contracts[tokenAddress].symbol, contracts[tokenAddress].decimals)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Find if selectedAddress has tokens with contract in contractAddress.
|
||||
*
|
||||
* @param {string} contractAddress Hex address of the token contract to explore.
|
||||
|
@ -4,8 +4,8 @@ const SINGLE_CALL_BALANCES_ADDRESS_ROPSTEN = '0xb8e671734ce5c8d7dfbbea5574fa4cf3
|
||||
const SINGLE_CALL_BALANCES_ADDRESS_KOVAN = '0xb1d3fbb2f83aecd196f474c16ca5d9cffa0d0ffc'
|
||||
|
||||
module.exports = {
|
||||
SINGLE_CALL_BALANCES_ADDRESS,
|
||||
SINGLE_CALL_BALANCES_ADDRESS_RINKEBY,
|
||||
SINGLE_CALL_BALANCES_ADDRESS_ROPSTEN,
|
||||
SINGLE_CALL_BALANCES_ADDRESS_KOVAN,
|
||||
SINGLE_CALL_BALANCES_ADDRESS,
|
||||
SINGLE_CALL_BALANCES_ADDRESS_RINKEBY,
|
||||
SINGLE_CALL_BALANCES_ADDRESS_ROPSTEN,
|
||||
SINGLE_CALL_BALANCES_ADDRESS_KOVAN,
|
||||
}
|
||||
|
43
app/scripts/controllers/onboarding.js
Normal file
@ -0,0 +1,43 @@
|
||||
const ObservableStore = require('obs-store')
|
||||
const extend = require('xtend')
|
||||
|
||||
/**
|
||||
* @typedef {Object} InitState
|
||||
* @property {Boolean} seedPhraseBackedUp Indicates whether the user has completed the seed phrase backup challenge
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} OnboardingOptions
|
||||
* @property {InitState} initState The initial controller state
|
||||
*/
|
||||
|
||||
/**
|
||||
* Controller responsible for maintaining
|
||||
* a cache of account balances in local storage
|
||||
*/
|
||||
class OnboardingController {
|
||||
/**
|
||||
* Creates a new controller instance
|
||||
*
|
||||
* @param {OnboardingOptions} [opts] Controller configuration parameters
|
||||
*/
|
||||
constructor (opts = {}) {
|
||||
const initState = extend({
|
||||
seedPhraseBackedUp: null,
|
||||
}, opts.initState)
|
||||
this.store = new ObservableStore(initState)
|
||||
}
|
||||
|
||||
setSeedPhraseBackedUp (newSeedPhraseBackUpState) {
|
||||
this.store.updateState({
|
||||
seedPhraseBackedUp: newSeedPhraseBackUpState,
|
||||
})
|
||||
}
|
||||
|
||||
getSeedPhraseBackedUp () {
|
||||
return this.store.getState().seedPhraseBackedUp
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = OnboardingController
|
@ -49,13 +49,12 @@ class PreferencesController {
|
||||
currentLocale: opts.initLangCode,
|
||||
identities: {},
|
||||
lostIdentities: {},
|
||||
seedWords: null,
|
||||
forgottenPassword: false,
|
||||
preferences: {
|
||||
useNativeCurrencyAsPrimaryCurrency: true,
|
||||
},
|
||||
completedOnboarding: false,
|
||||
completedUiMigration: true,
|
||||
migratedPrivacyMode: false,
|
||||
metaMetricsId: null,
|
||||
metaMetricsSendCount: 0,
|
||||
}, opts.initState)
|
||||
@ -70,7 +69,7 @@ class PreferencesController {
|
||||
return this.setFeatureFlag(key, value)
|
||||
}
|
||||
}
|
||||
// PUBLIC METHODS
|
||||
// PUBLIC METHODS
|
||||
|
||||
/**
|
||||
* Sets the {@code forgottenPassword} state property
|
||||
@ -80,14 +79,6 @@ class PreferencesController {
|
||||
this.store.updateState({ forgottenPassword })
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@code seedWords} seed words
|
||||
* @param {string|null} seedWords the seed words
|
||||
*/
|
||||
setSeedWords (seedWords) {
|
||||
this.store.updateState({ seedWords })
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for the `useBlockie` property
|
||||
*
|
||||
@ -139,9 +130,9 @@ class PreferencesController {
|
||||
* @param {String} type Indicates the type of first time flow - create or import - the user wishes to follow
|
||||
*
|
||||
*/
|
||||
setFirstTimeFlowType (type) {
|
||||
this.store.updateState({ firstTimeFlowType: type })
|
||||
}
|
||||
setFirstTimeFlowType (type) {
|
||||
this.store.updateState({ firstTimeFlowType: type })
|
||||
}
|
||||
|
||||
|
||||
getSuggestedTokens () {
|
||||
@ -503,22 +494,22 @@ class PreferencesController {
|
||||
* @returns {Promise<array>} Promise resolving to updated frequentRpcList.
|
||||
*
|
||||
*/
|
||||
addToFrequentRpcList (url, chainId, ticker = 'ETH', nickname = '', rpcPrefs = {}) {
|
||||
const rpcList = this.getFrequentRpcListDetail()
|
||||
const index = rpcList.findIndex((element) => { return element.rpcUrl === url })
|
||||
if (index !== -1) {
|
||||
rpcList.splice(index, 1)
|
||||
}
|
||||
if (url !== 'http://localhost:8545') {
|
||||
let checkedChainId
|
||||
if (!!chainId && !Number.isNaN(parseInt(chainId))) {
|
||||
checkedChainId = chainId
|
||||
}
|
||||
rpcList.push({ rpcUrl: url, chainId: checkedChainId, ticker, nickname, rpcPrefs })
|
||||
}
|
||||
this.store.updateState({ frequentRpcListDetail: rpcList })
|
||||
return Promise.resolve(rpcList)
|
||||
addToFrequentRpcList (url, chainId, ticker = 'ETH', nickname = '', rpcPrefs = {}) {
|
||||
const rpcList = this.getFrequentRpcListDetail()
|
||||
const index = rpcList.findIndex((element) => { return element.rpcUrl === url })
|
||||
if (index !== -1) {
|
||||
rpcList.splice(index, 1)
|
||||
}
|
||||
if (url !== 'http://localhost:8545') {
|
||||
let checkedChainId
|
||||
if (!!chainId && !Number.isNaN(parseInt(chainId))) {
|
||||
checkedChainId = chainId
|
||||
}
|
||||
rpcList.push({ rpcUrl: url, chainId: checkedChainId, ticker, nickname, rpcPrefs })
|
||||
}
|
||||
this.store.updateState({ frequentRpcListDetail: rpcList })
|
||||
return Promise.resolve(rpcList)
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes custom RPC url from state.
|
||||
@ -613,12 +604,11 @@ class PreferencesController {
|
||||
return Promise.resolve(true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@code completedUiMigration} state to {@code true}, indicating that the user has completed the UI switch.
|
||||
*/
|
||||
completeUiMigration () {
|
||||
this.store.updateState({ completedUiMigration: true })
|
||||
return Promise.resolve(true)
|
||||
unsetMigratedPrivacyMode () {
|
||||
this.store.updateState({
|
||||
migratedPrivacyMode: false,
|
||||
})
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -18,12 +18,13 @@ class ProviderApprovalController extends SafeEventEmitter {
|
||||
*/
|
||||
constructor ({ closePopup, keyringController, openPopup, preferencesController } = {}) {
|
||||
super()
|
||||
this.approvedOrigins = {}
|
||||
this.closePopup = closePopup
|
||||
this.keyringController = keyringController
|
||||
this.openPopup = openPopup
|
||||
this.preferencesController = preferencesController
|
||||
this.store = new ObservableStore({
|
||||
approvedOrigins: {},
|
||||
dismissedOrigins: {},
|
||||
providerRequests: [],
|
||||
})
|
||||
}
|
||||
@ -45,7 +46,7 @@ class ProviderApprovalController extends SafeEventEmitter {
|
||||
}
|
||||
// register the provider request
|
||||
const metadata = await getSiteMetadata(origin)
|
||||
this._handleProviderRequest(origin, metadata.name, metadata.icon, false, null)
|
||||
this._handleProviderRequest(origin, metadata.name, metadata.icon)
|
||||
// wait for resolution of request
|
||||
const approved = await new Promise(resolve => this.once(`resolvedRequest:${origin}`, ({ approved }) => resolve(approved)))
|
||||
if (approved) {
|
||||
@ -63,10 +64,12 @@ class ProviderApprovalController extends SafeEventEmitter {
|
||||
* @param {string} siteTitle - The title of the document requesting full provider access
|
||||
* @param {string} siteImage - The icon of the window requesting full provider access
|
||||
*/
|
||||
_handleProviderRequest (origin, siteTitle, siteImage, force, tabID) {
|
||||
this.store.updateState({ providerRequests: [{ origin, siteTitle, siteImage, tabID }] })
|
||||
_handleProviderRequest (origin, siteTitle, siteImage) {
|
||||
this.store.updateState({ providerRequests: [{ origin, siteTitle, siteImage }] })
|
||||
const isUnlocked = this.keyringController.memStore.getState().isUnlocked
|
||||
if (!force && this.approvedOrigins[origin] && this.caching && isUnlocked) {
|
||||
const { approvedOrigins, dismissedOrigins } = this.store.getState()
|
||||
const originAlreadyHandled = approvedOrigins[origin] || dismissedOrigins[origin]
|
||||
if (originAlreadyHandled && this.caching && isUnlocked) {
|
||||
return
|
||||
}
|
||||
this.openPopup && this.openPopup()
|
||||
@ -78,11 +81,27 @@ class ProviderApprovalController extends SafeEventEmitter {
|
||||
* @param {string} origin - origin of the domain that had provider access approved
|
||||
*/
|
||||
approveProviderRequestByOrigin (origin) {
|
||||
this.closePopup && this.closePopup()
|
||||
const requests = this.store.getState().providerRequests
|
||||
const providerRequests = requests.filter(request => request.origin !== origin)
|
||||
this.store.updateState({ providerRequests })
|
||||
this.approvedOrigins[origin] = true
|
||||
if (this.closePopup) {
|
||||
this.closePopup()
|
||||
}
|
||||
|
||||
const { approvedOrigins, dismissedOrigins, providerRequests } = this.store.getState()
|
||||
|
||||
let _dismissedOrigins = dismissedOrigins
|
||||
if (dismissedOrigins[origin]) {
|
||||
_dismissedOrigins = Object.assign({}, dismissedOrigins)
|
||||
delete _dismissedOrigins[origin]
|
||||
}
|
||||
|
||||
const remainingProviderRequests = providerRequests.filter(request => request.origin !== origin)
|
||||
this.store.updateState({
|
||||
approvedOrigins: {
|
||||
...approvedOrigins,
|
||||
[origin]: true,
|
||||
},
|
||||
dismissedOrigins: _dismissedOrigins,
|
||||
providerRequests: remainingProviderRequests,
|
||||
})
|
||||
this.emit(`resolvedRequest:${origin}`, { approved: true })
|
||||
}
|
||||
|
||||
@ -92,19 +111,62 @@ class ProviderApprovalController extends SafeEventEmitter {
|
||||
* @param {string} origin - origin of the domain that had provider access approved
|
||||
*/
|
||||
rejectProviderRequestByOrigin (origin) {
|
||||
this.closePopup && this.closePopup()
|
||||
const requests = this.store.getState().providerRequests
|
||||
const providerRequests = requests.filter(request => request.origin !== origin)
|
||||
this.store.updateState({ providerRequests })
|
||||
delete this.approvedOrigins[origin]
|
||||
if (this.closePopup) {
|
||||
this.closePopup()
|
||||
}
|
||||
|
||||
const { approvedOrigins, providerRequests, dismissedOrigins } = this.store.getState()
|
||||
const remainingProviderRequests = providerRequests.filter(request => request.origin !== origin)
|
||||
|
||||
// We're cloning and deleting keys here because we don't want to keep unneeded keys
|
||||
const _approvedOrigins = Object.assign({}, approvedOrigins)
|
||||
delete _approvedOrigins[origin]
|
||||
|
||||
this.store.putState({
|
||||
approvedOrigins: _approvedOrigins,
|
||||
providerRequests: remainingProviderRequests,
|
||||
dismissedOrigins: {
|
||||
...dismissedOrigins,
|
||||
[origin]: true,
|
||||
},
|
||||
})
|
||||
this.emit(`resolvedRequest:${origin}`, { approved: false })
|
||||
}
|
||||
|
||||
/**
|
||||
* Silently approves access to a full Ethereum provider API for the origin
|
||||
*
|
||||
* @param {string} origin - origin of the domain that had provider access approved
|
||||
*/
|
||||
forceApproveProviderRequestByOrigin (origin) {
|
||||
const { approvedOrigins, dismissedOrigins, providerRequests } = this.store.getState()
|
||||
const remainingProviderRequests = providerRequests.filter(request => request.origin !== origin)
|
||||
|
||||
let _dismissedOrigins = dismissedOrigins
|
||||
if (dismissedOrigins[origin]) {
|
||||
_dismissedOrigins = Object.assign({}, dismissedOrigins)
|
||||
delete _dismissedOrigins[origin]
|
||||
}
|
||||
|
||||
this.store.updateState({
|
||||
approvedOrigins: {
|
||||
...approvedOrigins,
|
||||
[origin]: true,
|
||||
},
|
||||
dismissedOrigins: _dismissedOrigins,
|
||||
providerRequests: remainingProviderRequests,
|
||||
})
|
||||
|
||||
this.emit(`forceResolvedRequest:${origin}`, { approved: true, forced: true })
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears any cached approvals for user-approved origins
|
||||
*/
|
||||
clearApprovedOrigins () {
|
||||
this.approvedOrigins = {}
|
||||
this.store.updateState({
|
||||
approvedOrigins: {},
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@ -115,8 +177,7 @@ class ProviderApprovalController extends SafeEventEmitter {
|
||||
*/
|
||||
shouldExposeAccounts (origin) {
|
||||
const privacyMode = this.preferencesController.getFeatureFlags().privacyMode
|
||||
const result = !privacyMode || Boolean(this.approvedOrigins[origin])
|
||||
return result
|
||||
return !privacyMode || Boolean(this.store.getState().approvedOrigins[origin])
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -129,7 +129,7 @@ class TransactionController extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
Adds a tx to the txlist
|
||||
@emits ${txMeta.id}:unapproved
|
||||
*/
|
||||
@ -220,7 +220,7 @@ class TransactionController extends EventEmitter {
|
||||
|
||||
return txMeta
|
||||
}
|
||||
/**
|
||||
/**
|
||||
adds the tx gas defaults: gas && gasPrice
|
||||
@param txMeta {Object} - the txMeta object
|
||||
@returns {Promise<object>} resolves with txMeta
|
||||
@ -495,9 +495,9 @@ class TransactionController extends EventEmitter {
|
||||
this.txStateManager.updateTx(txMeta, 'transactions#setTxHash')
|
||||
}
|
||||
|
||||
//
|
||||
// PRIVATE METHODS
|
||||
//
|
||||
//
|
||||
// PRIVATE METHODS
|
||||
//
|
||||
/** maps methods for convenience*/
|
||||
_mapMethods () {
|
||||
/** @returns the state in transaction controller */
|
||||
@ -537,14 +537,14 @@ class TransactionController extends EventEmitter {
|
||||
loadingDefaults: true,
|
||||
}).forEach((tx) => {
|
||||
this.addTxGasDefaults(tx)
|
||||
.then((txMeta) => {
|
||||
txMeta.loadingDefaults = false
|
||||
this.txStateManager.updateTx(txMeta, 'transactions: gas estimation for tx on boot')
|
||||
}).catch((error) => {
|
||||
tx.loadingDefaults = false
|
||||
this.txStateManager.updateTx(tx, 'failed to estimate gas during boot cleanup.')
|
||||
this.txStateManager.setTxStatusFailed(tx.id, error)
|
||||
})
|
||||
.then((txMeta) => {
|
||||
txMeta.loadingDefaults = false
|
||||
this.txStateManager.updateTx(txMeta, 'transactions: gas estimation for tx on boot')
|
||||
}).catch((error) => {
|
||||
tx.loadingDefaults = false
|
||||
this.txStateManager.updateTx(tx, 'failed to estimate gas during boot cleanup.')
|
||||
this.txStateManager.setTxStatusFailed(tx.id, error)
|
||||
})
|
||||
})
|
||||
|
||||
this.txStateManager.getFilteredTxList({
|
||||
|
@ -17,10 +17,10 @@ function migrateFromSnapshotsToDiffs (longHistory) {
|
||||
return (
|
||||
longHistory
|
||||
// convert non-initial history entries into diffs
|
||||
.map((entry, index) => {
|
||||
if (index === 0) return entry
|
||||
return generateHistoryEntry(longHistory[index - 1], entry)
|
||||
})
|
||||
.map((entry, index) => {
|
||||
if (index === 0) return entry
|
||||
return generateHistoryEntry(longHistory[index - 1], entry)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,7 @@ const normalizers = {
|
||||
gasPrice: gasPrice => addHexPrefix(gasPrice),
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
normalizes txParams
|
||||
@param txParams {object}
|
||||
@returns {object} normalized txParams
|
||||
@ -40,7 +40,7 @@ function normalizeTxParams (txParams, LowerCase) {
|
||||
return normalizedTxParams
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
validates txParams
|
||||
@param txParams {object}
|
||||
*/
|
||||
@ -59,7 +59,7 @@ function validateTxParams (txParams) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
validates the from field in txParams
|
||||
@param txParams {object}
|
||||
*/
|
||||
@ -68,7 +68,7 @@ function validateFrom (txParams) {
|
||||
if (!isValidAddress(txParams.from)) throw new Error('Invalid from address')
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
validates the to field in txParams
|
||||
@param txParams {object}
|
||||
*/
|
||||
@ -85,7 +85,7 @@ function validateRecipient (txParams) {
|
||||
return txParams
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
@returns an {array} of states that can be considered final
|
||||
*/
|
||||
function getFinalStates () {
|
||||
|
@ -186,7 +186,7 @@ class PendingTransactionTracker extends EventEmitter {
|
||||
this.emit('tx:warning', txMeta, err)
|
||||
}
|
||||
}
|
||||
/**
|
||||
/**
|
||||
checks to see if if the tx's nonce has been used by another transaction
|
||||
@param txMeta {Object} - txMeta object
|
||||
@emits tx:dropped
|
||||
@ -198,7 +198,7 @@ class PendingTransactionTracker extends EventEmitter {
|
||||
const nextNonce = await this.query.getTransactionCount(from)
|
||||
const { blockNumber } = await this.query.getTransactionByHash(hash) || {}
|
||||
if (!blockNumber && parseInt(nextNonce) > parseInt(nonce)) {
|
||||
return true
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ class TransactionStateManager extends EventEmitter {
|
||||
this.store = new ObservableStore(
|
||||
extend({
|
||||
transactions: [],
|
||||
}, initState))
|
||||
}, initState))
|
||||
this.txHistoryLimit = txHistoryLimit
|
||||
this.getNetwork = getNetwork
|
||||
}
|
||||
@ -245,7 +245,7 @@ class TransactionStateManager extends EventEmitter {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
@param opts {object} - an object of fields to search for eg:<br>
|
||||
let <code>thingsToLookFor = {<br>
|
||||
to: '0x0..',<br>
|
||||
@ -403,9 +403,9 @@ class TransactionStateManager extends EventEmitter {
|
||||
// Update state
|
||||
this._saveTxList(otherAccountTxs)
|
||||
}
|
||||
//
|
||||
// PRIVATE METHODS
|
||||
//
|
||||
//
|
||||
// PRIVATE METHODS
|
||||
//
|
||||
|
||||
// STATUS METHODS
|
||||
// statuses:
|
||||
|
@ -14,23 +14,23 @@ class EdgeEncryptor {
|
||||
* @returns {Promise<string>} Promise resolving to an object with ciphertext
|
||||
*/
|
||||
encrypt (password, dataObject) {
|
||||
var salt = this._generateSalt()
|
||||
return this._keyFromPassword(password, salt)
|
||||
.then(function (key) {
|
||||
var data = JSON.stringify(dataObject)
|
||||
var dataBuffer = Unibabel.utf8ToBuffer(data)
|
||||
var vector = global.crypto.getRandomValues(new Uint8Array(16))
|
||||
var resultbuffer = asmcrypto.AES_GCM.encrypt(dataBuffer, key, vector)
|
||||
var salt = this._generateSalt()
|
||||
return this._keyFromPassword(password, salt)
|
||||
.then(function (key) {
|
||||
var data = JSON.stringify(dataObject)
|
||||
var dataBuffer = Unibabel.utf8ToBuffer(data)
|
||||
var vector = global.crypto.getRandomValues(new Uint8Array(16))
|
||||
var resultbuffer = asmcrypto.AES_GCM.encrypt(dataBuffer, key, vector)
|
||||
|
||||
var buffer = new Uint8Array(resultbuffer)
|
||||
var vectorStr = Unibabel.bufferToBase64(vector)
|
||||
var vaultStr = Unibabel.bufferToBase64(buffer)
|
||||
return JSON.stringify({
|
||||
data: vaultStr,
|
||||
iv: vectorStr,
|
||||
salt: salt,
|
||||
})
|
||||
})
|
||||
var buffer = new Uint8Array(resultbuffer)
|
||||
var vectorStr = Unibabel.bufferToBase64(vector)
|
||||
var vaultStr = Unibabel.bufferToBase64(buffer)
|
||||
return JSON.stringify({
|
||||
data: vaultStr,
|
||||
iv: vectorStr,
|
||||
salt: salt,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@ -41,25 +41,25 @@ class EdgeEncryptor {
|
||||
* @returns {Promise<Object>} Promise resolving to copy of decrypted object
|
||||
*/
|
||||
decrypt (password, text) {
|
||||
const payload = JSON.parse(text)
|
||||
const salt = payload.salt
|
||||
return this._keyFromPassword(password, salt)
|
||||
.then(function (key) {
|
||||
const encryptedData = Unibabel.base64ToBuffer(payload.data)
|
||||
const vector = Unibabel.base64ToBuffer(payload.iv)
|
||||
return new Promise((resolve, reject) => {
|
||||
var result
|
||||
try {
|
||||
result = asmcrypto.AES_GCM.decrypt(encryptedData, key, vector)
|
||||
} catch (err) {
|
||||
return reject(new Error('Incorrect password'))
|
||||
}
|
||||
const decryptedData = new Uint8Array(result)
|
||||
const decryptedStr = Unibabel.bufferToUtf8(decryptedData)
|
||||
const decryptedObj = JSON.parse(decryptedStr)
|
||||
resolve(decryptedObj)
|
||||
})
|
||||
})
|
||||
const payload = JSON.parse(text)
|
||||
const salt = payload.salt
|
||||
return this._keyFromPassword(password, salt)
|
||||
.then(function (key) {
|
||||
const encryptedData = Unibabel.base64ToBuffer(payload.data)
|
||||
const vector = Unibabel.base64ToBuffer(payload.iv)
|
||||
return new Promise((resolve, reject) => {
|
||||
var result
|
||||
try {
|
||||
result = asmcrypto.AES_GCM.decrypt(encryptedData, key, vector)
|
||||
} catch (err) {
|
||||
return reject(new Error('Incorrect password'))
|
||||
}
|
||||
const decryptedData = new Uint8Array(result)
|
||||
const decryptedStr = Unibabel.bufferToUtf8(decryptedData)
|
||||
const decryptedObj = JSON.parse(decryptedStr)
|
||||
resolve(decryptedObj)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@ -72,12 +72,14 @@ class EdgeEncryptor {
|
||||
*/
|
||||
_keyFromPassword (password, salt) {
|
||||
|
||||
var passBuffer = Unibabel.utf8ToBuffer(password)
|
||||
var saltBuffer = Unibabel.base64ToBuffer(salt)
|
||||
return new Promise((resolve) => {
|
||||
var key = asmcrypto.PBKDF2_HMAC_SHA256.bytes(passBuffer, saltBuffer, 10000)
|
||||
resolve(key)
|
||||
})
|
||||
var passBuffer = Unibabel.utf8ToBuffer(password)
|
||||
var saltBuffer = Unibabel.base64ToBuffer(salt)
|
||||
const iterations = 10000
|
||||
const length = 32 // SHA256 hash size
|
||||
return new Promise((resolve) => {
|
||||
var key = asmcrypto.Pbkdf2HmacSha256(passBuffer, saltBuffer, iterations, length)
|
||||
resolve(key)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@ -87,10 +89,10 @@ class EdgeEncryptor {
|
||||
* @returns {string} Randomized base64 encoded data
|
||||
*/
|
||||
_generateSalt (byteCount = 32) {
|
||||
var view = new Uint8Array(byteCount)
|
||||
global.crypto.getRandomValues(view)
|
||||
var b64encoded = btoa(String.fromCharCode.apply(null, view))
|
||||
return b64encoded
|
||||
var view = new Uint8Array(byteCount)
|
||||
global.crypto.getRandomValues(view)
|
||||
var b64encoded = btoa(String.fromCharCode.apply(null, view))
|
||||
return b64encoded
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,36 @@
|
||||
/*global Web3*/
|
||||
|
||||
|
||||
// need to make sure we aren't affected by overlapping namespaces
|
||||
// and that we dont affect the app with our namespace
|
||||
// mostly a fix for web3's BigNumber if AMD's "define" is defined...
|
||||
let __define
|
||||
|
||||
/**
|
||||
* Caches reference to global define object and deletes it to
|
||||
* avoid conflicts with other global define objects, such as
|
||||
* AMD's define function
|
||||
*/
|
||||
const cleanContextForImports = () => {
|
||||
__define = global.define
|
||||
try {
|
||||
global.define = undefined
|
||||
} catch (_) {
|
||||
console.warn('MetaMask - global.define could not be deleted.')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores global define object from cached reference
|
||||
*/
|
||||
const restoreContextAfterImports = () => {
|
||||
try {
|
||||
global.define = __define
|
||||
} catch (_) {
|
||||
console.warn('MetaMask - global.define could not be overwritten.')
|
||||
}
|
||||
}
|
||||
|
||||
cleanContextForImports()
|
||||
require('web3/dist/web3.min.js')
|
||||
const log = require('loglevel')
|
||||
@ -46,6 +78,20 @@ inpageProvider.enable = function ({ force } = {}) {
|
||||
// this will be default true so it does not break any old apps.
|
||||
inpageProvider.autoRefreshOnNetworkChange = true
|
||||
|
||||
|
||||
// publicConfig isn't populated until we get a message from background.
|
||||
// Using this getter will ensure the state is available
|
||||
const getPublicConfigWhenReady = async () => {
|
||||
const store = inpageProvider.publicConfigStore
|
||||
let state = store.getState()
|
||||
// if state is missing, wait for first update
|
||||
if (!state.networkVersion) {
|
||||
state = await new Promise(resolve => store.once('update', resolve))
|
||||
console.log('new state', state)
|
||||
}
|
||||
return state
|
||||
}
|
||||
|
||||
// add metamask-specific convenience methods
|
||||
inpageProvider._metamask = new Proxy({
|
||||
/**
|
||||
@ -87,21 +133,8 @@ inpageProvider._metamask = new Proxy({
|
||||
},
|
||||
})
|
||||
|
||||
// publicConfig isn't populated until we get a message from background.
|
||||
// Using this getter will ensure the state is available
|
||||
async function getPublicConfigWhenReady () {
|
||||
const store = inpageProvider.publicConfigStore
|
||||
let state = store.getState()
|
||||
// if state is missing, wait for first update
|
||||
if (!state.networkVersion) {
|
||||
state = await new Promise(resolve => store.once('update', resolve))
|
||||
console.log('new state', state)
|
||||
}
|
||||
return state
|
||||
}
|
||||
|
||||
// Work around for web3@1.0 deleting the bound `sendAsync` but not the unbound
|
||||
// `sendAsync` method on the prototype, causing `this` reference issues with drizzle
|
||||
// `sendAsync` method on the prototype, causing `this` reference issues
|
||||
const proxiedInpageProvider = new Proxy(inpageProvider, {
|
||||
// straight up lie that we deleted the property so that it doesnt
|
||||
// throw an error in strict mode
|
||||
@ -161,33 +194,3 @@ inpageProvider.publicConfigStore.subscribe(function (state) {
|
||||
window.postMessage('onboardingcomplete', '*')
|
||||
}
|
||||
})
|
||||
|
||||
// need to make sure we aren't affected by overlapping namespaces
|
||||
// and that we dont affect the app with our namespace
|
||||
// mostly a fix for web3's BigNumber if AMD's "define" is defined...
|
||||
let __define
|
||||
|
||||
/**
|
||||
* Caches reference to global define object and deletes it to
|
||||
* avoid conflicts with other global define objects, such as
|
||||
* AMD's define function
|
||||
*/
|
||||
function cleanContextForImports () {
|
||||
__define = global.define
|
||||
try {
|
||||
global.define = undefined
|
||||
} catch (_) {
|
||||
console.warn('MetaMask - global.define could not be deleted.')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores global define object from cached reference
|
||||
*/
|
||||
function restoreContextAfterImports () {
|
||||
try {
|
||||
global.define = __define
|
||||
} catch (_) {
|
||||
console.warn('MetaMask - global.define could not be overwritten.')
|
||||
}
|
||||
}
|
||||
|
@ -183,23 +183,23 @@ class AccountTracker {
|
||||
|
||||
switch (currentNetwork) {
|
||||
case MAINNET_CODE:
|
||||
await this._updateAccountsViaBalanceChecker(addresses, SINGLE_CALL_BALANCES_ADDRESS)
|
||||
break
|
||||
await this._updateAccountsViaBalanceChecker(addresses, SINGLE_CALL_BALANCES_ADDRESS)
|
||||
break
|
||||
|
||||
case RINKEYBY_CODE:
|
||||
await this._updateAccountsViaBalanceChecker(addresses, SINGLE_CALL_BALANCES_ADDRESS_RINKEBY)
|
||||
break
|
||||
await this._updateAccountsViaBalanceChecker(addresses, SINGLE_CALL_BALANCES_ADDRESS_RINKEBY)
|
||||
break
|
||||
|
||||
case ROPSTEN_CODE:
|
||||
await this._updateAccountsViaBalanceChecker(addresses, SINGLE_CALL_BALANCES_ADDRESS_ROPSTEN)
|
||||
break
|
||||
await this._updateAccountsViaBalanceChecker(addresses, SINGLE_CALL_BALANCES_ADDRESS_ROPSTEN)
|
||||
break
|
||||
|
||||
case KOVAN_CODE:
|
||||
await this._updateAccountsViaBalanceChecker(addresses, SINGLE_CALL_BALANCES_ADDRESS_KOVAN)
|
||||
break
|
||||
await this._updateAccountsViaBalanceChecker(addresses, SINGLE_CALL_BALANCES_ADDRESS_KOVAN)
|
||||
break
|
||||
|
||||
default:
|
||||
await Promise.all(addresses.map(this._updateAccount.bind(this)))
|
||||
await Promise.all(addresses.map(this._updateAccount.bind(this)))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,11 +15,11 @@ function backEndMetaMetricsEvent (metaMaskState, eventData) {
|
||||
const stateEventData = getMetaMetricState({ metamask: metaMaskState })
|
||||
|
||||
if (stateEventData.participateInMetaMetrics) {
|
||||
sendMetaMetricsEvent({
|
||||
...stateEventData,
|
||||
...eventData,
|
||||
url: METAMETRICS_TRACKING_URL + '/backend',
|
||||
})
|
||||
sendMetaMetricsEvent({
|
||||
...stateEventData,
|
||||
...eventData,
|
||||
url: METAMETRICS_TRACKING_URL + '/backend',
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,13 +1,6 @@
|
||||
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) {
|
||||
@ -22,3 +15,9 @@ class AsyncWritableStream extends WritableStream {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function createStreamSink (asyncWriteFn, _opts) {
|
||||
return new AsyncWritableStream(asyncWriteFn, _opts)
|
||||
}
|
||||
|
||||
module.exports = createStreamSink
|
||||
|
@ -51,6 +51,8 @@ function setupEnsIpfsResolver ({ provider }) {
|
||||
}
|
||||
} else if (type === 'swarm-ns') {
|
||||
url = `https://swarm-gateways.net/bzz:/${hash}${path}${search || ''}`
|
||||
} else if (type === 'onion' || type === 'onion3') {
|
||||
url = `http://${hash}.onion${path}${search || ''}`
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn(err)
|
||||
|
@ -61,7 +61,7 @@ module.exports = class MessageManager extends EventEmitter {
|
||||
*/
|
||||
getUnapprovedMsgs () {
|
||||
return this.messages.filter(msg => msg.status === 'unapproved')
|
||||
.reduce((result, msg) => { result[msg.id] = msg; return result }, {})
|
||||
.reduce((result, msg) => { result[msg.id] = msg; return result }, {})
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -64,7 +64,7 @@ module.exports = class PersonalMessageManager extends EventEmitter {
|
||||
*/
|
||||
getUnapprovedMsgs () {
|
||||
return this.messages.filter(msg => msg.status === 'unapproved')
|
||||
.reduce((result, msg) => { result[msg.id] = msg; return result }, {})
|
||||
.reduce((result, msg) => { result[msg.id] = msg; return result }, {})
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -28,6 +28,7 @@ const PreferencesController = require('./controllers/preferences')
|
||||
const AppStateController = require('./controllers/app-state')
|
||||
const InfuraController = require('./controllers/infura')
|
||||
const CachedBalancesController = require('./controllers/cached-balances')
|
||||
const OnboardingController = require('./controllers/onboarding')
|
||||
const RecentBlocksController = require('./controllers/recent-blocks')
|
||||
const MessageManager = require('./lib/message-manager')
|
||||
const PersonalMessageManager = require('./lib/personal-message-manager')
|
||||
@ -68,7 +69,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
* @constructor
|
||||
* @param {Object} opts
|
||||
*/
|
||||
constructor (opts) {
|
||||
constructor (opts) {
|
||||
super()
|
||||
|
||||
this.defaultMaxListeners = 20
|
||||
@ -158,6 +159,10 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
initState: initState.CachedBalancesController,
|
||||
})
|
||||
|
||||
this.onboardingController = new OnboardingController({
|
||||
initState: initState.OnboardingController,
|
||||
})
|
||||
|
||||
// ensure accountTracker updates balances after network change
|
||||
this.networkController.on('networkDidChange', () => {
|
||||
this.accountTracker._updateAccounts()
|
||||
@ -262,6 +267,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
NetworkController: this.networkController.store,
|
||||
InfuraController: this.infuraController.store,
|
||||
CachedBalancesController: this.cachedBalancesController.store,
|
||||
OnboardingController: this.onboardingController.store,
|
||||
})
|
||||
|
||||
this.memStore = new ComposableObservableStore(null, {
|
||||
@ -283,6 +289,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
ShapeshiftController: this.shapeshiftController,
|
||||
InfuraController: this.infuraController.store,
|
||||
ProviderApprovalController: this.providerApprovalController.store,
|
||||
OnboardingController: this.onboardingController.store,
|
||||
})
|
||||
this.memStore.subscribe(this.sendUpdate.bind(this))
|
||||
}
|
||||
@ -362,9 +369,9 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
return publicConfigStore
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// EXPOSED TO THE UI SUBSYSTEM
|
||||
//=============================================================================
|
||||
//=============================================================================
|
||||
// EXPOSED TO THE UI SUBSYSTEM
|
||||
//=============================================================================
|
||||
|
||||
/**
|
||||
* The metamask-state of the various controllers, made available to the UI
|
||||
@ -398,6 +405,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
const txController = this.txController
|
||||
const networkController = this.networkController
|
||||
const providerApprovalController = this.providerApprovalController
|
||||
const onboardingController = this.onboardingController
|
||||
|
||||
return {
|
||||
// etc
|
||||
@ -420,9 +428,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
|
||||
// primary HD keyring management
|
||||
addNewAccount: nodeify(this.addNewAccount, this),
|
||||
placeSeedWords: this.placeSeedWords.bind(this),
|
||||
verifySeedPhrase: nodeify(this.verifySeedPhrase, this),
|
||||
clearSeedWordCache: this.clearSeedWordCache.bind(this),
|
||||
resetAccount: nodeify(this.resetAccount, this),
|
||||
removeAccount: nodeify(this.removeAccount, this),
|
||||
importAccountWithStrategy: nodeify(this.importAccountWithStrategy, this),
|
||||
@ -454,15 +460,16 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
setAccountLabel: nodeify(preferencesController.setAccountLabel, preferencesController),
|
||||
setFeatureFlag: nodeify(preferencesController.setFeatureFlag, preferencesController),
|
||||
setPreference: nodeify(preferencesController.setPreference, preferencesController),
|
||||
completeUiMigration: nodeify(preferencesController.completeUiMigration, preferencesController),
|
||||
completeOnboarding: nodeify(preferencesController.completeOnboarding, preferencesController),
|
||||
addKnownMethodData: nodeify(preferencesController.addKnownMethodData, preferencesController),
|
||||
unsetMigratedPrivacyMode: nodeify(preferencesController.unsetMigratedPrivacyMode, preferencesController),
|
||||
|
||||
// BlacklistController
|
||||
whitelistPhishingDomain: this.whitelistPhishingDomain.bind(this),
|
||||
|
||||
// AddressController
|
||||
setAddressBook: this.addressBookController.set.bind(this.addressBookController),
|
||||
removeFromAddressBook: this.addressBookController.delete.bind(this.addressBookController),
|
||||
|
||||
// AppStateController
|
||||
setLastActiveTime: nodeify(this.appStateController.setLastActiveTime, this.appStateController),
|
||||
@ -500,14 +507,18 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
// provider approval
|
||||
approveProviderRequestByOrigin: providerApprovalController.approveProviderRequestByOrigin.bind(providerApprovalController),
|
||||
rejectProviderRequestByOrigin: providerApprovalController.rejectProviderRequestByOrigin.bind(providerApprovalController),
|
||||
forceApproveProviderRequestByOrigin: providerApprovalController.forceApproveProviderRequestByOrigin.bind(providerApprovalController),
|
||||
clearApprovedOrigins: providerApprovalController.clearApprovedOrigins.bind(providerApprovalController),
|
||||
|
||||
// onboarding controller
|
||||
setSeedPhraseBackedUp: nodeify(onboardingController.setSeedPhraseBackedUp, onboardingController),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//=============================================================================
|
||||
// VAULT / KEYRING RELATED METHODS
|
||||
//=============================================================================
|
||||
//=============================================================================
|
||||
// VAULT / KEYRING RELATED METHODS
|
||||
//=============================================================================
|
||||
|
||||
/**
|
||||
* Creates a new Vault and create a new keychain.
|
||||
@ -617,7 +628,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
* with the mobile client for syncing purposes
|
||||
* @returns Promise<Object> Parts of the state that we want to syncx
|
||||
*/
|
||||
async fetchInfoToSync () {
|
||||
async fetchInfoToSync () {
|
||||
// Preferences
|
||||
const {
|
||||
accountTokens,
|
||||
@ -746,14 +757,14 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
const keyring = await this.getKeyringForDevice(deviceName, hdPath)
|
||||
let accounts = []
|
||||
switch (page) {
|
||||
case -1:
|
||||
accounts = await keyring.getPreviousPage()
|
||||
break
|
||||
case 1:
|
||||
accounts = await keyring.getNextPage()
|
||||
break
|
||||
default:
|
||||
accounts = await keyring.getFirstPage()
|
||||
case -1:
|
||||
accounts = await keyring.getPreviousPage()
|
||||
break
|
||||
case 1:
|
||||
accounts = await keyring.getNextPage()
|
||||
break
|
||||
default:
|
||||
accounts = await keyring.getFirstPage()
|
||||
}
|
||||
|
||||
// Merge with existing accounts
|
||||
@ -810,7 +821,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
|
||||
const { identities } = this.preferencesController.store.getState()
|
||||
return { ...keyState, identities }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
@ -845,26 +856,6 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
return {...keyState, identities}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the current vault's seed words to the UI's state tree.
|
||||
*
|
||||
* Used when creating a first vault, to allow confirmation.
|
||||
* Also used when revealing the seed words in the confirmation view.
|
||||
*
|
||||
* @param {Function} cb - A callback called on completion.
|
||||
*/
|
||||
placeSeedWords (cb) {
|
||||
|
||||
this.verifySeedPhrase()
|
||||
.then((seedWords) => {
|
||||
this.preferencesController.setSeedWords(seedWords)
|
||||
return cb(null, seedWords)
|
||||
})
|
||||
.catch((err) => {
|
||||
return cb(err)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the validity of the current vault's seed phrase.
|
||||
*
|
||||
@ -898,18 +889,6 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the primary account seed phrase from the UI's state tree.
|
||||
*
|
||||
* The seed phrase remains available in the background process.
|
||||
*
|
||||
* @param {function} cb Callback function called with the current address.
|
||||
*/
|
||||
clearSeedWordCache (cb) {
|
||||
this.preferencesController.setSeedWords(null)
|
||||
cb(null, this.preferencesController.getSelectedAddress())
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the transaction history, to allow users to force-reset their nonces.
|
||||
* Mostly used in development environments, when networks are restarted with
|
||||
@ -1009,16 +988,16 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
// sets the status op the message to 'approved'
|
||||
// and removes the metamaskId for signing
|
||||
return this.messageManager.approveMessage(msgParams)
|
||||
.then((cleanMsgParams) => {
|
||||
.then((cleanMsgParams) => {
|
||||
// signs the message
|
||||
return this.keyringController.signMessage(cleanMsgParams)
|
||||
})
|
||||
.then((rawSig) => {
|
||||
return this.keyringController.signMessage(cleanMsgParams)
|
||||
})
|
||||
.then((rawSig) => {
|
||||
// tells the listener that the message has been signed
|
||||
// and can be returned to the dapp
|
||||
this.messageManager.setMsgStatusSigned(msgId, rawSig)
|
||||
return this.getState()
|
||||
})
|
||||
this.messageManager.setMsgStatusSigned(msgId, rawSig)
|
||||
return this.getState()
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1067,16 +1046,16 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
// sets the status op the message to 'approved'
|
||||
// and removes the metamaskId for signing
|
||||
return this.personalMessageManager.approveMessage(msgParams)
|
||||
.then((cleanMsgParams) => {
|
||||
.then((cleanMsgParams) => {
|
||||
// signs the message
|
||||
return this.keyringController.signPersonalMessage(cleanMsgParams)
|
||||
})
|
||||
.then((rawSig) => {
|
||||
return this.keyringController.signPersonalMessage(cleanMsgParams)
|
||||
})
|
||||
.then((rawSig) => {
|
||||
// tells the listener that the message has been signed
|
||||
// and can be returned to the dapp
|
||||
this.personalMessageManager.setMsgStatusSigned(msgId, rawSig)
|
||||
return this.getState()
|
||||
})
|
||||
this.personalMessageManager.setMsgStatusSigned(msgId, rawSig)
|
||||
return this.getState()
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1176,7 +1155,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
restoreOldVaultAccounts (migratorOutput) {
|
||||
const { serialized } = migratorOutput
|
||||
return this.keyringController.restoreKeyring(serialized)
|
||||
.then(() => migratorOutput)
|
||||
.then(() => migratorOutput)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1219,9 +1198,9 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
})
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// END (VAULT / KEYRING RELATED METHODS)
|
||||
//=============================================================================
|
||||
//=============================================================================
|
||||
// END (VAULT / KEYRING RELATED METHODS)
|
||||
//=============================================================================
|
||||
|
||||
/**
|
||||
* Allows a user to try to speed up a transaction by retrying it
|
||||
@ -1270,9 +1249,9 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
})
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// PASSWORD MANAGEMENT
|
||||
//=============================================================================
|
||||
//=============================================================================
|
||||
// PASSWORD MANAGEMENT
|
||||
//=============================================================================
|
||||
|
||||
/**
|
||||
* Allows a user to begin the seed phrase recovery process.
|
||||
@ -1294,9 +1273,9 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
cb()
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// SETUP
|
||||
//=============================================================================
|
||||
//=============================================================================
|
||||
// SETUP
|
||||
//=============================================================================
|
||||
|
||||
/**
|
||||
* Used to create a multiplexed stream for connecting to an untrusted context
|
||||
@ -1319,6 +1298,8 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
const publicApi = this.setupPublicApi(mux.createStream('publicApi'), originDomain)
|
||||
this.setupProviderConnection(mux.createStream('provider'), originDomain, publicApi)
|
||||
this.setupPublicConfig(mux.createStream('publicConfig'), originDomain)
|
||||
|
||||
this.providerApprovalController.on(`forceResolvedRequest:${originDomain}`, publicApi.forceReloadSite)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1392,6 +1373,32 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
* @param {string} origin - The URI of the requesting resource.
|
||||
*/
|
||||
setupProviderConnection (outStream, origin, publicApi) {
|
||||
const getSiteMetadata = publicApi && publicApi.getSiteMetadata
|
||||
const engine = this.setupProviderEngine(origin, getSiteMetadata)
|
||||
|
||||
// setup connection
|
||||
const providerStream = createEngineStream({ engine })
|
||||
|
||||
pump(
|
||||
outStream,
|
||||
providerStream,
|
||||
outStream,
|
||||
(err) => {
|
||||
// cleanup filter polyfill middleware
|
||||
engine._middleware.forEach((mid) => {
|
||||
if (mid.destroy && typeof mid.destroy === 'function') {
|
||||
mid.destroy()
|
||||
}
|
||||
})
|
||||
if (err) log.error(err)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A method for creating a provider that is safely restricted for the requesting domain.
|
||||
**/
|
||||
setupProviderEngine (origin, getSiteMetadata) {
|
||||
// setup json rpc engine stack
|
||||
const engine = new RpcEngine()
|
||||
const provider = this.provider
|
||||
@ -1399,6 +1406,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
|
||||
// create filter polyfill middleware
|
||||
const filterMiddleware = createFilterMiddleware({ provider, blockTracker })
|
||||
|
||||
// create subscription polyfill middleware
|
||||
const subscriptionManager = createSubscriptionManager({ provider, blockTracker })
|
||||
subscriptionManager.events.on('notification', (message) => engine.emit('notification', message))
|
||||
@ -1414,24 +1422,11 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
// requestAccounts
|
||||
engine.push(this.providerApprovalController.createMiddleware({
|
||||
origin,
|
||||
getSiteMetadata: publicApi && publicApi.getSiteMetadata,
|
||||
getSiteMetadata,
|
||||
}))
|
||||
// forward to metamask primary provider
|
||||
engine.push(providerAsMiddleware(provider))
|
||||
|
||||
// setup connection
|
||||
const providerStream = createEngineStream({ engine })
|
||||
|
||||
pump(
|
||||
outStream,
|
||||
providerStream,
|
||||
outStream,
|
||||
(err) => {
|
||||
// cleanup filter polyfill middleware
|
||||
filterMiddleware.destroy()
|
||||
if (err) log.error(err)
|
||||
}
|
||||
)
|
||||
return engine
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1485,6 +1480,10 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
|
||||
const publicApi = {
|
||||
// wrap with an await remote
|
||||
forceReloadSite: async () => {
|
||||
const remote = await getRemote()
|
||||
return await pify(remote.forceReloadSite)()
|
||||
},
|
||||
getSiteMetadata: async () => {
|
||||
const remote = await getRemote()
|
||||
return await pify(remote.getSiteMetadata)()
|
||||
@ -1551,13 +1550,13 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
return GWEI_BN
|
||||
}
|
||||
return block.gasPrices
|
||||
.map(hexPrefix => hexPrefix.substr(2))
|
||||
.map(hex => new BN(hex, 16))
|
||||
.sort((a, b) => {
|
||||
return a.gt(b) ? 1 : -1
|
||||
})[0]
|
||||
.map(hexPrefix => hexPrefix.substr(2))
|
||||
.map(hex => new BN(hex, 16))
|
||||
.sort((a, b) => {
|
||||
return a.gt(b) ? 1 : -1
|
||||
})[0]
|
||||
})
|
||||
.map(number => number.div(GWEI_BN).toNumber())
|
||||
.map(number => number.div(GWEI_BN).toNumber())
|
||||
|
||||
const percentileNum = percentile(65, lowestPrices)
|
||||
const percentileNumBn = new BN(percentileNum)
|
||||
@ -1577,9 +1576,9 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
return pendingNonce
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// CONFIG
|
||||
//=============================================================================
|
||||
//=============================================================================
|
||||
// CONFIG
|
||||
//=============================================================================
|
||||
|
||||
// Log blocks
|
||||
|
||||
@ -1774,7 +1773,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
this.tokenRatesController.isActive = active
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Creates RPC engine middleware for processing eth_signTypedData requests
|
||||
*
|
||||
* @param {Object} req - request object
|
||||
@ -1798,3 +1797,4 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
return this.keyringController.setLocked()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -43,9 +43,9 @@ function transformState (state) {
|
||||
const newHistory = (
|
||||
txStateHistoryHelper.migrateFromSnapshotsToDiffs(txMeta.history)
|
||||
// remove empty diffs
|
||||
.filter((entry) => {
|
||||
return !Array.isArray(entry) || entry.length > 0
|
||||
})
|
||||
.filter((entry) => {
|
||||
return !Array.isArray(entry) || entry.length > 0
|
||||
})
|
||||
)
|
||||
txMeta.history = newHistory
|
||||
return txMeta
|
||||
|
@ -38,13 +38,13 @@ function transformState (state) {
|
||||
if (txMeta.status !== 'submitted') return txMeta
|
||||
|
||||
const confirmedTxs = txList.filter((tx) => tx.status === 'confirmed')
|
||||
.filter((tx) => tx.txParams.from === txMeta.txParams.from)
|
||||
.filter((tx) => tx.metamaskNetworkId.from === txMeta.metamaskNetworkId.from)
|
||||
.filter((tx) => tx.txParams.from === txMeta.txParams.from)
|
||||
.filter((tx) => tx.metamaskNetworkId.from === txMeta.metamaskNetworkId.from)
|
||||
const highestConfirmedNonce = getHighestNonce(confirmedTxs)
|
||||
|
||||
const pendingTxs = txList.filter((tx) => tx.status === 'submitted')
|
||||
.filter((tx) => tx.txParams.from === txMeta.txParams.from)
|
||||
.filter((tx) => tx.metamaskNetworkId.from === txMeta.metamaskNetworkId.from)
|
||||
.filter((tx) => tx.txParams.from === txMeta.txParams.from)
|
||||
.filter((tx) => tx.metamaskNetworkId.from === txMeta.metamaskNetworkId.from)
|
||||
const highestContinuousNonce = getHighestContinuousFrom(pendingTxs, highestConfirmedNonce)
|
||||
|
||||
const maxNonce = Math.max(highestContinuousNonce, highestConfirmedNonce)
|
||||
@ -78,7 +78,7 @@ function getHighestContinuousFrom (txList, startPoint) {
|
||||
|
||||
function getHighestNonce (txList) {
|
||||
const nonces = txList.map((txMeta) => {
|
||||
const nonce = txMeta.txParams.nonce
|
||||
const nonce = txMeta.txParams.nonce
|
||||
return parseInt(nonce || '0x0', 16)
|
||||
})
|
||||
const highestNonce = Math.max.apply(null, nonces)
|
||||
|
@ -32,7 +32,7 @@ function transformState (state) {
|
||||
txMeta.status === 'unapproved' &&
|
||||
txMeta.txParams &&
|
||||
txMeta.txParams.from
|
||||
) {
|
||||
) {
|
||||
txMeta.txParams.from = txMeta.txParams.from.toLowerCase()
|
||||
}
|
||||
return txMeta
|
||||
|
@ -2,14 +2,14 @@
|
||||
const version = 31
|
||||
const clone = require('clone')
|
||||
|
||||
/*
|
||||
/*
|
||||
* The purpose of this migration is to properly set the completedOnboarding flag baesd on the state
|
||||
* of the KeyringController.
|
||||
*/
|
||||
module.exports = {
|
||||
version,
|
||||
|
||||
migrate: async function (originalVersionedData) {
|
||||
migrate: async function (originalVersionedData) {
|
||||
const versionedData = clone(originalVersionedData)
|
||||
versionedData.meta.version = version
|
||||
const state = versionedData.data
|
||||
@ -19,13 +19,13 @@ module.exports = {
|
||||
},
|
||||
}
|
||||
|
||||
function transformState (state) {
|
||||
function transformState (state) {
|
||||
const { KeyringController, PreferencesController } = state
|
||||
|
||||
if (KeyringController && PreferencesController) {
|
||||
if (KeyringController && PreferencesController) {
|
||||
const { vault } = KeyringController
|
||||
PreferencesController.completedOnboarding = Boolean(vault)
|
||||
}
|
||||
|
||||
return state
|
||||
return state
|
||||
}
|
||||
|
33
app/scripts/migrations/034.js
Normal file
@ -0,0 +1,33 @@
|
||||
const version = 34
|
||||
const clone = require('clone')
|
||||
|
||||
/**
|
||||
* The purpose of this migration is to enable the {@code privacyMode} feature flag and set the user as being migrated
|
||||
* if it was {@code false}.
|
||||
*/
|
||||
module.exports = {
|
||||
version,
|
||||
migrate: async function (originalVersionedData) {
|
||||
const versionedData = clone(originalVersionedData)
|
||||
versionedData.meta.version = version
|
||||
const state = versionedData.data
|
||||
versionedData.data = transformState(state)
|
||||
return versionedData
|
||||
},
|
||||
}
|
||||
|
||||
function transformState (state) {
|
||||
const { PreferencesController } = state
|
||||
|
||||
if (PreferencesController) {
|
||||
const featureFlags = PreferencesController.featureFlags || {}
|
||||
|
||||
if (!featureFlags.privacyMode && typeof PreferencesController.migratedPrivacyMode === 'undefined') {
|
||||
// Mark the state has being migrated and enable Privacy Mode
|
||||
PreferencesController.migratedPrivacyMode = true
|
||||
featureFlags.privacyMode = true
|
||||
}
|
||||
}
|
||||
|
||||
return state
|
||||
}
|
28
app/scripts/migrations/035.js
Normal file
@ -0,0 +1,28 @@
|
||||
// next version number
|
||||
const version = 35
|
||||
|
||||
/*
|
||||
|
||||
Removes the deprecated 'seedWords' state
|
||||
|
||||
*/
|
||||
|
||||
const clone = require('clone')
|
||||
|
||||
module.exports = {
|
||||
version,
|
||||
|
||||
migrate: async function (originalVersionedData) {
|
||||
const versionedData = clone(originalVersionedData)
|
||||
versionedData.meta.version = version
|
||||
versionedData.data = transformState(versionedData.data)
|
||||
return versionedData
|
||||
},
|
||||
}
|
||||
|
||||
function transformState (state) {
|
||||
if (state.PreferencesController && state.PreferencesController.seedWords !== undefined) {
|
||||
delete state.PreferencesController.seedWords
|
||||
}
|
||||
return state
|
||||
}
|
@ -37,11 +37,11 @@ function start () {
|
||||
|
||||
function setupControllerConnection (connectionStream, cb) {
|
||||
const eventEmitter = new EventEmitter()
|
||||
const accountManagerDnode = dnode({
|
||||
const metaMaskControllerDnode = dnode({
|
||||
sendUpdate (state) {
|
||||
eventEmitter.emit('update', state)
|
||||
},
|
||||
})
|
||||
connectionStream.pipe(accountManagerDnode).pipe(connectionStream)
|
||||
accountManagerDnode.once('remote', (accountManager) => cb(null, accountManager))
|
||||
connectionStream.pipe(metaMaskControllerDnode).pipe(connectionStream)
|
||||
metaMaskControllerDnode.once('remote', (backgroundConnection) => cb(null, backgroundConnection))
|
||||
}
|
||||
|
@ -68,7 +68,7 @@ class ExtensionPlatform {
|
||||
const nonce = parseInt(txMeta.txParams.nonce, 16)
|
||||
|
||||
const title = 'Confirmed transaction'
|
||||
const message = `Transaction ${nonce} confirmed! View on EtherScan`
|
||||
const message = `Transaction ${nonce} confirmed! View on Etherscan`
|
||||
this._showNotification(title, message, url)
|
||||
}
|
||||
|
||||
@ -84,20 +84,20 @@ class ExtensionPlatform {
|
||||
extension.notifications.create(
|
||||
url,
|
||||
{
|
||||
'type': 'basic',
|
||||
'title': title,
|
||||
'iconUrl': extension.extension.getURL('../../images/icon-64.png'),
|
||||
'message': message,
|
||||
'type': 'basic',
|
||||
'title': title,
|
||||
'iconUrl': extension.extension.getURL('../../images/icon-64.png'),
|
||||
'message': message,
|
||||
})
|
||||
}
|
||||
|
||||
_subscribeToNotificationClicked () {
|
||||
if (!extension.notifications.onClicked.hasListener(this._viewOnEtherScan)) {
|
||||
extension.notifications.onClicked.addListener(this._viewOnEtherScan)
|
||||
if (!extension.notifications.onClicked.hasListener(this._viewOnEtherscan)) {
|
||||
extension.notifications.onClicked.addListener(this._viewOnEtherscan)
|
||||
}
|
||||
}
|
||||
|
||||
_viewOnEtherScan (txId) {
|
||||
_viewOnEtherscan (txId) {
|
||||
if (txId.startsWith('http://')) {
|
||||
extension.tabs.create({ url: txId })
|
||||
}
|
||||
|
@ -1,77 +0,0 @@
|
||||
const {EventEmitter} = require('events')
|
||||
const async = require('async')
|
||||
const Dnode = require('dnode')
|
||||
const Eth = require('ethjs')
|
||||
const EthQuery = require('eth-query')
|
||||
const launchMetamaskUi = require('../../ui')
|
||||
const StreamProvider = require('web3-stream-provider')
|
||||
const {setupMultiplex} = require('./lib/stream-utils.js')
|
||||
|
||||
module.exports = initializePopup
|
||||
|
||||
/**
|
||||
* Asynchronously initializes the MetaMask popup UI
|
||||
*
|
||||
* @param {{ container: Element, connectionStream: * }} config Popup configuration object
|
||||
* @param {Function} cb Called when initialization is complete
|
||||
*/
|
||||
function initializePopup ({ container, connectionStream }, cb) {
|
||||
// setup app
|
||||
async.waterfall([
|
||||
(cb) => connectToAccountManager(connectionStream, cb),
|
||||
(accountManager, cb) => launchMetamaskUi({ container, accountManager }, cb),
|
||||
], cb)
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes streamed connections to background scripts and a Web3 provider
|
||||
*
|
||||
* @param {PortDuplexStream} connectionStream PortStream instance establishing a background connection
|
||||
* @param {Function} cb Called when controller connection is established
|
||||
*/
|
||||
function connectToAccountManager (connectionStream, cb) {
|
||||
// setup communication with background
|
||||
// setup multiplexing
|
||||
const mx = setupMultiplex(connectionStream)
|
||||
// connect features
|
||||
setupControllerConnection(mx.createStream('controller'), cb)
|
||||
setupWeb3Connection(mx.createStream('provider'))
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes a streamed connection to a Web3 provider
|
||||
*
|
||||
* @param {PortDuplexStream} connectionStream PortStream instance establishing a background connection
|
||||
*/
|
||||
function setupWeb3Connection (connectionStream) {
|
||||
const providerStream = new StreamProvider()
|
||||
providerStream.pipe(connectionStream).pipe(providerStream)
|
||||
connectionStream.on('error', console.error.bind(console))
|
||||
providerStream.on('error', console.error.bind(console))
|
||||
global.ethereumProvider = providerStream
|
||||
global.ethQuery = new EthQuery(providerStream)
|
||||
global.eth = new Eth(providerStream)
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes a streamed connection to the background account manager
|
||||
*
|
||||
* @param {PortDuplexStream} connectionStream PortStream instance establishing a background connection
|
||||
* @param {Function} cb Called when the remote account manager connection is established
|
||||
*/
|
||||
function setupControllerConnection (connectionStream, cb) {
|
||||
// this is a really sneaky way of adding EventEmitter api
|
||||
// to a bi-directional dnode instance
|
||||
const eventEmitter = new EventEmitter()
|
||||
const accountManagerDnode = Dnode({
|
||||
sendUpdate: function (state) {
|
||||
eventEmitter.emit('update', state)
|
||||
},
|
||||
})
|
||||
connectionStream.pipe(accountManagerDnode).pipe(connectionStream)
|
||||
accountManagerDnode.once('remote', function (accountManager) {
|
||||
// setup push events
|
||||
accountManager.on = eventEmitter.on.bind(eventEmitter)
|
||||
cb(null, accountManager)
|
||||
})
|
||||
}
|
@ -1,14 +1,19 @@
|
||||
const injectCss = require('inject-css')
|
||||
const NewMetaMaskUiCss = require('../../ui/css')
|
||||
const startPopup = require('./popup-core')
|
||||
const PortStream = require('extension-port-stream')
|
||||
const { getEnvironmentType } = require('./lib/util')
|
||||
const { ENVIRONMENT_TYPE_NOTIFICATION, ENVIRONMENT_TYPE_FULLSCREEN } = require('./lib/enums')
|
||||
const { ENVIRONMENT_TYPE_NOTIFICATION, ENVIRONMENT_TYPE_FULLSCREEN, ENVIRONMENT_TYPE_POPUP } = require('./lib/enums')
|
||||
const extension = require('extensionizer')
|
||||
const ExtensionPlatform = require('./platforms/extension')
|
||||
const NotificationManager = require('./lib/notification-manager')
|
||||
const notificationManager = new NotificationManager()
|
||||
const setupSentry = require('./lib/setupSentry')
|
||||
const {EventEmitter} = require('events')
|
||||
const Dnode = require('dnode')
|
||||
const Eth = require('ethjs')
|
||||
const EthQuery = require('eth-query')
|
||||
const urlUtil = require('url')
|
||||
const launchMetaMaskUi = require('../../ui')
|
||||
const StreamProvider = require('web3-stream-provider')
|
||||
const {setupMultiplex} = require('./lib/stream-utils.js')
|
||||
const log = require('loglevel')
|
||||
|
||||
start().catch(log.error)
|
||||
@ -41,22 +46,8 @@ async function start () {
|
||||
const extensionPort = extension.runtime.connect({ name: windowType })
|
||||
const connectionStream = new PortStream(extensionPort)
|
||||
|
||||
// start ui
|
||||
const container = document.getElementById('app-content')
|
||||
startPopup({ container, connectionStream }, (err, store) => {
|
||||
if (err) return displayCriticalError(err)
|
||||
|
||||
const state = store.getState()
|
||||
const { metamask: { completedOnboarding } = {} } = state
|
||||
|
||||
if (!completedOnboarding && windowType !== ENVIRONMENT_TYPE_FULLSCREEN) {
|
||||
global.platform.openExtensionInBrowser()
|
||||
return
|
||||
}
|
||||
|
||||
injectCss(NewMetaMaskUiCss())
|
||||
})
|
||||
|
||||
const activeTab = await queryCurrentActiveTab(windowType)
|
||||
initializeUiWithTab(activeTab)
|
||||
|
||||
function closePopupIfOpen (windowType) {
|
||||
if (windowType !== ENVIRONMENT_TYPE_NOTIFICATION) {
|
||||
@ -65,11 +56,107 @@ async function start () {
|
||||
}
|
||||
}
|
||||
|
||||
function displayCriticalError (err) {
|
||||
function displayCriticalError (container, err) {
|
||||
container.innerHTML = '<div class="critical-error">The MetaMask app failed to load: please open and close MetaMask again to restart.</div>'
|
||||
container.style.height = '80px'
|
||||
log.error(err.stack)
|
||||
throw err
|
||||
}
|
||||
|
||||
function initializeUiWithTab (tab) {
|
||||
const container = document.getElementById('app-content')
|
||||
initializeUi(tab, container, connectionStream, (err, store) => {
|
||||
if (err) {
|
||||
return displayCriticalError(container, err)
|
||||
}
|
||||
|
||||
const state = store.getState()
|
||||
const { metamask: { completedOnboarding } = {} } = state
|
||||
|
||||
if (!completedOnboarding && windowType !== ENVIRONMENT_TYPE_FULLSCREEN) {
|
||||
global.platform.openExtensionInBrowser()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async function queryCurrentActiveTab (windowType) {
|
||||
return new Promise((resolve) => {
|
||||
// At the time of writing we only have the `activeTab` permission which means
|
||||
// that this query will only succeed in the popup context (i.e. after a "browserAction")
|
||||
if (windowType !== ENVIRONMENT_TYPE_POPUP) {
|
||||
resolve({})
|
||||
return
|
||||
}
|
||||
|
||||
extension.tabs.query({active: true, currentWindow: true}, (tabs) => {
|
||||
const [activeTab] = tabs
|
||||
const {title, url} = activeTab
|
||||
const { hostname: origin, protocol } = url ? urlUtil.parse(url) : {}
|
||||
resolve({
|
||||
title, origin, protocol, url,
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function initializeUi (activeTab, container, connectionStream, cb) {
|
||||
connectToAccountManager(connectionStream, (err, backgroundConnection) => {
|
||||
if (err) {
|
||||
return cb(err)
|
||||
}
|
||||
|
||||
launchMetaMaskUi({
|
||||
activeTab,
|
||||
container,
|
||||
backgroundConnection,
|
||||
}, cb)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes a connection to the background and a Web3 provider
|
||||
*
|
||||
* @param {PortDuplexStream} connectionStream PortStream instance establishing a background connection
|
||||
* @param {Function} cb Called when controller connection is established
|
||||
*/
|
||||
function connectToAccountManager (connectionStream, cb) {
|
||||
const mx = setupMultiplex(connectionStream)
|
||||
setupControllerConnection(mx.createStream('controller'), cb)
|
||||
setupWeb3Connection(mx.createStream('provider'))
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes a streamed connection to a Web3 provider
|
||||
*
|
||||
* @param {PortDuplexStream} connectionStream PortStream instance establishing a background connection
|
||||
*/
|
||||
function setupWeb3Connection (connectionStream) {
|
||||
const providerStream = new StreamProvider()
|
||||
providerStream.pipe(connectionStream).pipe(providerStream)
|
||||
connectionStream.on('error', console.error.bind(console))
|
||||
providerStream.on('error', console.error.bind(console))
|
||||
global.ethereumProvider = providerStream
|
||||
global.ethQuery = new EthQuery(providerStream)
|
||||
global.eth = new Eth(providerStream)
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes a streamed connection to the background account manager
|
||||
*
|
||||
* @param {PortDuplexStream} connectionStream PortStream instance establishing a background connection
|
||||
* @param {Function} cb Called when the remote account manager connection is established
|
||||
*/
|
||||
function setupControllerConnection (connectionStream, cb) {
|
||||
const eventEmitter = new EventEmitter()
|
||||
const backgroundDnode = Dnode({
|
||||
sendUpdate: function (state) {
|
||||
eventEmitter.emit('update', state)
|
||||
},
|
||||
})
|
||||
connectionStream.pipe(backgroundDnode).pipe(connectionStream)
|
||||
backgroundDnode.once('remote', function (backgroundConnection) {
|
||||
backgroundConnection.on = eventEmitter.on.bind(eventEmitter)
|
||||
cb(null, backgroundConnection)
|
||||
})
|
||||
}
|
||||
|
24
babel.config.js
Normal file
@ -0,0 +1,24 @@
|
||||
module.exports = function (api) {
|
||||
api.cache(false)
|
||||
return {
|
||||
presets: [
|
||||
[
|
||||
'@babel/preset-env',
|
||||
{
|
||||
targets: {
|
||||
browsers: [
|
||||
'chrome >= 58',
|
||||
'firefox >= 60',
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
'@babel/preset-react',
|
||||
],
|
||||
plugins: [
|
||||
'@babel/plugin-transform-runtime',
|
||||
'@babel/plugin-proposal-class-properties',
|
||||
'@babel/plugin-proposal-object-rest-spread',
|
||||
],
|
||||
}
|
||||
}
|
@ -1,26 +1,60 @@
|
||||
#! /bin/bash
|
||||
# update tags
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
set -u
|
||||
set -o pipefail
|
||||
|
||||
readonly URL='https://github.com/MetaMask/metamask-extension'
|
||||
|
||||
git fetch --tags
|
||||
# get origin
|
||||
URL='https://github.com/MetaMask/metamask-extension'
|
||||
# get git logs from last tag until HEAD, pretty by 'subject::body' filtered by grep for PRs made with Github squash merge or Github regular merge
|
||||
LOG=$(git log $(git describe --tags $(git rev-list --tags --max-count=1))..HEAD --pretty="%s::%b" --reverse --grep="Merge pull request #" --grep="(#");
|
||||
while read -r line; do
|
||||
# get git log subject
|
||||
SUBJECT=$(echo $line | sed -E 's/(.*):{2}(.*)/\1/')
|
||||
# get git log PR id, PR made with Github squash merge or Github regular merge
|
||||
PR=$(echo $SUBJECT | sed 's/^.*(#\([^&]*\)).*/\1/' | sed 's/^.*#\([^&]*\) from.*/\1/')
|
||||
# if PR made with Github squash merge, subject is the body
|
||||
if [ -z "$(echo $line | sed -E 's/(.*):{2}(.*)/\2/')" ]; then
|
||||
BODY=$(echo $SUBJECT | sed "s/(#$PR)//g"); else
|
||||
BODY=$(echo $line | sed -E 's/(.*):{2}(.*)/\2/')
|
||||
|
||||
most_recent_tag="$(git describe --tags "$(git rev-list --tags --max-count=1)")"
|
||||
|
||||
git rev-list "${most_recent_tag}"..HEAD | while read commit
|
||||
do
|
||||
subject="$(git show -s --format="%s" "$commit")"
|
||||
|
||||
# Squash & Merge: the commit subject is parsed as `<description> (#<PR ID>)`
|
||||
if grep -E -q '\(#[[:digit:]]+\)' <<< "$subject"
|
||||
then
|
||||
pr="$(awk '{print $NF}' <<< "$subject" | tr -d '()')"
|
||||
prefix="[$pr]($URL/pull/${pr###}): "
|
||||
description="$(awk '{NF--; print $0}' <<< "$subject")"
|
||||
|
||||
# Merge: the PR ID is parsed from the git subject (which is of the form `Merge pull request
|
||||
# #<PR ID> from <branch>`, and the description is assumed to be the first line of the body.
|
||||
# If no body is found, the description is set to the commit subject
|
||||
elif grep -E -q '#[[:digit:]]+\sfrom' <<< "$subject"
|
||||
then
|
||||
pr="$(awk '{print $4}' <<< "$subject")"
|
||||
prefix="[$pr]($URL/pull/${pr###}): "
|
||||
|
||||
first_line_of_body="$(git show -s --format="%b" "$commit" | head -n 1 | tr -d '\r')"
|
||||
if [[ -z "$first_line_of_body" ]]
|
||||
then
|
||||
description="$subject"
|
||||
else
|
||||
description="$first_line_of_body"
|
||||
fi
|
||||
|
||||
# Normal commits: The commit subject is the description, and the PR ID is omitted.
|
||||
else
|
||||
pr=''
|
||||
prefix=''
|
||||
description="$subject"
|
||||
fi
|
||||
|
||||
# add entry to CHANGELOG
|
||||
if [[ "$OSTYPE" == "linux-gnu" ]]; then
|
||||
if [[ "$OSTYPE" == "linux-gnu" ]]
|
||||
then
|
||||
# shellcheck disable=SC1004
|
||||
sed -i'' '/## Current Develop Branch/a\
|
||||
- [#'"$PR"']('"$URL"'/pull/'"$PR"'): '"$BODY"''$'\n' CHANGELOG.md; else
|
||||
- '"$prefix$description"''$'\n' CHANGELOG.md
|
||||
else
|
||||
# shellcheck disable=SC1004
|
||||
sed -i '' '/## Current Develop Branch/a\
|
||||
- [#'"$PR"']('"$URL"'/pull/'"$PR"'): '"$BODY"''$'\n' CHANGELOG.md;
|
||||
- '"$prefix$description"''$'\n' CHANGELOG.md
|
||||
fi
|
||||
done <<< "$LOG"
|
||||
done
|
||||
|
||||
echo 'CHANGELOG updated'
|
||||
|
@ -36,13 +36,13 @@ async function start () {
|
||||
console.log(`Posting to: ${POST_COMMENT_URI}`)
|
||||
|
||||
await request({
|
||||
method: 'POST',
|
||||
uri: POST_COMMENT_URI,
|
||||
body: JSON_PAYLOAD,
|
||||
headers: {
|
||||
'User-Agent': 'metamaskbot',
|
||||
'Authorization': `token ${GITHUB_COMMENT_TOKEN}`,
|
||||
},
|
||||
method: 'POST',
|
||||
uri: POST_COMMENT_URI,
|
||||
body: JSON_PAYLOAD,
|
||||
headers: {
|
||||
'User-Agent': 'metamaskbot',
|
||||
'Authorization': `token ${GITHUB_COMMENT_TOKEN}`,
|
||||
},
|
||||
})
|
||||
|
||||
}
|
||||
|
@ -8,8 +8,6 @@
|
||||
*
|
||||
* This is a convenient way to develop and test the plugin
|
||||
* without having to re-open the plugin or even re-build it.
|
||||
*
|
||||
* To use, run `npm run mock`.
|
||||
*/
|
||||
|
||||
const render = require('react-dom').render
|
||||
@ -58,13 +56,6 @@ function updateQueryParams (newView) {
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// CSS
|
||||
//
|
||||
|
||||
const MetaMaskUiCss = require('../ui/css')
|
||||
const injectCss = require('inject-css')
|
||||
|
||||
//
|
||||
// MetaMask Controller
|
||||
//
|
||||
@ -72,7 +63,6 @@ const injectCss = require('inject-css')
|
||||
const controller = new MetamaskController({
|
||||
// User confirmation callbacks:
|
||||
showUnconfirmedMessage: noop,
|
||||
unlockAccountMessage: noop,
|
||||
showUnapprovedTx: noop,
|
||||
platform: {},
|
||||
// initial state
|
||||
@ -101,9 +91,6 @@ function modifyBackgroundConnection (backgroundConnectionModifier) {
|
||||
actions._setBackgroundConnection(modifiedBackgroundConnection)
|
||||
}
|
||||
|
||||
var css = MetaMaskUiCss()
|
||||
injectCss(css)
|
||||
|
||||
// parse opts
|
||||
var store = configureStore(firstState)
|
||||
|
||||
@ -147,10 +134,10 @@ function startApp () {
|
||||
},
|
||||
}, [
|
||||
h(Root, {
|
||||
store: store,
|
||||
store: store,
|
||||
}),
|
||||
]),
|
||||
|
||||
]
|
||||
), container)
|
||||
), container)
|
||||
}
|
||||
|
@ -1,44 +0,0 @@
|
||||
/* MockExtension
|
||||
*
|
||||
* A module for importing the global extension polyfiller
|
||||
* and stubbing out all the extension methods with appropriate mocks.
|
||||
*/
|
||||
|
||||
const extension = require('extensionizer')
|
||||
const noop = function () {}
|
||||
|
||||
const apis = [
|
||||
'alarms',
|
||||
'bookmarks',
|
||||
'browserAction',
|
||||
'commands',
|
||||
'contextMenus',
|
||||
'cookies',
|
||||
'downloads',
|
||||
'events',
|
||||
'extension',
|
||||
'extensionTypes',
|
||||
'history',
|
||||
'i18n',
|
||||
'idle',
|
||||
'notifications',
|
||||
'pageAction',
|
||||
'runtime',
|
||||
'storage',
|
||||
'tabs',
|
||||
'webNavigation',
|
||||
'webRequest',
|
||||
'windows',
|
||||
]
|
||||
|
||||
apis.forEach(function (api) {
|
||||
extension[api] = {}
|
||||
})
|
||||
|
||||
extension.runtime.reload = noop
|
||||
extension.tabs.create = noop
|
||||
extension.runtime.getManifest = function () {
|
||||
return {
|
||||
version: 'development',
|
||||
}
|
||||
}
|
1
development/require-react-devtools.js
Normal file
@ -0,0 +1 @@
|
||||
require('react-devtools')
|
@ -5,7 +5,7 @@ echo "Rolling back to version $1"
|
||||
|
||||
# Checkout branch to increment version
|
||||
git checkout -b version-increment-$1
|
||||
npm run version:bump patch
|
||||
yarn version:bump patch
|
||||
|
||||
# Store the new version name
|
||||
NEW_VERSION=$(cat app/manifest.json | jq -r .version)
|
||||
|
28
development/run-ganache
Executable file
@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
set -u
|
||||
set -o pipefail
|
||||
|
||||
ganache_cli="$(npm bin)/ganache-cli"
|
||||
seed_phrase="${GANACHE_SEED_PHRASE:-phrase upgrade clock rough situate wedding elder clever doctor stamp excess tent}"
|
||||
|
||||
_term () {
|
||||
printf '%s\n' "Received SIGTERM, sending SIGKILL to Ganache"
|
||||
kill -KILL "$child" 2>/dev/null
|
||||
exit 42
|
||||
}
|
||||
|
||||
_int () {
|
||||
printf '%s\n' "Received SIGINT, sending SIGKILL to Ganache"
|
||||
kill -KILL "$child" 2>/dev/null
|
||||
exit 42
|
||||
}
|
||||
|
||||
trap _term SIGTERM
|
||||
trap _int SIGINT
|
||||
|
||||
$ganache_cli --noVMErrorsOnRPCResponse --networkId 5777 --mnemonic "$seed_phrase" ${GANACHE_ARGS:-} &
|
||||
|
||||
child=$!
|
||||
wait "$child"
|
@ -16,7 +16,7 @@ async function start () {
|
||||
if (versionAlreadyExists) {
|
||||
console.log(`Version "${VERSION}" already exists on Sentry, skipping version creation`)
|
||||
} else {
|
||||
// create sentry release
|
||||
// create sentry release
|
||||
console.log(`creating Sentry release for "${VERSION}"...`)
|
||||
await exec(`sentry-cli releases --org 'metamask' --project 'metamask' new ${VERSION}`)
|
||||
console.log(`removing any existing files from Sentry release "${VERSION}"...`)
|
||||
|
@ -133,7 +133,6 @@
|
||||
"address": "0x704107d04affddd9b66ab9de3dd7b095852e9b69"
|
||||
}
|
||||
},
|
||||
"selectedAddress": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc",
|
||||
"network": "1",
|
||||
"seedWords": null,
|
||||
"unconfMsgs": {},
|
||||
|
@ -99,7 +99,6 @@
|
||||
"status": "confirmed",
|
||||
"containsDelegateCall": false
|
||||
}],
|
||||
"selectedAddress": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc",
|
||||
"network": "2",
|
||||
"seedWords": null,
|
||||
"unconfMsgs": {},
|
||||
|
@ -57,7 +57,6 @@
|
||||
}
|
||||
},
|
||||
"transactions": [],
|
||||
"selectedAddress": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc",
|
||||
"network": "2",
|
||||
"seedWords": null,
|
||||
"unconfMsgs": {},
|
||||
|
@ -48,7 +48,6 @@
|
||||
"gasPrice": "4a817c800"
|
||||
}
|
||||
},
|
||||
"currentCurrency": "USD",
|
||||
"conversionRate": 12.7200827,
|
||||
"conversionDate": 1487363041,
|
||||
"noActiveNotices": true,
|
||||
|
@ -22,8 +22,6 @@
|
||||
"name": "Send Account 4"
|
||||
}
|
||||
},
|
||||
"unapprovedTxs": {},
|
||||
"currentCurrency": "USD",
|
||||
"conversionRate": 1200.88200327,
|
||||
"conversionDate": 1489013762,
|
||||
"noActiveNotices": true,
|
||||
@ -134,7 +132,6 @@
|
||||
"useNativeCurrencyAsPrimaryCurrency": true,
|
||||
"showFiatInTestnets": true
|
||||
},
|
||||
"completedUiMigration": true,
|
||||
"frequentRpcListDetail": []
|
||||
},
|
||||
"appState": {
|
||||
@ -203,7 +200,6 @@
|
||||
},
|
||||
"basicEstimateIsLoading": false,
|
||||
"gasEstimatesLoading": false,
|
||||
"basicPriceAndTimeEstimates": [],
|
||||
"priceAndTimeEstimates": [
|
||||
{
|
||||
"expectedTime": "1374.1168296452973076627",
|
||||
|
@ -24,8 +24,6 @@
|
||||
}
|
||||
},
|
||||
"cachedBalances": {},
|
||||
"unapprovedTxs": {},
|
||||
"currentCurrency": "USD",
|
||||
"conversionRate": 1200.88200327,
|
||||
"conversionDate": 1489013762,
|
||||
"noActiveNotices": true,
|
||||
@ -157,7 +155,6 @@
|
||||
"preferences": {
|
||||
"useNativeCurrencyAsPrimaryCurrency": true
|
||||
},
|
||||
"completedUiMigration": true,
|
||||
"frequentRpcListDetail": []
|
||||
},
|
||||
"appState": {
|
||||
@ -229,7 +226,6 @@
|
||||
},
|
||||
"basicEstimateIsLoading": false,
|
||||
"gasEstimatesLoading": false,
|
||||
"basicPriceAndTimeEstimates": [],
|
||||
"priceAndTimeEstimates": [
|
||||
{
|
||||
"expectedTime": "1374.1168296452973076627",
|
||||
|
@ -25,7 +25,6 @@
|
||||
},
|
||||
"cachedBalances": {},
|
||||
"unapprovedTxs": {},
|
||||
"currentCurrency": "USD",
|
||||
"conversionRate": 19855,
|
||||
"conversionDate": 1489013762,
|
||||
"noActiveNotices": true,
|
||||
@ -116,7 +115,6 @@
|
||||
"useNativeCurrencyAsPrimaryCurrency": true,
|
||||
"showFiatInTestnets": true
|
||||
},
|
||||
"completedUiMigration": true,
|
||||
"frequentRpcListDetail": []
|
||||
},
|
||||
"appState": {
|
||||
@ -179,7 +177,6 @@
|
||||
},
|
||||
"basicEstimateIsLoading": false,
|
||||
"gasEstimatesLoading": false,
|
||||
"basicPriceAndTimeEstimates": [],
|
||||
"priceAndTimeEstimates": [
|
||||
{
|
||||
"expectedTime": "1374.1168296452973076627",
|
||||
|
@ -54,7 +54,6 @@
|
||||
}
|
||||
},
|
||||
"transactions": [],
|
||||
"selectedAddress": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc",
|
||||
"network": "2",
|
||||
"seedWords": null,
|
||||
"unconfMsgs": {},
|
||||
|
@ -7,7 +7,6 @@
|
||||
"computedBalances": {},
|
||||
"frequentRpcList": [],
|
||||
"unapprovedTxs": {},
|
||||
"currentCurrency": "USD",
|
||||
"featureFlags": {"betaUI": false},
|
||||
"conversionRate": 12.7527416,
|
||||
"conversionDate": 1487624341,
|
||||
|
@ -11,7 +11,6 @@
|
||||
"conversionDate": 1473358355,
|
||||
"accounts": {},
|
||||
"transactions": [],
|
||||
"selectedAddress": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||
"network": "1473186153102",
|
||||
"seedWords": null,
|
||||
"unconfMsgs": {},
|
||||
|
@ -61,7 +61,6 @@
|
||||
}
|
||||
},
|
||||
"transactions": [],
|
||||
"selectedAddress": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc",
|
||||
"network": "2",
|
||||
"seedWords": null,
|
||||
"unconfMsgs": {},
|
||||
|
@ -36,7 +36,6 @@
|
||||
"provider": {
|
||||
"type": "testnet"
|
||||
},
|
||||
"selectedAddress": "0xa6ef573d60594731178b7f85d80da13cc2af52dd",
|
||||
"unconfMsgs": {},
|
||||
"messages": [],
|
||||
"selectedAddress": "0xa6ef573d60594731178b7f85d80da13cc2af52dd",
|
||||
|