Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial multi-chain support (aka. optional chainid fix) #4932

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions app/_locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,9 @@
"newRPC": {
"message": "New RPC URL"
},
"optionalChainId": {
"message": "ChainId (optional)"
},
"next": {
"message": "Next"
},
Expand Down Expand Up @@ -817,6 +820,9 @@
"ropsten": {
"message": "Ropsten Test Network"
},
"classic": {
"message": "Ethereum Classic Network"
},
"rpc": {
"message": "Custom RPC"
},
Expand All @@ -835,6 +841,9 @@
"connectingToRinkeby": {
"message": "Connecting to Rinkeby Test Network"
},
"connectingToClassic": {
"message": "Connecting to Ethereum Classic Network"
},
"connectingToUnknown": {
"message": "Connecting to Unknown Network"
},
Expand Down Expand Up @@ -1138,6 +1147,10 @@
"usedByClients": {
"message": "Used by a variety of different clients"
},
"useMultiChainMenu": {
"message": "Use MultiChain menu",
"description": "show available MultiChains in the dropdown menu"
},
"useOldUI": {
"message": "Use old UI"
},
Expand Down
20 changes: 20 additions & 0 deletions app/images/etc_logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
32 changes: 27 additions & 5 deletions app/scripts/controllers/currency.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class CurrencyController {
*/
constructor (opts = {}) {
const initState = extend({
fromCurrency: 'ETH',
currentCurrency: 'usd',
conversionRate: 0,
conversionDate: 'N/A',
Expand All @@ -36,6 +37,26 @@ class CurrencyController {
// PUBLIC METHODS
//

/**
* A getter for the fromCurrency property
*
* @returns {string} A 2-4 character shorthand that describes the specific currency
*
*/
getFromCurrency () {
return this.store.getState().fromCurrency
}

/**
* A setter for the fromCurrency property
*
* @param {string} fromCurrency The new currency to set as the fromCurrency in the store
*
*/
setFromCurrency (fromCurrency) {
this.store.updateState({ ticker: fromCurrency, fromCurrency })
}

/**
* A getter for the currentCurrency property
*
Expand Down Expand Up @@ -104,15 +125,16 @@ class CurrencyController {
*
*/
async updateConversionRate () {
let currentCurrency
let currentCurrency, fromCurrency
try {
currentCurrency = this.getCurrentCurrency()
const response = await fetch(`https://api.infura.io/v1/ticker/eth${currentCurrency.toLowerCase()}`)
fromCurrency = this.getFromCurrency()
const response = await fetch(`https://min-api.cryptocompare.com/data/pricehistorical?fsym=${fromCurrency.toUpperCase()}&tsyms=${currentCurrency.toUpperCase()}`)
const parsedResponse = await response.json()
this.setConversionRate(Number(parsedResponse.bid))
this.setConversionDate(Number(parsedResponse.timestamp))
this.setConversionRate(Number(parsedResponse[fromCurrency.toUpperCase()][currentCurrency.toUpperCase()]))
this.setConversionDate(parseInt(new Date().getTime() / 1000))
} catch (err) {
log.warn(`MetaMask - Failed to query currency conversion:`, currentCurrency, err)
log.warn(`MetaMask - Failed to query currency conversion:`, fromCurrency, currentCurrency, err)
this.setConversionRate(0)
this.setConversionDate('N/A')
}
Expand Down
6 changes: 6 additions & 0 deletions app/scripts/controllers/network/enums.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,35 @@ const RINKEBY = 'rinkeby'
const KOVAN = 'kovan'
const MAINNET = 'mainnet'
const LOCALHOST = 'localhost'
const CLASSIC = 'classic'

const MAINNET_CODE = 1
const ROPSTEN_CODE = 3
const RINKEYBY_CODE = 4
const KOVAN_CODE = 42
const CLASSIC_CODE = 61

const ROPSTEN_DISPLAY_NAME = 'Ropsten'
const RINKEBY_DISPLAY_NAME = 'Rinkeby'
const KOVAN_DISPLAY_NAME = 'Kovan'
const MAINNET_DISPLAY_NAME = 'Main Ethereum Network'
const CLASSIC_DISPLAY_NAME = 'Ethereum Classic'

module.exports = {
ROPSTEN,
RINKEBY,
KOVAN,
MAINNET,
CLASSIC,
LOCALHOST,
MAINNET_CODE,
ROPSTEN_CODE,
RINKEYBY_CODE,
KOVAN_CODE,
CLASSIC_CODE,
ROPSTEN_DISPLAY_NAME,
RINKEBY_DISPLAY_NAME,
KOVAN_DISPLAY_NAME,
MAINNET_DISPLAY_NAME,
CLASSIC_DISPLAY_NAME,
}
77 changes: 67 additions & 10 deletions app/scripts/controllers/network/network.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,19 @@ const createInfuraClient = require('./createInfuraClient')
const createJsonRpcClient = require('./createJsonRpcClient')
const createLocalhostClient = require('./createLocalhostClient')
const { createSwappableProxy, createEventEmitterProxy } = require('swappable-obj-proxy')
const networks = require('./networks')
const extend = require('xtend')

const {
ROPSTEN,
RINKEBY,
KOVAN,
MAINNET,
CLASSIC,
LOCALHOST,
} = require('./enums')
const INFURA_PROVIDER_TYPES = [ROPSTEN, RINKEBY, KOVAN, MAINNET]
const ALL_PROVIDER_TYPES = [ROPSTEN, RINKEBY, KOVAN, MAINNET, CLASSIC]

const env = process.env.METAMASK_ENV
const METAMASK_DEBUG = process.env.METAMASK_DEBUG
Expand All @@ -29,6 +33,10 @@ const defaultProviderConfig = {
type: testMode ? RINKEBY : MAINNET,
}

const defaultNetworkConfig = {
ticker: 'ETH',
}

module.exports = class NetworkController extends EventEmitter {

constructor (opts = {}) {
Expand All @@ -39,7 +47,8 @@ module.exports = class NetworkController extends EventEmitter {
// create stores
this.providerStore = new ObservableStore(providerConfig)
this.networkStore = new ObservableStore('loading')
this.store = new ComposedStore({ provider: this.providerStore, network: this.networkStore })
this.networkConfig = new ObservableStore(defaultNetworkConfig)
this.store = new ComposedStore({ provider: this.providerStore, network: this.networkStore, settings: this.networkConfig })
this.on('networkDidChange', this.lookupNetwork)
// provider and block tracker
this._provider = null
Expand All @@ -51,8 +60,8 @@ module.exports = class NetworkController extends EventEmitter {

initializeProvider (providerParams) {
this._baseProviderParams = providerParams
const { type, rpcTarget } = this.providerStore.getState()
this._configureProvider({ type, rpcTarget })
const { type, rpcTarget, chainId } = this.providerStore.getState()
this._configureProvider({ type, rpcTarget, chainId })
this.lookupNetwork()
}

Expand All @@ -72,7 +81,20 @@ module.exports = class NetworkController extends EventEmitter {
return this.networkStore.getState()
}

setNetworkState (network) {
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
return this.networkStore.putState(network)
}

Expand All @@ -85,25 +107,27 @@ module.exports = class NetworkController extends EventEmitter {
if (!this._provider) {
return log.warn('NetworkController - lookupNetwork aborted due to missing provider')
}
var { type } = this.providerStore.getState()
const ethQuery = new EthQuery(this._provider)
ethQuery.sendAsync({ method: 'net_version' }, (err, network) => {
if (err) return this.setNetworkState('loading')
log.info('web3.getNetwork returned ' + network)
this.setNetworkState(network)
this.setNetworkState(network, type)
})
}

setRpcTarget (rpcTarget) {
setRpcTarget (rpcTarget, chainId) {
const providerConfig = {
type: 'rpc',
rpcTarget,
chainId,
}
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}"`)
assert(ALL_PROVIDER_TYPES.includes(type) || type === LOCALHOST, `NetworkController - Unknown rpc type "${type}"`)
const providerConfig = { type }
this.providerConfig = providerConfig
}
Expand Down Expand Up @@ -132,17 +156,20 @@ module.exports = class NetworkController extends EventEmitter {
}

_configureProvider (opts) {
const { type, rpcTarget } = opts
const { type, rpcTarget, chainId } = opts
// infura type-based endpoints
const isInfura = INFURA_PROVIDER_TYPES.includes(type)
if (isInfura) {
this._configureInfuraProvider(opts)
// other predefined endpoints
} else if (ALL_PROVIDER_TYPES.includes(type)){
this._configurePredefinedProvider(opts)
// other type-based rpc endpoints
} else if (type === LOCALHOST) {
this._configureLocalhostProvider()
// url-based rpc endpoints
} else if (type === 'rpc') {
this._configureStandardProvider({ rpcUrl: rpcTarget })
this._configureStandardProvider({ rpcUrl: rpcTarget, chainId })
} else {
throw new Error(`NetworkController - _configureProvider - unknown type "${type}"`)
}
Expand All @@ -152,6 +179,11 @@ module.exports = class NetworkController extends EventEmitter {
log.info('NetworkController - configureInfuraProvider', type)
const networkClient = createInfuraClient({ network: type })
this._setNetworkClient(networkClient)
// setup networkConfig
var settings = {
ticker: 'ETH',
}
this.networkConfig.putState(settings)
}

_configureLocalhostProvider () {
Expand All @@ -160,9 +192,34 @@ module.exports = class NetworkController extends EventEmitter {
this._setNetworkClient(networkClient)
}

_configureStandardProvider ({ rpcUrl }) {
_configurePredefinedProvider ({ type }) {
log.info('NetworkController - configurePredefinedProvider', type)
// setup networkConfig
var settings = {
network: networks.networkList[type].chainId,
}
settings = extend(settings, networks.networkList[type])
const rpcUrl = networks.networkList[type].rpcUrl
const networkClient = createJsonRpcClient({ rpcUrl })
this.networkConfig.putState(settings)
this._setNetworkClient(networkClient)
}

_configureStandardProvider ({ rpcUrl, chainId }) {
log.info('NetworkController - configureStandardProvider', rpcUrl)
const networkClient = createJsonRpcClient({ rpcUrl })
// hack to add a 'rpc' network with chainId
networks.networkList['rpc'] = {
chainId: chainId,
rpcUrl,
ticker: 'ETH',
}
// setup networkConfig
var settings = {
network: chainId,
}
settings = extend(settings, networks.networkList['rpc'])
this.networkConfig.putState(settings)
this._setNetworkClient(networkClient)
}

Expand Down
23 changes: 23 additions & 0 deletions app/scripts/controllers/network/networks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
'use strict'
var networks = function() {}

const {
CLASSIC,
CLASSIC_CODE,
} = require('./enums')

networks.networkList = {
[CLASSIC]: {
'chainId': CLASSIC_CODE,
'ticker': 'ETC',
'blockExplorerTx': 'https://gastracker.io/tx/[[txHash]]',
'blockExplorerAddr': 'https://gastracker.io/addr/[[address]]',
'blockExplorerToken': 'https://gastracker.io/token/[[tokenAddress]]/[[address]]',
'service': 'ETC Cooperative',
'rpcUrl': 'https://ethereumclassic.network',
'exchanges': ['ShapeShift'],
'buyUrl': '',
},
}

module.exports = networks
5 changes: 5 additions & 0 deletions app/scripts/controllers/network/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,28 @@ const {
RINKEBY,
KOVAN,
MAINNET,
CLASSIC,
ROPSTEN_CODE,
RINKEYBY_CODE,
KOVAN_CODE,
CLASSIC_CODE,
ROPSTEN_DISPLAY_NAME,
RINKEBY_DISPLAY_NAME,
KOVAN_DISPLAY_NAME,
MAINNET_DISPLAY_NAME,
CLASSIC_DISPLAY_NAME,
} = require('./enums')

const networkToNameMap = {
[ROPSTEN]: ROPSTEN_DISPLAY_NAME,
[RINKEBY]: RINKEBY_DISPLAY_NAME,
[KOVAN]: KOVAN_DISPLAY_NAME,
[MAINNET]: MAINNET_DISPLAY_NAME,
[CLASSIC]: CLASSIC_DISPLAY_NAME,
[ROPSTEN_CODE]: ROPSTEN_DISPLAY_NAME,
[RINKEYBY_CODE]: RINKEBY_DISPLAY_NAME,
[KOVAN_CODE]: KOVAN_DISPLAY_NAME,
[CLASSIC_CODE]: CLASSIC_DISPLAY_NAME,
}

const getNetworkDisplayName = key => networkToNameMap[key]
Expand Down
Loading