1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-10-23 03:36:18 +02:00
metamask-extension/app/scripts/controllers/network/network.js

252 lines
7.9 KiB
JavaScript
Raw Normal View History

2017-09-30 01:09:38 +02:00
const assert = require('assert')
const EventEmitter = require('events')
const ObservableStore = require('obs-store')
2017-05-23 08:12:28 +02:00
const ComposedStore = require('obs-store/lib/composed')
const EthQuery = require('eth-query')
2018-05-17 06:08:19 +02:00
const JsonRpcEngine = require('json-rpc-engine')
const providerFromEngine = require('eth-json-rpc-middleware/providerFromEngine')
const log = require('loglevel')
const createMetamaskMiddleware = require('./createMetamaskMiddleware')
const createInfuraClient = require('./createInfuraClient')
const createJsonRpcClient = require('./createJsonRpcClient')
const createLocalhostClient = require('./createLocalhostClient')
const { createSwappableProxy, createEventEmitterProxy } = require('swappable-obj-proxy')
const extend = require('extend')
const networks = { networkList: {} }
2018-04-12 23:17:36 +02:00
const {
ROPSTEN,
RINKEBY,
KOVAN,
MAINNET,
LOCALHOST,
2018-04-12 23:17:36 +02:00
} = require('./enums')
const INFURA_PROVIDER_TYPES = [ROPSTEN, RINKEBY, KOVAN, MAINNET]
const env = process.env.METAMASK_ENV
const METAMASK_DEBUG = process.env.METAMASK_DEBUG
const testMode = (METAMASK_DEBUG || env === 'test')
const defaultProviderConfig = {
type: testMode ? RINKEBY : MAINNET,
}
const defaultNetworkConfig = {
ticker: 'ETH',
}
module.exports = class NetworkController extends EventEmitter {
constructor (opts = {}) {
super()
// parse options
const providerConfig = opts.provider || defaultProviderConfig
// create stores
this.providerStore = new ObservableStore(providerConfig)
2017-09-27 23:10:58 +02:00
this.networkStore = new ObservableStore('loading')
this.networkConfig = new ObservableStore(defaultNetworkConfig)
this.store = new ComposedStore({ provider: this.providerStore, network: this.networkStore, settings: this.networkConfig })
2017-05-23 08:12:28 +02:00
this.on('networkDidChange', this.lookupNetwork)
// provider and block tracker
this._provider = null
this._blockTracker = null
// provider and block tracker proxies - because the network changes
this._providerProxy = null
this._blockTrackerProxy = null
2017-05-23 07:56:10 +02:00
}
initializeProvider (providerParams) {
this._baseProviderParams = providerParams
const { type, rpcTarget, chainId, ticker, nickname } = this.providerStore.getState()
this._configureProvider({ type, rpcTarget, chainId, ticker, nickname })
this.lookupNetwork()
}
// return the proxies so the references will always be good
2018-05-17 06:08:19 +02:00
getProviderAndBlockTracker () {
const provider = this._providerProxy
const blockTracker = this._blockTrackerProxy
return { provider, blockTracker }
}
2017-05-23 07:56:10 +02:00
verifyNetwork () {
2017-09-30 01:09:38 +02:00
// Check network when restoring connectivity:
if (this.isNetworkLoading()) this.lookupNetwork()
}
getNetworkState () {
2017-05-23 08:12:28 +02:00
return this.networkStore.getState()
}
getNetworkConfig () {
return this.networkConfig.getState()
}
setNetworkState (network, type) {
if (network === 'loading') {
return this.networkStore.putState(network)
}
// type must be defined
if (!type) {
return
}
network = networks.networkList[type] && networks.networkList[type].chainId ? networks.networkList[type].chainId : network
2017-05-23 08:12:28 +02:00
return this.networkStore.putState(network)
}
isNetworkLoading () {
return this.getNetworkState() === 'loading'
}
2017-05-23 08:12:28 +02:00
lookupNetwork () {
// Prevent firing when provider is not defined.
if (!this._provider) {
return log.warn('NetworkController - lookupNetwork aborted due to missing provider')
}
var { type } = this.providerStore.getState()
const ethQuery = new EthQuery(this._provider)
2018-10-21 10:32:07 +02:00
// first attempt to perform lookup via eth_chainId
ethQuery.sendAsync({ method: 'eth_chainId' }, (err, chainIdHex) => {
if (err) {
// if eth_chainId is not supported, fallback to net_verion
ethQuery.sendAsync({ method: 'net_version' }, (err, network) => {
if (err) return this.setNetworkState('loading')
log.info(`net_version returned ${network}`)
this.setNetworkState(network, type)
})
return
}
const chainId = Number.parseInt(chainIdHex, 16)
log.info(`net_version returned ${chainId}`)
this.setNetworkState(chainId, type)
})
}
setRpcTarget (rpcTarget, chainId, ticker = 'ETH', nickname = '') {
const providerConfig = {
2017-05-23 08:12:28 +02:00
type: 'rpc',
rpcTarget,
chainId,
ticker,
nickname,
}
this.providerConfig = providerConfig
}
async setProviderType (type) {
assert.notEqual(type, 'rpc', `NetworkController - cannot call "setProviderType" with type 'rpc'. use "setRpcTarget"`)
assert(INFURA_PROVIDER_TYPES.includes(type) || type === LOCALHOST, `NetworkController - Unknown rpc type "${type}"`)
const providerConfig = { type }
this.providerConfig = providerConfig
}
resetConnection () {
this.providerConfig = this.getProviderConfig()
}
set providerConfig (providerConfig) {
this.providerStore.updateState(providerConfig)
this._switchNetwork(providerConfig)
}
2017-05-23 08:12:28 +02:00
getProviderConfig () {
return this.providerStore.getState()
}
2017-09-30 01:09:38 +02:00
//
// Private
//
_switchNetwork (opts) {
2017-09-30 01:09:38 +02:00
this.setNetworkState('loading')
this._configureProvider(opts)
2017-09-30 01:09:38 +02:00
this.emit('networkDidChange')
}
_configureProvider (opts) {
const { type, rpcTarget, chainId, ticker, nickname } = opts
// infura type-based endpoints
const isInfura = INFURA_PROVIDER_TYPES.includes(type)
if (isInfura) {
this._configureInfuraProvider(opts)
// other type-based rpc endpoints
} else if (type === LOCALHOST) {
this._configureLocalhostProvider()
// url-based rpc endpoints
2018-05-17 06:08:19 +02:00
} else if (type === 'rpc') {
this._configureStandardProvider({ rpcUrl: rpcTarget, chainId, ticker, nickname })
} else {
throw new Error(`NetworkController - _configureProvider - unknown type "${type}"`)
}
}
_configureInfuraProvider ({ type }) {
log.info('NetworkController - configureInfuraProvider', type)
const networkClient = createInfuraClient({ network: type })
this._setNetworkClient(networkClient)
// setup networkConfig
var settings = {
ticker: 'ETH',
}
this.networkConfig.putState(settings)
}
_configureLocalhostProvider () {
log.info('NetworkController - configureLocalhostProvider')
const networkClient = createLocalhostClient()
this._setNetworkClient(networkClient)
}
_configureStandardProvider ({ rpcUrl, chainId, ticker, nickname }) {
log.info('NetworkController - configureStandardProvider', rpcUrl)
const networkClient = createJsonRpcClient({ rpcUrl })
// hack to add a 'rpc' network with chainId
networks.networkList['rpc'] = {
chainId: chainId,
rpcUrl,
ticker: ticker || 'ETH',
nickname,
}
// setup networkConfig
var settings = {
network: chainId,
}
settings = extend(settings, networks.networkList['rpc'])
this.networkConfig.putState(settings)
this._setNetworkClient(networkClient)
}
_setNetworkClient ({ networkMiddleware, blockTracker }) {
const metamaskMiddleware = createMetamaskMiddleware(this._baseProviderParams)
const engine = new JsonRpcEngine()
engine.push(metamaskMiddleware)
engine.push(networkMiddleware)
const provider = providerFromEngine(engine)
this._setProviderAndBlockTracker({ provider, blockTracker })
}
_setProviderAndBlockTracker ({ provider, blockTracker }) {
// update or intialize proxies
if (this._providerProxy) {
this._providerProxy.setTarget(provider)
} else {
this._providerProxy = createSwappableProxy(provider)
}
if (this._blockTrackerProxy) {
this._blockTrackerProxy.setTarget(blockTracker)
} else {
this._blockTrackerProxy = createEventEmitterProxy(blockTracker, { eventFilter: 'skipInternal' })
}
// set new provider and blockTracker
this._provider = provider
this._blockTracker = blockTracker
}
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()
}
}