diff options
51 files changed, 3230 insertions, 342 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 59f116aed..823b89b45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,22 @@ ## Current Master +## 4.2.0 Tue Mar 06 2018 + +- Replace "Loose" wording to "Imported". +- Replace "Unlock" wording with "Log In". +- Add Imported Account disclaimer. +- Allow adding custom tokens to classic ui when balance is 0 +- Allow editing of symbol and decimal info when adding custom token in new-ui +- NewUI shapeshift form can select all coins (not just BTC) +- Add most of Microsoft Edge support. + +## 4.1.3 2018-2-28 + +- Ensure MetaMask's inpage provider is named MetamaskInpageProvider to keep some sites from breaking. +- Add retry transaction button back into classic ui. +- Add network dropdown styles to support long custom RPC urls + ## 4.1.2 2018-2-28 - Actually includes all the fixes mentioned in 4.1.1 (sorry) diff --git a/app/manifest.json b/app/manifest.json index 1c9c420f9..0c89c2b3e 100644 --- a/app/manifest.json +++ b/app/manifest.json @@ -1,7 +1,7 @@ { "name": "MetaMask", "short_name": "Metamask", - "version": "4.1.2", + "version": "4.2.0", "manifest_version": 2, "author": "https://metamask.io", "description": "Ethereum Browser Extension", @@ -59,7 +59,7 @@ "clipboardWrite", "http://localhost:8545/", "https://*.infura.io/" - ], + ], "web_accessible_resources": [ "scripts/inpage.js" ], @@ -68,4 +68,4 @@ "https://metamask.io/*" ] } -} +}
\ No newline at end of file diff --git a/app/scripts/background.js b/app/scripts/background.js index 4487ff318..601ae0372 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -16,6 +16,7 @@ const firstTimeState = require('./first-time-state') const setupRaven = require('./lib/setupRaven') const reportFailedTxToSentry = require('./lib/reportFailedTxToSentry') const setupMetamaskMeshMetrics = require('./lib/setupMetamaskMeshMetrics') +const EdgeEncryptor = require('./edge-encryptor') const STORAGE_KEY = 'metamask-config' @@ -32,6 +33,12 @@ global.METAMASK_NOTIFIER = notificationManager const release = platform.getVersion() const raven = setupRaven({ release }) +// browser check if it is Edge - https://stackoverflow.com/questions/9847580/how-to-detect-safari-chrome-ie-firefox-and-opera-browser +// Internet Explorer 6-11 +const isIE = !!document.documentMode +// Edge 20+ +const isEdge = !isIE && !!window.StyleMedia + let popupIsOpen = false let openMetamaskTabsIDs = {} @@ -81,6 +88,7 @@ function setupController (initState) { initState, // platform specific api platform, + encryptor: isEdge ? new EdgeEncryptor() : undefined, }) global.metamaskController = controller diff --git a/app/scripts/controllers/transactions.js b/app/scripts/controllers/transactions.js index ef5578d5a..9c2ca0dc8 100644 --- a/app/scripts/controllers/transactions.js +++ b/app/scripts/controllers/transactions.js @@ -3,7 +3,7 @@ const ObservableStore = require('obs-store') const ethUtil = require('ethereumjs-util') const Transaction = require('ethereumjs-tx') const EthQuery = require('ethjs-query') -const TransactionStateManger = require('../lib/tx-state-manager') +const TransactionStateManager = require('../lib/tx-state-manager') const TxGasUtil = require('../lib/tx-gas-utils') const PendingTransactionTracker = require('../lib/pending-tx-tracker') const createId = require('../lib/random-id') @@ -38,7 +38,7 @@ module.exports = class TransactionController extends EventEmitter { this.query = new EthQuery(this.provider) this.txGasUtil = new TxGasUtil(this.provider) - this.txStateManager = new TransactionStateManger({ + this.txStateManager = new TransactionStateManager({ initState: opts.initState, txHistoryLimit: opts.txHistoryLimit, getNetwork: this.getNetwork.bind(this), diff --git a/app/scripts/edge-encryptor.js b/app/scripts/edge-encryptor.js new file mode 100644 index 000000000..24c0c93a8 --- /dev/null +++ b/app/scripts/edge-encryptor.js @@ -0,0 +1,69 @@ +const asmcrypto = require('asmcrypto.js') +const Unibabel = require('browserify-unibabel') + +class EdgeEncryptor { + + 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 buffer = new Uint8Array(resultbuffer) + var vectorStr = Unibabel.bufferToBase64(vector) + var vaultStr = Unibabel.bufferToBase64(buffer) + return JSON.stringify({ + data: vaultStr, + iv: vectorStr, + salt: salt, + }) + }) + } + + 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) + }) + }) + } + + _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) + }) + } + + _generateSalt (byteCount = 32) { + var view = new Uint8Array(byteCount) + global.crypto.getRandomValues(view) + var b64encoded = btoa(String.fromCharCode.apply(null, view)) + return b64encoded + } +} + +module.exports = EdgeEncryptor diff --git a/app/scripts/lib/tx-state-manager.js b/app/scripts/lib/tx-state-manager.js index 051efd247..2eb006380 100644 --- a/app/scripts/lib/tx-state-manager.js +++ b/app/scripts/lib/tx-state-manager.js @@ -4,7 +4,7 @@ const ObservableStore = require('obs-store') const ethUtil = require('ethereumjs-util') const txStateHistoryHelper = require('./tx-state-history-helper') -module.exports = class TransactionStateManger extends EventEmitter { +module.exports = class TransactionStateManager extends EventEmitter { constructor ({ initState, txHistoryLimit, getNetwork }) { super() diff --git a/development/run-version-bump.js b/development/run-version-bump.js new file mode 100644 index 000000000..fde14566e --- /dev/null +++ b/development/run-version-bump.js @@ -0,0 +1,43 @@ +const { promisify } = require('util') +const fs = require('fs') +const readFile = promisify(fs.readFile) +const writeFile = promisify(fs.writeFile) +const path = require('path') +const changelogPath = path.join(__dirname, '..', 'CHANGELOG.md') +const manifestPath = path.join(__dirname, '..', 'app', 'manifest.json') +const manifest = require('../app/manifest.json') +const versionBump = require('./version-bump') +const bumpType = normalizeType(process.argv[2]) + +start().catch(console.error) + +async function start() { + + const changeBuffer = await readFile(changelogPath) + const changelog = changeBuffer.toString() + + const newData = await versionBump(bumpType, changelog, manifest) + + const manifestString = JSON.stringify(newData.manifest, null, 2) + + await writeFile(changelogPath, newData.changelog) + await writeFile(manifestPath, manifestString) + + console.log(`Bumped ${bumpType} to version ${newData.version}`) +} + + +function normalizeType (userInput) { + const err = new Error('First option must be a type (major, minor, or patch)') + if (!userInput || typeof userInput !== 'string') { + throw err + } + + const lower = userInput.toLowerCase() + + if (lower !== 'major' && lower !== 'minor' && lower !== 'patch') { + throw err + } + + return lower +} diff --git a/development/version-bump.js b/development/version-bump.js new file mode 100644 index 000000000..bedf87c01 --- /dev/null +++ b/development/version-bump.js @@ -0,0 +1,52 @@ +const clone = require('clone') + +async function versionBump(bumpType, changelog, oldManifest) { + const manifest = clone(oldManifest) + const newVersion = newVersionFrom(manifest, bumpType) + + manifest.version = newVersion + const date = (new Date()).toDateString() + + const logHeader = `\n## ${newVersion} ${date}` + const logLines = changelog.split('\n') + for (let i = 0; i < logLines.length; i++) { + if (logLines[i].includes('Current Master')) { + logLines.splice(i + 1, 0, logHeader) + break + } + } + + return { + version: newVersion, + manifest: manifest, + changelog: logLines.join('\n') + } +} + +function newVersionFrom (manifest, bumpType) { + const string = manifest.version + let segments = string.split('.').map((str) => parseInt(str)) + + switch (bumpType) { + case 'major': + segments[0] += 1 + segments[1] = 0 + segments[2] = 0 + break + case 'minor': + segments[1] += 1 + segments[2] = 0 + break + case 'patch': + segments[2] += 1 + break + } + + return segments.map(String).join('.') +} + +function bumpManifest (manifest, bumpType) { + +} + +module.exports = versionBump diff --git a/docs/bumping_version.md b/docs/bumping_version.md new file mode 100644 index 000000000..df38369a2 --- /dev/null +++ b/docs/bumping_version.md @@ -0,0 +1,33 @@ +# How to Bump MetaMask's Version Automatically + +``` +npm run version:bump patch +``` + +MetaMask publishes using a loose [semver](https://semver.org/) interpretation. We divide the three segments of our version into three types of version bump: + +## Major + +Means a breaking change, either an API removed, or a major user expectation changed. + +## Minor + +Means a new API or new user feature. + +## Patch + +Means a fix for a bug, or correcting something that should have been assumed to work a different way. + +# Bumping the version + +`npm run version:bump $BUMP_TYPE` where `$BUMP_TYPE` is one of `major`, `minor`, or `patch`. + +This will increment the version in the `app/manifest.json` and `CHANGELOG.md` files according to our current protocol, where the manifest's version is updated, and any line items currently under the changelog's "master" section are now under the new dated version section. + +# Modifying the bump script + +The script that is executed lives [here](../development/run-version-bump.js). +The main functions all live [here](../development/version-bump.js). +The test for this behavior is at `test/unit/development/version-bump-test.js`. + + diff --git a/docs/publishing.md b/docs/publishing.md index 00369acf9..3022b7eda 100644 --- a/docs/publishing.md +++ b/docs/publishing.md @@ -4,15 +4,15 @@ When publishing a new version of MetaMask, we follow this procedure: ## Incrementing Version & Changelog - You must be authorized already on the MetaMask plugin. +Version can be automatically incremented [using our bump script](./bumping-version.md). -1. Update the version in `app/manifest.json` and the Changelog in `CHANGELOG.md`. -2. Visit [the chrome developer dashboard](https://chrome.google.com/webstore/developer/dashboard?authuser=2). +npm run version:bump $BUMP_TYPE` where `$BUMP_TYPE` is one of `major`, `minor`, or `patch`. ## Publishing 1. `npm run dist` to generate the latest build. 2. Publish to chrome store. + - Visit [the chrome developer dashboard](https://chrome.google.com/webstore/developer/dashboard?authuser=2). 3. Publish to firefox addon marketplace. 4. Post on Github releases page. 5. `npm run announce`, post that announcement in our public places. diff --git a/gulpfile.js b/gulpfile.js index 3ade82f87..adfb148a9 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -407,7 +407,9 @@ function bundleTask(opts) { // loads map from browserify file .pipe(gulpif(debug, sourcemaps.init({ loadMaps: true }))) // Minification - .pipe(gulpif(opts.isBuild, uglify())) + .pipe(gulpif(opts.isBuild, uglify({ + mangle: { reserved: [ 'MetamaskInpageProvider' ] }, + }))) // writes .map file .pipe(gulpif(debug, sourcemaps.write('./'))) // write completed bundles diff --git a/mascara/src/app/first-time/import-seed-phrase-screen.js b/mascara/src/app/first-time/import-seed-phrase-screen.js index 93c3f9203..de8d675e1 100644 --- a/mascara/src/app/first-time/import-seed-phrase-screen.js +++ b/mascara/src/app/first-time/import-seed-phrase-screen.js @@ -1,13 +1,12 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' import {connect} from 'react-redux' -import LoadingScreen from './loading-screen' +import classnames from 'classnames' import { createNewVaultAndRestore, hideWarning, displayWarning, unMarkPasswordForgotten, - clearNotices, } from '../../../../ui/app/actions' class ImportSeedPhraseScreen extends Component { @@ -17,8 +16,8 @@ class ImportSeedPhraseScreen extends Component { next: PropTypes.func.isRequired, createNewVaultAndRestore: PropTypes.func.isRequired, hideWarning: PropTypes.func.isRequired, - isLoading: PropTypes.bool.isRequired, displayWarning: PropTypes.func, + leaveImportSeedScreenState: PropTypes.func, }; state = { @@ -27,98 +26,130 @@ class ImportSeedPhraseScreen extends Component { confirmPassword: '', } - onClick = () => { - const { password, seedPhrase, confirmPassword } = this.state - const { createNewVaultAndRestore, next, displayWarning, leaveImportSeedScreenState } = this.props + parseSeedPhrase = (seedPhrase) => { + return seedPhrase + .match(/\w+/g) + .join(' ') + } - if (seedPhrase.split(' ').length !== 12) { - this.warning = 'Seed Phrases are 12 words long' - displayWarning(this.warning) - return - } + onChange = ({ seedPhrase, password, confirmPassword }) => { + const { + password: prevPassword, + confirmPassword: prevConfirmPassword, + } = this.state + const { displayWarning, hideWarning } = this.props + + let warning = null - if (password.length < 8) { - this.warning = 'Passwords require a mimimum length of 8' - displayWarning(this.warning) - return + if (seedPhrase && this.parseSeedPhrase(seedPhrase).split(' ').length !== 12) { + warning = 'Seed Phrases are 12 words long' + } else if (password && password.length < 8) { + warning = 'Passwords require a mimimum length of 8' + } else if ((password || prevPassword) !== (confirmPassword || prevConfirmPassword)) { + warning = 'Confirmed password does not match' } - if (password !== confirmPassword) { - this.warning = 'Confirmed password does not match' - displayWarning(this.warning) - return + if (warning) { + displayWarning(warning) + } else { + hideWarning() } - this.warning = null + + seedPhrase && this.setState({ seedPhrase }) + password && this.setState({ password }) + confirmPassword && this.setState({ confirmPassword }) + } + + onClick = () => { + const { password, seedPhrase } = this.state + const { + createNewVaultAndRestore, + next, + displayWarning, + leaveImportSeedScreenState, + } = this.props + leaveImportSeedScreenState() - createNewVaultAndRestore(password, seedPhrase) + createNewVaultAndRestore(password, this.parseSeedPhrase(seedPhrase)) .then(next) } render () { - return this.props.isLoading - ? <LoadingScreen loadingMessage="Creating your new account" /> - : ( - <div className="import-account"> - <a - className="import-account__back-button" - onClick={e => { - e.preventDefault() - this.props.back() - }} - href="#" - > - {`< Back`} - </a> - <div className="import-account__title"> - Import an Account with Seed Phrase - </div> - <div className="import-account__selector-label"> - Enter your secret twelve word phrase here to restore your vault. - </div> - <div className="import-account__input-wrapper"> - <label className="import-account__input-label">Wallet Seed</label> - <textarea - className="import-account__secret-phrase" - onChange={e => this.setState({seedPhrase: e.target.value})} - placeholder="Separate each word with a single space" - /> - </div> - <span - className="error" - > - {this.props.warning} - </span> - <div className="import-account__input-wrapper"> - <label className="import-account__input-label">New Password</label> - <input - className="first-time-flow__input" - type="password" - placeholder="New Password (min 8 characters)" - onChange={e => this.setState({password: e.target.value})} - /> - </div> - <div className="import-account__input-wrapper"> - <label className="import-account__input-label">Confirm Password</label> - <input - className="first-time-flow__input" - type="password" - placeholder="Confirm Password" - onChange={e => this.setState({confirmPassword: e.target.value})} - /> - </div> - <button - className="first-time-flow__button" - onClick={this.onClick} - > - Import - </button> + const { seedPhrase, password, confirmPassword } = this.state + const { warning } = this.props + const importDisabled = warning || !seedPhrase || !password || !confirmPassword + return ( + <div className="import-account"> + <a + className="import-account__back-button" + onClick={e => { + e.preventDefault() + this.props.back() + }} + href="#" + > + {`< Back`} + </a> + <div className="import-account__title"> + Import an Account with Seed Phrase + </div> + <div className="import-account__selector-label"> + Enter your secret twelve word phrase here to restore your vault. + </div> + <div className="import-account__input-wrapper"> + <label className="import-account__input-label">Wallet Seed</label> + <textarea + className="import-account__secret-phrase" + onChange={e => this.onChange({seedPhrase: e.target.value})} + value={this.state.seedPhrase} + placeholder="Separate each word with a single space" + /> + </div> + <span + className="error" + > + {this.props.warning} + </span> + <div className="import-account__input-wrapper"> + <label className="import-account__input-label">New Password</label> + <input + className="first-time-flow__input" + type="password" + placeholder="New Password (min 8 characters)" + onChange={e => this.onChange({password: e.target.value})} + /> + </div> + <div className="import-account__input-wrapper"> + <label + className="import-account__input-label" + className={classnames('import-account__input-label', { + 'import-account__input-label__disabled': password.length < 8, + })} + >Confirm Password</label> + <input + className={classnames('first-time-flow__input', { + 'first-time-flow__input__disabled': password.length < 8, + })} + type="password" + placeholder="Confirm Password" + onChange={e => this.onChange({confirmPassword: e.target.value})} + disabled={password.length < 8} + /> </div> - ) + <button + className="first-time-flow__button" + onClick={() => !importDisabled && this.onClick()} + disabled={importDisabled} + > + Import + </button> + </div> + ) } } export default connect( - ({ appState: { isLoading, warning } }) => ({ isLoading, warning }), + ({ appState: { warning } }) => ({ warning }), dispatch => ({ leaveImportSeedScreenState: () => { dispatch(unMarkPasswordForgotten()) diff --git a/mascara/src/app/first-time/index.css b/mascara/src/app/first-time/index.css index 109946e1d..f59eb4ce1 100644 --- a/mascara/src/app/first-time/index.css +++ b/mascara/src/app/first-time/index.css @@ -1,11 +1,11 @@ .first-time-flow { - height: 100vh; width: 100vw; - background-color: #FFF; + background-color: #fff; overflow: auto; display: flex; justify-content: center; + flex: 1 0 auto; } .alpha-warning { @@ -45,12 +45,12 @@ .buy-ether { display: flex; flex-flow: column nowrap; - margin: 67px 0 50px 146px; + margin: 67px 0 50px; max-width: 35rem; } .create-password { - margin: 67px 0 50px 0px; + margin: 67px 0 50px; } .import-account { @@ -418,6 +418,10 @@ button.backup-phrase__confirm-seed-option:hover { line-height: 23px; } +.import-account__input-label__disabled { + opacity: 0.5; +} + .import-account__input { width: 325px !important; } @@ -564,6 +568,10 @@ button.backup-phrase__confirm-seed-option:hover { background-color: #FFFFFF; } +.first-time-flow__input__disabled { + opacity: 0.5; +} + .first-time-flow__input::placeholder { color: #9B9B9B; font-weight: 200; diff --git a/old-ui/app/accounts/import/index.js b/old-ui/app/accounts/import/index.js index 3502efe93..a57525ccf 100644 --- a/old-ui/app/accounts/import/index.js +++ b/old-ui/app/accounts/import/index.js @@ -34,10 +34,7 @@ AccountImportSubview.prototype.render = function () { const { type } = state return ( - h('div', { - style: { - }, - }, [ + h('div', [ h('.section-title.flex-row.flex-center', [ h('i.fa.fa-arrow-left.fa-lg.cursor-pointer', { onClick: (event) => { @@ -46,6 +43,27 @@ AccountImportSubview.prototype.render = function () { }), h('h2.page-subtitle', 'Import Accounts'), ]), + h('.error', { + style: { + display: 'inline-block', + alignItems: 'center', + padding: '5px 15px 0px 15px', + }, + }, [ + h('span', 'Imported accounts will not be associated with your originally created MetaMask account seedphrase. Learn more about imported accounts '), + h('span', { + style: { + color: 'rgba(247, 134, 28, 1)', + cursor: 'pointer', + textDecoration: 'underline', + }, + onClick: () => { + global.platform.openWindow({ + url: 'https://metamask.helpscoutdocs.com/article/17-what-are-loose-accounts', + }) + }, + }, 'here.'), + ]), h('div', { style: { padding: '10px', diff --git a/old-ui/app/add-token.js b/old-ui/app/add-token.js index 8a3e66978..e869ac39a 100644 --- a/old-ui/app/add-token.js +++ b/old-ui/app/add-token.js @@ -156,6 +156,9 @@ AddTokenScreen.prototype.render = function () { const { address, symbol, decimals } = this.state this.props.dispatch(actions.addToken(address.trim(), symbol.trim(), decimals)) + .then(() => { + this.props.dispatch(actions.goHome()) + }) }, }, 'Add'), ]), diff --git a/old-ui/app/components/account-dropdowns.js b/old-ui/app/components/account-dropdowns.js index 7a2357921..53468a1a1 100644 --- a/old-ui/app/components/account-dropdowns.js +++ b/old-ui/app/components/account-dropdowns.js @@ -79,7 +79,7 @@ class AccountDropdowns extends Component { try { // Sometimes keyrings aren't loaded yet: const type = keyring.type const isLoose = type !== 'HD Key Tree' - return isLoose ? h('.keyring-label', 'LOOSE') : null + return isLoose ? h('.keyring-label', 'IMPORTED') : null } catch (e) { return } } diff --git a/old-ui/app/components/token-list.js b/old-ui/app/components/token-list.js index 998ec901d..149733b89 100644 --- a/old-ui/app/components/token-list.js +++ b/old-ui/app/components/token-list.js @@ -194,10 +194,7 @@ TokenList.prototype.componentWillUpdate = function (nextProps) { } TokenList.prototype.updateBalances = function (tokens) { - const heldTokens = tokens.filter(token => { - return token.balance !== '0' && token.string !== '0.000' - }) - this.setState({ tokens: heldTokens, isLoading: false }) + this.setState({ tokens, isLoading: false }) } TokenList.prototype.componentWillUnmount = function () { diff --git a/old-ui/app/components/transaction-list-item.js b/old-ui/app/components/transaction-list-item.js index 95670bd54..e7251df8d 100644 --- a/old-ui/app/components/transaction-list-item.js +++ b/old-ui/app/components/transaction-list-item.js @@ -1,6 +1,7 @@ const Component = require('react').Component const h = require('react-hyperscript') const inherits = require('util').inherits +const connect = require('react-redux').connect const EthBalance = require('./eth-balance') const addressSummary = require('../util').addressSummary @@ -9,18 +10,33 @@ const CopyButton = require('./copyButton') const vreme = new (require('vreme'))() const Tooltip = require('./tooltip') const numberToBN = require('number-to-bn') +const actions = require('../../../ui/app/actions') const TransactionIcon = require('./transaction-list-item-icon') const ShiftListItem = require('./shift-list-item') -module.exports = TransactionListItem + +const mapDispatchToProps = dispatch => { + return { + retryTransaction: transactionId => dispatch(actions.retryTransaction(transactionId)), + } +} + +module.exports = connect(null, mapDispatchToProps)(TransactionListItem) inherits(TransactionListItem, Component) function TransactionListItem () { Component.call(this) } +TransactionListItem.prototype.showRetryButton = function () { + const { transaction = {} } = this.props + const { status, time } = transaction + return status === 'submitted' && Date.now() - time > 30000 +} + TransactionListItem.prototype.render = function () { const { transaction, network, conversionRate, currentCurrency } = this.props + const { status } = transaction if (transaction.key === 'shapeshift') { if (network === '1') return h(ShiftListItem, transaction) } @@ -32,7 +48,7 @@ TransactionListItem.prototype.render = function () { var isMsg = ('msgParams' in transaction) var isTx = ('txParams' in transaction) - var isPending = transaction.status === 'unapproved' + var isPending = status === 'unapproved' let txParams if (isTx) { txParams = transaction.txParams @@ -44,7 +60,7 @@ TransactionListItem.prototype.render = function () { const isClickable = ('hash' in transaction && isLinkable) || isPending return ( - h(`.transaction-list-item.flex-row.flex-space-between${isClickable ? '.pointer' : ''}`, { + h('.transaction-list-item.flex-column', { onClick: (event) => { if (isPending) { this.props.showTx(transaction.id) @@ -56,51 +72,92 @@ TransactionListItem.prototype.render = function () { }, style: { padding: '20px 0', - display: 'flex', - justifyContent: 'space-between', + alignItems: 'center', }, }, [ + h(`.flex-row.flex-space-between${isClickable ? '.pointer' : ''}`, { + style: { + width: '100%', + }, + }, [ + h('.identicon-wrapper.flex-column.flex-center.select-none', [ + h(TransactionIcon, { txParams, transaction, isTx, isMsg }), + ]), - h('.identicon-wrapper.flex-column.flex-center.select-none', [ - h(TransactionIcon, { txParams, transaction, isTx, isMsg }), + h(Tooltip, { + title: 'Transaction Number', + position: 'right', + }, [ + h('span', { + style: { + display: 'flex', + cursor: 'normal', + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'center', + padding: '10px', + }, + }, nonce), + ]), + + h('.flex-column', {style: {width: '200px', overflow: 'hidden'}}, [ + domainField(txParams), + h('div', date), + recipientField(txParams, transaction, isTx, isMsg), + ]), + + // Places a copy button if tx is successful, else places a placeholder empty div. + transaction.hash ? h(CopyButton, { value: transaction.hash }) : h('div', {style: { display: 'flex', alignItems: 'center', width: '26px' }}), + + isTx ? h(EthBalance, { + value: txParams.value, + conversionRate, + currentCurrency, + width: '55px', + shorten: true, + showFiat: false, + style: {fontSize: '15px'}, + }) : h('.flex-column'), ]), - h(Tooltip, { - title: 'Transaction Number', - position: 'right', + this.showRetryButton() && h('.transition-list-item__retry.grow-on-hover', { + onClick: event => { + event.stopPropagation() + this.resubmit() + }, + style: { + height: '22px', + borderRadius: '22px', + color: '#F9881B', + padding: '0 20px', + backgroundColor: '#FFE3C9', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + fontSize: '8px', + cursor: 'pointer', + }, }, [ - h('span', { + h('div', { style: { - display: 'flex', - cursor: 'normal', - flexDirection: 'column', - alignItems: 'center', - justifyContent: 'center', + paddingRight: '2px', }, - }, nonce), - ]), - - h('.flex-column', {style: {width: '150px', overflow: 'hidden'}}, [ - domainField(txParams), - h('div', date), - recipientField(txParams, transaction, isTx, isMsg), + }, 'Taking too long?'), + h('div', { + style: { + textDecoration: 'underline', + }, + }, 'Retry with a higher gas price here'), ]), - - // Places a copy button if tx is successful, else places a placeholder empty div. - transaction.hash ? h(CopyButton, { value: transaction.hash }) : h('div', {style: { display: 'flex', alignItems: 'center', width: '26px' }}), - - isTx ? h(EthBalance, { - value: txParams.value, - conversionRate, - currentCurrency, - shorten: true, - showFiat: false, - style: {fontSize: '15px'}, - }) : h('.flex-column'), ]) ) } +TransactionListItem.prototype.resubmit = function () { + const { transaction } = this.props + this.props.retryTransaction(transaction.id) +} + function domainField (txParams) { return h('div', { style: { diff --git a/old-ui/app/css/lib.css b/old-ui/app/css/lib.css index f3acbee76..fd63b2b2e 100644 --- a/old-ui/app/css/lib.css +++ b/old-ui/app/css/lib.css @@ -217,7 +217,7 @@ hr.horizontal-line { background: rgba(255,0,0,0.8); color: white; bottom: 0px; - left: -8px; + left: -18px; border-radius: 10px; height: 20px; min-width: 20px; diff --git a/old-ui/app/info.js b/old-ui/app/info.js index db9f30f23..d79b8a3d2 100644 --- a/old-ui/app/info.js +++ b/old-ui/app/info.js @@ -63,7 +63,7 @@ InfoScreen.prototype.render = function () { h('a', { href: 'https://metamask.io/privacy.html', target: '_blank', - onClick (event) { this.navigateTo(event.target.href) }, + onClick: (event) => { this.navigateTo(event.target.href) }, }, [ h('div.info', 'Privacy Policy'), ]), @@ -72,7 +72,7 @@ InfoScreen.prototype.render = function () { h('a', { href: 'https://metamask.io/terms.html', target: '_blank', - onClick (event) { this.navigateTo(event.target.href) }, + onClick: (event) => { this.navigateTo(event.target.href) }, }, [ h('div.info', 'Terms of Use'), ]), @@ -81,7 +81,7 @@ InfoScreen.prototype.render = function () { h('a', { href: 'https://metamask.io/attributions.html', target: '_blank', - onClick (event) { this.navigateTo(event.target.href) }, + onClick: (event) => { this.navigateTo(event.target.href) }, }, [ h('div.info', 'Attributions'), ]), diff --git a/old-ui/app/unlock.js b/old-ui/app/unlock.js index a1f791552..7bf4ad29f 100644 --- a/old-ui/app/unlock.js +++ b/old-ui/app/unlock.js @@ -69,7 +69,7 @@ UnlockScreen.prototype.render = function () { style: { margin: 10, }, - }, 'Unlock'), + }, 'Log In'), ]), h('.flex-row.flex-center.flex-grow', [ diff --git a/package.json b/package.json index d712e00ac..fd2554f73 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "dist": "npm run dist:clear && npm install && gulp dist", "dist:clear": "rm -rf node_modules/eth-contract-metadata && rm -rf node_modules/eth-phishing-detect", "test": "npm run lint && npm run test:coverage && npm run test:integration", - "test:unit": "METAMASK_ENV=test mocha --exit --compilers js:babel-core/register --require test/helper.js --recursive \"test/unit/**/*.js\"", + "test:unit": "METAMASK_ENV=test mocha --exit --require babel-core/register --require test/helper.js --recursive \"test/unit/**/*.js\"", "test:single": "METAMASK_ENV=test mocha --require test/helper.js", "test:integration": "gulp build:scss && npm run test:flat && npm run test:mascara", "test:coverage": "nyc npm run test:unit && npm run test:coveralls-upload", @@ -32,6 +32,7 @@ "lint:fix": "gulp lint:fix", "disc": "gulp disc --debug", "announce": "node development/announcer.js", + "version:bump": "node development/run-version-bump.js", "generateNotice": "node notices/notice-generator.js", "deleteNotice": "node notices/notice-delete.js" }, @@ -53,6 +54,7 @@ }, "dependencies": { "abi-decoder": "^1.0.9", + "asmcrypto.js": "0.22.0", "async": "^2.5.0", "await-semaphore": "^0.1.1", "babel-runtime": "^6.23.0", @@ -63,6 +65,7 @@ "boron": "^0.2.3", "browser-passworder": "^2.0.3", "browserify-derequire": "^0.9.4", + "browserify-unibabel": "^3.0.0", "classnames": "^2.2.5", "client-sw-ready-event": "^3.3.0", "clone": "^2.1.1", @@ -77,11 +80,11 @@ "eslint-plugin-react": "^7.4.0", "eth-bin-to-ops": "^1.0.1", "eth-block-tracker": "^2.3.0", + "eth-contract-metadata": "^1.1.5", + "eth-hd-keyring": "^1.2.1", "eth-json-rpc-filters": "^1.2.5", "eth-json-rpc-infura": "^3.0.0", "eth-keyring-controller": "^2.1.4", - "eth-contract-metadata": "^1.1.5", - "eth-hd-keyring": "^1.2.1", "eth-phishing-detect": "^1.1.4", "eth-query": "^2.1.2", "eth-sig-util": "^1.4.2", @@ -211,7 +214,7 @@ "gulp-stylefmt": "^1.1.0", "gulp-stylelint": "^4.0.0", "gulp-uglify": "^3.0.0", - "gulp-uglify-es": "^1.0.0", + "gulp-uglify-es": "^1.0.1", "gulp-util": "^3.0.7", "gulp-watch": "^5.0.0", "gulp-zip": "^4.0.0", @@ -219,7 +222,7 @@ "jsdom": "^11.1.0", "jsdom-global": "^3.0.2", "jshint-stylish": "~2.2.1", - "karma": "^1.7.1", + "karma": "^2.0.0", "karma-chrome-launcher": "^2.2.0", "karma-cli": "^1.0.1", "karma-firefox-launcher": "^1.0.1", diff --git a/test/stub/blacklist.json b/test/stub/blacklist.json new file mode 100644 index 000000000..6a3230b2f --- /dev/null +++ b/test/stub/blacklist.json @@ -0,0 +1,1374 @@ +{ + "version": 2, + "tolerance": 2, + "fuzzylist": [ + "metamask.io", + "myetherwallet.com", + "cryptokitties.co", + "mycrypto.com" + ], + "whitelist": [ + "crypto.pro", + "ocrypto.org", + "wecrypto.net", + "iccrypto.io", + "crypto.kred", + "ohmycrypto.io", + "spcrypto.net", + "melcrypto.com", + "zzcrypto.org", + "zzcrypto.net", + "crypto.bg", + "mycrypto24.online", + "acrypto.io", + "mycrypto.ca", + "scrypto.io", + "mycrypto.dk", + "mvzcrypto.com", + "ambcrypto.com", + "crypto.bi", + "crypto.jobs", + "crypto.help", + "my.crypt.observer", + "crypt.observer", + "ucrypto.com", + "cryptojobslist.com", + "crypto.review", + "crypto.me", + "b3crypto.com", + "mycrypto.ninja", + "jkcrypto.com", + "crypto.cr", + "mycrypto.live", + "yocrypto.io", + "crypto.ba", + "zacrypto.info", + "mycrypto.com", + "remix.ethereum.org", + "metahash.io", + "metahash.net", + "metahash.org", + "cryptotitties.com", + "cryptocities.net", + "cryptoshitties.co", + "cryptotitties.fun", + "cryptokitties.forsale", + "cryptokitties.care", + "metamate.cc", + "metamesh.tech", + "ico.nexus.social", + "metamesh.org", + "metatask.io", + "metmask.com", + "metarasa.com", + "metapack.com", + "metacase.com", + "metafas.nl", + "metamako.com", + "metamast.com", + "metamax.ru", + "metadesk.io", + "metadisk.com", + "metallsk.ru", + "metamag.fr", + "metamaks.ru", + "metamap.ru", + "metamaps.cc", + "metamats.com", + "metamax.by", + "metamax.com", + "metamax.io", + "metamuse.net", + "metarank.com", + "metaxas.com", + "megamas2.ru", + "metamask.io", + "myetherwallet.com", + "myethlerwallet.com", + "ethereum.org", + "myetheroll.com", + "myetherapi.com", + "ledgerwallet.com", + "databrokerdao.com", + "etherscan.io", + "etherid.org", + "ether.cards", + "etheroll.com", + "ethnews.com", + "ethex.market", + "ethereumdev.io", + "ethereumdev.kr", + "dether.io", + "ethermine.org", + "slaask.com", + "etherbtc.io", + "ethereal.capital", + "etherisc.com", + "m.famalk.net", + "etherecho.com", + "ethereum.os.tc", + "theethereum.wiki", + "metajack.im", + "etherhub.io", + "ethereum.network", + "ethereum.link", + "ethereum.com", + "prethereum.org", + "ethereumj.io", + "etheraus.com", + "ethereum.dev", + "1ethereum.ru", + "ethereum.nz", + "nethereum.com", + "metabank.com", + "metamas.com", + "aventus.io", + "metabase.com", + "etherdelta.com", + "metabase.one", + "cryptokitties.co", + "remme.io", + "jibrel.network" + ], + "blacklist": [ + "xn--myethrwalle-jb9e19a.com", + "xn--myetheralle-7b9ezl.com", + "iconfoundation.co", + "fundrequest.info", + "xn--myetherwale-os8e7x.com", + "remme-ico.eu", + "gonetwork.live", + "token.gonetwork.pro", + "gonetwork.pro", + "gonetwork.eu", + "nucleus-vision.cc", + "jibreltoken.in", + "dock.so", + "dock.promo", + "xn--mycrypt-r0a.com", + "xn--mycrypt-g1a.com", + "xn--mycrpto-y2a.com", + "ethexploit.org", + "remme.in", + "remme.ws", + "remme.com.ng", + "nyeitthervvallet.com", + "xn--myeerhwailet-ooc.com", + "myeterhwaliot.com", + "remme.live", + "xn--yethewalle-to2exkhi.com", + "myetherwallet.custom-token.com", + "custom-token.com", + "sale-earn.com", + "bankera.live", + "originprotocol.io", + "trx.foundation", + "tokensale.adhive.net", + "adhive.net", + "decentral.market", + "cryptoexploite.com", + "blockclain.net", + "xn--blckchin-5za9o.info", + "xn--blkhain-m0a4pb.info", + "xn--blocchal-gmb8m.info", + "xn--blocchaln-orb.info", + "xn--blocchan-gmb7c.info", + "xn--blockaden-lsen-5pb.com", + "xn--blockchai-3vb.info", + "xn--blockchai-jvb.info", + "xn--blockchal-3vb.info", + "xn--blockcham-ipb.info", + "xn--blockchan-2pb.com", + "xn--blockchan-75a.com", + "xn--blockchan-7sb.info", + "xn--blockchan-d5a.net", + "xn--blockchan-dob.info", + "xn--blockchan-ipb.com", + "xn--blockchan-ipb.info", + "xn--blockchan-nk7d.com", + "xn--blockchan-xub.info", + "xn--blockchann-4ub.com", + "xn--blockchi-n7a50e.info", + "xn--blockchi-o8a54d.info", + "xn--blockchi-p99co8a.com", + "xn--blockchim-hdb.info", + "xn--blockchin-1xb.info", + "xn--blockchin-61a.info", + "xn--blockchin-61a.net", + "xn--blockchin-6ib.info", + "xn--blockchin-ccb.info", + "xn--blockchin-h4a.com", + "xn--blockchin-h4a.info", + "xn--blockchin-hdb.info", + "xn--blockchin-hhb.info", + "xn--blockchin-mib.net", + "xn--blockchin-wcb.com", + "xn--blockchn-fza4j.com", + "xn--blockchn-fza4j.info", + "xn--blockchn-n7a43b.info", + "xn--blockchn-p0a.info", + "xn--blockchn-tx0d4p.com", + "xn--blockclai-3vb.info", + "xn--blockclin-hdb.com", + "xn--blockclin-hdb.info", + "xn--blockclin-hdb.org", + "xn--blockflte-kirchrode-w6b.de", + "xn--blockfltenquartett-windspiel-81c.de", + "xn--blockhai-obb78c.info", + "xn--blockhain-4eb.com", + "xn--blockhain-pfb.com", + "xn--blockhain-pfb.info", + "xn--blockhain-zdb.info", + "xn--blockhan-obb65a.info", + "xn--blockhas-d6a.com", + "xn--blockwallt-j7a.com", + "xn--blokchai-fqb.info", + "xn--blokchain-nfb.info", + "xn--blokhain-28ab.info", + "xn--bockclnain-eyb.info", + "xn--mymoeo-zt7bzf.com", + "xn--mymoer-nqc1368c.com", + "xn--mymoero-c13c.com", + "xn--mymoero-s13c.com", + "xn--mymoneo-f63c.com", + "xn--mymoneo-v63c.com", + "xn--mymoneo-y53c.com", + "xn--mymoner-j0a.com", + "xn--mymoner-j5b.com", + "xn--mymoner-r0a.com", + "xn--mymoner-z0a.com", + "xn--mymoner-z2c.com", + "xn--mymonro-fya.com", + "xn--mymonro-x8a.com", + "xn--myetheallet-l58emu.com", + "xn--myetheraet-9k2ea77h.com", + "xn--myetheralet-ms8e21b.com", + "xn--myetheralle-7b9exm.com", + "xn--myetherallet-5s5f.com", + "xn--myetherallet-fs5f.com", + "xn--myetherewalle-1t1g.com", + "xn--myetherllet-pl9e6k.com", + "xn--myethervvalle-8vc.com", + "xn--myetherwaet-61ea.com", + "xn--myetherwaet-8eda.com", + "xn--myetherwaet-ns8ea.com", + "xn--myetherwale-ns8e8x.com", + "xn--myetherwalet-0fb.com", + "xn--myetherwalet-0z4f.com", + "xn--myetherwalet-814f.com", + "xn--myetherwalet-d9b.com", + "xn--myetherwalet-h14f.com", + "xn--myetherwalle-9me.com", + "xn--myetherwalle-ek5f.com", + "xn--myetherwalle-fqc.com", + "xn--myetherwalle-opc.com", + "xn--myetherwalle-q05f.com", + "xn--myetherwllet-wob.com", + "xn--myetherwllt-r7a0i.com", + "xn--myethewaliet-9d5f.com", + "xn--myethewalle-3ic0947g.com", + "xn--myethewallet-0e5f.com", + "xn--myethewallet-1kc.com", + "xn--myethewallet-bkc.com", + "xn--myethewallet-vof.com", + "xn--myethewalliet-nm1g.com", + "xn--myethewallt-kbb3019g.com", + "xn--myethewallt-w48ew7b.com", + "xn--myethrwalet-6qb6408g.com", + "xn--myethrwalet-ms8e83d.com", + "xn--myethrwallet-1db.com", + "xn--myethrwallt-29af.com", + "xn--myethrwallt-29as.com", + "xn--myethrwllet-q7a31e.com", + "xn--myethrwllet-r8a3c.com", + "fintrux.eu", + "refereum-ico.eu", + "arcblock-ico.org", + "xn--fuson-1sa.org", + "refereum-token.com", + "fintrux.co", + "ico-ton.org", + "xn--mytherwallt-cbbv.com", + "xmoneta.co", + "data-wallet.co", + "tokensale.data-wallet.co", + "xn--myeerhwallot-ooc.com", + "xn--myeterwalet-cm8epi.com", + "xn--myeterwalle-cm8ev6a.com", + "rnyetherumwallet.com", + "republic-protocol.net", + "nyeihitervvallatt.com", + "arcblock.eu", + "republicprotocol.eu", + "tokensale-fusion.com", + "myetherwalletjoin.com", + "medicalchian.com", + "myeahteirwaliet.com", + "myenhtersvvailct.com", + "trinity-token.com", + "xn--eo-yzs.com", + "zilliqa.in", + "sparc.pro", + "myetherwallet.import-tokens.com", + "token-gram.org", + "xn--shapshift-e4a.com", + "xn--shapshift-y4a.com", + "xn--shpeshift-c2a.com", + "xn--shpeshift-r1a.com", + "xn--shapshift-o4a.com", + "xn--shpeshift-w2a.com", + "xn--shapeshft-w5a.com", + "tokensale-fusion.org", + "fusion-ico.com", + "beetolen.com", + "tokencrowdsale.online", + "fusion.tokencrowdsale.online", + "beetokem.com", + "block.chaiins.in", + "origintrail.in", + "bit-z.ru", + "xn--myetherallet-nu5f.com", + "xn--mytherwalet-3qb08c.com", + "xn--myeterwllet-cm8et1d.com", + "xn--mytherwllet-q7a01e.com", + "xn--biance-xt7b.com", + "xn--bnance-wic.com", + "xn--biance-jeb.com", + "xn--bttrx-9za8334c.com", + "wwwkodakcoin.com", + "myetherwallet.uk.com", + "kodakone.cc", + "nyeihitervvallet.com", + "xn--myeterwalet-cm8eoi.com", + "nucleus.foundation", + "beetoken-ico.com", + "data-token.com", + "tron-labs.com", + "ocoin.tech", + "aionfoundation.com", + "ico-telegram.org", + "nyeihitervvallat.com", + "telegramcoin.us", + "daddi.cloud", + "daditoken.com", + "blockarray.org", + "dadi-cloud.net", + "wanchainfunding.org", + "ico-telegram.io", + "iconfoundation.site", + "iost.co", + "beetoken-ico.eu", + "cindicator.network", + "wanchainetwork.org", + "wamchain.org", + "wanchainltd.org", + "wanchainalliance.org", + "nucleus-vision.net", + "ledgerwallet.by", + "nucleuss.vision", + "myenhterswailct.com", + "cobin-hood.com", + "wanchainfoundation.org", + "xn--polniex-ex4c.com", + "xn--polniex-s1a.com", + "xn--polonex-ieb.com", + "xn--polonex-sza.com", + "xn--polonex-zw4c.com", + "xn--polonix-ws4c.com", + "xn--polonix-y8a.com", + "xn--pooniex-ojb.com", + "gramico.info", + "dimnsions.network", + "www-gemini.com", + "login-kucoin.net", + "venchain.foundation", + "grampreico.com", + "tgram.cc", + "ton-gramico.com", + "wwwpaywithink.com", + "coniomi.com", + "paywithnk.com", + "paywithlnk.com", + "iluminatto.com.br", + "pundix.eu", + "xn--bttrx-esay.com", + "xn--bttrex-w8a.com", + "xn--bnance-bwa.com", + "xn--shpeshift-11a.com", + "xn--shapeshif-ts6d.com", + "xn--shapshift-yf7d.com", + "wwwbluzelle.com", + "bluzelie.com", + "nucleus-vision.org", + "omisegonetwork.site", + "etlherzero.com", + "etlherdelta.com", + "xn--condesk-0ya.com", + "xn--condesk-sfb.com", + "xn--coindsk-vs4c.com", + "iexecplatform.com", + "tongramico.com", + "nucleus-vision.eu", + "intchain.network", + "wanchain.cloud", + "bluzelle-ico.com", + "ethzero-wallet.com", + "xn--metherwalle-jb9et7d.com", + "xn--coinesk-jo3c.com", + "venchainfoundation.com", + "myenhtersvvailot.com", + "ether-zero.net", + "ins.foundation", + "nastoken.org", + "telcointoken.com", + "ether0.org", + "eterzero.org", + "bluzelle-ico.eu", + "bleuzelle.com", + "appcoinstoken.org", + "xn--quanstamp-8s6d.com", + "myehntersvvailct.com", + "myeherwalllet.com", + "ico-bluzelle.com", + "bluzelle.im", + "bluzelle.one", + "bluzele.sale", + "bluzele.co", + "sether.ws", + "xn--myetherwalet-6gf.com", + "xn--rnyethewaliet-om1g.com", + "rnyethervailet.com", + "mvetherwaliet.com", + "rnyetherwailet.com", + "myethervaliet.com", + "rnyethervaliet.com", + "mvetherwalilet.com", + "xn--myethewalie-3ic0947g.com", + "xn--mthrwallet-z6ac3y.com", + "xn--myeherwalie-vici.com", + "xn--myethervvalie-8vc.com", + "xn--mythrwallt-06acf.com", + "xn--mtherwallet-y9a6y.com", + "myetherwallet.applytoken.tk", + "ethereum-zero.com", + "quanstamptoken.tk", + "bluzelle.network", + "ether-wallet.org", + "tron-wallet.info", + "appcoinsproject.com", + "vechain.foundation", + "tronlab.site", + "tronlabs.network", + "bluzelle.cc", + "ethblender.com", + "ethpaperwallet.net", + "waltontoken.org", + "icoselfkey.org", + "etherzeroclaim.com", + "etherzero.promo", + "bluzelle.pro", + "token-selfkey.org", + "xn--etherdlta-0f7d.com", + "sether.in", + "xn--ttrex-ysa9423c.com", + "bluzelle.eu", + "bluzelle.site", + "gifto.tech", + "xn--os-g7s.com", + "selfkey.co", + "xn--myeherwalet-ns8exy.com", + "xn--coinelegraph-wk5f.com", + "dai-stablecoin.com", + "eos-token.org", + "venchain.org", + "gatcoins.io", + "deepbrainchain.co", + "myetherwalililet.info", + "myehvterwallet.com", + "myehterumswallet.com", + "nucleusico.com", + "tronlab.tech", + "0x-project.com", + "gift-token-events.mywebcommunity.org", + "funfairtoken.org", + "breadtokenapp.com", + "cloudpetstore.com", + "myethwalilet.com", + "selfkeys.org", + "wallet-ethereum.com", + "xn--methrwallt-26ar0z.com", + "xn--mytherwllet-r8a0c.com", + "bluzelle.promo", + "tokensale.bluzelle.promo", + "cedarlake.org", + "marketingleads4u.com", + "cashaa.co", + "xn--inance-hrb.com", + "wanchain.tech", + "zenprolocol.com", + "ethscan.io", + "etherscan.in", + "props-project.com", + "zilliaq.com", + "reqestnetwork.com", + "etherdelta.pw", + "ethereum-giveaway.org", + "mysimpletoken.org", + "binancc.com", + "blnance.org", + "elherdelta.io", + "xn--hapeshit-ez9c2y.com", + "tenxwallet.co", + "singularitynet.info", + "mytlherwaliet.info", + "iconmainnet.ml", + "tokenselfkey.org", + "xn--myetewallet-cm8e5y.com", + "envione.org", + "myetherwalletet.com", + "claimbcd.com", + "ripiocreditnetwork.in", + "xn--yeterwallet-ml8euo.com", + "ethclassicwallet.info", + "myltherwallet.ru.com", + "etherdella.com", + "xn--yeterwallet-bm8ewn.com", + "singularty.net", + "cloudkitties.co", + "iconfoundation.io", + "kittystat.com", + "gatscoin.io", + "singularitynet.in", + "sale.canay.io", + "canay.io", + "wabicoin.co", + "envion.top", + "sirinslabs.com", + "tronlab.co", + "paxful.com.ng", + "changellyli.com", + "ethereum-code.com", + "xn--plonex-6va6c.com", + "envion.co", + "envion.cc", + "envion.site", + "ethereumchain.info", + "xn--envon-1sa.org", + "xn--btstamp-rfb.net", + "envlon.org", + "envion-ico.org", + "spectivvr.org", + "sirinlbs.com", + "ethereumdoubler.life", + "xn--myetherwllet-fnb.com", + "sirin-labs.com", + "sirin-labs.org", + "envion.one", + "envion.live", + "propsproject.org", + "propsprojects.com", + "decentralland.org", + "xn--metherwalet-ns8ep4b.com", + "redpulsetoken.co", + "propsproject.tech", + "xn--myeterwalet-nl8emj.com", + "powrerledger.com", + "cryptokitties.com", + "sirinlabs.pro", + "sirinlabs.co", + "sirnlabs.com", + "superbitcoin-blockchain.info", + "hellobloom.me", + "mobus.network", + "powrrledger.com", + "xn--myeherwalet-ms8eyy.com", + "qlink-ico.com", + "gatcoin.in", + "tokensale.gamefllp.com", + "gamefllp.com", + "xn--myeherwalle-vici.com", + "xn--myetherwalet-39b.com", + "xn--polonex-ffb.com", + "xn--birex-leba.com", + "raiden-network.org", + "sirintabs.com", + "xn--metherwallt-79a30a.com", + "xn--myethrwllet-2kb3p.com", + "myethlerwallet.eu", + "xn--btrex-b4a.com", + "powerrledger.com", + "xn--cointeegraph-wz4f.com", + "myerherwalet.com", + "qauntstanp.com", + "myetherermwallet.com", + "xn--myethewalet-ns8eqq.com", + "xn--nvion-hza.org", + "nnyetherwallelt.ru.com", + "ico-wacoin.com", + "xn--myeterwalet-nl8enj.com", + "bitcoinsilver.io", + "t0zero.com", + "tokensale.gizer.in", + "gizer.in", + "wabitoken.com", + "gladius.ws", + "xn--metherwallt-8bb4w.com", + "quanttstamp.com", + "gladius.im", + "ethereumstorage.net", + "powerledgerr.com", + "xn--myeherwallet-4j5f.com", + "quamtstamp.com", + "quntstamp.com", + "xn--changely-j59c.com", + "shapeshlft.com", + "coinbasenews.co.uk", + "xn--metherwallet-hmb.com", + "envoin.org", + "powerledger.com", + "bitstannp.net", + "xn--myetherallet-4k5fwn.com", + "xn--coinbas-pya.com", + "requestt.network", + "oracls.network", + "sirinlabs.website", + "powrledger.io", + "slackconfirm.com", + "shape-shift.io", + "oracles-network.org", + "xn--myeherwalle-zb9eia.com", + "blockstack.one", + "urtust.io", + "bittrex.one", + "t0-ico.com", + "xn--cinbase-90a.com", + "xn--metherwalet-ns8ez1g.com", + "tzero-ico.com", + "tzero.su", + "tzero.website", + "blockstack.network", + "ico-tzero.com", + "spectre.site", + "tzero.pw", + "spectre-ai.net", + "xn--waxtokn-y8a.com", + "dmarket.pro", + "bittrex.com11648724328774.cf", + "bittrex.com1987465798.ga", + "autcus.org", + "t-zero.org", + "xn--zero-zxb.com", + "myetherwalletfork.com", + "blokclbain.info", + "datum.sale", + "spectre-ai.org", + "powerledgr.com", + "simpletoken.live", + "sale.simpletoken.live", + "qauntstamp.com", + "raiden-network.com", + "metalpayme.com", + "quantstamp-ico.com", + "myetherwailetclient.com", + "biockchain.biz", + "wallets-blockchain.com", + "golemairdrop.com", + "omisegoairdrop.net", + "blodkchainwallet.info", + "walton-chain.org", + "elite888-ico.com", + "bitflyerjp.com", + "chainlinksmartcontract.com", + "stormtoken.eu", + "omise-go.tech", + "saltending.com", + "stormltoken.com", + "xn--quanttamp-42b.com", + "stormtoken.co", + "storntoken.com", + "stromtoken.com", + "storm-token.com", + "stormtokens.io", + "ether-delta.com", + "ethconnect.live", + "ethconnect.trade", + "xn--bttrex-3va.net", + "quantstamp.com.co", + "wancha.in", + "augur-network.com", + "quantstamp.com.ua", + "myetherwalletmew.com", + "myetherumwalletts.com", + "xn--quanstamp-tmd.com", + "quantsstamps.com", + "changellyl.net", + "xn--myetherwalet-1fb.com", + "myethereumwallets.com", + "xn--myetherwalet-e9b.com", + "quantslamp.com", + "metelpay.com", + "xn--eterdelta-m75d.com", + "linksmartcontract.com", + "myetherwalletaccess.com", + "myetherwalletcheck.com", + "myetherwalletcheck.info", + "myetherwalletconf.com", + "myetherwalleteal.com", + "myetherwalletec.com", + "myetherwalletgeth.com", + "myetherwalletmetamask.com", + "myetherwalletmm.com", + "myetherwalletmy.com", + "myetherwalletnh.com", + "myetherwalletnod.com", + "myetherwalletrr.com", + "myetherwalletrty.com", + "myetherwalletsec.com", + "myetherwalletsecure.com", + "myetherwalletutc.com", + "myetherwalletver.info", + "myetherwalletview.com", + "myetherwalletview.info", + "myetherwalletvrf.com", + "myetherwalletmist.com", + "myetherwalletext.com", + "myetherwalletjson.com", + "mettalpay.com", + "bricklblock.io", + "bittrexy.com", + "utrust.so", + "myethierwallet.org", + "metallpay.com", + "kraken-wallet.com", + "dmarkt.io", + "etherdeltla.com", + "unlversa.io", + "universa.sale", + "mercuryprotocol.live", + "ripiocredlt.network", + "myetlherwa11et.com", + "dentacoin.in", + "rdrtg.com", + "myetherwallet.com.rdrgh.com", + "rdrgh.com", + "ripiocreditnetwork.co", + "riaden.network", + "hydrominer.biz", + "rdrblock.com", + "reqest.network", + "senstoken.com", + "myetherwallat.services", + "ripiocredit.net", + "xn--metherwallet-c06f.com", + "ico.ripiocredits.com", + "ripiocredits.com", + "raidens.network", + "artoken.co", + "myetherwalletlgn.com", + "etherblog.click", + "stormtoken.site", + "httpmyetherwallet.com", + "myetherwalletverify.com", + "byzantiumfork.com", + "myetherwallet.com.byzantiumfork.com", + "www-myethervvallet.com", + "ether24.info", + "block-v.io", + "bittrex.cash", + "shapishift.io", + "ripiocerdit.network", + "rnyetherwa11et.com", + "claimether.com", + "enigmatokensale.com", + "ethereum-org.com", + "mvetnerwallet.com", + "myctherwallet.com", + "myetherwaltet.com", + "myetherwatlet.com", + "privatix.me", + "myetherwalletcnf.com", + "myetherwalletver.com", + "privatix.top", + "privatix.pro", + "privatex.io", + "stormtoken.cc", + "raiden.online", + "stormstoken.com", + "myetereumwallet.com", + "stormtokens.net", + "myetherwalletconf.info", + "storrntoken.com", + "worldofbattles.io", + "ico.worldofbattles.io", + "privatix.live", + "riden.network", + "raidan.network", + "ralden.network", + "mymyetherwallet.com", + "myetherwallets.net", + "myetherwalletverify.info", + "stormxtoken.com", + "myethereum-wallet.com", + "myetherwallet-forkprep.pagedemo.co", + "myetnerwailet.com", + "www-mvetherwallet.com", + "etheirdelta.com", + "myetherwalletiu.com", + "myetherwaiiett.com", + "xn--mytherwalet-cbb87i.com", + "xn--myethrwallet-ivb.co", + "xn--myeterwallet-f1b.com", + "myehterwaliet.com", + "omegaone.co", + "myetherwaiietw.com", + "slack.com.ru", + "polkodot.network", + "request-network.net", + "requestnetwork.live", + "binancie.com", + "first-eth.info", + "myewerthwalliet.com", + "enjincoin.pw", + "xn--bitrex-k17b.com", + "alrswap.io", + "www-request.network", + "myetnenwallet.com", + "www-enigma.co", + "cryptoinsidenews.com", + "air-swap.tech", + "launch.airswap.cc", + "airswap.cc", + "airswaptoken.com", + "launch.airswap.in", + "airswap.in", + "security-steemit.com.mx", + "blockchalnwallet.com", + "blodkchainwallet.com", + "blodkchaln.com", + "myethereumwaiiet.com", + "myethereumwaliet.com", + "myethereumwalilet.com", + "myetherswailet.com", + "myetherswaliet.com", + "myetherswalilet.com", + "myetherwalilett.com", + "myetherwalletl.com", + "myetherwalletww.com", + "myethereunwallet.com", + "myethereumwallct.com", + "myetherwaiieti.com", + "myetherwaiiete.com", + "upfirng.com", + "paypie.net", + "paypie.tech", + "soam.co", + "myetherwaiict.com", + "numerai-token.com", + "www-bankera.com", + "vvanchain.org", + "omisegoairdrop.com", + "xn--enjncoin-41a.io", + "suncontract.su", + "myetherwaiietr.com", + "shapeshiff.io", + "warchain.org", + "myethwallett.com", + "myethervvaliet.com", + "wanchains.org", + "etherparty.in", + "enjincoin.me", + "etiam.io", + "invest.smartlands.tech", + "smartlands.tech", + "enijncoin.io", + "wanchain.network", + "nimiq.su", + "enjincoin.sale", + "tenxwallet.io", + "golem-network.net", + "myyethwallet.ml", + "mywetherwailiet.com", + "omg-omise.com", + "district0x.tech", + "centra-token.com", + "etherdetla.com", + "etnerparty.io", + "etherdelta.su", + "myetherwallett.neocities.org", + "myetherwallet-secure.com", + "myethereumwalletntw.info", + "real-markets.io", + "wallet-ethereum.org", + "request-network.com", + "shapeshifth.io", + "shiapeshift.in", + "coin.red-puise.com", + "ibittreix.com", + "coinkbase.com", + "cindicator.pro", + "myetherwallet.com.ailogin.me", + "eventchain.co", + "kinkik.in", + "myetherumwalletview.com", + "protostokenhub.com", + "coinrbase.com", + "myetherwalletlogin.com", + "omisegotoken.com", + "myethereumwalletntw.com", + "reall.markets", + "cobinhood.org", + "cobinhood.io", + "happy-coin.org", + "bitfinex.com.co", + "bitfienex.com", + "iconn.foundation", + "centra.vip", + "smartcontract.live", + "icon.community", + "air-token.com", + "centra.credit", + "myetherwallet-singin.com", + "smartcontractlink.com", + "shapesshift.io", + "0xtoken.io", + "augurproject.co", + "ethereumus.one", + "myetherumwalet.com", + "myetherwalletsignin.com", + "change-bank.org", + "charge-bank.com", + "myetherwalletsingin.com", + "myetherwalletcontract.com", + "change-bank.io", + "chainlink.tech", + "myetherwallet-confirm.com", + "tokensale.kybernet.network", + "kybernet.network", + "kyberr.network", + "kybernetwork.io", + "myetherwalletconfirm.com", + "kvnuke.github.io", + "kin.kikpro.co", + "myethereumwallet.co.uk", + "tokensale-kyber.network", + "kyber-network.co", + "tokensale.kyber-network.co", + "pyro0.github.io", + "tokensale.kyber.digital", + "kyber.digital", + "omise-go.me", + "my.etherwallet.com.de", + "bepartof.change-bank.co", + "change-bank.co", + "enigma-tokens.co", + "coinbase.com.eslogin.co", + "xn--bittrx-mva.com", + "ethrdelta.github.io", + "etherdellta.com", + "ico-nexus.social", + "red-pulse.tech", + "bitj0b.io", + "xn--bttrex-bwa.com", + "kin-klk.com", + "kin-crowdsale.com", + "ethedelta.com", + "coindash.su", + "myethwallet.co.uk", + "swarm.credit", + "myethereumwallet.uk", + "iconexu.social", + "wanchain.co", + "enigrna.co", + "linknetwork.co", + "qtum-token.com", + "omisego.com.co", + "rivetzintl.org", + "etherdelta.one", + "the-ether.pro", + "etherdelta.gitnub.io", + "kirkik.com", + "monetha.ltd", + "vlberate.io", + "ethereumwallet-kr.info", + "omise-go.org", + "iconexus.social", + "bittirrex.com", + "aventus.pro", + "atlant.solutions", + "aventus.group", + "metamak.io", + "omise.com.co", + "herotokens.io", + "starbase.pro", + "etherdelta.githulb.io", + "herotoken.co", + "kinico.net", + "dmarket.ltd", + "etherdelta.gilthub.io", + "golem-network.com", + "etnerscan.io", + "bllttriex.com", + "monetha.me", + "monetha.co", + "monetha-crowdsale.com", + "starbase.tech", + "aventus-crowdsale.com", + "shapeshift.pro", + "bllttrex.com", + "kickico.co", + "statustoken.im", + "bilttrex.com", + "tenxpay.io", + "bittrex.ltd", + "metalpay.im", + "aragon.im", + "coindash.tech", + "decentraland.tech", + "decentraland.pro", + "status-token.com", + "bittrex.cam", + "enigmatoken.com", + "unocoin.company", + "unocoin.fund", + "0xproject.io", + "0xtoken.com", + "numerai.tech", + "decentraiand.org", + "blockcrein.info", + "blockchealn.info", + "bllookchain.info", + "blockcbhain.info", + "myetherwallet.com.ethpromonodes.com", + "mettamask.io", + "tokenswap.org", + "netherum.com", + "etherexx.org", + "etherume.io", + "ethereum.plus", + "ehtereum.org", + "etereurm.org", + "etheream.com", + "ethererum.org", + "ethereum.io", + "etherdelta-glthub.com", + "cryptoalliance.herokuapp.com", + "bitspark2.com", + "indorsetoken.com", + "iconexus.tk", + "iconexus.ml", + "iconexus.ga", + "iconexus.cf", + "etherwallet.online", + "wallet-ethereum.net", + "bitsdigit.com", + "etherswap.org", + "eos.ac", + "uasfwallet.com", + "ziber.io", + "multiply-ethereum.info", + "bittrex.comze.com", + "karbon.vacau.com", + "etherdelta.gitlhub.io", + "etherdelta.glthub.io", + "digitaldevelopersfund.vacau.com", + "district-0x.io", + "coin-dash.com", + "coindash.ru", + "district0x.net", + "aragonproject.io", + "coin-wallet.info", + "coinswallet.info", + "contribute-status.im", + "ether-api.com", + "ether-wall.com", + "mycoinwallet.net", + "ethereumchamber.com", + "ethereumchamber.net", + "ethereumchest.com", + "ethewallet.com", + "myetherwallet.com.vc", + "myetherwallet.com.pe", + "myetherwallet.us.com", + "myetherwallet.com.u0387831.cp.regruhosting.ru", + "myethereumwallet.su", + "myetherweb.com.de", + "myetherieumwallet.com", + "myetehrwallet.com", + "myeterwalet.com", + "myetherwaiiet.com", + "myetherwallet.info", + "myetherwallet.ch", + "myetherwallet.om", + "myethervallet.com", + "myetherwallet.com.cm", + "myetherwallet.com.co", + "myetherwallet.com.de", + "myetherwallet.com.gl", + "myetherwallet.com.im", + "myetherwallet.com.ua", + "secure-myetherwallet.com", + "update-myetherwallet.com", + "wwwmyetherwallet.com", + "myeatherwallet.com", + "myetharwallet.com", + "myelherwallel.com", + "myetherwaillet.com", + "myetherwaliet.com", + "myetherwallel.com", + "myetherwallet.cam", + "myetherwallet.cc", + "myetherwallet.co", + "myetherwallet.cm", + "myetherwallet.cz", + "myetherwallet.org", + "myetherwallet.tech", + "myetherwallet.top", + "myetherwallet.net", + "myetherwallet.ru.com", + "myetherwallet.com.ru", + "metherwallet.com", + "myetrerwallet.com", + "myetlerwallet.com", + "myethterwallet.com", + "myethwallet.io", + "myethterwallet.co", + "myehterwallet.co", + "myaetherwallet.com", + "myetthterwallet.com", + "myetherwallet.one", + "myelterwallet.com", + "myetherwallet.gdn", + "myetherwallt.com", + "myeterwallet.com", + "myeteherwallet.com", + "myethearwailet.com", + "myetherwallelt.com", + "myetherwallett.com", + "etherwallet.org", + "myetherewallet.com", + "myeherwallet.com", + "myethcrwallet.com", + "myetherwallet.link", + "myetherwallets.com", + "myethearwaillet.com", + "myethearwallet.com", + "myetherawllet.com", + "myethereallet.com", + "myetherswallet.com", + "myetherwalet.com", + "myetherwaller.com", + "myetherwalliet.com", + "myetherwllet.com", + "etherwallet.io", + "myetherwallet.ca", + "myetherwallet.me", + "myetherwallet.ru", + "myetherwallet.xyz", + "myetherwallte.com", + "myethirwallet.com", + "myethrewallet.com", + "etherwallet.net", + "maetherwallet.com", + "meyetherwallet.com", + "my.ether-wallet.pw", + "myehterwallet.com", + "myeitherwallet.com", + "myelherwallet.com", + "myeltherwallet.com", + "myerherwallet.com", + "myethearwalet.com", + "myetherewalle.com", + "myethervvallet.com", + "myetherwallent.com", + "myetherwallet.fm", + "myetherwalllet.com", + "myetherwalltet.com", + "myetherwollet.com", + "myetlherwalet.com", + "myetlherwallet.com", + "rnyetherwallet.com", + "etherclassicwallet.com", + "omg-omise.co", + "omise-go.com", + "omise-go.net", + "omise-omg.com", + "omise-go.io", + "tenx-tech.com", + "bitclaive.com", + "tokensale-tenx.tech", + "ubiqcoin.org", + "metamask.com", + "ethtrade.io", + "myetcwallet.com", + "account-kigo.net", + "bitcoin-wallet.net", + "blocklichan.info", + "bloclkicihan.info", + "coindash.ml", + "eos-bonus.com", + "eos-io.info", + "ether-wallet.net", + "ethereum-wallet.info", + "ethereum-wallet.net", + "ethereumchest.net", + "reservations-kigo.net", + "reservations-lodgix.com", + "secure-liverez.com", + "secure-onerooftop.com", + "settings-liverez.com", + "software-liverez.com", + "software-lodgix.com", + "unhackableetherwallets.com", + "www-myetherwallet.com", + "etherwallet.co.za", + "etherwalletchain.com", + "etherwallets.net", + "etherwallets.nl", + "my-ethwallet.com", + "my.ether-wallet.co", + "myetherwallet.com.am", + "myetherwallet.com.ht", + "myetherwalletcom.com", + "myehterwailet.com", + "xn--myetherwalle-xoc.com", + "xn--myetherwalle-44i.com", + "xn--myetherwalle-xhk.com", + "xn--myetherwallt-cfb.com", + "xn--myetherwallt-6tb.com", + "xn--myetherwallt-xub.com", + "xn--myetherwallt-ovb.com", + "xn--myetherwallt-fwb.com", + "xn--myetherwallt-5wb.com", + "xn--myetherwallt-jzi.com", + "xn--myetherwallt-2ck.com", + "xn--myetherwallt-lok.com", + "xn--myetherwallt-lsl.com", + "xn--myetherwallt-ce6f.com", + "xn--myetherwalet-mcc.com", + "xn--myetherwalet-xhf.com", + "xn--myetherwalet-lcc.com", + "xn--myetherwaet-15ba.com", + "xn--myetherwalet-whf.com", + "xn--myetherwaet-v2ea.com", + "xn--myetherwllet-59a.com", + "xn--myetherwllet-jbb.com", + "xn--myetherwllet-wbb.com", + "xn--myetherwllet-9bb.com", + "xn--myetherwllet-ncb.com", + "xn--myetherwllet-0cb.com", + "xn--myetherwllet-5nb.com", + "xn--myetherwllet-ktd.com", + "xn--myetherwllet-mre.com", + "xn--myetherwllet-76e.com", + "xn--myetherwllet-o0l.com", + "xn--myetherwllet-c45f.com", + "xn--myetherallet-ejn.com", + "xn--myethewallet-4nf.com", + "xn--myethewallet-iof.com", + "xn--myethewallet-mpf.com", + "xn--myethewallet-6bk.com", + "xn--myethewallet-i31f.com", + "xn--myethrwallet-feb.com", + "xn--myethrwallt-fbbf.com", + "xn--myethrwallet-seb.com", + "xn--myethrwallt-rbbf.com", + "xn--myethrwallet-5eb.com", + "xn--myethrwallt-3bbf.com", + "xn--myethrwallet-0tb.com", + "xn--myethrwallt-tpbf.com", + "xn--myethrwallet-rub.com", + "xn--myethrwallt-iqbf.com", + "xn--myethrwallet-ivb.com", + "xn--myethrwallt-6qbf.com", + "xn--myethrwallet-8vb.com", + "xn--myethrwallt-vrbf.com", + "xn--myethrwallet-zwb.com", + "xn--myethrwallt-ksbf.com", + "xn--myethrwallet-dzi.com", + "xn--myethrwallt-wbif.com", + "xn--myethrwallet-wck.com", + "xn--myethrwallt-skjf.com", + "xn--myethrwallet-fok.com", + "xn--myethrwallt-fvjf.com", + "xn--myethrwallet-fsl.com", + "xn--myethrwallt-fwkf.com", + "xn--myethrwallet-5d6f.com", + "xn--myethrwallt-319ef.com", + "xn--myeterwallet-ufk.com", + "xn--myeterwallet-nrl.com", + "xn--myeterwallet-von.com", + "xn--myeterwallet-jl6c.com", + "xn--myeherwallet-ooc.com", + "xn--myeherwalle-6hci.com", + "xn--myeherwallet-v4i.com", + "xn--myeherwalle-zgii.com", + "xn--myeherwallet-ohk.com", + "xn--myeherwalle-6oji.com", + "xn--mytherwallet-ceb.com", + "xn--mythrwallet-cbbc.com", + "xn--mythrwallt-c7acf.com", + "xn--mytherwallet-peb.com", + "xn--mythrwallet-obbc.com", + "xn--mythrwallt-n7acf.com", + "xn--mytherwallet-2eb.com", + "xn--mythrwallet-0bbc.com", + "xn--mythrwallt-y7acf.com", + "xn--mytherwallet-xtb.com", + "xn--mythrwallet-qpbc.com", + "xn--mythrwallt-jlbcf.com", + "xn--mytherwallet-oub.com", + "xn--mythrwallet-fqbc.com", + "xn--mythrwallt-5lbcf.com", + "xn--mythrwallet-3qbc.com", + "xn--mythrwallt-smbcf.com", + "xn--mytherwallet-5vb.com", + "xn--mythrwallet-srbc.com", + "xn--mythrwallt-fnbcf.com", + "xn--mytherwallet-wwb.com", + "xn--mythrwallet-hsbc.com", + "xn--mythrwallt-1nbcf.com", + "xn--mytherwallet-9yi.com", + "xn--mythrwallet-tbic.com", + "xn--mythrwallt-dnhcf.com", + "xn--mytherwallet-tck.com", + "xn--mythrwallet-pkjc.com", + "xn--mythrwallt-lsicf.com", + "xn--mytherwallet-cok.com", + "xn--mythrwallet-cvjc.com", + "xn--mythrwallt-c2icf.com", + "xn--mytherwallet-csl.com", + "xn--mythrwallet-cwkc.com", + "xn--mythrwallt-c0jcf.com", + "xn--mytherwallet-2d6f.com", + "xn--mythrwallet-019ec.com", + "xn--mythrwallt-yq3ecf.com", + "xn--metherwallet-qlb.com", + "xn--metherwallet-1uf.com", + "xn--metherwallet-iyi.com", + "xn--metherwallet-zhk.com", + "xn--metherwallet-3ml.com", + "xn--mytherwallet-fvb.com", + "xn--myetherwallt-7db.com", + "xn--myetherwallt-leb.com", + "xn--myetherwallt-yeb.com", + "xn--yetherwallet-vjf.com", + "xn--yetherwallet-dfk.com", + "xn--yetherwallet-1t1f.com", + "xn--yetherwallet-634f.com", + "xn--myeherwallet-fpc.com", + "xn--myethewallt-crb.com", + "xn--metherwallet-1vc.com", + "xn--myeherwallt-kbb8039g.com", + "xn--myeherwallet-vk5f.com", + "xn--yethewallet-iw8ejl.com", + "xn--bittrx-th8b.com", + "xn--polniex-n0a.com", + "thekey.vin", + "thekey-vip.com", + "digitexftures.com", + "ethzero-wallet.org", + "zeepln.io", + "wepowers.network", + "wepower.vision" + ] +} diff --git a/test/unit/blacklist-controller-test.js b/test/unit/blacklist-controller-test.js index a9260466f..cbf73d3e5 100644 --- a/test/unit/blacklist-controller-test.js +++ b/test/unit/blacklist-controller-test.js @@ -38,4 +38,4 @@ describe('blacklist controller', function () { assert.equal(result, false) }) }) -})
\ No newline at end of file +}) diff --git a/test/unit/development/sample-changelog.md b/test/unit/development/sample-changelog.md new file mode 100644 index 000000000..8fc9d2145 --- /dev/null +++ b/test/unit/development/sample-changelog.md @@ -0,0 +1,914 @@ +# Changelog + +## Current Master + +## 4.1.3 2018-2-28 + +- Ensure MetaMask's inpage provider is named MetamaskInpageProvider to keep some sites from breaking. +- Add retry transaction button back into classic ui. + +## 4.1.2 2018-2-28 + +- Actually includes all the fixes mentioned in 4.1.1 (sorry) + +## 4.1.1 2018-2-28 + +- Fix "Add Token" screen referencing missing token logo urls +- Prevent user from switching network during signature request +- Fix misleading language "Contract Published" -> "Contract Deployment" +- Fix cancel button on "Buy Eth" screen +- Improve new-ui onboarding flow style + +## 4.1.0 2018-2-27 + +- Report failed txs to Sentry with more specific message +- Fix internal feature flags being sometimes undefined +- Standardized license to MIT + +## 4.0.0 2018-2-22 + +- Introduce new MetaMask user interface. + +## 3.14.2 2018-2-15 + +- Fix bug where log subscriptions would break when switching network. +- Fix bug where storage values were cached across blocks. +- Add MetaMask light client [testing container](https://github.com/MetaMask/mesh-testing) + +## 3.14.1 2018-2-1 + +- Further fix scrolling for Firefox. + +## 3.14.0 2018-2-1 + +- Removed unneeded data from storage +- Add a "reset account" feature to Settings +- Add warning for importing some kinds of files. +- Scrollable Setting view for Firefox. + +## 3.13.8 2018-1-29 + +- Fix provider for Kovan network. +- Bump limit for EventEmitter listeners before warning. +- Display Error when empty string is entered as a token address. + +## 3.13.7 2018-1-22 + +- Add ability to bypass gas estimation loading indicator. +- Forward failed transactions to Sentry error reporting service +- Re-add changes from 3.13.5 + +## 3.13.6 2017-1-18 + +- Roll back changes to 3.13.4 to fix some issues with the new Infura REST provider. + +## 3.13.5 2018-1-16 + +- Estimating gas limit for simple ether sends now faster & cheaper, by avoiding VM usage on recipients with no code. +- Add an extra px to address for Firefox clipping. +- Fix Firefox scrollbar. +- Open metamask popup for transaction confirmation before gas estimation finishes and add a loading screen over transaction confirmation. +- Fix bug that prevented eth_signTypedData from signing bytes. +- Further improve gas price estimation. + +## 3.13.4 2018-1-9 + +- Remove recipient field if application initializes a tx with an empty string, or 0x, and tx data. Throw an error with the same condition, but without tx data. +- Improve gas price suggestion to be closer to the lowest that will be accepted. +- Throw an error if a application tries to submit a tx whose value is a decimal, and inform that it should be in wei. +- Fix bug that prevented updating custom token details. +- No longer mark long-pending transactions as failed, since we now have button to retry with higher gas. +- Fix rounding error when specifying an ether amount that has too much precision. +- Fix bug where incorrectly inputting seed phrase would prevent any future attempts from succeeding. + +## 3.13.3 2017-12-14 + +- Show tokens that are held that have no balance. +- Reduce load on Infura by using a new block polling endpoint. + +## 3.13.2 2017-12-9 + +- Reduce new block polling interval to 8000 ms, to ease server load. + +## 3.13.1 2017-12-7 + +- Allow Dapps to specify a transaction nonce, allowing dapps to propose resubmit and force-cancel transactions. + +## 3.13.0 2017-12-7 + +- Allow resubmitting transactions that are taking long to complete. + +## 3.12.1 2017-11-29 + +- Fix bug where a user could be shown two different seed phrases. +- Detect when multiple web3 extensions are active, and provide useful error. +- Adds notice about seed phrase backup. + +## 3.12.0 2017-10-25 + +- Add support for alternative ENS TLDs (Ethereum Name Service Top-Level Domains). +- Lower minimum gas price to 0.1 GWEI. +- Remove web3 injection message from production (thanks to @ChainsawBaby) +- Add additional debugging info to our state logs, specifically OS version and browser version. + +## 3.11.2 2017-10-21 + +- Fix bug where reject button would sometimes not work. +- Fixed bug where sometimes MetaMask's connection to a page would be unreliable. + +## 3.11.1 2017-10-20 + +- Fix bug where log filters were not populated correctly +- Fix bug where web3 API was sometimes injected after the page loaded. +- Fix bug where first account was sometimes not selected correctly after creating or restoring a vault. +- Fix bug where imported accounts could not use new eth_signTypedData method. + +## 3.11.0 2017-10-11 + +- Add support for new eth_signTypedData method per EIP 712. +- Fix bug where some transactions would be shown as pending forever, even after successfully mined. +- Fix bug where a transaction might be shown as pending forever if another tx with the same nonce was mined. +- Fix link to support article on token addresses. + +## 3.10.9 2017-10-5 + +- Only rebrodcast transactions for a day not a days worth of blocks +- Remove Slack link from info page, since it is a big phishing target. +- Stop computing balance based on pending transactions, to avoid edge case where users are unable to send transactions. + +## 3.10.8 2017-9-28 + +- Fixed usage of new currency fetching API. + +## 3.10.7 2017-9-28 + +- Fixed bug where sometimes the current account was not correctly set and exposed to web apps. +- Added AUD, HKD, SGD, IDR, PHP to currency conversion list + +## 3.10.6 2017-9-27 + +- Fix bug where newly created accounts were not selected. +- Fix bug where selected account was not persisted between lockings. + +## 3.10.5 2017-9-27 + +- Fix block gas limit estimation. + +## 3.10.4 2017-9-27 + +- Fix bug that could mis-render token balances when very small. (Not actually included in 3.9.9) +- Fix memory leak warning. +- Fix bug where new event filters would not include historical events. + +## 3.10.3 2017-9-21 + +- Fix bug where metamask-dapp connections are lost on rpc error +- Fix bug that would sometimes display transactions as failed that could be successfully mined. + +## 3.10.2 2017-9-18 + +rollback to 3.10.0 due to bug + +## 3.10.1 2017-9-18 + +- Add ability to export private keys as a file. +- Add ability to export seed words as a file. +- Changed state logs to a file download than a clipboard copy. +- Add specific error for failed recipient address checksum. +- Fixed a long standing memory leak associated with filters installed by dapps +- Fix link to support center. +- Fixed tooltip icon locations to avoid overflow. +- Warn users when a dapp proposes a high gas limit (90% of blockGasLimit or higher +- Sort currencies by currency name (thanks to strelok1: https://github.com/strelok1). + +## 3.10.0 2017-9-11 + +- Readded loose keyring label back into the account list. +- Remove cryptonator from chrome permissions. +- Add info on token contract addresses. +- Add validation preventing users from inputting their own addresses as token tracking addresses. +- Added button to reject all transactions (thanks to davidp94! https://github.com/davidp94) + + +## 3.9.13 2017-9-8 + +- Changed the way we initialize the inpage provider to fix a bug affecting some developers. + +## 3.9.12 2017-9-6 + +- Fix bug that prevented Web3 1.0 compatibility +- Make eth_sign deprecation warning less noisy +- Add useful link to eth_sign deprecation warning. +- Fix bug with network version serialization over synchronous RPC +- Add MetaMask version to state logs. +- Add the total amount of tokens when multiple tokens are added under the token list +- Use HTTPS links for Etherscan. +- Update Support center link to new one with HTTPS. +- Make web3 deprecation notice more useful by linking to a descriptive article. + +## 3.9.11 2017-8-24 + +- Fix nonce calculation bug that would sometimes generate very wrong nonces. +- Give up resubmitting a transaction after 3500 blocks. + +## 3.9.10 2017-8-23 + +- Improve nonce calculation, to prevent bug where people are unable to send transactions reliably. +- Remove link to eth-tx-viz from identicons in tx history. + +## 3.9.9 2017-8-18 + +- Fix bug where some transaction submission errors would show an empty screen. +- Fix bug that could mis-render token balances when very small. +- Fix formatting of eth_sign "Sign Message" view. +- Add deprecation warning to eth_sign "Sign Message" view. + +## 3.9.8 2017-8-16 + +- Reenable token list. +- Remove default tokens. + +## 3.9.7 2017-8-15 + +- hotfix - disable token list +- Added a deprecation warning for web3 https://github.com/ethereum/mist/releases/tag/v0.9.0 + +## 3.9.6 2017-8-09 + +- Replace account screen with an account drop-down menu. +- Replace account buttons with a new account-specific drop-down menu. + +## 3.9.5 2017-8-04 + +- Improved phishing detection configuration update rate + +## 3.9.4 2017-8-03 + +- Fixed bug that prevented transactions from being rejected. + +## 3.9.3 2017-8-03 + +- Add support for EGO ujo token +- Continuously update blacklist for known phishing sites in background. +- Automatically detect suspicious URLs too similar to common phishing targets, and blacklist them. + +## 3.9.2 2017-7-26 + +- Fix bugs that could sometimes result in failed transactions after switching networks. +- Include stack traces in txMeta's to better understand the life cycle of transactions +- Enhance blacklister functionality to include levenshtein logic. (credit to @sogoiii and @409H for their help!) + +## 3.9.1 2017-7-19 + +- No longer automatically request 1 ropsten ether for the first account in a new vault. +- Now redirects from known malicious sites faster. +- Added a link to our new support page to the help screen. +- Fixed bug where a new transaction would be shown over the current transaction, creating a possible timing attack against user confirmation. +- Fixed bug in nonce tracker where an incorrect nonce would be calculated. +- Lowered minimum gas price to 1 Gwei. + +## 3.9.0 2017-7-12 + +- Now detects and blocks known phishing sites. + +## 3.8.6 2017-7-11 + +- Make transaction resubmission more resilient. +- No longer validate nonce client-side in retry loop. +- Fix bug where insufficient balance error was sometimes shown on successful transactions. + +## 3.8.5 2017-7-7 + +- Fix transaction resubmit logic to fail slightly less eagerly. + +## 3.8.4 2017-7-7 + +- Improve transaction resubmit logic to fail more eagerly when a user would expect it to. + +## 3.8.3 2017-7-6 + +- Re-enable default token list. +- Add origin header to dapp-bound requests to allow providers to throttle sites. +- Fix bug that could sometimes resubmit a transaction that had been stalled due to low balance after balance was restored. + +## 3.8.2 2017-7-3 + +- No longer show network loading indication on config screen, to allow selecting custom RPCs. +- Visually indicate that network spinner is a menu. +- Indicate what network is being searched for when disconnected. + +## 3.8.1 2017-6-30 + +- Temporarily disabled loading popular tokens by default to improve performance. +- Remove SEND token button until a better token sending form can be built, due to some precision issues. +- Fix precision bug in token balances. +- Cache token symbol and precisions to reduce network load. +- Transpile some newer JavaScript, restores compatibility with some older browsers. + +## 3.8.0 2017-6-28 + +- No longer stop rebroadcasting transactions +- Add list of popular tokens held to the account detail view. +- Add ability to add Tokens to token list. +- Add a warning to JSON file import. +- Add "send" link to token list, which goes to TokenFactory. +- Fix bug where slowly mined txs would sometimes be incorrectly marked as failed. +- Fix bug where badge count did not reflect personal_sign pending messages. +- Seed word confirmation wording is now scarier. +- Fix error for invalid seed words. +- Prevent users from submitting two duplicate transactions by disabling submit. +- Allow Dapps to specify gas price as hex string. +- Add button for copying state logs to clipboard. + +## 3.7.8 2017-6-12 + +- Add an `ethereum:` prefix to the QR code address +- The default network on installation is now MainNet +- Fix currency API URL from cryptonator. +- Update gasLimit params with every new block seen. +- Fix ENS resolver symbol UI. + +## 3.7.7 2017-6-8 + +- Fix bug where metamask would show old data after computer being asleep or disconnected from the internet. + +## 3.7.6 2017-6-5 + +- Fix bug that prevented publishing contracts. + +## 3.7.5 2017-6-5 + +- Prevent users from sending to the `0x0` address. +- Provide useful errors when entering bad characters in ENS name. +- Add ability to copy addresses from transaction confirmation view. + +## 3.7.4 2017-6-2 + +- Fix bug with inflight cache that caused some block lookups to return bad values (affected OasisDex). +- Fixed bug with gas limit calculation that would sometimes create unsubmittable gas limits. + +## 3.7.3 2017-6-1 + +- Rebuilt to fix cache clearing bug. + +## 3.7.2 2017-5-31 + +- Now when switching networks sites that use web3 will reload +- Now when switching networks the extension does not restart +- Cleanup decimal bugs in our gas inputs. +- Fix bug where submit button was enabled for invalid gas inputs. +- Now enforce 95% of block's gasLimit to protect users. +- Removing provider-engine from the inpage provider. This fixes some error handling inconsistencies introduced in 3.7.0. +- Added "inflight cache", which prevents identical requests from clogging up the network, dramatically improving ENS performance. +- Fixed bug where filter subscriptions would sometimes fail to unsubscribe. +- Some contracts will now display logos instead of jazzicons. +- Some contracts will now have names displayed in the confirmation view. + +## 3.7.0 2017-5-23 + +- Add Transaction Number (nonce) to transaction list. +- Label the pending tx icon with a tooltip. +- Fix bug where website filters would pile up and not deallocate when leaving a site. +- Continually resubmit pending txs for a period of time to ensure successful broadcast. +- ENS names will no longer resolve to their owner if no resolver is set. Resolvers must be explicitly set and configured. + +## 3.6.5 2017-5-17 + +- Fix bug where edited gas parameters would not take effect. +- Trim currency list. +- Enable decimals in our gas prices. +- Fix reset button. +- Fix event filter bug introduced by newer versions of Geth. +- Fix bug where decimals in gas inputs could result in strange values. + +## 3.6.4 2017-5-8 + +- Fix main-net ENS resolution. + +## 3.6.3 2017-5-8 + +- Fix bug that could stop newer versions of Geth from working with MetaMask. + +## 3.6.2 2017-5-8 + +- Input gas price in Gwei. +- Enforce Safe Gas Minimum recommended by EthGasStation. +- Fix bug where block-tracker could stop polling for new blocks. +- Reduce UI size by removing internal web3. +- Fix bug where gas parameters would not properly update on adjustment. + +## 3.6.1 2017-4-30 + +- Made fox less nosy. +- Fix bug where error was reported in debugger console when Chrome opened a new window. + +## 3.6.0 2017-4-26 + +- Add Rinkeby Test Network to our network list. + +## 3.5.4 2017-4-25 + +- Fix occasional nonce tracking issue. +- Fix bug where some events would not be emitted by web3. +- Fix bug where an error would be thrown when composing signatures for networks with large ID values. + +## 3.5.3 2017-4-24 + +- Popup new transactions in Firefox. +- Fix transition issue from account detail screen. +- Revise buy screen for more modularity. +- Fixed some other small bugs. + +## 3.5.2 2017-3-28 + +- Fix bug where gas estimate totals were sometimes wrong. +- Add link to Kovan Test Faucet instructions on buy view. +- Inject web3 into loaded iFrames. + +## 3.5.1 2017-3-27 + +- Fix edge case where users were unable to enable the notice button if notices were short enough to not require a scrollbar. + +## 3.5.0 2017-3-27 + +- Add better error messages for when a transaction fails on approval +- Allow sending to ENS names in send form on Ropsten. +- Added an address book functionality that remembers the last 15 unique addresses sent to. +- Can now change network to custom RPC URL from lock screen. +- Removed support for old, lightwallet based vault. Users who have not opened app in over a month will need to recover with their seed phrase. This will allow Firefox support sooner. +- Fixed bug where spinner wouldn't disappear on incorrect password submission on seed word reveal. +- Polish the private key UI. +- Enforce minimum values for gas price and gas limit. +- Fix bug where total gas was sometimes not live-updated. +- Fix bug where editing gas value could have some abrupt behaviors (#1233) +- Add Kovan as an option on our network list. +- Fixed bug where transactions on other networks would disappear when submitting a transaction on another network. + +## 3.4.0 2017-3-8 + +- Add two most recently used custom RPCs to network dropdown menu. +- Add personal_sign method support. +- Add personal_ecRecover method support. +- Add ability to customize gas and gasPrice on the transaction approval screen. +- Increase default gas buffer to 1.5x estimated gas value. + +## 3.3.0 2017-2-20 + +- net_version has been made synchronous. +- Test suite for migrations expanded. +- Network now changeable from lock screen. +- Improve test coverage of eth.sign behavior, including a code example of verifying a signature. + +## 3.2.2 2017-2-8 + +- Revert eth.sign behavior to the previous one with a big warning. We will be gradually implementing the new behavior over the coming time. https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign + +- Improve test coverage of eth.sign behavior, including a code example of verifying a signature. + +## 3.2.2 2017-2-8 + +- Revert eth.sign behavior to the previous one with a big warning. We will be gradually implementing the new behavior over the coming time. https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign + +## 3.2.1 2017-2-8 + +- Revert back to old style message signing. +- Fixed some build errors that were causing a variety of bugs. + +## 3.2.0 2017-2-8 + +- Add ability to import accounts in JSON file format (used by Mist, Geth, MyEtherWallet, and more!) +- Fix unapproved messages not being included in extension badge. +- Fix rendering bug where the Confirm transaction view would let you approve transactions when the account has insufficient balance. + +## 3.1.2 2017-1-24 + +- Fix "New Account" default keychain + +## 3.1.1 2017-1-20 + +- Fix HD wallet seed export + +## 3.1.0 2017-1-18 + +- Add ability to import accounts by private key. +- Fixed bug that returned the wrong transaction hashes on private networks that had not implemented EIP 155 replay protection (like TestRPC). + +## 3.0.1 2017-1-17 + +- Fixed bug that prevented eth.sign from working. +- Fix the displaying of transactions that have been submitted to the network in Transaction History + +## 3.0.0 2017-1-16 + +- Fix seed word account generation (https://medium.com/metamask/metamask-3-migration-guide-914b79533cdd#.t4i1qmmsz). +- Fix Bug where you see an empty transaction flash by on the confirm transaction view. +- Create visible difference in transaction history between an approved but not yet included in a block transaction and a transaction who has been confirmed. +- Fix memory leak in RPC Cache +- Override RPC commands eth_syncing and web3_clientVersion +- Remove certain non-essential permissions from certain builds. +- Add a check for when a tx is included in a block. +- Fix bug where browser-solidity would sometimes warn of a contract creation error when there was none. +- Minor modifications to network display. +- Network now displays properly for pending transactions. +- Implement replay attack protections allowed by EIP 155. +- Fix bug where sometimes loading account data would fail by querying a future block. + +## 2.14.1 2016-12-20 + +- Update Coinbase info. and increase the buy amount to $15 +- Fixed ropsten transaction links +- Temporarily disable extension reload detection causing infinite reload bug. +- Implemented basic checking for valid RPC URIs. + +## 2.14.0 2016-12-16 + +- Removed Morden testnet provider from provider menu. +- Add support for notices. +- Fix broken reload detection. +- Fix transaction forever cached-as-pending bug. + +## 2.13.11 2016-11-23 + +- Add support for synchronous RPC method "eth_uninstallFilter". +- Forgotten password prompts now send users directly to seed word restoration. + +## 2.13.10 2016-11-22 + +- Improve gas calculation logic. +- Default to Dapp-specified gas limits for transactions. +- Ropsten networks now properly point to the faucet when attempting to buy ether. +- Ropsten transactions now link to etherscan correctly. + +## 2.13.9 2016-11-21 + +- Add support for the new, default Ropsten Test Network. +- Fix bug that would cause MetaMask to occasionally lose its StreamProvider connection and drop requests. +- Fix bug that would cause the Custom RPC menu item to not appear when Localhost 8545 was selected. +- Point ropsten faucet button to actual faucet. +- Phase out ethereumjs-util from our encryptor module. + +## 2.13.8 2016-11-16 + +- Show a warning when a transaction fails during simulation. +- Fix bug where 20% of gas estimate was not being added properly. +- Render error messages in confirmation screen more gracefully. + +## 2.13.7 2016-11-8 + +- Fix bug where gas estimate would sometimes be very high. +- Increased our gas estimate from 100k gas to 20% of estimate. +- Fix GitHub link on info page to point at current repository. + +## 2.13.6 2016-10-26 + +- Add a check for improper Transaction data. +- Inject up to date version of web3.js +- Now nicknaming new accounts "Account #" instead of "Wallet #" for clarity. +- Fix bug where custom provider selection could show duplicate items. +- Fix bug where connecting to a local morden node would make two providers appear selected. +- Fix bug that was sometimes preventing transactions from being sent. + +## 2.13.5 2016-10-18 + +- Increase default max gas to `100000` over the RPC's `estimateGas` response. +- Fix bug where slow-loading dapps would sometimes trigger infinite reload loops. + +## 2.13.4 2016-10-17 + +- Add custom transaction fee field to send form. +- Fix bug where web3 was being injected into XML files. +- Fix bug where changing network would not reload current Dapps. + +## 2.13.3 2016-10-4 + +- Fix bug where log queries were filtered out. +- Decreased vault confirmation button font size to help some Linux users who could not see it. +- Made popup a little taller because it would sometimes cut off buttons. +- Fix bug where long account lists would get scrunched instead of scrolling. +- Add legal information to relevant pages. +- Rename UI elements to be more consistent with one another. +- Updated Terms of Service and Usage. +- Prompt users to re-agree to the Terms of Service when they are updated. + +## 2.13.2 2016-10-4 + +- Fix bug where chosen FIAT exchange rate does no persist when switching networks +- Fix additional parameters that made MetaMask sometimes receive errors from Parity. +- Fix bug where invalid transactions would still open the MetaMask popup. +- Removed hex prefix from private key export, to increase compatibility with Geth, MyEtherWallet, and Jaxx. + +## 2.13.1 2016-09-23 + +- Fix a bug with estimating gas on Parity +- Show loading indication when selecting ShapeShift as purchasing method. + +## 2.13.0 2016-09-18 + +- Add Parity compatibility, fixing Geth dependency issues. +- Add a link to the transaction in history that goes to https://metamask.github.io/eth-tx-viz +too help visualize transactions and to where they are going. +- Show "Buy Ether" button and warning on tx confirmation when sender balance is insufficient + +## 2.12.1 2016-09-14 + +- Fixed bug where if you send a transaction from within MetaMask extension the +popup notification opens up. +- Fixed bug where some tx errors would block subsequent txs until the plugin was refreshed. + +## 2.12.0 2016-09-14 + +- Add a QR button to the Account detail screen +- Fixed bug where opening MetaMask could close a non-metamask popup. +- Fixed memory leak that caused occasional crashes. + +## 2.11.1 2016-09-12 + +- Fix bug that prevented caches from being cleared in Opera. + +## 2.11.0 2016-09-12 + +- Fix bug where pending transactions from Test net (or other networks) show up In Main net. +- Add fiat conversion values to more views. +- On fresh install, open a new tab with the MetaMask Introduction video. Does not open on update. +- Block negative values from transactions. +- Fixed a memory leak. +- MetaMask logo now renders as super lightweight SVG, improving compatibility and performance. +- Now showing loading indication during vault unlocking, to clarify behavior for users who are experiencing slow unlocks. +- Now only initially creates one wallet when restoring a vault, to reduce some users' confusion. + +## 2.10.2 2016-09-02 + +- Fix bug where notification popup would not display. + +## 2.10.1 2016-09-02 + +- Fix bug where provider menu did not allow switching to custom network from a custom network. +- Sending a transaction from within MetaMask no longer triggers a popup. +- The ability to build without livereload features (such as for production) can be enabled with the gulp --disableLiveReload flag. +- Fix Ethereum JSON RPC Filters bug. + +## 2.10.0 2016-08-29 + +- Changed transaction approval from notifications system to popup system. +- Add a back button to locked screen to allow restoring vault from seed words when password is forgotten. +- Forms now retain their values even when closing the popup and reopening it. +- Fixed a spelling error in provider menu. + +## 2.9.2 2016-08-24 + +- Fixed shortcut bug from preventing installation. + +## 2.9.1 2016-08-24 + +- Added static image as fallback for when WebGL isn't supported. +- Transaction history now has a hard limit. +- Added info link on account screen that visits Etherscan. +- Fixed bug where a message signing request would be lost if the vault was locked. +- Added shortcut to open MetaMask (Ctrl+Alt+M or Cmd+Opt/Alt+M) +- Prevent API calls in tests. +- Fixed bug where sign message confirmation would sometimes render blank. + +## 2.9.0 2016-08-22 + +- Added ShapeShift to the transaction history +- Added affiliate key to Shapeshift requests +- Added feature to reflect current conversion rates of current vault balance. +- Modify balance display logic. + +## 2.8.0 2016-08-15 + +- Integrate ShapeShift +- Add a form for Coinbase to specify amount to buy +- Fix various typos. +- Make dapp-metamask connection more reliable +- Remove Ethereum Classic from provider menu. + +## 2.7.3 2016-07-29 + +- Fix bug where changing an account would not update in a live Dapp. + +## 2.7.2 2016-07-29 + +- Add Ethereum Classic to provider menu +- Fix bug where host store would fail to receive updates. + +## 2.7.1 2016-07-27 + +- Fix bug where web3 would sometimes not be injected in time for the application. +- Fixed bug where sometimes when opening the plugin, it would not fully open until closing and re-opening. +- Got most functionality working within Firefox (still working on review process before it can be available). +- Fixed menu dropdown bug introduced in Chrome 52. + +## 2.7.0 2016-07-21 + +- Added a Warning screen about storing ETH +- Add buy Button! +- MetaMask now throws descriptive errors when apps try to use synchronous web3 methods. +- Removed firefox-specific line in manifest. + +## 2.6.2 2016-07-20 + +- Fixed bug that would prevent the plugin from reopening on the first try after receiving a new transaction while locked. +- Fixed bug that would render 0 ETH as a non-exact amount. + +## 2.6.1 2016-07-13 + +- Fix tool tips on Eth balance to show the 6 decimals +- Fix rendering of recipient SVG in tx approval notification. +- New vaults now generate only one wallet instead of three. +- Bumped version of web3 provider engine. +- Fixed bug where some lowercase or uppercase addresses were not being recognized as valid. +- Fixed bug where gas cost was misestimated on the tx confirmation view. + +## 2.6.0 2016-07-11 + +- Fix formatting of ETH balance +- Fix formatting of account details. +- Use web3 minified dist for faster inject times +- Fix issue where dropdowns were not in front of icons. +- Update transaction approval styles. +- Align failed and successful transaction history text. +- Fix issue where large domain names and large transaction values would misalign the transaction history. +- Abbreviate ether balances on transaction details to maintain formatting. +- General code cleanup. + +## 2.5.0 2016-06-29 + +- Implement new account design. +- Added a network indicator mark in dropdown menu +- Added network name next to network indicator +- Add copy transaction hash button to completed transaction list items. +- Unify wording for transaction approve/reject options on notifications and the extension. +- Fix bug where confirmation view would be shown twice. + +## 2.4.5 2016-06-29 + +- Fixed bug where MetaMask interfered with PDF loading. +- Moved switch account icon into menu bar. +- Changed status shapes to be a yellow warning sign for failure and ellipsis for pending transactions. +- Now enforce 20 character limit on wallet names. +- Wallet titles are now properly truncated in transaction confirmation. +- Fix formatting on terms & conditions page. +- Now enforce 30 character limit on wallet names. +- Fix out-of-place positioning of pending transaction badges on wallet list. +- Change network status icons to reflect current design. + +## 2.4.4 2016-06-23 + +- Update web3-stream-provider for batch payload bug fix + +## 2.4.3 2016-06-23 + +- Remove redundant network option buttons from settings page +- Switch out font family Transat for Montserrat + +## 2.4.2 2016-06-22 + +- Change out export icon for key. +- Unify copy to clipboard icon +- Fixed eth.sign behavior. +- Fix behavior of batched outbound transactions. + +## 2.4.0 2016-06-20 + +- Clean up UI. +- Remove nonfunctional QR code button. +- Make network loading indicator clickable to select accessible network. +- Show more characters of addresses when space permits. +- Fixed bug when signing messages under 64 hex characters long. +- Add disclaimer view with placeholder text for first time users. + +## 2.3.1 2016-06-09 + +- Style up the info page +- Cache identicon images to optimize for long lists of transactions. +- Fix out of gas errors + +## 2.3.0 2016-06-06 + +- Show network status in title bar +- Added seed word recovery to config screen. +- Clicking network status indicator now reveals a provider menu. + +## 2.2.0 2016-06-02 + +- Redesigned init, vault create, vault restore and seed confirmation screens. +- Added pending transactions to transaction list on account screen. +- Clicking a pending transaction takes you back to the transaction approval screen. +- Update provider-engine to fix intermittent out of gas errors. + +## 2.1.0 2016-05-26 + +- Added copy address button to account list. +- Fixed back button on confirm transaction screen. +- Add indication of pending transactions to account list screen. +- Fixed bug where error warning was sometimes not cleared on view transition. +- Updated eth-lightwallet to fix a critical security issue. + +## 2.0.0 2016-05-23 + +- UI Overhaul per Vlad Todirut's designs. +- Replaced identicons with jazzicons. +- Fixed glitchy transitions. +- Added support for capitalization-based address checksums. +- Send value is no longer limited by javascript number precision, and is always in ETH. +- Added ability to generate new accounts. +- Added ability to locally nickname accounts. + +## 1.8.4 2016-05-13 + +- Point rpc servers to https endpoints. + +## 1.8.3 2016-05-12 + +- Bumped web3 to 0.6.0 +- Really fixed `eth_syncing` method response. + +## 1.8.2 2016-05-11 + +- Fixed bug where send view would not load correctly the first time it was visited per account. +- Migrated all users to new scalable backend. +- Fixed `eth_syncing` method response. + +## 1.8.1 2016-05-10 + +- Initial usage of scalable blockchain backend. +- Made official providers more easily configurable for us internally. + +## 1.8.0 2016-05-10 + +- Add support for calls to `eth.sign`. +- Moved account exporting within subview of the account detail view. +- Added buttons to the account export process. +- Improved visual appearance of account detail transition where button heights would change. +- Restored back button to account detail view. +- Show transaction list always, never collapsed. +- Changing provider now reloads current Dapps +- Improved appearance of transaction list in account detail view. + +## 1.7.0 2016-04-29 + +- Account detail view is now the primary view. +- The account detail view now has a "Change acct" button which shows the account list. +- Clicking accounts in the account list now both selects that account and displays that account's detail view. +- Selected account is now persisted between sessions, so the current account stays selected. +- Account icons are now "identicons" (deterministically generated from the address). +- Fixed link to Slack channel. +- Added a context guard for "define" to avoid UMD's exporting themselves to the wrong module system, fixing interference with some websites. +- Transaction list now only shows transactions for the current account. +- Transaction list now only shows transactions for the current network (mainnet, testnet, testrpc). +- Fixed transaction links to etherscan blockchain explorer. +- Fixed some UI transitions that had weird behavior. + +## 1.6.0 2016-04-22 + +- Pending transactions are now persisted to localStorage and resume even after browser is closed. +- Completed transactions are now persisted and can be displayed via UI. +- Added transaction list to account detail view. +- Fix bug on config screen where current RPC address was always displayed wrong. +- Fixed bug where entering a decimal value when sending a transaction would result in sending the wrong amount. +- Add save button to custom RPC input field. +- Add quick-select button for RPC on `localhost:8545`. +- Improve config view styling. +- Users have been migrated from old test-net RPC to a newer test-net RPC. + +## 1.5.1 2016-04-15 + +- Corrected text above account list. Selected account is visible to all sites, not just the current domain. +- Merged the UI codebase into the main plugin codebase for simpler maintenance. +- Fix Ether display rounding error. Now rendering to four decimal points. +- Fix some inpage synchronous methods +- Change account rendering to show four decimals and a leading zero. + +## 1.5.0 2016-04-13 + +- Added ability to send ether. +- Fixed bugs related to using Javascript numbers, which lacked appropriate precision. +- Replaced Etherscan main-net provider with our own production RPC. + +## 1.4.0 2016-04-08 + +- Removed extra entropy text field for simplified vault creation. +- Now supports exporting an account's private key. +- Unified button and input styles across the app. +- Removed some non-working placeholder UI until it works. +- Fix popup's web3 stream provider +- Temporarily deactivated fauceting indication because it would activate when restoring an empty account. + +## 1.3.2 2016-04-04 + + - When unlocking, first account is auto-selected. + - When creating a first vault on the test-net, the first account is auto-funded. + - Fixed some styling issues. + +## 1.0.1-1.3.1 + +Many changes not logged. Hopefully beginning to log consistently now! + +## 1.0.0 + +Made seed word restoring BIP44 compatible. + +## 0.14.0 + +Added the ability to restore accounts from seed words. diff --git a/test/unit/development/sample-manifest.json b/test/unit/development/sample-manifest.json new file mode 100644 index 000000000..2b3acf1b5 --- /dev/null +++ b/test/unit/development/sample-manifest.json @@ -0,0 +1,71 @@ +{ + "name": "MetaMask", + "short_name": "Metamask", + "version": "4.1.3", + "manifest_version": 2, + "author": "https://metamask.io", + "description": "Ethereum Browser Extension", + "commands": { + "_execute_browser_action": { + "suggested_key": { + "windows": "Alt+Shift+M", + "mac": "Alt+Shift+M", + "chromeos": "Alt+Shift+M", + "linux": "Alt+Shift+M" + } + } + }, + "icons": { + "16": "images/icon-16.png", + "128": "images/icon-128.png" + }, + "applications": { + "gecko": { + "id": "webextension@metamask.io" + } + }, + "default_locale": "en", + "background": { + "scripts": [ + "scripts/chromereload.js", + "scripts/background.js" + ], + "persistent": true + }, + "browser_action": { + "default_icon": { + "19": "images/icon-19.png", + "38": "images/icon-38.png" + }, + "default_title": "MetaMask", + "default_popup": "popup.html" + }, + "content_scripts": [ + { + "matches": [ + "file://*/*", + "http://*/*", + "https://*/*" + ], + "js": [ + "scripts/contentscript.js" + ], + "run_at": "document_start", + "all_frames": true + } + ], + "permissions": [ + "storage", + "clipboardWrite", + "http://localhost:8545/", + "https://*.infura.io/" + ], + "web_accessible_resources": [ + "scripts/inpage.js" + ], + "externally_connectable": { + "matches": [ + "https://metamask.io/*" + ] + } +} diff --git a/test/unit/development/version–bump-test.js b/test/unit/development/version–bump-test.js new file mode 100644 index 000000000..1c445c8b4 --- /dev/null +++ b/test/unit/development/version–bump-test.js @@ -0,0 +1,45 @@ +const assert = require('assert') +const versionBump = require('../../../development/version-bump') +const { promisify } = require('util') +const fs = require('fs') +const readFile = promisify(fs.readFile) +const path = require('path') +const changelogPath = path.join(__dirname, 'sample-changelog.md') +const manifest = require('./sample-manifest.json') +let changelog + + +describe('version bumper', function () { + + beforeEach(async () => { + // load changelog. Mock version is 4.1.3 + const changeBuffer = await readFile(changelogPath) + changelog = changeBuffer.toString() + }) + + it('returns a properly bumped major version', async function () { + const result = await versionBump('major', changelog, manifest) + const expected = '5.0.0' + assert.equal(result.version, expected, 'major bumps correctly') + assert.equal(result.manifest.version, expected, 'major bumps correctly') + assert.ok(result.changelog.includes(expected)) + }) + + it('returns a properly bumped minor version', async function () { + const result = await versionBump('minor', changelog, manifest) + const expected = '4.2.0' + assert.equal(result.version, expected, 'minor bumps correctly') + assert.equal(result.manifest.version, expected, 'minor bumps correctly') + assert.ok(result.changelog.includes(expected)) + }) + + it('returns a properly bumped patch version', async function () { + const result = await versionBump('patch', changelog, manifest) + const expected = '4.1.4' + assert.equal(result.version, expected, 'patch bumps correctly') + assert.equal(result.manifest.version, expected, 'patch bumps correctly') + assert.ok(result.changelog.includes(expected)) + }) +}) + + diff --git a/test/unit/edge-encryptor-test.js b/test/unit/edge-encryptor-test.js new file mode 100644 index 000000000..d3f014d74 --- /dev/null +++ b/test/unit/edge-encryptor-test.js @@ -0,0 +1,101 @@ +const assert = require('assert') + +const EdgeEncryptor = require('../../app/scripts/edge-encryptor') + +var password = 'passw0rd1' +var data = 'some random data' + +global.crypto = global.crypto || { + getRandomValues: function (array) { + for (let i = 0; i < array.length; i++) { + array[i] = Math.random() * 100 + } + return array + } +} + +describe('EdgeEncryptor', function () { + + const edgeEncryptor = new EdgeEncryptor() + describe('encrypt', function () { + + it('should encrypt the data.', function (done) { + edgeEncryptor.encrypt(password, data) + .then(function (encryptedData) { + assert.notEqual(data, encryptedData) + assert.notEqual(encryptedData.length, 0) + done() + }).catch(function (err) { + done(err) + }) + }) + + it('should return proper format.', function (done) { + edgeEncryptor.encrypt(password, data) + .then(function (encryptedData) { + let encryptedObject = JSON.parse(encryptedData) + assert.ok(encryptedObject.data, 'there is no data') + assert.ok(encryptedObject.iv && encryptedObject.iv.length != 0, 'there is no iv') + assert.ok(encryptedObject.salt && encryptedObject.salt.length != 0, 'there is no salt') + done() + }).catch(function (err) { + done(err) + }) + }) + + it('should not return the same twice.', function (done) { + + const encryptPromises = [] + encryptPromises.push(edgeEncryptor.encrypt(password, data)) + encryptPromises.push(edgeEncryptor.encrypt(password, data)) + + Promise.all(encryptPromises).then((encryptedData) => { + assert.equal(encryptedData.length, 2) + assert.notEqual(encryptedData[0], encryptedData[1]) + assert.notEqual(encryptedData[0].length, 0) + assert.notEqual(encryptedData[1].length, 0) + done() + }) + }) + }) + + describe('decrypt', function () { + it('should be able to decrypt the encrypted data.', function (done) { + + edgeEncryptor.encrypt(password, data) + .then(function (encryptedData) { + edgeEncryptor.decrypt(password, encryptedData) + .then(function (decryptedData) { + assert.equal(decryptedData, data) + done() + }) + .catch(function (err) { + done(err) + }) + }) + .catch(function (err) { + done(err) + }) + }) + + it('cannot decrypt the encrypted data with wrong password.', function (done) { + + edgeEncryptor.encrypt(password, data) + .then(function (encryptedData) { + edgeEncryptor.decrypt('wrong password', encryptedData) + .then(function (decryptedData) { + assert.fail('could decrypt with wrong password') + done() + }) + .catch(function (err) { + assert.ok(err instanceof Error) + assert.equal(err.message, 'Incorrect password') + done() + }) + }) + .catch(function (err) { + done(err) + }) + }) + }) +}) diff --git a/test/unit/message-manager-test.js b/test/unit/message-manager-test.js index 9b76241ed..5e7039841 100644 --- a/test/unit/message-manager-test.js +++ b/test/unit/message-manager-test.js @@ -1,11 +1,11 @@ const assert = require('assert') -const MessageManger = require('../../app/scripts/lib/message-manager') +const MessageManager = require('../../app/scripts/lib/message-manager') describe('Message Manager', function () { let messageManager beforeEach(function () { - messageManager = new MessageManger() + messageManager = new MessageManager() }) describe('#getMsgList', function () { diff --git a/test/unit/metamask-controller-test.js b/test/unit/metamask-controller-test.js index 3fc7f9a98..adeca9b5f 100644 --- a/test/unit/metamask-controller-test.js +++ b/test/unit/metamask-controller-test.js @@ -1,129 +1,103 @@ const assert = require('assert') const sinon = require('sinon') const clone = require('clone') +const nock = require('nock') const MetaMaskController = require('../../app/scripts/metamask-controller') +const blacklistJSON = require('../stub/blacklist') const firstTimeState = require('../../app/scripts/first-time-state') -const BN = require('ethereumjs-util').BN -const GWEI_BN = new BN('1000000000') describe('MetaMaskController', function () { - const noop = () => {} - const metamaskController = new MetaMaskController({ - showUnconfirmedMessage: noop, - unlockAccountMessage: noop, - showUnapprovedTx: noop, - platform: {}, - encryptor: { - encrypt: function(password, object) { - this.object = object - return Promise.resolve() - }, - decrypt: function () { - return Promise.resolve(this.object) - } - }, - // initial state - initState: clone(firstTimeState), - }) + let metamaskController + const sandbox = sinon.sandbox.create() + const noop = () => { } beforeEach(function () { - // sinon allows stubbing methods that are easily verified - this.sinon = sinon.sandbox.create() + + nock('https://api.infura.io') + .persist() + .get('/v2/blacklist') + .reply(200, blacklistJSON) + + nock('https://api.infura.io') + .persist() + .get(/.*/) + .reply(200) + + metamaskController = new MetaMaskController({ + showUnapprovedTx: noop, + encryptor: { + encrypt: function (password, object) { + this.object = object + return Promise.resolve() + }, + decrypt: function () { + return Promise.resolve(this.object) + }, + }, + initState: clone(firstTimeState), + }) + sandbox.spy(metamaskController.keyringController, 'createNewVaultAndKeychain') + sandbox.spy(metamaskController.keyringController, 'createNewVaultAndRestore') }) afterEach(function () { - // sinon requires cleanup otherwise it will overwrite context - this.sinon.restore() + nock.cleanAll() + sandbox.restore() }) - describe('Metamask Controller', function () { - assert(metamaskController) - - beforeEach(function () { - sinon.spy(metamaskController.keyringController, 'createNewVaultAndKeychain') - sinon.spy(metamaskController.keyringController, 'createNewVaultAndRestore') - }) - - afterEach(function () { - metamaskController.keyringController.createNewVaultAndKeychain.restore() - metamaskController.keyringController.createNewVaultAndRestore.restore() - }) - - describe('#getGasPrice', function () { - it('gives the 50th percentile lowest accepted gas price from recentBlocksController', async function () { - const realRecentBlocksController = metamaskController.recentBlocksController - metamaskController.recentBlocksController = { - store: { - getState: () => { - return { - recentBlocks: [ - { gasPrices: [ '0x3b9aca00', '0x174876e800'] }, - { gasPrices: [ '0x3b9aca00', '0x174876e800'] }, - { gasPrices: [ '0x174876e800', '0x174876e800' ]}, - { gasPrices: [ '0x174876e800', '0x174876e800' ]}, - ] - } - } - } - } - - const gasPrice = metamaskController.getGasPrice() - assert.equal(gasPrice, '0x3b9aca00', 'accurately estimates 50th percentile accepted gas price') - - metamaskController.recentBlocksController = realRecentBlocksController - }) - - it('gives the 1 gwei price if no blocks have been seen.', async function () { - const realRecentBlocksController = metamaskController.recentBlocksController - metamaskController.recentBlocksController = { - store: { - getState: () => { - return { - recentBlocks: [] - } + describe('#getGasPrice', function () { + it('gives the 50th percentile lowest accepted gas price from recentBlocksController', async function () { + const realRecentBlocksController = metamaskController.recentBlocksController + metamaskController.recentBlocksController = { + store: { + getState: () => { + return { + recentBlocks: [ + { gasPrices: [ '0x3b9aca00', '0x174876e800'] }, + { gasPrices: [ '0x3b9aca00', '0x174876e800'] }, + { gasPrices: [ '0x174876e800', '0x174876e800' ]}, + { gasPrices: [ '0x174876e800', '0x174876e800' ]}, + ], } - } - } - - const gasPrice = metamaskController.getGasPrice() - assert.equal(gasPrice, '0x' + GWEI_BN.toString(16), 'defaults to 1 gwei') + }, + }, + } - metamaskController.recentBlocksController = realRecentBlocksController - }) + const gasPrice = metamaskController.getGasPrice() + assert.equal(gasPrice, '0x3b9aca00', 'accurately estimates 50th percentile accepted gas price') + metamaskController.recentBlocksController = realRecentBlocksController }) + }) - describe('#createNewVaultAndKeychain', function () { - it('can only create new vault on keyringController once', async function () { - const selectStub = sinon.stub(metamaskController, 'selectFirstIdentity') - + describe('#createNewVaultAndKeychain', function () { + it('can only create new vault on keyringController once', async function () { + const selectStub = sandbox.stub(metamaskController, 'selectFirstIdentity') - const password = 'a-fake-password' + const password = 'a-fake-password' - const first = await metamaskController.createNewVaultAndKeychain(password) - const second = await metamaskController.createNewVaultAndKeychain(password) + await metamaskController.createNewVaultAndKeychain(password) + await metamaskController.createNewVaultAndKeychain(password) - assert(metamaskController.keyringController.createNewVaultAndKeychain.calledOnce) + assert(metamaskController.keyringController.createNewVaultAndKeychain.calledOnce) - selectStub.reset() - }) + selectStub.reset() }) + }) + + describe('#createNewVaultAndRestore', function () { + it('should be able to call newVaultAndRestore despite a mistake.', async function () { + + const password = 'what-what-what' + const wrongSeed = 'debris dizzy just program just float decrease vacant alarm reduce speak stadiu' + const rightSeed = 'debris dizzy just program just float decrease vacant alarm reduce speak stadium' + await metamaskController.createNewVaultAndRestore(password, wrongSeed) + .catch((e) => { + return + }) + await metamaskController.createNewVaultAndRestore(password, rightSeed) - describe('#createNewVaultAndRestore', function () { - it('should be able to call newVaultAndRestore despite a mistake.', async function () { - // const selectStub = sinon.stub(metamaskController, 'selectFirstIdentity') - - const password = 'what-what-what' - const wrongSeed = 'debris dizzy just program just float decrease vacant alarm reduce speak stadiu' - const rightSeed = 'debris dizzy just program just float decrease vacant alarm reduce speak stadium' - const first = await metamaskController.createNewVaultAndRestore(password, wrongSeed) - .catch((e) => { - return - }) - const second = await metamaskController.createNewVaultAndRestore(password, rightSeed) - - assert(metamaskController.keyringController.createNewVaultAndRestore.calledTwice) - }) + assert(metamaskController.keyringController.createNewVaultAndRestore.calledTwice) }) }) }) diff --git a/test/unit/network-contoller-test.js b/test/unit/network-contoller-test.js index 0b3b5adeb..51ad09f87 100644 --- a/test/unit/network-contoller-test.js +++ b/test/unit/network-contoller-test.js @@ -1,25 +1,38 @@ const assert = require('assert') +const nock = require('nock') const NetworkController = require('../../app/scripts/controllers/network') +const { createTestProviderTools } = require('../stub/provider') +const providerResultStub = {} +const provider = createTestProviderTools({ scaffold: providerResultStub }).provider + describe('# Network Controller', function () { let networkController + const noop = () => {} const networkControllerProviderInit = { - getAccounts: () => {}, + getAccounts: noop, } beforeEach(function () { + + nock('https://api.infura.io') + .get('/*/') + .reply(200) + + nock('https://rinkeby.infura.io') + .post('/metamask') + .reply(200) + networkController = new NetworkController({ - provider: { - type: 'rinkeby', - }, + provider, }) - networkController.initializeProvider(networkControllerProviderInit, dummyProviderConstructor) + networkController.initializeProvider(networkControllerProviderInit, provider) }) describe('network', function () { describe('#provider', function () { it('provider should be updatable without reassignment', function () { - networkController.initializeProvider(networkControllerProviderInit, dummyProviderConstructor) + networkController.initializeProvider(networkControllerProviderInit, provider) const proxy = networkController._proxy proxy.setTarget({ test: true, on: () => {} }) assert.ok(proxy.test) @@ -64,21 +77,4 @@ describe('# Network Controller', function () { }) }) }) -}) - -function dummyProviderConstructor() { - return { - // provider - sendAsync: noop, - // block tracker - _blockTracker: {}, - start: noop, - stop: noop, - on: noop, - addListener: noop, - once: noop, - removeAllListeners: noop, - } -} - -function noop() {}
\ No newline at end of file +})
\ No newline at end of file diff --git a/test/unit/tx-state-manager-test.js b/test/unit/tx-state-manager-test.js index 02dc52967..220bf501f 100644 --- a/test/unit/tx-state-manager-test.js +++ b/test/unit/tx-state-manager-test.js @@ -5,7 +5,7 @@ const TxStateManager = require('../../app/scripts/lib/tx-state-manager') const txStateHistoryHelper = require('../../app/scripts/lib/tx-state-history-helper') const noop = () => true -describe('TransactionStateManger', function () { +describe('TransactionStateManager', function () { let txStateManager const currentNetworkId = 42 const otherNetworkId = 2 @@ -281,4 +281,4 @@ describe('TransactionStateManger', function () { }) }) -})
\ No newline at end of file +}) diff --git a/ui/app/accounts/import/index.js b/ui/app/accounts/import/index.js index 71eb9ae23..adb52db74 100644 --- a/ui/app/accounts/import/index.js +++ b/ui/app/accounts/import/index.js @@ -35,6 +35,21 @@ AccountImportSubview.prototype.render = function () { return ( h('div.new-account-import-form', [ + h('.new-account-import-disclaimer', [ + h('span', 'Imported accounts will not be associated with your originally created MetaMask account seedphrase. Learn more about imported accounts '), + h('span', { + style: { + cursor: 'pointer', + textDecoration: 'underline', + }, + onClick: () => { + global.platform.openWindow({ + url: 'https://metamask.helpscoutdocs.com/article/17-what-are-loose-accounts', + }) + }, + }, 'here'), + ]), + h('div.new-account-import-form__select-section', [ h('div.new-account-import-form__select-label', 'Select Type'), diff --git a/ui/app/actions.js b/ui/app/actions.js index 9606841ae..3e26f333b 100644 --- a/ui/app/actions.js +++ b/ui/app/actions.js @@ -860,15 +860,16 @@ function markPasswordForgotten () { function unMarkPasswordForgotten () { return (dispatch) => { return background.unMarkPasswordForgotten(() => { - dispatch(actions.forgotPassword()) + dispatch(actions.forgotPassword(false)) forceUpdateMetamaskState(dispatch) }) } } -function forgotPassword () { +function forgotPassword (forgotPasswordState = true) { return { type: actions.FORGOT_PASSWORD, + value: forgotPasswordState, } } @@ -1504,6 +1505,7 @@ function pairUpdate (coin) { dispatch(actions.hideWarning()) shapeShiftRequest('marketinfo', {pair: `${coin.toLowerCase()}_eth`}, (mktResponse) => { dispatch(actions.hideSubLoadingIndication()) + if (mktResponse.error) return dispatch(actions.displayWarning(mktResponse.error)) dispatch({ type: actions.PAIR_UPDATE, value: { diff --git a/ui/app/add-token.js b/ui/app/add-token.js index 230ab35fe..51c577987 100644 --- a/ui/app/add-token.js +++ b/ui/app/add-token.js @@ -52,13 +52,16 @@ function AddTokenScreen () { isShowingConfirmation: false, customAddress: '', customSymbol: '', - customDecimals: 0, + customDecimals: '', searchQuery: '', isCollapsed: true, selectedTokens: {}, errors: {}, + autoFilled: false, } this.tokenAddressDidChange = this.tokenAddressDidChange.bind(this) + this.tokenSymbolDidChange = this.tokenSymbolDidChange.bind(this) + this.tokenDecimalsDidChange = this.tokenDecimalsDidChange.bind(this) this.onNext = this.onNext.bind(this) Component.call(this) } @@ -103,6 +106,16 @@ AddTokenScreen.prototype.tokenAddressDidChange = function (e) { } } +AddTokenScreen.prototype.tokenSymbolDidChange = function (e) { + const customSymbol = e.target.value.trim() + this.setState({ customSymbol }) +} + +AddTokenScreen.prototype.tokenDecimalsDidChange = function (e) { + const customDecimals = e.target.value.trim() + this.setState({ customDecimals }) +} + AddTokenScreen.prototype.checkExistingAddresses = function (address) { if (!address) return false const tokensList = this.props.tokens @@ -125,7 +138,7 @@ AddTokenScreen.prototype.validate = function () { errors.customAddress = 'Address is invalid. ' } - const validDecimals = customDecimals >= 0 && customDecimals < 36 + const validDecimals = customDecimals !== null && customDecimals >= 0 && customDecimals < 36 if (!validDecimals) { errors.customDecimals = 'Decimals must be at least 0, and not over 36.' } @@ -166,12 +179,13 @@ AddTokenScreen.prototype.attemptToAutoFillTokenParams = async function (address) this.setState({ customSymbol: symbol, customDecimals: decimals.toString(), + autoFilled: true, }) } } AddTokenScreen.prototype.renderCustomForm = function () { - const { customAddress, customSymbol, customDecimals, errors } = this.state + const { autoFilled, customAddress, customSymbol, customDecimals, errors } = this.state return !this.state.isCollapsed && ( h('div.add-token__add-custom-form', [ @@ -196,8 +210,9 @@ AddTokenScreen.prototype.renderCustomForm = function () { h('div.add-token__add-custom-label', 'Token Symbol'), h('input.add-token__add-custom-input', { type: 'text', + onChange: this.tokenSymbolDidChange, value: customSymbol, - disabled: true, + disabled: autoFilled, }), h('div.add-token__add-custom-error-message', errors.customSymbol), ]), @@ -209,8 +224,9 @@ AddTokenScreen.prototype.renderCustomForm = function () { h('div.add-token__add-custom-label', 'Decimals of Precision'), h('input.add-token__add-custom-input', { type: 'number', + onChange: this.tokenDecimalsDidChange, value: customDecimals, - disabled: true, + disabled: autoFilled, }), h('div.add-token__add-custom-error-message', errors.customDecimals), ]), diff --git a/ui/app/app.js b/ui/app/app.js index 5d37b9bdf..4e6da24c3 100644 --- a/ui/app/app.js +++ b/ui/app/app.js @@ -89,6 +89,8 @@ function mapStateToProps (state) { currentCurrency: state.metamask.currentCurrency, isMouseUser: state.appState.isMouseUser, betaUI: state.metamask.featureFlags.betaUI, + isRevealingSeedWords: state.metamask.isRevealingSeedWords, + Qr: state.appState.Qr, // state needed to get account dropdown temporarily rendering from app bar identities, @@ -286,10 +288,10 @@ App.prototype.renderAppBar = function () { }), // metamask name - h('h1', 'MetaMask'), - - h('div.beta-label', 'BETA'), - + h('.flex-row', [ + h('h1', 'MetaMask'), + h('div.beta-label', 'BETA'), + ]), ]), h('div.header__right-actions', [ @@ -362,9 +364,17 @@ App.prototype.renderBackButton = function (style, justArrow = false) { App.prototype.renderPrimary = function () { log.debug('rendering primary') var props = this.props - const {isMascara, isOnboarding, betaUI} = props + const { + isMascara, + isOnboarding, + betaUI, + isRevealingSeedWords, + Qr, + } = props + const isMascaraOnboarding = isMascara && isOnboarding + const isBetaUIOnboarding = betaUI && isOnboarding && !props.isPopup && !isRevealingSeedWords - if ((isMascara || betaUI) && isOnboarding && !props.isPopup) { + if (isMascaraOnboarding || isBetaUIOnboarding) { return h(MascaraFirstTime) } @@ -388,7 +398,7 @@ App.prototype.renderPrimary = function () { if (props.isInitialized && props.forgottenPassword) { log.debug('rendering restore vault screen') return h(HDRestoreVaultScreen, {key: 'HDRestoreVaultScreen'}) - } else if (!props.isInitialized && !props.isUnlocked) { + } else if (!props.isInitialized && !props.isUnlocked && !isRevealingSeedWords) { log.debug('rendering menu screen') return props.isPopup ? h(OldUIInitializeMenuScreen, {key: 'menuScreenInit'}) @@ -500,7 +510,7 @@ App.prototype.renderPrimary = function () { width: '285px', }, }, [ - h(QrView, {key: 'qr'}), + h(QrView, {key: 'qr', Qr}), ]), ]) diff --git a/ui/app/components/dropdowns/components/account-dropdowns.js b/ui/app/components/dropdowns/components/account-dropdowns.js index fa9ffc632..40918cc53 100644 --- a/ui/app/components/dropdowns/components/account-dropdowns.js +++ b/ui/app/components/dropdowns/components/account-dropdowns.js @@ -134,22 +134,6 @@ class AccountDropdowns extends Component { ]), ]), -// ======= -// }, -// ), -// this.indicateIfLoose(keyring), -// h('span', { -// style: { -// marginLeft: '20px', -// fontSize: '24px', -// maxWidth: '145px', -// whiteSpace: 'nowrap', -// overflow: 'hidden', -// textOverflow: 'ellipsis', -// }, -// }, identity.name || ''), -// h('span', { style: { marginLeft: '20px', fontSize: '24px' } }, isSelected ? h('.check', '✓') : null), -// >>>>>>> master:ui/app/components/account-dropdowns.js ] ) }) @@ -159,7 +143,7 @@ class AccountDropdowns extends Component { try { // Sometimes keyrings aren't loaded yet: const type = keyring.type const isLoose = type !== 'HD Key Tree' - return isLoose ? h('.keyring-label', 'LOOSE') : null + return isLoose ? h('.keyring-label', 'IMPORTED') : null } catch (e) { return } } diff --git a/ui/app/components/dropdowns/network-dropdown.js b/ui/app/components/dropdowns/network-dropdown.js index 9be5cc5d1..ff10c0758 100644 --- a/ui/app/components/dropdowns/network-dropdown.js +++ b/ui/app/components/dropdowns/network-dropdown.js @@ -114,7 +114,7 @@ NetworkDropdown.prototype.render = function () { [ providerType === 'mainnet' ? h('i.fa.fa-check') : h('.network-check__transparent', '✓'), h(NetworkDropdownIcon, { - backgroundColor: '#038789', // $blue-lagoon + backgroundColor: '#29B6AF', // $java isSelected: providerType === 'mainnet', }), h('span.network-name-item', { @@ -136,7 +136,7 @@ NetworkDropdown.prototype.render = function () { [ providerType === 'ropsten' ? h('i.fa.fa-check') : h('.network-check__transparent', '✓'), h(NetworkDropdownIcon, { - backgroundColor: '#e91550', // $crimson + backgroundColor: '#ff4a8d', // $wild-strawberry isSelected: providerType === 'ropsten', }), h('span.network-name-item', { @@ -158,7 +158,7 @@ NetworkDropdown.prototype.render = function () { [ providerType === 'kovan' ? h('i.fa.fa-check') : h('.network-check__transparent', '✓'), h(NetworkDropdownIcon, { - backgroundColor: '#690496', // $purple + backgroundColor: '#7057ff', // $cornflower-blue isSelected: providerType === 'kovan', }), h('span.network-name-item', { @@ -180,7 +180,7 @@ NetworkDropdown.prototype.render = function () { [ providerType === 'rinkeby' ? h('i.fa.fa-check') : h('.network-check__transparent', '✓'), h(NetworkDropdownIcon, { - backgroundColor: '#ebb33f', // $tulip-tree + backgroundColor: '#f6c343', // $saffron isSelected: providerType === 'rinkeby', }), h('span.network-name-item', { diff --git a/ui/app/components/modals/deposit-ether-modal.js b/ui/app/components/modals/deposit-ether-modal.js index 532d66653..7547dbcf5 100644 --- a/ui/app/components/modals/deposit-ether-modal.js +++ b/ui/app/components/modals/deposit-ether-modal.js @@ -33,6 +33,9 @@ function mapDispatchToProps (dispatch) { hideModal: () => { dispatch(actions.hideModal()) }, + hideWarning: () => { + dispatch(actions.hideWarning()) + }, showAccountDetailModal: () => { dispatch(actions.showModal({ name: 'ACCOUNT_DETAILS' })) }, @@ -119,6 +122,7 @@ DepositEtherModal.prototype.render = function () { h('div.deposit-ether-modal__header__close', { onClick: () => { this.setState({ buyingWithShapeshift: false }) + this.props.hideWarning() this.props.hideModal() }, }), @@ -179,6 +183,7 @@ DepositEtherModal.prototype.render = function () { } DepositEtherModal.prototype.goToAccountDetailsModal = function () { + this.props.hideWarning() this.props.hideModal() this.props.showAccountDetailModal() } diff --git a/ui/app/components/modals/modal.js b/ui/app/components/modals/modal.js index 97fe38292..8e9e58985 100644 --- a/ui/app/components/modals/modal.js +++ b/ui/app/components/modals/modal.js @@ -79,6 +79,7 @@ const MODALS = { contents: [ h(DepositEtherModal, {}, []), ], + onHide: (props) => props.hideWarning(), mobileModalStyle: { width: '100%', height: '100%', @@ -286,6 +287,10 @@ function mapDispatchToProps (dispatch) { hideModal: () => { dispatch(actions.hideModal()) }, + hideWarning: () => { + dispatch(actions.hideWarning()) + }, + } } @@ -308,7 +313,12 @@ Modal.prototype.render = function () { { className: 'modal', keyboard: false, - onHide: () => { this.onHide() }, + onHide: () => { + if (modal.onHide) { + modal.onHide(this.props) + } + this.onHide() + }, ref: (ref) => { this.modalRef = ref }, diff --git a/ui/app/components/shapeshift-form.js b/ui/app/components/shapeshift-form.js index 2270b8236..87eb1588a 100644 --- a/ui/app/components/shapeshift-form.js +++ b/ui/app/components/shapeshift-form.js @@ -14,11 +14,13 @@ function mapStateToProps (state) { tokenExchangeRates, selectedAddress, } = state.metamask + const { warning } = state.appState return { coinOptions, tokenExchangeRates, selectedAddress, + warning, } } @@ -51,8 +53,7 @@ ShapeshiftForm.prototype.componentWillMount = function () { this.props.shapeShiftSubview() } -ShapeshiftForm.prototype.onCoinChange = function (e) { - const coin = e.target.value +ShapeshiftForm.prototype.onCoinChange = function (coin) { this.setState({ depositCoin: coin, errorMessage: '', @@ -133,7 +134,7 @@ ShapeshiftForm.prototype.renderMarketInfo = function () { } ShapeshiftForm.prototype.renderQrCode = function () { - const { depositAddress, isLoading } = this.state + const { depositAddress, isLoading, depositCoin } = this.state const qrImage = qrcode(4, 'M') qrImage.addData(depositAddress) qrImage.make() @@ -141,7 +142,7 @@ ShapeshiftForm.prototype.renderQrCode = function () { return h('div.shapeshift-form', {}, [ h('div.shapeshift-form__deposit-instruction', [ - 'Deposit your BTC to the address below:', + `Deposit your ${depositCoin.toUpperCase()} to the address below:`, ]), h('div', depositAddress), @@ -164,7 +165,7 @@ ShapeshiftForm.prototype.renderQrCode = function () { ShapeshiftForm.prototype.render = function () { - const { coinOptions, btnClass } = this.props + const { coinOptions, btnClass, warning } = this.props const { depositCoin, errorMessage, showQrCode, depositAddress } = this.state const coinPair = `${depositCoin}_eth` const { tokenExchangeRates } = this.props @@ -182,7 +183,7 @@ ShapeshiftForm.prototype.render = function () { h(SimpleDropdown, { selectedOption: this.state.depositCoin, - onSelect: this.onCoinChange, + onSelect: (coin) => this.onCoinChange(coin), options: Object.entries(coinOptions).map(([coin]) => ({ value: coin.toLowerCase(), displayValue: coin, @@ -207,7 +208,9 @@ ShapeshiftForm.prototype.render = function () { ]), - h('div', { + warning && h('div.shapeshift-form__address-input-label', warning), + + !warning && h('div', { className: classnames('shapeshift-form__address-input-wrapper', { 'shapeshift-form__address-input-wrapper--error': errorMessage, }), @@ -228,7 +231,7 @@ ShapeshiftForm.prototype.render = function () { h('divshapeshift-form__address-input-error-message', [errorMessage]), ]), - this.renderMarketInfo(), + !warning && this.renderMarketInfo(), ]), diff --git a/ui/app/css/itcss/components/account-menu.scss b/ui/app/css/itcss/components/account-menu.scss index 8ad7481c7..4752741aa 100644 --- a/ui/app/css/itcss/components/account-menu.scss +++ b/ui/app/css/itcss/components/account-menu.scss @@ -66,8 +66,9 @@ .keyring-label { margin-top: 5px; - background-color: $black; - color: $dusty-gray; + background-color: $dusty-gray; + color: $black; + font-weight: normal; } } diff --git a/ui/app/css/itcss/components/header.scss b/ui/app/css/itcss/components/header.scss index d91ab3c48..eeed9ee06 100644 --- a/ui/app/css/itcss/components/header.scss +++ b/ui/app/css/itcss/components/header.scss @@ -80,11 +80,10 @@ font-family: Roboto; text-transform: uppercase; font-weight: 500; - font-size: 0.8rem; - padding-left: 9px; + font-size: .8rem; color: $buttercup; - align-self: flex-start; - margin-top: 10px; + margin-left: 5px; + line-height: initial; @media screen and (max-width: 575px) { display: none; diff --git a/ui/app/css/itcss/components/modal.scss b/ui/app/css/itcss/components/modal.scss index 919e1793b..53e3bff00 100644 --- a/ui/app/css/itcss/components/modal.scss +++ b/ui/app/css/itcss/components/modal.scss @@ -787,6 +787,10 @@ width: auto; flex: 1; } + + @media screen and (max-width: 575px) { + width: auto; + } } } diff --git a/ui/app/css/itcss/components/new-account.scss b/ui/app/css/itcss/components/new-account.scss index 81f919df3..c6c254ede 100644 --- a/ui/app/css/itcss/components/new-account.scss +++ b/ui/app/css/itcss/components/new-account.scss @@ -54,6 +54,16 @@ } +.new-account-import-disclaimer { + width: 120%; + background-color: #F4F9FC; + display: inline-block; + align-items: center; + padding: 20px 30px 20px; + font-size: 12px; + line-height: 1.5; +} + .new-account-import-form { display: flex; flex-flow: column; diff --git a/ui/app/css/itcss/settings/variables.scss b/ui/app/css/itcss/settings/variables.scss index 4c0972527..d96c1ae43 100644 --- a/ui/app/css/itcss/settings/variables.scss +++ b/ui/app/css/itcss/settings/variables.scss @@ -46,6 +46,10 @@ $manatee: #93949d; $spindle: #c7ddec; $mid-gray: #5b5d67; $cape-cod: #38393a; +$java: #29b6af; +$wild-strawberry: #ff4a8d; +$cornflower-blue: #7057ff; +$saffron: #f6c343; /* Z-Indicies diff --git a/ui/app/reducers/app.js b/ui/app/reducers/app.js index 02f024f7c..4dda839a2 100644 --- a/ui/app/reducers/app.js +++ b/ui/app/reducers/app.js @@ -140,10 +140,10 @@ function reduceApp (state, action) { case actions.FORGOT_PASSWORD: return extend(appState, { currentView: { - name: 'restoreVault', + name: action.value ? 'restoreVault' : 'accountDetail', }, transForward: false, - forgottenPassword: true, + forgottenPassword: action.value, }) case actions.SHOW_INIT_MENU: diff --git a/ui/app/reducers/metamask.js b/ui/app/reducers/metamask.js index beeba948d..cddcd0c1f 100644 --- a/ui/app/reducers/metamask.js +++ b/ui/app/reducers/metamask.js @@ -43,12 +43,15 @@ function reduceMetamask (state, action) { useBlockie: false, featureFlags: {}, networkEndpointType: OLD_UI_NETWORK_TYPE, + isRevealingSeedWords: false, }, state.metamask) switch (action.type) { case actions.SHOW_ACCOUNTS_PAGE: - newState = extend(metamaskState) + newState = extend(metamaskState, { + isRevealingSeedWords: false, + }) delete newState.seedWords return newState @@ -124,10 +127,12 @@ function reduceMetamask (state, action) { }, }) + case actions.SHOW_NEW_VAULT_SEED: return extend(metamaskState, { isUnlocked: true, isInitialized: false, + isRevealingSeedWords: true, seedWords: action.value, }) diff --git a/ui/app/send-v2.js b/ui/app/send-v2.js index 1d67150e3..fc1df1f51 100644 --- a/ui/app/send-v2.js +++ b/ui/app/send-v2.js @@ -361,8 +361,9 @@ SendTransactionScreen.prototype.validateAmount = function (value) { }) } + const verifyTokenBalance = selectedToken && tokenBalance !== null let sufficientTokens - if (selectedToken) { + if (verifyTokenBalance) { sufficientTokens = isTokenBalanceSufficient({ tokenBalance, amount, @@ -377,7 +378,7 @@ SendTransactionScreen.prototype.validateAmount = function (value) { if (conversionRate && !sufficientBalance) { amountError = 'Insufficient funds.' - } else if (selectedToken && !sufficientTokens) { + } else if (verifyTokenBalance && !sufficientTokens) { amountError = 'Insufficient tokens.' } else if (amountLessThanZero) { amountError = 'Can not send negative amounts of ETH.' @@ -396,14 +397,15 @@ SendTransactionScreen.prototype.renderAmountRow = function () { amount, setMaxModeTo, maxModeOn, + gasTotal, } = this.props return h('div.send-v2__form-row', [ - h('div.send-v2__form-label', [ + h('div.send-v2__form-label', [ 'Amount:', this.renderErrorMessage('amount'), - !errors.amount && h('div.send-v2__amount-max', { + !errors.amount && gasTotal && h('div.send-v2__amount-max', { onClick: (event) => { event.preventDefault() setMaxModeTo(true) @@ -491,9 +493,12 @@ SendTransactionScreen.prototype.renderFooter = function () { goHome, clearSend, gasTotal, + tokenBalance, + selectedToken, errors: { amount: amountError, to: toError }, } = this.props + const missingTokenBalance = selectedToken && !tokenBalance const noErrors = !amountError && toError === null return h('div.page-container__footer', [ @@ -504,7 +509,7 @@ SendTransactionScreen.prototype.renderFooter = function () { }, }, 'Cancel'), h('button.btn-clear.page-container__footer-button', { - disabled: !noErrors || !gasTotal, + disabled: !noErrors || !gasTotal || missingTokenBalance, onClick: event => this.onSubmit(event), }, 'Next'), ]) diff --git a/ui/app/unlock.js b/ui/app/unlock.js index 13c3f1274..cafe3a859 100644 --- a/ui/app/unlock.js +++ b/ui/app/unlock.js @@ -71,7 +71,7 @@ UnlockScreen.prototype.render = function () { style: { margin: 10, }, - }, 'Unlock'), + }, 'Log In'), ]), h('.flex-row.flex-center.flex-grow', [ @@ -104,7 +104,7 @@ UnlockScreen.prototype.render = function () { }, }, 'Use classic interface'), ]), - + ]) ) } @@ -7348,9 +7348,9 @@ mersenne-twister@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/mersenne-twister/-/mersenne-twister-1.1.0.tgz#f916618ee43d7179efcf641bec4531eb9670978a" -metamascara@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/metamascara/-/metamascara-1.3.1.tgz#a84d6f20ef4ba401ce44eba120857ee1d680747b" +metamascara@^2.0.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/metamascara/-/metamascara-2.2.1.tgz#f97b87045a245e1bd2e1bcae7a3d4dcd4e17c02a" dependencies: iframe "^1.0.0" iframe-stream "^3.0.0" |