2017-09-30 01:09:38 +02:00
|
|
|
const assert = require('assert')
|
2017-05-18 23:54:02 +02:00
|
|
|
const EventEmitter = require('events')
|
2017-10-19 00:09:32 +02:00
|
|
|
const createMetamaskProvider = require('web3-provider-engine/zero.js')
|
2017-12-14 03:57:27 +01:00
|
|
|
const createInfuraProvider = require('eth-json-rpc-infura/src/createProvider')
|
2017-05-18 23:54:02 +02:00
|
|
|
const ObservableStore = require('obs-store')
|
2017-05-23 08:12:28 +02:00
|
|
|
const ComposedStore = require('obs-store/lib/composed')
|
2017-05-18 23:54:02 +02:00
|
|
|
const extend = require('xtend')
|
|
|
|
const EthQuery = require('eth-query')
|
2017-09-26 20:52:57 +02:00
|
|
|
const createEventEmitterProxy = require('../lib/events-proxy.js')
|
2017-05-23 07:56:10 +02:00
|
|
|
const RPC_ADDRESS_LIST = require('../config.js').network
|
2017-05-23 08:12:28 +02:00
|
|
|
const DEFAULT_RPC = RPC_ADDRESS_LIST['rinkeby']
|
2017-12-14 03:57:27 +01:00
|
|
|
const INFURA_PROVIDER_TYPES = ['ropsten', 'rinkeby', 'kovan', 'mainnet']
|
2017-05-18 23:54:02 +02:00
|
|
|
|
|
|
|
module.exports = class NetworkController extends EventEmitter {
|
2017-09-30 02:10:34 +02:00
|
|
|
|
2017-05-23 08:12:28 +02:00
|
|
|
constructor (config) {
|
2017-05-18 23:54:02 +02:00
|
|
|
super()
|
2017-05-23 08:12:28 +02:00
|
|
|
config.provider.rpcTarget = this.getRpcAddressForType(config.provider.type, config.provider)
|
2017-09-27 23:10:58 +02:00
|
|
|
this.networkStore = new ObservableStore('loading')
|
2017-05-23 08:12:28 +02:00
|
|
|
this.providerStore = new ObservableStore(config.provider)
|
|
|
|
this.store = new ComposedStore({ provider: this.providerStore, network: this.networkStore })
|
2017-10-19 00:09:32 +02:00
|
|
|
this._proxy = createEventEmitterProxy()
|
2017-05-23 07:56:10 +02:00
|
|
|
|
2017-05-23 08:12:28 +02:00
|
|
|
this.on('networkDidChange', this.lookupNetwork)
|
2017-05-23 07:56:10 +02:00
|
|
|
}
|
|
|
|
|
2017-09-30 01:09:38 +02:00
|
|
|
initializeProvider (_providerParams) {
|
|
|
|
this._baseProviderParams = _providerParams
|
2017-12-14 03:57:27 +01:00
|
|
|
const { type, rpcTarget } = this.providerStore.getState()
|
|
|
|
// map rpcTarget to rpcUrl
|
|
|
|
const opts = {
|
|
|
|
type,
|
|
|
|
rpcUrl: rpcTarget,
|
|
|
|
}
|
|
|
|
this._configureProvider(opts)
|
2017-10-19 00:09:32 +02:00
|
|
|
this._proxy.on('block', this._logBlock.bind(this))
|
|
|
|
this._proxy.on('error', this.verifyNetwork.bind(this))
|
|
|
|
this.ethQuery = new EthQuery(this._proxy)
|
2017-05-18 23:54:02 +02:00
|
|
|
this.lookupNetwork()
|
2017-10-19 00:09:32 +02:00
|
|
|
return this._proxy
|
2017-05-18 23:54:02 +02:00
|
|
|
}
|
2017-05-23 07:56:10 +02:00
|
|
|
|
2017-05-18 23:54:02 +02:00
|
|
|
verifyNetwork () {
|
2017-09-30 01:09:38 +02:00
|
|
|
// Check network when restoring connectivity:
|
2017-05-18 23:54:02 +02:00
|
|
|
if (this.isNetworkLoading()) this.lookupNetwork()
|
|
|
|
}
|
|
|
|
|
|
|
|
getNetworkState () {
|
2017-05-23 08:12:28 +02:00
|
|
|
return this.networkStore.getState()
|
2017-05-18 23:54:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
setNetworkState (network) {
|
2017-05-23 08:12:28 +02:00
|
|
|
return this.networkStore.putState(network)
|
2017-05-18 23:54:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
isNetworkLoading () {
|
|
|
|
return this.getNetworkState() === 'loading'
|
|
|
|
}
|
|
|
|
|
2017-05-23 08:12:28 +02:00
|
|
|
lookupNetwork () {
|
2017-10-23 07:24:50 +02:00
|
|
|
// Prevent firing when provider is not defined.
|
|
|
|
if (!this.ethQuery || !this.ethQuery.sendAsync) {
|
2017-11-28 00:59:32 +01:00
|
|
|
return log.warn('NetworkController - lookupNetwork aborted due to missing ethQuery')
|
2017-10-23 07:24:50 +02:00
|
|
|
}
|
2017-05-18 23:54:02 +02:00
|
|
|
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({
|
2017-05-23 08:12:28 +02:00
|
|
|
type: 'rpc',
|
|
|
|
rpcTarget: rpcUrl,
|
2017-05-18 23:54:02 +02:00
|
|
|
})
|
2017-09-30 02:10:34 +02:00
|
|
|
this._switchNetwork({ rpcUrl })
|
2017-05-18 23:54:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
getCurrentRpcAddress () {
|
2017-05-23 08:12:28 +02:00
|
|
|
const provider = this.getProviderConfig()
|
2017-05-18 23:54:02 +02:00
|
|
|
if (!provider) return null
|
|
|
|
return this.getRpcAddressForType(provider.type)
|
|
|
|
}
|
|
|
|
|
2017-09-30 01:09:38 +02:00
|
|
|
async setProviderType (type) {
|
|
|
|
assert(type !== 'rpc', `NetworkController.setProviderType - cannot connect by type "rpc"`)
|
|
|
|
// skip if type already matches
|
2017-05-23 08:12:28 +02:00
|
|
|
if (type === this.getProviderConfig().type) return
|
2017-05-18 23:54:02 +02:00
|
|
|
const rpcTarget = this.getRpcAddressForType(type)
|
2017-09-30 01:09:38 +02:00
|
|
|
assert(rpcTarget, `NetworkController - unknown rpc address for type "${type}"`)
|
|
|
|
this.providerStore.updateState({ type, rpcTarget })
|
2017-12-14 03:57:27 +01:00
|
|
|
this._switchNetwork({ type })
|
2017-05-18 23:54:02 +02:00
|
|
|
}
|
|
|
|
|
2017-05-23 08:12:28 +02:00
|
|
|
getProviderConfig () {
|
|
|
|
return this.providerStore.getState()
|
2017-05-18 23:54:02 +02:00
|
|
|
}
|
|
|
|
|
2017-05-23 08:12:28 +02:00
|
|
|
getRpcAddressForType (type, provider = this.getProviderConfig()) {
|
|
|
|
if (RPC_ADDRESS_LIST[type]) return RPC_ADDRESS_LIST[type]
|
|
|
|
return provider && provider.rpcTarget ? provider.rpcTarget : DEFAULT_RPC
|
2017-05-23 07:56:10 +02:00
|
|
|
}
|
2017-05-18 23:54:02 +02:00
|
|
|
|
2017-09-30 01:09:38 +02:00
|
|
|
//
|
|
|
|
// Private
|
|
|
|
//
|
|
|
|
|
2017-12-14 03:57:27 +01:00
|
|
|
_switchNetwork (opts) {
|
2017-09-30 01:09:38 +02:00
|
|
|
this.setNetworkState('loading')
|
2017-12-14 03:57:27 +01:00
|
|
|
this._configureProvider(opts)
|
2017-09-30 01:09:38 +02:00
|
|
|
this.emit('networkDidChange')
|
|
|
|
}
|
|
|
|
|
2017-12-14 03:57:27 +01:00
|
|
|
_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) {
|
2017-12-15 21:43:55 +01:00
|
|
|
log.info('_configureInfuraProvider', opts)
|
2017-12-14 03:57:27 +01:00
|
|
|
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,
|
2017-12-10 01:58:09 +01:00
|
|
|
engineParams: {
|
|
|
|
pollingInterval: 8000,
|
|
|
|
},
|
|
|
|
})
|
2017-10-19 00:09:32 +02:00
|
|
|
const provider = createMetamaskProvider(providerParams)
|
|
|
|
this._setProvider(provider)
|
|
|
|
}
|
|
|
|
|
|
|
|
_setProvider (provider) {
|
|
|
|
// collect old block tracker events
|
|
|
|
const oldProvider = this._provider
|
|
|
|
let blockTrackerHandlers
|
|
|
|
if (oldProvider) {
|
|
|
|
// capture old block handlers
|
|
|
|
blockTrackerHandlers = oldProvider._blockTracker.proxyEventHandlers
|
|
|
|
// tear down
|
|
|
|
oldProvider.removeAllListeners()
|
|
|
|
oldProvider.stop()
|
2017-09-27 23:44:54 +02:00
|
|
|
}
|
2017-10-19 00:09:32 +02:00
|
|
|
// override block tracler
|
|
|
|
provider._blockTracker = createEventEmitterProxy(provider._blockTracker, blockTrackerHandlers)
|
2017-09-27 23:44:54 +02:00
|
|
|
// set as new provider
|
2017-10-19 00:09:32 +02:00
|
|
|
this._provider = provider
|
|
|
|
this._proxy.setTarget(provider)
|
2017-09-27 23:44:54 +02:00
|
|
|
}
|
|
|
|
|
2017-05-23 07:56:10 +02:00
|
|
|
_logBlock (block) {
|
|
|
|
log.info(`BLOCK CHANGED: #${block.number.toString('hex')} 0x${block.hash.toString('hex')}`)
|
|
|
|
this.verifyNetwork()
|
2017-05-18 23:54:02 +02:00
|
|
|
}
|
|
|
|
}
|