aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkumavis <kumavis@users.noreply.github.com>2017-05-24 15:18:40 +0800
committerGitHub <noreply@github.com>2017-05-24 15:18:40 +0800
commit6be1aa82c00c85e5c839de4cfb781088d229160c (patch)
tree4ade3448b5130a5f58a19f7fcebff049eb33e065
parente55329d28b61b2d1066cf794a2099a18a94be7a4 (diff)
parente8288ad4bfaa5b65b23f0a03d0c036457a24893f (diff)
downloadtangerine-wallet-browser-6be1aa82c00c85e5c839de4cfb781088d229160c.tar
tangerine-wallet-browser-6be1aa82c00c85e5c839de4cfb781088d229160c.tar.gz
tangerine-wallet-browser-6be1aa82c00c85e5c839de4cfb781088d229160c.tar.bz2
tangerine-wallet-browser-6be1aa82c00c85e5c839de4cfb781088d229160c.tar.lz
tangerine-wallet-browser-6be1aa82c00c85e5c839de4cfb781088d229160c.tar.xz
tangerine-wallet-browser-6be1aa82c00c85e5c839de4cfb781088d229160c.tar.zst
tangerine-wallet-browser-6be1aa82c00c85e5c839de4cfb781088d229160c.zip
Merge branch 'master' into Version-3.7.1
-rw-r--r--CHANGELOG.md10
-rw-r--r--app/scripts/controllers/network.js128
-rw-r--r--app/scripts/controllers/transactions.js66
-rw-r--r--app/scripts/first-time-state.js3
-rw-r--r--app/scripts/lib/config-manager.js62
-rw-r--r--app/scripts/lib/inpage-provider.js22
-rw-r--r--app/scripts/lib/tx-utils.js8
-rw-r--r--app/scripts/metamask-controller.js98
-rw-r--r--app/scripts/migrations/014.js34
-rw-r--r--app/scripts/migrations/index.js1
-rw-r--r--package.json5
-rw-r--r--test/unit/network-contoller-test.js74
-rw-r--r--test/unit/tx-controller-test.js4
-rw-r--r--test/unit/tx-utils-test.js6
-rw-r--r--ui/app/components/transaction-list-item-icon.js16
-rw-r--r--ui/app/components/transaction-list-item.js19
16 files changed, 422 insertions, 134 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 371b348ca..60c6a6f44 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,16 @@
## Current Master
+- Now when switching networks the extension does not restart
+
+## 3.7.0 2017-5-23
+
+- Add Transaction Number (nonce) to transaction list.
+- Label the pending tx icon with a tooltip.
+- Fix bug where website filters would pile up and not deallocate when leaving a site.
+- Continually resubmit pending txs for a period of time to ensure successful broadcast.
+- ENS names will no longer resolve to their owner if no resolver is set. Resolvers must be explicitly set and configured.
+
## 3.6.5 2017-5-17
- Fix bug where edited gas parameters would not take effect.
diff --git a/app/scripts/controllers/network.js b/app/scripts/controllers/network.js
new file mode 100644
index 000000000..4fdd92921
--- /dev/null
+++ b/app/scripts/controllers/network.js
@@ -0,0 +1,128 @@
+const EventEmitter = require('events')
+const MetaMaskProvider = require('web3-provider-engine/zero.js')
+const ObservableStore = require('obs-store')
+const ComposedStore = require('obs-store/lib/composed')
+const extend = require('xtend')
+const EthQuery = require('eth-query')
+const RPC_ADDRESS_LIST = require('../config.js').network
+const DEFAULT_RPC = RPC_ADDRESS_LIST['rinkeby']
+
+module.exports = class NetworkController extends EventEmitter {
+ constructor (config) {
+ super()
+ this.networkStore = new ObservableStore('loading')
+ config.provider.rpcTarget = this.getRpcAddressForType(config.provider.type, config.provider)
+ this.providerStore = new ObservableStore(config.provider)
+ this.store = new ComposedStore({ provider: this.providerStore, network: this.networkStore })
+ this._providerListeners = {}
+
+ this.on('networkDidChange', this.lookupNetwork)
+ this.providerStore.subscribe((state) => this.switchNetwork({rpcUrl: state.rpcTarget}))
+ }
+
+ get provider () {
+ return this._proxy
+ }
+
+ set provider (provider) {
+ this._provider = provider
+ }
+
+ initializeProvider (opts) {
+ this.providerInit = opts
+ this._provider = MetaMaskProvider(opts)
+ this._proxy = new Proxy(this._provider, {
+ get: (obj, name) => {
+ if (name === 'on') return this._on.bind(this)
+ return this._provider[name]
+ },
+ set: (obj, name, value) => {
+ this._provider[name] = value
+ },
+ })
+ this.provider.on('block', this._logBlock.bind(this))
+ this.provider.on('error', this.verifyNetwork.bind(this))
+ this.ethQuery = new EthQuery(this.provider)
+ this.lookupNetwork()
+ return this.provider
+ }
+
+ switchNetwork (providerInit) {
+ this.setNetworkState('loading')
+ const newInit = extend(this.providerInit, providerInit)
+ this.providerInit = newInit
+
+ this._provider.removeAllListeners()
+ this.provider = MetaMaskProvider(newInit)
+ // apply the listners created by other controllers
+ Object.keys(this._providerListeners).forEach((key) => {
+ this._providerListeners[key].forEach((handler) => this._provider.addListener(key, handler))
+ })
+ this.emit('networkDidChange')
+ }
+
+
+ verifyNetwork () {
+ // Check network when restoring connectivity:
+ if (this.isNetworkLoading()) this.lookupNetwork()
+ }
+
+ getNetworkState () {
+ return this.networkStore.getState()
+ }
+
+ setNetworkState (network) {
+ return this.networkStore.putState(network)
+ }
+
+ isNetworkLoading () {
+ return this.getNetworkState() === 'loading'
+ }
+
+ lookupNetwork () {
+ this.ethQuery.sendAsync({ method: 'net_version' }, (err, network) => {
+ if (err) return this.setNetworkState('loading')
+ log.info('web3.getNetwork returned ' + network)
+ this.setNetworkState(network)
+ })
+ }
+
+ setRpcTarget (rpcUrl) {
+ this.providerStore.updateState({
+ type: 'rpc',
+ rpcTarget: rpcUrl,
+ })
+ }
+
+ getCurrentRpcAddress () {
+ const provider = this.getProviderConfig()
+ if (!provider) return null
+ return this.getRpcAddressForType(provider.type)
+ }
+
+ setProviderType (type) {
+ if (type === this.getProviderConfig().type) return
+ const rpcTarget = this.getRpcAddressForType(type)
+ this.providerStore.updateState({type, rpcTarget})
+ }
+
+ getProviderConfig () {
+ return this.providerStore.getState()
+ }
+
+ getRpcAddressForType (type, provider = this.getProviderConfig()) {
+ if (RPC_ADDRESS_LIST[type]) return RPC_ADDRESS_LIST[type]
+ return provider && provider.rpcTarget ? provider.rpcTarget : DEFAULT_RPC
+ }
+
+ _logBlock (block) {
+ log.info(`BLOCK CHANGED: #${block.number.toString('hex')} 0x${block.hash.toString('hex')}`)
+ this.verifyNetwork()
+ }
+
+ _on (event, handler) {
+ if (!this._providerListeners[event]) this._providerListeners[event] = []
+ this._providerListeners[event].push(handler)
+ this._provider.on(event, handler)
+ }
+}
diff --git a/app/scripts/controllers/transactions.js b/app/scripts/controllers/transactions.js
index 21dd25b30..faccf1ab1 100644
--- a/app/scripts/controllers/transactions.js
+++ b/app/scripts/controllers/transactions.js
@@ -4,11 +4,14 @@ const extend = require('xtend')
const Semaphore = require('semaphore')
const ObservableStore = require('obs-store')
const ethUtil = require('ethereumjs-util')
-const EthQuery = require('eth-query')
const TxProviderUtil = require('../lib/tx-utils')
const createId = require('../lib/random-id')
+const denodeify = require('denodeify')
-module.exports = class TransactionManager extends EventEmitter {
+const RETRY_LIMIT = 200
+const RESUBMIT_INTERVAL = 10000 // Ten seconds
+
+module.exports = class TransactionController extends EventEmitter {
constructor (opts) {
super()
this.store = new ObservableStore(extend({
@@ -20,8 +23,8 @@ module.exports = class TransactionManager extends EventEmitter {
this.txHistoryLimit = opts.txHistoryLimit
this.provider = opts.provider
this.blockTracker = opts.blockTracker
- this.query = new EthQuery(this.provider)
- this.txProviderUtils = new TxProviderUtil(this.provider)
+ this.query = opts.ethQuery
+ this.txProviderUtils = new TxProviderUtil(this.query)
this.blockTracker.on('block', this.checkForTxInBlock.bind(this))
this.signEthTx = opts.signTransaction
this.nonceLock = Semaphore(1)
@@ -31,6 +34,8 @@ module.exports = class TransactionManager extends EventEmitter {
this.store.subscribe(() => this._updateMemstore())
this.networkStore.subscribe(() => this._updateMemstore())
this.preferencesStore.subscribe(() => this._updateMemstore())
+
+ this.continuallyResubmitPendingTxs()
}
getState () {
@@ -38,7 +43,7 @@ module.exports = class TransactionManager extends EventEmitter {
}
getNetwork () {
- return this.networkStore.getState().network
+ return this.networkStore.getState()
}
getSelectedAddress () {
@@ -230,7 +235,11 @@ module.exports = class TransactionManager extends EventEmitter {
})
}
- publishTransaction (txId, rawTx, cb) {
+ publishTransaction (txId, rawTx, cb = warn) {
+ const txMeta = this.getTx(txId)
+ txMeta.rawTx = rawTx
+ this.updateTx(txMeta)
+
this.txProviderUtils.publishTransaction(rawTx, (err, txHash) => {
if (err) return cb(err)
this.setTxHash(txId, txHash)
@@ -353,7 +362,7 @@ module.exports = class TransactionManager extends EventEmitter {
message: 'There was a problem loading this transaction.',
}
this.updateTx(txMeta)
- return console.error(err)
+ return log.error(err)
}
if (txParams.blockNumber) {
this.setTxStatusConfirmed(txId)
@@ -379,6 +388,7 @@ module.exports = class TransactionManager extends EventEmitter {
this.emit(`${txMeta.id}:${status}`, txId)
if (status === 'submitted' || status === 'rejected') {
this.emit(`${txMeta.id}:finished`, txMeta)
+
}
this.updateTx(txMeta)
this.emit('updateBadge')
@@ -398,7 +408,47 @@ module.exports = class TransactionManager extends EventEmitter {
})
this.memStore.updateState({ unapprovedTxs, selectedAddressTxList })
}
+
+ continuallyResubmitPendingTxs () {
+ const pending = this.getTxsByMetaData('status', 'submitted')
+ const resubmit = denodeify(this.resubmitTx.bind(this))
+ Promise.all(pending.map(txMeta => resubmit(txMeta)))
+ .catch((reason) => {
+ log.info('Problem resubmitting tx', reason)
+ })
+ .then(() => {
+ global.setTimeout(() => {
+ this.continuallyResubmitPendingTxs()
+ }, RESUBMIT_INTERVAL)
+ })
+ }
+
+ resubmitTx (txMeta, cb) {
+ // Increment a try counter.
+ if (!('retryCount' in txMeta)) {
+ txMeta.retryCount = 0
+ }
+
+ // Only auto-submit already-signed txs:
+ if (!('rawTx' in txMeta)) {
+ return cb()
+ }
+
+ if (txMeta.retryCount > RETRY_LIMIT) {
+ txMeta.err = {
+ isWarning: true,
+ message: 'Gave up submitting tx.',
+ }
+ this.updateTx(txMeta)
+ return log.error(txMeta.err.message)
+ }
+
+ txMeta.retryCount++
+ const rawTx = txMeta.rawTx
+ this.txProviderUtils.publishTransaction(rawTx, cb)
+ }
+
}
-const warn = () => console.warn('warn was used no cb provided')
+const warn = () => log.warn('warn was used no cb provided')
diff --git a/app/scripts/first-time-state.js b/app/scripts/first-time-state.js
index 29ec1d8d3..dc7788311 100644
--- a/app/scripts/first-time-state.js
+++ b/app/scripts/first-time-state.js
@@ -3,7 +3,8 @@
//
module.exports = {
- config: {
+ config: {},
+ NetworkController: {
provider: {
type: 'rinkeby',
},
diff --git a/app/scripts/lib/config-manager.js b/app/scripts/lib/config-manager.js
index d77cd2126..9c0dffe9c 100644
--- a/app/scripts/lib/config-manager.js
+++ b/app/scripts/lib/config-manager.js
@@ -1,6 +1,7 @@
-const MetamaskConfig = require('../config.js')
const ethUtil = require('ethereumjs-util')
const normalize = require('eth-sig-util').normalize
+const MetamaskConfig = require('../config.js')
+
const MAINNET_RPC = MetamaskConfig.network.mainnet
const ROPSTEN_RPC = MetamaskConfig.network.ropsten
@@ -33,36 +34,6 @@ ConfigManager.prototype.getConfig = function () {
return data.config
}
-ConfigManager.prototype.setRpcTarget = function (rpcUrl) {
- var config = this.getConfig()
- config.provider = {
- type: 'rpc',
- rpcTarget: rpcUrl,
- }
- this.setConfig(config)
-}
-
-ConfigManager.prototype.setProviderType = function (type) {
- var config = this.getConfig()
- config.provider = {
- type: type,
- }
- this.setConfig(config)
-}
-
-ConfigManager.prototype.useEtherscanProvider = function () {
- var config = this.getConfig()
- config.provider = {
- type: 'etherscan',
- }
- this.setConfig(config)
-}
-
-ConfigManager.prototype.getProvider = function () {
- var config = this.getConfig()
- return config.provider
-}
-
ConfigManager.prototype.setData = function (data) {
this.store.putState(data)
}
@@ -136,6 +107,35 @@ ConfigManager.prototype.getSeedWords = function () {
var data = this.getData()
return data.seedWords
}
+ConfigManager.prototype.setRpcTarget = function (rpcUrl) {
+ var config = this.getConfig()
+ config.provider = {
+ type: 'rpc',
+ rpcTarget: rpcUrl,
+ }
+ this.setConfig(config)
+}
+
+ConfigManager.prototype.setProviderType = function (type) {
+ var config = this.getConfig()
+ config.provider = {
+ type: type,
+ }
+ this.setConfig(config)
+}
+
+ConfigManager.prototype.useEtherscanProvider = function () {
+ var config = this.getConfig()
+ config.provider = {
+ type: 'etherscan',
+ }
+ this.setConfig(config)
+}
+
+ConfigManager.prototype.getProvider = function () {
+ var config = this.getConfig()
+ return config.provider
+}
ConfigManager.prototype.getCurrentRpcAddress = function () {
var provider = this.getProvider()
diff --git a/app/scripts/lib/inpage-provider.js b/app/scripts/lib/inpage-provider.js
index e5e398e24..e54f547bd 100644
--- a/app/scripts/lib/inpage-provider.js
+++ b/app/scripts/lib/inpage-provider.js
@@ -1,5 +1,7 @@
const pipe = require('pump')
-const StreamProvider = require('web3-stream-provider')
+const ProviderEngine = require('web3-provider-engine')
+const FilterSubprovider = require('web3-provider-engine/subproviders/filters')
+const StreamSubprovider = require('web3-provider-engine/subproviders/stream')
const LocalStorageStore = require('obs-store')
const ObjectMultiplex = require('./obj-multiplex')
const createRandomId = require('./random-id')
@@ -27,14 +29,24 @@ function MetamaskInpageProvider (connectionStream) {
)
// connect to async provider
- const asyncProvider = self.asyncProvider = new StreamProvider()
+ const engine = new ProviderEngine()
+
+ const filterSubprovider = new FilterSubprovider()
+ engine.addProvider(filterSubprovider)
+
+ const streamSubprovider = new StreamSubprovider()
+ engine.addProvider(streamSubprovider)
+
pipe(
- asyncProvider,
+ streamSubprovider,
multiStream.createStream('provider'),
- asyncProvider,
+ streamSubprovider,
(err) => logStreamDisconnectWarning('MetaMask RpcProvider', err)
)
+ // start polling
+ engine.start()
+
self.idMap = {}
// handle sendAsync requests via asyncProvider
self.sendAsync = function (payload, cb) {
@@ -46,7 +58,7 @@ function MetamaskInpageProvider (connectionStream) {
return message
})
// forward to asyncProvider
- asyncProvider.sendAsync(request, function (err, res) {
+ engine.sendAsync(request, function (err, res) {
if (err) return cb(err)
// transform messages to original ids
eachJsonMessage(res, (message) => {
diff --git a/app/scripts/lib/tx-utils.js b/app/scripts/lib/tx-utils.js
index 084ca3721..76b311653 100644
--- a/app/scripts/lib/tx-utils.js
+++ b/app/scripts/lib/tx-utils.js
@@ -1,5 +1,4 @@
const async = require('async')
-const EthQuery = require('eth-query')
const ethUtil = require('ethereumjs-util')
const Transaction = require('ethereumjs-tx')
const normalize = require('eth-sig-util').normalize
@@ -7,15 +6,14 @@ const BN = ethUtil.BN
/*
tx-utils are utility methods for Transaction manager
-its passed a provider and that is passed to ethquery
+its passed ethquery
and used to do things like calculate gas of a tx.
*/
module.exports = class txProviderUtils {
- constructor (provider) {
- this.provider = provider
- this.query = new EthQuery(provider)
+ constructor (ethQuery) {
+ this.query = ethQuery
}
analyzeGasUsage (txMeta, cb) {
diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js
index f18da9033..a7eb3d056 100644
--- a/app/scripts/metamask-controller.js
+++ b/app/scripts/metamask-controller.js
@@ -7,9 +7,9 @@ const ObservableStore = require('obs-store')
const EthStore = require('./lib/eth-store')
const EthQuery = require('eth-query')
const streamIntoProvider = require('web3-stream-provider/handler')
-const MetaMaskProvider = require('web3-provider-engine/zero.js')
const setupMultiplex = require('./lib/stream-utils.js').setupMultiplex
const KeyringController = require('./keyring-controller')
+const NetworkController = require('./controllers/network')
const PreferencesController = require('./controllers/preferences')
const CurrencyController = require('./controllers/currency')
const NoticeController = require('./notice-controller')
@@ -40,8 +40,8 @@ module.exports = class MetamaskController extends EventEmitter {
this.store = new ObservableStore(initState)
// network store
- this.networkStore = new ObservableStore({ network: 'loading' })
+ this.networkController = new NetworkController(initState.NetworkController)
// config manager
this.configManager = new ConfigManager({
store: this.store,
@@ -61,8 +61,6 @@ module.exports = class MetamaskController extends EventEmitter {
// rpc provider
this.provider = this.initializeProvider()
- this.provider.on('block', this.logBlock.bind(this))
- this.provider.on('error', this.verifyNetwork.bind(this))
// eth data query tools
this.ethQuery = new EthQuery(this.provider)
@@ -75,7 +73,7 @@ module.exports = class MetamaskController extends EventEmitter {
this.keyringController = new KeyringController({
initState: initState.KeyringController,
ethStore: this.ethStore,
- getNetwork: this.getNetworkState.bind(this),
+ getNetwork: this.networkController.getNetworkState.bind(this.networkController),
})
this.keyringController.on('newAccount', (address) => {
this.preferencesController.setSelectedAddress(address)
@@ -92,13 +90,14 @@ module.exports = class MetamaskController extends EventEmitter {
// tx mgmt
this.txController = new TransactionController({
initState: initState.TransactionController || initState.TransactionManager,
- networkStore: this.networkStore,
+ networkStore: this.networkController.networkStore,
preferencesStore: this.preferencesController.store,
txHistoryLimit: 40,
- getNetwork: this.getNetworkState.bind(this),
+ getNetwork: this.networkController.getNetworkState.bind(this),
signTransaction: this.keyringController.signTransaction.bind(this.keyringController),
provider: this.provider,
blockTracker: this.provider,
+ ethQuery: this.ethQuery,
})
// notices
@@ -113,7 +112,7 @@ module.exports = class MetamaskController extends EventEmitter {
initState: initState.ShapeShiftController,
})
- this.lookupNetwork()
+ this.networkController.lookupNetwork()
this.messageManager = new MessageManager()
this.personalMessageManager = new PersonalMessageManager()
this.publicConfigStore = this.initPublicConfigStore()
@@ -140,9 +139,12 @@ module.exports = class MetamaskController extends EventEmitter {
this.shapeshiftController.store.subscribe((state) => {
this.store.updateState({ ShapeShiftController: state })
})
+ this.networkController.store.subscribe((state) => {
+ this.store.updateState({ NetworkController: state })
+ })
// manual mem state subscriptions
- this.networkStore.subscribe(this.sendUpdate.bind(this))
+ this.networkController.store.subscribe(this.sendUpdate.bind(this))
this.ethStore.subscribe(this.sendUpdate.bind(this))
this.txController.memStore.subscribe(this.sendUpdate.bind(this))
this.messageManager.memStore.subscribe(this.sendUpdate.bind(this))
@@ -160,12 +162,12 @@ module.exports = class MetamaskController extends EventEmitter {
//
initializeProvider () {
- const provider = MetaMaskProvider({
+ return this.networkController.initializeProvider({
static: {
eth_syncing: false,
web3_clientVersion: `MetaMask/v${version}`,
},
- rpcUrl: this.configManager.getCurrentRpcAddress(),
+ rpcUrl: this.networkController.getCurrentRpcAddress(),
// account mgmt
getAccounts: (cb) => {
const isUnlocked = this.keyringController.memStore.getState().isUnlocked
@@ -185,7 +187,6 @@ module.exports = class MetamaskController extends EventEmitter {
// new style msg signing
processPersonalMessage: this.newUnsignedPersonalMessage.bind(this),
})
- return provider
}
initPublicConfigStore () {
@@ -221,7 +222,7 @@ module.exports = class MetamaskController extends EventEmitter {
{
isInitialized,
},
- this.networkStore.getState(),
+ this.networkController.store.getState(),
this.ethStore.getState(),
this.txController.memStore.getState(),
this.messageManager.memStore.getState(),
@@ -255,8 +256,7 @@ module.exports = class MetamaskController extends EventEmitter {
return {
// etc
getState: (cb) => cb(null, this.getState()),
- setProviderType: this.setProviderType.bind(this),
- useEtherscanProvider: this.useEtherscanProvider.bind(this),
+ setProviderType: this.networkController.setProviderType.bind(this.networkController),
setCurrentCurrency: this.setCurrentCurrency.bind(this),
markAccountsFound: this.markAccountsFound.bind(this),
// coinbase
@@ -590,10 +590,6 @@ module.exports = class MetamaskController extends EventEmitter {
//
// Log blocks
- logBlock (block) {
- log.info(`BLOCK CHANGED: #${block.number.toString('hex')} 0x${block.hash.toString('hex')}`)
- this.verifyNetwork()
- }
setCurrentCurrency (currencyCode, cb) {
try {
@@ -612,7 +608,7 @@ module.exports = class MetamaskController extends EventEmitter {
buyEth (address, amount) {
if (!amount) amount = '5'
- const network = this.getNetworkState()
+ const network = this.networkController.getNetworkState()
const url = getBuyEthUrl({ network, address, amount })
if (url) this.platform.openWindow({ url })
}
@@ -620,69 +616,21 @@ module.exports = class MetamaskController extends EventEmitter {
createShapeShiftTx (depositAddress, depositType) {
this.shapeshiftController.createShapeShiftTx(depositAddress, depositType)
}
-
- //
- // network
- //
-
- verifyNetwork () {
- // Check network when restoring connectivity:
- if (this.isNetworkLoading()) this.lookupNetwork()
- }
+// network
setDefaultRpc () {
- this.configManager.setRpcTarget('http://localhost:8545')
- this.platform.reload()
- this.lookupNetwork()
+ this.networkController.setRpcTarget('http://localhost:8545')
return Promise.resolve('http://localhost:8545')
}
setCustomRpc (rpcTarget, rpcList) {
- this.configManager.setRpcTarget(rpcTarget)
- return this.preferencesController.updateFrequentRpcList(rpcTarget)
- .then(() => {
- this.platform.reload()
- this.lookupNetwork()
- return Promise.resolve(rpcTarget)
- })
- }
+ this.networkController.setRpcTarget(rpcTarget)
- setProviderType (type) {
- this.configManager.setProviderType(type)
- this.platform.reload()
- this.lookupNetwork()
- }
-
- useEtherscanProvider () {
- this.configManager.useEtherscanProvider()
- this.platform.reload()
- }
-
- getNetworkState () {
- return this.networkStore.getState().network
- }
-
- setNetworkState (network) {
- return this.networkStore.updateState({ network })
- }
-
- isNetworkLoading () {
- return this.getNetworkState() === 'loading'
- }
-
- lookupNetwork (err) {
- if (err) {
- this.setNetworkState('loading')
- }
-
- this.ethQuery.sendAsync({ method: 'net_version' }, (err, network) => {
- if (err) {
- this.setNetworkState('loading')
- return
- }
- log.info('web3.getNetwork returned ' + network)
- this.setNetworkState(network)
+ return this.preferencesController.updateFrequentRpcList(rpcTarget)
+ .then(() => {
+ return Promise.resolve(rpcTarget)
})
}
+
}
diff --git a/app/scripts/migrations/014.js b/app/scripts/migrations/014.js
new file mode 100644
index 000000000..0fe92125b
--- /dev/null
+++ b/app/scripts/migrations/014.js
@@ -0,0 +1,34 @@
+const version = 14
+
+/*
+
+This migration removes provider from config and moves it too NetworkController.
+
+*/
+
+const clone = require('clone')
+
+module.exports = {
+ version,
+
+ migrate: function (originalVersionedData) {
+ const versionedData = clone(originalVersionedData)
+ versionedData.meta.version = version
+ try {
+ const state = versionedData.data
+ const newState = transformState(state)
+ versionedData.data = newState
+ } catch (err) {
+ console.warn(`MetaMask Migration #${version}` + err.stack)
+ }
+ return Promise.resolve(versionedData)
+ },
+}
+
+function transformState (state) {
+ const newState = state
+ newState.NetworkController = {}
+ newState.NetworkController.provider = newState.config.provider
+ delete newState.config.provider
+ return newState
+}
diff --git a/app/scripts/migrations/index.js b/app/scripts/migrations/index.js
index 3a95cf88e..fb1ad7863 100644
--- a/app/scripts/migrations/index.js
+++ b/app/scripts/migrations/index.js
@@ -24,4 +24,5 @@ module.exports = [
require('./011'),
require('./012'),
require('./013'),
+ require('./014'),
]
diff --git a/package.json b/package.json
index 14ddd2886..6b6996d9d 100644
--- a/package.json
+++ b/package.json
@@ -69,7 +69,7 @@
"ethereumjs-tx": "^1.3.0",
"ethereumjs-util": "ethereumjs/ethereumjs-util#ac5d0908536b447083ea422b435da27f26615de9",
"ethereumjs-wallet": "^0.6.0",
- "ethjs-ens": "^1.0.2",
+ "ethjs-ens": "^2.0.0",
"express": "^4.14.0",
"extension-link-enabler": "^1.0.0",
"extensionizer": "^1.0.0",
@@ -87,6 +87,7 @@
"mississippi": "^1.2.0",
"mkdirp": "^0.5.1",
"multiplex": "^6.7.0",
+ "number-to-bn": "^1.7.0",
"obs-store": "^2.3.1",
"once": "^1.3.3",
"ping-pong-stream": "^1.0.0",
@@ -121,7 +122,7 @@
"valid-url": "^1.0.9",
"vreme": "^3.0.2",
"web3": "0.18.2",
- "web3-provider-engine": "^12.0.6",
+ "web3-provider-engine": "^12.1.0",
"web3-stream-provider": "^2.0.6",
"xtend": "^4.0.1"
},
diff --git a/test/unit/network-contoller-test.js b/test/unit/network-contoller-test.js
new file mode 100644
index 000000000..183e69cab
--- /dev/null
+++ b/test/unit/network-contoller-test.js
@@ -0,0 +1,74 @@
+const EventEmitter = require('events')
+const assert = require('assert')
+const NetworkController = require('../../app/scripts/controllers/network')
+
+describe('# Network Controller', function () {
+ let networkController
+
+ beforeEach(function () {
+ networkController = new NetworkController({
+ provider: {
+ type: 'rinkeby',
+ },
+ })
+ // stub out provider
+ networkController._provider = new EventEmitter()
+ networkController.providerInit = {
+ getAccounts: () => {},
+ }
+
+ networkController.ethQuery = new Proxy({}, {
+ get: (obj, name) => {
+ return () => {}
+ },
+ })
+ })
+ describe('network', function () {
+ describe('#provider', function() {
+ it('provider should be updatable without reassignment', function () {
+ networkController.initializeProvider(networkController.providerInit)
+ const provider = networkController.provider
+ networkController._provider = {test: true}
+ assert.ok(provider.test)
+ })
+ })
+ describe('#getNetworkState', function () {
+ it('should return loading when new', function () {
+ let networkState = networkController.getNetworkState()
+ assert.equal(networkState, 'loading', 'network is loading')
+ })
+ })
+
+ describe('#setNetworkState', function () {
+ it('should update the network', function () {
+ networkController.setNetworkState(1)
+ let networkState = networkController.getNetworkState()
+ assert.equal(networkState, 1, 'network is 1')
+ })
+ })
+
+ describe('#getRpcAddressForType', function () {
+ it('should return the right rpc address', function () {
+ let rpcTarget = networkController.getRpcAddressForType('mainnet')
+ assert.equal(rpcTarget, 'https://mainnet.infura.io/metamask', 'returns the right rpcAddress')
+ })
+ })
+ describe('#setProviderType', function () {
+ it('should update provider.type', function () {
+ networkController.setProviderType('mainnet')
+ const type = networkController.getProviderConfig().type
+ assert.equal(type, 'mainnet', 'provider type is updated')
+ })
+ it('should set the network to loading', function () {
+ networkController.setProviderType('mainnet')
+ const loading = networkController.isNetworkLoading()
+ assert.ok(loading, 'network is loading')
+ })
+ it('should set the right rpcTarget', function () {
+ networkController.setProviderType('mainnet')
+ const rpcTarget = networkController.getProviderConfig().rpcTarget
+ assert.equal(rpcTarget, 'https://mainnet.infura.io/metamask', 'returns the right rpcAddress')
+ })
+ })
+ })
+})
diff --git a/test/unit/tx-controller-test.js b/test/unit/tx-controller-test.js
index d4e8d79f0..711e1ea79 100644
--- a/test/unit/tx-controller-test.js
+++ b/test/unit/tx-controller-test.js
@@ -2,6 +2,7 @@ const assert = require('assert')
const EventEmitter = require('events')
const ethUtil = require('ethereumjs-util')
const EthTx = require('ethereumjs-tx')
+const EthQuery = require('eth-query')
const ObservableStore = require('obs-store')
const clone = require('clone')
const sinon = require('sinon')
@@ -16,9 +17,10 @@ describe('Transaction Controller', function () {
beforeEach(function () {
txController = new TransactionController({
- networkStore: new ObservableStore({ network: currentNetworkId }),
+ networkStore: new ObservableStore(currentNetworkId),
txHistoryLimit: 10,
blockTracker: new EventEmitter(),
+ ethQuery: new EthQuery(new EventEmitter()),
signTransaction: (ethTx) => new Promise((resolve) => {
ethTx.sign(privKey)
resolve()
diff --git a/test/unit/tx-utils-test.js b/test/unit/tx-utils-test.js
index 57d4638a0..7ace1f587 100644
--- a/test/unit/tx-utils-test.js
+++ b/test/unit/tx-utils-test.js
@@ -9,7 +9,11 @@ describe('txUtils', function () {
let txUtils
before(function () {
- txUtils = new TxUtils()
+ txUtils = new TxUtils(new Proxy({}, {
+ get: (obj, name) => {
+ return () => {}
+ },
+ }))
})
describe('chain Id', function () {
diff --git a/ui/app/components/transaction-list-item-icon.js b/ui/app/components/transaction-list-item-icon.js
index d63cae259..431054340 100644
--- a/ui/app/components/transaction-list-item-icon.js
+++ b/ui/app/components/transaction-list-item-icon.js
@@ -1,6 +1,7 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
+const Tooltip = require('./tooltip')
const Identicon = require('./identicon')
@@ -32,11 +33,16 @@ TransactionIcon.prototype.render = function () {
})
case 'submitted':
- return h('i.fa.fa-ellipsis-h', {
- style: {
- fontSize: '27px',
- },
- })
+ return h(Tooltip, {
+ title: 'Pending',
+ position: 'bottom',
+ }, [
+ h('i.fa.fa-ellipsis-h', {
+ style: {
+ fontSize: '27px',
+ },
+ }),
+ ])
}
if (isMsg) {
diff --git a/ui/app/components/transaction-list-item.js b/ui/app/components/transaction-list-item.js
index c2a585003..dbda66a31 100644
--- a/ui/app/components/transaction-list-item.js
+++ b/ui/app/components/transaction-list-item.js
@@ -8,6 +8,7 @@ const explorerLink = require('../../lib/explorer-link')
const CopyButton = require('./copyButton')
const vreme = new (require('vreme'))
const Tooltip = require('./tooltip')
+const numberToBN = require('number-to-bn')
const TransactionIcon = require('./transaction-list-item-icon')
const ShiftListItem = require('./shift-list-item')
@@ -39,6 +40,8 @@ TransactionListItem.prototype.render = function () {
txParams = transaction.msgParams
}
+ const nonce = txParams.nonce ? numberToBN(txParams.nonce).toString(10) : ''
+
const isClickable = ('hash' in transaction && isLinkable) || isPending
return (
h(`.transaction-list-item.flex-row.flex-space-between${isClickable ? '.pointer' : ''}`, {
@@ -69,6 +72,22 @@ TransactionListItem.prototype.render = function () {
]),
]),
+ h(Tooltip, {
+ title: 'Transaction Number',
+ position: 'bottom',
+ }, [
+ 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),