Add route for signature request

feature/default_network_editable
Alexander Tseung 7 years ago
parent 706a6b0ad6
commit 5d1187c37b
  1. 131
      ui/app/actions.js
  2. 25
      ui/app/app.js
  3. 321
      ui/app/components/pages/signature-request.js
  4. 253
      ui/app/components/signature-request.js
  5. 27
      ui/app/conf-tx.js
  6. 2
      ui/app/routes.js

@ -489,35 +489,47 @@ function signMsg (msgData) {
return (dispatch) => {
dispatch(actions.showLoadingIndication())
log.debug(`actions calling background.signMessage`)
background.signMessage(msgData, (err, newState) => {
log.debug('signMessage called back')
dispatch(actions.updateMetamaskState(newState))
dispatch(actions.hideLoadingIndication())
return new Promise((resolve, reject) => {
log.debug(`actions calling background.signMessage`)
background.signMessage(msgData, (err, newState) => {
log.debug('signMessage called back')
dispatch(actions.updateMetamaskState(newState))
dispatch(actions.hideLoadingIndication())
if (err) log.error(err)
if (err) return dispatch(actions.displayWarning(err.message))
if (err) {
log.error(err)
dispatch(actions.displayWarning(err.message))
return reject(err)
}
dispatch(actions.completedTx(msgData.metamaskId))
dispatch(actions.completedTx(msgData.metamaskId))
return resolve(msgData)
})
})
}
}
function signPersonalMsg (msgData) {
log.debug('action - signPersonalMsg')
return (dispatch) => {
return dispatch => {
dispatch(actions.showLoadingIndication())
log.debug(`actions calling background.signPersonalMessage`)
background.signPersonalMessage(msgData, (err, newState) => {
log.debug('signPersonalMessage called back')
dispatch(actions.updateMetamaskState(newState))
dispatch(actions.hideLoadingIndication())
return new Promise((resolve, reject) => {
log.debug(`actions calling background.signPersonalMessage`)
background.signPersonalMessage(msgData, (err, newState) => {
log.debug('signPersonalMessage called back')
dispatch(actions.updateMetamaskState(newState))
dispatch(actions.hideLoadingIndication())
if (err) log.error(err)
if (err) return dispatch(actions.displayWarning(err.message))
if (err) {
log.error(err)
dispatch(actions.displayWarning(err.message))
return reject(err)
}
dispatch(actions.completedTx(msgData.metamaskId))
dispatch(actions.completedTx(msgData.metamaskId))
return resolve(msgData)
})
})
}
}
@ -527,16 +539,22 @@ function signTypedMsg (msgData) {
return (dispatch) => {
dispatch(actions.showLoadingIndication())
log.debug(`actions calling background.signTypedMessage`)
background.signTypedMessage(msgData, (err, newState) => {
log.debug('signTypedMessage called back')
dispatch(actions.updateMetamaskState(newState))
dispatch(actions.hideLoadingIndication())
return new Promise((resolve, reject) => {
log.debug(`actions calling background.signTypedMessage`)
background.signTypedMessage(msgData, (err, newState) => {
log.debug('signTypedMessage called back')
dispatch(actions.updateMetamaskState(newState))
dispatch(actions.hideLoadingIndication())
if (err) log.error(err)
if (err) return dispatch(actions.displayWarning(err.message))
if (err) {
log.error(err)
dispatch(actions.displayWarning(err.message))
return reject(err)
}
dispatch(actions.completedTx(msgData.metamaskId))
dispatch(actions.completedTx(msgData.metamaskId))
return resolve(msgData)
})
})
}
}
@ -744,21 +762,66 @@ function txError (err) {
}
function cancelMsg (msgData) {
log.debug(`background.cancelMessage`)
background.cancelMessage(msgData.id)
return actions.completedTx(msgData.id)
return dispatch => {
dispatch(actions.showLoadingIndication())
return new Promise((resolve, reject) => {
log.debug(`background.cancelMessage`)
background.cancelMessage(msgData.id, (err, newState) => {
dispatch(actions.updateMetamaskState(newState))
dispatch(actions.hideLoadingIndication())
if (err) {
return reject(err)
}
dispatch(actions.completedTx(msgData.id))
return resolve(msgData)
})
})
}
}
function cancelPersonalMsg (msgData) {
const id = msgData.id
background.cancelPersonalMessage(id)
return actions.completedTx(id)
return dispatch => {
dispatch(actions.showLoadingIndication())
return new Promise((resolve, reject) => {
const id = msgData.id
background.cancelPersonalMessage(id, (err, newState) => {
dispatch(actions.updateMetamaskState(newState))
dispatch(actions.hideLoadingIndication())
if (err) {
return reject(err)
}
dispatch(actions.completedTx(id))
return resolve(msgData)
})
})
}
}
function cancelTypedMsg (msgData) {
const id = msgData.id
background.cancelTypedMessage(id)
return actions.completedTx(id)
return dispatch => {
dispatch(actions.showLoadingIndication())
return new Promise((resolve, reject) => {
const id = msgData.id
background.cancelTypedMessage(id, (err, newState) => {
dispatch(actions.updateMetamaskState(newState))
dispatch(actions.hideLoadingIndication())
if (err) {
return reject(err)
}
dispatch(actions.completedTx(id))
return resolve(msgData)
})
})
}
}
function cancelTx (txData) {

@ -33,6 +33,7 @@ const RevealSeedPage = require('./components/pages/keychains/reveal-seed')
const AddTokenPage = require('./components/pages/add-token')
const ImportAccountPage = require('./components/pages/import-account')
const NoticeScreen = require('./components/pages/notice')
const SignatureRequestPage = require('./components/pages/signature-request')
const Loading = require('./components/loading')
const NetworkIndicator = require('./components/network')
@ -60,6 +61,7 @@ const {
CONFIRM_TRANSACTION_ROUTE,
INITIALIZE_ROUTE,
NOTICE_ROUTE,
SIGNATURE_REQUEST_ROUTE,
} = require('./routes')
class App extends Component {
@ -112,6 +114,7 @@ class App extends Component {
h(Authenticated, { path: SEND_ROUTE, exact, component: SendTransactionScreen2 }),
h(Authenticated, { path: ADD_TOKEN_ROUTE, exact, component: AddTokenPage }),
h(Authenticated, { path: IMPORT_ACCOUNT_ROUTE, exact, component: ImportAccountPage }),
h(Authenticated, { path: SIGNATURE_REQUEST_ROUTE, exact, component: SignatureRequestPage }),
h(Authenticated, { path: DEFAULT_ROUTE, exact, component: this.renderPrimary }),
])
)
@ -356,6 +359,9 @@ class App extends Component {
activeAddress,
unapprovedTxs = {},
seedWords,
unapprovedMsgCount = 0,
unapprovedPersonalMsgCount = 0,
unapprovedTypedMessagesCount = 0,
} = this.props
// seed words
@ -386,6 +392,15 @@ class App extends Component {
})
}
// unapproved messages
if (unapprovedTypedMessagesCount + unapprovedMsgCount + unapprovedPersonalMsgCount > 0) {
return h(Redirect, {
to: {
pathname: SIGNATURE_REQUEST_ROUTE,
},
})
}
// if (!props.noActiveNotices) {
// log.debug('rendering notice screen for unread notices.')
// return h(NoticeScreen, {
@ -595,6 +610,9 @@ App.propTypes = {
activeAddress: PropTypes.string,
unapprovedTxs: PropTypes.object,
seedWords: PropTypes.string,
unapprovedMsgCount: PropTypes.number,
unapprovedPersonalMsgCount: PropTypes.number,
unapprovedTypedMessagesCount: PropTypes.number,
}
function mapStateToProps (state) {
@ -617,6 +635,9 @@ function mapStateToProps (state) {
unapprovedTxs,
lastUnreadNotice,
lostAccounts,
unapprovedMsgCount,
unapprovedPersonalMsgCount,
unapprovedTypedMessagesCount,
} = metamask
const selected = address || Object.keys(accounts)[0]
@ -637,7 +658,9 @@ function mapStateToProps (state) {
isOnboarding: Boolean(!noActiveNotices || seedWords || !isInitialized),
seedWords: state.metamask.seedWords,
unapprovedTxs,
unapprovedMsgs: state.metamask.unapprovedMsgs,
unapprovedMsgCount,
unapprovedPersonalMsgCount,
unapprovedTypedMessagesCount,
menuOpen: state.appState.menuOpen,
network: state.metamask.network,
provider: state.metamask.provider,

@ -0,0 +1,321 @@
const { Component } = require('react')
const h = require('react-hyperscript')
const PropTypes = require('prop-types')
const Identicon = require('../identicon')
const { connect } = require('react-redux')
const ethUtil = require('ethereumjs-util')
const classnames = require('classnames')
const AccountDropdownMini = require('../dropdowns/account-dropdown-mini')
const actions = require('../../actions')
const { conversionUtil } = require('../../conversion-util')
const txHelper = require('../../../lib/tx-helper')
const { DEFAULT_ROUTE } = require('../../routes')
const {
getSelectedAccount,
getCurrentAccountWithSendEtherInfo,
getSelectedAddress,
accountsWithSendEtherInfoSelector,
conversionRateSelector,
} = require('../../selectors.js')
class SignatureRequest extends Component {
constructor (props) {
super(props)
this.state = {
selectedAccount: props.selectedAccount,
accountDropdownOpen: false,
}
}
componentWillMount () {
const {
unapprovedMsgCount = 0,
unapprovedPersonalMsgCount = 0,
unapprovedTypedMessagesCount = 0,
} = this.props
if (unapprovedMsgCount + unapprovedPersonalMsgCount + unapprovedTypedMessagesCount < 1) {
this.props.history.push(DEFAULT_ROUTE)
}
}
renderHeader () {
return h('div.request-signature__header', [
h('div.request-signature__header-background'),
h('div.request-signature__header__text', 'Signature Request'),
h('div.request-signature__header__tip-container', [
h('div.request-signature__header__tip'),
]),
])
}
renderAccountDropdown () {
const {
selectedAccount,
accountDropdownOpen,
} = this.state
const { accounts } = this.props
return h('div.request-signature__account', [
h('div.request-signature__account-text', ['Account:']),
h(AccountDropdownMini, {
selectedAccount,
accounts,
onSelect: selectedAccount => this.setState({ selectedAccount }),
dropdownOpen: accountDropdownOpen,
openDropdown: () => this.setState({ accountDropdownOpen: true }),
closeDropdown: () => this.setState({ accountDropdownOpen: false }),
}),
])
}
renderBalance () {
const { balance, conversionRate } = this.props
const balanceInEther = conversionUtil(balance, {
fromNumericBase: 'hex',
toNumericBase: 'dec',
fromDenomination: 'WEI',
numberOfDecimals: 6,
conversionRate,
})
return h('div.request-signature__balance', [
h('div.request-signature__balance-text', ['Balance:']),
h('div.request-signature__balance-value', `${balanceInEther} ETH`),
])
}
renderAccountInfo () {
return h('div.request-signature__account-info', [
this.renderAccountDropdown(),
this.renderRequestIcon(),
this.renderBalance(),
])
}
renderRequestIcon () {
const { requesterAddress } = this.props
return h('div.request-signature__request-icon', [
h(Identicon, {
diameter: 40,
address: requesterAddress,
}),
])
}
renderRequestInfo () {
return h('div.request-signature__request-info', [
h('div.request-signature__headline', [
`Your signature is being requested`,
]),
])
}
msgHexToText (hex) {
try {
const stripped = ethUtil.stripHexPrefix(hex)
const buff = Buffer.from(stripped, 'hex')
return buff.toString('utf8')
} catch (e) {
return hex
}
}
renderBody () {
let rows = []
let notice = 'You are signing:'
const { txData = {} } = this.props
const { type, msgParams = {} } = txData
const { data } = msgParams
if (type === 'personal_sign') {
rows = [{ name: 'Message', value: this.msgHexToText(data) }]
} else if (type === 'eth_signTypedData') {
rows = data
} else if (type === 'eth_sign') {
rows = [{ name: 'Message', value: data }]
notice = `Signing this message can have
dangerous side effects. Only sign messages from
sites you fully trust with your entire account.
This dangerous method will be removed in a future version. `
}
return h('div.request-signature__body', {}, [
this.renderAccountInfo(),
this.renderRequestInfo(),
h('div.request-signature__notice', {
className: classnames({
'request-signature__notice': type === 'personal_sign' || type === 'eth_signTypedData',
'request-signature__warning': type === 'eth_sign',
}),
}, [notice]),
h('div.request-signature__rows', [
...rows.map(({ name, value }) => {
return h('div.request-signature__row', [
h('div.request-signature__row-title', [`${name}:`]),
h('div.request-signature__row-value', value),
])
}),
]),
])
}
renderFooter () {
const {
txData = {},
signPersonalMessage,
signTypedMessage,
cancelPersonalMessage,
cancelTypedMessage,
signMessage,
cancelMessage,
history,
} = this.props
const { type } = txData
let cancel = () => Promise.resolve()
let sign = () => Promise.resolve()
const { msgParams: params = {}, id } = txData
params.id = id
params.metamaskId = id
switch (type) {
case 'personal_sign':
cancel = () => cancelPersonalMessage(params)
sign = () => signPersonalMessage(params)
break
case 'eth_signTypedData':
cancel = () => cancelTypedMessage(params)
sign = () => signTypedMessage(params)
break
case 'eth_sign':
cancel = () => cancelMessage(params)
sign = () => signMessage(params)
break
}
return h('div.request-signature__footer', [
h('button.request-signature__footer__cancel-button', {
onClick: () => {
cancel().then(() => history.push(DEFAULT_ROUTE))
},
}, 'CANCEL'),
h('button.request-signature__footer__sign-button', {
onClick: () => {
sign().then(() => history.push(DEFAULT_ROUTE))
},
}, 'SIGN'),
])
}
render () {
return (
h('div.request-signature__container', [
this.renderHeader(),
this.renderBody(),
this.renderFooter(),
])
)
}
}
SignatureRequest.propTypes = {
txData: PropTypes.object,
signPersonalMessage: PropTypes.func,
cancelPersonalMessage: PropTypes.func,
signTypedMessage: PropTypes.func,
cancelTypedMessage: PropTypes.func,
signMessage: PropTypes.func,
cancelMessage: PropTypes.func,
requesterAddress: PropTypes.string,
accounts: PropTypes.array,
conversionRate: PropTypes.number,
balance: PropTypes.string,
selectedAccount: PropTypes.object,
history: PropTypes.object,
unapprovedMsgCount: PropTypes.number,
unapprovedPersonalMsgCount: PropTypes.number,
unapprovedTypedMessagesCount: PropTypes.number,
}
const mapStateToProps = state => {
const { metamask } = state
const {
unapprovedTxs,
unapprovedMsgs,
unapprovedPersonalMsgs,
unapprovedTypedMessages,
network,
unapprovedMsgCount,
unapprovedPersonalMsgCount,
unapprovedTypedMessagesCount,
} = metamask
const unconfTxList = txHelper(
unapprovedTxs,
unapprovedMsgs,
unapprovedPersonalMsgs,
unapprovedTypedMessages,
network
) || []
return {
balance: getSelectedAccount(state).balance,
selectedAccount: getCurrentAccountWithSendEtherInfo(state),
selectedAddress: getSelectedAddress(state),
accounts: accountsWithSendEtherInfoSelector(state),
conversionRate: conversionRateSelector(state),
unapprovedMsgCount,
unapprovedPersonalMsgCount,
unapprovedTypedMessagesCount,
txData: unconfTxList[0] || {},
}
}
const mapDispatchToProps = dispatch => {
return {
signPersonalMessage: params => dispatch(actions.signPersonalMsg(params)),
cancelPersonalMessage: params => dispatch(actions.cancelPersonalMsg(params)),
signTypedMessage: params => dispatch(actions.signTypedMsg(params)),
cancelTypedMessage: params => dispatch(actions.cancelTypedMsg(params)),
signMessage: params => dispatch(actions.signMsg(params)),
cancelMessage: params => dispatch(actions.cancelMsg(params)),
}
}
module.exports = connect(mapStateToProps, mapDispatchToProps)(SignatureRequest)

@ -1,253 +0,0 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const Identicon = require('./identicon')
const connect = require('react-redux').connect
const ethUtil = require('ethereumjs-util')
const classnames = require('classnames')
const AccountDropdownMini = require('./dropdowns/account-dropdown-mini')
const actions = require('../actions')
const { conversionUtil } = require('../conversion-util')
const {
getSelectedAccount,
getCurrentAccountWithSendEtherInfo,
getSelectedAddress,
accountsWithSendEtherInfoSelector,
conversionRateSelector,
} = require('../selectors.js')
function mapStateToProps (state) {
return {
balance: getSelectedAccount(state).balance,
selectedAccount: getCurrentAccountWithSendEtherInfo(state),
selectedAddress: getSelectedAddress(state),
requester: null,
requesterAddress: null,
accounts: accountsWithSendEtherInfoSelector(state),
conversionRate: conversionRateSelector(state),
}
}
function mapDispatchToProps (dispatch) {
return {
goHome: () => dispatch(actions.goHome()),
}
}
module.exports = connect(mapStateToProps, mapDispatchToProps)(SignatureRequest)
inherits(SignatureRequest, Component)
function SignatureRequest (props) {
Component.call(this)
this.state = {
selectedAccount: props.selectedAccount,
accountDropdownOpen: false,
}
}
SignatureRequest.prototype.renderHeader = function () {
return h('div.request-signature__header', [
h('div.request-signature__header-background'),
h('div.request-signature__header__text', 'Signature Request'),
h('div.request-signature__header__tip-container', [
h('div.request-signature__header__tip'),
]),
])
}
SignatureRequest.prototype.renderAccountDropdown = function () {
const {
selectedAccount,
accountDropdownOpen,
} = this.state
const {
accounts,
} = this.props
return h('div.request-signature__account', [
h('div.request-signature__account-text', ['Account:']),
h(AccountDropdownMini, {
selectedAccount,
accounts,
onSelect: selectedAccount => this.setState({ selectedAccount }),
dropdownOpen: accountDropdownOpen,
openDropdown: () => this.setState({ accountDropdownOpen: true }),
closeDropdown: () => this.setState({ accountDropdownOpen: false }),
}),
])
}
SignatureRequest.prototype.renderBalance = function () {
const { balance, conversionRate } = this.props
const balanceInEther = conversionUtil(balance, {
fromNumericBase: 'hex',
toNumericBase: 'dec',
fromDenomination: 'WEI',
numberOfDecimals: 6,
conversionRate,
})
return h('div.request-signature__balance', [
h('div.request-signature__balance-text', ['Balance:']),
h('div.request-signature__balance-value', `${balanceInEther} ETH`),
])
}
SignatureRequest.prototype.renderAccountInfo = function () {
return h('div.request-signature__account-info', [
this.renderAccountDropdown(),
this.renderRequestIcon(),
this.renderBalance(),
])
}
SignatureRequest.prototype.renderRequestIcon = function () {
const { requesterAddress } = this.props
return h('div.request-signature__request-icon', [
h(Identicon, {
diameter: 40,
address: requesterAddress,
}),
])
}
SignatureRequest.prototype.renderRequestInfo = function () {
return h('div.request-signature__request-info', [
h('div.request-signature__headline', [
`Your signature is being requested`,
]),
])
}
SignatureRequest.prototype.msgHexToText = function (hex) {
try {
const stripped = ethUtil.stripHexPrefix(hex)
const buff = Buffer.from(stripped, 'hex')
return buff.toString('utf8')
} catch (e) {
return hex
}
}
SignatureRequest.prototype.renderBody = function () {
let rows
let notice = 'You are signing:'
const { txData } = this.props
const { type, msgParams: { data } } = txData
if (type === 'personal_sign') {
rows = [{ name: 'Message', value: this.msgHexToText(data) }]
} else if (type === 'eth_signTypedData') {
rows = data
} else if (type === 'eth_sign') {
rows = [{ name: 'Message', value: data }]
notice = `Signing this message can have
dangerous side effects. Only sign messages from
sites you fully trust with your entire account.
This dangerous method will be removed in a future version. `
}
return h('div.request-signature__body', {}, [
this.renderAccountInfo(),
this.renderRequestInfo(),
h('div.request-signature__notice', {
className: classnames({
'request-signature__notice': type === 'personal_sign' || type === 'eth_signTypedData',
'request-signature__warning': type === 'eth_sign',
}),
}, [notice]),
h('div.request-signature__rows', [
...rows.map(({ name, value }) => {
return h('div.request-signature__row', [
h('div.request-signature__row-title', [`${name}:`]),
h('div.request-signature__row-value', value),
])
}),
]),
])
}
SignatureRequest.prototype.renderFooter = function () {
const {
signPersonalMessage,
signTypedMessage,
cancelPersonalMessage,
cancelTypedMessage,
signMessage,
cancelMessage,
} = this.props
const { txData } = this.props
const { type } = txData
let cancel
let sign
if (type === 'personal_sign') {
cancel = cancelPersonalMessage
sign = signPersonalMessage
} else if (type === 'eth_signTypedData') {
cancel = cancelTypedMessage
sign = signTypedMessage
} else if (type === 'eth_sign') {
cancel = cancelMessage
sign = signMessage
}
return h('div.request-signature__footer', [
h('button.request-signature__footer__cancel-button', {
onClick: cancel,
}, 'CANCEL'),
h('button.request-signature__footer__sign-button', {
onClick: sign,
}, 'SIGN'),
])
}
SignatureRequest.prototype.render = function () {
return (
h('div.request-signature__container', [
this.renderHeader(),
this.renderBody(),
this.renderFooter(),
])
)
}

@ -8,7 +8,6 @@ const actions = require('./actions')
const txHelper = require('../lib/tx-helper')
const PendingTx = require('./components/pending-tx')
const SignatureRequest = require('./components/signature-request')
// const PendingMsg = require('./components/pending-msg')
// const PendingPersonalMsg = require('./components/pending-personal-msg')
// const PendingTypedMsg = require('./components/pending-typed-msg')
@ -21,6 +20,12 @@ module.exports = compose(
)(ConfirmTxScreen)
function mapStateToProps (state) {
const { metamask } = state
const {
unapprovedMsgCount,
unapprovedPersonalMsgCount,
} = metamask
return {
identities: state.metamask.identities,
accounts: state.metamask.accounts,
@ -37,6 +42,8 @@ function mapStateToProps (state) {
currentCurrency: state.metamask.currentCurrency,
blockGasLimit: state.metamask.currentBlockGasLimit,
computedBalances: state.metamask.computedBalances,
unapprovedMsgCount,
unapprovedPersonalMsgCount,
}
}
@ -125,27 +132,13 @@ ConfirmTxScreen.prototype.render = function () {
function currentTxView (opts) {
log.info('rendering current tx view')
const { txData } = opts
const { txParams, msgParams } = txData
const { txParams } = txData
if (txParams) {
log.debug('txParams detected, rendering pending tx')
return h(PendingTx, opts)
} else if (msgParams) {
log.debug('msgParams detected, rendering pending msg')
return h(SignatureRequest, opts)
// if (type === 'eth_sign') {
// log.debug('rendering eth_sign message')
// return h(PendingMsg, opts)
// } else if (type === 'personal_sign') {
// log.debug('rendering personal_sign message')
// return h(PendingPersonalMsg, opts)
// } else if (type === 'eth_signTypedData') {
// log.debug('rendering eth_signTypedData message')
// return h(PendingTypedMsg, opts)
// }
}
return h(Loading)
}

@ -11,6 +11,7 @@ const SEND_ROUTE = '/send'
const CONFIRM_TRANSACTION_ROUTE = '/confirm-transaction'
const INITIALIZE_ROUTE = '/initialize'
const NOTICE_ROUTE = '/notice'
const SIGNATURE_REQUEST_ROUTE = '/signature-request'
module.exports = {
DEFAULT_ROUTE,
@ -26,4 +27,5 @@ module.exports = {
CONFIRM_TRANSACTION_ROUTE,
INITIALIZE_ROUTE,
NOTICE_ROUTE,
SIGNATURE_REQUEST_ROUTE,
}

Loading…
Cancel
Save