Fix merge conflicts from uat-next

feature/default_network_editable
Alexander Tseung 7 years ago
commit 4f1fe1da62
  1. 10
      CHANGELOG.md
  2. 2
      app/manifest.json
  3. 15
      app/scripts/background.js
  4. 32
      app/scripts/controllers/transactions.js
  5. 3
      app/scripts/lib/tx-gas-utils.js
  6. 9
      app/scripts/popup.js
  7. 26
      app/scripts/setupRaven.js
  8. 3
      app/scripts/vendor/raven.min.js
  9. 5
      development/mockExtension.js
  10. 154
      development/states/confirm-new-ui.json
  11. 154
      development/states/send-edit.json
  12. 133
      development/states/send-new-ui.json
  13. 2
      gulpfile.js
  14. 2
      mock-dev.js
  15. 12
      old-ui/app/components/loading.js
  16. 6
      old-ui/app/conf-tx.js
  17. 12
      package.json
  18. 41
      test/integration/lib/first-time.js
  19. 229
      test/integration/lib/send-new-ui.js
  20. 27
      test/stub/provider.js
  21. 4
      test/unit/pending-tx-test.js
  22. 32
      test/unit/tx-controller-test.js
  23. 4
      test/unit/tx-gas-util-test.js
  24. 13
      ui/app/actions.js
  25. 2
      ui/app/components/network.js
  26. 1
      ui/app/components/pages/add-token.js
  27. 2
      ui/app/components/pages/create-account/import-account/index.js
  28. 19
      ui/app/components/pages/create-account/import-account/private-key.js
  29. 12
      ui/app/components/pages/create-account/new-account.js
  30. 1
      ui/app/components/send/send-v2-container.js
  31. 31
      ui/app/components/tooltip-v2.js
  32. 36
      ui/app/components/wallet-view.js
  33. 1
      ui/app/conf-tx.js
  34. 2
      ui/app/css/itcss/components/index.scss
  35. 25
      ui/app/css/itcss/components/new-account.scss
  36. 11
      ui/app/css/itcss/components/newui-sections.scss
  37. 7
      ui/app/css/itcss/components/tooltip.scss
  38. 199
      ui/app/info.js
  39. 5
      ui/app/reducers/app.js
  40. 96
      yarn.lock

@ -2,6 +2,16 @@
## Current Master ## Current Master
## 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 ## 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. - Estimating gas limit for simple ether sends now faster & cheaper, by avoiding VM usage on recipients with no code.

@ -1,7 +1,7 @@
{ {
"name": "MetaMask", "name": "MetaMask",
"short_name": "Metamask", "short_name": "Metamask",
"version": "4.0.9", "version": "3.13.7",
"manifest_version": 2, "manifest_version": 2,
"author": "https://metamask.io", "author": "https://metamask.io",
"description": "Ethereum Browser Extension", "description": "Ethereum Browser Extension",

@ -13,6 +13,7 @@ const PortStream = require('./lib/port-stream.js')
const NotificationManager = require('./lib/notification-manager.js') const NotificationManager = require('./lib/notification-manager.js')
const MetamaskController = require('./metamask-controller') const MetamaskController = require('./metamask-controller')
const firstTimeState = require('./first-time-state') const firstTimeState = require('./first-time-state')
const setupRaven = require('./setupRaven')
const STORAGE_KEY = 'metamask-config' const STORAGE_KEY = 'metamask-config'
const METAMASK_DEBUG = 'GULP_METAMASK_DEBUG' const METAMASK_DEBUG = 'GULP_METAMASK_DEBUG'
@ -24,6 +25,10 @@ const platform = new ExtensionPlatform()
const notificationManager = new NotificationManager() const notificationManager = new NotificationManager()
global.METAMASK_NOTIFIER = notificationManager global.METAMASK_NOTIFIER = notificationManager
// setup sentry error reporting
const release = platform.getVersion()
const raven = setupRaven({ release })
let popupIsOpen = false let popupIsOpen = false
// state persistence // state persistence
@ -72,6 +77,16 @@ function setupController (initState) {
}) })
global.metamaskController = controller global.metamaskController = controller
// report failed transactions to Sentry
controller.txController.on(`tx:status-update`, (txId, status) => {
if (status !== 'failed') return
const txMeta = controller.txController.txStateManager.getTx(txId)
raven.captureMessage('Transaction Failed', {
// "extra" key is required by Sentry
extra: txMeta,
})
})
// setup state persistence // setup state persistence
pump( pump(
asStream(controller.store), asStream(controller.store),

@ -43,6 +43,28 @@ module.exports = class TransactionController extends EventEmitter {
txHistoryLimit: opts.txHistoryLimit, txHistoryLimit: opts.txHistoryLimit,
getNetwork: this.getNetwork.bind(this), getNetwork: this.getNetwork.bind(this),
}) })
this.txStateManager.getFilteredTxList({
status: 'unapproved',
loadingDefaults: true,
}).forEach((tx) => {
this.addTxDefaults(tx)
.then((txMeta) => {
txMeta.loadingDefaults = false
this.txStateManager.updateTx(txMeta, 'transactions: gas estimation for tx on boot')
}).catch((error) => {
this.txStateManager.setTxStatusFailed(tx.id, error)
})
})
this.txStateManager.getFilteredTxList({
status: 'approved',
}).forEach((txMeta) => {
const txSignError = new Error('Transaction found as "approved" during boot - possibly stuck during signing')
this.txStateManager.setTxStatusFailed(txMeta.id, txSignError)
})
this.store = this.txStateManager.store this.store = this.txStateManager.store
this.txStateManager.on('tx:status-update', this.emit.bind(this, 'tx:status-update')) this.txStateManager.on('tx:status-update', this.emit.bind(this, 'tx:status-update'))
this.nonceTracker = new NonceTracker({ this.nonceTracker = new NonceTracker({
@ -171,11 +193,17 @@ module.exports = class TransactionController extends EventEmitter {
this.addTx(txMeta) this.addTx(txMeta)
this.emit('newUnapprovedTx', txMeta) this.emit('newUnapprovedTx', txMeta)
// add default tx params // add default tx params
await this.addTxDefaults(txMeta) try {
await this.addTxDefaults(txMeta)
} catch (error) {
console.log(error)
this.txStateManager.setTxStatusFailed(txMeta.id, error)
throw error
}
txMeta.loadingDefaults = false txMeta.loadingDefaults = false
// save txMeta // save txMeta
this.txStateManager.updateTx(txMeta) this.txStateManager.updateTx(txMeta)
return txMeta return txMeta
} }

@ -12,7 +12,8 @@ its passed ethquery
and used to do things like calculate gas of a tx. and used to do things like calculate gas of a tx.
*/ */
module.exports = class txProvideUtil { module.exports = class TxGasUtil {
constructor (provider) { constructor (provider) {
this.query = new EthQuery(provider) this.query = new EthQuery(provider)
} }

@ -8,10 +8,19 @@ const extension = require('extensionizer')
const ExtensionPlatform = require('./platforms/extension') const ExtensionPlatform = require('./platforms/extension')
const NotificationManager = require('./lib/notification-manager') const NotificationManager = require('./lib/notification-manager')
const notificationManager = new NotificationManager() const notificationManager = new NotificationManager()
const setupRaven = require('./setupRaven')
// create platform global // create platform global
global.platform = new ExtensionPlatform() global.platform = new ExtensionPlatform()
// setup sentry error reporting
const release = global.platform.getVersion()
setupRaven({ release })
// inject css
// const css = MetaMaskUiCss()
// injectCss(css)
// identify window type (popup, notification) // identify window type (popup, notification)
const windowType = isPopupOrNotification() const windowType = isPopupOrNotification()
global.METAMASK_UI_TYPE = windowType global.METAMASK_UI_TYPE = windowType

@ -0,0 +1,26 @@
const Raven = require('./vendor/raven.min.js')
const METAMASK_DEBUG = 'GULP_METAMASK_DEBUG'
const PROD = 'https://3567c198f8a8412082d32655da2961d0@sentry.io/273505'
const DEV = 'https://f59f3dd640d2429d9d0e2445a87ea8e1@sentry.io/273496'
module.exports = setupRaven
// Setup raven / sentry remote error reporting
function setupRaven(opts) {
const { release } = opts
let ravenTarget
if (METAMASK_DEBUG) {
console.log('Setting up Sentry Remote Error Reporting: DEV')
ravenTarget = DEV
} else {
console.log('Setting up Sentry Remote Error Reporting: PROD')
ravenTarget = PROD
}
Raven.config(ravenTarget, {
release,
}).install()
return Raven
}

File diff suppressed because one or more lines are too long

@ -37,3 +37,8 @@ apis.forEach(function (api) {
extension.runtime.reload = noop extension.runtime.reload = noop
extension.tabs.create = noop extension.tabs.create = noop
extension.runtime.getManifest = function () {
return {
version: 'development'
}
}

@ -0,0 +1,154 @@
{
"metamask": {
"isInitialized": true,
"isUnlocked": true,
"featureFlags": {"betaUI": true},
"rpcTarget": "https://rawtestrpc.metamask.io/",
"identities": {
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825": {
"address": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
"name": "Send Account 1"
},
"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb": {
"address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb",
"name": "Send Account 2"
},
"0x2f8d4a878cfa04a6e60d46362f5644deab66572d": {
"address": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d",
"name": "Send Account 3"
},
"0xd85a4b6a394794842887b8284293d69163007bbb": {
"address": "0xd85a4b6a394794842887b8284293d69163007bbb",
"name": "Send Account 4"
}
},
"unapprovedTxs": {},
"currentCurrency": "USD",
"conversionRate": 1200.88200327,
"conversionDate": 1489013762,
"noActiveNotices": true,
"frequentRpcList": [],
"network": "3",
"accounts": {
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825": {
"code": "0x",
"balance": "0x47c9d71831c76efe",
"nonce": "0x1b",
"address": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"
},
"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb": {
"code": "0x",
"balance": "0x37452b1315889f80",
"nonce": "0xa",
"address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb"
},
"0x2f8d4a878cfa04a6e60d46362f5644deab66572d": {
"code": "0x",
"balance": "0x30c9d71831c76efe",
"nonce": "0x1c",
"address": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d"
},
"0xd85a4b6a394794842887b8284293d69163007bbb": {
"code": "0x",
"balance": "0x0",
"nonce": "0x0",
"address": "0xd85a4b6a394794842887b8284293d69163007bbb"
}
},
"addressBook": [
{
"address": "0x06195827297c7a80a443b6894d3bdb8824b43896",
"name": "Address Book Account 1"
}
],
"tokens": [],
"transactions": {},
"selectedAddressTxList": [],
"unapprovedTxs": {
"4768706228115573": {
"id": 4768706228115573,
"time": 1487363153561,
"status": "unapproved",
"gasMultiplier": 1,
"metamaskNetworkId": "3",
"txParams": {
"from": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb",
"to": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d",
"value": "0x1bc16d674ec80000",
"metamaskId": 4768706228115573,
"metamaskNetworkId": "3",
"gas": "0xea60",
"gasPrice": "0xba43b7400"
}
}
},
"unapprovedMsgs": {},
"unapprovedMsgCount": 0,
"unapprovedPersonalMsgs": {},
"unapprovedPersonalMsgCount": 0,
"keyringTypes": [
"Simple Key Pair",
"HD Key Tree"
],
"keyrings": [
{
"type": "HD Key Tree",
"accounts": [
"fdea65c8e26263f6d9a1b5de9555d2931a33b825",
"c5b8dbac4c1d3f152cdeb400e2313f309c410acb",
"2f8d4a878cfa04a6e60d46362f5644deab66572d"
]
},
{
"type": "Simple Key Pair",
"accounts": [
"0xd85a4b6a394794842887b8284293d69163007bbb"
]
}
],
"selectedAddress": "0xd85a4b6a394794842887b8284293d69163007bbb",
"currentCurrency": "USD",
"provider": {
"type": "testnet"
},
"shapeShiftTxList": [],
"lostAccounts": [],
"send": {
"gasLimit": "0xea60",
"gasPrice": "0xba43b7400",
"gasTotal": "0xb451dc41b578",
"tokenBalance": null,
"from": {
"address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb",
"balance": "0x37452b1315889f80"
},
"to": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d",
"amount": "0x1bc16d674ec80000",
"memo": "",
"errors": {},
"maxModeOn": false,
"editingTransactionId": null
}
},
"appState": {
"menuOpen": false,
"currentView": {
"name": "confTx",
"detailView": null,
"context": 0
},
"accountDetail": {
"subview": "transactions"
},
"modal": {
"modalState": {},
"previousModalState": {}
},
"transForward": true,
"isLoading": false,
"warning": null,
"scrollToBottom": false,
"forgottenPassword": null
},
"identities": {}
}

@ -0,0 +1,154 @@
{
"metamask": {
"isInitialized": true,
"isUnlocked": true,
"featureFlags": {"betaUI": true},
"rpcTarget": "https://rawtestrpc.metamask.io/",
"identities": {
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825": {
"address": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
"name": "Send Account 1"
},
"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb": {
"address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb",
"name": "Send Account 2"
},
"0x2f8d4a878cfa04a6e60d46362f5644deab66572d": {
"address": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d",
"name": "Send Account 3"
},
"0xd85a4b6a394794842887b8284293d69163007bbb": {
"address": "0xd85a4b6a394794842887b8284293d69163007bbb",
"name": "Send Account 4"
}
},
"unapprovedTxs": {},
"currentCurrency": "USD",
"conversionRate": 1200.88200327,
"conversionDate": 1489013762,
"noActiveNotices": true,
"frequentRpcList": [],
"network": "3",
"accounts": {
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825": {
"code": "0x",
"balance": "0x47c9d71831c76efe",
"nonce": "0x1b",
"address": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"
},
"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb": {
"code": "0x",
"balance": "0x37452b1315889f80",
"nonce": "0xa",
"address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb"
},
"0x2f8d4a878cfa04a6e60d46362f5644deab66572d": {
"code": "0x",
"balance": "0x30c9d71831c76efe",
"nonce": "0x1c",
"address": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d"
},
"0xd85a4b6a394794842887b8284293d69163007bbb": {
"code": "0x",
"balance": "0x0",
"nonce": "0x0",
"address": "0xd85a4b6a394794842887b8284293d69163007bbb"
}
},
"addressBook": [
{
"address": "0x06195827297c7a80a443b6894d3bdb8824b43896",
"name": "Address Book Account 1"
}
],
"tokens": [],
"transactions": {},
"selectedAddressTxList": [],
"unapprovedTxs": {
"4768706228115573": {
"id": 4768706228115573,
"time": 1487363153561,
"status": "unapproved",
"gasMultiplier": 1,
"metamaskNetworkId": "3",
"txParams": {
"from": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb",
"to": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d",
"value": "0x1bc16d674ec80000",
"metamaskId": 4768706228115573,
"metamaskNetworkId": "3",
"gas": "0xea60",
"gasPrice": "0xba43b7400"
}
}
},
"unapprovedMsgs": {},
"unapprovedMsgCount": 0,
"unapprovedPersonalMsgs": {},
"unapprovedPersonalMsgCount": 0,
"keyringTypes": [
"Simple Key Pair",
"HD Key Tree"
],
"keyrings": [
{
"type": "HD Key Tree",
"accounts": [
"fdea65c8e26263f6d9a1b5de9555d2931a33b825",
"c5b8dbac4c1d3f152cdeb400e2313f309c410acb",
"2f8d4a878cfa04a6e60d46362f5644deab66572d"
]
},
{
"type": "Simple Key Pair",
"accounts": [
"0xd85a4b6a394794842887b8284293d69163007bbb"
]
}
],
"selectedAddress": "0xd85a4b6a394794842887b8284293d69163007bbb",
"currentCurrency": "USD",
"provider": {
"type": "testnet"
},
"shapeShiftTxList": [],
"lostAccounts": [],
"send": {
"gasLimit": "0xea60",
"gasPrice": "0xba43b7400",
"gasTotal": "0xb451dc41b578",
"tokenBalance": null,
"from": {
"address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb",
"balance": "0x37452b1315889f80"
},
"to": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d",
"amount": "0x1bc16d674ec80000",
"memo": "",
"errors": {},
"maxModeOn": false,
"editingTransactionId": null
}
},
"appState": {
"menuOpen": false,
"currentView": {
"name": "confTx",
"detailView": null,
"context": 0
},
"accountDetail": {
"subview": "transactions"
},
"modal": {
"modalState": {},
"previousModalState": {}
},
"transForward": true,
"isLoading": false,
"warning": null,
"scrollToBottom": false,
"forgottenPassword": null
},
"identities": {}
}

@ -0,0 +1,133 @@
{
"metamask": {
"isInitialized": true,
"isUnlocked": true,
"featureFlags": {"betaUI": true},
"rpcTarget": "https://rawtestrpc.metamask.io/",
"identities": {
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825": {
"address": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
"name": "Send Account 1"
},
"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb": {
"address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb",
"name": "Send Account 2"
},
"0x2f8d4a878cfa04a6e60d46362f5644deab66572d": {
"address": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d",
"name": "Send Account 3"
},
"0xd85a4b6a394794842887b8284293d69163007bbb": {
"address": "0xd85a4b6a394794842887b8284293d69163007bbb",
"name": "Send Account 4"
}
},
"unapprovedTxs": {},
"currentCurrency": "USD",
"conversionRate": 1200.88200327,
"conversionDate": 1489013762,
"noActiveNotices": true,
"frequentRpcList": [],
"network": "3",
"accounts": {
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825": {
"code": "0x",
"balance": "0x47c9d71831c76efe",
"nonce": "0x1b",
"address": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"
},
"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb": {
"code": "0x",
"balance": "0x37452b1315889f80",
"nonce": "0xa",
"address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb"
},
"0x2f8d4a878cfa04a6e60d46362f5644deab66572d": {
"code": "0x",
"balance": "0x30c9d71831c76efe",
"nonce": "0x1c",
"address": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d"
},
"0xd85a4b6a394794842887b8284293d69163007bbb": {
"code": "0x",
"balance": "0x0",
"nonce": "0x0",
"address": "0xd85a4b6a394794842887b8284293d69163007bbb"
}
},
"addressBook": [
{
"address": "0x06195827297c7a80a443b6894d3bdb8824b43896",
"name": "Address Book Account 1"
}
],
"tokens": [],
"transactions": {},
"selectedAddressTxList": [],
"unapprovedMsgs": {},
"unapprovedMsgCount": 0,
"unapprovedPersonalMsgs": {},
"unapprovedPersonalMsgCount": 0,
"keyringTypes": [
"Simple Key Pair",
"HD Key Tree"
],
"keyrings": [
{
"type": "HD Key Tree",
"accounts": [
"fdea65c8e26263f6d9a1b5de9555d2931a33b825",
"c5b8dbac4c1d3f152cdeb400e2313f309c410acb",
"2f8d4a878cfa04a6e60d46362f5644deab66572d"
]
},
{
"type": "Simple Key Pair",
"accounts": [
"0xd85a4b6a394794842887b8284293d69163007bbb"
]
}
],
"selectedAddress": "0xd85a4b6a394794842887b8284293d69163007bbb",
"currentCurrency": "USD",
"provider": {
"type": "testnet"
},
"shapeShiftTxList": [],
"lostAccounts": [],
"send": {
"gasLimit": null,
"gasPrice": null,
"gasTotal": "0xb451dc41b578",
"tokenBalance": null,
"from": "",
"to": "",
"amount": "0x0",
"memo": "",
"errors": {},
"maxModeOn": false,
"editingTransactionId": null
}
},
"appState": {
"menuOpen": false,
"currentView": {
"name": "accountDetail",
"detailView": null,
"context": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc"
},
"accountDetail": {
"subview": "transactions"
},
"modal": {
"modalState": {},
"previousModalState": {}
},
"transForward": true,
"isLoading": false,
"warning": null,
"scrollToBottom": false,
"forgottenPassword": null
},
"identities": {}
}

@ -179,7 +179,7 @@ gulp.task('deps', function (cb) {
gulp.task('lint', function () { gulp.task('lint', function () {
// Ignoring node_modules, dist/firefox, and docs folders: // Ignoring node_modules, dist/firefox, and docs folders:
return gulp.src(['app/**/*.js', 'ui/**/*.js', 'mascara/src/*.js', 'mascara/server/*.js', '!node_modules/**', '!dist/firefox/**', '!docs/**', '!app/scripts/chromereload.js', '!mascara/test/jquery-3.1.0.min.js']) return gulp.src(['app/**/*.js', '!app/scripts/vendor/**/*.js', 'ui/**/*.js', 'mascara/src/*.js', 'mascara/server/*.js', '!node_modules/**', '!dist/firefox/**', '!docs/**', '!app/scripts/chromereload.js', '!mascara/test/jquery-3.1.0.min.js'])
.pipe(eslint(fs.readFileSync(path.join(__dirname, '.eslintrc')))) .pipe(eslint(fs.readFileSync(path.join(__dirname, '.eslintrc'))))
// eslint.format() outputs the lint results to the console. // eslint.format() outputs the lint results to the console.
// Alternatively use eslint.formatEach() (see Docs). // Alternatively use eslint.formatEach() (see Docs).

@ -23,6 +23,7 @@ const states = require('./development/states')
const Selector = require('./development/selector') const Selector = require('./development/selector')
const MetamaskController = require('./app/scripts/metamask-controller') const MetamaskController = require('./app/scripts/metamask-controller')
const firstTimeState = require('./app/scripts/first-time-state') const firstTimeState = require('./app/scripts/first-time-state')
const ExtensionPlatform = require('./app/scripts/platforms/extension')
const extension = require('./development/mockExtension') const extension = require('./development/mockExtension')
const noop = function () {} const noop = function () {}
@ -67,6 +68,7 @@ const controller = new MetamaskController({
initState: firstTimeState, initState: firstTimeState,
}) })
global.metamaskController = controller global.metamaskController = controller
global.platform = new ExtensionPlatform
// //
// User Interface // User Interface

@ -11,7 +11,7 @@ function LoadingIndicator () {
} }
LoadingIndicator.prototype.render = function () { LoadingIndicator.prototype.render = function () {
const { isLoading, loadingMessage } = this.props const { isLoading, loadingMessage, canBypass, bypass } = this.props
return ( return (
isLoading ? h('.full-flex-height', { isLoading ? h('.full-flex-height', {
@ -28,6 +28,16 @@ LoadingIndicator.prototype.render = function () {
background: 'rgba(255, 255, 255, 0.8)', background: 'rgba(255, 255, 255, 0.8)',
}, },
}, [ }, [
canBypass ? h( 'i.fa.fa-close.cursor-pointer.close-loading', {
style: {
position: 'absolute',
top: '1px',
right: '15px',
color: '#AEAEAE',
},
onClick: bypass,
}) : null,
h('img', { h('img', {
src: 'images/loading.svg', src: 'images/loading.svg',
}), }),

@ -62,8 +62,12 @@ ConfirmTxScreen.prototype.render = function () {
h('.flex-column.flex-grow', [ h('.flex-column.flex-grow', [
h(LoadingIndicator, { h(LoadingIndicator, {
isLoading: txData.loadingDefaults, isLoading: this.state ? !this.state.bypassLoadingScreen : txData.loadingDefaults,
loadingMessage: 'Estimating transaction cost…', loadingMessage: 'Estimating transaction cost…',
canBypass: true,
bypass: () => {
this.setState({bypassLoadingScreen: true})
},
}), }),
// subtitle and nav // subtitle and nav

@ -76,9 +76,9 @@
"ensnare": "^1.0.0", "ensnare": "^1.0.0",
"eslint-plugin-react": "^7.4.0", "eslint-plugin-react": "^7.4.0",
"eth-bin-to-ops": "^1.0.1", "eth-bin-to-ops": "^1.0.1",
"eth-block-tracker": "^2.2.0", "eth-block-tracker": "^2.3.0",
"eth-json-rpc-filters": "^1.2.5", "eth-json-rpc-filters": "^1.2.5",
"eth-json-rpc-infura": "^2.0.5", "eth-json-rpc-infura": "^2.0.11",
"eth-keyring-controller": "^2.1.4", "eth-keyring-controller": "^2.1.4",
"eth-contract-metadata": "^1.1.5", "eth-contract-metadata": "^1.1.5",
"eth-hd-keyring": "^1.2.1", "eth-hd-keyring": "^1.2.1",
@ -113,7 +113,7 @@
"iframe-stream": "^3.0.0", "iframe-stream": "^3.0.0",
"inject-css": "^0.1.1", "inject-css": "^0.1.1",
"jazzicon": "^1.2.0", "jazzicon": "^1.2.0",
"json-rpc-engine": "3.2.0", "json-rpc-engine": "^3.6.1",
"json-rpc-middleware-stream": "^1.0.1", "json-rpc-middleware-stream": "^1.0.1",
"lodash.debounce": "^4.0.8", "lodash.debounce": "^4.0.8",
"lodash.memoize": "^4.1.2", "lodash.memoize": "^4.1.2",
@ -150,6 +150,7 @@
"react-router-dom": "^4.2.2", "react-router-dom": "^4.2.2",
"react-select": "^1.0.0", "react-select": "^1.0.0",
"react-simple-file-input": "^2.0.0", "react-simple-file-input": "^2.0.0",
"react-tippy": "^1.2.2",
"react-toggle-button": "^2.2.0", "react-toggle-button": "^2.2.0",
"react-tooltip-component": "^0.3.0", "react-tooltip-component": "^0.3.0",
"react-transition-group": "^2.2.1", "react-transition-group": "^2.2.1",
@ -171,7 +172,7 @@
"valid-url": "^1.0.9", "valid-url": "^1.0.9",
"vreme": "^3.0.2", "vreme": "^3.0.2",
"web3": "^0.20.1", "web3": "^0.20.1",
"web3-provider-engine": "^13.5.0", "web3-provider-engine": "^13.5.6",
"web3-stream-provider": "^3.0.1", "web3-stream-provider": "^3.0.1",
"xtend": "^4.0.1" "xtend": "^4.0.1"
}, },
@ -189,6 +190,7 @@
"brfs": "^1.4.3", "brfs": "^1.4.3",
"browserify": "^14.4.0", "browserify": "^14.4.0",
"chai": "^4.1.0", "chai": "^4.1.0",
"compression": "^1.7.1",
"coveralls": "^3.0.0", "coveralls": "^3.0.0",
"deep-freeze-strict": "^1.1.1", "deep-freeze-strict": "^1.1.1",
"del": "^3.0.0", "del": "^3.0.0",
@ -200,7 +202,7 @@
"eslint-plugin-react": "^7.4.0", "eslint-plugin-react": "^7.4.0",
"eth-json-rpc-middleware": "^1.2.7", "eth-json-rpc-middleware": "^1.2.7",
"fs-promise": "^2.0.3", "fs-promise": "^2.0.3",
"gulp": "github:gulpjs/gulp#4.0", "gulp": "github:gulpjs/gulp#6d71a658c61edb3090221579d8f97dbe086ba2ed",
"gulp-babel": "^7.0.0", "gulp-babel": "^7.0.0",
"gulp-if": "^2.0.2", "gulp-if": "^2.0.2",
"gulp-json-editor": "^2.2.1", "gulp-json-editor": "^2.2.1",

@ -39,9 +39,8 @@ async function runFirstTimeUsageTest(assert, done) {
await timeout() await timeout()
// Scroll through terms // Scroll through terms
const title = app.find('h1').text() const title = app.find('h1')[1]
// TODO Find where Metamask is getting added twice in the title assert.equal(title.textContent, 'MetaMask', 'title screen')
assert.equal(title, 'MetaMaskMetaMask', 'title screen')
// enter password // enter password
const pwBox = app.find('#password-box')[0] const pwBox = app.find('#password-box')[0]
@ -67,19 +66,19 @@ async function runFirstTimeUsageTest(assert, done) {
await timeout(1000) await timeout(1000)
const detail = app.find('.wallet-view')[0] const detail = app.find('.account-detail-section')[0]
assert.ok(detail, 'Account detail section loaded.') assert.ok(detail, 'Account detail section loaded.')
await timeout(1000) const sandwich = app.find('.sandwich-expando')[0]
sandwich.click()
const menu = app.find('.account-menu__icon')[0]
menu.click()
await timeout(1000) await timeout()
const lock = app.find('.account-menu__logout-button')[0] const menu = app.find('.menu-droppo')[0]
assert.ok(lock, 'Lock menu item found') const children = menu.children
lock.click() const logout = children[2]
assert.ok(logout, 'Lock menu item found')
logout.click()
await timeout(1000) await timeout(1000)
@ -91,30 +90,36 @@ async function runFirstTimeUsageTest(assert, done) {
await timeout(1000) await timeout(1000)
const detail2 = app.find('.wallet-view')[0] const detail2 = app.find('.account-detail-section')[0]
assert.ok(detail2, 'Account detail section loaded again.') assert.ok(detail2, 'Account detail section loaded again.')
await timeout() await timeout()
// open account settings dropdown // open account settings dropdown
const qrButton = app.find('.wallet-view__details-button')[0] const qrButton = app.find('.fa.fa-ellipsis-h')[0]
qrButton.click() qrButton.click()
await timeout(1000) await timeout(1000)
const qrHeader = app.find('.editable-label__value')[0] // qr code item
const qrContainer = app.find('.qr-wrapper')[0] const qrButton2 = app.find('.dropdown-menu-item')[1]
qrButton2.click()
await timeout(1000)
const qrHeader = app.find('.qr-header')[0]
const qrContainer = app.find('#qr-container')[0]
assert.equal(qrHeader.textContent, 'Account 1', 'Should show account label.') assert.equal(qrHeader.textContent, 'Account 1', 'Should show account label.')
assert.ok(qrContainer, 'QR Container found') assert.ok(qrContainer, 'QR Container found')
await timeout() await timeout()
const networkMenu = app.find('.network-component')[0] const networkMenu = app.find('.network-indicator')[0]
networkMenu.click() networkMenu.click()
await timeout() await timeout()
const networkMenu2 = app.find('.menu-droppo')[0] const networkMenu2 = app.find('.network-indicator')[0]
const children2 = networkMenu2.children const children2 = networkMenu2.children
children2.length[3] children2.length[3]
assert.ok(children2, 'All network options present') assert.ok(children2, 'All network options present')

@ -0,0 +1,229 @@
const reactTriggerChange = require('react-trigger-change')
const PASSWORD = 'password123'
QUnit.module('new ui send flow')
QUnit.test('successful send flow', (assert) => {
const done = assert.async()
runSendFlowTest(assert).then(done).catch((err) => {
assert.notOk(err, `Error was thrown: ${err.stack}`)
done()
})
})
global.ethQuery = {
sendTransaction: () => {},
}
async function runSendFlowTest(assert, done) {
console.log('*** start runSendFlowTest')
const selectState = $('select')
selectState.val('send new ui')
reactTriggerChange(selectState[0])
await timeout(2000)
const sendScreenButton = $('button.btn-clear.hero-balance-button')
assert.ok(sendScreenButton[1], 'send screen button present')
sendScreenButton[1].click()
await timeout(1000)
const sendContainer = $('.send-v2__container')
assert.ok(sendContainer[0], 'send container renders')
const sendHeader = $('.send-v2__send-header-icon')
assert.ok(sendHeader[0], 'send screen has a header icon')
const sendTitle = $('.send-v2__title')
assert.equal(sendTitle[0].textContent, 'Send Funds', 'Send screen title is correct')
const sendCopy = $('.send-v2__copy')
assert.equal(sendCopy[0].textContent, 'Only send ETH to an Ethereum address.', 'Send screen has copy')
const sendFromField = $('.send-v2__form-field')
assert.ok(sendFromField[0], 'send screen has a from field')
let sendFromFieldItemAddress = $('.account-list-item__account-name')
assert.equal(sendFromFieldItemAddress[0].textContent, 'Send Account 4', 'send from field shows correct account name')
const sendFromFieldItem = $('.account-list-item')
sendFromFieldItem[0].click()
await timeout()
const sendFromDropdownList = $('.send-v2__from-dropdown__list')
assert.equal(sendFromDropdownList.children().length, 4, 'send from dropdown shows all accounts')
console.log(`!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! sendFromDropdownList.children()[1]`, sendFromDropdownList.children()[1]);
sendFromDropdownList.children()[1].click()
await timeout()
sendFromFieldItemAddress = $('.account-list-item__account-name')
console.log(`!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! sendFromFieldItemAddress[0]`, sendFromFieldItemAddress[0]);
assert.equal(sendFromFieldItemAddress[0].textContent, 'Send Account 2', 'send from field dropdown changes account name')
let sendToFieldInput = $('.send-v2__to-autocomplete__input')
sendToFieldInput[0].focus()
await timeout()
const sendToDropdownList = $('.send-v2__from-dropdown__list')
assert.equal(sendToDropdownList.children().length, 5, 'send to dropdown shows all accounts and address book accounts')
sendToDropdownList.children()[2].click()
await timeout()
const sendToAccountAddress = sendToFieldInput.val()
assert.equal(sendToAccountAddress, '0x2f8d4a878cfa04a6e60d46362f5644deab66572d', 'send to dropdown selects the correct address')
const sendAmountField = $('.send-v2__form-row:eq(2)')
sendAmountField.find('.currency-display')[0].click()
await timeout()
const sendAmountFieldInput = sendAmountField.find('input:text')
sendAmountFieldInput.val('5.1')
reactTriggerChange(sendAmountField.find('input')[0])
await timeout()
let errorMessage = $('.send-v2__error')
assert.equal(errorMessage[0].textContent, 'Insufficient funds.', 'send should render an insufficient fund error message')
sendAmountFieldInput.val('2.0')
reactTriggerChange(sendAmountFieldInput[0])
await timeout()
errorMessage = $('.send-v2__error')
assert.equal(errorMessage.length, 0, 'send should stop rendering amount error message after amount is corrected')
const sendGasField = $('.send-v2__gas-fee-display')
assert.equal(
sendGasField.find('.currency-display__input-wrapper > input').val(),
'0.000198',
'send gas field should show estimated gas total'
)
assert.equal(
sendGasField.find('.currency-display__converted-value')[0].textContent,
'0.24 USD',
'send gas field should show estimated gas total converted to USD'
)
const sendGasOpenCustomizeModalButton = $('.send-v2__sliders-icon-container'
)
sendGasOpenCustomizeModalButton[0].click()
await timeout(1000)
const customizeGasModal = $('.send-v2__customize-gas')
assert.ok(customizeGasModal[0], 'should render the customize gas modal')
const customizeGasPriceInput = $('.send-v2__gas-modal-card').first().find('input')
customizeGasPriceInput.val(50)
reactTriggerChange(customizeGasPriceInput[0])
const customizeGasLimitInput = $('.send-v2__gas-modal-card').last().find('input')
customizeGasLimitInput.val(60000)
reactTriggerChange(customizeGasLimitInput[0])
await timeout()
const customizeGasSaveButton = $('.send-v2__customize-gas__save')
customizeGasSaveButton[0].click()
await timeout()
assert.equal(
sendGasField.find('.currency-display__input-wrapper > input').val(),
'0.003',
'send gas field should show customized gas total'
)
assert.equal(
sendGasField.find('.currency-display__converted-value')[0].textContent,
'3.60 USD',
'send gas field should show customized gas total converted to USD'
)
const sendButton = $('.send-v2__next-btn')
sendButton[0].click()
await timeout(2000)
selectState.val('send edit')
reactTriggerChange(selectState[0])
await timeout(2000)
const confirmFromName = $('.confirm-screen-account-name').first()
assert.equal(confirmFromName[0].textContent, 'Send Account 2', 'confirm screen should show correct from name')
const confirmToName = $('.confirm-screen-account-name').last()
assert.equal(confirmToName[0].textContent, 'Send Account 3', 'confirm screen should show correct to name')
const confirmScreenRows = $('.confirm-screen-rows')
const confirmScreenGas = confirmScreenRows.find('.confirm-screen-row-info')[2]
assert.equal(confirmScreenGas.textContent, '3.6 USD', 'confirm screen should show correct gas')
const confirmScreenTotal = confirmScreenRows.find('.confirm-screen-row-info')[3]
assert.equal(confirmScreenTotal.textContent, '2405.36 USD', 'confirm screen should show correct total')
const confirmScreenBackButton = $('.confirm-screen-back-button')
confirmScreenBackButton[0].click()
await timeout(1000)
const sendFromFieldItemInEdit = $('.account-list-item')
sendFromFieldItemInEdit[0].click()
await timeout()
const sendFromDropdownListInEdit = $('.send-v2__from-dropdown__list')
sendFromDropdownListInEdit.children()[2].click()
await timeout()
const sendToFieldInputInEdit = $('.send-v2__to-autocomplete__input')
sendToFieldInputInEdit[0].focus()
sendToFieldInputInEdit.val('0xd85a4b6a394794842887b8284293d69163007bbb')
await timeout()
const sendAmountFieldInEdit = $('.send-v2__form-row:eq(2)')
sendAmountFieldInEdit.find('.currency-display')[0].click()
await timeout()
const sendAmountFieldInputInEdit = sendAmountFieldInEdit.find('input:text')
sendAmountFieldInputInEdit.val('1.0')
reactTriggerChange(sendAmountFieldInputInEdit[0])
await timeout()
const sendButtonInEdit = $('.send-v2__next-btn')
sendButtonInEdit[0].click()
await timeout()
// TODO: Need a way to mock background so that we can test correct transition from editing to confirm
selectState.val('confirm new ui')
reactTriggerChange(selectState[0])
await timeout(2000)
const confirmScreenConfirmButton = $('.confirm-screen-confirm-button')
console.log(`+++++++++++++++++++++++++++++++= confirmScreenConfirmButton[0]`, confirmScreenConfirmButton[0]);
confirmScreenConfirmButton[0].click()
await timeout(2000)
const txView = $('.tx-view')
console.log(`++++++++++++++++++++++++++++++++ txView[0]`, txView[0]);
assert.ok(txView[0], 'Should return to the account details screen after confirming')
}
function timeout (time) {
return new Promise((resolve, reject) => {
setTimeout(resolve, time || 1500)
})
}

@ -1,12 +1,12 @@
const JsonRpcEngine = require('json-rpc-engine') const JsonRpcEngine = require('json-rpc-engine')
const scaffoldMiddleware = require('eth-json-rpc-middleware/scaffold') const scaffoldMiddleware = require('eth-json-rpc-middleware/scaffold')
const TestBlockchain = require('eth-block-tracker/test/util/testBlockMiddleware')
module.exports = { module.exports = {
createEngineForTestData, createEngineForTestData,
providerFromEngine, providerFromEngine,
scaffoldMiddleware, scaffoldMiddleware,
createEthJsQueryStub, createTestProviderTools,
createStubedProvider,
} }
@ -19,20 +19,13 @@ function providerFromEngine (engine) {
return provider return provider
} }
function createEthJsQueryStub (stubProvider) { function createTestProviderTools (opts = {}) {
return new Proxy({}, {
get: (obj, method) => {
return (...params) => {
return new Promise((resolve, reject) => {
stubProvider.sendAsync({ method: `eth_${method}`, params }, (err, ress) => resolve(ress.result))
})
}
},
})
}
function createStubedProvider (resultStub) {
const engine = createEngineForTestData() const engine = createEngineForTestData()
engine.push(scaffoldMiddleware(resultStub)) const testBlockchain = new TestBlockchain()
return providerFromEngine(engine) // handle provided hooks
engine.push(scaffoldMiddleware(opts.scaffold || {}))
// handle block tracker methods
engine.push(testBlockchain.createMiddleware())
const provider = providerFromEngine(engine)
return { provider, engine, testBlockchain }
} }

@ -3,7 +3,7 @@ const ethUtil = require('ethereumjs-util')
const EthTx = require('ethereumjs-tx') const EthTx = require('ethereumjs-tx')
const ObservableStore = require('obs-store') const ObservableStore = require('obs-store')
const clone = require('clone') const clone = require('clone')
const { createStubedProvider } = require('../stub/provider') const { createTestProviderTools } = require('../stub/provider')
const PendingTransactionTracker = require('../../app/scripts/lib/pending-tx-tracker') const PendingTransactionTracker = require('../../app/scripts/lib/pending-tx-tracker')
const MockTxGen = require('../lib/mock-tx-gen') const MockTxGen = require('../lib/mock-tx-gen')
const sinon = require('sinon') const sinon = require('sinon')
@ -40,7 +40,7 @@ describe('PendingTransactionTracker', function () {
txParams: { from: '0x1678a085c290ebd122dc42cba69373b5953b831d'}, txParams: { from: '0x1678a085c290ebd122dc42cba69373b5953b831d'},
} }
providerResultStub = {} providerResultStub = {}
provider = createStubedProvider(providerResultStub) provider = createTestProviderTools({ scaffold: providerResultStub }).provider
pendingTxTracker = new PendingTransactionTracker({ pendingTxTracker = new PendingTransactionTracker({
provider, provider,

@ -1,11 +1,12 @@
const assert = require('assert') const assert = require('assert')
const ethUtil = require('ethereumjs-util') const ethUtil = require('ethereumjs-util')
const EthTx = require('ethereumjs-tx') const EthTx = require('ethereumjs-tx')
const EthjsQuery = require('ethjs-query')
const ObservableStore = require('obs-store') const ObservableStore = require('obs-store')
const sinon = require('sinon') const sinon = require('sinon')
const TransactionController = require('../../app/scripts/controllers/transactions') const TransactionController = require('../../app/scripts/controllers/transactions')
const TxGasUtils = require('../../app/scripts/lib/tx-gas-utils') const TxGasUtils = require('../../app/scripts/lib/tx-gas-utils')
const { createStubedProvider, createEthJsQueryStub } = require('../stub/provider') const { createTestProviderTools } = require('../stub/provider')
const noop = () => true const noop = () => true
const currentNetworkId = 42 const currentNetworkId = 42
@ -14,11 +15,18 @@ const privKey = new Buffer('8718b9618a37d1fc78c436511fc6df3c8258d3250635bba617f3
describe('Transaction Controller', function () { describe('Transaction Controller', function () {
let txController, provider, providerResultStub let txController, provider, providerResultStub, testBlockchain
beforeEach(function () { beforeEach(function () {
providerResultStub = {} providerResultStub = {
provider = createStubedProvider(providerResultStub) // 1 gwei
eth_gasPrice: '0x0de0b6b3a7640000',
// by default, all accounts are external accounts (not contracts)
eth_getCode: '0x',
}
const providerTools = createTestProviderTools({ scaffold: providerResultStub })
provider = providerTools.provider
testBlockchain = providerTools.testBlockchain
txController = new TransactionController({ txController = new TransactionController({
provider, provider,
@ -30,10 +38,7 @@ describe('Transaction Controller', function () {
resolve() resolve()
}), }),
}) })
txController.query = createEthJsQueryStub(provider)
txController.txGasUtil.query = createEthJsQueryStub(provider)
txController.nonceTracker.getNonceLock = () => Promise.resolve({ nextNonce: 0, releaseLock: noop }) txController.nonceTracker.getNonceLock = () => Promise.resolve({ nextNonce: 0, releaseLock: noop })
txController.txProviderUtils = new TxGasUtils(txController.provider)
}) })
describe('#getState', function () { describe('#getState', function () {
@ -155,15 +160,6 @@ describe('Transaction Controller', function () {
}) })
describe('#addUnapprovedTransaction', function () { describe('#addUnapprovedTransaction', function () {
let addTxDefaults
beforeEach(() => {
addTxDefaults = txController.addTxDefaults
txController.addTxDefaults = function addTxDefaultsStub () { return Promise.resolve() }
})
afterEach(() => {
txController.addTxDefaults = addTxDefaults
})
it('should add an unapproved transaction and return a valid txMeta', function (done) { it('should add an unapproved transaction and return a valid txMeta', function (done) {
txController.addUnapprovedTransaction({}) txController.addUnapprovedTransaction({})
@ -219,7 +215,7 @@ describe('Transaction Controller', function () {
var sample = { var sample = {
value: '0x01', value: '0x01',
} }
txController.txProviderUtils.validateTxParams(sample).then(() => { txController.txGasUtil.validateTxParams(sample).then(() => {
done() done()
}).catch(done) }).catch(done)
}) })
@ -228,7 +224,7 @@ describe('Transaction Controller', function () {
var sample = { var sample = {
value: '-0x01', value: '-0x01',
} }
txController.txProviderUtils.validateTxParams(sample) txController.txGasUtil.validateTxParams(sample)
.then(() => done('expected to thrown on negativity values but didn\'t')) .then(() => done('expected to thrown on negativity values but didn\'t'))
.catch((err) => { .catch((err) => {
assert.ok(err, 'error') assert.ok(err, 'error')

@ -1,12 +1,12 @@
const assert = require('assert') const assert = require('assert')
const TxGasUtils = require('../../app/scripts/lib/tx-gas-utils') const TxGasUtils = require('../../app/scripts/lib/tx-gas-utils')
const { createStubedProvider } = require('../stub/provider') const { createTestProviderTools } = require('../stub/provider')
describe('Tx Gas Util', function () { describe('Tx Gas Util', function () {
let txGasUtil, provider, providerResultStub let txGasUtil, provider, providerResultStub
beforeEach(function () { beforeEach(function () {
providerResultStub = {} providerResultStub = {}
provider = createStubedProvider(providerResultStub) provider = createTestProviderTools({ scaffold: providerResultStub }).provider
txGasUtil = new TxGasUtils({ txGasUtil = new TxGasUtils({
provider, provider,
}) })

@ -55,6 +55,7 @@ var actions = {
SET_NEW_ACCOUNT_FORM: 'SET_NEW_ACCOUNT_FORM', SET_NEW_ACCOUNT_FORM: 'SET_NEW_ACCOUNT_FORM',
unlockMetamask: unlockMetamask, unlockMetamask: unlockMetamask,
unlockFailed: unlockFailed, unlockFailed: unlockFailed,
unlockSucceeded,
showCreateVault: showCreateVault, showCreateVault: showCreateVault,
showRestoreVault: showRestoreVault, showRestoreVault: showRestoreVault,
showInitializeMenu: showInitializeMenu, showInitializeMenu: showInitializeMenu,
@ -78,6 +79,7 @@ var actions = {
// unlock screen // unlock screen
UNLOCK_IN_PROGRESS: 'UNLOCK_IN_PROGRESS', UNLOCK_IN_PROGRESS: 'UNLOCK_IN_PROGRESS',
UNLOCK_FAILED: 'UNLOCK_FAILED', UNLOCK_FAILED: 'UNLOCK_FAILED',
UNLOCK_SUCCEEDED: 'UNLOCK_SUCCEEDED',
UNLOCK_METAMASK: 'UNLOCK_METAMASK', UNLOCK_METAMASK: 'UNLOCK_METAMASK',
LOCK_METAMASK: 'LOCK_METAMASK', LOCK_METAMASK: 'LOCK_METAMASK',
tryUnlockMetamask: tryUnlockMetamask, tryUnlockMetamask: tryUnlockMetamask,
@ -281,12 +283,14 @@ function tryUnlockMetamask (password) {
log.debug(`background.submitPassword`) log.debug(`background.submitPassword`)
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
background.submitPassword(password, err => { background.submitPassword(password, (err) => {
dispatch(actions.hideLoadingIndication()) dispatch(actions.hideLoadingIndication())
if (err) { if (err) {
dispatch(actions.unlockFailed(err.message))
reject(err) reject(err)
} else { } else {
dispatch(actions.unlockSucceeded())
dispatch(actions.transitionForward()) dispatch(actions.transitionForward())
return forceUpdateMetamaskState(dispatch).then(resolve) return forceUpdateMetamaskState(dispatch).then(resolve)
} }
@ -967,6 +971,13 @@ function unlockFailed (message) {
} }
} }
function unlockSucceeded (message) {
return {
type: actions.UNLOCK_SUCCEEDED,
value: message,
}
}
function unlockMetamask (account) { function unlockMetamask (account) {
return { return {
type: actions.UNLOCK_METAMASK, type: actions.UNLOCK_METAMASK,

@ -24,7 +24,7 @@ Network.prototype.render = function () {
let iconName, hoverText let iconName, hoverText
if (networkNumber === 'loading') { if (networkNumber === 'loading') {
return h('span.pointer', { return h('span.pointer.network-indicator', {
style: { style: {
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',

@ -43,7 +43,6 @@ function mapStateToProps (state) {
function mapDispatchToProps (dispatch) { function mapDispatchToProps (dispatch) {
return { return {
goHome: () => dispatch(actions.goHome()),
addTokens: tokens => dispatch(actions.addTokens(tokens)), addTokens: tokens => dispatch(actions.addTokens(tokens)),
} }
} }

@ -37,7 +37,7 @@ AccountImportSubview.prototype.render = function () {
h('div.new-account-import-form__select-section', [ h('div.new-account-import-form__select-section', [
h('div.new-account-import-form__select-label', 'SELECT TYPE'), h('div.new-account-import-form__select-label', 'Select Type'),
h(Select, { h(Select, {
className: 'new-account-import-form__select', className: 'new-account-import-form__select',

@ -37,15 +37,20 @@ PrivateKeyImportView.prototype.render = function () {
return ( return (
h('div.new-account-import-form__private-key', [ h('div.new-account-import-form__private-key', [
h('span.new-account-create-form__instruction', 'Paste your private key string here:'),
h('input.new-account-import-form__input-password', { h('div.new-account-import-form__private-key-password-container', [
type: 'password',
id: 'private-key-box',
onKeyPress: () => this.createKeyringOnEnter(),
}),
h('div.new-account-create-form__buttons', {}, [ h('span.new-account-import-form__instruction', 'Paste your private key string here:'),
h('input.new-account-import-form__input-password', {
type: 'password',
id: 'private-key-box',
onKeyPress: () => this.createKeyringOnEnter(),
}),
]),
h('div.new-account-import-form__buttons', {}, [
h('button.new-account-create-form__button-cancel', { h('button.new-account-create-form__button-cancel', {
onClick: () => this.props.history.push(DEFAULT_ROUTE), onClick: () => this.props.history.push(DEFAULT_ROUTE),

@ -8,16 +8,18 @@ const { DEFAULT_ROUTE } = require('../../../routes')
class NewAccountCreateForm extends Component { class NewAccountCreateForm extends Component {
constructor (props) { constructor (props) {
super(props) super(props)
const { numberOfExistingAccounts = 0 } = props const { numberOfExistingAccounts = 0 } = props
const newAccountNumber = numberOfExistingAccounts + 1 const newAccountNumber = numberOfExistingAccounts + 1
this.state = { this.state = {
newAccountName: `Account ${newAccountNumber}`, newAccountName: '',
defaultAccountName: `Account ${newAccountNumber}`,
} }
} }
render () { render () {
const { newAccountName } = this.state const { newAccountName, defaultAccountName } = this.state
const { history, createAccount } = this.props const { history, createAccount } = this.props
return h('div.new-account-create-form', [ return h('div.new-account-create-form', [
@ -28,8 +30,8 @@ class NewAccountCreateForm extends Component {
h('div.new-account-create-form__input-wrapper', {}, [ h('div.new-account-create-form__input-wrapper', {}, [
h('input.new-account-create-form__input', { h('input.new-account-create-form__input', {
value: this.state.newAccountName, value: newAccountName,
placeholder: 'E.g. My new account', placeholder: defaultAccountName,
onChange: event => this.setState({ newAccountName: event.target.value }), onChange: event => this.setState({ newAccountName: event.target.value }),
}, []), }, []),
]), ]),
@ -44,7 +46,7 @@ class NewAccountCreateForm extends Component {
h('button.new-account-create-form__button-create', { h('button.new-account-create-form__button-create', {
onClick: () => { onClick: () => {
createAccount(newAccountName) createAccount(newAccountName || defaultAccountName)
.then(() => history.push(DEFAULT_ROUTE)) .then(() => history.push(DEFAULT_ROUTE))
}, },
}, [ }, [

@ -83,7 +83,6 @@ function mapDispatchToProps (dispatch) {
updateSendAmount: newAmount => dispatch(actions.updateSendAmount(newAmount)), updateSendAmount: newAmount => dispatch(actions.updateSendAmount(newAmount)),
updateSendMemo: newMemo => dispatch(actions.updateSendMemo(newMemo)), updateSendMemo: newMemo => dispatch(actions.updateSendMemo(newMemo)),
updateSendErrors: newError => dispatch(actions.updateSendErrors(newError)), updateSendErrors: newError => dispatch(actions.updateSendErrors(newError)),
goHome: () => dispatch(actions.goHome()),
clearSend: () => dispatch(actions.clearSend()), clearSend: () => dispatch(actions.clearSend()),
setMaxModeTo: bool => dispatch(actions.setMaxModeTo(bool)), setMaxModeTo: bool => dispatch(actions.setMaxModeTo(bool)),
} }

@ -0,0 +1,31 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const ReactTippy = require('react-tippy').Tooltip
module.exports = Tooltip
inherits(Tooltip, Component)
function Tooltip () {
Component.call(this)
}
Tooltip.prototype.render = function () {
const props = this.props
const { position, title, children, wrapperClassName } = props
return h('div', {
className: wrapperClassName,
}, [
h(ReactTippy, {
title,
position: position || 'left',
trigger: 'mouseenter',
hideOnClick: false,
size: 'small',
arrow: true,
}, children),
])
}

@ -4,8 +4,10 @@ const h = require('react-hyperscript')
const { withRouter } = require('react-router-dom') const { withRouter } = require('react-router-dom')
const { compose } = require('recompose') const { compose } = require('recompose')
const inherits = require('util').inherits const inherits = require('util').inherits
const classnames = require('classnames')
const Identicon = require('./identicon') const Identicon = require('./identicon')
// const AccountDropdowns = require('./dropdowns/index.js').AccountDropdowns // const AccountDropdowns = require('./dropdowns/index.js').AccountDropdowns
const Tooltip = require('./tooltip-v2.js')
const copyToClipboard = require('copy-to-clipboard') const copyToClipboard = require('copy-to-clipboard')
const actions = require('../actions') const actions = require('../actions')
const BalanceComponent = require('./balance-component') const BalanceComponent = require('./balance-component')
@ -51,6 +53,7 @@ function WalletView () {
Component.call(this) Component.call(this)
this.state = { this.state = {
hasCopied: false, hasCopied: false,
copyToClipboardPressed: false,
} }
} }
@ -140,17 +143,30 @@ WalletView.prototype.render = function () {
]), ]),
]), ]),
h(Tooltip, {
h('div.wallet-view__address', { position: 'bottom',
onClick: () => { title: this.state.hasCopied ? 'Copied!' : 'Copy to clipboard',
copyToClipboard(selectedAddress) wrapperClassName: 'wallet-view__tooltip',
this.setState({ hasCopied: true })
setTimeout(() => this.setState({ hasCopied: false }), 3000)
},
}, [ }, [
this.state.hasCopied && 'Copied to Clipboard', h('button.wallet-view__address', {
!this.state.hasCopied && `${selectedAddress.slice(0, 4)}...${selectedAddress.slice(-4)}`, className: classnames({
h('i.fa.fa-clipboard', { style: { marginLeft: '8px' } }), 'wallet-view__address__pressed': this.state.copyToClipboardPressed,
}),
onClick: () => {
copyToClipboard(selectedAddress)
this.setState({ hasCopied: true })
setTimeout(() => this.setState({ hasCopied: false }), 3000)
},
onMouseDown: () => {
this.setState({ copyToClipboardPressed: true })
},
onMouseUp: () => {
this.setState({ copyToClipboardPressed: false })
},
}, [
`${selectedAddress.slice(0, 4)}...${selectedAddress.slice(-4)}`,
h('i.fa.fa-clipboard', { style: { marginLeft: '8px' } }),
]),
]), ]),
this.renderWalletBalance(), this.renderWalletBalance(),

@ -126,7 +126,6 @@ ConfirmTxScreen.prototype.render = function () {
cancelPersonalMessage: this.cancelPersonalMessage.bind(this, txData), cancelPersonalMessage: this.cancelPersonalMessage.bind(this, txData),
cancelTypedMessage: this.cancelTypedMessage.bind(this, txData), cancelTypedMessage: this.cancelTypedMessage.bind(this, txData),
}) })
} }
function currentTxView (opts) { function currentTxView (opts) {

@ -55,3 +55,5 @@
@import './pages/index.scss'; @import './pages/index.scss';
@import './new-account.scss'; @import './new-account.scss';
@import './tooltip.scss';

@ -56,11 +56,17 @@
} }
.new-account-import-form { .new-account-import-form {
display: flex;
flex-flow: column;
align-items: center;
padding: 0 30px;
&__select-section { &__select-section {
display: flex; display: flex;
justify-content: space-evenly; justify-content: space-between;
align-items: center; align-items: center;
margin-top: 29px; margin-top: 29px;
width: 100%;
} }
&__select-label { &__select-label {
@ -92,19 +98,25 @@
} }
} }
&__private-key-password-container {
display: flex;
flex-flow: column;
align-items: center;
width: 100%;
}
&__instruction { &__instruction {
color: $scorpion; color: $scorpion;
font-family: Roboto; font-family: Roboto;
font-size: 16px; font-size: 16px;
line-height: 21px; line-height: 21px;
align-self: flex-start; align-self: flex-start;
margin-left: 30px;
} }
&__private-key { &__private-key {
display: flex; display: flex;
flex-flow: column; flex-flow: column;
align-items: center; align-items: flex-start;
margin-top: 34px; margin-top: 34px;
} }
@ -127,6 +139,13 @@
align-items: center; align-items: center;
margin-top: 29px; margin-top: 29px;
} }
&__buttons {
margin-top: 39px;
display: flex;
width: 100%;
justify-content: space-between;
}
} }
.new-account-create-form { .new-account-create-form {

@ -89,6 +89,12 @@ $wallet-view-bg: $alabaster;
flex: 0 0 auto; flex: 0 0 auto;
} }
&__tooltip {
display: flex;
justify-content: center;
padding: 24px;
}
&__address { &__address {
border-radius: 3px; border-radius: 3px;
background-color: $alto; background-color: $alto;
@ -96,10 +102,13 @@ $wallet-view-bg: $alabaster;
font-size: 14px; font-size: 14px;
line-height: 12px; line-height: 12px;
padding: 4px 12px; padding: 4px 12px;
margin: 24px auto;
font-weight: 300; font-weight: 300;
cursor: pointer; cursor: pointer;
flex: 0 0 auto; flex: 0 0 auto;
&__pressed {
background-color: $manatee,
}
} }
&__sidebar-close { &__sidebar-close {

File diff suppressed because one or more lines are too long

@ -15,134 +15,91 @@ function InfoScreen () {
Component.call(this) Component.call(this)
} }
InfoScreen.prototype.render = function () { InfoScreen.prototype.renderLogo = function () {
const state = this.props
const version = global.platform.getVersion()
return ( return (
h('.flex-column.flex-grow', { h('div.settings__info-logo-wrapper', [
style: { h('img.settings__info-logo', { src: 'images/info-logo.png' }),
maxWidth: '400px', ])
}, )
}, [ }
// subtitle and nav InfoScreen.prototype.renderInfoLinks = function () {
h('.section-title.flex-row.flex-center', [ return (
h('i.fa.fa-arrow-left.fa-lg.cursor-pointer', { h('div.settings__content-item.settings__content-item--without-height', [
onClick: (event) => { h('div.settings__info-link-header', 'Links'),
state.dispatch(actions.goHome()) h('div.settings__info-link-item', [
}, h('a', {
}), href: 'https://metamask.io/privacy.html',
h('h2.page-subtitle', 'Info'), target: '_blank',
}, [
h('span.settings__info-link', 'Privacy Policy'),
]),
]), ]),
h('div.settings__info-link-item', [
// main view h('a', {
h('.flex-column.flex-justify-center.flex-grow.select-none', [ href: 'https://metamask.io/terms.html',
h('.flex-space-around', { target: '_blank',
style: {
padding: '20px',
},
}, [ }, [
// current version number h('span.settings__info-link', 'Terms of Use'),
]),
h('.info.info-gray', [ ]),
h('div', 'Metamask'), h('div.settings__info-link-item', [
h('div', { h('a', {
style: { href: 'https://metamask.io/attributions.html',
marginBottom: '10px', target: '_blank',
}, }, [
}, `Version: ${version}`), h('span.settings__info-link', 'Attributions'),
]), ]),
]),
h('div', { h('hr.settings__info-separator'),
style: { h('div.settings__info-link-item', [
marginBottom: '5px', h('a', {
}}, href: 'https://support.metamask.io',
[ target: '_blank',
h('div', [ }, [
h('a', { h('span.settings__info-link', 'Visit our Support Center'),
href: 'https://metamask.io/privacy.html', ]),
target: '_blank', ]),
onClick (event) { this.navigateTo(event.target.href) }, h('div.settings__info-link-item', [
}, [ h('a', {
h('div.info', 'Privacy Policy'), href: 'https://metamask.io/',
]), target: '_blank',
]), }, [
h('div', [ h('span.settings__info-link', 'Visit our web site'),
h('a', { ]),
href: 'https://metamask.io/terms.html', ]),
target: '_blank', h('div.settings__info-link-item', [
onClick (event) { this.navigateTo(event.target.href) }, h('a', {
}, [ target: '_blank',
h('div.info', 'Terms of Use'), href: 'mailto:help@metamask.io?subject=Feedback',
]), }, [
]), h('span.settings__info-link', 'Email us!'),
h('div', [ ]),
h('a', { ]),
href: 'https://metamask.io/attributions.html', ])
target: '_blank', )
onClick (event) { this.navigateTo(event.target.href) }, }
}, [
h('div.info', 'Attributions'),
]),
]),
]
),
h('hr', {
style: {
margin: '10px 0 ',
width: '7em',
},
}),
h('div', {
style: {
paddingLeft: '30px',
}},
[
h('div.fa.fa-support', [
h('a.info', {
href: 'https://metamask.helpscoutdocs.com/',
target: '_blank',
}, 'Visit our Knowledge Base'),
]),
h('div', [
h('a', {
href: 'https://metamask.io/',
target: '_blank',
}, [
h('img.icon-size', {
src: 'images/icon-128.png',
style: {
// IE6-9
filter: 'grayscale(100%)',
// Microsoft Edge and Firefox 35+
WebkitFilter: 'grayscale(100%)',
},
}),
h('div.info', 'Visit our web site'),
]),
]),
h('div', [ InfoScreen.prototype.render = function () {
h('.fa.fa-twitter', [ const version = global.platform.getVersion()
h('a.info', {
href: 'https://twitter.com/metamask_io',
target: '_blank',
}, 'Follow us on Twitter'),
]),
]),
h('div.fa.fa-envelope', [ return (
h('a.info', { h('div.settings__content', [
target: '_blank', h('div.settings__content-row', [
href: 'mailto:support@metamask.io?subject=MetaMask Support', h('div.settings__content-item.settings__content-item--without-height', [
}, 'Email us!'), this.renderLogo(),
]), h('div.settings__info-item', [
]), h('div.settings__info-version-header', 'MetaMask Version'),
h('div.settings__info-version-number', `${version}`),
]),
h('div.settings__info-item', [
h(
'div.settings__info-about',
'MetaMask is designed and built in California.'
),
]),
]), ]),
this.renderInfoLinks(),
]), ]),
]) ])
) )

@ -484,6 +484,11 @@ function reduceApp (state, action) {
warning: action.value || 'Incorrect password. Try again.', warning: action.value || 'Incorrect password. Try again.',
}) })
case actions.UNLOCK_SUCCEEDED:
return extend(appState, {
warning: '',
})
case actions.SHOW_LOADING: case actions.SHOW_LOADING:
return extend(appState, { return extend(appState, {
isLoading: true, isLoading: true,

@ -2503,6 +2503,24 @@ component-inherit@0.0.3:
version "0.0.3" version "0.0.3"
resolved "https://registry.yarnpkg.com/component-inherit/-/component-inherit-0.0.3.tgz#645fc4adf58b72b649d5cae65135619db26ff143" resolved "https://registry.yarnpkg.com/component-inherit/-/component-inherit-0.0.3.tgz#645fc4adf58b72b649d5cae65135619db26ff143"
compressible@~2.0.11:
version "2.0.12"
resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.12.tgz#c59a5c99db76767e9876500e271ef63b3493bd66"
dependencies:
mime-db ">= 1.30.0 < 2"
compression@^1.7.1:
version "1.7.1"
resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.1.tgz#eff2603efc2e22cf86f35d2eb93589f9875373db"
dependencies:
accepts "~1.3.4"
bytes "3.0.0"
compressible "~2.0.11"
debug "2.6.9"
on-headers "~1.0.1"
safe-buffer "5.1.1"
vary "~1.1.2"
concat-map@0.0.1: concat-map@0.0.1:
version "0.0.1" version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
@ -3805,7 +3823,18 @@ eth-block-tracker@^1.0.7:
pify "^2.3.0" pify "^2.3.0"
tape "^4.6.3" tape "^4.6.3"
eth-block-tracker@^2.1.2, eth-block-tracker@^2.2.0, eth-block-tracker@^2.2.2: eth-block-tracker@^2.1.2, eth-block-tracker@^2.2.2:
version "2.2.2"
resolved "https://registry.yarnpkg.com/eth-block-tracker/-/eth-block-tracker-2.2.2.tgz#b3d72cd82ba5ee37471d22bac4f56387ee4137cf"
dependencies:
async-eventemitter ahultgren/async-eventemitter#fa06e39e56786ba541c180061dbf2c0a5bbf951c
babelify "^7.3.0"
eth-query "^2.1.0"
ethjs-util "^0.1.3"
pify "^2.3.0"
tape "^4.6.3"
eth-block-tracker@^2.3.0:
version "2.3.0" version "2.3.0"
resolved "https://registry.yarnpkg.com/eth-block-tracker/-/eth-block-tracker-2.3.0.tgz#4cb782c8ef8fde2f5dc894921ae1f5c1077c35a4" resolved "https://registry.yarnpkg.com/eth-block-tracker/-/eth-block-tracker-2.3.0.tgz#4cb782c8ef8fde2f5dc894921ae1f5c1077c35a4"
dependencies: dependencies:
@ -3849,7 +3878,7 @@ eth-json-rpc-filters@^1.2.5:
json-rpc-engine "^3.4.0" json-rpc-engine "^3.4.0"
lodash.flatmap "^4.5.0" lodash.flatmap "^4.5.0"
eth-json-rpc-infura@^2.0.5: eth-json-rpc-infura@^2.0.11:
version "2.0.11" version "2.0.11"
resolved "https://registry.yarnpkg.com/eth-json-rpc-infura/-/eth-json-rpc-infura-2.0.11.tgz#134bf54ff15e96a9116424c0db9b66aa079bfbbe" resolved "https://registry.yarnpkg.com/eth-json-rpc-infura/-/eth-json-rpc-infura-2.0.11.tgz#134bf54ff15e96a9116424c0db9b66aa079bfbbe"
dependencies: dependencies:
@ -6379,16 +6408,27 @@ json-parse-better-errors@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.1.tgz#50183cd1b2d25275de069e9e71b467ac9eab973a" resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.1.tgz#50183cd1b2d25275de069e9e71b467ac9eab973a"
json-rpc-engine@3.2.0: json-rpc-engine@^3.0.1, json-rpc-engine@^3.1.0, json-rpc-engine@^3.4.0:
version "3.2.0" version "3.4.0"
resolved "https://registry.yarnpkg.com/json-rpc-engine/-/json-rpc-engine-3.2.0.tgz#d34dff106c8339c337a894da801f73b1f77b1bc8" resolved "https://registry.yarnpkg.com/json-rpc-engine/-/json-rpc-engine-3.4.0.tgz#8a1647a7f2cc7018f4802f41ec8208d281f78bfc"
dependencies:
async "^2.0.1"
babel-preset-env "^1.3.2"
babelify "^7.3.0"
json-rpc-error "^2.0.0"
promise-to-callback "^1.0.0"
json-rpc-engine@^3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/json-rpc-engine/-/json-rpc-engine-3.6.0.tgz#0cc673dcb4b71103523fec81d1bba195a457f993"
dependencies: dependencies:
async "^2.0.1" async "^2.0.1"
babel-preset-env "^1.3.2" babel-preset-env "^1.3.2"
babelify "^7.3.0" babelify "^7.3.0"
json-rpc-error "^2.0.0" json-rpc-error "^2.0.0"
promise-to-callback "^1.0.0"
json-rpc-engine@^3.0.1, json-rpc-engine@^3.1.0, json-rpc-engine@^3.4.0, json-rpc-engine@^3.6.0: json-rpc-engine@^3.6.1:
version "3.6.1" version "3.6.1"
resolved "https://registry.yarnpkg.com/json-rpc-engine/-/json-rpc-engine-3.6.1.tgz#f53084726dc6dedeead0e2c457eeb997135f1e25" resolved "https://registry.yarnpkg.com/json-rpc-engine/-/json-rpc-engine-3.6.1.tgz#f53084726dc6dedeead0e2c457eeb997135f1e25"
dependencies: dependencies:
@ -7341,6 +7381,10 @@ miller-rabin@^4.0.0:
bn.js "^4.0.0" bn.js "^4.0.0"
brorand "^1.0.1" brorand "^1.0.1"
"mime-db@>= 1.30.0 < 2":
version "1.32.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.32.0.tgz#485b3848b01a3cda5f968b4882c0771e58e09414"
mime-db@~1.30.0: mime-db@~1.30.0:
version "1.30.0" version "1.30.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.30.0.tgz#74c643da2dd9d6a45399963465b26d5ca7d71f01" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.30.0.tgz#74c643da2dd9d6a45399963465b26d5ca7d71f01"
@ -8040,6 +8084,10 @@ on-finished@~2.3.0:
dependencies: dependencies:
ee-first "1.1.1" ee-first "1.1.1"
on-headers@~1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.1.tgz#928f5d0f470d49342651ea6794b0857c100693f7"
once@^1.3.0, once@^1.3.1, once@^1.3.2, once@^1.3.3, once@^1.4.0: once@^1.3.0, once@^1.3.1, once@^1.3.2, once@^1.3.3, once@^1.4.0:
version "1.4.0" version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
@ -8479,6 +8527,10 @@ polyfill-crypto.getrandomvalues@^1.0.0:
dependencies: dependencies:
mersenne-twister "^1.0.1" mersenne-twister "^1.0.1"
popper.js@^1.11.1:
version "1.13.0"
resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.13.0.tgz#e1e7ff65cc43f7cf9cf16f1510a75e81f84f4565"
portfinder@~0.2.1: portfinder@~0.2.1:
version "0.2.1" version "0.2.1"
resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-0.2.1.tgz#b2b9b0164f9e17fa3a9c7db2304d0a75140c71ad" resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-0.2.1.tgz#b2b9b0164f9e17fa3a9c7db2304d0a75140c71ad"
@ -9040,6 +9092,12 @@ react-testutils-additions@^15.2.0:
object-assign "3.0.0" object-assign "3.0.0"
sizzle "2.3.3" sizzle "2.3.3"
react-tippy@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/react-tippy/-/react-tippy-1.2.2.tgz#061467d34d29e7a5a9421822d125c451d6bb5153"
dependencies:
popper.js "^1.11.1"
react-toggle-button@^2.2.0: react-toggle-button@^2.2.0:
version "2.2.0" version "2.2.0"
resolved "https://registry.yarnpkg.com/react-toggle-button/-/react-toggle-button-2.2.0.tgz#a1b92143aa0df414642fcb141f0879f545bc5a89" resolved "https://registry.yarnpkg.com/react-toggle-button/-/react-toggle-button-2.2.0.tgz#a1b92143aa0df414642fcb141f0879f545bc5a89"
@ -11553,7 +11611,31 @@ weak@^1.0.0:
bindings "^1.2.1" bindings "^1.2.1"
nan "^2.0.5" nan "^2.0.5"
web3-provider-engine@^13.3.2, web3-provider-engine@^13.5.0: web3-provider-engine@^13.3.2:
version "13.4.0"
resolved "https://registry.yarnpkg.com/web3-provider-engine/-/web3-provider-engine-13.4.0.tgz#78c2794ba926d0c5b94c6e8955abb994bb8e8854"
dependencies:
async "^2.5.0"
clone "^2.0.0"
eth-block-tracker "^2.2.2"
eth-sig-util "^1.3.0"
ethereumjs-block "^1.2.2"
ethereumjs-tx "^1.2.0"
ethereumjs-util "^5.1.1"
ethereumjs-vm "^2.0.2"
fetch-ponyfill "^4.0.0"
json-rpc-error "^2.0.0"
json-stable-stringify "^1.0.1"
promise-to-callback "^1.0.0"
readable-stream "^2.2.9"
request "^2.67.0"
semaphore "^1.0.3"
solc "^0.4.2"
tape "^4.4.0"
xhr "^2.2.0"
xtend "^4.0.1"
web3-provider-engine@^13.5.6:
version "13.6.0" version "13.6.0"
resolved "https://registry.yarnpkg.com/web3-provider-engine/-/web3-provider-engine-13.6.0.tgz#836f51c4ee48bd7583acf3696033779c704c2214" resolved "https://registry.yarnpkg.com/web3-provider-engine/-/web3-provider-engine-13.6.0.tgz#836f51c4ee48bd7583acf3696033779c704c2214"
dependencies: dependencies:

Loading…
Cancel
Save