From a171b9b847bf0a889d1a29f51ddd475da8061874 Mon Sep 17 00:00:00 2001 From: Anatoli Babenia Date: Tue, 5 Dec 2017 00:46:19 +0300 Subject: [PATCH 01/42] Download State Logs with .json extension That doesn't fix #2095, but at least makes downloaded files syntax-highlighted when opened in editors --- ui/app/config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/app/config.js b/ui/app/config.js index c14fa1d28..9cb2a0aad 100644 --- a/ui/app/config.js +++ b/ui/app/config.js @@ -117,7 +117,7 @@ ConfigScreen.prototype.render = function () { if (err) { state.dispatch(actions.displayWarning('Error in retrieving state logs.')) } else { - exportAsFile('MetaMask State Logs', result) + exportAsFile('MetaMask State Logs.json', result) } }) }, From 03f86acd60655cf73a8f52dbe503df71e9d2b718 Mon Sep 17 00:00:00 2001 From: William Chong Date: Tue, 5 Dec 2017 19:25:35 +0800 Subject: [PATCH 02/42] fix #1398, prevent injecting xml without xml suffix --- app/scripts/contentscript.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/app/scripts/contentscript.js b/app/scripts/contentscript.js index ffbbc73cc..2ed7c87b6 100644 --- a/app/scripts/contentscript.js +++ b/app/scripts/contentscript.js @@ -96,7 +96,7 @@ function logStreamDisconnectWarning (remoteLabel, err) { } function shouldInjectWeb3 () { - return doctypeCheck() || suffixCheck() + return doctypeCheck() && suffixCheck() && documentElementCheck() } function doctypeCheck () { @@ -104,7 +104,7 @@ function doctypeCheck () { if (doctype) { return doctype.name === 'html' } else { - return false + return true } } @@ -121,6 +121,14 @@ function suffixCheck () { return true } +function documentElementCheck () { + var documentElement = document.documentElement.nodeName + if (documentElement) { + return documentElement.toLowerCase() === 'html' + } + return true +} + function redirectToPhishingWarning () { console.log('MetaMask - redirecting to phishing warning') window.location.href = 'https://metamask.io/phishing.html' From 7854321faeeb8184b3171e3b214f57d6e61c0814 Mon Sep 17 00:00:00 2001 From: vicnaum Date: Wed, 6 Dec 2017 22:49:24 +0100 Subject: [PATCH 03/42] Fix for #2644 - Specified Nonce isn't used Added nonceSpecified. And a check if nonce was specified - then we should use the specified nonce instead of generating a new one. --- app/scripts/controllers/transactions.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/scripts/controllers/transactions.js b/app/scripts/controllers/transactions.js index ce709bd28..060484b87 100644 --- a/app/scripts/controllers/transactions.js +++ b/app/scripts/controllers/transactions.js @@ -177,6 +177,7 @@ module.exports = class TransactionController extends EventEmitter { const txParams = txMeta.txParams // ensure value txMeta.gasPriceSpecified = Boolean(txParams.gasPrice) + txMeta.nonceSpecified = Boolean(txParams.nonce) const gasPrice = txParams.gasPrice || await this.query.gasPrice() txParams.gasPrice = ethUtil.addHexPrefix(gasPrice.toString(16)) txParams.value = txParams.value || '0x0' @@ -200,7 +201,11 @@ module.exports = class TransactionController extends EventEmitter { // wait for a nonce nonceLock = await this.nonceTracker.getNonceLock(fromAddress) // add nonce to txParams - txMeta.txParams.nonce = ethUtil.addHexPrefix(nonceLock.nextNonce.toString(16)) + if (txMeta.nonceSpecified) { + txMeta.txParams.nonce = ethUtil.addHexPrefix(txMeta.txParams.nonce.toString(16)) + } else { + txMeta.txParams.nonce = ethUtil.addHexPrefix(nonceLock.nextNonce.toString(16)) + } // add nonce debugging information to txMeta txMeta.nonceDetails = nonceLock.nonceDetails this.txStateManager.updateTx(txMeta, 'transactions#approveTransaction') From 6ff580584a74c6d85f54ce7cfc500db822904957 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 6 Dec 2017 22:20:11 -0500 Subject: [PATCH 04/42] Add retry background method and action --- app/scripts/controllers/transactions.js | 4 ++++ app/scripts/lib/tx-state-manager.js | 4 ++++ app/scripts/metamask-controller.js | 1 + ui/app/actions.js | 6 ++++++ ui/app/components/transaction-list-item.js | 10 +++++++++- 5 files changed, 24 insertions(+), 1 deletion(-) diff --git a/app/scripts/controllers/transactions.js b/app/scripts/controllers/transactions.js index ce709bd28..67043b401 100644 --- a/app/scripts/controllers/transactions.js +++ b/app/scripts/controllers/transactions.js @@ -184,6 +184,10 @@ module.exports = class TransactionController extends EventEmitter { return await this.txGasUtil.analyzeGasUsage(txMeta) } + async retryTransaction (txId) { + return this.txStateManager.setTxStatusUnapproved(txId) + } + async updateAndApproveTransaction (txMeta) { this.txStateManager.updateTx(txMeta, 'confTx: user approved transaction') await this.approveTransaction(txMeta.id) diff --git a/app/scripts/lib/tx-state-manager.js b/app/scripts/lib/tx-state-manager.js index 0fd6bed4b..cc441c584 100644 --- a/app/scripts/lib/tx-state-manager.js +++ b/app/scripts/lib/tx-state-manager.js @@ -187,6 +187,10 @@ module.exports = class TransactionStateManger extends EventEmitter { this._setTxStatus(txId, 'rejected') } + // should update the status of the tx to 'unapproved'. + setTxStatusUnapproved (txId) { + this._setTxStatus(txId, 'unapproved') + } // should update the status of the tx to 'approved'. setTxStatusApproved (txId) { this._setTxStatus(txId, 'approved') diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 130ad1471..3a8100d12 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -363,6 +363,7 @@ module.exports = class MetamaskController extends EventEmitter { // txController cancelTransaction: nodeify(txController.cancelTransaction, txController), updateAndApproveTransaction: nodeify(txController.updateAndApproveTransaction, txController), + retryTransaction: nodeify(txController.retryTransaction, txController), // messageManager signMessage: nodeify(this.signMessage, this), diff --git a/ui/app/actions.js b/ui/app/actions.js index 04fd35b20..2ab68b62d 100644 --- a/ui/app/actions.js +++ b/ui/app/actions.js @@ -168,6 +168,7 @@ var actions = { callBackgroundThenUpdate, forceUpdateMetamaskState, + retryTransaction, } module.exports = actions @@ -759,6 +760,11 @@ function markAccountsFound () { return callBackgroundThenUpdate(background.markAccountsFound) } +function retryTransaction () { + log.debug(`background.retryTransaction`) + return callBackgroundThenUpdate(background.retryTransaction) +} + // // config // diff --git a/ui/app/components/transaction-list-item.js b/ui/app/components/transaction-list-item.js index 891d5e227..ddef4a4ae 100644 --- a/ui/app/components/transaction-list-item.js +++ b/ui/app/components/transaction-list-item.js @@ -9,6 +9,7 @@ const CopyButton = require('./copyButton') const vreme = new (require('vreme'))() const Tooltip = require('./tooltip') const numberToBN = require('number-to-bn') +const actions = require('../actions') const TransactionIcon = require('./transaction-list-item-icon') const ShiftListItem = require('./shift-list-item') @@ -21,6 +22,7 @@ function TransactionListItem () { TransactionListItem.prototype.render = function () { const { transaction, network, conversionRate, currentCurrency } = this.props + const { status } = transaction if (transaction.key === 'shapeshift') { if (network === '1') return h(ShiftListItem, transaction) } @@ -32,7 +34,7 @@ TransactionListItem.prototype.render = function () { var isMsg = ('msgParams' in transaction) var isTx = ('txParams' in transaction) - var isPending = transaction.status === 'unapproved' + var isPending = status === 'unapproved' let txParams if (isTx) { txParams = transaction.txParams @@ -97,10 +99,16 @@ TransactionListItem.prototype.render = function () { showFiat: false, style: {fontSize: '15px'}, }) : h('.flex-column'), + ]) ) } +TransactionListItem.prototype.resubmit = function () { + const { transaction } = this.props + this.props.dispatch(actions.resubmitTx(transaction.id)) +} + function domainField (txParams) { return h('div', { style: { From 500fbe450a10e1a3c756707e44225b196601372e Mon Sep 17 00:00:00 2001 From: Alexander Tseung Date: Wed, 6 Dec 2017 22:22:40 -0500 Subject: [PATCH 05/42] Add button to retry transaction --- ui/app/components/transaction-list-item.js | 103 ++++++++++++++------- 1 file changed, 69 insertions(+), 34 deletions(-) diff --git a/ui/app/components/transaction-list-item.js b/ui/app/components/transaction-list-item.js index ddef4a4ae..9c512a89d 100644 --- a/ui/app/components/transaction-list-item.js +++ b/ui/app/components/transaction-list-item.js @@ -46,7 +46,7 @@ TransactionListItem.prototype.render = function () { const isClickable = ('hash' in transaction && isLinkable) || isPending return ( - h(`.transaction-list-item.flex-row.flex-space-between${isClickable ? '.pointer' : ''}`, { + h('.transaction-list-item.flex-column', { onClick: (event) => { if (isPending) { this.props.showTx(transaction.id) @@ -58,48 +58,83 @@ TransactionListItem.prototype.render = function () { }, style: { padding: '20px 0', + alignItems: 'center', }, }, [ + h(`.flex-row.flex-space-between${isClickable ? '.pointer' : ''}`, { + style: { + width: '100%', + }, + }, [ + h('.identicon-wrapper.flex-column.flex-center.select-none', [ + h(TransactionIcon, { txParams, transaction, isTx, isMsg }), + ]), - h('.identicon-wrapper.flex-column.flex-center.select-none', [ - h(TransactionIcon, { txParams, transaction, isTx, isMsg }), + h(Tooltip, { + title: 'Transaction Number', + position: 'right', + }, [ + h('span', { + style: { + display: 'flex', + cursor: 'normal', + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'center', + padding: '10px', + }, + }, nonce), + ]), + + h('.flex-column', {style: {width: '200px', overflow: 'hidden'}}, [ + domainField(txParams), + h('div', date), + recipientField(txParams, transaction, isTx, isMsg), + ]), + + // Places a copy button if tx is successful, else places a placeholder empty div. + transaction.hash ? h(CopyButton, { value: transaction.hash }) : h('div', {style: { display: 'flex', alignItems: 'center', width: '26px' }}), + + isTx ? h(EthBalance, { + value: txParams.value, + conversionRate, + currentCurrency, + width: '55px', + shorten: true, + showFiat: false, + style: {fontSize: '15px'}, + }) : h('.flex-column'), ]), - h(Tooltip, { - title: 'Transaction Number', - position: 'right', + transaction.status === 'submitted' && h('.transition-list-item__retry', { + onClick: event => { + event.stopPropagation() + this.resubmit() + }, + style: { + height: '30px', + borderRadius: '30px', + color: '#F9881B', + padding: '0 25px', + backgroundColor: '#FFE3C9', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + fontSize: '9px', + cursor: 'pointer', + }, }, [ - h('span', { + h('div', { style: { - display: 'flex', - cursor: 'normal', - flexDirection: 'column', - alignItems: 'center', - justifyContent: 'center', - padding: '10px', + paddingRight: '2px', }, - }, nonce), - ]), - - h('.flex-column', {style: {width: '200px', overflow: 'hidden'}}, [ - domainField(txParams), - h('div', date), - recipientField(txParams, transaction, isTx, isMsg), + }, 'Taking too long?'), + h('div', { + style: { + textDecoration: 'underline', + }, + }, 'Retry with a higher gas price here'), ]), - - // Places a copy button if tx is successful, else places a placeholder empty div. - transaction.hash ? h(CopyButton, { value: transaction.hash }) : h('div', {style: { display: 'flex', alignItems: 'center', width: '26px' }}), - - isTx ? h(EthBalance, { - value: txParams.value, - conversionRate, - currentCurrency, - width: '55px', - shorten: true, - showFiat: false, - style: {fontSize: '15px'}, - }) : h('.flex-column'), - ]) ) } From 02736d2d361a415f145404a96f553c73e3707eb5 Mon Sep 17 00:00:00 2001 From: Alexander Tseung Date: Wed, 6 Dec 2017 22:48:26 -0500 Subject: [PATCH 06/42] Connect to redux for retryTransaction --- ui/app/components/transaction-list-item.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/ui/app/components/transaction-list-item.js b/ui/app/components/transaction-list-item.js index 9c512a89d..56e90e26c 100644 --- a/ui/app/components/transaction-list-item.js +++ b/ui/app/components/transaction-list-item.js @@ -1,6 +1,7 @@ const Component = require('react').Component const h = require('react-hyperscript') const inherits = require('util').inherits +const connect = require('react-redux').connect const EthBalance = require('./eth-balance') const addressSummary = require('../util').addressSummary @@ -13,7 +14,14 @@ const actions = require('../actions') const TransactionIcon = require('./transaction-list-item-icon') const ShiftListItem = require('./shift-list-item') -module.exports = TransactionListItem + +const mapDispatchToProps = dispatch => { + return { + retryTransaction: transactionId => dispatch(actions.retryTransaction(transactionId)), + } +} + +module.exports = connect(null, mapDispatchToProps)(TransactionListItem) inherits(TransactionListItem, Component) function TransactionListItem () { @@ -141,7 +149,7 @@ TransactionListItem.prototype.render = function () { TransactionListItem.prototype.resubmit = function () { const { transaction } = this.props - this.props.dispatch(actions.resubmitTx(transaction.id)) + this.props.retryTransaction(transaction.id) } function domainField (txParams) { From 1bd5fc1ba40dac52a71099db7d5649f9704b0ee3 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 6 Dec 2017 22:55:04 -0500 Subject: [PATCH 07/42] Add development state --- development/states/pending-tx.json | 739 +++++++++++++++++++++++++++++ 1 file changed, 739 insertions(+) create mode 100644 development/states/pending-tx.json diff --git a/development/states/pending-tx.json b/development/states/pending-tx.json new file mode 100644 index 000000000..bfa93f7ae --- /dev/null +++ b/development/states/pending-tx.json @@ -0,0 +1,739 @@ +{ + "metamask": { + "isInitialized": true, + "isUnlocked": true, + "isMascara": false, + "rpcTarget": "https://rawtestrpc.metamask.io/", + "identities": { + "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825": { + "address": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825", + "name": "Account 1" + } + }, + "unapprovedTxs": {}, + "noActiveNotices": true, + "frequentRpcList": [ + "http://192.168.1.34:7545/" + ], + "addressBook": [], + "tokenExchangeRates": {}, + "coinOptions": {}, + "provider": { + "type": "mainnet", + "rpcTarget": "https://mainnet.infura.io/metamask" + }, + "network": "1", + "accounts": { + "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825": { + "code": "0x", + "balance": "0x1b3f641ed0c2f62", + "nonce": "0x35", + "address": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825" + } + }, + "currentBlockGasLimit": "0x66df83", + "selectedAddressTxList": [ + { + "id": 3516145537630216, + "time": 1512615655535, + "status": "submitted", + "metamaskNetworkId": "1", + "txParams": { + "from": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825", + "to": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825", + "value": "0x16345785d8a0000", + "gasPrice": "0xc1b710800", + "gas": "0x7b0c", + "nonce": "0x35", + "chainId": "0x1" + }, + "gasPriceSpecified": false, + "gasLimitSpecified": false, + "estimatedGas": "5208", + "history": [ + { + "id": 3516145537630216, + "time": 1512615655535, + "status": "unapproved", + "metamaskNetworkId": "1", + "txParams": { + "from": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825", + "to": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825", + "value": "0x16345785d8a0000", + "gasPrice": "0xe6f7cec00", + "gas": "0x7b0c" + }, + "gasPriceSpecified": false, + "gasLimitSpecified": false, + "estimatedGas": "5208" + }, + [ + { + "op": "replace", + "path": "/txParams/gasPrice", + "value": "0xc1b710800", + "note": "confTx: user approved transaction" + } + ], + [ + { + "op": "replace", + "path": "/status", + "value": "approved", + "note": "txStateManager: setting status to approved" + } + ], + [ + { + "op": "add", + "path": "/txParams/nonce", + "value": "0x35", + "note": "transactions#approveTransaction" + }, + { + "op": "add", + "path": "/nonceDetails", + "value": { + "params": { + "highestLocalNonce": 53, + "highestSuggested": 53, + "nextNetworkNonce": 53 + }, + "local": { + "name": "local", + "nonce": 53, + "details": { + "startPoint": 53, + "highest": 53 + } + }, + "network": { + "name": "network", + "nonce": 53, + "details": { + "baseCount": 53 + } + } + } + } + ], + [ + { + "op": "add", + "path": "/txParams/chainId", + "value": "0x1", + "note": "txStateManager: setting status to signed" + }, + { + "op": "replace", + "path": "/status", + "value": "signed" + } + ], + [ + { + "op": "add", + "path": "/rawTx", + "value": "0xf86c35850c1b710800827b0c94fdea65c8e26263f6d9a1b5de9555d2931a33b82588016345785d8a00008026a0f5142ba79a13ca7ec65548953017edafb217803244bbf9821d9ad077d89921e9a03afcb614169c90be9905d5b469d06984825c76675d3a535937cdb8f2ad1c0a95", + "note": "transactions#publishTransaction" + } + ], + [ + { + "op": "add", + "path": "/hash", + "value": "0x7ce19c0d128ca11293b44a4e6d3cc9063665c00ea8c8eb400f548e132c147353", + "note": "transactions#setTxHash" + } + ], + [ + { + "op": "replace", + "path": "/status", + "value": "submitted", + "note": "txStateManager: setting status to submitted" + } + ], + [ + { + "op": "add", + "path": "/firstRetryBlockNumber", + "value": "0x478ab3", + "note": "transactions/pending-tx-tracker#event: tx:block-update" + } + ] + ], + "nonceDetails": { + "params": { + "highestLocalNonce": 53, + "highestSuggested": 53, + "nextNetworkNonce": 53 + }, + "local": { + "name": "local", + "nonce": 53, + "details": { + "startPoint": 53, + "highest": 53 + } + }, + "network": { + "name": "network", + "nonce": 53, + "details": { + "baseCount": 53 + } + } + }, + "rawTx": "0xf86c35850c1b710800827b0c94fdea65c8e26263f6d9a1b5de9555d2931a33b82588016345785d8a00008026a0f5142ba79a13ca7ec65548953017edafb217803244bbf9821d9ad077d89921e9a03afcb614169c90be9905d5b469d06984825c76675d3a535937cdb8f2ad1c0a95", + "hash": "0x7ce19c0d128ca11293b44a4e6d3cc9063665c00ea8c8eb400f548e132c147353", + "firstRetryBlockNumber": "0x478ab3" + }, + { + "id": 3516145537630211, + "time": 1512613432658, + "status": "confirmed", + "metamaskNetworkId": "1", + "txParams": { + "from": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825", + "to": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825", + "value": "0x16345785d8a0000", + "gasPrice": "0xba43b7400", + "gas": "0x7b0c", + "nonce": "0x34", + "chainId": "0x1" + }, + "gasPriceSpecified": false, + "gasLimitSpecified": false, + "estimatedGas": "5208", + "history": [ + { + "id": 3516145537630211, + "time": 1512613432658, + "status": "unapproved", + "metamaskNetworkId": "1", + "txParams": { + "from": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825", + "to": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825", + "value": "0x16345785d8a0000", + "gasPrice": "0xdf8475800", + "gas": "0x7b0c" + }, + "gasPriceSpecified": false, + "gasLimitSpecified": false, + "estimatedGas": "5208" + }, + [ + { + "op": "replace", + "path": "/txParams/gasPrice", + "value": "0xba43b7400", + "note": "confTx: user approved transaction" + } + ], + [ + { + "op": "replace", + "path": "/status", + "value": "approved", + "note": "txStateManager: setting status to approved" + } + ], + [ + { + "op": "add", + "path": "/txParams/nonce", + "value": "0x34", + "note": "transactions#approveTransaction" + }, + { + "op": "add", + "path": "/nonceDetails", + "value": { + "params": { + "highestLocalNonce": 52, + "highestSuggested": 52, + "nextNetworkNonce": 52 + }, + "local": { + "name": "local", + "nonce": 52, + "details": { + "startPoint": 52, + "highest": 52 + } + }, + "network": { + "name": "network", + "nonce": 52, + "details": { + "baseCount": 52 + } + } + } + } + ], + [ + { + "op": "add", + "path": "/txParams/chainId", + "value": "0x1", + "note": "txStateManager: setting status to signed" + }, + { + "op": "replace", + "path": "/status", + "value": "signed" + } + ], + [ + { + "op": "add", + "path": "/rawTx", + "value": "0xf86c34850ba43b7400827b0c94fdea65c8e26263f6d9a1b5de9555d2931a33b82588016345785d8a00008026a073a4afdb8e8ad32b0cf9039af56c66baffd60d30e75cee5c1b783208824eafb8a0021ca6c1714a2c71281333ab77f776d3514348ab77967280fca8a5b4be44285e", + "note": "transactions#publishTransaction" + } + ], + [ + { + "op": "add", + "path": "/hash", + "value": "0x5c98409883fdfd3cd24058a83b91470da6c40ffae41a40eb90d7dee0b837d26d", + "note": "transactions#setTxHash" + } + ], + [ + { + "op": "replace", + "path": "/status", + "value": "submitted", + "note": "txStateManager: setting status to submitted" + } + ], + [ + { + "op": "add", + "path": "/firstRetryBlockNumber", + "value": "0x478a2c", + "note": "transactions/pending-tx-tracker#event: tx:block-update" + } + ], + [ + { + "op": "replace", + "path": "/status", + "value": "confirmed", + "note": "txStateManager: setting status to confirmed" + } + ] + ], + "nonceDetails": { + "params": { + "highestLocalNonce": 52, + "highestSuggested": 52, + "nextNetworkNonce": 52 + }, + "local": { + "name": "local", + "nonce": 52, + "details": { + "startPoint": 52, + "highest": 52 + } + }, + "network": { + "name": "network", + "nonce": 52, + "details": { + "baseCount": 52 + } + } + }, + "rawTx": "0xf86c34850ba43b7400827b0c94fdea65c8e26263f6d9a1b5de9555d2931a33b82588016345785d8a00008026a073a4afdb8e8ad32b0cf9039af56c66baffd60d30e75cee5c1b783208824eafb8a0021ca6c1714a2c71281333ab77f776d3514348ab77967280fca8a5b4be44285e", + "hash": "0x5c98409883fdfd3cd24058a83b91470da6c40ffae41a40eb90d7dee0b837d26d", + "firstRetryBlockNumber": "0x478a2c" + }, + { + "id": 3516145537630210, + "time": 1512612826136, + "status": "confirmed", + "metamaskNetworkId": "1", + "txParams": { + "from": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825", + "to": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825", + "value": "0x16345785d8a0000", + "gasPrice": "0xa7a358200", + "gas": "0x7b0c", + "nonce": "0x33", + "chainId": "0x1" + }, + "gasPriceSpecified": false, + "gasLimitSpecified": false, + "estimatedGas": "5208", + "history": [ + { + "id": 3516145537630210, + "time": 1512612826136, + "status": "unapproved", + "metamaskNetworkId": "1", + "txParams": { + "from": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825", + "to": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825", + "value": "0x16345785d8a0000", + "gasPrice": "0xba43b7400", + "gas": "0x7b0c" + }, + "gasPriceSpecified": false, + "gasLimitSpecified": false, + "estimatedGas": "5208" + }, + [ + { + "op": "replace", + "path": "/txParams/gasPrice", + "value": "0xa7a358200", + "note": "confTx: user approved transaction" + } + ], + [ + { + "op": "replace", + "path": "/status", + "value": "approved", + "note": "txStateManager: setting status to approved" + } + ], + [ + { + "op": "add", + "path": "/txParams/nonce", + "value": "0x33", + "note": "transactions#approveTransaction" + }, + { + "op": "add", + "path": "/nonceDetails", + "value": { + "params": { + "highestLocalNonce": 0, + "highestSuggested": 51, + "nextNetworkNonce": 51 + }, + "local": { + "name": "local", + "nonce": 51, + "details": { + "startPoint": 51, + "highest": 51 + } + }, + "network": { + "name": "network", + "nonce": 51, + "details": { + "baseCount": 51 + } + } + } + } + ], + [ + { + "op": "add", + "path": "/txParams/chainId", + "value": "0x1", + "note": "txStateManager: setting status to signed" + }, + { + "op": "replace", + "path": "/status", + "value": "signed" + } + ], + [ + { + "op": "add", + "path": "/rawTx", + "value": "0xf86c33850a7a358200827b0c94fdea65c8e26263f6d9a1b5de9555d2931a33b82588016345785d8a00008026a0021a8cd6c10208cc593e22af53637e5d127cee5cc6f9443a3e758a02afff1d7ca025f7420e974d3f2c668c165040987c72543a8e709bfea3528a62836a6ced9ce8", + "note": "transactions#publishTransaction" + } + ], + [ + { + "op": "add", + "path": "/hash", + "value": "0x289772800898bc9cd414530d8581c0da257a9055e4aaaa6d10d92d700bfbd044", + "note": "transactions#setTxHash" + } + ], + [ + { + "op": "replace", + "path": "/status", + "value": "submitted", + "note": "txStateManager: setting status to submitted" + } + ], + [ + { + "op": "add", + "path": "/firstRetryBlockNumber", + "value": "0x478a04", + "note": "transactions/pending-tx-tracker#event: tx:block-update" + } + ], + [ + { + "op": "replace", + "path": "/status", + "value": "confirmed", + "note": "txStateManager: setting status to confirmed" + } + ] + ], + "nonceDetails": { + "params": { + "highestLocalNonce": 0, + "highestSuggested": 51, + "nextNetworkNonce": 51 + }, + "local": { + "name": "local", + "nonce": 51, + "details": { + "startPoint": 51, + "highest": 51 + } + }, + "network": { + "name": "network", + "nonce": 51, + "details": { + "baseCount": 51 + } + } + }, + "rawTx": "0xf86c33850a7a358200827b0c94fdea65c8e26263f6d9a1b5de9555d2931a33b82588016345785d8a00008026a0021a8cd6c10208cc593e22af53637e5d127cee5cc6f9443a3e758a02afff1d7ca025f7420e974d3f2c668c165040987c72543a8e709bfea3528a62836a6ced9ce8", + "hash": "0x289772800898bc9cd414530d8581c0da257a9055e4aaaa6d10d92d700bfbd044", + "firstRetryBlockNumber": "0x478a04" + }, + { + "id": 3516145537630209, + "time": 1512612809252, + "status": "failed", + "metamaskNetworkId": "1", + "txParams": { + "from": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825", + "to": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825", + "value": "0x16345785d8a0000", + "gasPrice": "0x77359400", + "gas": "0x7b0c", + "nonce": "0x33", + "chainId": "0x1" + }, + "gasPriceSpecified": false, + "gasLimitSpecified": false, + "estimatedGas": "5208", + "history": [ + { + "id": 3516145537630209, + "time": 1512612809252, + "status": "unapproved", + "metamaskNetworkId": "1", + "txParams": { + "from": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825", + "to": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825", + "value": "0x16345785d8a0000", + "gasPrice": "0xba43b7400", + "gas": "0x7b0c" + }, + "gasPriceSpecified": false, + "gasLimitSpecified": false, + "estimatedGas": "5208" + }, + [ + { + "op": "replace", + "path": "/txParams/gasPrice", + "value": "0x77359400", + "note": "confTx: user approved transaction" + } + ], + [ + { + "op": "replace", + "path": "/status", + "value": "approved", + "note": "txStateManager: setting status to approved" + } + ], + [ + { + "op": "add", + "path": "/txParams/nonce", + "value": "0x33", + "note": "transactions#approveTransaction" + }, + { + "op": "add", + "path": "/nonceDetails", + "value": { + "params": { + "highestLocalNonce": 0, + "highestSuggested": 51, + "nextNetworkNonce": 51 + }, + "local": { + "name": "local", + "nonce": 51, + "details": { + "startPoint": 51, + "highest": 51 + } + }, + "network": { + "name": "network", + "nonce": 51, + "details": { + "baseCount": 51 + } + } + } + } + ], + [ + { + "op": "add", + "path": "/txParams/chainId", + "value": "0x1", + "note": "txStateManager: setting status to signed" + }, + { + "op": "replace", + "path": "/status", + "value": "signed" + } + ], + [ + { + "op": "add", + "path": "/rawTx", + "value": "0xf86b338477359400827b0c94fdea65c8e26263f6d9a1b5de9555d2931a33b82588016345785d8a00008025a098624a27ae79b2b1adc63b913850f266a920cb9d93e6588b8df9b8883eb1b323a00cc6fd855723a234f4f93b48caf7a7659366d09e5c5887f0a4c2e5fa68012cd7", + "note": "transactions#publishTransaction" + } + ], + [ + { + "op": "add", + "path": "/err", + "value": { + "message": "Error: [ethjs-rpc] rpc error with payload {\"id\":7801900228852,\"jsonrpc\":\"2.0\",\"params\":[\"0xf86b338477359400827b0c94fdea65c8e26263f6d9a1b5de9555d2931a33b82588016345785d8a00008025a098624a27ae79b2b1adc63b913850f266a920cb9d93e6588b8df9b8883eb1b323a00cc6fd855723a234f4f93b48caf7a7659366d09e5c5887f0a4c2e5fa68012cd7\"],\"method\":\"eth_sendRawTransaction\"} Error: transaction underpriced", + "stack": "Error: [ethjs-rpc] rpc error with payload {\"id\":7801900228852,\"jsonrpc\":\"2.0\",\"params\":[\"0xf86b338477359400827b0c94fdea65c8e26263f6d9a1b5de9555d2931a33b82588016345785d8a00008025a098624a27ae79b2b1adc63b913850f266a920cb9d93e6588b8df9b8883eb1b323a00cc6fd855723a234f4f93b48caf7a7659366d09e5c5887f0a4c2e5fa68012cd7\"],\"method\":\"eth_sendRawTransaction\"} Error: transaction underpriced\n at chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:60327:26\n at chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:88030:9\n at chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:16678:16\n at replenish (chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:16522:25)\n at iterateeCallback (chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:16512:17)\n at chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:16694:16\n at resultObj.id (chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:88012:9)\n at chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:16813:16\n at replenish (chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:16527:17)\n at iterateeCallback (chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:16512:17)" + } + } + ], + [ + { + "op": "replace", + "path": "/status", + "value": "failed", + "note": "txStateManager: setting status to failed" + } + ] + ], + "nonceDetails": { + "params": { + "highestLocalNonce": 0, + "highestSuggested": 51, + "nextNetworkNonce": 51 + }, + "local": { + "name": "local", + "nonce": 51, + "details": { + "startPoint": 51, + "highest": 51 + } + }, + "network": { + "name": "network", + "nonce": 51, + "details": { + "baseCount": 51 + } + } + }, + "rawTx": "0xf86b338477359400827b0c94fdea65c8e26263f6d9a1b5de9555d2931a33b82588016345785d8a00008025a098624a27ae79b2b1adc63b913850f266a920cb9d93e6588b8df9b8883eb1b323a00cc6fd855723a234f4f93b48caf7a7659366d09e5c5887f0a4c2e5fa68012cd7", + "err": { + "message": "Error: [ethjs-rpc] rpc error with payload {\"id\":7801900228852,\"jsonrpc\":\"2.0\",\"params\":[\"0xf86b338477359400827b0c94fdea65c8e26263f6d9a1b5de9555d2931a33b82588016345785d8a00008025a098624a27ae79b2b1adc63b913850f266a920cb9d93e6588b8df9b8883eb1b323a00cc6fd855723a234f4f93b48caf7a7659366d09e5c5887f0a4c2e5fa68012cd7\"],\"method\":\"eth_sendRawTransaction\"} Error: transaction underpriced", + "stack": "Error: [ethjs-rpc] rpc error with payload {\"id\":7801900228852,\"jsonrpc\":\"2.0\",\"params\":[\"0xf86b338477359400827b0c94fdea65c8e26263f6d9a1b5de9555d2931a33b82588016345785d8a00008025a098624a27ae79b2b1adc63b913850f266a920cb9d93e6588b8df9b8883eb1b323a00cc6fd855723a234f4f93b48caf7a7659366d09e5c5887f0a4c2e5fa68012cd7\"],\"method\":\"eth_sendRawTransaction\"} Error: transaction underpriced\n at chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:60327:26\n at chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:88030:9\n at chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:16678:16\n at replenish (chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:16522:25)\n at iterateeCallback (chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:16512:17)\n at chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:16694:16\n at resultObj.id (chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:88012:9)\n at chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:16813:16\n at replenish (chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:16527:17)\n at iterateeCallback (chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:16512:17)" + } + } + ], + "unapprovedMsgs": {}, + "unapprovedMsgCount": 0, + "unapprovedPersonalMsgs": {}, + "unapprovedPersonalMsgCount": 0, + "unapprovedTypedMessages": {}, + "unapprovedTypedMessagesCount": 0, + "keyringTypes": [ + "Simple Key Pair", + "HD Key Tree" + ], + "keyrings": [ + { + "type": "HD Key Tree", + "accounts": [ + "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825" + ] + } + ], + "computedBalances": {}, + "currentAccountTab": "history", + "tokens": [ + { + "address": "0x0d8775f648430679a709e98d2b0cb6250d2887ef", + "symbol": "BAT", + "decimals": "18" + } + ], + "selectedAddress": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825", + "currentCurrency": "usd", + "conversionRate": 418.62, + "conversionDate": 1512615622, + "infuraNetworkStatus": { + "mainnet": "ok", + "ropsten": "ok", + "kovan": "ok", + "rinkeby": "ok" + }, + "shapeShiftTxList": [], + "lostAccounts": [] + }, + "appState": { + "shouldClose": true, + "menuOpen": false, + "currentView": { + "name": "accountDetail", + "context": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825" + }, + "accountDetail": { + "subview": "transactions", + "accountExport": "none", + "privateKey": "" + }, + "transForward": false, + "isLoading": false, + "warning": null, + "forgottenPassword": false, + "scrollToBottom": false + }, + "identities": {}, + "version": "3.12.1", + "platform": { + "arch": "x86-64", + "nacl_arch": "x86-64", + "os": "mac" + }, + "browser": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36" +} \ No newline at end of file From 97abbc5cbe97157304f27f0f20336b543de62428 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 6 Dec 2017 22:55:47 -0500 Subject: [PATCH 08/42] Fix action --- ui/app/actions.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/app/actions.js b/ui/app/actions.js index 2ab68b62d..35eb4b0c8 100644 --- a/ui/app/actions.js +++ b/ui/app/actions.js @@ -760,9 +760,9 @@ function markAccountsFound () { return callBackgroundThenUpdate(background.markAccountsFound) } -function retryTransaction () { +function retryTransaction (txId) { log.debug(`background.retryTransaction`) - return callBackgroundThenUpdate(background.retryTransaction) + return callBackgroundThenUpdate(background.retryTransaction, txId) } // From 81fb9db1bcd08fb4e92d87d0c0905cb02dac63c2 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 6 Dec 2017 23:09:32 -0500 Subject: [PATCH 09/42] View tx after editing state to unconfirmed --- ui/app/actions.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/ui/app/actions.js b/ui/app/actions.js index 35eb4b0c8..0dc4f3832 100644 --- a/ui/app/actions.js +++ b/ui/app/actions.js @@ -762,7 +762,15 @@ function markAccountsFound () { function retryTransaction (txId) { log.debug(`background.retryTransaction`) - return callBackgroundThenUpdate(background.retryTransaction, txId) + return (dispatch) => { + background.retryTransaction(txId, (err) => { + if (err) { + return dispatch(actions.displayWarning(err.message)) + } + forceUpdateMetamaskState(dispatch) + dispatch(actions.viewPendingTx(txId)) + }) + } } // From 31564e0a86072ae2b49923dcf28983075308c432 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 6 Dec 2017 23:20:15 -0500 Subject: [PATCH 10/42] Fix retry action --- app/scripts/metamask-controller.js | 10 +++++++++- ui/app/actions.js | 4 ++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 3a8100d12..9d126b416 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -363,7 +363,7 @@ module.exports = class MetamaskController extends EventEmitter { // txController cancelTransaction: nodeify(txController.cancelTransaction, txController), updateAndApproveTransaction: nodeify(txController.updateAndApproveTransaction, txController), - retryTransaction: nodeify(txController.retryTransaction, txController), + retryTransaction: nodeify(this.retryTransaction, this), // messageManager signMessage: nodeify(this.signMessage, this), @@ -574,6 +574,14 @@ module.exports = class MetamaskController extends EventEmitter { // // Identity Management // + // + + async retryTransaction (txId, cb) { + await this.txController.retryTransaction(txId) + const state = await this.getState() + return state + } + newUnsignedMessage (msgParams, cb) { const msgId = this.messageManager.addUnapprovedMessage(msgParams) diff --git a/ui/app/actions.js b/ui/app/actions.js index 0dc4f3832..52ea899aa 100644 --- a/ui/app/actions.js +++ b/ui/app/actions.js @@ -763,11 +763,11 @@ function markAccountsFound () { function retryTransaction (txId) { log.debug(`background.retryTransaction`) return (dispatch) => { - background.retryTransaction(txId, (err) => { + background.retryTransaction(txId, (err, newState) => { if (err) { return dispatch(actions.displayWarning(err.message)) } - forceUpdateMetamaskState(dispatch) + dispatch(actions.updateMetamaskState(newState)) dispatch(actions.viewPendingTx(txId)) }) } From 6b3909547f14533cfe09e3d12ac61f0cf57eedd4 Mon Sep 17 00:00:00 2001 From: Alexander Tseung Date: Wed, 6 Dec 2017 23:27:43 -0500 Subject: [PATCH 11/42] Fix styling of Retry buton --- ui/app/components/transaction-list-item.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ui/app/components/transaction-list-item.js b/ui/app/components/transaction-list-item.js index 56e90e26c..fa6c5915d 100644 --- a/ui/app/components/transaction-list-item.js +++ b/ui/app/components/transaction-list-item.js @@ -120,15 +120,15 @@ TransactionListItem.prototype.render = function () { this.resubmit() }, style: { - height: '30px', - borderRadius: '30px', + height: '22px', + borderRadius: '22px', color: '#F9881B', - padding: '0 25px', + padding: '0 20px', backgroundColor: '#FFE3C9', display: 'flex', justifyContent: 'center', alignItems: 'center', - fontSize: '9px', + fontSize: '8px', cursor: 'pointer', }, }, [ From 0e25129028dd45d717d27dfe0c06db8a4052bd4e Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 6 Dec 2017 23:42:47 -0500 Subject: [PATCH 12/42] Enforce retry tx at minimum gas of previous tx --- app/scripts/controllers/transactions.js | 5 ++++- ui/app/components/pending-tx.js | 10 +++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/app/scripts/controllers/transactions.js b/app/scripts/controllers/transactions.js index 67043b401..685db6269 100644 --- a/app/scripts/controllers/transactions.js +++ b/app/scripts/controllers/transactions.js @@ -185,7 +185,10 @@ module.exports = class TransactionController extends EventEmitter { } async retryTransaction (txId) { - return this.txStateManager.setTxStatusUnapproved(txId) + this.txStateManager.setTxStatusUnapproved(txId) + const txMeta = this.txStateManager.getTx(txId) + txMeta.lastGasPrice = txMeta.txParams.gasPrice + this.txStateManager.updateTx(txMeta, 'retryTransaction: manual retry') } async updateAndApproveTransaction (txMeta) { diff --git a/ui/app/components/pending-tx.js b/ui/app/components/pending-tx.js index 5b1b367c6..51e57dcd2 100644 --- a/ui/app/components/pending-tx.js +++ b/ui/app/components/pending-tx.js @@ -38,6 +38,14 @@ PendingTx.prototype.render = function () { const txMeta = this.gatherTxMeta() const txParams = txMeta.txParams || {} + // Allow retry txs + const { lastGasPrice } = txMeta + let forceGasMin + if (lastGasPrice) { + const stripped = ethUtil.stripHexPrefix(lastGasPrice) + forceGasMin = new BN(stripped, 16).add(MIN_GAS_PRICE_BN) + } + // Account Details const address = txParams.from || props.selectedAddress const identity = props.identities[address] || { address: address } @@ -199,7 +207,7 @@ PendingTx.prototype.render = function () { precision: 9, scale: 9, suffix: 'GWEI', - min: MIN_GAS_PRICE_BN, + min: forceGasMin || MIN_GAS_PRICE_BN, style: { position: 'relative', top: '5px', From b26c97529424f8d8cb90364e9114c054c76e10e5 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 6 Dec 2017 23:43:50 -0500 Subject: [PATCH 13/42] Bump changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 009cd5f7c..7b07b6867 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Current Master +- Allow resubmitting transactions that are taking long to complete. + ## 3.12.1 2017-11-29 - Fix bug where a user could be shown two different seed phrases. From b9f2f8c2a78332a2522d49baedf25e894273ef38 Mon Sep 17 00:00:00 2001 From: Alexander Tseung Date: Thu, 7 Dec 2017 00:01:11 -0500 Subject: [PATCH 14/42] Show retry button on submitted transactions greater than 30 seconds, add hover styling --- ui/app/components/transaction-list-item.js | 8 +++++++- ui/app/css/index.css | 4 ++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/ui/app/components/transaction-list-item.js b/ui/app/components/transaction-list-item.js index fa6c5915d..42ef665b1 100644 --- a/ui/app/components/transaction-list-item.js +++ b/ui/app/components/transaction-list-item.js @@ -28,6 +28,12 @@ function TransactionListItem () { Component.call(this) } +TransactionListItem.prototype.showRetryButton = function () { + const { transaction = {} } = this.props + const { status, time } = transaction + return status === 'submitted' && Date.now() - time > 30000 +} + TransactionListItem.prototype.render = function () { const { transaction, network, conversionRate, currentCurrency } = this.props const { status } = transaction @@ -114,7 +120,7 @@ TransactionListItem.prototype.render = function () { }) : h('.flex-column'), ]), - transaction.status === 'submitted' && h('.transition-list-item__retry', { + this.showRetryButton() && h('.transition-list-item__retry.grow-on-hover', { onClick: event => { event.stopPropagation() this.resubmit() diff --git a/ui/app/css/index.css b/ui/app/css/index.css index 0630c4c12..c0bf18c23 100644 --- a/ui/app/css/index.css +++ b/ui/app/css/index.css @@ -108,6 +108,10 @@ button:not([disabled]):active, input[type="submit"]:not([disabled]):active { transform: scale(0.95); } +.grow-on-hover:hover { + transform: scale(1.05); +} + a { text-decoration: none; color: inherit; From c391b25015b391d2818de067fa17ad3d9a3a9b30 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 7 Dec 2017 03:01:46 -0500 Subject: [PATCH 15/42] Version 3.13.0 --- CHANGELOG.md | 2 ++ app/manifest.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b07b6867..faffb8a2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Current Master +## 3.13.0 2017-12-7 + - Allow resubmitting transactions that are taking long to complete. ## 3.12.1 2017-11-29 diff --git a/app/manifest.json b/app/manifest.json index 4219f3298..aa23f85ff 100644 --- a/app/manifest.json +++ b/app/manifest.json @@ -1,7 +1,7 @@ { "name": "MetaMask", "short_name": "Metamask", - "version": "3.12.1", + "version": "3.13.0", "manifest_version": 2, "author": "https://metamask.io", "description": "Ethereum Browser Extension", From c82fd990aafd72ede0c08d2820c0c1f1db6bfa81 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 7 Dec 2017 04:02:11 -0500 Subject: [PATCH 16/42] Add 10% price bump to retry attempts. Turns out geth requires at least a 10% price bump to replace txs: https://github.com/ethereum/go-ethereum/blob/9619a610248e9630968ba1d9be8e214b645c9c55/core/tx_pool.go#L133 --- ui/app/components/pending-tx.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ui/app/components/pending-tx.js b/ui/app/components/pending-tx.js index 51e57dcd2..32d54902e 100644 --- a/ui/app/components/pending-tx.js +++ b/ui/app/components/pending-tx.js @@ -43,7 +43,9 @@ PendingTx.prototype.render = function () { let forceGasMin if (lastGasPrice) { const stripped = ethUtil.stripHexPrefix(lastGasPrice) - forceGasMin = new BN(stripped, 16).add(MIN_GAS_PRICE_BN) + const lastGas = new BN(stripped, 16) + const priceBump = lastGas.divn('10') + forceGasMin = lastGas.add(priceBump) } // Account Details From 553d713636d317b74d819d62b5b931bdf2d112d8 Mon Sep 17 00:00:00 2001 From: vicnaum Date: Thu, 7 Dec 2017 15:30:05 +0100 Subject: [PATCH 17/42] A more expressive way replaced ifs with ? : --- app/scripts/controllers/transactions.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/app/scripts/controllers/transactions.js b/app/scripts/controllers/transactions.js index 060484b87..6bdbd2cfc 100644 --- a/app/scripts/controllers/transactions.js +++ b/app/scripts/controllers/transactions.js @@ -201,11 +201,8 @@ module.exports = class TransactionController extends EventEmitter { // wait for a nonce nonceLock = await this.nonceTracker.getNonceLock(fromAddress) // add nonce to txParams - if (txMeta.nonceSpecified) { - txMeta.txParams.nonce = ethUtil.addHexPrefix(txMeta.txParams.nonce.toString(16)) - } else { - txMeta.txParams.nonce = ethUtil.addHexPrefix(nonceLock.nextNonce.toString(16)) - } + const nonce = txMeta.nonceSpecified ? txMeta.txParams.nonce : nonceLock.nextNonce + txMeta.txParams.nonce = ethUtil.addHexPrefix(nonce.toString(16)) // add nonce debugging information to txMeta txMeta.nonceDetails = nonceLock.nonceDetails this.txStateManager.updateTx(txMeta, 'transactions#approveTransaction') From 950ec9596c931055c3e0f2212f2733c9ca07739d Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 7 Dec 2017 16:13:38 -0500 Subject: [PATCH 18/42] Do not allow nonces larger than the next valid nonce To avoid situations where a user signs a transaction that will become surprisingly valid in the future. --- app/scripts/controllers/transactions.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/scripts/controllers/transactions.js b/app/scripts/controllers/transactions.js index bb408d445..6110b9c75 100644 --- a/app/scripts/controllers/transactions.js +++ b/app/scripts/controllers/transactions.js @@ -209,6 +209,10 @@ module.exports = class TransactionController extends EventEmitter { nonceLock = await this.nonceTracker.getNonceLock(fromAddress) // add nonce to txParams const nonce = txMeta.nonceSpecified ? txMeta.txParams.nonce : nonceLock.nextNonce + if (nonce > nonceLock.nextNonce) { + const message = `Specified nonce may not be larger than account's next valid nonce.` + throw new Error(message) + } txMeta.txParams.nonce = ethUtil.addHexPrefix(nonce.toString(16)) // add nonce debugging information to txMeta txMeta.nonceDetails = nonceLock.nonceDetails From 9bb4df46ec8a2cd90aa50546799e9cf5ef738632 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 7 Dec 2017 16:16:47 -0500 Subject: [PATCH 19/42] Bump changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index faffb8a2d..f3979fbbb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Current Master +- Allow Dapps to specify a transaction nonce, allowing dapps to propose resubmit and force-cancel transactions. + ## 3.13.0 2017-12-7 - Allow resubmitting transactions that are taking long to complete. From dc4e3ef24185f3f4dae1fedc5840e36b703617d2 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 7 Dec 2017 16:17:33 -0500 Subject: [PATCH 20/42] Version 3.13.1 --- CHANGELOG.md | 2 ++ app/manifest.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f3979fbbb..ea7e1cc8d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Current Master +## 3.13.1 2017-12-7 + - Allow Dapps to specify a transaction nonce, allowing dapps to propose resubmit and force-cancel transactions. ## 3.13.0 2017-12-7 diff --git a/app/manifest.json b/app/manifest.json index aa23f85ff..e95e75f6d 100644 --- a/app/manifest.json +++ b/app/manifest.json @@ -1,7 +1,7 @@ { "name": "MetaMask", "short_name": "Metamask", - "version": "3.13.0", + "version": "3.13.1", "manifest_version": 2, "author": "https://metamask.io", "description": "Ethereum Browser Extension", From a91200fd08b429c81d4096de17cdd9066a632ade Mon Sep 17 00:00:00 2001 From: kumavis Date: Thu, 7 Dec 2017 18:04:14 -0500 Subject: [PATCH 21/42] tx-controller - failed state is a finished state --- app/scripts/controllers/transactions.js | 14 ++++++++------ app/scripts/lib/tx-state-manager.js | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/app/scripts/controllers/transactions.js b/app/scripts/controllers/transactions.js index 6110b9c75..f95b5e39a 100644 --- a/app/scripts/controllers/transactions.js +++ b/app/scripts/controllers/transactions.js @@ -138,18 +138,20 @@ module.exports = class TransactionController extends EventEmitter { async newUnapprovedTransaction (txParams) { log.debug(`MetaMaskController newUnapprovedTransaction ${JSON.stringify(txParams)}`) - const txMeta = await this.addUnapprovedTransaction(txParams) - this.emit('newUnapprovedTx', txMeta) + const initialTxMeta = await this.addUnapprovedTransaction(txParams) + this.emit('newUnapprovedTx', initialTxMeta) // listen for tx completion (success, fail) return new Promise((resolve, reject) => { - this.txStateManager.once(`${txMeta.id}:finished`, (completedTx) => { - switch (completedTx.status) { + this.txStateManager.once(`${initialTxMeta.id}:finished`, (finishedTxMeta) => { + switch (finishedTxMeta.status) { case 'submitted': - return resolve(completedTx.hash) + return resolve(finishedTxMeta.hash) case 'rejected': return reject(new Error('MetaMask Tx Signature: User denied transaction signature.')) + case 'failed': + return reject(new Error(finishedTxMeta.err.message)) default: - return reject(new Error(`MetaMask Tx Signature: Unknown problem: ${JSON.stringify(completedTx.txParams)}`)) + return reject(new Error(`MetaMask Tx Signature: Unknown problem: ${JSON.stringify(finishedTxMeta.txParams)}`)) } }) }) diff --git a/app/scripts/lib/tx-state-manager.js b/app/scripts/lib/tx-state-manager.js index cc441c584..a8ef39891 100644 --- a/app/scripts/lib/tx-state-manager.js +++ b/app/scripts/lib/tx-state-manager.js @@ -240,7 +240,7 @@ module.exports = class TransactionStateManger extends EventEmitter { txMeta.status = status this.emit(`${txMeta.id}:${status}`, txId) this.emit(`tx:status-update`, txId, status) - if (status === 'submitted' || status === 'rejected') { + if (['submitted', 'rejected', 'failed'].includes(status)) { this.emit(`${txMeta.id}:finished`, txMeta) } this.updateTx(txMeta, `txStateManager: setting status to ${status}`) From bfec077d798aea7b2869b3ff0d292243eec60da5 Mon Sep 17 00:00:00 2001 From: Dan Date: Fri, 8 Dec 2017 13:09:53 -0330 Subject: [PATCH 22/42] Fixes the cancel button in the buy screen to take the user home. --- ui/app/components/coinbase-form.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/app/components/coinbase-form.js b/ui/app/components/coinbase-form.js index f44d86045..f70208625 100644 --- a/ui/app/components/coinbase-form.js +++ b/ui/app/components/coinbase-form.js @@ -40,7 +40,7 @@ CoinbaseForm.prototype.render = function () { }, 'Continue to Coinbase'), h('button.btn-red', { - onClick: () => props.dispatch(actions.backTobuyView(props.accounts.address)), + onClick: () => props.dispatch(actions.goHome()), }, 'Cancel'), ]), ]) From 542e0035dd99d07f8a6613d231a36005c5b39825 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Sat, 9 Dec 2017 19:58:09 -0500 Subject: [PATCH 23/42] Pass polling interval to zero client provider --- app/scripts/controllers/network.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/scripts/controllers/network.js b/app/scripts/controllers/network.js index 045bfcc5d..cee918796 100644 --- a/app/scripts/controllers/network.js +++ b/app/scripts/controllers/network.js @@ -106,7 +106,11 @@ module.exports = class NetworkController extends EventEmitter { } _configureStandardProvider (_providerParams) { - const providerParams = extend(this._baseProviderParams, _providerParams) + const providerParams = extend(this._baseProviderParams, _providerParams, { + engineParams: { + pollingInterval: 8000, + }, + }) const provider = createMetamaskProvider(providerParams) this._setProvider(provider) } From 408fc0f2fc3d17ae4bc1f383b71e7a5076157d5c Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Sat, 9 Dec 2017 19:58:55 -0500 Subject: [PATCH 24/42] Bump changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ea7e1cc8d..6cde7589a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Current Master +- Reduce new block polling interval to 8000 ms, to ease server load. + ## 3.13.1 2017-12-7 - Allow Dapps to specify a transaction nonce, allowing dapps to propose resubmit and force-cancel transactions. From 1ebd5140aaa3dfe98668fad3893834653c5f7109 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Sat, 9 Dec 2017 20:00:54 -0500 Subject: [PATCH 25/42] Version 3.13.2 --- CHANGELOG.md | 2 ++ app/manifest.json | 2 +- package.json | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6cde7589a..8e35e8775 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Current Master +## 3.13.2 2017-12-9 + - Reduce new block polling interval to 8000 ms, to ease server load. ## 3.13.1 2017-12-7 diff --git a/app/manifest.json b/app/manifest.json index e95e75f6d..a5645bb7c 100644 --- a/app/manifest.json +++ b/app/manifest.json @@ -1,7 +1,7 @@ { "name": "MetaMask", "short_name": "Metamask", - "version": "3.13.1", + "version": "3.13.2", "manifest_version": 2, "author": "https://metamask.io", "description": "Ethereum Browser Extension", diff --git a/package.json b/package.json index 5129dd3cb..ebe298aea 100644 --- a/package.json +++ b/package.json @@ -152,7 +152,7 @@ "valid-url": "^1.0.9", "vreme": "^3.0.2", "web3": "^0.20.1", - "web3-provider-engine": "^13.3.2", + "web3-provider-engine": "^13.3.4", "web3-stream-provider": "^3.0.1", "xtend": "^4.0.1" }, From d110c916a4dde947dcfdc610aaadf9fba251449a Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Tue, 12 Dec 2017 09:47:51 -0800 Subject: [PATCH 26/42] Show watched tokens with no balance --- ui/app/components/token-list.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/ui/app/components/token-list.js b/ui/app/components/token-list.js index 998ec901d..149733b89 100644 --- a/ui/app/components/token-list.js +++ b/ui/app/components/token-list.js @@ -194,10 +194,7 @@ TokenList.prototype.componentWillUpdate = function (nextProps) { } TokenList.prototype.updateBalances = function (tokens) { - const heldTokens = tokens.filter(token => { - return token.balance !== '0' && token.string !== '0.000' - }) - this.setState({ tokens: heldTokens, isLoading: false }) + this.setState({ tokens, isLoading: false }) } TokenList.prototype.componentWillUnmount = function () { From 3456efa3f6a8afdda7a7d2218d780311acf9d3fc Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Tue, 12 Dec 2017 09:48:09 -0800 Subject: [PATCH 27/42] Bump changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e35e8775..106881ea5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Current Master +- Show tokens that are held that have no balance. + ## 3.13.2 2017-12-9 - Reduce new block polling interval to 8000 ms, to ease server load. From da209a9cfda60b3acd567ac1c0b3bb535cc6e2e8 Mon Sep 17 00:00:00 2001 From: frankiebee Date: Wed, 13 Dec 2017 11:18:43 -0800 Subject: [PATCH 28/42] dont aggresively query account data --- app/scripts/lib/account-tracker.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/scripts/lib/account-tracker.js b/app/scripts/lib/account-tracker.js index ce6642150..8c3dd8c71 100644 --- a/app/scripts/lib/account-tracker.js +++ b/app/scripts/lib/account-tracker.js @@ -117,8 +117,6 @@ class AccountTracker extends EventEmitter { const query = this._query async.parallel({ balance: query.getBalance.bind(query, address), - nonce: query.getTransactionCount.bind(query, address), - code: query.getCode.bind(query, address), }, cb) } From 33e6befdaa1c3f969581117b3ad6136ee263e353 Mon Sep 17 00:00:00 2001 From: kumavis Date: Wed, 13 Dec 2017 13:51:36 -0800 Subject: [PATCH 29/42] network - for infura networks use the infura block tracker provider --- app/scripts/controllers/network.js | 57 ++++++++++++++++++++++++++---- package.json | 3 +- 2 files changed, 52 insertions(+), 8 deletions(-) diff --git a/app/scripts/controllers/network.js b/app/scripts/controllers/network.js index cee918796..65d58008a 100644 --- a/app/scripts/controllers/network.js +++ b/app/scripts/controllers/network.js @@ -1,6 +1,7 @@ const assert = require('assert') const EventEmitter = require('events') const createMetamaskProvider = require('web3-provider-engine/zero.js') +const createInfuraProvider = require('eth-json-rpc-infura/src/createProvider') const ObservableStore = require('obs-store') const ComposedStore = require('obs-store/lib/composed') const extend = require('xtend') @@ -8,6 +9,7 @@ const EthQuery = require('eth-query') const createEventEmitterProxy = require('../lib/events-proxy.js') const RPC_ADDRESS_LIST = require('../config.js').network const DEFAULT_RPC = RPC_ADDRESS_LIST['rinkeby'] +const INFURA_PROVIDER_TYPES = ['ropsten', 'rinkeby', 'kovan', 'mainnet'] module.exports = class NetworkController extends EventEmitter { @@ -24,8 +26,13 @@ module.exports = class NetworkController extends EventEmitter { initializeProvider (_providerParams) { this._baseProviderParams = _providerParams - const rpcUrl = this.getCurrentRpcAddress() - this._configureStandardProvider({ rpcUrl }) + const { type, rpcTarget } = this.providerStore.getState() + // map rpcTarget to rpcUrl + const opts = { + type, + rpcUrl: rpcTarget, + } + this._configureProvider(opts) this._proxy.on('block', this._logBlock.bind(this)) this._proxy.on('error', this.verifyNetwork.bind(this)) this.ethQuery = new EthQuery(this._proxy) @@ -83,7 +90,7 @@ module.exports = class NetworkController extends EventEmitter { const rpcTarget = this.getRpcAddressForType(type) assert(rpcTarget, `NetworkController - unknown rpc address for type "${type}"`) this.providerStore.updateState({ type, rpcTarget }) - this._switchNetwork({ rpcUrl: rpcTarget }) + this._switchNetwork({ type }) } getProviderConfig () { @@ -99,14 +106,50 @@ module.exports = class NetworkController extends EventEmitter { // Private // - _switchNetwork (providerParams) { + _switchNetwork (opts) { this.setNetworkState('loading') - this._configureStandardProvider(providerParams) + this._configureProvider(opts) this.emit('networkDidChange') } - _configureStandardProvider (_providerParams) { - const providerParams = extend(this._baseProviderParams, _providerParams, { + _configureProvider (opts) { + // type-based rpc endpoints + const { type } = opts + if (type) { + // type-based infura rpc endpoints + const isInfura = INFURA_PROVIDER_TYPES.includes(type) + opts.rpcUrl = this.getRpcAddressForType(type) + if (isInfura) { + this._configureInfuraProvider(opts) + // other type-based rpc endpoints + } else { + this._configureStandardProvider(opts) + } + // url-based rpc endpoints + } else { + this._configureStandardProvider(opts) + } + } + + _configureInfuraProvider (opts) { + console.log('_configureInfuraProvider', opts) + const blockTrackerProvider = createInfuraProvider({ + network: opts.type, + }) + const providerParams = extend(this._baseProviderParams, { + rpcUrl: opts.rpcUrl, + engineParams: { + pollingInterval: 8000, + blockTrackerProvider, + }, + }) + const provider = createMetamaskProvider(providerParams) + this._setProvider(provider) + } + + _configureStandardProvider ({ rpcUrl }) { + const providerParams = extend(this._baseProviderParams, { + rpcUrl, engineParams: { pollingInterval: 8000, }, diff --git a/package.json b/package.json index ebe298aea..3a7e33b7a 100644 --- a/package.json +++ b/package.json @@ -74,6 +74,7 @@ "eth-contract-metadata": "^1.1.4", "eth-hd-keyring": "^1.2.1", "eth-json-rpc-filters": "^1.2.4", + "eth-json-rpc-infura": "^1.0.1", "eth-keyring-controller": "^2.1.2", "eth-phishing-detect": "^1.1.4", "eth-query": "^2.1.2", @@ -152,7 +153,7 @@ "valid-url": "^1.0.9", "vreme": "^3.0.2", "web3": "^0.20.1", - "web3-provider-engine": "^13.3.4", + "web3-provider-engine": "^13.4.0", "web3-stream-provider": "^3.0.1", "xtend": "^4.0.1" }, From 5df7dcd758c5a16b503a5ee237970c05c7e990c9 Mon Sep 17 00:00:00 2001 From: Frankie Date: Wed, 13 Dec 2017 17:46:56 -0800 Subject: [PATCH 30/42] Revert "network - for infura networks use the infura block tracker provider" --- app/scripts/controllers/network.js | 57 ++++-------------------------- package.json | 3 +- 2 files changed, 8 insertions(+), 52 deletions(-) diff --git a/app/scripts/controllers/network.js b/app/scripts/controllers/network.js index 65d58008a..cee918796 100644 --- a/app/scripts/controllers/network.js +++ b/app/scripts/controllers/network.js @@ -1,7 +1,6 @@ const assert = require('assert') const EventEmitter = require('events') const createMetamaskProvider = require('web3-provider-engine/zero.js') -const createInfuraProvider = require('eth-json-rpc-infura/src/createProvider') const ObservableStore = require('obs-store') const ComposedStore = require('obs-store/lib/composed') const extend = require('xtend') @@ -9,7 +8,6 @@ const EthQuery = require('eth-query') const createEventEmitterProxy = require('../lib/events-proxy.js') const RPC_ADDRESS_LIST = require('../config.js').network const DEFAULT_RPC = RPC_ADDRESS_LIST['rinkeby'] -const INFURA_PROVIDER_TYPES = ['ropsten', 'rinkeby', 'kovan', 'mainnet'] module.exports = class NetworkController extends EventEmitter { @@ -26,13 +24,8 @@ module.exports = class NetworkController extends EventEmitter { initializeProvider (_providerParams) { this._baseProviderParams = _providerParams - const { type, rpcTarget } = this.providerStore.getState() - // map rpcTarget to rpcUrl - const opts = { - type, - rpcUrl: rpcTarget, - } - this._configureProvider(opts) + const rpcUrl = this.getCurrentRpcAddress() + this._configureStandardProvider({ rpcUrl }) this._proxy.on('block', this._logBlock.bind(this)) this._proxy.on('error', this.verifyNetwork.bind(this)) this.ethQuery = new EthQuery(this._proxy) @@ -90,7 +83,7 @@ module.exports = class NetworkController extends EventEmitter { const rpcTarget = this.getRpcAddressForType(type) assert(rpcTarget, `NetworkController - unknown rpc address for type "${type}"`) this.providerStore.updateState({ type, rpcTarget }) - this._switchNetwork({ type }) + this._switchNetwork({ rpcUrl: rpcTarget }) } getProviderConfig () { @@ -106,50 +99,14 @@ module.exports = class NetworkController extends EventEmitter { // Private // - _switchNetwork (opts) { + _switchNetwork (providerParams) { this.setNetworkState('loading') - this._configureProvider(opts) + this._configureStandardProvider(providerParams) this.emit('networkDidChange') } - _configureProvider (opts) { - // type-based rpc endpoints - const { type } = opts - if (type) { - // type-based infura rpc endpoints - const isInfura = INFURA_PROVIDER_TYPES.includes(type) - opts.rpcUrl = this.getRpcAddressForType(type) - if (isInfura) { - this._configureInfuraProvider(opts) - // other type-based rpc endpoints - } else { - this._configureStandardProvider(opts) - } - // url-based rpc endpoints - } else { - this._configureStandardProvider(opts) - } - } - - _configureInfuraProvider (opts) { - console.log('_configureInfuraProvider', opts) - const blockTrackerProvider = createInfuraProvider({ - network: opts.type, - }) - const providerParams = extend(this._baseProviderParams, { - rpcUrl: opts.rpcUrl, - engineParams: { - pollingInterval: 8000, - blockTrackerProvider, - }, - }) - const provider = createMetamaskProvider(providerParams) - this._setProvider(provider) - } - - _configureStandardProvider ({ rpcUrl }) { - const providerParams = extend(this._baseProviderParams, { - rpcUrl, + _configureStandardProvider (_providerParams) { + const providerParams = extend(this._baseProviderParams, _providerParams, { engineParams: { pollingInterval: 8000, }, diff --git a/package.json b/package.json index 3a7e33b7a..ebe298aea 100644 --- a/package.json +++ b/package.json @@ -74,7 +74,6 @@ "eth-contract-metadata": "^1.1.4", "eth-hd-keyring": "^1.2.1", "eth-json-rpc-filters": "^1.2.4", - "eth-json-rpc-infura": "^1.0.1", "eth-keyring-controller": "^2.1.2", "eth-phishing-detect": "^1.1.4", "eth-query": "^2.1.2", @@ -153,7 +152,7 @@ "valid-url": "^1.0.9", "vreme": "^3.0.2", "web3": "^0.20.1", - "web3-provider-engine": "^13.4.0", + "web3-provider-engine": "^13.3.4", "web3-stream-provider": "^3.0.1", "xtend": "^4.0.1" }, From 222e80dbbeb3f24b13f8be54bce10d546782c319 Mon Sep 17 00:00:00 2001 From: kumavis Date: Wed, 13 Dec 2017 18:43:12 -0800 Subject: [PATCH 31/42] deps - bump eth-json-rpc-infura --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3a7e33b7a..fce548066 100644 --- a/package.json +++ b/package.json @@ -74,7 +74,7 @@ "eth-contract-metadata": "^1.1.4", "eth-hd-keyring": "^1.2.1", "eth-json-rpc-filters": "^1.2.4", - "eth-json-rpc-infura": "^1.0.1", + "eth-json-rpc-infura": "^1.0.2", "eth-keyring-controller": "^2.1.2", "eth-phishing-detect": "^1.1.4", "eth-query": "^2.1.2", From 161aa66cb4c6791659a48738578aac1f8c0c1376 Mon Sep 17 00:00:00 2001 From: kumavis Date: Wed, 13 Dec 2017 18:57:27 -0800 Subject: [PATCH 32/42] Revert "Revert "network - for infura networks use the infura block tracker provider"" This reverts commit 5df7dcd758c5a16b503a5ee237970c05c7e990c9. --- app/scripts/controllers/network.js | 57 ++++++++++++++++++++++++++---- package.json | 2 +- 2 files changed, 51 insertions(+), 8 deletions(-) diff --git a/app/scripts/controllers/network.js b/app/scripts/controllers/network.js index cee918796..65d58008a 100644 --- a/app/scripts/controllers/network.js +++ b/app/scripts/controllers/network.js @@ -1,6 +1,7 @@ const assert = require('assert') const EventEmitter = require('events') const createMetamaskProvider = require('web3-provider-engine/zero.js') +const createInfuraProvider = require('eth-json-rpc-infura/src/createProvider') const ObservableStore = require('obs-store') const ComposedStore = require('obs-store/lib/composed') const extend = require('xtend') @@ -8,6 +9,7 @@ const EthQuery = require('eth-query') const createEventEmitterProxy = require('../lib/events-proxy.js') const RPC_ADDRESS_LIST = require('../config.js').network const DEFAULT_RPC = RPC_ADDRESS_LIST['rinkeby'] +const INFURA_PROVIDER_TYPES = ['ropsten', 'rinkeby', 'kovan', 'mainnet'] module.exports = class NetworkController extends EventEmitter { @@ -24,8 +26,13 @@ module.exports = class NetworkController extends EventEmitter { initializeProvider (_providerParams) { this._baseProviderParams = _providerParams - const rpcUrl = this.getCurrentRpcAddress() - this._configureStandardProvider({ rpcUrl }) + const { type, rpcTarget } = this.providerStore.getState() + // map rpcTarget to rpcUrl + const opts = { + type, + rpcUrl: rpcTarget, + } + this._configureProvider(opts) this._proxy.on('block', this._logBlock.bind(this)) this._proxy.on('error', this.verifyNetwork.bind(this)) this.ethQuery = new EthQuery(this._proxy) @@ -83,7 +90,7 @@ module.exports = class NetworkController extends EventEmitter { const rpcTarget = this.getRpcAddressForType(type) assert(rpcTarget, `NetworkController - unknown rpc address for type "${type}"`) this.providerStore.updateState({ type, rpcTarget }) - this._switchNetwork({ rpcUrl: rpcTarget }) + this._switchNetwork({ type }) } getProviderConfig () { @@ -99,14 +106,50 @@ module.exports = class NetworkController extends EventEmitter { // Private // - _switchNetwork (providerParams) { + _switchNetwork (opts) { this.setNetworkState('loading') - this._configureStandardProvider(providerParams) + this._configureProvider(opts) this.emit('networkDidChange') } - _configureStandardProvider (_providerParams) { - const providerParams = extend(this._baseProviderParams, _providerParams, { + _configureProvider (opts) { + // type-based rpc endpoints + const { type } = opts + if (type) { + // type-based infura rpc endpoints + const isInfura = INFURA_PROVIDER_TYPES.includes(type) + opts.rpcUrl = this.getRpcAddressForType(type) + if (isInfura) { + this._configureInfuraProvider(opts) + // other type-based rpc endpoints + } else { + this._configureStandardProvider(opts) + } + // url-based rpc endpoints + } else { + this._configureStandardProvider(opts) + } + } + + _configureInfuraProvider (opts) { + console.log('_configureInfuraProvider', opts) + const blockTrackerProvider = createInfuraProvider({ + network: opts.type, + }) + const providerParams = extend(this._baseProviderParams, { + rpcUrl: opts.rpcUrl, + engineParams: { + pollingInterval: 8000, + blockTrackerProvider, + }, + }) + const provider = createMetamaskProvider(providerParams) + this._setProvider(provider) + } + + _configureStandardProvider ({ rpcUrl }) { + const providerParams = extend(this._baseProviderParams, { + rpcUrl, engineParams: { pollingInterval: 8000, }, diff --git a/package.json b/package.json index b37389eeb..fce548066 100644 --- a/package.json +++ b/package.json @@ -153,7 +153,7 @@ "valid-url": "^1.0.9", "vreme": "^3.0.2", "web3": "^0.20.1", - "web3-provider-engine": "^13.3.4", + "web3-provider-engine": "^13.4.0", "web3-stream-provider": "^3.0.1", "xtend": "^4.0.1" }, From 622a1f7f4a972b7ea665de941d61bedb5ff03331 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 14 Dec 2017 11:45:51 -0800 Subject: [PATCH 33/42] Version 3.13.3 --- CHANGELOG.md | 3 +++ app/manifest.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 106881ea5..3b7256466 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,10 @@ ## Current Master +## 3.13.3 2017-12-14 + - Show tokens that are held that have no balance. +- Reduce load on Infura by using a new block polling endpoint. ## 3.13.2 2017-12-9 diff --git a/app/manifest.json b/app/manifest.json index a5645bb7c..d6c57d681 100644 --- a/app/manifest.json +++ b/app/manifest.json @@ -1,7 +1,7 @@ { "name": "MetaMask", "short_name": "Metamask", - "version": "3.13.2", + "version": "3.13.3", "manifest_version": 2, "author": "https://metamask.io", "description": "Ethereum Browser Extension", From 828734b977f7c26c249c81af73fc8eef0b66e416 Mon Sep 17 00:00:00 2001 From: frankiebee Date: Thu, 14 Dec 2017 18:15:38 -0800 Subject: [PATCH 34/42] transactions:gas-utils - handle new type of estimateGas error --- app/scripts/lib/tx-gas-utils.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/scripts/lib/tx-gas-utils.js b/app/scripts/lib/tx-gas-utils.js index 7e72ea71d..56bee19f7 100644 --- a/app/scripts/lib/tx-gas-utils.js +++ b/app/scripts/lib/tx-gas-utils.js @@ -22,7 +22,11 @@ module.exports = class txProvideUtil { try { estimatedGasHex = await this.estimateTxGas(txMeta, block.gasLimit) } catch (err) { - if (err.message.includes('Transaction execution error.')) { + const simulationFailed = ( + err.message.includes('Transaction execution error.') || + err.message.includes('gas required exceeds allowance or always failing transaction') + ) + if ( simulationFailed ) { txMeta.simulationFails = true return txMeta } From c6e7bc0a5d91869fc27bb58298ba2add5b83844b Mon Sep 17 00:00:00 2001 From: Thomas Huang Date: Fri, 15 Dec 2017 14:41:58 -0600 Subject: [PATCH 35/42] Add exit flag to mocha to exit after tests run --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index fce548066..7184e3ea8 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "dist": "npm run dist:clear && npm install && gulp dist", "dist:clear": "rm -rf node_modules/eth-contract-metadata && rm -rf node_modules/eth-phishing-detect", "test": "npm run lint && npm run test:coverage && npm run test:integration", - "test:unit": "METAMASK_ENV=test mocha --compilers js:babel-core/register --require test/helper.js --recursive \"test/unit/**/*.js\"", + "test:unit": "METAMASK_ENV=test mocha --exit --compilers js:babel-core/register --require test/helper.js --recursive \"test/unit/**/*.js\"", "test:single": "METAMASK_ENV=test mocha --require test/helper.js", "test:integration": "npm run test:flat && npm run test:mascara", "test:coverage": "nyc npm run test:unit && npm run test:coveralls-upload", From fb8744771a78eabe98b5be7fb32af19666b107bd Mon Sep 17 00:00:00 2001 From: Thomas Huang Date: Fri, 15 Dec 2017 14:43:55 -0600 Subject: [PATCH 36/42] Change console.log for opts in _configureInfuraProvider --- app/scripts/controllers/network.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/scripts/controllers/network.js b/app/scripts/controllers/network.js index 65d58008a..377ba6eca 100644 --- a/app/scripts/controllers/network.js +++ b/app/scripts/controllers/network.js @@ -132,7 +132,7 @@ module.exports = class NetworkController extends EventEmitter { } _configureInfuraProvider (opts) { - console.log('_configureInfuraProvider', opts) + log.info('_configureInfuraProvider', opts) const blockTrackerProvider = createInfuraProvider({ network: opts.type, }) From 1fb57d8cb8624b7c78df2b3702523a356a0f6a95 Mon Sep 17 00:00:00 2001 From: kumavis Date: Fri, 15 Dec 2017 15:06:06 -0800 Subject: [PATCH 37/42] readme - fix "plugin" to "extension" --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b549ade08..913723ad5 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# MetaMask Plugin +# MetaMask Browser Extension [![Build Status](https://circleci.com/gh/MetaMask/metamask-extension.svg?style=shield&circle-token=a1ddcf3cd38e29267f254c9c59d556d513e3a1fd)](https://circleci.com/gh/MetaMask/metamask-extension) [![Coverage Status](https://coveralls.io/repos/github/MetaMask/metamask-extension/badge.svg?branch=master)](https://coveralls.io/github/MetaMask/metamask-extension?branch=master) [![Greenkeeper badge](https://badges.greenkeeper.io/MetaMask/metamask-extension.svg)](https://greenkeeper.io/) [![Stories in Ready](https://badge.waffle.io/MetaMask/metamask-extension.png?label=in%20progress&title=waffle.io)](http://waffle.io/MetaMask/metamask-extension) From 10ff77477cf3163eaea5ac9485977d91bcc102c0 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Sun, 17 Dec 2017 16:36:03 -0800 Subject: [PATCH 38/42] Add Recent Blocks controller Tracks recent blocks, useful for estimating recent successful gas prices. --- app/scripts/controllers/recent-blocks.js | 37 ++++++++++++++++++++++ app/scripts/metamask-controller.js | 39 +++++++++++++++--------- 2 files changed, 62 insertions(+), 14 deletions(-) create mode 100644 app/scripts/controllers/recent-blocks.js diff --git a/app/scripts/controllers/recent-blocks.js b/app/scripts/controllers/recent-blocks.js new file mode 100644 index 000000000..27f9013ff --- /dev/null +++ b/app/scripts/controllers/recent-blocks.js @@ -0,0 +1,37 @@ +const ObservableStore = require('obs-store') +const extend = require('xtend') + +class RecentBlocksController { + + constructor (opts = {}) { + const { blockTracker } = opts + this.blockTracker = blockTracker + this.historyLength = opts.historyLength || 40 + + const initState = extend({ + recentBlocks: [], + }, opts.initState) + this.store = new ObservableStore(initState) + + this.blockTracker.on('block', this.processBlock.bind(this)) + } + + resetState () { + this.store.updateState({ + recentBlocks: [], + }) + } + + processBlock (newBlock) { + const state = this.store.getState() + state.recentBlocks.push(newBlock) + + while (state.recentBlocks.length > this.historyLength) { + state.recentBlocks.shift() + } + + this.store.updateState(state) + } +} + +module.exports = RecentBlocksController diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 9d126b416..23f2a1598 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -23,6 +23,7 @@ const ShapeShiftController = require('./controllers/shapeshift') const AddressBookController = require('./controllers/address-book') const InfuraController = require('./controllers/infura') const BlacklistController = require('./controllers/blacklist') +const RecentBlocksController = require('./controllers/recent-blocks') const MessageManager = require('./lib/message-manager') const PersonalMessageManager = require('./lib/personal-message-manager') const TypedMessageManager = require('./lib/typed-message-manager') @@ -91,6 +92,10 @@ module.exports = class MetamaskController extends EventEmitter { this.provider = this.initializeProvider() this.blockTracker = this.provider._blockTracker + this.recentBlocksController = new RecentBlocksController({ + blockTracker: this.blockTracker, + }) + // eth data query tools this.ethQuery = new EthQuery(this.provider) // account tracker watches balances, nonces, and any code at their address. @@ -196,25 +201,30 @@ module.exports = class MetamaskController extends EventEmitter { this.blacklistController.store.subscribe((state) => { this.store.updateState({ BlacklistController: state }) }) + this.recentBlocksController.store.subscribe((state) => { + this.store.updateState({ RecentBlocks: state }) + }) this.infuraController.store.subscribe((state) => { this.store.updateState({ InfuraController: state }) }) // manual mem state subscriptions - this.networkController.store.subscribe(this.sendUpdate.bind(this)) - this.accountTracker.store.subscribe(this.sendUpdate.bind(this)) - this.txController.memStore.subscribe(this.sendUpdate.bind(this)) - this.balancesController.store.subscribe(this.sendUpdate.bind(this)) - this.messageManager.memStore.subscribe(this.sendUpdate.bind(this)) - this.personalMessageManager.memStore.subscribe(this.sendUpdate.bind(this)) - this.typedMessageManager.memStore.subscribe(this.sendUpdate.bind(this)) - this.keyringController.memStore.subscribe(this.sendUpdate.bind(this)) - this.preferencesController.store.subscribe(this.sendUpdate.bind(this)) - this.addressBookController.store.subscribe(this.sendUpdate.bind(this)) - this.currencyController.store.subscribe(this.sendUpdate.bind(this)) - this.noticeController.memStore.subscribe(this.sendUpdate.bind(this)) - this.shapeshiftController.store.subscribe(this.sendUpdate.bind(this)) - this.infuraController.store.subscribe(this.sendUpdate.bind(this)) + const sendUpdate = this.sendUpdate.bind(this) + this.networkController.store.subscribe(sendUpdate) + this.accountTracker.store.subscribe(sendUpdate) + this.txController.memStore.subscribe(sendUpdate) + this.balancesController.store.subscribe(sendUpdate) + this.messageManager.memStore.subscribe(sendUpdate) + this.personalMessageManager.memStore.subscribe(sendUpdate) + this.typedMessageManager.memStore.subscribe(sendUpdate) + this.keyringController.memStore.subscribe(sendUpdate) + this.preferencesController.store.subscribe(sendUpdate) + this.recentBlocksController.store.subscribe(sendUpdate) + this.addressBookController.store.subscribe(sendUpdate) + this.currencyController.store.subscribe(sendUpdate) + this.noticeController.memStore.subscribe(sendUpdate) + this.shapeshiftController.store.subscribe(sendUpdate) + this.infuraController.store.subscribe(sendUpdate) } // @@ -298,6 +308,7 @@ module.exports = class MetamaskController extends EventEmitter { this.currencyController.store.getState(), this.noticeController.memStore.getState(), this.infuraController.store.getState(), + this.recentBlocksController.store.getState(), // config manager this.configManager.getConfig(), this.shapeshiftController.store.getState(), From 9bb701f0633bb117bf67f13935b5e121b6b9837a Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Mon, 18 Dec 2017 15:49:55 -0800 Subject: [PATCH 39/42] Add failing test for updating token details --- test/unit/preferences-controller-test.js | 48 ++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 test/unit/preferences-controller-test.js diff --git a/test/unit/preferences-controller-test.js b/test/unit/preferences-controller-test.js new file mode 100644 index 000000000..9fb5e4251 --- /dev/null +++ b/test/unit/preferences-controller-test.js @@ -0,0 +1,48 @@ +const assert = require('assert') +const PreferencesController = require('../../app/scripts/controllers/preferences') + +describe('preferences controller', function () { + let preferencesController + + before(() => { + preferencesController = new PreferencesController() + }) + + describe('addToken', function () { + it('should add that token to its state', async function () { + const address = '0xabcdef1234567' + const symbol = 'ABBR' + const decimals = 5 + + await preferencesController.addToken(address, symbol, decimals) + + const tokens = preferencesController.getTokens() + assert.equal(tokens.length, 1, 'one token added') + + const added = tokens[0] + assert.equal(added.address, address, 'set address correctly') + assert.equal(added.symbol, symbol, 'set symbol correctly') + assert.equal(added.decimals, decimals, 'set decimals correctly') + }) + + it('should allow updating a token value', async function () { + const address = '0xabcdef1234567' + const symbol = 'ABBR' + const decimals = 5 + + await preferencesController.addToken(address, symbol, decimals) + + const newDecimals = 6 + await preferencesController.addToken(address, symbol, newDecimals) + + const tokens = preferencesController.getTokens() + assert.equal(tokens.length, 1, 'one token added') + + const added = tokens[0] + assert.equal(added.address, address, 'set address correctly') + assert.equal(added.symbol, symbol, 'set symbol correctly') + assert.equal(added.decimals, newDecimals, 'updated decimals correctly') + }) + }) +}) + From 975f7279c713d88bb59205073540b2185f3a812f Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Mon, 18 Dec 2017 15:57:04 -0800 Subject: [PATCH 40/42] Allow updating token details Fixes #2173 --- app/scripts/controllers/preferences.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js index bc4848421..c42f47037 100644 --- a/app/scripts/controllers/preferences.js +++ b/app/scripts/controllers/preferences.js @@ -26,23 +26,23 @@ class PreferencesController { return this.store.getState().selectedAddress } - addToken (rawAddress, symbol, decimals) { + async addToken (rawAddress, symbol, decimals) { const address = normalizeAddress(rawAddress) const newEntry = { address, symbol, decimals } const tokens = this.store.getState().tokens - const previousIndex = tokens.find((token, index) => { + const previousEntry = tokens.find((token, index) => { return token.address === address }) + const previousIndex = tokens.indexOf(previousEntry) - if (previousIndex) { + if (previousEntry) { tokens[previousIndex] = newEntry } else { tokens.push(newEntry) } this.store.updateState({ tokens }) - return Promise.resolve() } getTokens () { From 8cc7e47369ab36c579a1996e0a119e1e27a8e8f2 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Mon, 18 Dec 2017 15:57:37 -0800 Subject: [PATCH 41/42] Bump changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b7256466..8952236d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Current Master +- Fix bug that prevented updating custom token details. + ## 3.13.3 2017-12-14 - Show tokens that are held that have no balance. From 30b45c8a385e9ae4c6b78d0a7ec6929f7bad4d9a Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Tue, 19 Dec 2017 12:22:48 -0800 Subject: [PATCH 42/42] Do not log whole txs in recent block controller. Only record gas prices, because that has a current use. --- app/scripts/controllers/recent-blocks.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/app/scripts/controllers/recent-blocks.js b/app/scripts/controllers/recent-blocks.js index 27f9013ff..4a906261e 100644 --- a/app/scripts/controllers/recent-blocks.js +++ b/app/scripts/controllers/recent-blocks.js @@ -23,8 +23,15 @@ class RecentBlocksController { } processBlock (newBlock) { + const block = extend(newBlock, { + gasPrices: newBlock.transactions.map((tx) => { + return tx.gasPrice + }), + }) + delete block.transactions + const state = this.store.getState() - state.recentBlocks.push(newBlock) + state.recentBlocks.push(block) while (state.recentBlocks.length > this.historyLength) { state.recentBlocks.shift()