From 3a7d4a5d4e94d9a4c2556b161c5bad7dc00837a4 Mon Sep 17 00:00:00 2001 From: Kevin Serrano Date: Mon, 11 Sep 2017 16:13:28 -0700 Subject: [PATCH 1/6] Basic private key download file functionality. --- ui/app/components/account-export.js | 39 ++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/ui/app/components/account-export.js b/ui/app/components/account-export.js index 330f73805..5bbdfca39 100644 --- a/ui/app/components/account-export.js +++ b/ui/app/components/account-export.js @@ -20,20 +20,21 @@ function mapStateToProps (state) { } ExportAccountView.prototype.render = function () { - var state = this.props - var accountDetail = state.accountDetail + const state = this.props + const accountDetail = state.accountDetail + const nickname = state.identities[state.address].name if (!accountDetail) return h('div') - var accountExport = accountDetail.accountExport + const accountExport = accountDetail.accountExport - var notExporting = accountExport === 'none' - var exportRequested = accountExport === 'requested' - var accountExported = accountExport === 'completed' + const notExporting = accountExport === 'none' + const exportRequested = accountExport === 'requested' + const accountExported = accountExport === 'completed' if (notExporting) return h('div') if (exportRequested) { - var warning = `Export private keys at your own risk.` + const warning = `Export private keys at your own risk.` return ( h('div', { style: { @@ -89,6 +90,8 @@ ExportAccountView.prototype.render = function () { } if (accountExported) { + const plainKey = ethUtil.stripHexPrefix(accountDetail.privateKey) + return h('div.privateKey', { style: { margin: '0 20px', @@ -105,10 +108,13 @@ ExportAccountView.prototype.render = function () { onClick: function (event) { copyToClipboard(ethUtil.stripHexPrefix(accountDetail.privateKey)) }, - }, ethUtil.stripHexPrefix(accountDetail.privateKey)), + }, plainKey), h('button', { onClick: () => this.props.dispatch(actions.backToAccountDetail(this.props.address)), }, 'Done'), + h('button', { + onClick: () => this.exportAsFile(`MetaMask ${nickname} Private Key`, plainKey), + }, 'Save as File'), ]) } } @@ -117,6 +123,21 @@ ExportAccountView.prototype.onExportKeyPress = function (event) { if (event.key !== 'Enter') return event.preventDefault() - var input = document.getElementById('exportAccount').value + const input = document.getElementById('exportAccount').value this.props.dispatch(actions.exportAccount(input, this.props.address)) } + +ExportAccountView.prototype.exportAsFile = function (filename, data) { + // source: https://stackoverflow.com/a/33542499 by Ludovic Feltz + const blob = new Blob([data], {type: 'text/csv'}) + if (window.navigator.msSaveOrOpenBlob) { + window.navigator.msSaveBlob(blob, filename) + } else { + const elem = window.document.createElement('a') + elem.href = window.URL.createObjectURL(blob) + elem.download = filename + document.body.appendChild(elem) + elem.click() + document.body.removeChild(elem) + } +} From fa1ec5dcd16ba09d31720a42d4015dedde91148c Mon Sep 17 00:00:00 2001 From: Kevin Serrano Date: Mon, 11 Sep 2017 16:22:20 -0700 Subject: [PATCH 2/6] Move function as util function. --- ui/app/components/account-export.js | 18 ++---------------- ui/app/util.js | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/ui/app/components/account-export.js b/ui/app/components/account-export.js index 5bbdfca39..d438c9ca5 100644 --- a/ui/app/components/account-export.js +++ b/ui/app/components/account-export.js @@ -1,6 +1,7 @@ const Component = require('react').Component const h = require('react-hyperscript') const inherits = require('util').inherits +const exportAsFile = require('../util').exportAsFile const copyToClipboard = require('copy-to-clipboard') const actions = require('../actions') const ethUtil = require('ethereumjs-util') @@ -113,7 +114,7 @@ ExportAccountView.prototype.render = function () { onClick: () => this.props.dispatch(actions.backToAccountDetail(this.props.address)), }, 'Done'), h('button', { - onClick: () => this.exportAsFile(`MetaMask ${nickname} Private Key`, plainKey), + onClick: () => exportAsFile(`MetaMask ${nickname} Private Key`, plainKey), }, 'Save as File'), ]) } @@ -126,18 +127,3 @@ ExportAccountView.prototype.onExportKeyPress = function (event) { const input = document.getElementById('exportAccount').value this.props.dispatch(actions.exportAccount(input, this.props.address)) } - -ExportAccountView.prototype.exportAsFile = function (filename, data) { - // source: https://stackoverflow.com/a/33542499 by Ludovic Feltz - const blob = new Blob([data], {type: 'text/csv'}) - if (window.navigator.msSaveOrOpenBlob) { - window.navigator.msSaveBlob(blob, filename) - } else { - const elem = window.document.createElement('a') - elem.href = window.URL.createObjectURL(blob) - elem.download = filename - document.body.appendChild(elem) - elem.click() - document.body.removeChild(elem) - } -} diff --git a/ui/app/util.js b/ui/app/util.js index ac3f42c6b..1368ebf11 100644 --- a/ui/app/util.js +++ b/ui/app/util.js @@ -36,6 +36,7 @@ module.exports = { valueTable: valueTable, bnTable: bnTable, isHex: isHex, + exportAsFile: exportAsFile, } function valuesFor (obj) { @@ -215,3 +216,18 @@ function readableDate (ms) { function isHex (str) { return Boolean(str.match(/^(0x)?[0-9a-fA-F]+$/)) } + +function exportAsFile (filename, data) { + // source: https://stackoverflow.com/a/33542499 by Ludovic Feltz + const blob = new Blob([data], {type: 'text/csv'}) + if (window.navigator.msSaveOrOpenBlob) { + window.navigator.msSaveBlob(blob, filename) + } else { + const elem = window.document.createElement('a') + elem.href = window.URL.createObjectURL(blob) + elem.download = filename + document.body.appendChild(elem) + elem.click() + document.body.removeChild(elem) + } +} From aa021b920942efd8075ca4e89cd92d50a1b21ed0 Mon Sep 17 00:00:00 2001 From: Kevin Serrano Date: Mon, 11 Sep 2017 16:40:45 -0700 Subject: [PATCH 3/6] Add some styling. --- ui/app/components/account-export.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ui/app/components/account-export.js b/ui/app/components/account-export.js index d438c9ca5..53a7f0fa1 100644 --- a/ui/app/components/account-export.js +++ b/ui/app/components/account-export.js @@ -115,6 +115,9 @@ ExportAccountView.prototype.render = function () { }, 'Done'), h('button', { onClick: () => exportAsFile(`MetaMask ${nickname} Private Key`, plainKey), + stlye: { + marginLeft: '10px', + }, }, 'Save as File'), ]) } From e7c1a11b4de100f1966f639c79632a53763a8dac Mon Sep 17 00:00:00 2001 From: Kevin Serrano Date: Mon, 11 Sep 2017 16:48:02 -0700 Subject: [PATCH 4/6] Add ability to save seed words as file. --- ui/app/keychains/hd/create-vault-complete.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ui/app/keychains/hd/create-vault-complete.js b/ui/app/keychains/hd/create-vault-complete.js index c32751fff..745990351 100644 --- a/ui/app/keychains/hd/create-vault-complete.js +++ b/ui/app/keychains/hd/create-vault-complete.js @@ -3,6 +3,7 @@ const Component = require('react').Component const connect = require('react-redux').connect const h = require('react-hyperscript') const actions = require('../../actions') +const exportAsFile = require('../../util').exportAsFile module.exports = connect(mapStateToProps)(CreateVaultCompleteScreen) @@ -65,8 +66,17 @@ CreateVaultCompleteScreen.prototype.render = function () { style: { margin: '24px', fontSize: '0.9em', + marginBottom: '10px', }, }, 'I\'ve copied it somewhere safe'), + + h('button.primary', { + onClick: () => exportAsFile(`MetaMask Seed Words`, seed), + style: { + margin: '10px', + fontSize: '0.9em', + }, + }, 'Save Seed Words As File'), ]) ) } From 1a0c465dc96ba31c2114626e8cb8d577b5a28e9a Mon Sep 17 00:00:00 2001 From: Kevin Serrano Date: Mon, 11 Sep 2017 16:48:18 -0700 Subject: [PATCH 5/6] Change the changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d41742be..44aae4cba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## Current Master +- Add ability to export private keys as a file. +- Add ability to export seed words as a file. + ## 3.10.0 2017-9-11 - Readded loose keyring label back into the account list. From 213af0cd6c84366b76a7ba5206fa5a6b79880028 Mon Sep 17 00:00:00 2001 From: Kevin Serrano Date: Mon, 11 Sep 2017 16:52:35 -0700 Subject: [PATCH 6/6] Fix typo. --- ui/app/components/account-export.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/app/components/account-export.js b/ui/app/components/account-export.js index 53a7f0fa1..32b103c86 100644 --- a/ui/app/components/account-export.js +++ b/ui/app/components/account-export.js @@ -114,10 +114,10 @@ ExportAccountView.prototype.render = function () { onClick: () => this.props.dispatch(actions.backToAccountDetail(this.props.address)), }, 'Done'), h('button', { - onClick: () => exportAsFile(`MetaMask ${nickname} Private Key`, plainKey), - stlye: { + style: { marginLeft: '10px', }, + onClick: () => exportAsFile(`MetaMask ${nickname} Private Key`, plainKey), }, 'Save as File'), ]) }