Merge pull request #1027 from MetaMask/Version-3.1.0

Version 3.1.0
feature/default_network_editable
Dan Finlay 8 years ago committed by GitHub
commit 25e5793992
  1. 5
      .eslintrc
  2. 8
      CHANGELOG.md
  3. 2
      app/manifest.json
  4. 6
      app/scripts/keyring-controller.js
  5. 2
      app/scripts/keyrings/hd.js
  6. 21
      app/scripts/keyrings/simple.js
  7. 2
      app/scripts/lib/config-manager.js
  8. 7
      app/scripts/metamask-controller.js
  9. 11
      app/scripts/transaction-manager.js
  10. 84
      development/states/account-list-with-imported.json
  11. 124
      development/states/compilation-bug.json
  12. 92
      development/states/import-private-key-warning.json
  13. 64
      development/states/import-private-key.json
  14. 66
      development/states/new-account.json
  15. 12
      notices/notice_0.md
  16. 1
      package.json
  17. 3
      test/integration/lib/first-time.js
  18. 18
      test/unit/keyrings/simple-test.js
  19. 91
      ui/app/accounts/import/index.js
  20. 27
      ui/app/accounts/import/json.js
  21. 69
      ui/app/accounts/import/private-key.js
  22. 30
      ui/app/accounts/import/seed.js
  23. 10
      ui/app/accounts/index.js
  24. 26
      ui/app/actions.js
  25. 11
      ui/app/app.js
  26. 82
      ui/app/components/buy-button-subview.js
  27. 35
      ui/app/components/tab-bar.js
  28. 15
      ui/app/conf-tx.js
  29. 8
      ui/app/css/lib.css
  30. 2
      ui/app/info.js
  31. 17
      ui/app/reducers/app.js
  32. 2
      ui/app/unlock.js
  33. 1
      ui/css.js

@ -1,5 +1,6 @@
{
"parserOptions": {
"sourceType": "module",
"ecmaVersion": 6,
"ecmaFeatures": {
"experimentalObjectRestSpread": true,
@ -44,7 +45,7 @@
"eol-last": 1,
"eqeqeq": [2, "allow-null"],
"generator-star-spacing": [2, { "before": true, "after": true }],
"handle-callback-err": [2, "^(err|error)$" ],
"handle-callback-err": [1, "^(err|error)$" ],
"indent": [2, 2, { "SwitchCase": 1 }],
"jsx-quotes": [2, "prefer-single"],
"key-spacing": [2, { "beforeColon": false, "afterColon": true }],
@ -145,6 +146,6 @@
"wrap-iife": [2, "any"],
"yield-star-spacing": [2, "both"],
"yoda": [2, "never"],
"prefer-const": 1
"prefer-const": 1,
}
}

@ -2,6 +2,14 @@
## Current Master
## 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

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

@ -234,7 +234,10 @@ module.exports = class KeyringController extends EventEmitter {
addNewKeyring (type, opts) {
const Keyring = this.getKeyringClassForType(type)
const keyring = new Keyring(opts)
return keyring.getAccounts()
return keyring.deserialize(opts)
.then(() => {
return keyring.getAccounts()
})
.then((accounts) => {
this.keyrings.push(keyring)
return this.setupAccounts(accounts)
@ -397,6 +400,7 @@ module.exports = class KeyringController extends EventEmitter {
}).then((rawSig) => {
cb(null, rawSig)
approvalCb(null, true)
messageManager.confirmMsg(msgId)
return rawSig
})
} catch (e) {

@ -76,7 +76,7 @@ class HdKeyring extends EventEmitter {
// For eth_sign, we need to sign transactions:
signMessage (withAccount, data) {
const wallet = this._getWalletForAccount(withAccount)
const message = ethUtil.removeHexPrefix(data)
const message = ethUtil.stripHexPrefix(data)
var privKey = wallet.getPrivateKey()
var msgSig = ethUtil.ecsign(new Buffer(message, 'hex'), privKey)
var rawMsgSig = ethUtil.bufferToHex(sigUtil.concatSig(msgSig.v, msgSig.r, msgSig.s))

@ -20,13 +20,19 @@ class SimpleKeyring extends EventEmitter {
}
deserialize (privateKeys = []) {
this.wallets = privateKeys.map((privateKey) => {
const stripped = ethUtil.stripHexPrefix(privateKey)
const buffer = new Buffer(stripped, 'hex')
const wallet = Wallet.fromPrivateKey(buffer)
return wallet
return new Promise((resolve, reject) => {
try {
this.wallets = privateKeys.map((privateKey) => {
const stripped = ethUtil.stripHexPrefix(privateKey)
const buffer = new Buffer(stripped, 'hex')
const wallet = Wallet.fromPrivateKey(buffer)
return wallet
})
} catch (e) {
reject(e)
}
resolve()
})
return Promise.resolve()
}
addAccounts (n = 1) {
@ -54,8 +60,7 @@ class SimpleKeyring extends EventEmitter {
// For eth_sign, we need to sign transactions:
signMessage (withAccount, data) {
const wallet = this._getWalletForAccount(withAccount)
const message = ethUtil.removeHexPrefix(data)
const message = ethUtil.stripHexPrefix(data)
var privKey = wallet.getPrivateKey()
var msgSig = ethUtil.ecsign(new Buffer(message, 'hex'), privKey)
var rawMsgSig = ethUtil.bufferToHex(sigUtil.concatSig(msgSig.v, msgSig.r, msgSig.s))

@ -306,7 +306,7 @@ ConfigManager.prototype.updateConversionRate = function () {
this.setConversionPrice(parsedResponse.ticker.price)
this.setConversionDate(parsedResponse.timestamp)
}).catch((err) => {
console.error('Error in conversion.', err)
console.warn('MetaMask - Failed to query currency conversion.')
this.setConversionPrice(0)
this.setConversionDate('N/A')
})

@ -115,7 +115,12 @@ module.exports = class MetamaskController extends EventEmitter {
.then((newState) => { cb(null, newState) })
.catch((reason) => { cb(reason) })
},
addNewKeyring: nodeify(keyringController.addNewKeyring).bind(keyringController),
addNewKeyring: (type, opts, cb) => {
keyringController.addNewKeyring(type, opts)
.then(() => keyringController.fullUpdate())
.then((newState) => { cb(null, newState) })
.catch((reason) => { cb(reason) })
},
addNewAccount: nodeify(keyringController.addNewAccount).bind(keyringController),
setSelectedAccount: nodeify(keyringController.setSelectedAccount).bind(keyringController),
saveAccountLabel: nodeify(keyringController.saveAccountLabel).bind(keyringController),

@ -190,7 +190,7 @@ module.exports = class TransactionManager extends EventEmitter {
let fromAddress = txParams.from
let ethTx = this.txProviderUtils.buildEthTxFromParams(txParams, txMeta.gasMultiplier)
this.signEthTx(ethTx, fromAddress).then(() => {
this.updateTxAsSigned(txMeta.id, ethTx)
this.setTxStatusSigned(txMeta.id)
cb(null, ethUtil.bufferToHex(ethTx.serialize()))
}).catch((err) => {
cb(err)
@ -198,21 +198,20 @@ module.exports = class TransactionManager extends EventEmitter {
}
publishTransaction (txId, rawTx, cb) {
this.txProviderUtils.publishTransaction(rawTx, (err) => {
this.txProviderUtils.publishTransaction(rawTx, (err, txHash) => {
if (err) return cb(err)
this.setTxHash(txId, txHash)
this.setTxStatusSubmitted(txId)
cb()
})
}
// receives a signed tx object and updates the tx hash
updateTxAsSigned (txId, ethTx) {
// receives a txHash records the tx as signed
setTxHash (txId, txHash) {
// Add the tx hash to the persisted meta-tx object
let txHash = ethUtil.bufferToHex(ethTx.hash())
let txMeta = this.getTx(txId)
txMeta.hash = txHash
this.updateTx(txMeta)
this.setTxStatusSigned(txMeta.id)
}
/*

@ -0,0 +1,84 @@
{
"metamask": {
"isInitialized": true,
"isUnlocked": true,
"rpcTarget": "https://rawtestrpc.metamask.io/",
"identities": {
"0x58bda1f9d87dc7d2bcc6f7c2513efc9d03fca683": {
"address": "0x58bda1f9d87dc7d2bcc6f7c2513efc9d03fca683",
"name": "Account 1"
},
"0x9858e7d8b79fc3e6d989636721584498926da38a": {
"address": "0x9858e7d8b79fc3e6d989636721584498926da38a",
"name": "Imported Account"
}
},
"unconfTxs": {},
"currentFiat": "USD",
"conversionRate": 10.19458075,
"conversionDate": 1484696373,
"noActiveNotices": true,
"network": "3",
"accounts": {
"0x58bda1f9d87dc7d2bcc6f7c2513efc9d03fca683": {
"code": "0x",
"balance": "0x0",
"nonce": "0x0",
"address": "0x58bda1f9d87dc7d2bcc6f7c2513efc9d03fca683"
},
"0x9858e7d8b79fc3e6d989636721584498926da38a": {
"code": "0x",
"balance": "0x0",
"nonce": "0x0",
"address": "0x9858e7d8b79fc3e6d989636721584498926da38a"
}
},
"transactions": [],
"provider": {
"type": "testnet"
},
"selectedAccount": "0x9858e7d8b79fc3e6d989636721584498926da38a",
"selectedAccountTxList": [],
"isDisclaimerConfirmed": true,
"unconfMsgs": {},
"messages": [],
"shapeShiftTxList": [],
"keyringTypes": [
"Simple Key Pair",
"HD Key Tree"
],
"keyrings": [
{
"type": "HD Key Tree",
"accounts": [
"58bda1f9d87dc7d2bcc6f7c2513efc9d03fca683"
]
},
{
"type": "Simple Key Pair",
"accounts": [
"0x9858e7d8b79fc3e6d989636721584498926da38a"
]
}
],
"lostAccounts": [],
"seedWords": null
},
"appState": {
"menuOpen": false,
"currentView": {
"name": "accounts"
},
"accountDetail": {
"subview": "transactions",
"accountExport": "none",
"privateKey": ""
},
"transForward": true,
"isLoading": false,
"warning": null,
"scrollToBottom": false,
"forgottenPassword": false
},
"identities": {}
}

@ -0,0 +1,124 @@
{
"metamask": {
"isInitialized": true,
"isUnlocked": true,
"rpcTarget": "https://rawtestrpc.metamask.io/",
"identities": {
"0xac39b311dceb2a4b2f5d8461c1cdaf756f4f7ae9": {
"address": "0xac39b311dceb2a4b2f5d8461c1cdaf756f4f7ae9",
"name": "Account 1"
},
"0xd7c0cd9e7d2701c710d64fc492c7086679bdf7b4": {
"address": "0xd7c0cd9e7d2701c710d64fc492c7086679bdf7b4",
"name": "Account 2"
},
"0x1acfb961c5a8268eac8e09d6241a26cbeff42241": {
"address": "0x1acfb961c5a8268eac8e09d6241a26cbeff42241",
"name": "Account 3"
},
"0xabc2bca51709b8615147352c62420f547a63a00c": {
"address": "0xabc2bca51709b8615147352c62420f547a63a00c",
"name": "Account 4"
}
},
"unconfTxs": {
"7992944905869041": {
"id": 7992944905869041,
"txParams": {
"from": "0xac39b311dceb2a4b2f5d8461c1cdaf756f4f7ae9",
"value": "0x0",
"data": "0x606060405234610000575b60da806100186000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630dbe671f14603c575b6000565b3460005760466088565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16815600a165627a7a72305820a99dfa6091771f518dd1ae8d1ee347bae3304dffd98fd24b1b99a8380bc60a750029",
"gas": "0x1af75",
"metamaskId": 7992944905869041,
"metamaskNetworkId": "3"
},
"time": 1482279685589,
"status": "unconfirmed",
"gasMultiplier": 1,
"metamaskNetworkId": "3",
"gasLimitSpecified": true,
"estimatedGas": "0x1af75",
"simulationFails": true
}
},
"currentFiat": "USD",
"conversionRate": 7.69158136,
"conversionDate": 1482279663,
"noActiveNotices": true,
"network": "3",
"accounts": {
"0xac39b311dceb2a4b2f5d8461c1cdaf756f4f7ae9": {
"code": "0x",
"nonce": "0x3",
"balance": "0x11f646fe14c9c000",
"address": "0xac39b311dceb2a4b2f5d8461c1cdaf756f4f7ae9"
},
"0xd7c0cd9e7d2701c710d64fc492c7086679bdf7b4": {
"code": "0x",
"nonce": "0x0",
"balance": "0x0",
"address": "0xd7c0cd9e7d2701c710d64fc492c7086679bdf7b4"
},
"0x1acfb961c5a8268eac8e09d6241a26cbeff42241": {
"code": "0x",
"balance": "0x0",
"nonce": "0x0",
"address": "0x1acfb961c5a8268eac8e09d6241a26cbeff42241"
},
"0xabc2bca51709b8615147352c62420f547a63a00c": {
"code": "0x",
"balance": "0x0",
"nonce": "0x0",
"address": "0xabc2bca51709b8615147352c62420f547a63a00c"
}
},
"transactions": [
{
"id": 7992944905869041,
"txParams": {
"from": "0xac39b311dceb2a4b2f5d8461c1cdaf756f4f7ae9",
"value": "0x0",
"data": "0x606060405234610000575b60da806100186000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630dbe671f14603c575b6000565b3460005760466088565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16815600a165627a7a72305820a99dfa6091771f518dd1ae8d1ee347bae3304dffd98fd24b1b99a8380bc60a750029",
"gas": "0x1af75",
"metamaskId": 7992944905869041,
"metamaskNetworkId": "3"
},
"time": 1482279685589,
"status": "unconfirmed",
"gasMultiplier": 1,
"metamaskNetworkId": "3",
"gasLimitSpecified": true,
"estimatedGas": "0x1af75",
"simulationFails": true
}
],
"provider": {
"type": "testnet"
},
"selectedAccount": "0xac39b311dceb2a4b2f5d8461c1cdaf756f4f7ae9",
"seedWords": false,
"isDisclaimerConfirmed": true,
"unconfMsgs": {},
"messages": [],
"shapeShiftTxList": [],
"keyringTypes": [
"Simple Key Pair",
"HD Key Tree"
],
"lostAccounts": []
},
"appState": {
"menuOpen": false,
"currentView": {
"name": "confTx",
"context": 0
},
"accountDetail": {
"subview": "transactions"
},
"transForward": true,
"isLoading": false,
"warning": null
},
"identities": {}
}

@ -0,0 +1,92 @@
{
"metamask": {
"isInitialized": true,
"isUnlocked": true,
"rpcTarget": "https://rawtestrpc.metamask.io/",
"identities": {
"0x01208723ba84e15da2e71656544a2963b0c06d40": {
"address": "0x01208723ba84e15da2e71656544a2963b0c06d40",
"name": "Account 1"
}
},
"unconfTxs": {},
"currentFiat": "USD",
"conversionRate": 10.1219126,
"conversionDate": 1484695442,
"noActiveNotices": true,
"network": "3",
"accounts": {
"0x01208723ba84e15da2e71656544a2963b0c06d40": {
"nonce": "0x0",
"balance": "0x0",
"code": "0x",
"address": "0x01208723ba84e15da2e71656544a2963b0c06d40"
}
},
"transactions": [],
"provider": {
"type": "testnet"
},
"selectedAccount": "0x01208723ba84e15da2e71656544a2963b0c06d40",
"selectedAccountTxList": [],
"seedWords": false,
"isDisclaimerConfirmed": true,
"unconfMsgs": {},
"messages": [],
"shapeShiftTxList": [],
"keyringTypes": [
"Simple Key Pair",
"HD Key Tree"
],
"keyrings": [
{
"type": "Simple Key Pair",
"accounts": []
},
{
"type": "Simple Key Pair",
"accounts": []
},
{
"type": "Simple Key Pair",
"accounts": []
},
{
"type": "Simple Key Pair",
"accounts": []
},
{
"type": "Simple Key Pair",
"accounts": []
},
{
"type": "Simple Key Pair",
"accounts": []
},
{
"type": "Simple Key Pair",
"accounts": []
},
{
"type": "HD Key Tree",
"accounts": [
"01208723ba84e15da2e71656544a2963b0c06d40"
]
}
],
"lostAccounts": []
},
"appState": {
"menuOpen": false,
"currentView": {
"name": "import-menu"
},
"accountDetail": {
"subview": "transactions"
},
"transForward": true,
"isLoading": false,
"warning": "Invalid hex string"
},
"identities": {}
}

@ -0,0 +1,64 @@
{
"metamask": {
"isInitialized": true,
"isUnlocked": true,
"rpcTarget": "https://rawtestrpc.metamask.io/",
"identities": {
"0x01208723ba84e15da2e71656544a2963b0c06d40": {
"address": "0x01208723ba84e15da2e71656544a2963b0c06d40",
"name": "Account 1"
}
},
"unconfTxs": {},
"currentFiat": "USD",
"conversionRate": 10.10788584,
"conversionDate": 1484694362,
"noActiveNotices": true,
"network": "3",
"accounts": {
"0x01208723ba84e15da2e71656544a2963b0c06d40": {
"balance": "0x0",
"code": "0x",
"nonce": "0x0",
"address": "0x01208723ba84e15da2e71656544a2963b0c06d40"
}
},
"transactions": [],
"provider": {
"type": "testnet"
},
"selectedAccount": "0x01208723ba84e15da2e71656544a2963b0c06d40",
"selectedAccountTxList": [],
"seedWords": null,
"isDisclaimerConfirmed": true,
"unconfMsgs": {},
"messages": [],
"shapeShiftTxList": [],
"keyringTypes": [
"Simple Key Pair",
"HD Key Tree"
],
"keyrings": [
{
"type": "HD Key Tree",
"accounts": [
"01208723ba84e15da2e71656544a2963b0c06d40"
]
}
],
"lostAccounts": []
},
"appState": {
"menuOpen": false,
"currentView": {
"name": "import-menu"
},
"accountDetail": {
"subview": "transactions"
},
"transForward": true,
"isLoading": false,
"warning": null
},
"identities": {}
}

@ -0,0 +1,66 @@
{
"metamask": {
"isInitialized": true,
"isUnlocked": true,
"rpcTarget": "https://rawtestrpc.metamask.io/",
"identities": {
"0xa6ef573d60594731178b7f85d80da13cc2af52dd": {
"address": "0xa6ef573d60594731178b7f85d80da13cc2af52dd",
"name": "Dan! 1"
},
"0xf9f52e84ad2c9122caa87478d27041ddaa215666": {
"address": "0xf9f52e84ad2c9122caa87478d27041ddaa215666",
"name": "Account 2"
}
},
"unconfTxs": {},
"currentFiat": "USD",
"conversionRate": 10.92067835,
"conversionDate": 1478282884,
"network": null,
"accounts": {
"0xa6ef573d60594731178b7f85d80da13cc2af52dd": {
"balance": "0x00",
"nonce": "0x100000",
"code": "0x",
"address": "0xa6ef573d60594731178b7f85d80da13cc2af52dd"
},
"0xf9f52e84ad2c9122caa87478d27041ddaa215666": {
"balance": "0x00",
"nonce": "0x100000",
"code": "0x",
"address": "0xf9f52e84ad2c9122caa87478d27041ddaa215666"
}
},
"transactions": [],
"provider": {
"type": "testnet"
},
"selectedAccount": "0xa6ef573d60594731178b7f85d80da13cc2af52dd",
"isConfirmed": true,
"unconfMsgs": {},
"messages": [],
"selectedAddress": "0xa6ef573d60594731178b7f85d80da13cc2af52dd",
"shapeShiftTxList": [],
"keyringTypes": [
"Simple Key Pair",
"HD Key Tree"
]
},
"appState": {
"menuOpen": false,
"currentView": {
"name": "new-account"
},
"accountDetail": {
"subview": "transactions"
},
"transForward": true,
"isLoading": false,
"warning": null,
"forgottenPassword": null,
"detailView": {},
"scrollToBottom": false
},
"identities": {}
}

@ -1,12 +0,0 @@
Due to [recent events](https://blog.ethereum.org/2016/11/20/from-morden-to-ropsten/), MetaMask is now deprecating support for the Morden Test Network.
Users will still be able to access Morden through a locally hosted node, but we will no longer be providing hosted access to this network through [Infura](http://infura.io/).
Please use the new Ropsten Network as your new default test network.
You can fund your Ropsten account using the buy button on your account page.
Best wishes!
The MetaMask Team

@ -83,6 +83,7 @@
"react-hyperscript": "^2.2.2",
"react-markdown": "^2.3.0",
"react-redux": "^4.4.5",
"react-select": "^1.0.0-rc.2",
"react-tooltip-component": "^0.3.0",
"readable-stream": "^2.1.2",
"redux": "^3.0.5",

@ -66,7 +66,8 @@ QUnit.test('agree to terms', function (assert) {
}).then(function() {
var sandwich = app.find('.menu-droppo')[0]
var lock = sandwich.children[2]
var children = sandwich.children
var lock = children[children.length - 2]
assert.ok(lock, 'Lock menu item found')
lock.click()

@ -49,6 +49,24 @@ describe('simple-keyring', function() {
})
})
describe('#signMessage', function() {
const address = '0x9858e7d8b79fc3e6d989636721584498926da38a'
const message = '0x879a053d4800c6354e76c7985a865d2922c82fb5b3f4577b2fe08b998954f2e0'
const privateKey = '0x7dd98753d7b4394095de7d176c58128e2ed6ee600abe97c9f6d9fd65015d9b18'
const expectedResult = '0x28fcb6768e5110144a55b2e6ce9d1ea5a58103033632d272d2b5cf506906f7941a00b539383fd872109633d8c71c404e13dba87bc84166ee31b0e36061a69e161c'
it('passes the dennis test', function(done) {
keyring.deserialize([ privateKey ])
.then(() => {
return keyring.signMessage(address, message)
})
.then((result) => {
assert.equal(result, expectedResult)
done()
})
})
})
describe('#addAccounts', function() {
describe('with no arguments', function() {
it('creates a single wallet', function() {

@ -0,0 +1,91 @@
const inherits = require('util').inherits
const Component = require('react').Component
const h = require('react-hyperscript')
const connect = require('react-redux').connect
import Select from 'react-select'
// Subviews
const JsonImportView = require('./json.js')
const SeedImportView = require('./seed.js')
const PrivateKeyImportView = require('./private-key.js')
const menuItems = [
'Private Key',
]
module.exports = connect(mapStateToProps)(AccountImportSubview)
function mapStateToProps (state) {
return {
menuItems,
}
}
inherits(AccountImportSubview, Component)
function AccountImportSubview () {
Component.call(this)
}
AccountImportSubview.prototype.render = function () {
const props = this.props
const state = this.state || {}
const { menuItems } = props
const { type } = state
return (
h('div', {
style: {
},
}, [
h('div', {
style: {
padding: '10px',
color: 'rgb(174, 174, 174)',
},
}, [
h('h3', { style: { padding: '3px' } }, 'SELECT TYPE'),
h('style', `
.has-value.Select--single > .Select-control .Select-value .Select-value-label, .Select-value-label {
color: rgb(174,174,174);
}
`),
h(Select, {
name: 'import-type-select',
clearable: false,
value: type || menuItems[0],
options: menuItems.map((type) => {
return {
value: type,
label: type,
}
}),
onChange: (opt) => {
this.setState({ type: opt.value })
},
}),
]),
this.renderImportView(),
])
)
}
AccountImportSubview.prototype.renderImportView = function() {
const props = this.props
const state = this.state || {}
const { type } = state
const { menuItems } = props
const current = type || menuItems[0]
switch (current) {
case 'HD Key Tree':
return h(SeedImportView)
case 'Private Key':
return h(PrivateKeyImportView)
default:
return h(JsonImportView)
}
}

@ -0,0 +1,27 @@
const inherits = require('util').inherits
const Component = require('react').Component
const h = require('react-hyperscript')
const connect = require('react-redux').connect
module.exports = connect(mapStateToProps)(JsonImportSubview)
function mapStateToProps (state) {
return {}
}
inherits(JsonImportSubview, Component)
function JsonImportSubview () {
Component.call(this)
}
JsonImportSubview.prototype.render = function () {
return (
h('div', {
style: {
},
}, [
`Upload your json file here!`,
])
)
}

@ -0,0 +1,69 @@
const inherits = require('util').inherits
const Component = require('react').Component
const h = require('react-hyperscript')
const connect = require('react-redux').connect
const type = 'Simple Key Pair'
const actions = require('../../actions')
module.exports = connect(mapStateToProps)(PrivateKeyImportView)
function mapStateToProps (state) {
return {
error: state.appState.warning,
}
}
inherits(PrivateKeyImportView, Component)
function PrivateKeyImportView () {
Component.call(this)
}
PrivateKeyImportView.prototype.render = function () {
const { error } = this.props
return (
h('div', {
style: {
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
padding: '5px 15px 0px 15px',
},
}, [
h('span', 'Paste your private key string here'),
h('input.large-input.letter-spacey', {
type: 'password',
id: 'private-key-box',
onKeyPress: this.createKeyringOnEnter.bind(this),
style: {
width: 260,
marginTop: 12,
},
}),
h('button.primary', {
onClick: this.createNewKeychain.bind(this),
style: {
margin: 12,
},
}, 'Import'),
error ? h('span.warning', error) : null,
])
)
}
PrivateKeyImportView.prototype.createKeyringOnEnter = function (event) {
if (event.key === 'Enter') {
event.preventDefault()
this.createNewKeychain()
}
}
PrivateKeyImportView.prototype.createNewKeychain = function () {
const input = document.getElementById('private-key-box')
const privateKey = input.value
this.props.dispatch(actions.addNewKeyring(type, [ privateKey ]))
}

@ -0,0 +1,30 @@
const inherits = require('util').inherits
const Component = require('react').Component
const h = require('react-hyperscript')
const connect = require('react-redux').connect
module.exports = connect(mapStateToProps)(SeedImportSubview)
function mapStateToProps (state) {
return {}
}
inherits(SeedImportSubview, Component)
function SeedImportSubview () {
Component.call(this)
}
SeedImportSubview.prototype.render = function () {
return (
h('div', {
style: {
},
}, [
`Paste your seed phrase here!`,
h('textarea'),
h('br'),
h('button', 'Submit'),
])
)
}

@ -73,7 +73,8 @@ AccountsScreen.prototype.render = function () {
const simpleAddress = identity.address.substring(2).toLowerCase()
const keyring = keyrings.find((kr) => {
return kr.accounts.includes(simpleAddress)
return kr.accounts.includes(simpleAddress) ||
kr.accounts.includes(identity.address)
})
return h(AccountListItem, {
@ -154,6 +155,13 @@ AccountsScreen.prototype.addNewAccount = function () {
this.props.dispatch(actions.addNewAccount(0))
}
/* An optional view proposed in this design:
* https://consensys.quip.com/zZVrAysM5znY
AccountsScreen.prototype.addNewAccount = function () {
this.props.dispatch(actions.navigateToNewAccountScreen())
}
*/
AccountsScreen.prototype.goHome = function () {
this.props.dispatch(actions.goHome())
}

@ -32,16 +32,20 @@ var actions = {
SHOW_INIT_MENU: 'SHOW_INIT_MENU',
SHOW_NEW_VAULT_SEED: 'SHOW_NEW_VAULT_SEED',
SHOW_INFO_PAGE: 'SHOW_INFO_PAGE',
SHOW_IMPORT_PAGE: 'SHOW_IMPORT_PAGE',
unlockMetamask: unlockMetamask,
unlockFailed: unlockFailed,
showCreateVault: showCreateVault,
showRestoreVault: showRestoreVault,
showInitializeMenu: showInitializeMenu,
showImportPage,
createNewVaultAndKeychain: createNewVaultAndKeychain,
createNewVaultAndRestore: createNewVaultAndRestore,
createNewVaultInProgress: createNewVaultInProgress,
addNewKeyring,
addNewAccount,
NEW_ACCOUNT_SCREEN: 'NEW_ACCOUNT_SCREEN',
navigateToNewAccountScreen,
showNewVaultSeed: showNewVaultSeed,
showInfoPage: showInfoPage,
// seed recovery actions
@ -249,7 +253,21 @@ function requestRevealSeed (password) {
}
function addNewKeyring (type, opts) {
return callBackgroundThenUpdate(background.addNewKeyring, type, opts)
return (dispatch) => {
dispatch(actions.showLoadingIndication())
background.addNewKeyring(type, opts, (err, newState) => {
dispatch(actions.hideLoadingIndication())
if (err) return dispatch(actions.displayWarning(err.message))
dispatch(actions.updateMetamaskState(newState))
dispatch(actions.showAccountsPage())
})
}
}
function navigateToNewAccountScreen() {
return {
type: this.NEW_ACCOUNT_SCREEN,
}
}
function addNewAccount (ringNumber = 0) {
@ -376,6 +394,12 @@ function showInitializeMenu () {
}
}
function showImportPage () {
return {
type: actions.SHOW_IMPORT_PAGE,
}
}
function agreeToDisclaimer () {
return (dispatch) => {
dispatch(this.showLoadingIndication())

@ -20,6 +20,7 @@ const NoticeScreen = require('./components/notice')
const generateLostAccountsNotice = require('../lib/lost-accounts-notice')
// other views
const ConfigScreen = require('./config')
const Import = require('./accounts/import')
const InfoScreen = require('./info')
const LoadingIndicator = require('./components/loading')
const SandwichExpando = require('sandwich-expando')
@ -304,6 +305,13 @@ App.prototype.renderDropdown = function () {
icon: h('i.fa.fa-gear.fa-lg'),
}),
h(DropMenuItem, {
label: 'Import Account',
closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }),
action: () => this.props.dispatch(actions.showImportPage()),
icon: h('i.fa.fa-arrow-circle-o-up.fa-lg'),
}),
h(DropMenuItem, {
label: 'Lock',
closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }),
@ -411,6 +419,9 @@ App.prototype.renderPrimary = function () {
case 'config':
return h(ConfigScreen, {key: 'config'})
case 'import-menu':
return h(Import, {key: 'import-menu'})
case 'reveal-seed-conf':
return h(RevealSeedConfirmation, {key: 'reveal-seed-conf'})

@ -7,6 +7,7 @@ const CoinbaseForm = require('./coinbase-form')
const ShapeshiftForm = require('./shapeshift-form')
const extension = require('../../../app/scripts/lib/extension')
const Loading = require('./loading')
const TabBar = require('./tab-bar')
module.exports = connect(mapStateToProps)(BuyButtonSubview)
@ -29,7 +30,6 @@ function BuyButtonSubview () {
BuyButtonSubview.prototype.render = function () {
const props = this.props
const currentForm = props.buyView.formView
const isLoading = props.isSubLoading
return (
@ -53,43 +53,53 @@ BuyButtonSubview.prototype.render = function () {
h(Loading, { isLoading }),
h('h3.flex-row.text-transform-uppercase', {
style: {
background: '#EBEBEB',
color: '#AEAEAE',
paddingTop: '4px',
justifyContent: 'space-around',
h(TabBar, {
tabs: [
{
content: [
'Coinbase',
h('a', {
onClick: (event) => this.navigateTo('https://github.com/MetaMask/faq/blob/master/COINBASE.md'),
}, [
h('i.fa.fa-question-circle', {
style: {
margin: '0px 5px',
},
}),
]),
],
key: 'coinbase',
},
{
content: [
'Shapeshift',
h('a', {
href: 'https://github.com/MetaMask/faq/blob/master/COINBASE.md',
onClick: (event) => this.navigateTo('https://info.shapeshift.io/about'),
}, [
h('i.fa.fa-question-circle', {
style: {
margin: '0px 5px',
},
}),
]),
],
key: 'shapeshift',
},
],
defaultTab: 'coinbase',
tabSelected: (key) => {
switch (key) {
case 'coinbase':
props.dispatch(actions.coinBaseSubview())
break
case 'shapeshift':
props.dispatch(actions.shapeShiftSubview(props.provider.type))
break
}
},
}, [
h(currentForm.coinbase ? '.activeForm' : '.inactiveForm.pointer', {
onClick: () => props.dispatch(actions.coinBaseSubview()),
}, 'Coinbase'),
h('a', {
onClick: (event) => this.navigateTo('https://github.com/MetaMask/faq/blob/master/COINBASE.md'),
}, [
h('i.fa.fa-question-circle', {
style: {
position: 'relative',
right: '33px',
},
}),
]),
h(currentForm.shapeshift ? '.activeForm' : '.inactiveForm.pointer', {
onClick: () => props.dispatch(actions.shapeShiftSubview(props.provider.type)),
}, 'Shapeshift'),
}),
h('a', {
href: 'https://github.com/MetaMask/faq/blob/master/COINBASE.md',
onClick: (event) => this.navigateTo('https://info.shapeshift.io/about'),
}, [
h('i.fa.fa-question-circle', {
style: {
position: 'relative',
right: '28px',
},
}),
]),
]),
this.formVersionSubview(),
])
)

@ -0,0 +1,35 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
module.exports = TabBar
inherits(TabBar, Component)
function TabBar () {
Component.call(this)
}
TabBar.prototype.render = function () {
const props = this.props
const state = this.state || {}
const { tabs = [], defaultTab, tabSelected } = props
const { subview = defaultTab } = state
return (
h('.flex-row.space-around.text-transform-uppercase', {
style: {
background: '#EBEBEB',
color: '#AEAEAE',
paddingTop: '4px',
},
}, tabs.map((tab) => {
const { key, content } = tab
return h(subview === key ? '.activeForm' : '.inactiveForm.pointer', {
onClick: () => {
this.setState({ subview: key })
tabSelected(key)
},
}, content)
}))
)
}

@ -41,12 +41,13 @@ ConfirmTxScreen.prototype.render = function () {
var provider = state.provider
var unconfTxs = state.unconfTxs
var unconfMsgs = state.unconfMsgs
var unconfTxList = txHelper(unconfTxs, unconfMsgs, network)
var index = state.index !== undefined ? state.index : 0
var index = state.index !== undefined && unconfTxList[index] ? state.index : 0
var txData = unconfTxList[index] || {}
var txParams = txData.txParams
var txParams = txData.params || {}
var isNotification = isPopupOrNotification() === 'notification'
if (!txParams) return null
if (unconfTxList.length === 0) return null
return (
@ -116,15 +117,19 @@ ConfirmTxScreen.prototype.render = function () {
}
function currentTxView (opts) {
if ('txParams' in opts.txData) {
const { txData } = opts
const { txParams, msgParams } = txData
if (txParams) {
// This is a pending transaction
return h(PendingTx, opts)
} else if ('msgParams' in opts.txData) {
} else if (msgParams) {
// This is a pending message to sign
return h(PendingMsg, opts)
}
}
ConfirmTxScreen.prototype.checkBalanceAgainstTx = function (txData) {
if (!txData.txParams) return false
var state = this.props
var address = txData.txParams.from || state.selectedAccount
var account = state.accounts[address]

@ -23,6 +23,14 @@
flex-direction: column;
}
.space-between {
justify-content: space-between;
}
.space-around {
justify-content: space-around;
}
.flex-column-bottom {
display: flex;
flex-direction: column-reverse;

@ -110,7 +110,7 @@ InfoScreen.prototype.render = function () {
onClick (event) { this.navigateTo(event.target.href) },
}, [
h('img.icon-size', {
src: manifest.icons[128],
src: manifest.icons['128'],
style: {
filter: 'grayscale(100%)', /* IE6-9 */
WebkitFilter: 'grayscale(100%)', /* Microsoft Edge and Firefox 35+ */

@ -99,6 +99,14 @@ function reduceApp (state, action) {
transForward: action.value,
})
case actions.SHOW_IMPORT_PAGE:
return extend(appState, {
currentView: {
name: 'import-menu',
},
transForward: true,
})
case actions.SHOW_INFO_PAGE:
return extend(appState, {
currentView: {
@ -128,6 +136,15 @@ function reduceApp (state, action) {
isLoading: false,
})
case actions.NEW_ACCOUNT_SCREEN:
return extend(appState, {
currentView: {
name: 'new-account',
context: appState.currentView.context,
},
transForward: true,
})
case actions.SHOW_SEND_PAGE:
return extend(appState, {
currentView: {

@ -26,7 +26,7 @@ UnlockScreen.prototype.render = function () {
const state = this.props
const warning = state.warning
return (
h('.flex-column.hey-im-here', [
h('.flex-column', [
h('.unlock-screen.flex-column.flex-center.flex-grow', [
h(Mascot, {

@ -10,6 +10,7 @@ var cssFiles = {
'index.css': fs.readFileSync(path.join(__dirname, '/app/css/index.css'), 'utf8'),
'transitions.css': fs.readFileSync(path.join(__dirname, '/app/css/transitions.css'), 'utf8'),
'react-tooltip-component.css': fs.readFileSync(path.join(__dirname, '..', 'node_modules', 'react-tooltip-component', 'dist', 'react-tooltip-component.css'), 'utf8'),
'react-css': fs.readFileSync(path.join(__dirname, '..', 'node_modules', 'react-select', 'dist', 'react-select.css'), 'utf8'),
}
function bundleCss () {

Loading…
Cancel
Save