mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-22 17:33:23 +01:00
Allow importing of private key strings
Fixes #1021 A top-right menu item now allows `Account Import`. It has a menu (with one item for now) that allows importing a private key string. Errors are displayed, and a success navigates the user to their account list, where the imported account is labeled `LOOSE`.
This commit is contained in:
parent
958cbfbde4
commit
1ff4894b67
@ -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)
|
||||
|
@ -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) {
|
||||
|
@ -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),
|
||||
|
84
development/states/account-list-with-imported.json
Normal file
84
development/states/account-list-with-imported.json
Normal file
@ -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": {}
|
||||
}
|
92
development/states/import-private-key-warning.json
Normal file
92
development/states/import-private-key-warning.json
Normal file
@ -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": {}
|
||||
}
|
64
development/states/import-private-key.json
Normal file
64
development/states/import-private-key.json
Normal file
@ -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": {}
|
||||
}
|
@ -7,12 +7,17 @@ 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 {
|
||||
types: state.metamask.keyringTypes,
|
||||
menuItems,
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,7 +29,7 @@ function AccountImportSubview () {
|
||||
AccountImportSubview.prototype.render = function () {
|
||||
const props = this.props
|
||||
const state = this.state || {}
|
||||
const { types } = props
|
||||
const { menuItems } = props
|
||||
const { type } = state
|
||||
|
||||
return (
|
||||
@ -50,8 +55,8 @@ AccountImportSubview.prototype.render = function () {
|
||||
h(Select, {
|
||||
name: 'import-type-select',
|
||||
clearable: false,
|
||||
value: type || types[0],
|
||||
options: types.map((type) => {
|
||||
value: type || menuItems[0],
|
||||
options: menuItems.map((type) => {
|
||||
return {
|
||||
value: type,
|
||||
label: type,
|
||||
@ -71,11 +76,15 @@ AccountImportSubview.prototype.render = function () {
|
||||
AccountImportSubview.prototype.renderImportView = function() {
|
||||
const props = this.props
|
||||
const state = this.state || {}
|
||||
const { type } = state || props.types[0]
|
||||
const { type } = state
|
||||
const { menuItems } = props
|
||||
const current = type || menuItems[0]
|
||||
|
||||
switch (type) {
|
||||
switch (current) {
|
||||
case 'HD Key Tree':
|
||||
return h(SeedImportView)
|
||||
case 'Private Key':
|
||||
return h(PrivateKeyImportView)
|
||||
default:
|
||||
return h(JsonImportView)
|
||||
}
|
||||
|
69
ui/app/accounts/import/private-key.js
Normal file
69
ui/app/accounts/import/private-key.js
Normal file
@ -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 ]))
|
||||
}
|
||||
|
@ -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, {
|
||||
|
@ -253,7 +253,15 @@ 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() {
|
||||
|
Loading…
Reference in New Issue
Block a user