1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-12-01 00:28:06 +01:00
metamask-extension/app/scripts/controllers/network/network.js

297 lines
8.2 KiB
JavaScript
Raw Normal View History

import assert from 'assert'
import EventEmitter from 'events'
2020-12-16 22:14:49 +01:00
import { ComposedStore, ObservableStore } from '@metamask/obs-store'
2020-12-02 20:41:24 +01:00
import { JsonRpcEngine } from 'json-rpc-engine'
import providerFromEngine from 'eth-json-rpc-middleware/providerFromEngine'
import log from 'loglevel'
2020-11-03 00:41:28 +01:00
import {
createSwappableProxy,
createEventEmitterProxy,
} from 'swappable-obj-proxy'
import EthQuery from 'eth-query'
import {
RINKEBY,
MAINNET,
INFURA_PROVIDER_TYPES,
NETWORK_TYPE_RPC,
NETWORK_TYPE_TO_ID_MAP,
MAINNET_CHAIN_ID,
RINKEBY_CHAIN_ID,
} from '../../../../shared/constants/network'
import {
isPrefixedFormattedHexString,
isSafeChainId,
} from '../../../../shared/modules/utils'
import createMetamaskMiddleware from './createMetamaskMiddleware'
import createInfuraClient from './createInfuraClient'
import createJsonRpcClient from './createJsonRpcClient'
const env = process.env.METAMASK_ENV
let defaultProviderConfigOpts
if (process.env.IN_TEST === 'true') {
defaultProviderConfigOpts = {
type: NETWORK_TYPE_RPC,
rpcUrl: 'http://localhost:8545',
chainId: '0x539',
nickname: 'Localhost 8545',
}
} else if (process.env.METAMASK_DEBUG || env === 'test') {
defaultProviderConfigOpts = { type: RINKEBY, chainId: RINKEBY_CHAIN_ID }
} else {
defaultProviderConfigOpts = { type: MAINNET, chainId: MAINNET_CHAIN_ID }
}
const defaultProviderConfig = {
ticker: 'ETH',
...defaultProviderConfigOpts,
}
export default class NetworkController extends EventEmitter {
2020-11-03 00:41:28 +01:00
constructor(opts = {}) {
super()
// create stores
this.providerStore = new ObservableStore(
opts.provider || { ...defaultProviderConfig },
)
this.previousProviderStore = new ObservableStore(
this.providerStore.getState(),
)
2017-09-27 23:10:58 +02:00
this.networkStore = new ObservableStore('loading')
this.store = new ComposedStore({
provider: this.providerStore,
previousProviderStore: this.previousProviderStore,
network: this.networkStore,
})
// 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
this.on('networkDidChange', this.lookupNetwork)
2017-05-23 07:56:10 +02:00
}
/**
* Sets the Infura project ID
*
* @param {string} projectId - The Infura project ID
* @throws {Error} if the project ID is not a valid string
* @return {void}
*/
2020-11-03 00:41:28 +01:00
setInfuraProjectId(projectId) {
if (!projectId || typeof projectId !== 'string') {
throw new Error('Invalid Infura project ID')
}
this._infuraProjectId = projectId
}
2020-11-03 00:41:28 +01:00
initializeProvider(providerParams) {
this._baseProviderParams = providerParams
const { type, rpcUrl, chainId } = this.getProviderConfig()
this._configureProvider({ type, rpcUrl, chainId })
this.lookupNetwork()
}
// return the proxies so the references will always be good
2020-11-03 00:41:28 +01:00
getProviderAndBlockTracker() {
const provider = this._providerProxy
const blockTracker = this._blockTrackerProxy
return { provider, blockTracker }
}
2017-05-23 07:56:10 +02:00
2020-11-03 00:41:28 +01:00
verifyNetwork() {
2017-09-30 01:09:38 +02:00
// Check network when restoring connectivity:
if (this.isNetworkLoading()) {
this.lookupNetwork()
}
}
2020-11-03 00:41:28 +01:00
getNetworkState() {
2017-05-23 08:12:28 +02:00
return this.networkStore.getState()
}
2020-11-03 00:41:28 +01:00
setNetworkState(network) {
this.networkStore.putState(network)
}
2020-11-03 00:41:28 +01:00
isNetworkLoading() {
return this.getNetworkState() === 'loading'
}
2020-11-03 00:41:28 +01:00
lookupNetwork() {
// Prevent firing when provider is not defined.
if (!this._provider) {
2020-11-03 00:41:28 +01:00
log.warn(
'NetworkController - lookupNetwork aborted due to missing provider',
)
return
}
const chainId = this.getCurrentChainId()
if (!chainId) {
2020-11-03 00:41:28 +01:00
log.warn(
'NetworkController - lookupNetwork aborted due to missing chainId',
)
this.setNetworkState('loading')
return
}
// Ping the RPC endpoint so we can confirm that it works
const ethQuery = new EthQuery(this._provider)
const initialNetwork = this.getNetworkState()
ethQuery.sendAsync({ method: 'net_version' }, (err, networkVersion) => {
const currentNetwork = this.getNetworkState()
if (initialNetwork === currentNetwork) {
if (err) {
this.setNetworkState('loading')
return
}
this.setNetworkState(networkVersion)
}
})
}
2020-11-03 00:41:28 +01:00
getCurrentChainId() {
const { type, chainId: configChainId } = this.getProviderConfig()
return NETWORK_TYPE_TO_ID_MAP[type]?.chainId || configChainId
}
2020-11-03 00:41:28 +01:00
setRpcTarget(rpcUrl, chainId, ticker = 'ETH', nickname = '', rpcPrefs) {
assert.ok(
isPrefixedFormattedHexString(chainId),
`Invalid chain ID "${chainId}": invalid hex string.`,
)
assert.ok(
isSafeChainId(parseInt(chainId, 16)),
`Invalid chain ID "${chainId}": numerical value greater than max safe value.`,
)
this.setProviderConfig({
type: NETWORK_TYPE_RPC,
rpcUrl,
chainId,
ticker,
nickname,
New settings custom rpc form (#6490) * Add networks tab to settings, with header. * Adds network list to settings network tab. * Adds form to settings networks tab and connects it to network list. * Network tab: form adding and editing working * Settings network form properly handles input errors * Add translations for settings network form * Clean up styles of settings network tab. * Add popup-view styles and behaviour to settings network tab. * Fix save button on settings network form * Adds 'Add Network' button and addMode to settings networks tab * Lint fix for settings networks tab addition * Fix navigation in settings networks tab. * Editing an rpcurl in networks tab does not create new network, just changes rpc of old * Fix layout of settings tabs other than network * Networks dropdown 'Custom Rpc' item links to networks tab in settings. * Update settings sidebar networks subheader. * Make networks tab buttons width consistent with input widths in extension view. * Fix settings screen subheader height in popup view * Fix height of add networks button in popup view * Add optional label to chainId and symbol form labels in networks setting tab * Style fixes for networks tab headers * Add ability to customize block explorer used by custom rpc * Stylistic improvements+fixes to custom rpc form. * Hide cancel button. * Highlight and show network form of provider by default. * Standardize network subheader name to 'Networks' * Update e2e tests for new settings network form * Update unit tests for new rpcPrefs prop * Extract blockexplorer url construction into method. * Fix broken styles on non-network tabs in popup mode * Fix block explorer url links for cases when provider in state has not been updated. * Fix vertical spacing of network form * Don't allow click of save button on network form if nothing has changed * Ensure add network button is shown in popup view * Lint fix for networks tab * Fix block explorer url preference setting. * Fix e2e tests for custom blockexplorer in account details modal changes. * Update integration test states to include frequentRpcList property * Fix some capitalizations in en/messages.json * Remove some console.logs added during custom rpc form work * Fix external account link text and url for modal and dropdown. * Documentation, url validation, proptype required additions and lint fixes on network tab and form.
2019-05-09 19:27:14 +02:00
rpcPrefs,
})
}
2020-11-03 00:41:28 +01:00
async setProviderType(type, rpcUrl = '', ticker = 'ETH', nickname = '') {
assert.notStrictEqual(
2020-11-03 00:41:28 +01:00
type,
NETWORK_TYPE_RPC,
`NetworkController - cannot call "setProviderType" with type "${NETWORK_TYPE_RPC}". Use "setRpcTarget"`,
2020-11-03 00:41:28 +01:00
)
assert.ok(
2020-11-03 00:41:28 +01:00
INFURA_PROVIDER_TYPES.includes(type),
`Unknown Infura provider type "${type}".`,
2020-11-03 00:41:28 +01:00
)
const { chainId } = NETWORK_TYPE_TO_ID_MAP[type]
this.setProviderConfig({ type, rpcUrl, chainId, ticker, nickname })
}
2020-11-03 00:41:28 +01:00
resetConnection() {
this.setProviderConfig(this.getProviderConfig())
}
/**
* Sets the provider config and switches the network.
*/
2020-11-03 00:41:28 +01:00
setProviderConfig(config) {
this.previousProviderStore.updateState(this.getProviderConfig())
this.providerStore.updateState(config)
this._switchNetwork(config)
}
rollbackToPreviousProvider() {
const config = this.previousProviderStore.getState()
this.providerStore.updateState(config)
this._switchNetwork(config)
}
2020-11-03 00:41:28 +01:00
getProviderConfig() {
2017-05-23 08:12:28 +02:00
return this.providerStore.getState()
}
2020-12-02 22:41:30 +01:00
getNetworkIdentifier() {
const provider = this.providerStore.getState()
return provider.type === NETWORK_TYPE_RPC ? provider.rpcUrl : provider.type
2020-12-02 22:41:30 +01:00
}
2017-09-30 01:09:38 +02:00
//
// Private
//
2020-11-03 00:41:28 +01:00
_switchNetwork(opts) {
2017-09-30 01:09:38 +02:00
this.setNetworkState('loading')
this._configureProvider(opts)
this.emit('networkDidChange', opts.type)
2017-09-30 01:09:38 +02:00
}
2020-11-03 00:41:28 +01:00
_configureProvider({ type, rpcUrl, chainId }) {
// infura type-based endpoints
const isInfura = INFURA_PROVIDER_TYPES.includes(type)
if (isInfura) {
this._configureInfuraProvider(type, this._infuraProjectId)
2020-11-03 00:41:28 +01:00
// url-based rpc endpoints
} else if (type === NETWORK_TYPE_RPC) {
this._configureStandardProvider(rpcUrl, chainId)
} else {
2020-11-03 00:41:28 +01:00
throw new Error(
`NetworkController - _configureProvider - unknown type "${type}"`,
)
}
}
2020-11-03 00:41:28 +01:00
_configureInfuraProvider(type, projectId) {
log.info('NetworkController - configureInfuraProvider', type)
const networkClient = createInfuraClient({
network: type,
projectId,
})
this._setNetworkClient(networkClient)
}
2020-11-03 00:41:28 +01:00
_configureStandardProvider(rpcUrl, chainId) {
log.info('NetworkController - configureStandardProvider', rpcUrl)
const networkClient = createJsonRpcClient({ rpcUrl, chainId })
this._setNetworkClient(networkClient)
}
2020-11-03 00:41:28 +01:00
_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 })
}
2020-11-03 00:41:28 +01:00
_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 {
2020-11-03 00:41:28 +01:00
this._blockTrackerProxy = createEventEmitterProxy(blockTracker, {
eventFilter: 'skipInternal',
})
}
// set new provider and blockTracker
this._provider = provider
this._blockTracker = blockTracker
}
}