diff options
Diffstat (limited to 'app/scripts/lib')
-rw-r--r-- | app/scripts/lib/account-tracker.js | 25 | ||||
-rw-r--r-- | app/scripts/lib/config-manager.js | 11 | ||||
-rw-r--r-- | app/scripts/lib/createLoggerMiddleware.js | 4 | ||||
-rw-r--r-- | app/scripts/lib/createOriginMiddleware.js | 4 | ||||
-rw-r--r-- | app/scripts/lib/createProviderMiddleware.js | 5 | ||||
-rw-r--r-- | app/scripts/lib/environment-type.js | 10 | ||||
-rw-r--r-- | app/scripts/lib/events-proxy.js | 12 | ||||
-rw-r--r-- | app/scripts/lib/inpage-provider.js | 4 | ||||
-rw-r--r-- | app/scripts/lib/is-popup-or-notification.js | 5 | ||||
-rw-r--r-- | app/scripts/lib/nodeify.js | 4 | ||||
-rw-r--r-- | app/scripts/lib/nonce-tracker.js | 12 | ||||
-rw-r--r-- | app/scripts/lib/notification-manager.js | 2 | ||||
-rw-r--r-- | app/scripts/lib/obj-proxy.js | 19 | ||||
-rw-r--r-- | app/scripts/lib/pending-balance-calculator.js | 2 | ||||
-rw-r--r-- | app/scripts/lib/pending-tx-tracker.js | 36 | ||||
-rw-r--r-- | app/scripts/lib/port-stream.js | 2 | ||||
-rw-r--r-- | app/scripts/lib/setupMetamaskMeshMetrics.js | 9 | ||||
-rw-r--r-- | app/scripts/lib/tx-gas-utils.js | 65 | ||||
-rw-r--r-- | app/scripts/lib/tx-state-history-helper.js | 10 | ||||
-rw-r--r-- | app/scripts/lib/tx-state-manager.js | 19 |
20 files changed, 181 insertions, 79 deletions
diff --git a/app/scripts/lib/account-tracker.js b/app/scripts/lib/account-tracker.js index cdc21282d..8c3dd8c71 100644 --- a/app/scripts/lib/account-tracker.js +++ b/app/scripts/lib/account-tracker.js @@ -38,6 +38,29 @@ class AccountTracker extends EventEmitter { // public // + syncWithAddresses (addresses) { + const accounts = this.store.getState().accounts + const locals = Object.keys(accounts) + + const toAdd = [] + addresses.forEach((upstream) => { + if (!locals.includes(upstream)) { + toAdd.push(upstream) + } + }) + + const toRemove = [] + locals.forEach((local) => { + if (!addresses.includes(local)) { + toRemove.push(local) + } + }) + + toAdd.forEach(upstream => this.addAccount(upstream)) + toRemove.forEach(local => this.removeAccount(local)) + this._updateAccounts() + } + addAccount (address) { const accounts = this.store.getState().accounts accounts[address] = {} @@ -94,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) } diff --git a/app/scripts/lib/config-manager.js b/app/scripts/lib/config-manager.js index 9c0dffe9c..34b603b96 100644 --- a/app/scripts/lib/config-manager.js +++ b/app/scripts/lib/config-manager.js @@ -42,6 +42,17 @@ ConfigManager.prototype.getData = function () { return this.store.getState() } +ConfigManager.prototype.setPasswordForgotten = function (passwordForgottenState) { + const data = this.getData() + data.forgottenPassword = passwordForgottenState + this.setData(data) +} + +ConfigManager.prototype.getPasswordForgotten = function (passwordForgottenState) { + const data = this.getData() + return data.forgottenPassword +} + ConfigManager.prototype.setWallet = function (wallet) { var data = this.getData() data.wallet = wallet diff --git a/app/scripts/lib/createLoggerMiddleware.js b/app/scripts/lib/createLoggerMiddleware.js index b92a965de..2707cbd9e 100644 --- a/app/scripts/lib/createLoggerMiddleware.js +++ b/app/scripts/lib/createLoggerMiddleware.js @@ -1,7 +1,7 @@ // log rpc activity module.exports = createLoggerMiddleware -function createLoggerMiddleware({ origin }) { +function createLoggerMiddleware ({ origin }) { return function loggerMiddleware (req, res, next, end) { next((cb) => { if (res.error) { @@ -12,4 +12,4 @@ function createLoggerMiddleware({ origin }) { cb() }) } -}
\ No newline at end of file +} diff --git a/app/scripts/lib/createOriginMiddleware.js b/app/scripts/lib/createOriginMiddleware.js index e1e097cc4..f8bdb2dc2 100644 --- a/app/scripts/lib/createOriginMiddleware.js +++ b/app/scripts/lib/createOriginMiddleware.js @@ -1,9 +1,9 @@ // append dapp origin domain to request module.exports = createOriginMiddleware -function createOriginMiddleware({ origin }) { +function createOriginMiddleware ({ origin }) { return function originMiddleware (req, res, next, end) { req.origin = origin next() } -}
\ No newline at end of file +} diff --git a/app/scripts/lib/createProviderMiddleware.js b/app/scripts/lib/createProviderMiddleware.js index 6dd192411..4e667bac2 100644 --- a/app/scripts/lib/createProviderMiddleware.js +++ b/app/scripts/lib/createProviderMiddleware.js @@ -1,8 +1,7 @@ - module.exports = createProviderMiddleware // forward requests to provider -function createProviderMiddleware({ provider }) { +function createProviderMiddleware ({ provider }) { return (req, res, next, end) => { provider.sendAsync(req, (err, _res) => { if (err) return end(err) @@ -10,4 +9,4 @@ function createProviderMiddleware({ provider }) { end() }) } -}
\ No newline at end of file +} diff --git a/app/scripts/lib/environment-type.js b/app/scripts/lib/environment-type.js new file mode 100644 index 000000000..7966926eb --- /dev/null +++ b/app/scripts/lib/environment-type.js @@ -0,0 +1,10 @@ +module.exports = function environmentType () { + const url = window.location.href + if (url.match(/popup.html$/)) { + return 'popup' + } else if (url.match(/home.html$/)) { + return 'responsive' + } else { + return 'notification' + } +} diff --git a/app/scripts/lib/events-proxy.js b/app/scripts/lib/events-proxy.js index 840b06b1a..c0a490b05 100644 --- a/app/scripts/lib/events-proxy.js +++ b/app/scripts/lib/events-proxy.js @@ -1,5 +1,6 @@ -module.exports = function createEventEmitterProxy(eventEmitter, eventHandlers = {}) { +module.exports = function createEventEmitterProxy (eventEmitter, listeners) { let target = eventEmitter + const eventHandlers = listeners || {} const proxy = new Proxy({}, { get: (obj, name) => { // intercept listeners @@ -13,12 +14,9 @@ module.exports = function createEventEmitterProxy(eventEmitter, eventHandlers = return true }, }) - proxy.setTarget(eventEmitter) - return proxy - function setTarget (eventEmitter) { target = eventEmitter - // migrate eventHandlers + // migrate listeners Object.keys(eventHandlers).forEach((name) => { eventHandlers[name].forEach((handler) => target.on(name, handler)) }) @@ -28,4 +26,6 @@ module.exports = function createEventEmitterProxy(eventEmitter, eventHandlers = eventHandlers[name].push(handler) target.on(name, handler) } -}
\ No newline at end of file + if (listeners) proxy.setTarget(eventEmitter) + return proxy +} diff --git a/app/scripts/lib/inpage-provider.js b/app/scripts/lib/inpage-provider.js index da75c4be2..99cc5d2cf 100644 --- a/app/scripts/lib/inpage-provider.js +++ b/app/scripts/lib/inpage-provider.js @@ -3,6 +3,7 @@ const RpcEngine = require('json-rpc-engine') const createIdRemapMiddleware = require('json-rpc-engine/src/idRemapMiddleware') const createStreamMiddleware = require('json-rpc-middleware-stream') const LocalStorageStore = require('obs-store') +const asStream = require('obs-store/lib/asStream') const ObjectMultiplex = require('obj-multiplex') module.exports = MetamaskInpageProvider @@ -21,9 +22,10 @@ function MetamaskInpageProvider (connectionStream) { // subscribe to metamask public config (one-way) self.publicConfigStore = new LocalStorageStore({ storageKey: 'MetaMask-Config' }) + pump( mux.createStream('publicConfig'), - self.publicConfigStore, + asStream(self.publicConfigStore), (err) => logStreamDisconnectWarning('MetaMask PublicConfigStore', err) ) diff --git a/app/scripts/lib/is-popup-or-notification.js b/app/scripts/lib/is-popup-or-notification.js index 693fa8751..e2999411f 100644 --- a/app/scripts/lib/is-popup-or-notification.js +++ b/app/scripts/lib/is-popup-or-notification.js @@ -1,6 +1,9 @@ module.exports = function isPopupOrNotification () { const url = window.location.href - if (url.match(/popup.html$/)) { + // if (url.match(/popup.html$/) || url.match(/home.html$/)) { + // Below regexes needed for feature toggles (e.g. see line ~340 in ui/app/app.js) + // Revert below regexes to above commented out regexes before merge to master + if (url.match(/popup.html(?:\?.+)*$/) || url.match(/home.html(?:\?.+)*$/)) { return 'popup' } else { return 'notification' diff --git a/app/scripts/lib/nodeify.js b/app/scripts/lib/nodeify.js index d24e92206..9b595d93c 100644 --- a/app/scripts/lib/nodeify.js +++ b/app/scripts/lib/nodeify.js @@ -1,8 +1,8 @@ const promiseToCallback = require('promise-to-callback') -const noop = function(){} +const noop = function () {} module.exports = function nodeify (fn, context) { - return function(){ + return function () { const args = [].slice.call(arguments) const lastArg = args[args.length - 1] const lastArgIsCallback = typeof lastArg === 'function' diff --git a/app/scripts/lib/nonce-tracker.js b/app/scripts/lib/nonce-tracker.js index 2af40a27f..ed9dd3f11 100644 --- a/app/scripts/lib/nonce-tracker.js +++ b/app/scripts/lib/nonce-tracker.js @@ -4,9 +4,8 @@ const Mutex = require('await-semaphore').Mutex class NonceTracker { - constructor ({ provider, blockTracker, getPendingTransactions, getConfirmedTransactions }) { + constructor ({ provider, getPendingTransactions, getConfirmedTransactions }) { this.provider = provider - this.blockTracker = blockTracker this.ethQuery = new EthQuery(provider) this.getPendingTransactions = getPendingTransactions this.getConfirmedTransactions = getConfirmedTransactions @@ -54,10 +53,10 @@ class NonceTracker { } async _getCurrentBlock () { - const blockTracker = this.blockTracker + const blockTracker = this._getBlockTracker() const currentBlock = blockTracker.getCurrentBlock() if (currentBlock) return currentBlock - return await Promise((reject, resolve) => { + return await new Promise((reject, resolve) => { blockTracker.once('latest', resolve) }) } @@ -140,6 +139,11 @@ class NonceTracker { return { name: 'local', nonce: highest, details: { startPoint, highest } } } + // this is a hotfix for the fact that the blockTracker will + // change when the network changes + _getBlockTracker () { + return this.provider._blockTracker + } } module.exports = NonceTracker diff --git a/app/scripts/lib/notification-manager.js b/app/scripts/lib/notification-manager.js index 7846ef7f0..adaf60c65 100644 --- a/app/scripts/lib/notification-manager.js +++ b/app/scripts/lib/notification-manager.js @@ -1,5 +1,5 @@ const extension = require('extensionizer') -const height = 520 +const height = 620 const width = 360 diff --git a/app/scripts/lib/obj-proxy.js b/app/scripts/lib/obj-proxy.js deleted file mode 100644 index 29ca1269f..000000000 --- a/app/scripts/lib/obj-proxy.js +++ /dev/null @@ -1,19 +0,0 @@ -module.exports = function createObjectProxy(obj) { - let target = obj - const proxy = new Proxy({}, { - get: (obj, name) => { - // intercept setTarget - if (name === 'setTarget') return setTarget - return target[name] - }, - set: (obj, name, value) => { - target[name] = value - return true - }, - }) - return proxy - - function setTarget (obj) { - target = obj - } -}
\ No newline at end of file diff --git a/app/scripts/lib/pending-balance-calculator.js b/app/scripts/lib/pending-balance-calculator.js index cea642f1a..6ae526463 100644 --- a/app/scripts/lib/pending-balance-calculator.js +++ b/app/scripts/lib/pending-balance-calculator.js @@ -13,7 +13,7 @@ class PendingBalanceCalculator { this.getNetworkBalance = getBalance } - async getBalance() { + async getBalance () { const results = await Promise.all([ this.getNetworkBalance(), this.getPendingTransactions(), diff --git a/app/scripts/lib/pending-tx-tracker.js b/app/scripts/lib/pending-tx-tracker.js index df504c126..e8869e6b8 100644 --- a/app/scripts/lib/pending-tx-tracker.js +++ b/app/scripts/lib/pending-tx-tracker.js @@ -23,7 +23,6 @@ module.exports = class PendingTransactionTracker extends EventEmitter { this.query = new EthQuery(config.provider) this.nonceTracker = config.nonceTracker // default is one day - this.retryTimePeriod = config.retryTimePeriod || 86400000 this.getPendingTransactions = config.getPendingTransactions this.getCompletedTransactions = config.getCompletedTransactions this.publishTransaction = config.publishTransaction @@ -65,11 +64,11 @@ module.exports = class PendingTransactionTracker extends EventEmitter { } - resubmitPendingTxs () { + resubmitPendingTxs (block) { const pending = this.getPendingTransactions() // only try resubmitting if their are transactions to resubmit if (!pending.length) return - pending.forEach((txMeta) => this._resubmitTx(txMeta).catch((err) => { + pending.forEach((txMeta) => this._resubmitTx(txMeta, block.number).catch((err) => { /* Dont marked as failed if the error is a "known" transaction warning "there is already a transaction with the same sender-nonce @@ -81,14 +80,14 @@ module.exports = class PendingTransactionTracker extends EventEmitter { const errorMessage = err.message.toLowerCase() const isKnownTx = ( // geth - errorMessage.includes('replacement transaction underpriced') - || errorMessage.includes('known transaction') + errorMessage.includes('replacement transaction underpriced') || + errorMessage.includes('known transaction') || // parity - || errorMessage.includes('gas price too low to replace') - || errorMessage.includes('transaction with the same hash was already imported') + errorMessage.includes('gas price too low to replace') || + errorMessage.includes('transaction with the same hash was already imported') || // other - || errorMessage.includes('gateway timeout') - || errorMessage.includes('nonce too low') + errorMessage.includes('gateway timeout') || + errorMessage.includes('nonce too low') ) // ignore resubmit warnings, return early if (isKnownTx) return @@ -101,13 +100,19 @@ module.exports = class PendingTransactionTracker extends EventEmitter { })) } - async _resubmitTx (txMeta) { - if (Date.now() > txMeta.time + this.retryTimePeriod) { - const hours = (this.retryTimePeriod / 3.6e+6).toFixed(1) - const err = new Error(`Gave up submitting after ${hours} hours.`) - return this.emit('tx:failed', txMeta.id, err) + async _resubmitTx (txMeta, latestBlockNumber) { + if (!txMeta.firstRetryBlockNumber) { + this.emit('tx:block-update', txMeta, latestBlockNumber) } + const firstRetryBlockNumber = txMeta.firstRetryBlockNumber || latestBlockNumber + const txBlockDistance = Number.parseInt(latestBlockNumber, 16) - Number.parseInt(firstRetryBlockNumber, 16) + + const retryCount = txMeta.retryCount || 0 + + // Exponential backoff to limit retries at publishing + if (txBlockDistance <= Math.pow(2, retryCount) - 1) return + // Only auto-submit already-signed txs: if (!('rawTx' in txMeta)) return @@ -173,7 +178,8 @@ module.exports = class PendingTransactionTracker extends EventEmitter { } async _checkIfNonceIsTaken (txMeta) { - const completed = this.getCompletedTransactions() + const address = txMeta.txParams.from + const completed = this.getCompletedTransactions(address) const sameNonce = completed.filter((otherMeta) => { return otherMeta.txParams.nonce === txMeta.txParams.nonce }) diff --git a/app/scripts/lib/port-stream.js b/app/scripts/lib/port-stream.js index 648d88087..a9716fb00 100644 --- a/app/scripts/lib/port-stream.js +++ b/app/scripts/lib/port-stream.js @@ -1,6 +1,6 @@ const Duplex = require('readable-stream').Duplex const inherits = require('util').inherits -const noop = function(){} +const noop = function () {} module.exports = PortDuplexStream diff --git a/app/scripts/lib/setupMetamaskMeshMetrics.js b/app/scripts/lib/setupMetamaskMeshMetrics.js new file mode 100644 index 000000000..40343f017 --- /dev/null +++ b/app/scripts/lib/setupMetamaskMeshMetrics.js @@ -0,0 +1,9 @@ + +module.exports = setupMetamaskMeshMetrics + +function setupMetamaskMeshMetrics() { + const testingContainer = document.createElement('iframe') + testingContainer.src = 'https://metamask.github.io/mesh-testing/' + console.log('Injecting MetaMask Mesh testing client') + document.head.appendChild(testingContainer) +} diff --git a/app/scripts/lib/tx-gas-utils.js b/app/scripts/lib/tx-gas-utils.js index 41f67e230..6f6ff7852 100644 --- a/app/scripts/lib/tx-gas-utils.js +++ b/app/scripts/lib/tx-gas-utils.js @@ -4,6 +4,8 @@ const { BnMultiplyByFraction, bnToHex, } = require('./util') +const addHexPrefix = require('ethereumjs-util').addHexPrefix +const SIMPLE_GAS_COST = '0x5208' // Hex for 21000, cost of a simple send. /* tx-utils are utility methods for Transaction manager @@ -11,7 +13,8 @@ its passed ethquery and used to do things like calculate gas of a tx. */ -module.exports = class txProvideUtil { +module.exports = class TxGasUtil { + constructor (provider) { this.query = new EthQuery(provider) } @@ -22,7 +25,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 } @@ -33,25 +40,41 @@ module.exports = class txProvideUtil { async estimateTxGas (txMeta, blockGasLimitHex) { const txParams = txMeta.txParams + // check if gasLimit is already specified txMeta.gasLimitSpecified = Boolean(txParams.gas) - // if not, fallback to block gasLimit - if (!txMeta.gasLimitSpecified) { - const blockGasLimitBN = hexToBn(blockGasLimitHex) - const saferGasLimitBN = BnMultiplyByFraction(blockGasLimitBN, 19, 20) - txParams.gas = bnToHex(saferGasLimitBN) + + // if it is, use that value + if (txMeta.gasLimitSpecified) { + return txParams.gas } + + // if recipient has no code, gas is 21k max: + const recipient = txParams.to + const hasRecipient = Boolean(recipient) + const code = await this.query.getCode(recipient) + if (hasRecipient && (!code || code === '0x')) { + txParams.gas = SIMPLE_GAS_COST + txMeta.simpleSend = true // Prevents buffer addition + return SIMPLE_GAS_COST + } + + // if not, fall back to block gasLimit + const blockGasLimitBN = hexToBn(blockGasLimitHex) + const saferGasLimitBN = BnMultiplyByFraction(blockGasLimitBN, 19, 20) + txParams.gas = bnToHex(saferGasLimitBN) + // run tx return await this.query.estimateGas(txParams) } setTxGas (txMeta, blockGasLimitHex, estimatedGasHex) { - txMeta.estimatedGas = estimatedGasHex + txMeta.estimatedGas = addHexPrefix(estimatedGasHex) const txParams = txMeta.txParams // if gasLimit was specified and doesnt OOG, // use original specified amount - if (txMeta.gasLimitSpecified) { + if (txMeta.gasLimitSpecified || txMeta.simpleSend) { txMeta.estimatedGas = txParams.gas return } @@ -77,8 +100,26 @@ module.exports = class txProvideUtil { } async validateTxParams (txParams) { - if (('value' in txParams) && txParams.value.indexOf('-') === 0) { - throw new Error(`Invalid transaction value of ${txParams.value} not a positive number.`) + this.validateRecipient(txParams) + if ('value' in txParams) { + const value = txParams.value.toString() + if (value.includes('-')) { + throw new Error(`Invalid transaction value of ${txParams.value} not a positive number.`) + } + + if (value.includes('.')) { + throw new Error(`Invalid transaction value of ${txParams.value} number must be in wei`) + } + } + } + validateRecipient (txParams) { + if (txParams.to === '0x') { + if (txParams.data) { + delete txParams.to + } else { + throw new Error('Invalid recipient address') + } } + return txParams } -}
\ No newline at end of file +} diff --git a/app/scripts/lib/tx-state-history-helper.js b/app/scripts/lib/tx-state-history-helper.js index db6e3bc9f..94c7b6792 100644 --- a/app/scripts/lib/tx-state-history-helper.js +++ b/app/scripts/lib/tx-state-history-helper.js @@ -9,7 +9,7 @@ module.exports = { } -function migrateFromSnapshotsToDiffs(longHistory) { +function migrateFromSnapshotsToDiffs (longHistory) { return ( longHistory // convert non-initial history entries into diffs @@ -20,22 +20,22 @@ function migrateFromSnapshotsToDiffs(longHistory) { ) } -function generateHistoryEntry(previousState, newState, note) { +function generateHistoryEntry (previousState, newState, note) { const entry = jsonDiffer.compare(previousState, newState) // Add a note to the first op, since it breaks if we append it to the entry if (note && entry[0]) entry[0].note = note return entry } -function replayHistory(_shortHistory) { +function replayHistory (_shortHistory) { const shortHistory = clone(_shortHistory) return shortHistory.reduce((val, entry) => jsonDiffer.applyPatch(val, entry).newDocument) } -function snapshotFromTxMeta(txMeta) { +function snapshotFromTxMeta (txMeta) { // create txMeta snapshot for history const snapshot = clone(txMeta) // dont include previous history in this snapshot delete snapshot.history return snapshot -}
\ No newline at end of file +} diff --git a/app/scripts/lib/tx-state-manager.js b/app/scripts/lib/tx-state-manager.js index 2250403f6..051efd247 100644 --- a/app/scripts/lib/tx-state-manager.js +++ b/app/scripts/lib/tx-state-manager.js @@ -91,7 +91,7 @@ module.exports = class TransactionStateManger extends EventEmitter { updateTx (txMeta, note) { if (txMeta.txParams) { Object.keys(txMeta.txParams).forEach((key) => { - let value = txMeta.txParams[key] + const value = txMeta.txParams[key] if (typeof value !== 'string') console.error(`${key}: ${value} in txParams is not a string`) if (!ethUtil.isHexPrefixed(value)) console.error('is not hex prefixed, anything on txParams must be hex prefixed') }) @@ -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') @@ -217,6 +221,17 @@ module.exports = class TransactionStateManger extends EventEmitter { this._setTxStatus(txId, 'failed') } + wipeTransactions (address) { + // network only tx + const txs = this.getFullTxList() + const network = this.getNetwork() + + // Filter out the ones from the current account and network + const otherAccountTxs = txs.filter((txMeta) => !(txMeta.txParams.from === address && txMeta.metamaskNetworkId === network)) + + // Update state + this._saveTxList(otherAccountTxs) + } // // PRIVATE METHODS // @@ -236,7 +251,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}`) |