From 4910e2f9bdf1305469edd9c55c59fb90d0e5267d Mon Sep 17 00:00:00 2001 From: Frances Pangilinan Date: Tue, 20 Dec 2016 13:49:22 -0800 Subject: [PATCH 01/43] remove network Id 2 from explorer link and Add network Id 3 --- test/unit/explorer-link-test.js | 2 +- ui/app/components/transaction-list-item.js | 3 +-- ui/lib/explorer-link.js | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/test/unit/explorer-link-test.js b/test/unit/explorer-link-test.js index 961b400fd..8aa58bff9 100644 --- a/test/unit/explorer-link-test.js +++ b/test/unit/explorer-link-test.js @@ -4,7 +4,7 @@ var linkGen = require('../../ui/lib/explorer-link') describe('explorer-link', function() { it('adds testnet prefix to morden test network', function() { - var result = linkGen('hash', '2') + var result = linkGen('hash', '3') assert.notEqual(result.indexOf('testnet'), -1, 'testnet injected') }) diff --git a/ui/app/components/transaction-list-item.js b/ui/app/components/transaction-list-item.js index 491e90c7c..f92a4ab2e 100644 --- a/ui/app/components/transaction-list-item.js +++ b/ui/app/components/transaction-list-item.js @@ -27,7 +27,7 @@ TransactionListItem.prototype.render = function () { let isLinkable = false const numericNet = parseInt(network) - isLinkable = numericNet === 1 || numericNet === 2 + isLinkable = numericNet === 1 || numericNet === 3 var isMsg = ('msgParams' in transaction) var isTx = ('txParams' in transaction) @@ -41,7 +41,6 @@ TransactionListItem.prototype.render = function () { } const isClickable = ('hash' in transaction && isLinkable) || isPending - return ( h(`.transaction-list-item.flex-row.flex-space-between${isClickable ? '.pointer' : ''}`, { onClick: (event) => { diff --git a/ui/lib/explorer-link.js b/ui/lib/explorer-link.js index 2993d1cf1..dc6be2984 100644 --- a/ui/lib/explorer-link.js +++ b/ui/lib/explorer-link.js @@ -5,7 +5,7 @@ module.exports = function (hash, network) { case 1: // main net prefix = '' break - case 2: // morden test net + case 3: // morden test net prefix = 'testnet.' break default: From 10d9cf0912d1b4c287fd7ca3b3be393ea75eae80 Mon Sep 17 00:00:00 2001 From: Frances Pangilinan Date: Tue, 20 Dec 2016 13:52:51 -0800 Subject: [PATCH 02/43] Add to CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0fd1e83bf..44c841821 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Current Master +- Removed network Id 2 from the explorer links and added network Id 3 - Temporarily disable extension reload detection causing infinite reload bug. ## 2.14.0 2016-12-16 From 05e62cbc17889f1020c5ec93456b799bb4ad2b4c Mon Sep 17 00:00:00 2001 From: Frankie Date: Tue, 20 Dec 2016 14:11:00 -0800 Subject: [PATCH 03/43] make CHANGELOG more human readable --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 44c841821..70ee9add3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ## Current Master -- Removed network Id 2 from the explorer links and added network Id 3 +- Fixed ropsten transaction links - Temporarily disable extension reload detection causing infinite reload bug. ## 2.14.0 2016-12-16 From 29060acb722c855344b02416754ab7513be44493 Mon Sep 17 00:00:00 2001 From: Frankie Date: Tue, 10 Jan 2017 12:18:39 -0800 Subject: [PATCH 04/43] update coinbase info and view --- ui/app/components/coinbase-form.js | 8 ++++---- ui/app/reducers/app.js | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ui/app/components/coinbase-form.js b/ui/app/components/coinbase-form.js index 3c5708bf8..32e3066fd 100644 --- a/ui/app/components/coinbase-form.js +++ b/ui/app/components/coinbase-form.js @@ -72,7 +72,7 @@ CoinbaseForm.prototype.render = function () { lineHeight: '13px', }, }, - `there is a USD$ 5 a day max and a USD$ 50 + `there is a USD$ 15 a day max and a USD$ 50 dollar limit per the life time of an account without a coinbase account. A fee of 3.75% will be aplied to debit/credit cards.`), @@ -138,14 +138,14 @@ function isValidAmountforCoinBase(amount) { amount = parseFloat(amount) if (amount) { - if (amount <= 5 && amount > 0) { + if (amount <= 15 && amount > 0) { return { valid: true, } - } else if (amount > 5) { + } else if (amount > 15) { return { valid: false, - message: 'The amount can not be greater then $5', + message: 'The amount can not be greater then $15', } } else { return { diff --git a/ui/app/reducers/app.js b/ui/app/reducers/app.js index 5c5c0acce..31b9e109f 100644 --- a/ui/app/reducers/app.js +++ b/ui/app/reducers/app.js @@ -418,7 +418,7 @@ function reduceApp (state, action) { }, buyView: { subview: 'buyForm', - amount: '5.00', + amount: '15.00', buyAddress: action.value, formView: { coinbase: true, From efde16aae506158d269f70b53b74b09eefd3eff8 Mon Sep 17 00:00:00 2001 From: Frankie Date: Tue, 10 Jan 2017 12:19:02 -0800 Subject: [PATCH 05/43] add to CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 70ee9add3..807f03aa6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Current Master +- Update Coinbase info. and increase the buy amount to $15 - Fixed ropsten transaction links - Temporarily disable extension reload detection causing infinite reload bug. From 0fae263a9acb1f4023070b37ee1b91815e34de86 Mon Sep 17 00:00:00 2001 From: Frankie Date: Tue, 10 Jan 2017 11:52:25 -0800 Subject: [PATCH 06/43] Take some of the tx Logic out of the UI and create a visble state for pending and unaproved transactions --- app/scripts/background.js | 1 - app/scripts/keyring-controller.js | 2 +- app/scripts/metamask-controller.js | 1 + app/scripts/transaction-manager.js | 33 +++++++++++++++---- ui/app/account-detail.js | 16 ++------- ui/app/components/pending-tx-details.js | 12 ++----- .../components/transaction-list-item-icon.js | 18 +++++++++- ui/app/components/transaction-list-item.js | 18 ++++++---- ui/app/components/transaction-list.js | 9 ++--- ui/app/conf-tx.js | 12 ++----- 10 files changed, 68 insertions(+), 54 deletions(-) diff --git a/app/scripts/background.js b/app/scripts/background.js index 6b7926526..3f15488ee 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -27,7 +27,6 @@ function triggerUi () { if (!popupIsOpen) notification.show() } // On first install, open a window to MetaMask website to how-it-works. - extension.runtime.onInstalled.addListener(function (details) { if ((details.reason === 'install') && (!METAMASK_DEBUG)) { extension.tabs.create({url: 'https://metamask.io/#how-it-works'}) diff --git a/app/scripts/keyring-controller.js b/app/scripts/keyring-controller.js index c58be0aae..a457a2560 100644 --- a/app/scripts/keyring-controller.js +++ b/app/scripts/keyring-controller.js @@ -95,7 +95,6 @@ module.exports = class KeyringController extends EventEmitter { isInitialized: (!!wallet || !!vault), isUnlocked: Boolean(this.password), isDisclaimerConfirmed: this.configManager.getConfirmedDisclaimer(), - transactions: this.configManager.getTxList(), unconfMsgs: messageManager.unconfirmedMsgs(), messages: messageManager.getMsgList(), selectedAccount: address, @@ -273,6 +272,7 @@ module.exports = class KeyringController extends EventEmitter { setSelectedAccount (address) { var addr = normalize(address) this.configManager.setSelectedAccount(addr) + this.emit('update') return Promise.resolve(addr) } diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 555460f3d..ae7aee9e3 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -64,6 +64,7 @@ module.exports = class MetamaskController extends EventEmitter { this.ethStore.on('update', this.sendUpdate.bind(this)) this.keyringController.on('update', this.sendUpdate.bind(this)) + this.txManager.on('update', this.sendUpdate.bind(this)) } getState () { diff --git a/app/scripts/transaction-manager.js b/app/scripts/transaction-manager.js index 6becfa6d1..a279ba23a 100644 --- a/app/scripts/transaction-manager.js +++ b/app/scripts/transaction-manager.js @@ -25,9 +25,8 @@ module.exports = class TransactionManager extends EventEmitter { getState () { var selectedAccount = this.getSelectedAccount() return { - transactions: this.getTxList(), unconfTxs: this.getUnapprovedTxList(), - selectedAccountTxList: this.getFilteredTxList({metamaskNetworkId: this.getNetwork(), from: selectedAccount}), + transactions: this.getFilteredTxList({metamaskNetworkId: this.getNetwork(), from: selectedAccount}), } } @@ -113,10 +112,26 @@ module.exports = class TransactionManager extends EventEmitter { txDidComplete (txMeta, onTxDoneCb, cb, err) { if (err) return cb(err) + var {maxCost, txFee} = this.getMaxTxCostAndFee(txMeta) + txMeta.maxCost = maxCost + txMeta.txFee = txFee this.addTx(txMeta, onTxDoneCb) cb(null, txMeta) } + getMaxTxCostAndFee (txMeta) { + var txParams = txMeta.txParams + + var gasMultiplier = txMeta.gasMultiplier + var gasCost = new BN(ethUtil.stripHexPrefix(txParams.gas || txMeta.estimatedGas), 16) + var gasPrice = new BN(ethUtil.stripHexPrefix(txParams.gasPrice || '0x4a817c800'), 16) + gasPrice = gasPrice.mul(new BN(gasMultiplier * 100), 10).div(new BN(100, 10)) + var txFee = gasCost.mul(gasPrice) + var txValue = new BN(ethUtil.stripHexPrefix(txParams.value || '0x0'), 16) + var maxCost = txValue.add(txFee) + return {maxCost, txFee} + } + getUnapprovedTxList () { var txList = this.getTxList() return txList.filter((txMeta) => txMeta.status === 'unapproved') @@ -227,6 +242,7 @@ module.exports = class TransactionManager extends EventEmitter { setTxStatusConfirmed (txId) { this._setTxStatus(txId, 'confirmed') + this.emit('update') } // merges txParams obj onto txData.txParams @@ -240,17 +256,20 @@ module.exports = class TransactionManager extends EventEmitter { // checks if a signed tx is in a block and // if included sets the tx status as 'confirmed' checkForTxInBlock () { - var signedTxList = this.getFilteredTxList({status: 'signed', err: undefined}) + var signedTxList = this.getFilteredTxList({status: 'signed'}) if (!signedTxList.length) return signedTxList.forEach((tx) => { var txHash = tx.hash var txId = tx.id - if (!txHash) return + if (!txHash) { + tx.err = { errCode: 'No hash was provided', message: 'Tx could possibly have not been submitted or an error accrued during signing'} + return this.updateTx(tx) + } this.txProviderUtils.query.getTransactionByHash(txHash, (err, txMeta) => { - if (err || !txMeta) { - tx.err = err || 'Tx could possibly have not been submitted' + if (err) { + tx.err = {errorCode: err, message: 'Tx could possibly have not been submitted to the block chain',} this.updateTx(tx) - return txMeta ? console.error(err) : console.debug(`txMeta is ${txMeta} for:`, tx) + return console.error(err) } if (txMeta.blockNumber) { this.setTxStatusConfirmed(txId) diff --git a/ui/app/account-detail.js b/ui/app/account-detail.js index c41ba61fd..cfc59d99b 100644 --- a/ui/app/account-detail.js +++ b/ui/app/account-detail.js @@ -26,11 +26,10 @@ function mapStateToProps (state) { accounts: state.metamask.accounts, address: state.metamask.selectedAccount, accountDetail: state.appState.accountDetail, - transactions: state.metamask.transactions, network: state.metamask.network, - unconfTxs: valuesFor(state.metamask.unconfTxs), unconfMsgs: valuesFor(state.metamask.unconfMsgs), shapeShiftTxList: state.metamask.shapeShiftTxList, + transactions: state.metamask.transactions, } } @@ -248,20 +247,11 @@ AccountDetailScreen.prototype.subview = function () { } AccountDetailScreen.prototype.transactionList = function () { - const { transactions, unconfTxs, unconfMsgs, address, network, shapeShiftTxList } = this.props - - var txsToRender = transactions.concat(unconfTxs) - // only transactions that are from the current address - .filter(tx => tx.txParams.from === address) - // only transactions that are on the current network - .filter(tx => tx.txParams.metamaskNetworkId === network) + const {transactions, unconfMsgs, address, network, shapeShiftTxList } = this.props // sort by recency - .sort((a, b) => b.time - a.time) - return h(TransactionList, { - txsToRender, + transactions, network, - unconfTxs, unconfMsgs, address, shapeShiftTxList, diff --git a/ui/app/components/pending-tx-details.js b/ui/app/components/pending-tx-details.js index 89472b221..c40cd01b1 100644 --- a/ui/app/components/pending-tx-details.js +++ b/ui/app/components/pending-tx-details.js @@ -7,8 +7,6 @@ const EthBalance = require('./eth-balance') const util = require('../util') const addressSummary = util.addressSummary const nameForAddress = require('../../lib/contract-namer') -const ethUtil = require('ethereumjs-util') -const BN = ethUtil.BN module.exports = PendingTxDetails @@ -29,15 +27,9 @@ PTXP.render = function () { var account = props.accounts[address] var balance = account ? account.balance : '0x0' - var gasMultiplier = txData.gasMultiplier - var gasCost = new BN(ethUtil.stripHexPrefix(txParams.gas || txData.estimatedGas), 16) - var gasPrice = new BN(ethUtil.stripHexPrefix(txParams.gasPrice || '0x4a817c800'), 16) - gasPrice = gasPrice.mul(new BN(gasMultiplier * 100), 10).div(new BN(100, 10)) - var txFee = gasCost.mul(gasPrice) - var txValue = new BN(ethUtil.stripHexPrefix(txParams.value || '0x0'), 16) - var maxCost = txValue.add(txFee) + var txFee = txData.txFee + var maxCost = txData.maxCost var dataLength = txParams.data ? (txParams.data.length - 2) / 2 : 0 - var imageify = props.imageifyIdenticons === undefined ? true : props.imageifyIdenticons return ( diff --git a/ui/app/components/transaction-list-item-icon.js b/ui/app/components/transaction-list-item-icon.js index 8b118b1d4..58aa733f1 100644 --- a/ui/app/components/transaction-list-item-icon.js +++ b/ui/app/components/transaction-list-item-icon.js @@ -13,15 +13,31 @@ function TransactionIcon () { TransactionIcon.prototype.render = function () { const { transaction, txParams, isMsg } = this.props + if (transaction.status === 'unapproved') { + return h('.unapproved-tx', { + style: { + width: '15px', + height: '15px', + background: '#00bfff', + borderRadius: '13px', + }, + }) - if (transaction.status === 'rejected') { + } else if (transaction.status === 'rejected') { return h('i.fa.fa-exclamation-triangle.fa-lg.warning', { style: { width: '24px', }, }) + } else if (transaction.status === 'signed') { + return h('i.fa.fa-ellipsis-h', { + style: { + fontSize: '27px', + }, + }) } + if (isMsg) { return h('i.fa.fa-certificate.fa-lg', { style: { diff --git a/ui/app/components/transaction-list-item.js b/ui/app/components/transaction-list-item.js index bb685abda..bcd50c333 100644 --- a/ui/app/components/transaction-list-item.js +++ b/ui/app/components/transaction-list-item.js @@ -8,6 +8,7 @@ const explorerLink = require('../../lib/explorer-link') const CopyButton = require('./copyButton') const vreme = new (require('vreme')) const extension = require('../../../app/scripts/lib/extension') +const Tooltip = require('./tooltip') const TransactionIcon = require('./transaction-list-item-icon') const ShiftListItem = require('./shift-list-item') @@ -59,11 +60,7 @@ TransactionListItem.prototype.render = function () { }, [ h('.identicon-wrapper.flex-column.flex-center.select-none', [ - transaction.status === 'unapproved' ? h('i.fa.fa-ellipsis-h', { - style: { - fontSize: '27px', - }, - }) : h('.pop-hover', { + h('.pop-hover', { onClick: (event) => { event.stopPropagation() if (!isTx || isPending) return @@ -139,7 +136,14 @@ function failIfFailed (transaction) { if (transaction.status === 'rejected') { return h('span.error', ' (Rejected)') } - if (transaction.status === 'failed') { - return h('span.error', ' (Failed)') + if (transaction.err) { + + return h(Tooltip, { + title: transaction.err.message, + position: 'bottom', + }, [ + h('span.error', ' (Failed)'), + ]) } + } diff --git a/ui/app/components/transaction-list.js b/ui/app/components/transaction-list.js index 7e1bedb05..105b34c90 100644 --- a/ui/app/components/transaction-list.js +++ b/ui/app/components/transaction-list.js @@ -13,12 +13,13 @@ function TransactionList () { } TransactionList.prototype.render = function () { - const { txsToRender, network, unconfMsgs } = this.props + const { transactions = [], network, unconfMsgs } = this.props + var shapeShiftTxList if (network === '1') { shapeShiftTxList = this.props.shapeShiftTxList } - const transactions = !shapeShiftTxList ? txsToRender.concat(unconfMsgs) : txsToRender.concat(unconfMsgs, shapeShiftTxList) + const txsToRender = !shapeShiftTxList ? transactions.concat(unconfMsgs) : txsToRender.concat(unconfMsgs, shapeShiftTxList) .sort((a, b) => b.time - a.time) return ( @@ -55,8 +56,8 @@ TransactionList.prototype.render = function () { }, }, [ - transactions.length - ? transactions.map((transaction, i) => { + txsToRender.length + ? txsToRender.map((transaction, i) => { let key switch (transaction.key) { case 'shapeshift': diff --git a/ui/app/conf-tx.js b/ui/app/conf-tx.js index 5a645022a..79699965b 100644 --- a/ui/app/conf-tx.js +++ b/ui/app/conf-tx.js @@ -46,7 +46,6 @@ ConfirmTxScreen.prototype.render = function () { var txData = unconfTxList[index] || unconfTxList[0] || {} var txParams = txData.txParams || {} var isNotification = isPopupOrNotification() === 'notification' - return ( h('.flex-column.flex-grow', [ @@ -125,17 +124,10 @@ function currentTxView (opts) { } ConfirmTxScreen.prototype.checkBalanceAgainstTx = function (txData) { var state = this.props - - var txParams = txData.txParams || {} - var address = txParams.from || state.selectedAccount + var address = txData.txParams.from || state.selectedAccount var account = state.accounts[address] var balance = account ? account.balance : '0x0' - - var gasCost = new BN(ethUtil.stripHexPrefix(txParams.gas || txData.estimatedGas), 16) - var gasPrice = new BN(ethUtil.stripHexPrefix(txParams.gasPrice || '0x4a817c800'), 16) - var txFee = gasCost.mul(gasPrice) - var txValue = new BN(ethUtil.stripHexPrefix(txParams.value || '0x0'), 16) - var maxCost = txValue.add(txFee) + var maxCost = new BN(txData.maxCost) var balanceBn = new BN(ethUtil.stripHexPrefix(balance), 16) return maxCost.gt(balanceBn) From bbd2f2738b5b260f0e666b9cfb8d0c843342abb2 Mon Sep 17 00:00:00 2001 From: Frankie Date: Wed, 11 Jan 2017 12:23:00 -0800 Subject: [PATCH 07/43] Add to CHANGELOG --- CHANGELOG.md | 1 + app/scripts/transaction-manager.js | 10 ++++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f1ec6823..113d2f7cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Current Master +- Create visible difference in transaction history between a approved but not yet included in a block transaction and a transaction who has been confirmed. - Fix memory leak in RPC Cache - Override RPC commands eth_syncing and web3_clientVersion - Remove certain non-essential permissions from certain builds. diff --git a/app/scripts/transaction-manager.js b/app/scripts/transaction-manager.js index a279ba23a..f83bc41c9 100644 --- a/app/scripts/transaction-manager.js +++ b/app/scripts/transaction-manager.js @@ -262,12 +262,18 @@ module.exports = class TransactionManager extends EventEmitter { var txHash = tx.hash var txId = tx.id if (!txHash) { - tx.err = { errCode: 'No hash was provided', message: 'Tx could possibly have not been submitted or an error accrued during signing'} + tx.err = { + errCode: 'No hash was provided', + message: 'Tx could possibly have not been submitted or an error accrued during signing', + } return this.updateTx(tx) } this.txProviderUtils.query.getTransactionByHash(txHash, (err, txMeta) => { if (err) { - tx.err = {errorCode: err, message: 'Tx could possibly have not been submitted to the block chain',} + tx.err = { + errorCode: err, + message: 'Tx could possibly have not been submitted to the block chain', + } this.updateTx(tx) return console.error(err) } From d87a7b2a767def40d89138103eb53c665419cc3d Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 11 Jan 2017 14:58:20 -0800 Subject: [PATCH 08/43] Send update to UI when changing selected account Fixes #981 --- app/scripts/keyring-controller.js | 2 +- ui/app/actions.js | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/app/scripts/keyring-controller.js b/app/scripts/keyring-controller.js index c58be0aae..d4c0d863e 100644 --- a/app/scripts/keyring-controller.js +++ b/app/scripts/keyring-controller.js @@ -273,7 +273,7 @@ module.exports = class KeyringController extends EventEmitter { setSelectedAccount (address) { var addr = normalize(address) this.configManager.setSelectedAccount(addr) - return Promise.resolve(addr) + return this.fullUpdate() } // Save Account Label diff --git a/ui/app/actions.js b/ui/app/actions.js index d63d36f19..5a3968f82 100644 --- a/ui/app/actions.js +++ b/ui/app/actions.js @@ -263,9 +263,7 @@ function showInfoPage () { } function setSelectedAccount (address) { - return (dispatch) => { - background.setSelectedAccount(address) - } + return callBackgroundThenUpdate(background.setSelectedAccount, address) } function setCurrentFiat (fiat) { @@ -457,15 +455,16 @@ function lockMetamask () { function showAccountDetail (address) { return (dispatch) => { dispatch(actions.showLoadingIndication()) - background.setSelectedAccount(address, (err, address) => { + background.setSelectedAccount(address, (err, newState) => { dispatch(actions.hideLoadingIndication()) if (err) { return dispatch(actions.displayWarning(err.message)) } + dispatch(actions.updateMetamaskState(newState)) dispatch({ type: actions.SHOW_ACCOUNT_DETAIL, - value: address, + value: newState.selectedAccount, }) }) } From 576e2ad64df293adcc8c2494a3648100ba4b28f5 Mon Sep 17 00:00:00 2001 From: Frankie Date: Wed, 11 Jan 2017 15:44:21 -0800 Subject: [PATCH 09/43] Fix wording and icon of failed txs --- app/scripts/keyring-controller.js | 1 - app/scripts/transaction-manager.js | 30 ++++++++++--------- .../components/transaction-list-item-icon.js | 6 ++++ 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/app/scripts/keyring-controller.js b/app/scripts/keyring-controller.js index a457a2560..81e6a4905 100644 --- a/app/scripts/keyring-controller.js +++ b/app/scripts/keyring-controller.js @@ -272,7 +272,6 @@ module.exports = class KeyringController extends EventEmitter { setSelectedAccount (address) { var addr = normalize(address) this.configManager.setSelectedAccount(addr) - this.emit('update') return Promise.resolve(addr) } diff --git a/app/scripts/transaction-manager.js b/app/scripts/transaction-manager.js index f83bc41c9..527899835 100644 --- a/app/scripts/transaction-manager.js +++ b/app/scripts/transaction-manager.js @@ -82,6 +82,7 @@ module.exports = class TransactionManager extends EventEmitter { var index = txList.findIndex(txData => txData.id === txId) txList[index] = txMeta this._saveTxList(txList) + this.emit('update') } get unapprovedTxCount () { @@ -182,7 +183,6 @@ module.exports = class TransactionManager extends EventEmitter { this.updateTx(metaTx) var rawTx = ethUtil.bufferToHex(tx.serialize()) return Promise.resolve(rawTx) - } /* @@ -242,7 +242,6 @@ module.exports = class TransactionManager extends EventEmitter { setTxStatusConfirmed (txId) { this._setTxStatus(txId, 'confirmed') - this.emit('update') } // merges txParams obj onto txData.txParams @@ -258,26 +257,29 @@ module.exports = class TransactionManager extends EventEmitter { checkForTxInBlock () { var signedTxList = this.getFilteredTxList({status: 'signed'}) if (!signedTxList.length) return - signedTxList.forEach((tx) => { - var txHash = tx.hash - var txId = tx.id + signedTxList.forEach((txMeta) => { + var txHash = txMeta.hash + var txId = txMeta.id if (!txHash) { - tx.err = { + txMeta.err = { errCode: 'No hash was provided', - message: 'Tx could possibly have not been submitted or an error accrued during signing', + message: 'We had an error while submitting this transaction, please try again.', } - return this.updateTx(tx) + this.updateTx(txMeta) + return this._setTxStatus(txId, 'failed') } - this.txProviderUtils.query.getTransactionByHash(txHash, (err, txMeta) => { - if (err) { - tx.err = { + this.txProviderUtils.query.getTransactionByHash(txHash, (err, txParams) => { + if (err || !txParams) { + if (!txParams) return + txMeta.err = { + isWarning: true, errorCode: err, - message: 'Tx could possibly have not been submitted to the block chain', + message: 'There was a problem loading this transaction.', } - this.updateTx(tx) + this.updateTx(txMeta) return console.error(err) } - if (txMeta.blockNumber) { + if (txParams.blockNumber) { this.setTxStatusConfirmed(txId) } }) diff --git a/ui/app/components/transaction-list-item-icon.js b/ui/app/components/transaction-list-item-icon.js index 58aa733f1..356b77c3b 100644 --- a/ui/app/components/transaction-list-item-icon.js +++ b/ui/app/components/transaction-list-item-icon.js @@ -35,6 +35,12 @@ TransactionIcon.prototype.render = function () { fontSize: '27px', }, }) + } else if (transaction.status === 'failed') { + return h('i.fa.fa-exclamation-triangle.fa-lg.warning', { + style: { + fontSize: '24px', + }, + }) } From 34081c8cb2635bba0d6dd8a93d381f6554faa861 Mon Sep 17 00:00:00 2001 From: Frankie Date: Wed, 11 Jan 2017 16:27:05 -0800 Subject: [PATCH 10/43] Fix sorting and instances where something could be undefined --- ui/app/account-detail.js | 3 ++- ui/app/components/pending-tx-details.js | 4 ++-- ui/app/conf-tx.js | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/ui/app/account-detail.js b/ui/app/account-detail.js index cfc59d99b..76554472e 100644 --- a/ui/app/account-detail.js +++ b/ui/app/account-detail.js @@ -249,8 +249,9 @@ AccountDetailScreen.prototype.subview = function () { AccountDetailScreen.prototype.transactionList = function () { const {transactions, unconfMsgs, address, network, shapeShiftTxList } = this.props // sort by recency + var soretedTxs = transactions.sort((a, b) => b.time - a.time) return h(TransactionList, { - transactions, + transactions: soretedTxs, network, unconfMsgs, address, diff --git a/ui/app/components/pending-tx-details.js b/ui/app/components/pending-tx-details.js index c40cd01b1..286931f6f 100644 --- a/ui/app/components/pending-tx-details.js +++ b/ui/app/components/pending-tx-details.js @@ -27,8 +27,8 @@ PTXP.render = function () { var account = props.accounts[address] var balance = account ? account.balance : '0x0' - var txFee = txData.txFee - var maxCost = txData.maxCost + var txFee = txData.txFee || '' + var maxCost = txData.maxCost || '' var dataLength = txParams.data ? (txParams.data.length - 2) / 2 : 0 var imageify = props.imageifyIdenticons === undefined ? true : props.imageifyIdenticons diff --git a/ui/app/conf-tx.js b/ui/app/conf-tx.js index 79699965b..2c5ba5618 100644 --- a/ui/app/conf-tx.js +++ b/ui/app/conf-tx.js @@ -43,7 +43,7 @@ ConfirmTxScreen.prototype.render = function () { var unconfMsgs = state.unconfMsgs var unconfTxList = txHelper(unconfTxs, unconfMsgs, network) var index = state.index !== undefined ? state.index : 0 - var txData = unconfTxList[index] || unconfTxList[0] || {} + var txData = unconfTxList[index] || unconfTxList[0] || {txParams: {}} var txParams = txData.txParams || {} var isNotification = isPopupOrNotification() === 'notification' return ( From 65ba9fc39e8545785b08d949072405ab1c7a567a Mon Sep 17 00:00:00 2001 From: kumavis Date: Wed, 11 Jan 2017 19:09:49 -0800 Subject: [PATCH 11/43] bugfix - portstream - dont wrap cb in try catch --- app/scripts/lib/port-stream.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/scripts/lib/port-stream.js b/app/scripts/lib/port-stream.js index 6f4ccc6ab..607a9c9ed 100644 --- a/app/scripts/lib/port-stream.js +++ b/app/scripts/lib/port-stream.js @@ -51,11 +51,11 @@ PortDuplexStream.prototype._write = function (msg, encoding, cb) { // console.log('PortDuplexStream - sent message', msg) this._port.postMessage(msg) } - cb() } catch (err) { // console.error(err) - cb(new Error('PortDuplexStream - disconnected')) + return cb(new Error('PortDuplexStream - disconnected')) } + cb() } // util From 29e83d71a82bfdbeadc9fbecfa97d73ef11fecfb Mon Sep 17 00:00:00 2001 From: kumavis Date: Fri, 13 Jan 2017 02:00:11 -0800 Subject: [PATCH 12/43] background - handle tx finalization in controllers instead of provider-engine --- app/scripts/keyring-controller.js | 10 +- app/scripts/lib/tx-utils.js | 48 +++++++ app/scripts/metamask-controller.js | 55 +++----- app/scripts/transaction-manager.js | 181 ++++++++++++++++---------- package.json | 3 +- test/unit/metamask-controller-test.js | 18 --- test/unit/tx-manager-test.js | 31 ++++- 7 files changed, 215 insertions(+), 131 deletions(-) diff --git a/app/scripts/keyring-controller.js b/app/scripts/keyring-controller.js index d4c0d863e..df2910187 100644 --- a/app/scripts/keyring-controller.js +++ b/app/scripts/keyring-controller.js @@ -317,13 +317,11 @@ module.exports = class KeyringController extends EventEmitter { // This method signs tx and returns a promise for // TX Manager to update the state after signing - signTransaction (ethTx, selectedAddress, txId) { - const address = normalize(selectedAddress) - return this.getKeyringForAccount(address) + signTransaction (ethTx, _fromAddress) { + const fromAddress = normalize(_fromAddress) + return this.getKeyringForAccount(fromAddress) .then((keyring) => { - return keyring.signTransaction(address, ethTx) - }).then((tx) => { - return {tx, txId} + return keyring.signTransaction(fromAddress, ethTx) }) } // Add Unconfirmed Message diff --git a/app/scripts/lib/tx-utils.js b/app/scripts/lib/tx-utils.js index d1fb98f42..eba537d0a 100644 --- a/app/scripts/lib/tx-utils.js +++ b/app/scripts/lib/tx-utils.js @@ -1,6 +1,8 @@ const async = require('async') const EthQuery = require('eth-query') const ethUtil = require('ethereumjs-util') +const Transaction = require('ethereumjs-tx') +const normalize = require('./sig-util').normalize const BN = ethUtil.BN /* @@ -14,6 +16,7 @@ module.exports = class txProviderUtils { this.provider = provider this.query = new EthQuery(provider) } + analyzeGasUsage (txData, cb) { var self = this this.query.getBlockByNumber('latest', true, (err, block) => { @@ -71,4 +74,49 @@ module.exports = class txProviderUtils { const correct = bnGas.add(gasBuffer) return ethUtil.addHexPrefix(correct.toString(16)) } + + fillInTxParams (txParams, cb) { + let fromAddress = txParams.from + let reqs = {} + + if (isUndef(txParams.gas)) reqs.gas = (cb) => this.query.estimateGas(txParams, cb) + if (isUndef(txParams.gasPrice)) reqs.gasPrice = (cb) => this.query.gasPrice(cb) + if (isUndef(txParams.nonce)) reqs.nonce = (cb) => this.query.getTransactionCount(fromAddress, 'pending', cb) + + async.parallel(reqs, function(err, result) { + if (err) return cb(err) + // write results to txParams obj + Object.assign(txParams, result) + cb() + }) + } + + // builds ethTx from txParams object + buildEthTxFromParams (txParams, gasMultiplier = 1) { + // apply gas multiplyer + let gasPrice = new BN(ethUtil.stripHexPrefix(txParams.gasPrice), 16) + // multiply and divide by 100 so as to add percision to integer mul + gasPrice = gasPrice.mul(new BN(gasMultiplier * 100, 10)).div(new BN(100, 10)) + txParams.gasPrice = ethUtil.intToHex(gasPrice.toNumber()) + // normalize values + txParams.to = normalize(txParams.to) + txParams.from = normalize(txParams.from) + txParams.value = normalize(txParams.value) + txParams.data = normalize(txParams.data) + txParams.gasLimit = normalize(txParams.gasLimit || txParams.gas) + txParams.nonce = normalize(txParams.nonce) + // build ethTx + const ethTx = new Transaction(txParams) + return ethTx + } + + publishTransaction (rawTx, cb) { + this.query.sendRawTransaction(rawTx, cb) + } } + +// util + +function isUndef(value) { + return value === undefined +} \ No newline at end of file diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 1fc97e81d..67c35dd67 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -45,6 +45,7 @@ module.exports = class MetamaskController extends EventEmitter { getSelectedAccount: this.configManager.getSelectedAccount.bind(this.configManager), getGasMultiplier: this.configManager.getGasMultiplier.bind(this.configManager), getNetwork: this.getStateNetwork.bind(this), + signTransaction: this.keyringController.signTransaction.bind(this.keyringController), provider: this.provider, blockTracker: this.provider, }) @@ -188,26 +189,7 @@ module.exports = class MetamaskController extends EventEmitter { cb(null, result) }, // tx signing - approveTransaction: this.newUnsignedTransaction.bind(this), - signTransaction: (txParams, cb) => { - this.txManager.formatTxForSigining(txParams) - .then(({ethTx, address, txId}) => { - return this.keyringController.signTransaction(ethTx, address, txId) - }) - .then(({tx, txId}) => { - return this.txManager.resolveSignedTransaction({tx, txId}) - }) - .then((rawTx) => { - cb(null, rawTx) - this.sendUpdate() - this.txManager.emit(`${txParams.metamaskId}:signingComplete`) - }) - .catch((err) => { - console.error(err) - cb(err) - }) - }, - + processTransaction: (txParams, cb) => this.newUnapprovedTransaction(txParams, cb), // msg signing approveMessage: this.newUnsignedMessage.bind(this), signMessage: (...args) => { @@ -256,24 +238,29 @@ module.exports = class MetamaskController extends EventEmitter { return publicConfigStore } - newUnsignedTransaction (txParams, onTxDoneCb) { - const txManager = this.txManager - const err = this.enforceTxValidations(txParams) - if (err) return onTxDoneCb(err) - txManager.addUnapprovedTransaction(txParams, onTxDoneCb, (err, txData) => { - if (err) return onTxDoneCb(err) + newUnapprovedTransaction (txParams, cb) { + this.txManager.addUnapprovedTransaction(txParams, (err, txMeta) => { + if (err) return cb(err) this.sendUpdate() - this.opts.showUnapprovedTx(txParams, txData, onTxDoneCb) + this.opts.showUnapprovedTx(txMeta) + // listen for tx completion (success, fail) + this.txManager.once(`${txMeta.id}:submitted`, successHandler) + this.txManager.once(`${txMeta.id}:rejected`, failHandler) + function successHandler(rawTx) { + removeHandlers() + cb(null, rawTx) + } + function failHandler() { + removeHandlers() + cb(new Error('User denied message signature.')) + } + function removeHandlers() { + this.txManager.removeListener(`${txMeta.id}:submitted`, successHandler) + this.txManager.removeListener(`${txMeta.id}:rejected`, failHandler) + } }) } - enforceTxValidations (txParams) { - if (('value' in txParams) && txParams.value.indexOf('-') === 0) { - const msg = `Invalid transaction value of ${txParams.value} not a positive number.` - return new Error(msg) - } - } - newUnsignedMessage (msgParams, cb) { var state = this.keyringController.getState() if (!state.isUnlocked) { diff --git a/app/scripts/transaction-manager.js b/app/scripts/transaction-manager.js index 6becfa6d1..ec08b6af7 100644 --- a/app/scripts/transaction-manager.js +++ b/app/scripts/transaction-manager.js @@ -1,11 +1,10 @@ const EventEmitter = require('events') +const async = require('async') const extend = require('xtend') +const Semaphore = require('semaphore') const ethUtil = require('ethereumjs-util') -const Transaction = require('ethereumjs-tx') -const BN = ethUtil.BN const TxProviderUtil = require('./lib/tx-utils') const createId = require('./lib/random-id') -const normalize = require('./lib/sig-util').normalize module.exports = class TransactionManager extends EventEmitter { constructor (opts) { @@ -15,11 +14,14 @@ module.exports = class TransactionManager extends EventEmitter { this.txHistoryLimit = opts.txHistoryLimit this.getSelectedAccount = opts.getSelectedAccount this.provider = opts.provider + this.query = opts.query this.blockTracker = opts.blockTracker this.txProviderUtils = new TxProviderUtil(this.provider) this.blockTracker.on('block', this.checkForTxInBlock.bind(this)) this.getGasMultiplier = opts.getGasMultiplier this.getNetwork = opts.getNetwork + this.signEthTx = opts.signTransaction + this.nonceLock = Semaphore(1) } getState () { @@ -37,7 +39,7 @@ module.exports = class TransactionManager extends EventEmitter { } // Adds a tx to the txlist - addTx (txMeta, onTxDoneCb = warn) { + addTx (txMeta) { var txList = this.getTxList() var txHistoryLimit = this.txHistoryLimit @@ -53,16 +55,11 @@ module.exports = class TransactionManager extends EventEmitter { txList.push(txMeta) this._saveTxList(txList) - // keep the onTxDoneCb around in a listener - // for after approval/denial (requires user interaction) - // This onTxDoneCb fires completion to the Dapp's write operation. this.once(`${txMeta.id}:signed`, function (txId) { this.removeAllListeners(`${txMeta.id}:rejected`) - onTxDoneCb(null, true) }) this.once(`${txMeta.id}:rejected`, function (txId) { this.removeAllListeners(`${txMeta.id}:signed`) - onTxDoneCb(null, false) }) this.emit('updateBadge') @@ -93,28 +90,35 @@ module.exports = class TransactionManager extends EventEmitter { return this.getTxsByMetaData('status', 'signed').length } - addUnapprovedTransaction (txParams, onTxDoneCb, cb) { - // create txData obj with parameters and meta data - var time = (new Date()).getTime() - var txId = createId() - txParams.metamaskId = txId - txParams.metamaskNetworkId = this.getNetwork() - var txData = { - id: txId, - txParams: txParams, - time: time, - status: 'unapproved', - gasMultiplier: this.getGasMultiplier() || 1, - metamaskNetworkId: this.getNetwork(), - } - this.txProviderUtils.analyzeGasUsage(txData, this.txDidComplete.bind(this, txData, onTxDoneCb, cb)) - // calculate metadata for tx - } - - txDidComplete (txMeta, onTxDoneCb, cb, err) { - if (err) return cb(err) - this.addTx(txMeta, onTxDoneCb) - cb(null, txMeta) + addUnapprovedTransaction (txParams, done) { + let txMeta + async.waterfall([ + // validate + (cb) => this.validateTxParams(txParams, cb), + // prepare txMeta + (cb) => { + // create txMeta obj with parameters and meta data + let time = (new Date()).getTime() + let txId = createId() + txParams.metamaskId = txId + txParams.metamaskNetworkId = this.getNetwork() + txMeta = { + id: txId, + time: time, + status: 'unapproved', + gasMultiplier: this.getGasMultiplier() || 1, + metamaskNetworkId: this.getNetwork(), + txParams: txParams, + } + // calculate metadata for tx + this.txProviderUtils.analyzeGasUsage(txMeta, cb) + }, + // save txMeta + (cb) => { + this.addTx(txMeta) + cb(null, txMeta) + }, + ], done) } getUnapprovedTxList () { @@ -127,8 +131,23 @@ module.exports = class TransactionManager extends EventEmitter { } approveTransaction (txId, cb = warn) { - this.setTxStatusSigned(txId) - this.once(`${txId}:signingComplete`, cb) + const self = this + // approve + self.setTxStatusApproved(txId) + // only allow one tx at a time for atomic nonce usage + self.nonceLock.take(() => { + // begin signature process + async.waterfall([ + (cb) => self.fillInTxParams(txId, cb), + (cb) => self.signTransaction(txId, cb), + (rawTx, cb) => self.publishTransaction(txId, rawTx, cb), + ], (err) => { + self.nonceLock.leave() + // TODO: move tx to error state + if (err) return cb(err) + cb() + }) + }) } cancelTransaction (txId, cb = warn) { @@ -136,38 +155,52 @@ module.exports = class TransactionManager extends EventEmitter { cb() } - // formats txParams so the keyringController can sign it - formatTxForSigining (txParams) { - var address = txParams.from - var metaTx = this.getTx(txParams.metamaskId) - var gasMultiplier = metaTx.gasMultiplier - var gasPrice = new BN(ethUtil.stripHexPrefix(txParams.gasPrice), 16) - gasPrice = gasPrice.mul(new BN(gasMultiplier * 100, 10)).div(new BN(100, 10)) - txParams.gasPrice = ethUtil.intToHex(gasPrice.toNumber()) - - // normalize values - txParams.to = normalize(txParams.to) - txParams.from = normalize(txParams.from) - txParams.value = normalize(txParams.value) - txParams.data = normalize(txParams.data) - txParams.gasLimit = normalize(txParams.gasLimit || txParams.gas) - txParams.nonce = normalize(txParams.nonce) - const ethTx = new Transaction(txParams) - var txId = txParams.metamaskId - return Promise.resolve({ethTx, address, txId}) + fillInTxParams (txId, cb) { + let txMeta = this.getTx(txId) + this.txProviderUtils.fillInTxParams(txMeta.txParams, (err) => { + if (err) return cb(err) + this.updateTx(txMeta) + cb() + }) + } + + signTransaction (txId, cb) { + let txMeta = this.getTx(txId) + let txParams = txMeta.txParams + let fromAddress = txParams.from + let ethTx = this.txProviderUtils.buildEthTxFromParams(txParams, txMeta.gasMultiplier) + this.signEthTx(ethTx, fromAddress).then(() => { + this.updateTxAsSigned(txMeta.id, ethTx) + cb(null, ethUtil.bufferToHex(ethTx.serialize())) + }).catch((err) => { + cb(err) + }) + } + + publishTransaction (txId, rawTx, cb) { + this.txProviderUtils.publishTransaction(rawTx, (err) => { + if (err) return cb(err) + this.setTxStatusSubmitted(txId, rawTx) + cb() + }) + } + + validateTxParams (txParams, cb) { + if (('value' in txParams) && txParams.value.indexOf('-') === 0) { + cb(new Error(`Invalid transaction value of ${txParams.value} not a positive number.`)) + } else { + cb() + } } // receives a signed tx object and updates the tx hash - // and pass it to the cb to be sent off - resolveSignedTransaction ({tx, txId, cb = warn}) { + updateTxAsSigned (txId, ethTx) { // Add the tx hash to the persisted meta-tx object - var txHash = ethUtil.bufferToHex(tx.hash()) - var metaTx = this.getTx(txId) - metaTx.hash = txHash - this.updateTx(metaTx) - var rawTx = ethUtil.bufferToHex(tx.serialize()) - return Promise.resolve(rawTx) - + let txHash = ethUtil.bufferToHex(ethTx.hash()) + let txMeta = this.getTx(txId) + txMeta.hash = txHash + this.updateTx(txMeta) + this.setTxStatusSigned(txMeta.id) } /* @@ -212,23 +245,32 @@ module.exports = class TransactionManager extends EventEmitter { return txMeta.status } + // should update the status of the tx to 'rejected'. + setTxStatusRejected (txId) { + this._setTxStatus(txId, 'rejected') + } + + // should update the status of the tx to 'approved'. + setTxStatusApproved (txId) { + this._setTxStatus(txId, 'approved') + } // should update the status of the tx to 'signed'. setTxStatusSigned (txId) { this._setTxStatus(txId, 'signed') - this.emit('updateBadge') } - // should update the status of the tx to 'rejected'. - setTxStatusRejected (txId) { - this._setTxStatus(txId, 'rejected') - this.emit('updateBadge') + // should update the status of the tx to 'submitted'. + setTxStatusSubmitted (txId, rawTx) { + this._setTxStatus(txId, 'submitted', rawTx) } + // should update the status of the tx to 'confirmed'. setTxStatusConfirmed (txId) { this._setTxStatus(txId, 'confirmed') } + // merges txParams obj onto txData.txParams // use extend to ensure that all fields are filled updateTxParams (txId, txParams) { @@ -266,13 +308,16 @@ module.exports = class TransactionManager extends EventEmitter { // should set the status in txData // - `'unapproved'` the user has not responded // - `'rejected'` the user has responded no! + // - `'approved'` the user has approved the tx // - `'signed'` the tx is signed // - `'submitted'` the tx is sent to a server // - `'confirmed'` the tx has been included in a block. - _setTxStatus (txId, status) { + // "value" is an optional parameter to emit + _setTxStatus (txId, status, value) { var txMeta = this.getTx(txId) txMeta.status = status - this.emit(`${txMeta.id}:${status}`, txId) + this.emit(`${txMeta.id}:${status}`, value) + this.emit('updateBadge') this.updateTx(txMeta) } diff --git a/package.json b/package.json index 0d0835a86..52708fdab 100644 --- a/package.json +++ b/package.json @@ -89,13 +89,14 @@ "redux-logger": "^2.3.1", "redux-thunk": "^1.0.2", "sandwich-expando": "^1.0.5", + "semaphore": "^1.0.5", "textarea-caret": "^3.0.1", "three.js": "^0.73.2", "through2": "^2.0.1", "valid-url": "^1.0.9", "vreme": "^3.0.2", "web3": "0.17.0-beta", - "web3-provider-engine": "^8.2.0", + "web3-provider-engine": "^8.4.0", "web3-stream-provider": "^2.0.6", "xtend": "^4.0.1" }, diff --git a/test/unit/metamask-controller-test.js b/test/unit/metamask-controller-test.js index 414610404..a6164c9a0 100644 --- a/test/unit/metamask-controller-test.js +++ b/test/unit/metamask-controller-test.js @@ -25,24 +25,6 @@ describe('MetaMaskController', function() { this.sinon.restore() }) - describe('#enforceTxValidations', function () { - it('returns null for positive values', function() { - var sample = { - value: '0x01' - } - var res = controller.enforceTxValidations(sample) - assert.equal(res, null, 'no error') - }) - - - it('returns error for negative values', function() { - var sample = { - value: '-0x01' - } - var res = controller.enforceTxValidations(sample) - assert.ok(res, 'error') - }) - }) }) diff --git a/test/unit/tx-manager-test.js b/test/unit/tx-manager-test.js index be16facad..d5d386234 100644 --- a/test/unit/tx-manager-test.js +++ b/test/unit/tx-manager-test.js @@ -18,6 +18,27 @@ describe('Transaction Manager', function() { }) }) + describe('#validateTxParams', function () { + it('returns null for positive values', function() { + var sample = { + value: '0x01' + } + var res = txManager.validateTxParams(sample, (err) => { + assert.equal(err, null, 'no error') + }) + }) + + + it('returns error for negative values', function() { + var sample = { + value: '-0x01' + } + var res = txManager.validateTxParams(sample, (err) => { + assert.ok(err, 'error') + }) + }) + }) + describe('#getTxList', function() { it('when new should return empty array', function() { var result = txManager.getTxList() @@ -100,11 +121,12 @@ describe('Transaction Manager', function() { it('should emit a signed event to signal the exciton of callback', (done) => { this.timeout(10000) var tx = { id: 1, status: 'unapproved' } - let onTxDoneCb = function (err, txId) { + let onTxDoneCb = function () { assert(true, 'event listener has been triggered and onTxDoneCb executed') done() } - txManager.addTx(tx, onTxDoneCb) + txManager.addTx(tx) + txManager.on('1:signed', onTxDoneCb) txManager.setTxStatusSigned(1) }) }) @@ -112,7 +134,7 @@ describe('Transaction Manager', function() { describe('#setTxStatusRejected', function() { it('sets the tx status to rejected', function() { var tx = { id: 1, status: 'unapproved' } - txManager.addTx(tx, onTxDoneCb) + txManager.addTx(tx) txManager.setTxStatusRejected(1) var result = txManager.getTxList() assert.ok(Array.isArray(result)) @@ -123,11 +145,12 @@ describe('Transaction Manager', function() { it('should emit a rejected event to signal the exciton of callback', (done) => { this.timeout(10000) var tx = { id: 1, status: 'unapproved' } + txManager.addTx(tx) let onTxDoneCb = function (err, txId) { assert(true, 'event listener has been triggered and onTxDoneCb executed') done() } - txManager.addTx(tx, onTxDoneCb) + txManager.on('1:rejected', onTxDoneCb) txManager.setTxStatusRejected(1) }) From 5ed52eed680f503adb0e510320b2610658157d4d Mon Sep 17 00:00:00 2001 From: Frankie Date: Fri, 13 Jan 2017 10:44:22 -0800 Subject: [PATCH 13/43] Clean up code --- app/scripts/transaction-manager.js | 3 +- ui/app/account-detail.js | 5 +- .../components/transaction-list-item-icon.js | 61 ++++++++++--------- ui/app/components/transaction-list.js | 4 +- 4 files changed, 39 insertions(+), 34 deletions(-) diff --git a/app/scripts/transaction-manager.js b/app/scripts/transaction-manager.js index 527899835..f5b57f3c2 100644 --- a/app/scripts/transaction-manager.js +++ b/app/scripts/transaction-manager.js @@ -25,8 +25,9 @@ module.exports = class TransactionManager extends EventEmitter { getState () { var selectedAccount = this.getSelectedAccount() return { + transactions: this.getTxList(), unconfTxs: this.getUnapprovedTxList(), - transactions: this.getFilteredTxList({metamaskNetworkId: this.getNetwork(), from: selectedAccount}), + selectedAccountTxList: this.getFilteredTxList({metamaskNetworkId: this.getNetwork(), from: selectedAccount}), } } diff --git a/ui/app/account-detail.js b/ui/app/account-detail.js index 76554472e..d7473aee5 100644 --- a/ui/app/account-detail.js +++ b/ui/app/account-detail.js @@ -29,7 +29,7 @@ function mapStateToProps (state) { network: state.metamask.network, unconfMsgs: valuesFor(state.metamask.unconfMsgs), shapeShiftTxList: state.metamask.shapeShiftTxList, - transactions: state.metamask.transactions, + transactions: state.metamask.selectedAccountTxList || [], } } @@ -249,9 +249,8 @@ AccountDetailScreen.prototype.subview = function () { AccountDetailScreen.prototype.transactionList = function () { const {transactions, unconfMsgs, address, network, shapeShiftTxList } = this.props // sort by recency - var soretedTxs = transactions.sort((a, b) => b.time - a.time) return h(TransactionList, { - transactions: soretedTxs, + transactions: transactions.sort((a, b) => b.time - a.time), network, unconfMsgs, address, diff --git a/ui/app/components/transaction-list-item-icon.js b/ui/app/components/transaction-list-item-icon.js index 356b77c3b..eca0d693a 100644 --- a/ui/app/components/transaction-list-item-icon.js +++ b/ui/app/components/transaction-list-item-icon.js @@ -13,36 +13,41 @@ function TransactionIcon () { TransactionIcon.prototype.render = function () { const { transaction, txParams, isMsg } = this.props - if (transaction.status === 'unapproved') { - return h('.unapproved-tx', { - style: { - width: '15px', - height: '15px', - background: '#00bfff', - borderRadius: '13px', - }, - }) + switch (transaction.status) { + case 'unapproved': + return h('.unapproved-tx', { + style: { + width: '24px', + height: '24px', + background: '#4dffff', + border: 'solid', + borderColor: '#AEAEAE', + borderWidth: '0.5px', + borderRadius: '13px', + }, + }) - } else if (transaction.status === 'rejected') { - return h('i.fa.fa-exclamation-triangle.fa-lg.warning', { - style: { - width: '24px', - }, - }) - } else if (transaction.status === 'signed') { - return h('i.fa.fa-ellipsis-h', { - style: { - fontSize: '27px', - }, - }) - } else if (transaction.status === 'failed') { - return h('i.fa.fa-exclamation-triangle.fa-lg.warning', { - style: { - fontSize: '24px', - }, - }) - } + case 'rejected': + return h('i.fa.fa-exclamation-triangle.fa-lg.warning', { + style: { + width: '24px', + }, + }) + case 'failed': + return h('i.fa.fa-exclamation-triangle.fa-lg.error', { + style: { + width: '24px', + }, + }) + + case 'signed': + return h('i.fa.fa-ellipsis-h', { + style: { + fontSize: '27px', + }, + }) + } if (isMsg) { return h('i.fa.fa-certificate.fa-lg', { diff --git a/ui/app/components/transaction-list.js b/ui/app/components/transaction-list.js index 105b34c90..b055ca9d5 100644 --- a/ui/app/components/transaction-list.js +++ b/ui/app/components/transaction-list.js @@ -13,13 +13,13 @@ function TransactionList () { } TransactionList.prototype.render = function () { - const { transactions = [], network, unconfMsgs } = this.props + const { transactions, network, unconfMsgs } = this.props var shapeShiftTxList if (network === '1') { shapeShiftTxList = this.props.shapeShiftTxList } - const txsToRender = !shapeShiftTxList ? transactions.concat(unconfMsgs) : txsToRender.concat(unconfMsgs, shapeShiftTxList) + const txsToRender = !shapeShiftTxList ? transactions.concat(unconfMsgs) : transactions.concat(unconfMsgs, shapeShiftTxList) .sort((a, b) => b.time - a.time) return ( From c1656aff195ad37d8365a0bdc84133eac6fb0263 Mon Sep 17 00:00:00 2001 From: Frankie Date: Fri, 13 Jan 2017 10:53:16 -0800 Subject: [PATCH 14/43] lint --- ui/app/conf-tx.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/app/conf-tx.js b/ui/app/conf-tx.js index 2c5ba5618..8d8285f3d 100644 --- a/ui/app/conf-tx.js +++ b/ui/app/conf-tx.js @@ -43,7 +43,7 @@ ConfirmTxScreen.prototype.render = function () { var unconfMsgs = state.unconfMsgs var unconfTxList = txHelper(unconfTxs, unconfMsgs, network) var index = state.index !== undefined ? state.index : 0 - var txData = unconfTxList[index] || unconfTxList[0] || {txParams: {}} + var txData = unconfTxList[index] || {txParams: {}} var txParams = txData.txParams || {} var isNotification = isPopupOrNotification() === 'notification' return ( From b72987405e0ca60db75821ce38f401eb395ae356 Mon Sep 17 00:00:00 2001 From: Frankie Date: Fri, 13 Jan 2017 11:30:58 -0800 Subject: [PATCH 15/43] remove orphaned comment --- ui/app/account-detail.js | 1 - 1 file changed, 1 deletion(-) diff --git a/ui/app/account-detail.js b/ui/app/account-detail.js index d7473aee5..7a0c599ba 100644 --- a/ui/app/account-detail.js +++ b/ui/app/account-detail.js @@ -248,7 +248,6 @@ AccountDetailScreen.prototype.subview = function () { AccountDetailScreen.prototype.transactionList = function () { const {transactions, unconfMsgs, address, network, shapeShiftTxList } = this.props - // sort by recency return h(TransactionList, { transactions: transactions.sort((a, b) => b.time - a.time), network, From d755b66e204277ac6a904ba432f7b10a340c9288 Mon Sep 17 00:00:00 2001 From: kumavis Date: Fri, 13 Jan 2017 15:51:08 -0800 Subject: [PATCH 16/43] background - metamask controller - fix 'this' ref --- app/scripts/metamask-controller.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 67c35dd67..315b9832c 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -239,13 +239,14 @@ module.exports = class MetamaskController extends EventEmitter { } newUnapprovedTransaction (txParams, cb) { - this.txManager.addUnapprovedTransaction(txParams, (err, txMeta) => { + const self = this + self.txManager.addUnapprovedTransaction(txParams, (err, txMeta) => { if (err) return cb(err) - this.sendUpdate() - this.opts.showUnapprovedTx(txMeta) + self.sendUpdate() + self.opts.showUnapprovedTx(txMeta) // listen for tx completion (success, fail) - this.txManager.once(`${txMeta.id}:submitted`, successHandler) - this.txManager.once(`${txMeta.id}:rejected`, failHandler) + self.txManager.once(`${txMeta.id}:submitted`, successHandler) + self.txManager.once(`${txMeta.id}:rejected`, failHandler) function successHandler(rawTx) { removeHandlers() cb(null, rawTx) @@ -255,8 +256,8 @@ module.exports = class MetamaskController extends EventEmitter { cb(new Error('User denied message signature.')) } function removeHandlers() { - this.txManager.removeListener(`${txMeta.id}:submitted`, successHandler) - this.txManager.removeListener(`${txMeta.id}:rejected`, failHandler) + self.txManager.removeListener(`${txMeta.id}:submitted`, successHandler) + self.txManager.removeListener(`${txMeta.id}:rejected`, failHandler) } }) } From 212ef0b850d3bd07c24a7e2d662fe1952cf9a6dd Mon Sep 17 00:00:00 2001 From: Frankie Date: Fri, 13 Jan 2017 16:53:10 -0800 Subject: [PATCH 17/43] fix the maxcost not being included in txMeta --- app/scripts/transaction-manager.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/app/scripts/transaction-manager.js b/app/scripts/transaction-manager.js index 034cf3aeb..5e0544755 100644 --- a/app/scripts/transaction-manager.js +++ b/app/scripts/transaction-manager.js @@ -117,12 +117,14 @@ module.exports = class TransactionManager extends EventEmitter { // save txMeta (cb) => { this.addTx(txMeta) + debugger + this.setMaxTxCostAndFee(txMeta) cb(null, txMeta) }, ], done) } - getMaxTxCostAndFee (txMeta) { + setMaxTxCostAndFee (txMeta) { var txParams = txMeta.txParams var gasMultiplier = txMeta.gasMultiplier @@ -132,7 +134,9 @@ module.exports = class TransactionManager extends EventEmitter { var txFee = gasCost.mul(gasPrice) var txValue = new BN(ethUtil.stripHexPrefix(txParams.value || '0x0'), 16) var maxCost = txValue.add(txFee) - return {maxCost, txFee} + txMeta.txValue = txValue + txMeta.maxCost = maxCost + this.updateTx(txMeta) } getUnapprovedTxList () { @@ -263,7 +267,7 @@ module.exports = class TransactionManager extends EventEmitter { setTxStatusRejected (txId) { this._setTxStatus(txId, 'rejected') } - + // should update the status of the tx to 'approved'. setTxStatusApproved (txId) { this._setTxStatus(txId, 'approved') From 580d93188cb77cb4d4ce809b46dade8525efc380 Mon Sep 17 00:00:00 2001 From: Frankie Date: Fri, 13 Jan 2017 17:47:20 -0800 Subject: [PATCH 18/43] Satisfy review needs: removed unnecessary this.query = opts.query from constructor Created a tx error state for errors in approveTransaction validateTxParams has been moved to tx-utils removed "value" arg from _setTxStatus --- app/scripts/lib/tx-utils.js | 14 ++++++++++++-- app/scripts/transaction-manager.js | 30 +++++++++++------------------- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/app/scripts/lib/tx-utils.js b/app/scripts/lib/tx-utils.js index eba537d0a..5116cb93b 100644 --- a/app/scripts/lib/tx-utils.js +++ b/app/scripts/lib/tx-utils.js @@ -16,7 +16,7 @@ module.exports = class txProviderUtils { this.provider = provider this.query = new EthQuery(provider) } - + analyzeGasUsage (txData, cb) { var self = this this.query.getBlockByNumber('latest', true, (err, block) => { @@ -113,10 +113,20 @@ module.exports = class txProviderUtils { publishTransaction (rawTx, cb) { this.query.sendRawTransaction(rawTx, cb) } + + validateTxParams (txParams, cb) { + if (('value' in txParams) && txParams.value.indexOf('-') === 0) { + cb(new Error(`Invalid transaction value of ${txParams.value} not a positive number.`)) + } else { + cb() + } + } + + } // util function isUndef(value) { return value === undefined -} \ No newline at end of file +} diff --git a/app/scripts/transaction-manager.js b/app/scripts/transaction-manager.js index 5e0544755..9462d166e 100644 --- a/app/scripts/transaction-manager.js +++ b/app/scripts/transaction-manager.js @@ -14,7 +14,6 @@ module.exports = class TransactionManager extends EventEmitter { this.txHistoryLimit = opts.txHistoryLimit this.getSelectedAccount = opts.getSelectedAccount this.provider = opts.provider - this.query = opts.query this.blockTracker = opts.blockTracker this.txProviderUtils = new TxProviderUtil(this.provider) this.blockTracker.on('block', this.checkForTxInBlock.bind(this)) @@ -95,7 +94,7 @@ module.exports = class TransactionManager extends EventEmitter { let txMeta async.waterfall([ // validate - (cb) => this.validateTxParams(txParams, cb), + (cb) => this.txProviderUtils.validateTxParams(txParams, cb), // prepare txMeta (cb) => { // create txMeta obj with parameters and meta data @@ -117,7 +116,6 @@ module.exports = class TransactionManager extends EventEmitter { // save txMeta (cb) => { this.addTx(txMeta) - debugger this.setMaxTxCostAndFee(txMeta) cb(null, txMeta) }, @@ -161,7 +159,7 @@ module.exports = class TransactionManager extends EventEmitter { (rawTx, cb) => self.publishTransaction(txId, rawTx, cb), ], (err) => { self.nonceLock.leave() - // TODO: move tx to error state + this.setTxStatusFailed(txId) if (err) return cb(err) cb() }) @@ -198,19 +196,11 @@ module.exports = class TransactionManager extends EventEmitter { publishTransaction (txId, rawTx, cb) { this.txProviderUtils.publishTransaction(rawTx, (err) => { if (err) return cb(err) - this.setTxStatusSubmitted(txId, rawTx) + this.setTxStatusSubmitted(txId) cb() }) } - validateTxParams (txParams, cb) { - if (('value' in txParams) && txParams.value.indexOf('-') === 0) { - cb(new Error(`Invalid transaction value of ${txParams.value} not a positive number.`)) - } else { - cb() - } - } - // receives a signed tx object and updates the tx hash updateTxAsSigned (txId, ethTx) { // Add the tx hash to the persisted meta-tx object @@ -279,8 +269,8 @@ module.exports = class TransactionManager extends EventEmitter { } // should update the status of the tx to 'submitted'. - setTxStatusSubmitted (txId, rawTx) { - this._setTxStatus(txId, 'submitted', rawTx) + setTxStatusSubmitted (txId) { + this._setTxStatus(txId, 'submitted') } // should update the status of the tx to 'confirmed'. @@ -288,6 +278,9 @@ module.exports = class TransactionManager extends EventEmitter { this._setTxStatus(txId, 'confirmed') } + setTxStatusFailed (txId) { + this._setTxStatus(txId, 'failed') + } // merges txParams obj onto txData.txParams // use extend to ensure that all fields are filled @@ -311,7 +304,7 @@ module.exports = class TransactionManager extends EventEmitter { message: 'We had an error while submitting this transaction, please try again.', } this.updateTx(txMeta) - return this._setTxStatus(txId, 'failed') + return this.setTxStatusFailed(txId) } this.txProviderUtils.query.getTransactionByHash(txHash, (err, txParams) => { if (err || !txParams) { @@ -342,11 +335,10 @@ module.exports = class TransactionManager extends EventEmitter { // - `'signed'` the tx is signed // - `'submitted'` the tx is sent to a server // - `'confirmed'` the tx has been included in a block. - // "value" is an optional parameter to emit - _setTxStatus (txId, status, value) { + _setTxStatus (txId, status) { var txMeta = this.getTx(txId) txMeta.status = status - this.emit(`${txMeta.id}:${status}`, value) + this.emit(`${txMeta.id}:${status}`, txId) this.emit('updateBadge') this.updateTx(txMeta) } From 87505e1742ee56e78fed7f17645f58bf169c4ef7 Mon Sep 17 00:00:00 2001 From: Frankie Date: Fri, 13 Jan 2017 18:01:50 -0800 Subject: [PATCH 19/43] fix for linting --- app/scripts/transaction-manager.js | 1 + test/unit/tx-manager-test.js | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/scripts/transaction-manager.js b/app/scripts/transaction-manager.js index 9462d166e..c33c4ed16 100644 --- a/app/scripts/transaction-manager.js +++ b/app/scripts/transaction-manager.js @@ -3,6 +3,7 @@ const async = require('async') const extend = require('xtend') const Semaphore = require('semaphore') const ethUtil = require('ethereumjs-util') +const BN = require('ethereumjs-util').BN const TxProviderUtil = require('./lib/tx-utils') const createId = require('./lib/random-id') diff --git a/test/unit/tx-manager-test.js b/test/unit/tx-manager-test.js index d5d386234..5f18e357f 100644 --- a/test/unit/tx-manager-test.js +++ b/test/unit/tx-manager-test.js @@ -23,7 +23,7 @@ describe('Transaction Manager', function() { var sample = { value: '0x01' } - var res = txManager.validateTxParams(sample, (err) => { + var res = txManager.txProviderUtils.validateTxParams(sample, (err) => { assert.equal(err, null, 'no error') }) }) @@ -33,7 +33,7 @@ describe('Transaction Manager', function() { var sample = { value: '-0x01' } - var res = txManager.validateTxParams(sample, (err) => { + var res = txManager.txProviderUtils.validateTxParams(sample, (err) => { assert.ok(err, 'error') }) }) From fdcf03f57d0516731799266c8279c0caa5ffcbed Mon Sep 17 00:00:00 2001 From: Frankie Date: Sat, 14 Jan 2017 13:32:35 -0800 Subject: [PATCH 20/43] Fix the inclusion of the txFee in the meta tx object --- app/scripts/transaction-manager.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/scripts/transaction-manager.js b/app/scripts/transaction-manager.js index c33c4ed16..7dbfc0dbc 100644 --- a/app/scripts/transaction-manager.js +++ b/app/scripts/transaction-manager.js @@ -125,7 +125,6 @@ module.exports = class TransactionManager extends EventEmitter { setMaxTxCostAndFee (txMeta) { var txParams = txMeta.txParams - var gasMultiplier = txMeta.gasMultiplier var gasCost = new BN(ethUtil.stripHexPrefix(txParams.gas || txMeta.estimatedGas), 16) var gasPrice = new BN(ethUtil.stripHexPrefix(txParams.gasPrice || '0x4a817c800'), 16) @@ -133,6 +132,7 @@ module.exports = class TransactionManager extends EventEmitter { var txFee = gasCost.mul(gasPrice) var txValue = new BN(ethUtil.stripHexPrefix(txParams.value || '0x0'), 16) var maxCost = txValue.add(txFee) + txMeta.txFee = txFee txMeta.txValue = txValue txMeta.maxCost = maxCost this.updateTx(txMeta) @@ -160,8 +160,10 @@ module.exports = class TransactionManager extends EventEmitter { (rawTx, cb) => self.publishTransaction(txId, rawTx, cb), ], (err) => { self.nonceLock.leave() - this.setTxStatusFailed(txId) - if (err) return cb(err) + if (err) { + this.setTxStatusFailed(txId) + return cb(err) + } cb() }) }) From c3d491a37cb4d704e405b1e19560c14dd2c401ae Mon Sep 17 00:00:00 2001 From: kumavis Date: Sat, 14 Jan 2017 20:51:29 -0800 Subject: [PATCH 21/43] background - return txHash to provider-engine on done --- app/scripts/metamask-controller.js | 24 ++++++++++-------------- app/scripts/transaction-manager.js | 3 +++ 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 13008893b..b94b98eac 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -246,20 +246,16 @@ module.exports = class MetamaskController extends EventEmitter { self.sendUpdate() self.opts.showUnapprovedTx(txMeta) // listen for tx completion (success, fail) - self.txManager.once(`${txMeta.id}:submitted`, successHandler) - self.txManager.once(`${txMeta.id}:rejected`, failHandler) - function successHandler(rawTx) { - removeHandlers() - cb(null, rawTx) - } - function failHandler() { - removeHandlers() - cb(new Error('User denied message signature.')) - } - function removeHandlers() { - self.txManager.removeListener(`${txMeta.id}:submitted`, successHandler) - self.txManager.removeListener(`${txMeta.id}:rejected`, failHandler) - } + self.txManager.once(`${txMeta.id}:finished`, (status) => { + switch (status) { + case 'submitted': + return cb(null, txMeta.hash) + case 'rejected': + return cb(new Error('MetaMask Tx Signature: User denied transaction signature.')) + default: + return cb(new Error(`MetaMask Tx Signature: Unknown problem: ${JSON.stringify(txMeta.txParams)}`)) + } + }) }) } diff --git a/app/scripts/transaction-manager.js b/app/scripts/transaction-manager.js index 7dbfc0dbc..5a44705b7 100644 --- a/app/scripts/transaction-manager.js +++ b/app/scripts/transaction-manager.js @@ -342,6 +342,9 @@ module.exports = class TransactionManager extends EventEmitter { var txMeta = this.getTx(txId) txMeta.status = status this.emit(`${txMeta.id}:${status}`, txId) + if (status === 'submitted' || status === 'rejected') { + this.emit(`${txMeta.id}:finished`, status) + } this.emit('updateBadge') this.updateTx(txMeta) } From f49fb149ccae56f5522a892778d83360856fe880 Mon Sep 17 00:00:00 2001 From: kumavis Date: Sat, 14 Jan 2017 21:29:46 -0800 Subject: [PATCH 22/43] background - txManager - filter txs by network --- app/scripts/background.js | 5 +++-- app/scripts/transaction-manager.js | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/scripts/background.js b/app/scripts/background.js index 3f15488ee..f3837a028 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -22,7 +22,7 @@ const controller = new MetamaskController({ setData, loadData, }) -const txManager = controller.txManager + function triggerUi () { if (!popupIsOpen) notification.show() } @@ -93,7 +93,8 @@ function setupControllerConnection (stream) { // plugin badge text // -txManager.on('updateBadge', updateBadge) +controller.txManager.on('updateBadge', updateBadge) +updateBadge() function updateBadge () { var label = '' diff --git a/app/scripts/transaction-manager.js b/app/scripts/transaction-manager.js index 5a44705b7..87f99ce62 100644 --- a/app/scripts/transaction-manager.js +++ b/app/scripts/transaction-manager.js @@ -35,7 +35,8 @@ module.exports = class TransactionManager extends EventEmitter { // Returns the tx list getTxList () { - return this.txList + let network = this.getNetwork() + return this.txList.filter(txMeta => txMeta.metamaskNetworkId === network) } // Adds a tx to the txlist @@ -345,8 +346,8 @@ module.exports = class TransactionManager extends EventEmitter { if (status === 'submitted' || status === 'rejected') { this.emit(`${txMeta.id}:finished`, status) } - this.emit('updateBadge') this.updateTx(txMeta) + this.emit('updateBadge') } // Saves the new/updated txList. From ef81bde98b85a3add8a5e5681f8c9515567c97ea Mon Sep 17 00:00:00 2001 From: kumavis Date: Sat, 14 Jan 2017 21:46:40 -0800 Subject: [PATCH 23/43] eth-store - emit update on new account add --- app/scripts/lib/eth-store.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/scripts/lib/eth-store.js b/app/scripts/lib/eth-store.js index a42b2417f..7e2caf884 100644 --- a/app/scripts/lib/eth-store.js +++ b/app/scripts/lib/eth-store.js @@ -43,7 +43,9 @@ EthereumStore.prototype.addAccount = function (address) { self._currentState.accounts[address] = {} self._didUpdate() if (!self.currentBlockNumber) return - self._updateAccount(address, noop) + self._updateAccount(address, () => { + self._didUpdate() + }) } EthereumStore.prototype.removeAccount = function (address) { From 3df9ce9809b18253f990458a404a65f01b1947c4 Mon Sep 17 00:00:00 2001 From: kumavis Date: Sat, 14 Jan 2017 21:52:09 -0800 Subject: [PATCH 24/43] tests - txManager - add getNetwork fn to fixture txManager --- test/unit/tx-manager-test.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/unit/tx-manager-test.js b/test/unit/tx-manager-test.js index 5f18e357f..20392a5ca 100644 --- a/test/unit/tx-manager-test.js +++ b/test/unit/tx-manager-test.js @@ -15,6 +15,7 @@ describe('Transaction Manager', function() { provider: "testnet", txHistoryLimit: 10, blockTracker: new EventEmitter(), + getNetwork: function(){ return 'unit test' } }) }) From 993daebc4e67346b8eeedb0af50e34bc451d0400 Mon Sep 17 00:00:00 2001 From: kumavis Date: Sat, 14 Jan 2017 22:09:19 -0800 Subject: [PATCH 25/43] test - txManager - add network to all txs --- test/unit/tx-manager-test.js | 37 ++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/test/unit/tx-manager-test.js b/test/unit/tx-manager-test.js index 20392a5ca..a66003f85 100644 --- a/test/unit/tx-manager-test.js +++ b/test/unit/tx-manager-test.js @@ -53,7 +53,7 @@ describe('Transaction Manager', function() { describe('#_saveTxList', function() { it('saves the submitted data to the tx list', function() { - var target = [{ foo: 'bar' }] + var target = [{ foo: 'bar', metamaskNetworkId: 'unit test' }] txManager._saveTxList(target) var result = txManager.getTxList() assert.equal(result[0].foo, 'bar') @@ -62,7 +62,7 @@ describe('Transaction Manager', function() { describe('#addTx', function() { it('adds a tx returned in getTxList', function() { - var tx = { id: 1, status: 'confirmed',} + var tx = { id: 1, status: 'confirmed', metamaskNetworkId: 'unit test' } txManager.addTx(tx, onTxDoneCb) var result = txManager.getTxList() assert.ok(Array.isArray(result)) @@ -73,7 +73,7 @@ describe('Transaction Manager', function() { it('cuts off early txs beyond a limit', function() { const limit = txManager.txHistoryLimit for (let i = 0; i < limit + 1; i++) { - let tx = { id: i, time: new Date(), status: 'confirmed'} + let tx = { id: i, time: new Date(), status: 'confirmed', metamaskNetworkId: 'unit test' } txManager.addTx(tx, onTxDoneCb) } var result = txManager.getTxList() @@ -81,10 +81,10 @@ describe('Transaction Manager', function() { assert.equal(result[0].id, 1, 'early txs truncted') }) - it('cuts off early txs beyond a limit weather or not it is confirmed or rejected', function() { + it('cuts off early txs beyond a limit whether or not it is confirmed or rejected', function() { const limit = txManager.txHistoryLimit for (let i = 0; i < limit + 1; i++) { - let tx = { id: i, time: new Date(), status: 'rejected'} + let tx = { id: i, time: new Date(), status: 'rejected', metamaskNetworkId: 'unit test' } txManager.addTx(tx, onTxDoneCb) } var result = txManager.getTxList() @@ -93,11 +93,11 @@ describe('Transaction Manager', function() { }) it('cuts off early txs beyond a limit but does not cut unapproved txs', function() { - var unconfirmedTx = { id: 0, time: new Date(), status: 'unapproved'} + var unconfirmedTx = { id: 0, time: new Date(), status: 'unapproved', metamaskNetworkId: 'unit test' } txManager.addTx(unconfirmedTx, onTxDoneCb) const limit = txManager.txHistoryLimit for (let i = 1; i < limit + 1; i++) { - let tx = { id: i, time: new Date(), status: 'confirmed'} + let tx = { id: i, time: new Date(), status: 'confirmed', metamaskNetworkId: 'unit test' } txManager.addTx(tx, onTxDoneCb) } var result = txManager.getTxList() @@ -110,7 +110,7 @@ describe('Transaction Manager', function() { describe('#setTxStatusSigned', function() { it('sets the tx status to signed', function() { - var tx = { id: 1, status: 'unapproved' } + var tx = { id: 1, status: 'unapproved', metamaskNetworkId: 'unit test' } txManager.addTx(tx, onTxDoneCb) txManager.setTxStatusSigned(1) var result = txManager.getTxList() @@ -121,7 +121,7 @@ describe('Transaction Manager', function() { it('should emit a signed event to signal the exciton of callback', (done) => { this.timeout(10000) - var tx = { id: 1, status: 'unapproved' } + var tx = { id: 1, status: 'unapproved', metamaskNetworkId: 'unit test' } let onTxDoneCb = function () { assert(true, 'event listener has been triggered and onTxDoneCb executed') done() @@ -134,7 +134,7 @@ describe('Transaction Manager', function() { describe('#setTxStatusRejected', function() { it('sets the tx status to rejected', function() { - var tx = { id: 1, status: 'unapproved' } + var tx = { id: 1, status: 'unapproved', metamaskNetworkId: 'unit test' } txManager.addTx(tx) txManager.setTxStatusRejected(1) var result = txManager.getTxList() @@ -145,7 +145,7 @@ describe('Transaction Manager', function() { it('should emit a rejected event to signal the exciton of callback', (done) => { this.timeout(10000) - var tx = { id: 1, status: 'unapproved' } + var tx = { id: 1, status: 'unapproved', metamaskNetworkId: 'unit test' } txManager.addTx(tx) let onTxDoneCb = function (err, txId) { assert(true, 'event listener has been triggered and onTxDoneCb executed') @@ -159,9 +159,9 @@ describe('Transaction Manager', function() { describe('#updateTx', function() { it('replaces the tx with the same id', function() { - txManager.addTx({ id: '1', status: 'unapproved' }, onTxDoneCb) - txManager.addTx({ id: '2', status: 'confirmed' }, onTxDoneCb) - txManager.updateTx({ id: '1', status: 'blah', hash: 'foo' }) + txManager.addTx({ id: '1', status: 'unapproved', metamaskNetworkId: 'unit test' }, onTxDoneCb) + txManager.addTx({ id: '2', status: 'confirmed', metamaskNetworkId: 'unit test' }, onTxDoneCb) + txManager.updateTx({ id: '1', status: 'blah', hash: 'foo', metamaskNetworkId: 'unit test' }) var result = txManager.getTx('1') assert.equal(result.hash, 'foo') }) @@ -169,8 +169,8 @@ describe('Transaction Manager', function() { describe('#getUnapprovedTxList', function() { it('returns unapproved txs in a hash', function() { - txManager.addTx({ id: '1', status: 'unapproved' }, onTxDoneCb) - txManager.addTx({ id: '2', status: 'confirmed' }, onTxDoneCb) + txManager.addTx({ id: '1', status: 'unapproved', metamaskNetworkId: 'unit test' }, onTxDoneCb) + txManager.addTx({ id: '2', status: 'confirmed', metamaskNetworkId: 'unit test' }, onTxDoneCb) let result = txManager.getUnapprovedTxList() assert.equal(typeof result, 'object') assert.equal(result['1'].status, 'unapproved') @@ -180,8 +180,8 @@ describe('Transaction Manager', function() { describe('#getTx', function() { it('returns a tx with the requested id', function() { - txManager.addTx({ id: '1', status: 'unapproved' }, onTxDoneCb) - txManager.addTx({ id: '2', status: 'confirmed' }, onTxDoneCb) + txManager.addTx({ id: '1', status: 'unapproved', metamaskNetworkId: 'unit test' }, onTxDoneCb) + txManager.addTx({ id: '2', status: 'confirmed', metamaskNetworkId: 'unit test' }, onTxDoneCb) assert.equal(txManager.getTx('1').status, 'unapproved') assert.equal(txManager.getTx('2').status, 'confirmed') }) @@ -195,6 +195,7 @@ describe('Transaction Manager', function() { let everyOther = i % 2 txManager.addTx({ id: i, status: everyOther ? 'unapproved' : 'confirmed', + metamaskNetworkId: 'unit test', txParams: { from: everyOther ? 'foop' : 'zoop', to: everyOther ? 'zoop' : 'foop', From 91e5cc0f29df68dc57edc3404d90d969e4345bf2 Mon Sep 17 00:00:00 2001 From: kumavis Date: Sat, 14 Jan 2017 22:30:12 -0800 Subject: [PATCH 26/43] test - clear localStorage in case CI caches it (?) --- test/unit/notice-controller-test.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/unit/notice-controller-test.js b/test/unit/notice-controller-test.js index 4aa4c8e7b..e953fc580 100644 --- a/test/unit/notice-controller-test.js +++ b/test/unit/notice-controller-test.js @@ -6,6 +6,7 @@ const configManagerGen = require('../lib/mock-config-manager') const NoticeController = require('../../app/scripts/notice-controller') const STORAGE_KEY = 'metamask-persistance-key' // Hacking localStorage support into JSDom +if (window.localStorage) window.localStorage.clear() window.localStorage = {} describe('notice-controller', function() { From 85634326e8b7fd077b36d4b007190da2a66d6a89 Mon Sep 17 00:00:00 2001 From: kumavis Date: Sat, 14 Jan 2017 22:35:26 -0800 Subject: [PATCH 27/43] test - clear localStorage on test start --- test/unit/notice-controller-test.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/unit/notice-controller-test.js b/test/unit/notice-controller-test.js index e953fc580..cf00daeba 100644 --- a/test/unit/notice-controller-test.js +++ b/test/unit/notice-controller-test.js @@ -5,14 +5,14 @@ const nock = require('nock') const configManagerGen = require('../lib/mock-config-manager') const NoticeController = require('../../app/scripts/notice-controller') const STORAGE_KEY = 'metamask-persistance-key' -// Hacking localStorage support into JSDom -if (window.localStorage) window.localStorage.clear() -window.localStorage = {} describe('notice-controller', function() { var noticeController beforeEach(function() { + // simple localStorage polyfill + window.localStorage = {} + if (window.localStorage.clear) window.localStorage.clear() let configManager = configManagerGen() noticeController = new NoticeController({ configManager: configManager, From 6481f9ced425a03f9b5260f5d5606c506c6dfef6 Mon Sep 17 00:00:00 2001 From: Frankie Date: Sat, 14 Jan 2017 22:59:05 -0800 Subject: [PATCH 28/43] Dont render conf-tx if their are no txParams to show --- ui/app/conf-tx.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ui/app/conf-tx.js b/ui/app/conf-tx.js index 8d8285f3d..8e255a867 100644 --- a/ui/app/conf-tx.js +++ b/ui/app/conf-tx.js @@ -43,9 +43,11 @@ ConfirmTxScreen.prototype.render = function () { var unconfMsgs = state.unconfMsgs var unconfTxList = txHelper(unconfTxs, unconfMsgs, network) var index = state.index !== undefined ? state.index : 0 - var txData = unconfTxList[index] || {txParams: {}} - var txParams = txData.txParams || {} + var txData = unconfTxList[index] || {} + var txParams = txData.txParams var isNotification = isPopupOrNotification() === 'notification' + if (!txParams) return null + return ( h('.flex-column.flex-grow', [ From 83468cbd032533542fea934e57d35f9ccec895de Mon Sep 17 00:00:00 2001 From: Frankie Date: Sat, 14 Jan 2017 23:12:53 -0800 Subject: [PATCH 29/43] add to CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 113d2f7cd..47ea81606 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Current Master +- Fix Bug where you see a empty transaction flash by on the confirm transaction view. - Create visible difference in transaction history between a approved but not yet included in a block transaction and a transaction who has been confirmed. - Fix memory leak in RPC Cache - Override RPC commands eth_syncing and web3_clientVersion From ac6a2b4b61f34d3a60fc7cab926d390e172ed276 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Mon, 16 Jan 2017 10:59:01 -0800 Subject: [PATCH 30/43] Bump changelog to mention BIP44 fix --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 826bf9bcc..2c50ce4b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Current Master +- Fix seed word account generation (https://medium.com/metamask/metamask-3-migration-guide-914b79533cdd#.t4i1qmmsz). - Fix Bug where you see a empty transaction flash by on the confirm transaction view. - Create visible difference in transaction history between a approved but not yet included in a block transaction and a transaction who has been confirmed. - Fix memory leak in RPC Cache From e7cf0f4bdd537b69d27afa3c788f67b758d02c05 Mon Sep 17 00:00:00 2001 From: kumavis Date: Mon, 16 Jan 2017 11:49:31 -0800 Subject: [PATCH 31/43] keyring - simple - fix address generation --- app/scripts/keyrings/simple.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/scripts/keyrings/simple.js b/app/scripts/keyrings/simple.js index 9717f1c45..6f4512b70 100644 --- a/app/scripts/keyrings/simple.js +++ b/app/scripts/keyrings/simple.js @@ -35,12 +35,12 @@ class SimpleKeyring extends EventEmitter { newWallets.push(Wallet.generate()) } this.wallets = this.wallets.concat(newWallets) - const hexWallets = newWallets.map(w => w.getAddress().toString('hex')) + const hexWallets = newWallets.map(w => ethUtil.bufferToHex(w.getAddress())) return Promise.resolve(hexWallets) } getAccounts () { - return Promise.resolve(this.wallets.map(w => w.getAddress().toString('hex'))) + return Promise.resolve(this.wallets.map(w => ethUtil.bufferToHex(w.getAddress()))) } // tx is an instance of the ethereumjs-transaction class. @@ -70,7 +70,7 @@ class SimpleKeyring extends EventEmitter { /* PRIVATE METHODS */ _getWalletForAccount (account) { - return this.wallets.find(w => w.getAddress().toString('hex') === account) + return this.wallets.find(w => ethUtil.bufferToHex(w.getAddress()) === account) } } From 82012cbbce47c691d322b290ad9e77723b289f0b Mon Sep 17 00:00:00 2001 From: kumavis Date: Mon, 16 Jan 2017 11:54:59 -0800 Subject: [PATCH 32/43] keyring - simple - throw error if wallet not found for address --- app/scripts/keyrings/simple.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/scripts/keyrings/simple.js b/app/scripts/keyrings/simple.js index 6f4512b70..6b16137ae 100644 --- a/app/scripts/keyrings/simple.js +++ b/app/scripts/keyrings/simple.js @@ -54,6 +54,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) var privKey = wallet.getPrivateKey() var msgSig = ethUtil.ecsign(new Buffer(message, 'hex'), privKey) @@ -70,7 +71,9 @@ class SimpleKeyring extends EventEmitter { /* PRIVATE METHODS */ _getWalletForAccount (account) { - return this.wallets.find(w => ethUtil.bufferToHex(w.getAddress()) === account) + let wallet = this.wallets.find(w => ethUtil.bufferToHex(w.getAddress()) === account) + if (!wallet) throw new Error('Simple Keyring - Unable to find matching address.') + return wallet } } From 4a09f856d11660db3118d05f98eb410d3b208b56 Mon Sep 17 00:00:00 2001 From: kumavis Date: Mon, 16 Jan 2017 12:08:20 -0800 Subject: [PATCH 33/43] test - keyring - simple - fix fixture data --- test/unit/keyrings/simple-test.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/test/unit/keyrings/simple-test.js b/test/unit/keyrings/simple-test.js index 979abdb69..687318f99 100644 --- a/test/unit/keyrings/simple-test.js +++ b/test/unit/keyrings/simple-test.js @@ -1,5 +1,6 @@ const assert = require('assert') const extend = require('xtend') +const ethUtil = require('ethereumjs-util') const SimpleKeyring = require('../../../app/scripts/keyrings/simple') const TYPE_STR = 'Simple Key Pair' @@ -72,14 +73,10 @@ describe('simple-keyring', function() { it('calls getAddress on each wallet', function(done) { // Push a mock wallet - const desiredOutput = 'foo' + const desiredOutput = '0x18a3462427bcc9133bb46e88bcbe39cd7ef0e761' keyring.wallets.push({ getAddress() { - return { - toString() { - return desiredOutput - } - } + return ethUtil.toBuffer(desiredOutput) } }) From 9203b8c30525e37c3a8760b1d6146add099f4490 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Mon, 16 Jan 2017 14:14:06 -0800 Subject: [PATCH 34/43] Version 3.0.0 --- CHANGELOG.md | 2 ++ app/manifest.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c50ce4b8..bca599344 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Current Master +## 3.0.0 2017-1-16 + - Fix seed word account generation (https://medium.com/metamask/metamask-3-migration-guide-914b79533cdd#.t4i1qmmsz). - Fix Bug where you see a empty transaction flash by on the confirm transaction view. - Create visible difference in transaction history between a approved but not yet included in a block transaction and a transaction who has been confirmed. diff --git a/app/manifest.json b/app/manifest.json index 95dcfc31a..9eb7a8eeb 100644 --- a/app/manifest.json +++ b/app/manifest.json @@ -1,7 +1,7 @@ { "name": "MetaMask", "short_name": "Metamask", - "version": "2.14.1", + "version": "2.15.0", "manifest_version": 2, "author": "https://metamask.io", "description": "Ethereum Browser Extension", From 5de6eaf35d9800ba4b64487d602155b3cb2cc956 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Mon, 16 Jan 2017 15:02:52 -0800 Subject: [PATCH 35/43] Fix manifest version --- app/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/manifest.json b/app/manifest.json index 9eb7a8eeb..9c6558d15 100644 --- a/app/manifest.json +++ b/app/manifest.json @@ -1,7 +1,7 @@ { "name": "MetaMask", "short_name": "Metamask", - "version": "2.15.0", + "version": "3.0.0", "manifest_version": 2, "author": "https://metamask.io", "description": "Ethereum Browser Extension", From fe6c84e40b92cbbffdb6f68104f3c2b449210aec Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Mon, 16 Jan 2017 15:41:28 -0800 Subject: [PATCH 36/43] Remove old notice This notice is not relevant to new users, should be removed. --- notices/notice_0.md | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 notices/notice_0.md 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 - From 7e886dff778f7772f5664491a1255614c08483f2 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Mon, 16 Jan 2017 23:26:37 -0800 Subject: [PATCH 37/43] Wrote failing test for eth.sign --- test/unit/keyrings/simple-test.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) 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() { From d5ad84aa125280e86c5ae3c3cec5a0f73dfb35fe Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Mon, 16 Jan 2017 23:26:48 -0800 Subject: [PATCH 38/43] Wrote fix for eth.sign --- app/scripts/keyrings/simple.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/scripts/keyrings/simple.js b/app/scripts/keyrings/simple.js index 6b16137ae..d604430b8 100644 --- a/app/scripts/keyrings/simple.js +++ b/app/scripts/keyrings/simple.js @@ -54,8 +54,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)) From 665d6c5406c1a3f635592cbcf6827d411314d2bd Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Mon, 16 Jan 2017 23:58:25 -0800 Subject: [PATCH 39/43] Bump changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bca599344..d30c34144 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Current Master +- Fixed bug that prevented eth.sign from working. + ## 3.0.0 2017-1-16 - Fix seed word account generation (https://medium.com/metamask/metamask-3-migration-guide-914b79533cdd#.t4i1qmmsz). From 54c536e72cf328e1141de6cbd0eb2e1e24b7afce Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Mon, 16 Jan 2017 23:59:25 -0800 Subject: [PATCH 40/43] Fix message signing UI rendering [A recent PR](https://github.com/MetaMask/metamask-plugin/pull/1004) removed logic that rendered pending messages for approval. This commit fixes that. --- ui/app/conf-tx.js | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) 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] From 7ae2e005eda08907cbab33a42ecc0c9d899c6802 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Tue, 17 Jan 2017 00:03:56 -0800 Subject: [PATCH 41/43] Fix removeHexPrefix to stripHexPrefix --- app/scripts/keyrings/hd.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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)) From 8fcade92d309d363ca223e4ec4aceaee0c330b68 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Tue, 17 Jan 2017 00:13:38 -0800 Subject: [PATCH 42/43] Fix bug where signed messages were not dismissed --- app/scripts/keyring-controller.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app/scripts/keyring-controller.js b/app/scripts/keyring-controller.js index 79cfe6fbd..4be00a5a5 100644 --- a/app/scripts/keyring-controller.js +++ b/app/scripts/keyring-controller.js @@ -397,6 +397,7 @@ module.exports = class KeyringController extends EventEmitter { }).then((rawSig) => { cb(null, rawSig) approvalCb(null, true) + messageManager.confirmMsg(msgId) return rawSig }) } catch (e) { From a208ed1d83fd72b0dcc5146a733d8cd506a5d2a4 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Tue, 17 Jan 2017 00:14:25 -0800 Subject: [PATCH 43/43] Version 3.0.1 --- CHANGELOG.md | 2 ++ app/manifest.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d30c34144..f7306146c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Current Master +## 3.0.1 2017-1-17 + - Fixed bug that prevented eth.sign from working. ## 3.0.0 2017-1-16 diff --git a/app/manifest.json b/app/manifest.json index 9c6558d15..a13b43ca7 100644 --- a/app/manifest.json +++ b/app/manifest.json @@ -1,7 +1,7 @@ { "name": "MetaMask", "short_name": "Metamask", - "version": "3.0.0", + "version": "3.0.1", "manifest_version": 2, "author": "https://metamask.io", "description": "Ethereum Browser Extension",