mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 18:00:18 +01:00
Merge pull request #169 from MetaMask/ImplementEthSign
implement eth_sign
This commit is contained in:
commit
95582f8bde
@ -2,6 +2,8 @@
|
||||
|
||||
## Current Master
|
||||
|
||||
- Add support for calls to `eth.sign`.
|
||||
|
||||
## 1.7.0 2016-04-29
|
||||
|
||||
- Account detail view is now the primary view.
|
||||
|
@ -10,6 +10,7 @@ const IdentityStore = require('./lib/idStore')
|
||||
const createTxNotification = require('./lib/notifications.js').createTxNotification
|
||||
const createMsgNotification = require('./lib/notifications.js').createMsgNotification
|
||||
const configManager = require('./lib/config-manager-singleton')
|
||||
const messageManager = require('./lib/message-manager')
|
||||
const setupMultiplex = require('./lib/stream-utils.js').setupMultiplex
|
||||
const HostStore = require('./lib/remote-store.js').HostStore
|
||||
const Web3 = require('web3')
|
||||
@ -175,6 +176,8 @@ function setupControllerConnection(stream){
|
||||
setSelectedAddress: idStore.setSelectedAddress.bind(idStore),
|
||||
approveTransaction: idStore.approveTransaction.bind(idStore),
|
||||
cancelTransaction: idStore.cancelTransaction.bind(idStore),
|
||||
signMessage: idStore.signMessage.bind(idStore),
|
||||
cancelMessage: idStore.cancelMessage.bind(idStore),
|
||||
setLocked: idStore.setLocked.bind(idStore),
|
||||
clearSeedWordCache: idStore.clearSeedWordCache.bind(idStore),
|
||||
exportAccount: idStore.exportAccount.bind(idStore),
|
||||
@ -206,7 +209,10 @@ idStore.on('update', updateBadge)
|
||||
function updateBadge(state){
|
||||
var label = ''
|
||||
var unconfTxs = configManager.unconfirmedTxs()
|
||||
var count = Object.keys(unconfTxs).length
|
||||
var unconfTxLen = Object.keys(unconfTxs).length
|
||||
var unconfMsgs = messageManager.unconfirmedMsgs()
|
||||
var unconfMsgLen = Object.keys(unconfMsgs).length
|
||||
var count = unconfTxLen + unconfMsgLen
|
||||
if (count) {
|
||||
label = String(count)
|
||||
}
|
||||
|
@ -211,73 +211,6 @@ ConfigManager.prototype.updateTx = function(tx) {
|
||||
this._saveTxList(transactions)
|
||||
}
|
||||
|
||||
//
|
||||
// Msg
|
||||
//
|
||||
|
||||
ConfigManager.prototype.getMsgList = function() {
|
||||
var data = this.migrator.getData()
|
||||
if (data.messages !== undefined) {
|
||||
return data.messages
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
ConfigManager.prototype.unconfirmedMsgs = function() {
|
||||
var messages = this.getMsgList()
|
||||
return messages.filter(msg => msg.status === 'unconfirmed')
|
||||
.reduce((result, msg) => { result[msg.id] = msg; return result }, {})
|
||||
}
|
||||
|
||||
ConfigManager.prototype._saveMsgList = function(msgList) {
|
||||
var data = this.migrator.getData()
|
||||
data.messages = msgList
|
||||
this.setData(data)
|
||||
}
|
||||
|
||||
ConfigManager.prototype.addMsg = function(msg) {
|
||||
var messages = this.getMsgList()
|
||||
messages.push(msg)
|
||||
this._saveMsgList(messages)
|
||||
}
|
||||
|
||||
ConfigManager.prototype.getMsg = function(msgId) {
|
||||
var messages = this.getMsgList()
|
||||
var matching = messages.filter(msg => msg.id === msgId)
|
||||
return matching.length > 0 ? matching[0] : null
|
||||
}
|
||||
|
||||
ConfigManager.prototype.confirmMsg = function(msgId) {
|
||||
this._setMsgStatus(msgId, 'confirmed')
|
||||
}
|
||||
|
||||
ConfigManager.prototype.rejectMsg = function(msgId) {
|
||||
this._setMsgStatus(msgId, 'rejected')
|
||||
}
|
||||
|
||||
ConfigManager.prototype._setMsgStatus = function(msgId, status) {
|
||||
var msg = this.getMsg(msgId)
|
||||
msg.status = status
|
||||
this.updateMsg(msg)
|
||||
}
|
||||
|
||||
ConfigManager.prototype.updateMsg = function(msg) {
|
||||
var messages = this.getMsgList()
|
||||
var found, index
|
||||
messages.forEach((otherMsg, i) => {
|
||||
if (otherMsg.id === msg.id) {
|
||||
found = true
|
||||
index = i
|
||||
}
|
||||
})
|
||||
if (found) {
|
||||
messages[index] = msg
|
||||
}
|
||||
this._saveMsgList(messages)
|
||||
}
|
||||
|
||||
|
||||
|
||||
// observable
|
||||
|
||||
|
@ -9,6 +9,7 @@ const extend = require('xtend')
|
||||
const createId = require('web3-provider-engine/util/random-id')
|
||||
const autoFaucet = require('./auto-faucet')
|
||||
const configManager = require('./config-manager-singleton')
|
||||
const messageManager = require('./message-manager')
|
||||
const DEFAULT_RPC = 'https://testrpc.metamask.io/'
|
||||
|
||||
|
||||
@ -32,6 +33,7 @@ function IdentityStore(opts = {}) {
|
||||
selectedAddress: null,
|
||||
identities: {},
|
||||
}
|
||||
|
||||
// not part of serilized metamask state - only kept in memory
|
||||
this._unconfTxCbs = {}
|
||||
this._unconfMsgCbs = {}
|
||||
@ -85,6 +87,8 @@ IdentityStore.prototype.getState = function(){
|
||||
seedWords: seedWords,
|
||||
unconfTxs: configManager.unconfirmedTxs(),
|
||||
transactions: configManager.getTxList(),
|
||||
unconfMsgs: messageManager.unconfirmedMsgs(),
|
||||
messages: messageManager.getMsgList(),
|
||||
selectedAddress: configManager.getSelectedAccount(),
|
||||
}))
|
||||
}
|
||||
@ -226,7 +230,7 @@ IdentityStore.prototype.addUnconfirmedMessage = function(msgParams, cb){
|
||||
time: time,
|
||||
status: 'unconfirmed',
|
||||
}
|
||||
configManager.addMsg(msgData)
|
||||
messageManager.addMsg(msgData)
|
||||
console.log('addUnconfirmedMessage:', msgData)
|
||||
|
||||
// keep the cb around for after approval (requires user interaction)
|
||||
@ -241,27 +245,27 @@ IdentityStore.prototype.addUnconfirmedMessage = function(msgParams, cb){
|
||||
|
||||
// comes from metamask ui
|
||||
IdentityStore.prototype.approveMessage = function(msgId, cb){
|
||||
var msgData = configManager.getMsg(msgId)
|
||||
var msgData = messageManager.getMsg(msgId)
|
||||
var approvalCb = this._unconfMsgCbs[msgId] || noop
|
||||
|
||||
// accept msg
|
||||
cb()
|
||||
approvalCb(null, true)
|
||||
// clean up
|
||||
configManager.confirmMsg(msgId)
|
||||
messageManager.confirmMsg(msgId)
|
||||
delete this._unconfMsgCbs[msgId]
|
||||
this._didUpdate()
|
||||
}
|
||||
|
||||
// comes from metamask ui
|
||||
IdentityStore.prototype.cancelMessage = function(msgId){
|
||||
var txData = configManager.getMsg(msgId)
|
||||
var txData = messageManager.getMsg(msgId)
|
||||
var approvalCb = this._unconfMsgCbs[msgId] || noop
|
||||
|
||||
// reject tx
|
||||
approvalCb(null, false)
|
||||
// clean up
|
||||
configManager.rejectMsg(msgId)
|
||||
messageManager.rejectMsg(msgId)
|
||||
delete this._unconfTxCbs[msgId]
|
||||
this._didUpdate()
|
||||
}
|
||||
@ -271,7 +275,14 @@ IdentityStore.prototype.signMessage = function(msgParams, cb){
|
||||
try {
|
||||
console.log('signing msg...', msgParams.data)
|
||||
var rawMsg = this._idmgmt.signMsg(msgParams.from, msgParams.data)
|
||||
cb(null, rawMsg)
|
||||
if ('metamaskId' in msgParams) {
|
||||
var id = msgParams.metamaskId
|
||||
delete msgParams.metamaskId
|
||||
|
||||
this.approveMessage(id, cb)
|
||||
} else {
|
||||
cb(null, rawMsg)
|
||||
}
|
||||
} catch (err) {
|
||||
cb(err)
|
||||
}
|
||||
@ -426,7 +437,7 @@ function IdManagement(opts) {
|
||||
var privKeyHex = this.exportPrivateKey(txParams.from)
|
||||
var privKey = ethUtil.toBuffer(privKeyHex)
|
||||
tx.sign(privKey)
|
||||
|
||||
|
||||
// Add the tx hash to the persisted meta-tx object
|
||||
var txHash = ethUtil.bufferToHex(tx.hash())
|
||||
var metaTx = configManager.getTx(txParams.metamaskId)
|
||||
@ -472,4 +483,4 @@ function concatSig(v, r, s) {
|
||||
s = ethUtil.toUnsigned(s).toString('hex')
|
||||
v = ethUtil.stripHexPrefix(ethUtil.intToHex(v))
|
||||
return ethUtil.addHexPrefix(r.concat(s, v).toString("hex"))
|
||||
}
|
||||
}
|
||||
|
61
app/scripts/lib/message-manager.js
Normal file
61
app/scripts/lib/message-manager.js
Normal file
@ -0,0 +1,61 @@
|
||||
module.exports = new MessageManager()
|
||||
|
||||
function MessageManager(opts) {
|
||||
this.messages = []
|
||||
}
|
||||
|
||||
MessageManager.prototype.getMsgList = function() {
|
||||
return this.messages
|
||||
}
|
||||
|
||||
MessageManager.prototype.unconfirmedMsgs = function() {
|
||||
var messages = this.getMsgList()
|
||||
return messages.filter(msg => msg.status === 'unconfirmed')
|
||||
.reduce((result, msg) => { result[msg.id] = msg; return result }, {})
|
||||
}
|
||||
|
||||
MessageManager.prototype._saveMsgList = function(msgList) {
|
||||
this.messages = msgList
|
||||
}
|
||||
|
||||
MessageManager.prototype.addMsg = function(msg) {
|
||||
var messages = this.getMsgList()
|
||||
messages.push(msg)
|
||||
this._saveMsgList(messages)
|
||||
}
|
||||
|
||||
MessageManager.prototype.getMsg = function(msgId) {
|
||||
var messages = this.getMsgList()
|
||||
var matching = messages.filter(msg => msg.id === msgId)
|
||||
return matching.length > 0 ? matching[0] : null
|
||||
}
|
||||
|
||||
MessageManager.prototype.confirmMsg = function(msgId) {
|
||||
this._setMsgStatus(msgId, 'confirmed')
|
||||
}
|
||||
|
||||
MessageManager.prototype.rejectMsg = function(msgId) {
|
||||
this._setMsgStatus(msgId, 'rejected')
|
||||
}
|
||||
|
||||
MessageManager.prototype._setMsgStatus = function(msgId, status) {
|
||||
var msg = this.getMsg(msgId)
|
||||
if (msg) msg.status = status
|
||||
this.updateMsg(msg)
|
||||
}
|
||||
|
||||
MessageManager.prototype.updateMsg = function(msg) {
|
||||
var messages = this.getMsgList()
|
||||
var found, index
|
||||
messages.forEach((otherMsg, i) => {
|
||||
if (otherMsg.id === msg.id) {
|
||||
found = true
|
||||
index = i
|
||||
}
|
||||
})
|
||||
if (found) {
|
||||
messages[index] = msg
|
||||
}
|
||||
this._saveMsgList(messages)
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ function mapStateToProps(state) {
|
||||
return {
|
||||
identities: state.metamask.identities,
|
||||
accounts: state.metamask.accounts,
|
||||
address: state.appState.currentView.context,
|
||||
address: state.metamask.selectedAccount,
|
||||
accountDetail: state.appState.accountDetail,
|
||||
transactions: state.metamask.transactions,
|
||||
networkVersion: state.metamask.network,
|
||||
@ -27,6 +27,7 @@ function AccountDetailScreen() {
|
||||
|
||||
AccountDetailScreen.prototype.render = function() {
|
||||
var state = this.props
|
||||
var selected = state.address || Object.keys(state.accounts[0]).address
|
||||
var identity = state.identities[state.address]
|
||||
var account = state.accounts[state.address]
|
||||
var accountDetail = state.accountDetail
|
||||
|
@ -42,6 +42,7 @@ var actions = {
|
||||
SHOW_ACCOUNT_DETAIL: 'SHOW_ACCOUNT_DETAIL',
|
||||
SHOW_ACCOUNTS_PAGE: 'SHOW_ACCOUNTS_PAGE',
|
||||
SHOW_CONF_TX_PAGE: 'SHOW_CONF_TX_PAGE',
|
||||
SHOW_CONF_MSG_PAGE: 'SHOW_CONF_MSG_PAGE',
|
||||
// account detail screen
|
||||
SHOW_SEND_PAGE: 'SHOW_SEND_PAGE',
|
||||
showSendPage: showSendPage,
|
||||
@ -57,7 +58,8 @@ var actions = {
|
||||
NEXT_TX: 'NEXT_TX',
|
||||
PREVIOUS_TX: 'PREV_TX',
|
||||
setSelectedAddress: setSelectedAddress,
|
||||
signTx: signTx,
|
||||
signMsg: signMsg,
|
||||
cancelMsg: cancelMsg,
|
||||
sendTx: sendTx,
|
||||
cancelTx: cancelTx,
|
||||
completedTx: completedTx,
|
||||
@ -111,7 +113,6 @@ function tryUnlockMetamask(password) {
|
||||
dispatch(this.unlockFailed())
|
||||
} else {
|
||||
dispatch(this.unlockMetamask())
|
||||
dispatch(this.showAccountDetail(selectedAccount))
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -152,16 +153,15 @@ function setSelectedAddress(address) {
|
||||
}
|
||||
}
|
||||
|
||||
function signTx(txData) {
|
||||
function signMsg(msgData) {
|
||||
return (dispatch) => {
|
||||
dispatch(this.showLoadingIndication())
|
||||
|
||||
web3.eth.sendTransaction(txData, (err, data) => {
|
||||
_accountManager.signMessage(msgData, (err) => {
|
||||
dispatch(this.hideLoadingIndication())
|
||||
|
||||
if (err) return dispatch(this.displayWarning(err.message))
|
||||
dispatch(this.hideWarning())
|
||||
dispatch(this.goHome())
|
||||
dispatch(this.completedTx(msgData.metamaskId))
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -193,9 +193,14 @@ function txError(err) {
|
||||
}
|
||||
}
|
||||
|
||||
function cancelMsg(msgData){
|
||||
_accountManager.cancelMessage(msgData.id)
|
||||
return this.completedTx(msgData.id)
|
||||
}
|
||||
|
||||
function cancelTx(txData){
|
||||
_accountManager.cancelTransaction(txData.id)
|
||||
return this.goHome()
|
||||
return this.completedTx(txData.id)
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -23,6 +23,7 @@ const ConfirmTxScreen = require('./conf-tx')
|
||||
const ConfigScreen = require('./config')
|
||||
const InfoScreen = require('./info')
|
||||
const LoadingIndicator = require('./loading')
|
||||
const txHelper = require('../lib/tx-helper')
|
||||
|
||||
module.exports = connect(mapStateToProps)(App)
|
||||
|
||||
@ -39,6 +40,8 @@ function mapStateToProps(state) {
|
||||
activeAddress: state.appState.activeAddress,
|
||||
transForward: state.appState.transForward,
|
||||
seedWords: state.metamask.seedWords,
|
||||
unconfTxs: state.metamask.unconfTxs,
|
||||
unconfMsgs: state.metamask.unconfMsgs,
|
||||
}
|
||||
}
|
||||
|
||||
@ -202,8 +205,20 @@ App.prototype.renderPrimary = function(state){
|
||||
return h(CreateVaultScreen, {key: 'createVault'})
|
||||
|
||||
default:
|
||||
return h(AccountsScreen, {key: 'accounts'})
|
||||
}
|
||||
if (this.hasPendingTxs()) {
|
||||
return h(ConfirmTxScreen, {key: 'confirm-tx'})
|
||||
} else {
|
||||
return h(AccountDetailScreen, {key: 'account-detail'})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
App.prototype.hasPendingTxs = function() {
|
||||
var state = this.props
|
||||
var unconfTxs = state.unconfTxs
|
||||
var unconfMsgs = state.unconfMsgs
|
||||
var unconfTxList = txHelper(unconfTxs, unconfMsgs)
|
||||
return unconfTxList.length > 0
|
||||
}
|
||||
|
||||
function onOffToggle(state){
|
||||
|
72
ui/app/components/pending-msg.js
Normal file
72
ui/app/components/pending-msg.js
Normal file
@ -0,0 +1,72 @@
|
||||
const Component = require('react').Component
|
||||
const h = require('react-hyperscript')
|
||||
const inherits = require('util').inherits
|
||||
|
||||
const AccountPanel = require('./account-panel')
|
||||
const addressSummary = require('../util').addressSummary
|
||||
const readableDate = require('../util').readableDate
|
||||
const formatBalance = require('../util').formatBalance
|
||||
const dataSize = require('../util').dataSize
|
||||
|
||||
module.exports = PendingMsg
|
||||
|
||||
|
||||
inherits(PendingMsg, Component)
|
||||
function PendingMsg() {
|
||||
Component.call(this)
|
||||
}
|
||||
|
||||
PendingMsg.prototype.render = function() {
|
||||
var state = this.props
|
||||
var msgData = state.txData
|
||||
|
||||
var msgParams = msgData.msgParams || {}
|
||||
var address = msgParams.from || state.selectedAddress
|
||||
var identity = state.identities[address] || { address: address }
|
||||
var account = state.accounts[address] || { address: address }
|
||||
|
||||
return (
|
||||
h('.transaction', {
|
||||
key: msgData.id,
|
||||
}, [
|
||||
|
||||
h('h3', {
|
||||
style: {
|
||||
fontWeight: 'bold',
|
||||
textAlign: 'center',
|
||||
}
|
||||
}, 'Sign Message'),
|
||||
|
||||
// account that will sign
|
||||
h(AccountPanel, {
|
||||
showFullAddress: true,
|
||||
identity: identity,
|
||||
account: account,
|
||||
}),
|
||||
|
||||
// tx data
|
||||
h('.tx-data.flex-column.flex-justify-center.flex-grow.select-none', [
|
||||
h('.flex-row.flex-space-between', [
|
||||
h('label.font-small', 'DATE'),
|
||||
h('span.font-small', readableDate(msgData.time)),
|
||||
]),
|
||||
|
||||
h('.flex-row.flex-space-between', [
|
||||
h('label.font-small', 'MESSAGE'),
|
||||
h('span.font-small', msgParams.data),
|
||||
]),
|
||||
]),
|
||||
|
||||
// send + cancel
|
||||
h('.flex-row.flex-space-around', [
|
||||
h('button', {
|
||||
onClick: state.cancelMessage,
|
||||
}, 'Cancel'),
|
||||
h('button', {
|
||||
onClick: state.signMessage,
|
||||
}, 'Sign'),
|
||||
]),
|
||||
])
|
||||
)
|
||||
}
|
||||
|
78
ui/app/components/pending-tx.js
Normal file
78
ui/app/components/pending-tx.js
Normal file
@ -0,0 +1,78 @@
|
||||
const Component = require('react').Component
|
||||
const h = require('react-hyperscript')
|
||||
const inherits = require('util').inherits
|
||||
|
||||
const AccountPanel = require('./account-panel')
|
||||
const addressSummary = require('../util').addressSummary
|
||||
const readableDate = require('../util').readableDate
|
||||
const formatBalance = require('../util').formatBalance
|
||||
const dataSize = require('../util').dataSize
|
||||
|
||||
module.exports = PendingTx
|
||||
|
||||
|
||||
inherits(PendingTx, Component)
|
||||
function PendingTx() {
|
||||
Component.call(this)
|
||||
}
|
||||
|
||||
PendingTx.prototype.render = function() {
|
||||
var state = this.props
|
||||
var txData = state.txData
|
||||
|
||||
var txParams = txData.txParams || {}
|
||||
var address = txParams.from || state.selectedAddress
|
||||
var identity = state.identities[address] || { address: address }
|
||||
var account = state.accounts[address] || { address: address }
|
||||
|
||||
return (
|
||||
h('.transaction', {
|
||||
key: txData.id,
|
||||
}, [
|
||||
|
||||
h('h3', {
|
||||
style: {
|
||||
fontWeight: 'bold',
|
||||
textAlign: 'center',
|
||||
}
|
||||
}, 'Submit Transaction'),
|
||||
|
||||
// account that will sign
|
||||
h(AccountPanel, {
|
||||
showFullAddress: true,
|
||||
identity: identity,
|
||||
account: account,
|
||||
}),
|
||||
|
||||
// tx data
|
||||
h('.tx-data.flex-column.flex-justify-center.flex-grow.select-none', [
|
||||
|
||||
h('.flex-row.flex-space-between', [
|
||||
h('label.font-small', 'TO ADDRESS'),
|
||||
h('span.font-small', addressSummary(txParams.to)),
|
||||
]),
|
||||
|
||||
h('.flex-row.flex-space-between', [
|
||||
h('label.font-small', 'DATE'),
|
||||
h('span.font-small', readableDate(txData.time)),
|
||||
]),
|
||||
|
||||
h('.flex-row.flex-space-between', [
|
||||
h('label.font-small', 'AMOUNT'),
|
||||
h('span.font-small', formatBalance(txParams.value)),
|
||||
]),
|
||||
]),
|
||||
|
||||
// send + cancel
|
||||
h('.flex-row.flex-space-around', [
|
||||
h('button', {
|
||||
onClick: state.cancelTransaction,
|
||||
}, 'Cancel'),
|
||||
h('button', {
|
||||
onClick: state.sendTransaction,
|
||||
}, 'Send'),
|
||||
]),
|
||||
])
|
||||
)
|
||||
}
|
||||
|
19
ui/app/components/template.js
Normal file
19
ui/app/components/template.js
Normal file
@ -0,0 +1,19 @@
|
||||
const Component = require('react').Component
|
||||
const h = require('react-hyperscript')
|
||||
const inherits = require('util').inherits
|
||||
|
||||
module.exports = NewComponent
|
||||
|
||||
|
||||
inherits(NewComponent, Component)
|
||||
function NewComponent() {
|
||||
Component.call(this)
|
||||
}
|
||||
|
||||
NewComponent.prototype.render = function() {
|
||||
var state = this.props
|
||||
|
||||
return (
|
||||
h('span', 'Placeholder component')
|
||||
)
|
||||
}
|
@ -7,10 +7,10 @@ const copyToClipboard = require('copy-to-clipboard')
|
||||
const actions = require('./actions')
|
||||
const AccountPanel = require('./components/account-panel')
|
||||
const valuesFor = require('./util').valuesFor
|
||||
const addressSummary = require('./util').addressSummary
|
||||
const readableDate = require('./util').readableDate
|
||||
const formatBalance = require('./util').formatBalance
|
||||
const dataSize = require('./util').dataSize
|
||||
const txHelper = require('../lib/tx-helper')
|
||||
|
||||
const ConfirmTx = require('./components/pending-tx')
|
||||
const PendingMsg = require('./components/pending-msg')
|
||||
|
||||
module.exports = connect(mapStateToProps)(ConfirmTxScreen)
|
||||
|
||||
@ -20,7 +20,9 @@ function mapStateToProps(state) {
|
||||
accounts: state.metamask.accounts,
|
||||
selectedAddress: state.metamask.selectedAddress,
|
||||
unconfTxs: state.metamask.unconfTxs,
|
||||
unconfMsgs: state.metamask.unconfMsgs,
|
||||
index: state.appState.currentView.context,
|
||||
warning: state.appState.warning,
|
||||
}
|
||||
}
|
||||
|
||||
@ -32,12 +34,12 @@ function ConfirmTxScreen() {
|
||||
|
||||
ConfirmTxScreen.prototype.render = function() {
|
||||
var state = this.props
|
||||
var unconfTxList = valuesFor(state.unconfTxs).sort(tx => tx.time)
|
||||
var txData = unconfTxList[state.index] || {}
|
||||
var txParams = txData.txParams || {}
|
||||
var address = txParams.from || state.selectedAddress
|
||||
var identity = state.identities[address] || { address: address }
|
||||
var account = state.accounts[address] || { address: address }
|
||||
|
||||
var unconfTxs = state.unconfTxs
|
||||
var unconfMsgs = state.unconfMsgs
|
||||
var unconfTxList = txHelper(unconfTxs, unconfMsgs)
|
||||
var index = state.index !== undefined ? state.index : 0
|
||||
var txData = unconfTxList[index] || {}
|
||||
|
||||
return (
|
||||
|
||||
@ -46,9 +48,9 @@ ConfirmTxScreen.prototype.render = function() {
|
||||
// subtitle and nav
|
||||
h('.section-title.flex-row.flex-center', [
|
||||
h('i.fa.fa-arrow-left.fa-lg.cursor-pointer', {
|
||||
onClick: this.navigateToAccounts.bind(this),
|
||||
onClick: this.goHome.bind(this),
|
||||
}),
|
||||
h('h2.page-subtitle', 'Confirm Transaction'),
|
||||
h('h2.page-subtitle', 'Confirmation'),
|
||||
]),
|
||||
|
||||
h('h3', {
|
||||
@ -63,7 +65,7 @@ ConfirmTxScreen.prototype.render = function() {
|
||||
},
|
||||
onClick: () => state.dispatch(actions.previousTx()),
|
||||
}),
|
||||
` Transaction ${state.index + 1} of ${unconfTxList.length} `,
|
||||
` ${state.index + 1} of ${unconfTxList.length} `,
|
||||
h('i.fa.fa-arrow-right.fa-lg.cursor-pointer', {
|
||||
style: {
|
||||
display: state.index + 1 === unconfTxList.length ? 'none' : 'inline-block',
|
||||
@ -72,58 +74,44 @@ ConfirmTxScreen.prototype.render = function() {
|
||||
}),
|
||||
]),
|
||||
|
||||
warningIfExists(state.warning),
|
||||
|
||||
h(ReactCSSTransitionGroup, {
|
||||
transitionName: "main",
|
||||
transitionEnterTimeout: 300,
|
||||
transitionLeaveTimeout: 300,
|
||||
}, [
|
||||
|
||||
h('.transaction', {
|
||||
currentTxView({
|
||||
// Properties
|
||||
txData: txData,
|
||||
key: txData.id,
|
||||
}, [
|
||||
selectedAddress: state.selectedAddress,
|
||||
accounts: state.accounts,
|
||||
identities: state.identities,
|
||||
// Actions
|
||||
sendTransaction: this.sendTransaction.bind(this, txData),
|
||||
cancelTransaction: this.cancelTransaction.bind(this, txData),
|
||||
signMessage: this.signMessage.bind(this, txData),
|
||||
cancelMessage: this.cancelMessage.bind(this, txData),
|
||||
}),
|
||||
|
||||
// account that will sign
|
||||
h(AccountPanel, {
|
||||
showFullAddress: true,
|
||||
identity: identity,
|
||||
account: account,
|
||||
}),
|
||||
|
||||
// tx data
|
||||
h('.tx-data.flex-column.flex-justify-center.flex-grow.select-none', [
|
||||
|
||||
h('.flex-row.flex-space-between', [
|
||||
h('label.font-small', 'TO ADDRESS'),
|
||||
h('span.font-small', addressSummary(txParams.to)),
|
||||
]),
|
||||
|
||||
h('.flex-row.flex-space-between', [
|
||||
h('label.font-small', 'DATE'),
|
||||
h('span.font-small', readableDate(txData.time)),
|
||||
]),
|
||||
|
||||
h('.flex-row.flex-space-between', [
|
||||
h('label.font-small', 'AMOUNT'),
|
||||
h('span.font-small', formatBalance(txParams.value)),
|
||||
]),
|
||||
|
||||
]),
|
||||
|
||||
// send + cancel
|
||||
h('.flex-row.flex-space-around', [
|
||||
h('button', {
|
||||
onClick: this.cancelTransaction.bind(this, txData),
|
||||
}, 'Cancel'),
|
||||
h('button', {
|
||||
onClick: this.sendTransaction.bind(this, txData),
|
||||
}, 'Send'),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
]) // No comma or semicolon can go here
|
||||
])
|
||||
)
|
||||
}
|
||||
|
||||
function currentTxView (opts) {
|
||||
|
||||
if ('txParams' in opts.txData) {
|
||||
// This is a pending transaction
|
||||
return h(ConfirmTx, opts)
|
||||
} else if ('msgParams' in opts.txData) {
|
||||
// This is a pending message to sign
|
||||
return h(PendingMsg, opts)
|
||||
}
|
||||
}
|
||||
|
||||
ConfirmTxScreen.prototype.sendTransaction = function(txData, event){
|
||||
event.stopPropagation()
|
||||
this.props.dispatch(actions.sendTx(txData))
|
||||
@ -134,7 +122,25 @@ ConfirmTxScreen.prototype.cancelTransaction = function(txData, event){
|
||||
this.props.dispatch(actions.cancelTx(txData))
|
||||
}
|
||||
|
||||
ConfirmTxScreen.prototype.navigateToAccounts = function(event){
|
||||
ConfirmTxScreen.prototype.signMessage = function(msgData, event){
|
||||
var params = msgData.msgParams
|
||||
params.metamaskId = msgData.id
|
||||
event.stopPropagation()
|
||||
this.props.dispatch(actions.showAccountsPage())
|
||||
this.props.dispatch(actions.signMsg(params))
|
||||
}
|
||||
|
||||
ConfirmTxScreen.prototype.cancelMessage = function(msgData, event){
|
||||
event.stopPropagation()
|
||||
this.props.dispatch(actions.cancelMsg(msgData))
|
||||
}
|
||||
|
||||
ConfirmTxScreen.prototype.goHome = function(event){
|
||||
event.stopPropagation()
|
||||
this.props.dispatch(actions.goHome())
|
||||
}
|
||||
|
||||
function warningIfExists(warning) {
|
||||
if (warning) {
|
||||
return h('span.error', { style: { margin: 'auto' } }, warning)
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
const extend = require('xtend')
|
||||
const actions = require('../actions')
|
||||
const valuesFor = require('../util').valuesFor
|
||||
const txHelper = require('../../lib/tx-helper')
|
||||
|
||||
module.exports = reduceApp
|
||||
|
||||
@ -107,6 +108,7 @@ function reduceApp(state, action) {
|
||||
|
||||
case actions.UNLOCK_METAMASK:
|
||||
return extend(appState, {
|
||||
currentView: {},
|
||||
transForward: true,
|
||||
warning: null,
|
||||
})
|
||||
@ -127,10 +129,7 @@ function reduceApp(state, action) {
|
||||
|
||||
case actions.GO_HOME:
|
||||
return extend(appState, {
|
||||
currentView: {
|
||||
name: 'accountDetail',
|
||||
context: appState.currentView.context,
|
||||
},
|
||||
currentView: {},
|
||||
accountDetail: {
|
||||
accountExport: 'none',
|
||||
privateKey: '',
|
||||
@ -185,9 +184,24 @@ function reduceApp(state, action) {
|
||||
warning: null,
|
||||
})
|
||||
|
||||
case actions.SHOW_CONF_MSG_PAGE:
|
||||
return extend(appState, {
|
||||
currentView: {
|
||||
name: 'confTx',
|
||||
context: 0,
|
||||
},
|
||||
transForward: true,
|
||||
warning: null,
|
||||
})
|
||||
|
||||
case actions.COMPLETED_TX:
|
||||
var unconfTxs = Object.keys(state.metamask.unconfTxs).filter(tx => tx !== tx.id)
|
||||
if (unconfTxs && unconfTxs.length > 0) {
|
||||
var unconfTxs = state.metamask.unconfTxs
|
||||
var unconfMsgs = state.metamask.unconfMsgs
|
||||
|
||||
var unconfTxList = txHelper(unconfTxs, unconfMsgs)
|
||||
.filter(tx => tx !== tx.id)
|
||||
|
||||
if (unconfTxList && unconfTxList.length > 0) {
|
||||
return extend(appState, {
|
||||
transForward: false,
|
||||
currentView: {
|
||||
@ -202,7 +216,7 @@ function reduceApp(state, action) {
|
||||
warning: null,
|
||||
currentView: {
|
||||
name: 'accountDetail',
|
||||
context: appState.currentView.context,
|
||||
context: state.metamask.selectedAddress,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
@ -44,13 +44,19 @@ function reduceMetamask(state, action) {
|
||||
case actions.COMPLETED_TX:
|
||||
var stringId = String(action.id)
|
||||
var newState = extend(metamaskState, {
|
||||
unconfTxs: {}
|
||||
unconfTxs: {},
|
||||
unconfMsgs: {},
|
||||
})
|
||||
for (var id in metamaskState.unconfTxs) {
|
||||
if (id !== stringId) {
|
||||
newState.unconfTxs[id] = metamaskState.unconfTxs[id]
|
||||
}
|
||||
}
|
||||
for (var id in metamaskState.unconfMsgs) {
|
||||
if (id !== stringId) {
|
||||
newState.unconfMsgs[id] = metamaskState.unconfMsgs[id]
|
||||
}
|
||||
}
|
||||
return newState
|
||||
|
||||
case actions.CLEAR_SEED_WORD_CACHE:
|
||||
|
@ -43,6 +43,11 @@ function startApp(metamaskState, accountManager, opts){
|
||||
store.dispatch(actions.showConfTxPage())
|
||||
}
|
||||
|
||||
// if unconfirmed messages, start on msgConf page
|
||||
if (Object.keys(metamaskState.unconfMsgs || {}).length) {
|
||||
store.dispatch(actions.showConfTxPage())
|
||||
}
|
||||
|
||||
accountManager.on('update', function(metamaskState){
|
||||
store.dispatch(actions.updateMetamaskState(metamaskState))
|
||||
})
|
||||
|
8
ui/lib/tx-helper.js
Normal file
8
ui/lib/tx-helper.js
Normal file
@ -0,0 +1,8 @@
|
||||
const valuesFor = require('../app/util').valuesFor
|
||||
|
||||
module.exports = function(unconfTxs, unconfMsgs) {
|
||||
var txValues = valuesFor(unconfTxs)
|
||||
var msgValues = valuesFor(unconfMsgs)
|
||||
var allValues = txValues.concat(msgValues)
|
||||
return allValues.sort(tx => tx.time)
|
||||
}
|
Loading…
Reference in New Issue
Block a user