aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.eslintrc5
-rw-r--r--CHANGELOG.md8
-rw-r--r--app/manifest.json2
-rw-r--r--app/scripts/keyring-controller.js6
-rw-r--r--app/scripts/keyrings/hd.js2
-rw-r--r--app/scripts/keyrings/simple.js21
-rw-r--r--app/scripts/lib/config-manager.js2
-rw-r--r--app/scripts/metamask-controller.js7
-rw-r--r--app/scripts/transaction-manager.js11
-rw-r--r--development/states/account-list-with-imported.json84
-rw-r--r--development/states/compilation-bug.json124
-rw-r--r--development/states/import-private-key-warning.json92
-rw-r--r--development/states/import-private-key.json64
-rw-r--r--development/states/new-account.json66
-rw-r--r--notices/notice_0.md12
-rw-r--r--package.json1
-rw-r--r--test/integration/lib/first-time.js3
-rw-r--r--test/unit/keyrings/simple-test.js18
-rw-r--r--ui/app/accounts/import/index.js91
-rw-r--r--ui/app/accounts/import/json.js27
-rw-r--r--ui/app/accounts/import/private-key.js69
-rw-r--r--ui/app/accounts/import/seed.js30
-rw-r--r--ui/app/accounts/index.js10
-rw-r--r--ui/app/actions.js26
-rw-r--r--ui/app/app.js11
-rw-r--r--ui/app/components/buy-button-subview.js82
-rw-r--r--ui/app/components/tab-bar.js35
-rw-r--r--ui/app/conf-tx.js15
-rw-r--r--ui/app/css/lib.css8
-rw-r--r--ui/app/info.js2
-rw-r--r--ui/app/reducers/app.js17
-rw-r--r--ui/app/unlock.js2
-rw-r--r--ui/css.js1
33 files changed, 875 insertions, 79 deletions
diff --git a/.eslintrc b/.eslintrc
index 72b3d3e6d..84f65bea4 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -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,
}
}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c6e8259d0..51bea159c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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
diff --git a/app/manifest.json b/app/manifest.json
index 9c6558d15..2f1ae9b25 100644
--- a/app/manifest.json
+++ b/app/manifest.json
@@ -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",
diff --git a/app/scripts/keyring-controller.js b/app/scripts/keyring-controller.js
index 79cfe6fbd..e609403cc 100644
--- a/app/scripts/keyring-controller.js
+++ b/app/scripts/keyring-controller.js
@@ -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) {
diff --git a/app/scripts/keyrings/hd.js b/app/scripts/keyrings/hd.js
index 80b713b58..1b9796e07 100644
--- a/app/scripts/keyrings/hd.js
+++ b/app/scripts/keyrings/hd.js
@@ -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))
diff --git a/app/scripts/keyrings/simple.js b/app/scripts/keyrings/simple.js
index 6b16137ae..46687fcaf 100644
--- a/app/scripts/keyrings/simple.js
+++ b/app/scripts/keyrings/simple.js
@@ -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))
diff --git a/app/scripts/lib/config-manager.js b/app/scripts/lib/config-manager.js
index 3a1f12ac0..e927c78ec 100644
--- a/app/scripts/lib/config-manager.js
+++ b/app/scripts/lib/config-manager.js
@@ -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')
})
diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js
index b94b98eac..629216e42 100644
--- a/app/scripts/metamask-controller.js
+++ b/app/scripts/metamask-controller.js
@@ -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),
diff --git a/app/scripts/transaction-manager.js b/app/scripts/transaction-manager.js
index cc9082394..6d0121afd 100644
--- a/app/scripts/transaction-manager.js
+++ b/app/scripts/transaction-manager.js
@@ -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)
}
/*
diff --git a/development/states/account-list-with-imported.json b/development/states/account-list-with-imported.json
new file mode 100644
index 000000000..e32327743
--- /dev/null
+++ b/development/states/account-list-with-imported.json
@@ -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": {}
+} \ No newline at end of file
diff --git a/development/states/compilation-bug.json b/development/states/compilation-bug.json
new file mode 100644
index 000000000..a9dfc4d4e
--- /dev/null
+++ b/development/states/compilation-bug.json
@@ -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": {}
+} \ No newline at end of file
diff --git a/development/states/import-private-key-warning.json b/development/states/import-private-key-warning.json
new file mode 100644
index 000000000..f4ac99b05
--- /dev/null
+++ b/development/states/import-private-key-warning.json
@@ -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": {}
+} \ No newline at end of file
diff --git a/development/states/import-private-key.json b/development/states/import-private-key.json
new file mode 100644
index 000000000..c70f02a36
--- /dev/null
+++ b/development/states/import-private-key.json
@@ -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": {}
+} \ No newline at end of file
diff --git a/development/states/new-account.json b/development/states/new-account.json
new file mode 100644
index 000000000..8c9be3654
--- /dev/null
+++ b/development/states/new-account.json
@@ -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": {}
+} \ No newline at end of file
diff --git a/notices/notice_0.md b/notices/notice_0.md
deleted file mode 100644
index 1b2d5d018..000000000
--- a/notices/notice_0.md
+++ /dev/null
@@ -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
-
diff --git a/package.json b/package.json
index 52708fdab..2c0c30523 100644
--- a/package.json
+++ b/package.json
@@ -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",
diff --git a/test/integration/lib/first-time.js b/test/integration/lib/first-time.js
index 1811ccbd4..777fcbb7e 100644
--- a/test/integration/lib/first-time.js
+++ b/test/integration/lib/first-time.js
@@ -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()
diff --git a/test/unit/keyrings/simple-test.js b/test/unit/keyrings/simple-test.js
index 687318f99..77eeb834c 100644
--- a/test/unit/keyrings/simple-test.js
+++ b/test/unit/keyrings/simple-test.js
@@ -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() {
diff --git a/ui/app/accounts/import/index.js b/ui/app/accounts/import/index.js
new file mode 100644
index 000000000..18a6b985c
--- /dev/null
+++ b/ui/app/accounts/import/index.js
@@ -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)
+ }
+}
diff --git a/ui/app/accounts/import/json.js b/ui/app/accounts/import/json.js
new file mode 100644
index 000000000..22cf95cfd
--- /dev/null
+++ b/ui/app/accounts/import/json.js
@@ -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!`,
+ ])
+ )
+}
+
diff --git a/ui/app/accounts/import/private-key.js b/ui/app/accounts/import/private-key.js
new file mode 100644
index 000000000..6b988a76b
--- /dev/null
+++ b/ui/app/accounts/import/private-key.js
@@ -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 ]))
+}
+
diff --git a/ui/app/accounts/import/seed.js b/ui/app/accounts/import/seed.js
new file mode 100644
index 000000000..b4a7c0afa
--- /dev/null
+++ b/ui/app/accounts/import/seed.js
@@ -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'),
+ ])
+ )
+}
+
diff --git a/ui/app/accounts/index.js b/ui/app/accounts/index.js
index edb15eafe..e6f376735 100644
--- a/ui/app/accounts/index.js
+++ b/ui/app/accounts/index.js
@@ -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())
}
diff --git a/ui/app/actions.js b/ui/app/actions.js
index 5a3968f82..7934a329a 100644
--- a/ui/app/actions.js
+++ b/ui/app/actions.js
@@ -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())
diff --git a/ui/app/app.js b/ui/app/app.js
index 9efe95874..0e04c334c 100644
--- a/ui/app/app.js
+++ b/ui/app/app.js
@@ -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')
@@ -305,6 +306,13 @@ App.prototype.renderDropdown = function () {
}),
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 }),
action: () => this.props.dispatch(actions.lockMetamask()),
@@ -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'})
diff --git a/ui/app/components/buy-button-subview.js b/ui/app/components/buy-button-subview.js
index 35eda647e..afda5bf59 100644
--- a/ui/app/components/buy-button-subview.js
+++ b/ui/app/components/buy-button-subview.js
@@ -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(),
])
)
diff --git a/ui/app/components/tab-bar.js b/ui/app/components/tab-bar.js
new file mode 100644
index 000000000..65078e0a4
--- /dev/null
+++ b/ui/app/components/tab-bar.js
@@ -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)
+ }))
+ )
+}
diff --git a/ui/app/conf-tx.js b/ui/app/conf-tx.js
index 8e255a867..a6e03c3ed 100644
--- a/ui/app/conf-tx.js
+++ b/ui/app/conf-tx.js
@@ -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]
diff --git a/ui/app/css/lib.css b/ui/app/css/lib.css
index abbf8667e..a8df1d115 100644
--- a/ui/app/css/lib.css
+++ b/ui/app/css/lib.css
@@ -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;
diff --git a/ui/app/info.js b/ui/app/info.js
index cc753b2ea..e79580be4 100644
--- a/ui/app/info.js
+++ b/ui/app/info.js
@@ -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+ */
diff --git a/ui/app/reducers/app.js b/ui/app/reducers/app.js
index dc7344b3e..ae91272cc 100644
--- a/ui/app/reducers/app.js
+++ b/ui/app/reducers/app.js
@@ -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: {
diff --git a/ui/app/unlock.js b/ui/app/unlock.js
index 19f5eaec2..1aee3c5d0 100644
--- a/ui/app/unlock.js
+++ b/ui/app/unlock.js
@@ -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, {
diff --git a/ui/css.js b/ui/css.js
index 01f317acd..043363cd7 100644
--- a/ui/css.js
+++ b/ui/css.js
@@ -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 () {