Add support for RPC endpoints with custom chain IDs (#5134)

feature/default_network_editable
HackyMiner 6 years ago committed by Whymarrh Whitby
parent eaca9a0e8a
commit 54a8ade266
  1. 22
      app/_locales/en/messages.json
  2. 52
      app/scripts/controllers/currency.js
  3. 60
      app/scripts/controllers/network/network.js
  4. 31
      app/scripts/controllers/preferences.js
  5. 18
      app/scripts/metamask-controller.js
  6. 2
      development/states/add-token.json
  7. 2
      development/states/confirm-sig-requests.json
  8. 2
      development/states/currency-localization.json
  9. 2
      development/states/first-time.json
  10. 2
      development/states/send-new-ui.json
  11. 2
      development/states/tx-list-items.json
  12. 2
      old-ui/app/app.js
  13. 15
      old-ui/app/components/app-bar.js
  14. 11
      old-ui/app/components/balance.js
  15. 12
      old-ui/app/components/eth-balance.js
  16. 76
      old-ui/app/config.js
  17. 8
      old-ui/app/util.js
  18. 4
      test/data/mock-state.json
  19. 2
      test/unit/app/controllers/network-contoller-test.js
  20. 16
      test/unit/app/controllers/preferences-controller-test.js
  21. 2
      test/unit/ui/app/actions.spec.js
  22. 12
      ui/app/actions.js
  23. 8
      ui/app/app.js
  24. 7
      ui/app/components/balance-component.js
  25. 4
      ui/app/components/currency-display/currency-display.component.js
  26. 5
      ui/app/components/currency-input/currency-input.component.js
  27. 7
      ui/app/components/currency-input/currency-input.container.js
  28. 12
      ui/app/components/currency-input/tests/currency-input.component.test.js
  29. 5
      ui/app/components/currency-input/tests/currency-input.container.test.js
  30. 6
      ui/app/components/dropdowns/components/account-dropdowns.js
  31. 35
      ui/app/components/dropdowns/network-dropdown.js
  32. 4
      ui/app/components/dropdowns/tests/network-dropdown.test.js
  33. 12
      ui/app/components/eth-balance.js
  34. 4
      ui/app/components/network-display/network-display.component.js
  35. 5
      ui/app/components/network.js
  36. 12
      ui/app/components/pages/settings/settings-tab/index.scss
  37. 122
      ui/app/components/pages/settings/settings-tab/settings-tab.component.js
  38. 14
      ui/app/components/pages/settings/settings-tab/settings-tab.container.js
  39. 2
      ui/app/components/send/account-list-item/account-list-item.container.js
  40. 1
      ui/app/components/send/account-list-item/tests/account-list-item-component.test.js
  41. 2
      ui/app/components/send/account-list-item/tests/account-list-item-container.test.js
  42. 5
      ui/app/components/send/send.selectors.js
  43. 1
      ui/app/components/send/tests/send-selectors-test-data.js
  44. 10
      ui/app/components/send/tests/send-selectors.test.js
  45. 3
      ui/app/components/transaction-activity-log/tests/transaction-activity-log.container.test.js
  46. 11
      ui/app/components/transaction-activity-log/transaction-activity-log.component.js
  47. 3
      ui/app/components/transaction-activity-log/transaction-activity-log.container.js
  48. 2
      ui/app/components/transaction-breakdown/index.js
  49. 7
      ui/app/components/transaction-breakdown/transaction-breakdown.component.js
  50. 11
      ui/app/components/transaction-breakdown/transaction-breakdown.container.js
  51. 3
      ui/app/components/transaction-view-balance/transaction-view-balance.container.js
  52. 22
      ui/app/components/user-preferenced-currency-display/tests/user-preferenced-currency-display.container.test.js
  53. 1
      ui/app/components/user-preferenced-currency-display/user-preferenced-currency-display.component.js
  54. 18
      ui/app/components/user-preferenced-currency-display/user-preferenced-currency-display.container.js
  55. 6
      ui/app/components/user-preferenced-currency-input/tests/user-preferenced-currency-input.component.test.js
  56. 4
      ui/app/components/user-preferenced-currency-input/tests/user-preferenced-currency-input.container.test.js
  57. 6
      ui/app/components/user-preferenced-currency-input/user-preferenced-currency-input.component.js
  58. 4
      ui/app/components/user-preferenced-currency-input/user-preferenced-currency-input.container.js
  59. 6
      ui/app/components/user-preferenced-token-input/tests/user-preferenced-token-input.component.test.js
  60. 4
      ui/app/components/user-preferenced-token-input/tests/user-preferenced-token-input.container.test.js
  61. 6
      ui/app/components/user-preferenced-token-input/user-preferenced-token-input.component.js
  62. 4
      ui/app/components/user-preferenced-token-input/user-preferenced-token-input.container.js
  63. 10
      ui/app/ducks/confirm-transaction.duck.js
  64. 8
      ui/app/helpers/confirm-transaction/util.js
  65. 10
      ui/app/helpers/conversions.util.js
  66. 2
      ui/app/reducers/metamask.js
  67. 5
      ui/app/selectors.js
  68. 1
      ui/app/selectors/confirm-transaction.js
  69. 8
      ui/app/util.js

@ -692,9 +692,27 @@
"newRecipient": { "newRecipient": {
"message": "New Recipient" "message": "New Recipient"
}, },
"newRPC": { "newNetwork": {
"message": "New Network"
},
"rpcURL": {
"message": "New RPC URL" "message": "New RPC URL"
}, },
"showAdvancedOptions": {
"message": "Show Advanced Options"
},
"hideAdvancedOptions": {
"message": "Hide Advanced Options"
},
"optionalChainId": {
"message": "ChainID (optional)"
},
"optionalSymbol": {
"message": "Symbol (optional)"
},
"optionalNickname": {
"message": "Nickname (optional)"
},
"next": { "next": {
"message": "Next" "message": "Next"
}, },
@ -803,7 +821,7 @@
"message": "Primary Currency" "message": "Primary Currency"
}, },
"primaryCurrencySettingDescription": { "primaryCurrencySettingDescription": {
"message": "Select ETH to prioritize displaying values in ETH. Select Fiat to prioritize displaying values in your selected currency." "message": "Select native to prioritize displaying values in the native currency of the chain (e.g. ETH). Select Fiat to prioritize displaying values in your selected fiat currency."
}, },
"privacyMsg": { "privacyMsg": {
"message": "Privacy Policy" "message": "Privacy Policy"

@ -21,6 +21,7 @@ class CurrencyController {
* since midnight of January 1, 1970 * since midnight of January 1, 1970
* @property {number} conversionInterval The id of the interval created by the scheduleConversionInterval method. * @property {number} conversionInterval The id of the interval created by the scheduleConversionInterval method.
* Used to clear an existing interval on subsequent calls of that method. * Used to clear an existing interval on subsequent calls of that method.
* @property {string} nativeCurrency The ticker/symbol of the native chain currency
* *
*/ */
constructor (opts = {}) { constructor (opts = {}) {
@ -28,6 +29,7 @@ class CurrencyController {
currentCurrency: 'usd', currentCurrency: 'usd',
conversionRate: 0, conversionRate: 0,
conversionDate: 'N/A', conversionDate: 'N/A',
nativeCurrency: 'ETH',
}, opts.initState) }, opts.initState)
this.store = new ObservableStore(initState) this.store = new ObservableStore(initState)
} }
@ -36,6 +38,29 @@ class CurrencyController {
// PUBLIC METHODS // PUBLIC METHODS
// //
/**
* A getter for the nativeCurrency property
*
* @returns {string} A 2-4 character shorthand that describes the specific currency
*
*/
getNativeCurrency () {
return this.store.getState().nativeCurrency
}
/**
* A setter for the nativeCurrency property
*
* @param {string} nativeCurrency The new currency to set as the nativeCurrency in the store
*
*/
setNativeCurrency (nativeCurrency) {
this.store.updateState({
nativeCurrency,
ticker: nativeCurrency,
})
}
/** /**
* A getter for the currentCurrency property * A getter for the currentCurrency property
* *
@ -104,15 +129,32 @@ class CurrencyController {
* *
*/ */
async updateConversionRate () { async updateConversionRate () {
let currentCurrency let currentCurrency, nativeCurrency
try { try {
currentCurrency = this.getCurrentCurrency() currentCurrency = this.getCurrentCurrency()
const response = await fetch(`https://api.infura.io/v1/ticker/eth${currentCurrency.toLowerCase()}`) nativeCurrency = this.getNativeCurrency()
let apiUrl
if (nativeCurrency === 'ETH') {
apiUrl = `https://api.infura.io/v1/ticker/eth${currentCurrency.toLowerCase()}`
} else {
apiUrl = `https://min-api.cryptocompare.com/data/price?fsym=${nativeCurrency.toUpperCase()}&tsyms=${currentCurrency.toUpperCase()}`
}
const response = await fetch(apiUrl)
const parsedResponse = await response.json() const parsedResponse = await response.json()
this.setConversionRate(Number(parsedResponse.bid)) if (nativeCurrency === 'ETH') {
this.setConversionDate(Number(parsedResponse.timestamp)) this.setConversionRate(Number(parsedResponse.bid))
this.setConversionDate(Number(parsedResponse.timestamp))
} else {
if (parsedResponse[currentCurrency.toUpperCase()]) {
this.setConversionRate(Number(parsedResponse[currentCurrency.toUpperCase()]))
this.setConversionDate(parseInt((new Date()).getTime() / 1000))
} else {
this.setConversionRate(0)
this.setConversionDate('N/A')
}
}
} catch (err) { } catch (err) {
log.warn(`MetaMask - Failed to query currency conversion:`, currentCurrency, err) log.warn(`MetaMask - Failed to query currency conversion:`, nativeCurrency, currentCurrency, err)
this.setConversionRate(0) this.setConversionRate(0)
this.setConversionDate('N/A') this.setConversionDate('N/A')
} }

@ -11,6 +11,8 @@ const createInfuraClient = require('./createInfuraClient')
const createJsonRpcClient = require('./createJsonRpcClient') const createJsonRpcClient = require('./createJsonRpcClient')
const createLocalhostClient = require('./createLocalhostClient') const createLocalhostClient = require('./createLocalhostClient')
const { createSwappableProxy, createEventEmitterProxy } = require('swappable-obj-proxy') const { createSwappableProxy, createEventEmitterProxy } = require('swappable-obj-proxy')
const extend = require('extend')
const networks = { networkList: {} }
const { const {
ROPSTEN, ROPSTEN,
@ -29,6 +31,10 @@ const defaultProviderConfig = {
type: testMode ? RINKEBY : MAINNET, type: testMode ? RINKEBY : MAINNET,
} }
const defaultNetworkConfig = {
ticker: 'ETH',
}
module.exports = class NetworkController extends EventEmitter { module.exports = class NetworkController extends EventEmitter {
constructor (opts = {}) { constructor (opts = {}) {
@ -39,7 +45,8 @@ module.exports = class NetworkController extends EventEmitter {
// create stores // create stores
this.providerStore = new ObservableStore(providerConfig) this.providerStore = new ObservableStore(providerConfig)
this.networkStore = new ObservableStore('loading') 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) this.on('networkDidChange', this.lookupNetwork)
// provider and block tracker // provider and block tracker
this._provider = null this._provider = null
@ -51,8 +58,8 @@ module.exports = class NetworkController extends EventEmitter {
initializeProvider (providerParams) { initializeProvider (providerParams) {
this._baseProviderParams = providerParams this._baseProviderParams = providerParams
const { type, rpcTarget } = this.providerStore.getState() const { type, rpcTarget, chainId, ticker, nickname } = this.providerStore.getState()
this._configureProvider({ type, rpcTarget }) this._configureProvider({ type, rpcTarget, chainId, ticker, nickname })
this.lookupNetwork() this.lookupNetwork()
} }
@ -72,7 +79,20 @@ module.exports = class NetworkController extends EventEmitter {
return this.networkStore.getState() 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) return this.networkStore.putState(network)
} }
@ -85,18 +105,22 @@ module.exports = class NetworkController extends EventEmitter {
if (!this._provider) { if (!this._provider) {
return log.warn('NetworkController - lookupNetwork aborted due to missing provider') return log.warn('NetworkController - lookupNetwork aborted due to missing provider')
} }
var { type } = this.providerStore.getState()
const ethQuery = new EthQuery(this._provider) const ethQuery = new EthQuery(this._provider)
ethQuery.sendAsync({ method: 'net_version' }, (err, network) => { ethQuery.sendAsync({ method: 'net_version' }, (err, network) => {
if (err) return this.setNetworkState('loading') if (err) return this.setNetworkState('loading')
log.info('web3.getNetwork returned ' + network) log.info('web3.getNetwork returned ' + network)
this.setNetworkState(network) this.setNetworkState(network, type)
}) })
} }
setRpcTarget (rpcTarget) { setRpcTarget (rpcTarget, chainId, ticker = 'ETH', nickname = '') {
const providerConfig = { const providerConfig = {
type: 'rpc', type: 'rpc',
rpcTarget, rpcTarget,
chainId,
ticker,
nickname,
} }
this.providerConfig = providerConfig this.providerConfig = providerConfig
} }
@ -132,7 +156,7 @@ module.exports = class NetworkController extends EventEmitter {
} }
_configureProvider (opts) { _configureProvider (opts) {
const { type, rpcTarget } = opts const { type, rpcTarget, chainId, ticker, nickname } = opts
// infura type-based endpoints // infura type-based endpoints
const isInfura = INFURA_PROVIDER_TYPES.includes(type) const isInfura = INFURA_PROVIDER_TYPES.includes(type)
if (isInfura) { if (isInfura) {
@ -142,7 +166,7 @@ module.exports = class NetworkController extends EventEmitter {
this._configureLocalhostProvider() this._configureLocalhostProvider()
// url-based rpc endpoints // url-based rpc endpoints
} else if (type === 'rpc') { } else if (type === 'rpc') {
this._configureStandardProvider({ rpcUrl: rpcTarget }) this._configureStandardProvider({ rpcUrl: rpcTarget, chainId, ticker, nickname })
} else { } else {
throw new Error(`NetworkController - _configureProvider - unknown type "${type}"`) throw new Error(`NetworkController - _configureProvider - unknown type "${type}"`)
} }
@ -152,6 +176,11 @@ module.exports = class NetworkController extends EventEmitter {
log.info('NetworkController - configureInfuraProvider', type) log.info('NetworkController - configureInfuraProvider', type)
const networkClient = createInfuraClient({ network: type }) const networkClient = createInfuraClient({ network: type })
this._setNetworkClient(networkClient) this._setNetworkClient(networkClient)
// setup networkConfig
var settings = {
ticker: 'ETH',
}
this.networkConfig.putState(settings)
} }
_configureLocalhostProvider () { _configureLocalhostProvider () {
@ -160,9 +189,22 @@ module.exports = class NetworkController extends EventEmitter {
this._setNetworkClient(networkClient) this._setNetworkClient(networkClient)
} }
_configureStandardProvider ({ rpcUrl }) { _configureStandardProvider ({ rpcUrl, chainId, ticker, nickname }) {
log.info('NetworkController - configureStandardProvider', rpcUrl) log.info('NetworkController - configureStandardProvider', rpcUrl)
const networkClient = createJsonRpcClient({ 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) this._setNetworkClient(networkClient)
} }

@ -25,7 +25,7 @@ class PreferencesController {
*/ */
constructor (opts = {}) { constructor (opts = {}) {
const initState = extend({ const initState = extend({
frequentRpcList: [], frequentRpcListDetail: [],
currentAccountTab: 'history', currentAccountTab: 'history',
accountTokens: {}, accountTokens: {},
assetImages: {}, assetImages: {},
@ -39,7 +39,7 @@ class PreferencesController {
seedWords: null, seedWords: null,
forgottenPassword: false, forgottenPassword: false,
preferences: { preferences: {
useETHAsPrimaryCurrency: true, useNativeCurrencyAsPrimaryCurrency: true,
}, },
}, opts.initState) }, opts.initState)
@ -392,19 +392,22 @@ class PreferencesController {
* Adds custom RPC url to state. * Adds custom RPC url to state.
* *
* @param {string} url The RPC url to add to frequentRpcList. * @param {string} url The RPC url to add to frequentRpcList.
* @param {number} chainId Optional chainId of the selected network.
* @param {string} ticker Optional ticker symbol of the selected network.
* @param {string} nickname Optional nickname of the selected network.
* @returns {Promise<array>} Promise resolving to updated frequentRpcList. * @returns {Promise<array>} Promise resolving to updated frequentRpcList.
* *
*/ */
addToFrequentRpcList (url) { addToFrequentRpcList (url, chainId, ticker = 'ETH', nickname = '') {
const rpcList = this.getFrequentRpcList() const rpcList = this.getFrequentRpcListDetail()
const index = rpcList.findIndex((element) => { return element === url }) const index = rpcList.findIndex((element) => { return element.rpcUrl === url })
if (index !== -1) { if (index !== -1) {
rpcList.splice(index, 1) rpcList.splice(index, 1)
} }
if (url !== 'http://localhost:8545') { if (url !== 'http://localhost:8545') {
rpcList.push(url) rpcList.push({ rpcUrl: url, chainId, ticker, nickname })
} }
this.store.updateState({ frequentRpcList: rpcList }) this.store.updateState({ frequentRpcListiDetail: rpcList })
return Promise.resolve(rpcList) return Promise.resolve(rpcList)
} }
@ -416,23 +419,23 @@ class PreferencesController {
* *
*/ */
removeFromFrequentRpcList (url) { removeFromFrequentRpcList (url) {
const rpcList = this.getFrequentRpcList() const rpcList = this.getFrequentRpcListDetail()
const index = rpcList.findIndex((element) => { return element === url }) const index = rpcList.findIndex((element) => { return element.rpcUrl === url })
if (index !== -1) { if (index !== -1) {
rpcList.splice(index, 1) rpcList.splice(index, 1)
} }
this.store.updateState({ frequentRpcList: rpcList }) this.store.updateState({ frequentRpcListDetail: rpcList })
return Promise.resolve(rpcList) return Promise.resolve(rpcList)
} }
/** /**
* Getter for the `frequentRpcList` property. * Getter for the `frequentRpcListDetail` property.
* *
* @returns {array<string>} An array of one or two rpc urls. * @returns {array<array>} An array of rpc urls.
* *
*/ */
getFrequentRpcList () { getFrequentRpcListDetail () {
return this.store.getState().frequentRpcList return this.store.getState().frequentRpcListDetail
} }
/** /**

@ -138,12 +138,12 @@ module.exports = class MetamaskController extends EventEmitter {
this.accountTracker.stop() this.accountTracker.stop()
} }
}) })
// ensure accountTracker updates balances after network change // ensure accountTracker updates balances after network change
this.networkController.on('networkDidChange', () => { this.networkController.on('networkDidChange', () => {
this.accountTracker._updateAccounts() this.accountTracker._updateAccounts()
}) })
// key mgmt // key mgmt
const additionalKeyrings = [TrezorKeyring, LedgerBridgeKeyring] const additionalKeyrings = [TrezorKeyring, LedgerBridgeKeyring]
this.keyringController = new KeyringController({ this.keyringController = new KeyringController({
@ -197,6 +197,8 @@ module.exports = class MetamaskController extends EventEmitter {
}) })
this.networkController.on('networkDidChange', () => { this.networkController.on('networkDidChange', () => {
this.balancesController.updateAllBalances() this.balancesController.updateAllBalances()
var currentCurrency = this.currencyController.getCurrentCurrency()
this.setCurrentCurrency(currentCurrency, function() {})
}) })
this.balancesController.updateAllBalances() this.balancesController.updateAllBalances()
@ -1412,10 +1414,13 @@ module.exports = class MetamaskController extends EventEmitter {
* @param {Function} cb - A callback function returning currency info. * @param {Function} cb - A callback function returning currency info.
*/ */
setCurrentCurrency (currencyCode, cb) { setCurrentCurrency (currencyCode, cb) {
const { ticker } = this.networkController.getNetworkConfig()
try { try {
this.currencyController.setNativeCurrency(ticker)
this.currencyController.setCurrentCurrency(currencyCode) this.currencyController.setCurrentCurrency(currencyCode)
this.currencyController.updateConversionRate() this.currencyController.updateConversionRate()
const data = { const data = {
nativeCurrency: ticker || 'ETH',
conversionRate: this.currencyController.getConversionRate(), conversionRate: this.currencyController.getConversionRate(),
currentCurrency: this.currencyController.getCurrentCurrency(), currentCurrency: this.currencyController.getCurrentCurrency(),
conversionDate: this.currencyController.getConversionDate(), conversionDate: this.currencyController.getConversionDate(),
@ -1454,11 +1459,14 @@ module.exports = class MetamaskController extends EventEmitter {
/** /**
* A method for selecting a custom URL for an ethereum RPC provider. * A method for selecting a custom URL for an ethereum RPC provider.
* @param {string} rpcTarget - A URL for a valid Ethereum RPC API. * @param {string} rpcTarget - A URL for a valid Ethereum RPC API.
* @param {number} chainId - The chainId of the selected network.
* @param {string} ticker - The ticker symbol of the selected network.
* @param {string} nickname - Optional nickname of the selected network.
* @returns {Promise<String>} - The RPC Target URL confirmed. * @returns {Promise<String>} - The RPC Target URL confirmed.
*/ */
async setCustomRpc (rpcTarget) { async setCustomRpc (rpcTarget, chainId, ticker = 'ETH', nickname = '') {
this.networkController.setRpcTarget(rpcTarget) this.networkController.setRpcTarget(rpcTarget, chainId, ticker, nickname)
await this.preferencesController.addToFrequentRpcList(rpcTarget) await this.preferencesController.addToFrequentRpcList(rpcTarget, chainId, ticker, nickname)
return rpcTarget return rpcTarget
} }

@ -109,7 +109,7 @@
}, },
"currentLocale": "en", "currentLocale": "en",
"preferences": { "preferences": {
"useETHAsPrimaryCurrency": true "useNativeCurrencyAsPrimaryCurrency": true
} }
}, },
"appState": { "appState": {

@ -152,7 +152,7 @@
}, },
"currentLocale": "en", "currentLocale": "en",
"preferences": { "preferences": {
"useETHAsPrimaryCurrency": true "useNativeCurrencyAsPrimaryCurrency": true
} }
}, },
"appState": { "appState": {

@ -110,7 +110,7 @@
}, },
"currentLocale": "en", "currentLocale": "en",
"preferences": { "preferences": {
"useETHAsPrimaryCurrency": true "useNativeCurrencyAsPrimaryCurrency": true
} }
}, },
"appState": { "appState": {

@ -39,7 +39,7 @@
"tokens": [], "tokens": [],
"currentLocale": "en", "currentLocale": "en",
"preferences": { "preferences": {
"useETHAsPrimaryCurrency": true "useNativeCurrencyAsPrimaryCurrency": true
} }
}, },
"appState": { "appState": {

@ -111,7 +111,7 @@
}, },
"currentLocale": "en", "currentLocale": "en",
"preferences": { "preferences": {
"useETHAsPrimaryCurrency": true "useNativeCurrencyAsPrimaryCurrency": true
} }
}, },
"appState": { "appState": {

@ -104,7 +104,7 @@
"send": {}, "send": {},
"currentLocale": "en", "currentLocale": "en",
"preferences": { "preferences": {
"useETHAsPrimaryCurrency": true "useNativeCurrencyAsPrimaryCurrency": true
} }
}, },
"appState": { "appState": {

@ -73,7 +73,7 @@ function mapStateToProps (state) {
forgottenPassword: state.appState.forgottenPassword, forgottenPassword: state.appState.forgottenPassword,
nextUnreadNotice: state.metamask.nextUnreadNotice, nextUnreadNotice: state.metamask.nextUnreadNotice,
lostAccounts: state.metamask.lostAccounts, lostAccounts: state.metamask.lostAccounts,
frequentRpcList: state.metamask.frequentRpcList || [], frequentRpcListDetail: state.metamask.frequentRpcListDetail || [],
featureFlags, featureFlags,
suggestedTokens: state.metamask.suggestedTokens, suggestedTokens: state.metamask.suggestedTokens,

@ -17,7 +17,7 @@ module.exports = class AppBar extends Component {
static propTypes = { static propTypes = {
dispatch: PropTypes.func.isRequired, dispatch: PropTypes.func.isRequired,
frequentRpcList: PropTypes.array.isRequired, frequentRpcListDetail: PropTypes.array.isRequired,
isMascara: PropTypes.bool.isRequired, isMascara: PropTypes.bool.isRequired,
isOnboarding: PropTypes.bool.isRequired, isOnboarding: PropTypes.bool.isRequired,
identities: PropTypes.any.isRequired, identities: PropTypes.any.isRequired,
@ -196,7 +196,7 @@ module.exports = class AppBar extends Component {
renderNetworkDropdown () { renderNetworkDropdown () {
const { const {
dispatch, dispatch,
frequentRpcList: rpcList, frequentRpcListDetail: rpcList,
provider, provider,
} = this.props } = this.props
const { const {
@ -321,8 +321,8 @@ module.exports = class AppBar extends Component {
]) ])
} }
renderCustomOption ({ rpcTarget, type }) { renderCustomOption ({ rpcTarget, type, ticker }) {
const {dispatch} = this.props const {dispatch, network} = this.props
if (type !== 'rpc') { if (type !== 'rpc') {
return null return null
@ -340,7 +340,7 @@ module.exports = class AppBar extends Component {
default: default:
return h(DropdownMenuItem, { return h(DropdownMenuItem, {
key: rpcTarget, key: rpcTarget,
onClick: () => dispatch(actions.setRpcTarget(rpcTarget)), onClick: () => dispatch(actions.setRpcTarget(rpcTarget, network, ticker)),
closeMenu: () => this.setState({ isNetworkMenuOpen: false }), closeMenu: () => this.setState({ isNetworkMenuOpen: false }),
}, [ }, [
h('i.fa.fa-question-circle.fa-lg.menu-icon'), h('i.fa.fa-question-circle.fa-lg.menu-icon'),
@ -354,7 +354,8 @@ module.exports = class AppBar extends Component {
const {dispatch} = this.props const {dispatch} = this.props
const reversedRpcList = rpcList.slice().reverse() const reversedRpcList = rpcList.slice().reverse()
return reversedRpcList.map((rpc) => { return reversedRpcList.map((entry) => {
const rpc = entry.rpcUrl
const currentRpcTarget = provider.type === 'rpc' && rpc === provider.rpcTarget const currentRpcTarget = provider.type === 'rpc' && rpc === provider.rpcTarget
if ((rpc === LOCALHOST_RPC_URL) || currentRpcTarget) { if ((rpc === LOCALHOST_RPC_URL) || currentRpcTarget) {
@ -363,7 +364,7 @@ module.exports = class AppBar extends Component {
return h(DropdownMenuItem, { return h(DropdownMenuItem, {
key: `common${rpc}`, key: `common${rpc}`,
closeMenu: () => this.setState({ isNetworkMenuOpen: false }), closeMenu: () => this.setState({ isNetworkMenuOpen: false }),
onClick: () => dispatch(actions.setRpcTarget(rpc)), onClick: () => dispatch(actions.setRpcTarget(rpc, entry.chainId, entry.ticker)),
}, [ }, [
h('i.fa.fa-question-circle.fa-lg.menu-icon'), h('i.fa.fa-question-circle.fa-lg.menu-icon'),
rpc, rpc,

@ -1,12 +1,18 @@
const Component = require('react').Component const Component = require('react').Component
const h = require('react-hyperscript') const h = require('react-hyperscript')
const connect = require('react-redux').connect
const inherits = require('util').inherits const inherits = require('util').inherits
const formatBalance = require('../util').formatBalance const formatBalance = require('../util').formatBalance
const generateBalanceObject = require('../util').generateBalanceObject const generateBalanceObject = require('../util').generateBalanceObject
const Tooltip = require('./tooltip.js') const Tooltip = require('./tooltip.js')
const FiatValue = require('./fiat-value.js') const FiatValue = require('./fiat-value.js')
module.exports = EthBalanceComponent module.exports = connect(mapStateToProps)(EthBalanceComponent)
function mapStateToProps (state) {
return {
ticker: state.metamask.ticker,
}
}
inherits(EthBalanceComponent, Component) inherits(EthBalanceComponent, Component)
function EthBalanceComponent () { function EthBalanceComponent () {
@ -16,9 +22,10 @@ function EthBalanceComponent () {
EthBalanceComponent.prototype.render = function () { EthBalanceComponent.prototype.render = function () {
var props = this.props var props = this.props
let { value } = props let { value } = props
const { ticker } = props
var style = props.style var style = props.style
var needsParse = this.props.needsParse !== undefined ? this.props.needsParse : true var needsParse = this.props.needsParse !== undefined ? this.props.needsParse : true
value = value ? formatBalance(value, 6, needsParse) : '...' value = value ? formatBalance(value, 6, needsParse, ticker) : '...'
var width = props.width var width = props.width
return ( return (

@ -1,12 +1,18 @@
const Component = require('react').Component const Component = require('react').Component
const h = require('react-hyperscript') const h = require('react-hyperscript')
const connect = require('react-redux').connect
const inherits = require('util').inherits const inherits = require('util').inherits
const formatBalance = require('../util').formatBalance const formatBalance = require('../util').formatBalance
const generateBalanceObject = require('../util').generateBalanceObject const generateBalanceObject = require('../util').generateBalanceObject
const Tooltip = require('./tooltip.js') const Tooltip = require('./tooltip.js')
const FiatValue = require('./fiat-value.js') const FiatValue = require('./fiat-value.js')
module.exports = EthBalanceComponent module.exports = connect(mapStateToProps)(EthBalanceComponent)
function mapStateToProps (state) {
return {
ticker: state.metamask.ticker,
}
}
inherits(EthBalanceComponent, Component) inherits(EthBalanceComponent, Component)
function EthBalanceComponent () { function EthBalanceComponent () {
@ -16,9 +22,9 @@ function EthBalanceComponent () {
EthBalanceComponent.prototype.render = function () { EthBalanceComponent.prototype.render = function () {
var props = this.props var props = this.props
let { value } = props let { value } = props
const { style, width } = props const { ticker, style, width } = props
var needsParse = this.props.needsParse !== undefined ? this.props.needsParse : true var needsParse = this.props.needsParse !== undefined ? this.props.needsParse : true
value = value ? formatBalance(value, 6, needsParse) : '...' value = value ? formatBalance(value, 6, needsParse, ticker) : '...'
return ( return (

@ -68,7 +68,7 @@ ConfigScreen.prototype.render = function () {
currentProviderDisplay(metamaskState), currentProviderDisplay(metamaskState),
h('div', { style: {display: 'flex'} }, [ h('div', { style: {display: 'block'} }, [
h('input#new_rpc', { h('input#new_rpc', {
placeholder: 'New RPC URL', placeholder: 'New RPC URL',
style: { style: {
@ -81,7 +81,70 @@ ConfigScreen.prototype.render = function () {
if (event.key === 'Enter') { if (event.key === 'Enter') {
var element = event.target var element = event.target
var newRpc = element.value var newRpc = element.value
rpcValidation(newRpc, state) var chainid = document.querySelector('input#chainid')
var ticker = document.querySelector('input#ticker')
var nickname = document.querySelector('input#nickname')
rpcValidation(newRpc, chainid.value, ticker.value, nickname.value, state)
}
},
}),
h('br'),
h('input#chainid', {
placeholder: 'ChainId (optional)',
style: {
width: 'inherit',
flex: '1 0 auto',
height: '30px',
margin: '8px',
},
onKeyPress (event) {
if (event.key === 'Enter') {
var element = document.querySelector('input#new_rpc')
var newRpc = element.value
var chainid = document.querySelector('input#chainid')
var ticker = document.querySelector('input#ticker')
var nickname = document.querySelector('input#nickname')
rpcValidation(newRpc, chainid.value, ticker.value, nickname.value, state)
}
},
}),
h('br'),
h('input#ticker', {
placeholder: 'Symbol (optional)',
style: {
width: 'inherit',
flex: '1 0 auto',
height: '30px',
margin: '8px',
},
onKeyPress (event) {
if (event.key === 'Enter') {
var element = document.querySelector('input#new_rpc')
var newRpc = element.value
var chainid = document.querySelector('input#chainid')
var ticker = document.querySelector('input#ticker')
var nickname = document.querySelector('input#nickname')
rpcValidation(newRpc, chainid.value, ticker.value, nickname.value, state)
}
},
}),
h('br'),
h('input#nickname', {
placeholder: 'Nickname (optional)',
style: {
width: 'inherit',
flex: '1 0 auto',
height: '30px',
margin: '8px',
},
onKeyPress (event) {
if (event.key === 'Enter') {
var element = document.querySelector('input#new_rpc')
var newRpc = element.value
var chainid = document.querySelector('input#chainid')
var ticker = document.querySelector('input#ticker')
var nickname = document.querySelector('input#nickname')
rpcValidation(newRpc, chainid.value, ticker.value, nickname.value, state)
} }
}, },
}), }),
@ -93,7 +156,10 @@ ConfigScreen.prototype.render = function () {
event.preventDefault() event.preventDefault()
var element = document.querySelector('input#new_rpc') var element = document.querySelector('input#new_rpc')
var newRpc = element.value var newRpc = element.value
rpcValidation(newRpc, state) var chainid = document.querySelector('input#chainid')
var ticker = document.querySelector('input#ticker')
var nickname = document.querySelector('input#nickname')
rpcValidation(newRpc, chainid.value, ticker.value, nickname.value, state)
}, },
}, 'Save'), }, 'Save'),
]), ]),
@ -189,9 +255,9 @@ ConfigScreen.prototype.render = function () {
) )
} }
function rpcValidation (newRpc, state) { function rpcValidation (newRpc, chainid, ticker = 'ETH', nickname = '', state) {
if (validUrl.isWebUri(newRpc)) { if (validUrl.isWebUri(newRpc)) {
state.dispatch(actions.setRpcTarget(newRpc)) state.dispatch(actions.setRpcTarget(newRpc, chainid, ticker, nickname))
} else { } else {
var appendedRpc = `http://${newRpc}` var appendedRpc = `http://${newRpc}`
if (validUrl.isWebUri(appendedRpc)) { if (validUrl.isWebUri(appendedRpc)) {

@ -102,7 +102,7 @@ function parseBalance (balance) {
// Takes wei hex, returns an object with three properties. // Takes wei hex, returns an object with three properties.
// Its "formatted" property is what we generally use to render values. // Its "formatted" property is what we generally use to render values.
function formatBalance (balance, decimalsToKeep, needsParse = true) { function formatBalance (balance, decimalsToKeep, needsParse = true, ticker = 'ETH') {
var parsed = needsParse ? parseBalance(balance) : balance.split('.') var parsed = needsParse ? parseBalance(balance) : balance.split('.')
var beforeDecimal = parsed[0] var beforeDecimal = parsed[0]
var afterDecimal = parsed[1] var afterDecimal = parsed[1]
@ -112,14 +112,14 @@ function formatBalance (balance, decimalsToKeep, needsParse = true) {
if (afterDecimal !== '0') { if (afterDecimal !== '0') {
var sigFigs = afterDecimal.match(/^0*(.{2})/) // default: grabs 2 most significant digits var sigFigs = afterDecimal.match(/^0*(.{2})/) // default: grabs 2 most significant digits
if (sigFigs) { afterDecimal = sigFigs[0] } if (sigFigs) { afterDecimal = sigFigs[0] }
formatted = '0.' + afterDecimal + ' ETH' formatted = '0.' + afterDecimal + ` ${ticker}`
} }
} else { } else {
formatted = beforeDecimal + '.' + afterDecimal.slice(0, 3) + ' ETH' formatted = beforeDecimal + '.' + afterDecimal.slice(0, 3) + ` ${ticker}`
} }
} else { } else {
afterDecimal += Array(decimalsToKeep).join('0') afterDecimal += Array(decimalsToKeep).join('0')
formatted = beforeDecimal + '.' + afterDecimal.slice(0, decimalsToKeep) + ' ETH' formatted = beforeDecimal + '.' + afterDecimal.slice(0, decimalsToKeep) + ` ${ticker}`
} }
return formatted return formatted
} }

@ -111,7 +111,9 @@
"0x108cf70c7d384c552f42c07c41c0e1e46d77ea0d": 0.00039345803819379796, "0x108cf70c7d384c552f42c07c41c0e1e46d77ea0d": 0.00039345803819379796,
"0xd8f6a2ffb0fc5952d16c9768b71cfd35b6399aa5": 0.00008189274407698049 "0xd8f6a2ffb0fc5952d16c9768b71cfd35b6399aa5": 0.00008189274407698049
}, },
"ticker": "ETH",
"currentCurrency": "usd", "currentCurrency": "usd",
"nativeCurrency": "ETH",
"conversionRate": 556.12, "conversionRate": 556.12,
"addressBook": [ "addressBook": [
{ {
@ -1248,4 +1250,4 @@
"context": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc" "context": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc"
} }
} }
} }

@ -47,7 +47,7 @@ describe('# Network Controller', function () {
describe('#setNetworkState', function () { describe('#setNetworkState', function () {
it('should update the network', function () { it('should update the network', function () {
networkController.setNetworkState(1) networkController.setNetworkState(1, 'rpc')
const networkState = networkController.getNetworkState() const networkState = networkController.getNetworkState()
assert.equal(networkState, 1, 'network is 1') assert.equal(networkState, 1, 'network is 1')
}) })

@ -487,20 +487,20 @@ describe('preferences controller', function () {
describe('on updateFrequentRpcList', function () { describe('on updateFrequentRpcList', function () {
it('should add custom RPC url to state', function () { it('should add custom RPC url to state', function () {
preferencesController.addToFrequentRpcList('rpc_url') preferencesController.addToFrequentRpcList('rpc_url', 1)
preferencesController.addToFrequentRpcList('http://localhost:8545') preferencesController.addToFrequentRpcList('http://localhost:8545', 1)
assert.deepEqual(preferencesController.store.getState().frequentRpcList, ['rpc_url']) assert.deepEqual(preferencesController.store.getState().frequentRpcListDetail, [{ rpcUrl: 'rpc_url', chainId: 1, ticker: 'ETH', nickname: '' }] )
preferencesController.addToFrequentRpcList('rpc_url') preferencesController.addToFrequentRpcList('rpc_url', 1)
assert.deepEqual(preferencesController.store.getState().frequentRpcList, ['rpc_url']) assert.deepEqual(preferencesController.store.getState().frequentRpcListDetail, [{ rpcUrl: 'rpc_url', chainId: 1, ticker: 'ETH', nickname: '' }] )
}) })
it('should remove custom RPC url from state', function () { it('should remove custom RPC url from state', function () {
preferencesController.addToFrequentRpcList('rpc_url') preferencesController.addToFrequentRpcList('rpc_url', 1)
assert.deepEqual(preferencesController.store.getState().frequentRpcList, ['rpc_url']) assert.deepEqual(preferencesController.store.getState().frequentRpcListDetail, [{ rpcUrl: 'rpc_url', chainId: 1, ticker: 'ETH', nickname: '' }] )
preferencesController.removeFromFrequentRpcList('other_rpc_url') preferencesController.removeFromFrequentRpcList('other_rpc_url')
preferencesController.removeFromFrequentRpcList('http://localhost:8545') preferencesController.removeFromFrequentRpcList('http://localhost:8545')
preferencesController.removeFromFrequentRpcList('rpc_url') preferencesController.removeFromFrequentRpcList('rpc_url')
assert.deepEqual(preferencesController.store.getState().frequentRpcList, []) assert.deepEqual(preferencesController.store.getState().frequentRpcListDetail, [])
}) })
}) })
}) })

@ -1133,7 +1133,7 @@ describe('Actions', () => {
{ type: 'DISPLAY_WARNING', value: 'Had a problem changing networks!' }, { type: 'DISPLAY_WARNING', value: 'Had a problem changing networks!' },
] ]
setRpcTargetSpy.callsFake((newRpc, callback) => { setRpcTargetSpy.callsFake((newRpc, chainId, ticker, nickname, callback) => {
callback(new Error('error')) callback(new Error('error'))
}) })

@ -309,7 +309,7 @@ var actions = {
setPreference, setPreference,
updatePreferences, updatePreferences,
UPDATE_PREFERENCES: 'UPDATE_PREFERENCES', UPDATE_PREFERENCES: 'UPDATE_PREFERENCES',
setUseETHAsPrimaryCurrencyPreference, setUseNativeCurrencyAsPrimaryCurrencyPreference,
setMouseUserState, setMouseUserState,
SET_MOUSE_USER_STATE: 'SET_MOUSE_USER_STATE', SET_MOUSE_USER_STATE: 'SET_MOUSE_USER_STATE',
@ -1874,10 +1874,10 @@ function updateProviderType (type) {
} }
} }
function setRpcTarget (newRpc) { function setRpcTarget (newRpc, chainId, ticker = 'ETH', nickname = '') {
return (dispatch) => { return (dispatch) => {
log.debug(`background.setRpcTarget: ${newRpc}`) log.debug(`background.setRpcTarget: ${newRpc} ${chainId} ${ticker} ${nickname}`)
background.setCustomRpc(newRpc, (err, result) => { background.setCustomRpc(newRpc, chainId, ticker, nickname, (err, result) => {
if (err) { if (err) {
log.error(err) log.error(err)
return dispatch(actions.displayWarning('Had a problem changing networks!')) return dispatch(actions.displayWarning('Had a problem changing networks!'))
@ -2330,8 +2330,8 @@ function updatePreferences (value) {
} }
} }
function setUseETHAsPrimaryCurrencyPreference (value) { function setUseNativeCurrencyAsPrimaryCurrencyPreference (value) {
return setPreference('useETHAsPrimaryCurrency', value) return setPreference('useNativeCurrencyAsPrimaryCurrency', value)
} }
function setNetworkNonce (networkNonce) { function setNetworkNonce (networkNonce) {

@ -101,7 +101,7 @@ class App extends Component {
network, network,
isMouseUser, isMouseUser,
provider, provider,
frequentRpcList, frequentRpcListDetail,
currentView, currentView,
setMouseUserState, setMouseUserState,
sidebar, sidebar,
@ -147,7 +147,7 @@ class App extends Component {
// network dropdown // network dropdown
h(NetworkDropdown, { h(NetworkDropdown, {
provider, provider,
frequentRpcList, frequentRpcListDetail,
}, []), }, []),
h(AccountMenu), h(AccountMenu),
@ -230,7 +230,7 @@ App.propTypes = {
alertMessage: PropTypes.string, alertMessage: PropTypes.string,
network: PropTypes.string, network: PropTypes.string,
provider: PropTypes.object, provider: PropTypes.object,
frequentRpcList: PropTypes.array, frequentRpcListDetail: PropTypes.array,
currentView: PropTypes.object, currentView: PropTypes.object,
sidebar: PropTypes.object, sidebar: PropTypes.object,
alertOpen: PropTypes.bool, alertOpen: PropTypes.bool,
@ -322,7 +322,7 @@ function mapStateToProps (state) {
forgottenPassword: state.appState.forgottenPassword, forgottenPassword: state.appState.forgottenPassword,
nextUnreadNotice, nextUnreadNotice,
lostAccounts, lostAccounts,
frequentRpcList: state.metamask.frequentRpcList || [], frequentRpcListDetail: state.metamask.frequentRpcListDetail || [],
currentCurrency: state.metamask.currentCurrency, currentCurrency: state.metamask.currentCurrency,
isMouseUser: state.appState.isMouseUser, isMouseUser: state.appState.isMouseUser,
betaUI: state.metamask.featureFlags.betaUI, betaUI: state.metamask.featureFlags.betaUI,

@ -6,7 +6,7 @@ import TokenBalance from './token-balance'
import Identicon from './identicon' import Identicon from './identicon'
import UserPreferencedCurrencyDisplay from './user-preferenced-currency-display' import UserPreferencedCurrencyDisplay from './user-preferenced-currency-display'
import { PRIMARY, SECONDARY } from '../constants/common' import { PRIMARY, SECONDARY } from '../constants/common'
const { getAssetImages, conversionRateSelector, getCurrentCurrency} = require('../selectors') const { getNativeCurrency, getAssetImages, conversionRateSelector, getCurrentCurrency} = require('../selectors')
const { formatBalance } = require('../util') const { formatBalance } = require('../util')
@ -21,6 +21,7 @@ function mapStateToProps (state) {
return { return {
account, account,
network, network,
nativeCurrency: getNativeCurrency(state),
conversionRate: conversionRateSelector(state), conversionRate: conversionRateSelector(state),
currentCurrency: getCurrentCurrency(state), currentCurrency: getCurrentCurrency(state),
assetImages: getAssetImages(state), assetImages: getAssetImages(state),
@ -66,10 +67,10 @@ BalanceComponent.prototype.renderTokenBalance = function () {
BalanceComponent.prototype.renderBalance = function () { BalanceComponent.prototype.renderBalance = function () {
const props = this.props const props = this.props
const { account } = props const { account, nativeCurrency } = props
const balanceValue = account && account.balance const balanceValue = account && account.balance
const needsParse = 'needsParse' in props ? props.needsParse : true const needsParse = 'needsParse' in props ? props.needsParse : true
const formattedBalance = balanceValue ? formatBalance(balanceValue, 6, needsParse) : '...' const formattedBalance = balanceValue ? formatBalance(balanceValue, 6, needsParse, nativeCurrency) : '...'
const showFiat = 'showFiat' in props ? props.showFiat : true const showFiat = 'showFiat' in props ? props.showFiat : true
if (formattedBalance === 'None' || formattedBalance === '...') { if (formattedBalance === 'None' || formattedBalance === '...') {

@ -1,7 +1,7 @@
import React, { PureComponent } from 'react' import React, { PureComponent } from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import classnames from 'classnames' import classnames from 'classnames'
import { ETH, GWEI } from '../../constants/common' import { GWEI } from '../../constants/common'
export default class CurrencyDisplay extends PureComponent { export default class CurrencyDisplay extends PureComponent {
static propTypes = { static propTypes = {
@ -12,7 +12,7 @@ export default class CurrencyDisplay extends PureComponent {
style: PropTypes.object, style: PropTypes.object,
suffix: PropTypes.string, suffix: PropTypes.string,
// Used in container // Used in container
currency: PropTypes.oneOf([ETH]), currency: PropTypes.string,
denomination: PropTypes.oneOf([GWEI]), denomination: PropTypes.oneOf([GWEI]),
value: PropTypes.string, value: PropTypes.string,
numberOfDecimals: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), numberOfDecimals: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

@ -14,6 +14,7 @@ export default class CurrencyInput extends PureComponent {
static propTypes = { static propTypes = {
conversionRate: PropTypes.number, conversionRate: PropTypes.number,
currentCurrency: PropTypes.string, currentCurrency: PropTypes.string,
nativeCurrency: PropTypes.string,
onChange: PropTypes.func, onChange: PropTypes.func,
onBlur: PropTypes.func, onBlur: PropTypes.func,
suffix: PropTypes.string, suffix: PropTypes.string,
@ -77,13 +78,13 @@ export default class CurrencyInput extends PureComponent {
} }
renderConversionComponent () { renderConversionComponent () {
const { useFiat, currentCurrency } = this.props const { useFiat, currentCurrency, nativeCurrency } = this.props
const { hexValue } = this.state const { hexValue } = this.state
let currency, numberOfDecimals let currency, numberOfDecimals
if (useFiat) { if (useFiat) {
// Display ETH // Display ETH
currency = ETH currency = nativeCurrency || ETH
numberOfDecimals = 6 numberOfDecimals = 6
} else { } else {
// Display Fiat // Display Fiat

@ -3,18 +3,19 @@ import CurrencyInput from './currency-input.component'
import { ETH } from '../../constants/common' import { ETH } from '../../constants/common'
const mapStateToProps = state => { const mapStateToProps = state => {
const { metamask: { currentCurrency, conversionRate } } = state const { metamask: { nativeCurrency, currentCurrency, conversionRate } } = state
return { return {
nativeCurrency,
currentCurrency, currentCurrency,
conversionRate, conversionRate,
} }
} }
const mergeProps = (stateProps, dispatchProps, ownProps) => { const mergeProps = (stateProps, dispatchProps, ownProps) => {
const { currentCurrency } = stateProps const { nativeCurrency, currentCurrency } = stateProps
const { useFiat } = ownProps const { useFiat } = ownProps
const suffix = useFiat ? currentCurrency.toUpperCase() : ETH const suffix = useFiat ? currentCurrency.toUpperCase() : nativeCurrency || ETH
return { return {
...stateProps, ...stateProps,

@ -22,6 +22,7 @@ describe('CurrencyInput Component', () => {
it('should render properly with a suffix', () => { it('should render properly with a suffix', () => {
const mockStore = { const mockStore = {
metamask: { metamask: {
nativeCurrency: 'ETH',
currentCurrency: 'usd', currentCurrency: 'usd',
conversionRate: 231.06, conversionRate: 231.06,
}, },
@ -32,6 +33,7 @@ describe('CurrencyInput Component', () => {
<Provider store={store}> <Provider store={store}>
<CurrencyInput <CurrencyInput
suffix="ETH" suffix="ETH"
nativeCurrency="ETH"
/> />
</Provider> </Provider>
) )
@ -45,6 +47,7 @@ describe('CurrencyInput Component', () => {
it('should render properly with an ETH value', () => { it('should render properly with an ETH value', () => {
const mockStore = { const mockStore = {
metamask: { metamask: {
nativeCurrency: 'ETH',
currentCurrency: 'usd', currentCurrency: 'usd',
conversionRate: 231.06, conversionRate: 231.06,
}, },
@ -56,6 +59,7 @@ describe('CurrencyInput Component', () => {
<CurrencyInput <CurrencyInput
value="de0b6b3a7640000" value="de0b6b3a7640000"
suffix="ETH" suffix="ETH"
nativeCurrency="ETH"
currentCurrency="usd" currentCurrency="usd"
conversionRate={231.06} conversionRate={231.06}
/> />
@ -75,6 +79,7 @@ describe('CurrencyInput Component', () => {
it('should render properly with a fiat value', () => { it('should render properly with a fiat value', () => {
const mockStore = { const mockStore = {
metamask: { metamask: {
nativeCurrency: 'ETH',
currentCurrency: 'usd', currentCurrency: 'usd',
conversionRate: 231.06, conversionRate: 231.06,
}, },
@ -87,6 +92,7 @@ describe('CurrencyInput Component', () => {
value="f602f2234d0ea" value="f602f2234d0ea"
suffix="USD" suffix="USD"
useFiat useFiat
nativeCurrency="ETH"
currentCurrency="usd" currentCurrency="usd"
conversionRate={231.06} conversionRate={231.06}
/> />
@ -116,6 +122,7 @@ describe('CurrencyInput Component', () => {
it('should call onChange and onBlur on input changes with the hex value for ETH', () => { it('should call onChange and onBlur on input changes with the hex value for ETH', () => {
const mockStore = { const mockStore = {
metamask: { metamask: {
nativeCurrency: 'ETH',
currentCurrency: 'usd', currentCurrency: 'usd',
conversionRate: 231.06, conversionRate: 231.06,
}, },
@ -127,6 +134,7 @@ describe('CurrencyInput Component', () => {
onChange={handleChangeSpy} onChange={handleChangeSpy}
onBlur={handleBlurSpy} onBlur={handleBlurSpy}
suffix="ETH" suffix="ETH"
nativeCurrency="ETH"
currentCurrency="usd" currentCurrency="usd"
conversionRate={231.06} conversionRate={231.06}
/> />
@ -160,6 +168,7 @@ describe('CurrencyInput Component', () => {
it('should call onChange and onBlur on input changes with the hex value for fiat', () => { it('should call onChange and onBlur on input changes with the hex value for fiat', () => {
const mockStore = { const mockStore = {
metamask: { metamask: {
nativeCurrency: 'ETH',
currentCurrency: 'usd', currentCurrency: 'usd',
conversionRate: 231.06, conversionRate: 231.06,
}, },
@ -171,6 +180,7 @@ describe('CurrencyInput Component', () => {
onChange={handleChangeSpy} onChange={handleChangeSpy}
onBlur={handleBlurSpy} onBlur={handleBlurSpy}
suffix="USD" suffix="USD"
nativeCurrency="ETH"
currentCurrency="usd" currentCurrency="usd"
conversionRate={231.06} conversionRate={231.06}
useFiat useFiat
@ -205,6 +215,7 @@ describe('CurrencyInput Component', () => {
it('should change the state and pass in a new decimalValue when props.value changes', () => { it('should change the state and pass in a new decimalValue when props.value changes', () => {
const mockStore = { const mockStore = {
metamask: { metamask: {
nativeCurrency: 'ETH',
currentCurrency: 'usd', currentCurrency: 'usd',
conversionRate: 231.06, conversionRate: 231.06,
}, },
@ -216,6 +227,7 @@ describe('CurrencyInput Component', () => {
onChange={handleChangeSpy} onChange={handleChangeSpy}
onBlur={handleBlurSpy} onBlur={handleBlurSpy}
suffix="USD" suffix="USD"
nativeCurrency="ETH"
currentCurrency="usd" currentCurrency="usd"
conversionRate={231.06} conversionRate={231.06}
useFiat useFiat

@ -20,12 +20,14 @@ describe('CurrencyInput container', () => {
metamask: { metamask: {
conversionRate: 280.45, conversionRate: 280.45,
currentCurrency: 'usd', currentCurrency: 'usd',
nativeCurrency: 'ETH',
}, },
} }
assert.deepEqual(mapStateToProps(mockState), { assert.deepEqual(mapStateToProps(mockState), {
conversionRate: 280.45, conversionRate: 280.45,
currentCurrency: 'usd', currentCurrency: 'usd',
nativeCurrency: 'ETH',
}) })
}) })
}) })
@ -35,12 +37,14 @@ describe('CurrencyInput container', () => {
const mockStateProps = { const mockStateProps = {
conversionRate: 280.45, conversionRate: 280.45,
currentCurrency: 'usd', currentCurrency: 'usd',
nativeCurrency: 'ETH',
} }
const mockDispatchProps = {} const mockDispatchProps = {}
assert.deepEqual(mergeProps(mockStateProps, mockDispatchProps, { useFiat: true }), { assert.deepEqual(mergeProps(mockStateProps, mockDispatchProps, { useFiat: true }), {
conversionRate: 280.45, conversionRate: 280.45,
currentCurrency: 'usd', currentCurrency: 'usd',
nativeCurrency: 'ETH',
useFiat: true, useFiat: true,
suffix: 'USD', suffix: 'USD',
}) })
@ -48,6 +52,7 @@ describe('CurrencyInput container', () => {
assert.deepEqual(mergeProps(mockStateProps, mockDispatchProps, {}), { assert.deepEqual(mergeProps(mockStateProps, mockDispatchProps, {}), {
conversionRate: 280.45, conversionRate: 280.45,
currentCurrency: 'usd', currentCurrency: 'usd',
nativeCurrency: 'ETH',
suffix: 'ETH', suffix: 'ETH',
}) })
}) })

@ -26,14 +26,14 @@ class AccountDropdowns extends Component {
} }
renderAccounts () { renderAccounts () {
const { identities, accounts, selected, menuItemStyles, actions, keyrings } = this.props const { identities, accounts, selected, menuItemStyles, actions, keyrings, ticker } = this.props
return Object.keys(identities).map((key, index) => { return Object.keys(identities).map((key, index) => {
const identity = identities[key] const identity = identities[key]
const isSelected = identity.address === selected const isSelected = identity.address === selected
const balanceValue = accounts[key].balance const balanceValue = accounts[key].balance
const formattedBalance = balanceValue ? formatBalance(balanceValue, 6) : '...' const formattedBalance = balanceValue ? formatBalance(balanceValue, 6, true, ticker) : '...'
const simpleAddress = identity.address.substring(2).toLowerCase() const simpleAddress = identity.address.substring(2).toLowerCase()
const keyring = keyrings.find((kr) => { const keyring = keyrings.find((kr) => {
@ -421,6 +421,7 @@ AccountDropdowns.propTypes = {
network: PropTypes.number, network: PropTypes.number,
// actions.showExportPrivateKeyModal: , // actions.showExportPrivateKeyModal: ,
style: PropTypes.object, style: PropTypes.object,
ticker: PropTypes.string,
enableAccountsSelector: PropTypes.bool, enableAccountsSelector: PropTypes.bool,
enableAccountOption: PropTypes.bool, enableAccountOption: PropTypes.bool,
enableAccountOptions: PropTypes.bool, enableAccountOptions: PropTypes.bool,
@ -458,6 +459,7 @@ const mapDispatchToProps = (dispatch) => {
function mapStateToProps (state) { function mapStateToProps (state) {
return { return {
ticker: state.metamask.ticker,
keyrings: state.metamask.keyrings, keyrings: state.metamask.keyrings,
sidebarOpen: state.appState.sidebar.isOpen, sidebarOpen: state.appState.sidebar.isOpen,
} }

@ -24,8 +24,9 @@ const notToggleElementClassnames = [
function mapStateToProps (state) { function mapStateToProps (state) {
return { return {
provider: state.metamask.provider, provider: state.metamask.provider,
frequentRpcList: state.metamask.frequentRpcList || [], frequentRpcListDetail: state.metamask.frequentRpcListDetail || [],
networkDropdownOpen: state.appState.networkDropdownOpen, networkDropdownOpen: state.appState.networkDropdownOpen,
network: state.metamask.network,
} }
} }
@ -40,8 +41,8 @@ function mapDispatchToProps (dispatch) {
setDefaultRpcTarget: type => { setDefaultRpcTarget: type => {
dispatch(actions.setDefaultRpcTarget(type)) dispatch(actions.setDefaultRpcTarget(type))
}, },
setRpcTarget: (target) => { setRpcTarget: (target, network, ticker, nickname) => {
dispatch(actions.setRpcTarget(target)) dispatch(actions.setRpcTarget(target, network, ticker, nickname))
}, },
delRpcTarget: (target) => { delRpcTarget: (target) => {
dispatch(actions.delRpcTarget(target)) dispatch(actions.delRpcTarget(target))
@ -71,7 +72,7 @@ module.exports = compose(
NetworkDropdown.prototype.render = function () { NetworkDropdown.prototype.render = function () {
const props = this.props const props = this.props
const { provider: { type: providerType, rpcTarget: activeNetwork } } = props const { provider: { type: providerType, rpcTarget: activeNetwork } } = props
const rpcList = props.frequentRpcList const rpcListDetail = props.frequentRpcListDetail
const isOpen = this.props.networkDropdownOpen const isOpen = this.props.networkDropdownOpen
const dropdownMenuItemStyle = { const dropdownMenuItemStyle = {
fontSize: '16px', fontSize: '16px',
@ -225,7 +226,7 @@ NetworkDropdown.prototype.render = function () {
), ),
this.renderCustomOption(props.provider), this.renderCustomOption(props.provider),
this.renderCommonRpc(rpcList, props.provider), this.renderCommonRpc(rpcListDetail, props.provider),
h( h(
DropdownMenuItem, DropdownMenuItem,
@ -267,28 +268,33 @@ NetworkDropdown.prototype.getNetworkName = function () {
} else if (providerName === 'rinkeby') { } else if (providerName === 'rinkeby') {
name = this.context.t('rinkeby') name = this.context.t('rinkeby')
} else { } else {
name = this.context.t('unknownNetwork') name = provider.nickname || this.context.t('unknownNetwork')
} }
return name return name
} }
NetworkDropdown.prototype.renderCommonRpc = function (rpcList, provider) { NetworkDropdown.prototype.renderCommonRpc = function (rpcListDetail, provider) {
const props = this.props const props = this.props
const reversedRpcList = rpcList.slice().reverse() const reversedRpcListDetail = rpcListDetail.slice().reverse()
const network = props.network
return reversedRpcList.map((rpc) => { return reversedRpcListDetail.map((entry) => {
const rpc = entry.rpcUrl
const ticker = entry.ticker || 'ETH'
const nickname = entry.nickname || ''
const currentRpcTarget = provider.type === 'rpc' && rpc === provider.rpcTarget const currentRpcTarget = provider.type === 'rpc' && rpc === provider.rpcTarget
if ((rpc === 'http://localhost:8545') || currentRpcTarget) { if ((rpc === 'http://localhost:8545') || currentRpcTarget) {
return null return null
} else { } else {
const chainId = entry.chainId || network
return h( return h(
DropdownMenuItem, DropdownMenuItem,
{ {
key: `common${rpc}`, key: `common${rpc}`,
closeMenu: () => this.props.hideNetworkDropdown(), closeMenu: () => this.props.hideNetworkDropdown(),
onClick: () => props.setRpcTarget(rpc), onClick: () => props.setRpcTarget(rpc, chainId, ticker, nickname),
style: { style: {
fontSize: '16px', fontSize: '16px',
lineHeight: '20px', lineHeight: '20px',
@ -302,7 +308,7 @@ NetworkDropdown.prototype.renderCommonRpc = function (rpcList, provider) {
style: { style: {
color: currentRpcTarget ? '#ffffff' : '#9b9b9b', color: currentRpcTarget ? '#ffffff' : '#9b9b9b',
}, },
}, rpc), }, nickname || rpc),
h('i.fa.fa-times.delete', h('i.fa.fa-times.delete',
{ {
onClick: (e) => { onClick: (e) => {
@ -317,8 +323,9 @@ NetworkDropdown.prototype.renderCommonRpc = function (rpcList, provider) {
} }
NetworkDropdown.prototype.renderCustomOption = function (provider) { NetworkDropdown.prototype.renderCustomOption = function (provider) {
const { rpcTarget, type } = provider const { rpcTarget, type, ticker, nickname } = provider
const props = this.props const props = this.props
const network = props.network
if (type !== 'rpc') return null if (type !== 'rpc') return null
@ -332,7 +339,7 @@ NetworkDropdown.prototype.renderCustomOption = function (provider) {
DropdownMenuItem, DropdownMenuItem,
{ {
key: rpcTarget, key: rpcTarget,
onClick: () => props.setRpcTarget(rpcTarget), onClick: () => props.setRpcTarget(rpcTarget, network, ticker, nickname),
closeMenu: () => this.props.hideNetworkDropdown(), closeMenu: () => this.props.hideNetworkDropdown(),
style: { style: {
fontSize: '16px', fontSize: '16px',
@ -347,7 +354,7 @@ NetworkDropdown.prototype.renderCustomOption = function (provider) {
style: { style: {
color: '#ffffff', color: '#ffffff',
}, },
}, rpcTarget), }, nickname || rpcTarget),
] ]
) )
} }

@ -45,8 +45,8 @@ describe('Network Dropdown', () => {
provider: { provider: {
'type': 'test', 'type': 'test',
}, },
frequentRpcList: [ frequentRpcListDetail: [
'http://localhost:7545', { rpcUrl: 'http://localhost:7545' },
], ],
}, },
appState: { appState: {

@ -1,5 +1,6 @@
const { Component } = require('react') const { Component } = require('react')
const h = require('react-hyperscript') const h = require('react-hyperscript')
const connect = require('react-redux').connect
const { inherits } = require('util') const { inherits } = require('util')
const { const {
formatBalance, formatBalance,
@ -8,7 +9,12 @@ const {
const Tooltip = require('./tooltip.js') const Tooltip = require('./tooltip.js')
const FiatValue = require('./fiat-value.js') const FiatValue = require('./fiat-value.js')
module.exports = EthBalanceComponent module.exports = connect(mapStateToProps)(EthBalanceComponent)
function mapStateToProps (state) {
return {
ticker: state.metamask.ticker,
}
}
inherits(EthBalanceComponent, Component) inherits(EthBalanceComponent, Component)
function EthBalanceComponent () { function EthBalanceComponent () {
@ -17,9 +23,9 @@ function EthBalanceComponent () {
EthBalanceComponent.prototype.render = function () { EthBalanceComponent.prototype.render = function () {
const props = this.props const props = this.props
const { value, style, width, needsParse = true } = props const { ticker, value, style, width, needsParse = true } = props
const formattedValue = value ? formatBalance(value, 6, needsParse) : '...' const formattedValue = value ? formatBalance(value, 6, needsParse, ticker) : '...'
return ( return (

@ -41,7 +41,7 @@ export default class NetworkDisplay extends Component {
} }
render () { render () {
const { network, provider: { type } } = this.props const { network, provider: { type, nickname } } = this.props
const networkClass = networkToClassHash[network] const networkClass = networkToClassHash[network]
return ( return (
@ -61,7 +61,7 @@ export default class NetworkDisplay extends Component {
/> />
} }
<div className="network-display__name"> <div className="network-display__name">
{ this.context.t(type) } { type === 'rpc' && nickname ? nickname : this.context.t(type) }
</div> </div>
</div> </div>
) )

@ -23,9 +23,10 @@ Network.prototype.render = function () {
const props = this.props const props = this.props
const context = this.context const context = this.context
const networkNumber = props.network const networkNumber = props.network
let providerName let providerName, providerNick
try { try {
providerName = props.provider.type providerName = props.provider.type
providerNick = props.provider.nickname || ''
} catch (e) { } catch (e) {
providerName = null providerName = null
} }
@ -131,7 +132,7 @@ Network.prototype.render = function () {
}, },
}), }),
h('.network-name', context.t('privateNetwork')), h('.network-name', providerNick || context.t('privateNetwork')),
h('i.fa.fa-chevron-down.fa-lg.network-caret'), h('i.fa.fa-chevron-down.fa-lg.network-caret'),
]) ])
} }

@ -5,12 +5,9 @@
color: $crimson; color: $crimson;
} }
&__rpc-save-button { &__advanced-link {
align-self: flex-end; color: $curious-blue;
padding: 5px; padding-left: 5px;
text-transform: uppercase;
color: $dusty-gray;
cursor: pointer;
} }
&__rpc-save-button { &__rpc-save-button {
@ -19,6 +16,9 @@
text-transform: uppercase; text-transform: uppercase;
color: $dusty-gray; color: $dusty-gray;
cursor: pointer; cursor: pointer;
width: 25%;
min-width: 80px;
height: 33px;
} }
&__button--red { &__button--red {

@ -55,12 +55,17 @@ export default class SettingsTab extends PureComponent {
sendHexData: PropTypes.bool, sendHexData: PropTypes.bool,
currentCurrency: PropTypes.string, currentCurrency: PropTypes.string,
conversionDate: PropTypes.number, conversionDate: PropTypes.number,
useETHAsPrimaryCurrency: PropTypes.bool, nativeCurrency: PropTypes.string,
setUseETHAsPrimaryCurrencyPreference: PropTypes.func, useNativeCurrencyAsPrimaryCurrency: PropTypes.bool,
setUseNativeCurrencyAsPrimaryCurrencyPreference: PropTypes.func,
} }
state = { state = {
newRpc: '', newRpc: '',
chainId: '',
showOptions: false,
ticker: '',
nickname: '',
} }
renderCurrentConversion () { renderCurrentConversion () {
@ -121,37 +126,98 @@ export default class SettingsTab extends PureComponent {
renderNewRpcUrl () { renderNewRpcUrl () {
const { t } = this.context const { t } = this.context
const { newRpc } = this.state const { newRpc, chainId, ticker, nickname } = this.state
return ( return (
<div className="settings-page__content-row"> <div className="settings-page__content-row">
<div className="settings-page__content-item"> <div className="settings-page__content-item">
<span>{ t('newRPC') }</span> <span>{ t('newNetwork') }</span>
</div> </div>
<div className="settings-page__content-item"> <div className="settings-page__content-item">
<div className="settings-page__content-item-col"> <div className="settings-page__content-item-col">
<TextField <TextField
type="text" type="text"
id="new-rpc" id="new-rpc"
placeholder={t('newRPC')} placeholder={t('rpcURL')}
value={newRpc} value={newRpc}
onChange={e => this.setState({ newRpc: e.target.value })} onChange={e => this.setState({ newRpc: e.target.value })}
onKeyPress={e => { onKeyPress={e => {
if (e.key === 'Enter') { if (e.key === 'Enter') {
this.validateRpc(newRpc) this.validateRpc(newRpc, chainId, ticker, nickname)
} }
}} }}
fullWidth fullWidth
margin="none" margin="dense"
/> />
<div <TextField
className="settings-tab__rpc-save-button" type="text"
onClick={e => { id="chainid"
e.preventDefault() placeholder={t('optionalChainId')}
this.validateRpc(newRpc) value={chainId}
onChange={e => this.setState({ chainId: e.target.value })}
onKeyPress={e => {
if (e.key === 'Enter') {
this.validateRpc(newRpc, chainId, ticker, nickname)
}
}} }}
> style={{
{ t('save') } display: this.state.showOptions ? null : 'none',
}}
fullWidth
margin="dense"
/>
<TextField
type="text"
id="ticker"
placeholder={t('optionalSymbol')}
value={ticker}
onChange={e => this.setState({ ticker: e.target.value })}
onKeyPress={e => {
if (e.key === 'Enter') {
this.validateRpc(newRpc, chainId, ticker, nickname)
}
}}
style={{
display: this.state.showOptions ? null : 'none',
}}
fullWidth
margin="dense"
/>
<TextField
type="text"
id="nickname"
placeholder={t('optionalNickname')}
value={nickname}
onChange={e => this.setState({ nickname: e.target.value })}
onKeyPress={e => {
if (e.key === 'Enter') {
this.validateRpc(newRpc, chainId, ticker, nickname)
}
}}
style={{
display: this.state.showOptions ? null : 'none',
}}
fullWidth
margin="dense"
/>
<div className="flex-row flex-align-center space-between">
<span className="settings-tab__advanced-link"
onClick={e => {
e.preventDefault()
this.setState({ showOptions: !this.state.showOptions })
}}
>
{ t(this.state.showOptions ? 'hideAdvancedOptions' : 'showAdvancedOptions') }
</span>
<button
className="button btn-primary settings-tab__rpc-save-button"
onClick={e => {
e.preventDefault()
this.validateRpc(newRpc, chainId, ticker, nickname)
}}
>
{ t('save') }
</button>
</div> </div>
</div> </div>
</div> </div>
@ -159,11 +225,11 @@ export default class SettingsTab extends PureComponent {
) )
} }
validateRpc (newRpc) { validateRpc (newRpc, chainId, ticker = 'ETH', nickname) {
const { setRpcTarget, displayWarning } = this.props const { setRpcTarget, displayWarning } = this.props
if (validUrl.isWebUri(newRpc)) { if (validUrl.isWebUri(newRpc)) {
setRpcTarget(newRpc) setRpcTarget(newRpc, chainId, ticker, nickname)
} else { } else {
const appendedRpc = `http://${newRpc}` const appendedRpc = `http://${newRpc}`
@ -341,9 +407,13 @@ export default class SettingsTab extends PureComponent {
) )
} }
renderUseEthAsPrimaryCurrency () { renderUsePrimaryCurrencyOptions () {
const { t } = this.context const { t } = this.context
const { useETHAsPrimaryCurrency, setUseETHAsPrimaryCurrencyPreference } = this.props const {
nativeCurrency,
setUseNativeCurrencyAsPrimaryCurrencyPreference,
useNativeCurrencyAsPrimaryCurrency,
} = this.props
return ( return (
<div className="settings-page__content-row"> <div className="settings-page__content-row">
@ -359,23 +429,23 @@ export default class SettingsTab extends PureComponent {
<div className="settings-tab__radio-button"> <div className="settings-tab__radio-button">
<input <input
type="radio" type="radio"
id="eth-primary-currency" id="native-primary-currency"
onChange={() => setUseETHAsPrimaryCurrencyPreference(true)} onChange={() => setUseNativeCurrencyAsPrimaryCurrencyPreference(true)}
checked={Boolean(useETHAsPrimaryCurrency)} checked={Boolean(useNativeCurrencyAsPrimaryCurrency)}
/> />
<label <label
htmlFor="eth-primary-currency" htmlFor="native-primary-currency"
className="settings-tab__radio-label" className="settings-tab__radio-label"
> >
{ t('eth') } { nativeCurrency }
</label> </label>
</div> </div>
<div className="settings-tab__radio-button"> <div className="settings-tab__radio-button">
<input <input
type="radio" type="radio"
id="fiat-primary-currency" id="fiat-primary-currency"
onChange={() => setUseETHAsPrimaryCurrencyPreference(false)} onChange={() => setUseNativeCurrencyAsPrimaryCurrencyPreference(false)}
checked={!useETHAsPrimaryCurrency} checked={!useNativeCurrencyAsPrimaryCurrency}
/> />
<label <label
htmlFor="fiat-primary-currency" htmlFor="fiat-primary-currency"
@ -398,7 +468,7 @@ export default class SettingsTab extends PureComponent {
<div className="settings-page__content"> <div className="settings-page__content">
{ warning && <div className="settings-tab__error">{ warning }</div> } { warning && <div className="settings-tab__error">{ warning }</div> }
{ this.renderCurrentConversion() } { this.renderCurrentConversion() }
{ this.renderUseEthAsPrimaryCurrency() } { this.renderUsePrimaryCurrencyOptions() }
{ this.renderCurrentLocale() } { this.renderCurrentLocale() }
{ this.renderNewRpcUrl() } { this.renderNewRpcUrl() }
{ this.renderStateLogs() } { this.renderStateLogs() }

@ -11,7 +11,7 @@ import {
updateCurrentLocale, updateCurrentLocale,
setFeatureFlag, setFeatureFlag,
showModal, showModal,
setUseETHAsPrimaryCurrencyPreference, setUseNativeCurrencyAsPrimaryCurrencyPreference,
} from '../../../../actions' } from '../../../../actions'
import { preferencesSelector } from '../../../../selectors' import { preferencesSelector } from '../../../../selectors'
@ -20,13 +20,14 @@ const mapStateToProps = state => {
const { const {
currentCurrency, currentCurrency,
conversionDate, conversionDate,
nativeCurrency,
useBlockie, useBlockie,
featureFlags: { sendHexData } = {}, featureFlags: { sendHexData } = {},
provider = {}, provider = {},
isMascara, isMascara,
currentLocale, currentLocale,
} = metamask } = metamask
const { useETHAsPrimaryCurrency } = preferencesSelector(state) const { useNativeCurrencyAsPrimaryCurrency } = preferencesSelector(state)
return { return {
warning, warning,
@ -34,17 +35,18 @@ const mapStateToProps = state => {
currentLocale, currentLocale,
currentCurrency, currentCurrency,
conversionDate, conversionDate,
nativeCurrency,
useBlockie, useBlockie,
sendHexData, sendHexData,
provider, provider,
useETHAsPrimaryCurrency, useNativeCurrencyAsPrimaryCurrency,
} }
} }
const mapDispatchToProps = dispatch => { const mapDispatchToProps = dispatch => {
return { return {
setCurrentCurrency: currency => dispatch(setCurrentCurrency(currency)), setCurrentCurrency: currency => dispatch(setCurrentCurrency(currency)),
setRpcTarget: newRpc => dispatch(setRpcTarget(newRpc)), setRpcTarget: (newRpc, chainId, ticker, nickname) => dispatch(setRpcTarget(newRpc, chainId, ticker, nickname)),
displayWarning: warning => dispatch(displayWarning(warning)), displayWarning: warning => dispatch(displayWarning(warning)),
revealSeedConfirmation: () => dispatch(revealSeedConfirmation()), revealSeedConfirmation: () => dispatch(revealSeedConfirmation()),
setUseBlockie: value => dispatch(setUseBlockie(value)), setUseBlockie: value => dispatch(setUseBlockie(value)),
@ -54,8 +56,8 @@ const mapDispatchToProps = dispatch => {
}, },
setHexDataFeatureFlag: shouldShow => dispatch(setFeatureFlag('sendHexData', shouldShow)), setHexDataFeatureFlag: shouldShow => dispatch(setFeatureFlag('sendHexData', shouldShow)),
showResetAccountConfirmationModal: () => dispatch(showModal({ name: 'CONFIRM_RESET_ACCOUNT' })), showResetAccountConfirmationModal: () => dispatch(showModal({ name: 'CONFIRM_RESET_ACCOUNT' })),
setUseETHAsPrimaryCurrencyPreference: value => { setUseNativeCurrencyAsPrimaryCurrencyPreference: value => {
return dispatch(setUseETHAsPrimaryCurrencyPreference(value)) return dispatch(setUseNativeCurrencyAsPrimaryCurrencyPreference(value))
}, },
} }
} }

@ -2,6 +2,7 @@ import { connect } from 'react-redux'
import { import {
getConversionRate, getConversionRate,
getCurrentCurrency, getCurrentCurrency,
getNativeCurrency,
} from '../send.selectors.js' } from '../send.selectors.js'
import AccountListItem from './account-list-item.component' import AccountListItem from './account-list-item.component'
@ -11,5 +12,6 @@ function mapStateToProps (state) {
return { return {
conversionRate: getConversionRate(state), conversionRate: getConversionRate(state),
currentCurrency: getCurrentCurrency(state), currentCurrency: getCurrentCurrency(state),
nativeCurrency: getNativeCurrency(state),
} }
} }

@ -28,6 +28,7 @@ describe('AccountListItem Component', function () {
className={'mockClassName'} className={'mockClassName'}
conversionRate={4} conversionRate={4}
currentCurrency={'mockCurrentyCurrency'} currentCurrency={'mockCurrentyCurrency'}
nativeCurrency={'ETH'}
displayAddress={false} displayAddress={false}
displayBalance={false} displayBalance={false}
handleClick={propsMethodSpies.handleClick} handleClick={propsMethodSpies.handleClick}

@ -13,6 +13,7 @@ proxyquire('../account-list-item.container.js', {
'../send.selectors.js': { '../send.selectors.js': {
getConversionRate: (s) => `mockConversionRate:${s}`, getConversionRate: (s) => `mockConversionRate:${s}`,
getCurrentCurrency: (s) => `mockCurrentCurrency:${s}`, getCurrentCurrency: (s) => `mockCurrentCurrency:${s}`,
getNativeCurrency: (s) => `mockNativeCurrency:${s}`,
}, },
}) })
@ -24,6 +25,7 @@ describe('account-list-item container', () => {
assert.deepEqual(mapStateToProps('mockState'), { assert.deepEqual(mapStateToProps('mockState'), {
conversionRate: 'mockConversionRate:mockState', conversionRate: 'mockConversionRate:mockState',
currentCurrency: 'mockCurrentCurrency:mockState', currentCurrency: 'mockCurrentCurrency:mockState',
nativeCurrency: 'mockNativeCurrency:mockState',
}) })
}) })

@ -19,6 +19,7 @@ const selectors = {
getCurrentNetwork, getCurrentNetwork,
getCurrentViewContext, getCurrentViewContext,
getForceGasMin, getForceGasMin,
getNativeCurrency,
getGasLimit, getGasLimit,
getGasPrice, getGasPrice,
getGasPriceFromRecentBlocks, getGasPriceFromRecentBlocks,
@ -111,6 +112,10 @@ function getCurrentCurrency (state) {
return state.metamask.currentCurrency return state.metamask.currentCurrency
} }
function getNativeCurrency (state) {
return state.metamask.nativeCurrency
}
function getCurrentNetwork (state) { function getCurrentNetwork (state) {
return state.metamask.network return state.metamask.network
} }

@ -26,6 +26,7 @@ module.exports = {
'currentCurrency': 'USD', 'currentCurrency': 'USD',
'conversionRate': 1200.88200327, 'conversionRate': 1200.88200327,
'conversionDate': 1489013762, 'conversionDate': 1489013762,
'nativeCurrency': 'ETH',
'noActiveNotices': true, 'noActiveNotices': true,
'frequentRpcList': [], 'frequentRpcList': [],
'network': '3', 'network': '3',

@ -12,6 +12,7 @@ const {
getCurrentCurrency, getCurrentCurrency,
getCurrentNetwork, getCurrentNetwork,
getCurrentViewContext, getCurrentViewContext,
getNativeCurrency,
getForceGasMin, getForceGasMin,
getGasLimit, getGasLimit,
getGasPrice, getGasPrice,
@ -178,6 +179,15 @@ describe('send selectors', () => {
}) })
}) })
describe('getNativeCurrency()', () => {
it('should return the ticker symbol of the selected network', () => {
assert.equal(
getNativeCurrency(mockState),
'ETH'
)
})
})
describe('getCurrentNetwork()', () => { describe('getCurrentNetwork()', () => {
it('should return the id of the currently selected network', () => { it('should return the id of the currently selected network', () => {
assert.equal( assert.equal(

@ -18,10 +18,11 @@ describe('TransactionActivityLog container', () => {
const mockState = { const mockState = {
metamask: { metamask: {
conversionRate: 280.45, conversionRate: 280.45,
nativeCurrency: 'ETH',
}, },
} }
assert.deepEqual(mapStateToProps(mockState), { conversionRate: 280.45 }) assert.deepEqual(mapStateToProps(mockState), { conversionRate: 280.45, nativeCurrency: 'ETH' })
}) })
}) })
}) })

@ -4,7 +4,6 @@ import classnames from 'classnames'
import { getActivities } from './transaction-activity-log.util' import { getActivities } from './transaction-activity-log.util'
import Card from '../card' import Card from '../card'
import { getEthConversionFromWeiHex, getValueFromWeiHex } from '../../helpers/conversions.util' import { getEthConversionFromWeiHex, getValueFromWeiHex } from '../../helpers/conversions.util'
import { ETH } from '../../constants/common'
import { formatDate } from '../../util' import { formatDate } from '../../util'
export default class TransactionActivityLog extends PureComponent { export default class TransactionActivityLog extends PureComponent {
@ -16,6 +15,7 @@ export default class TransactionActivityLog extends PureComponent {
transaction: PropTypes.object, transaction: PropTypes.object,
className: PropTypes.string, className: PropTypes.string,
conversionRate: PropTypes.number, conversionRate: PropTypes.number,
nativeCurrency: PropTypes.string,
} }
state = { state = {
@ -45,16 +45,17 @@ export default class TransactionActivityLog extends PureComponent {
} }
renderActivity (activity, index) { renderActivity (activity, index) {
const { conversionRate } = this.props const { conversionRate, nativeCurrency } = this.props
const { eventKey, value, timestamp } = activity const { eventKey, value, timestamp } = activity
const ethValue = index === 0 const ethValue = index === 0
? `${getValueFromWeiHex({ ? `${getValueFromWeiHex({
value, value,
toCurrency: ETH, fromCurrency: nativeCurrency,
toCurrency: nativeCurrency,
conversionRate, conversionRate,
numberOfDecimals: 6, numberOfDecimals: 6,
})} ${ETH}` })} ${nativeCurrency}`
: getEthConversionFromWeiHex({ value, toCurrency: ETH, conversionRate }) : getEthConversionFromWeiHex({ value, fromCurrency: nativeCurrency, conversionRate })
const formattedTimestamp = formatDate(timestamp) const formattedTimestamp = formatDate(timestamp)
const activityText = this.context.t(eventKey, [ethValue, formattedTimestamp]) const activityText = this.context.t(eventKey, [ethValue, formattedTimestamp])

@ -1,10 +1,11 @@
import { connect } from 'react-redux' import { connect } from 'react-redux'
import TransactionActivityLog from './transaction-activity-log.component' import TransactionActivityLog from './transaction-activity-log.component'
import { conversionRateSelector } from '../../selectors' import { conversionRateSelector, getNativeCurrency } from '../../selectors'
const mapStateToProps = state => { const mapStateToProps = state => {
return { return {
conversionRate: conversionRateSelector(state), conversionRate: conversionRateSelector(state),
nativeCurrency: getNativeCurrency(state),
} }
} }

@ -1 +1 @@
export { default } from './transaction-breakdown.component' export { default } from './transaction-breakdown.container'

@ -6,7 +6,7 @@ import Card from '../card'
import CurrencyDisplay from '../currency-display' import CurrencyDisplay from '../currency-display'
import UserPreferencedCurrencyDisplay from '../user-preferenced-currency-display' import UserPreferencedCurrencyDisplay from '../user-preferenced-currency-display'
import HexToDecimal from '../hex-to-decimal' import HexToDecimal from '../hex-to-decimal'
import { ETH, GWEI, PRIMARY, SECONDARY } from '../../constants/common' import { GWEI, PRIMARY, SECONDARY } from '../../constants/common'
import { getHexGasTotal } from '../../helpers/confirm-transaction/util' import { getHexGasTotal } from '../../helpers/confirm-transaction/util'
import { sumHexes } from '../../helpers/transactions.util' import { sumHexes } from '../../helpers/transactions.util'
@ -18,6 +18,7 @@ export default class TransactionBreakdown extends PureComponent {
static propTypes = { static propTypes = {
transaction: PropTypes.object, transaction: PropTypes.object,
className: PropTypes.string, className: PropTypes.string,
nativeCurrency: PropTypes.string.isRequired,
} }
static defaultProps = { static defaultProps = {
@ -26,7 +27,7 @@ export default class TransactionBreakdown extends PureComponent {
render () { render () {
const { t } = this.context const { t } = this.context
const { transaction, className } = this.props const { transaction, className, nativeCurrency } = this.props
const { txParams: { gas, gasPrice, value } = {}, txReceipt: { gasUsed } = {} } = transaction const { txParams: { gas, gasPrice, value } = {}, txReceipt: { gasUsed } = {} } = transaction
const gasLimit = typeof gasUsed === 'string' ? gasUsed : gas const gasLimit = typeof gasUsed === 'string' ? gasUsed : gas
@ -72,7 +73,7 @@ export default class TransactionBreakdown extends PureComponent {
<TransactionBreakdownRow title={t('gasPrice')}> <TransactionBreakdownRow title={t('gasPrice')}>
<CurrencyDisplay <CurrencyDisplay
className="transaction-breakdown__value" className="transaction-breakdown__value"
currency={ETH} currency={nativeCurrency}
denomination={GWEI} denomination={GWEI}
value={gasPrice} value={gasPrice}
hideLabel hideLabel

@ -0,0 +1,11 @@
import { connect } from 'react-redux'
import TransactionBreakdown from './transaction-breakdown.component'
import { getNativeCurrency } from '../../selectors'
const mapStateToProps = (state) => {
return {
nativeCurrency: getNativeCurrency(state),
}
}
export default connect(mapStateToProps)(TransactionBreakdown)

@ -2,7 +2,7 @@ import { connect } from 'react-redux'
import { withRouter } from 'react-router-dom' import { withRouter } from 'react-router-dom'
import { compose } from 'recompose' import { compose } from 'recompose'
import TransactionViewBalance from './transaction-view-balance.component' import TransactionViewBalance from './transaction-view-balance.component'
import { getSelectedToken, getSelectedAddress, getSelectedTokenAssetImage } from '../../selectors' import { getSelectedToken, getSelectedAddress, getNativeCurrency, getSelectedTokenAssetImage } from '../../selectors'
import { showModal } from '../../actions' import { showModal } from '../../actions'
const mapStateToProps = state => { const mapStateToProps = state => {
@ -15,6 +15,7 @@ const mapStateToProps = state => {
selectedToken: getSelectedToken(state), selectedToken: getSelectedToken(state),
network, network,
balance, balance,
nativeCurrency: getNativeCurrency(state),
assetImage: getSelectedTokenAssetImage(state), assetImage: getSelectedTokenAssetImage(state),
} }
} }

@ -18,14 +18,16 @@ describe('UserPreferencedCurrencyDisplay container', () => {
it('should return the correct props', () => { it('should return the correct props', () => {
const mockState = { const mockState = {
metamask: { metamask: {
nativeCurrency: 'ETH',
preferences: { preferences: {
useETHAsPrimaryCurrency: true, useNativeCurrencyAsPrimaryCurrency: true,
}, },
}, },
} }
assert.deepEqual(mapStateToProps(mockState), { assert.deepEqual(mapStateToProps(mockState), {
useETHAsPrimaryCurrency: true, nativeCurrency: 'ETH',
useNativeCurrencyAsPrimaryCurrency: true,
}) })
}) })
}) })
@ -37,33 +39,38 @@ describe('UserPreferencedCurrencyDisplay container', () => {
const tests = [ const tests = [
{ {
stateProps: { stateProps: {
useETHAsPrimaryCurrency: true, useNativeCurrencyAsPrimaryCurrency: true,
nativeCurrency: 'ETH',
}, },
ownProps: { ownProps: {
type: 'PRIMARY', type: 'PRIMARY',
}, },
result: { result: {
currency: 'ETH', currency: 'ETH',
nativeCurrency: 'ETH',
numberOfDecimals: 6, numberOfDecimals: 6,
prefix: undefined, prefix: undefined,
}, },
}, },
{ {
stateProps: { stateProps: {
useETHAsPrimaryCurrency: false, useNativeCurrencyAsPrimaryCurrency: false,
nativeCurrency: 'ETH',
}, },
ownProps: { ownProps: {
type: 'PRIMARY', type: 'PRIMARY',
}, },
result: { result: {
currency: undefined, currency: undefined,
nativeCurrency: 'ETH',
numberOfDecimals: 2, numberOfDecimals: 2,
prefix: undefined, prefix: undefined,
}, },
}, },
{ {
stateProps: { stateProps: {
useETHAsPrimaryCurrency: true, useNativeCurrencyAsPrimaryCurrency: true,
nativeCurrency: 'ETH',
}, },
ownProps: { ownProps: {
type: 'SECONDARY', type: 'SECONDARY',
@ -71,6 +78,7 @@ describe('UserPreferencedCurrencyDisplay container', () => {
fiatPrefix: '-', fiatPrefix: '-',
}, },
result: { result: {
nativeCurrency: 'ETH',
currency: undefined, currency: undefined,
numberOfDecimals: 4, numberOfDecimals: 4,
prefix: '-', prefix: '-',
@ -78,7 +86,8 @@ describe('UserPreferencedCurrencyDisplay container', () => {
}, },
{ {
stateProps: { stateProps: {
useETHAsPrimaryCurrency: false, useNativeCurrencyAsPrimaryCurrency: false,
nativeCurrency: 'ETH',
}, },
ownProps: { ownProps: {
type: 'SECONDARY', type: 'SECONDARY',
@ -89,6 +98,7 @@ describe('UserPreferencedCurrencyDisplay container', () => {
}, },
result: { result: {
currency: 'ETH', currency: 'ETH',
nativeCurrency: 'ETH',
numberOfDecimals: 3, numberOfDecimals: 3,
prefix: 'b', prefix: 'b',
}, },

@ -21,6 +21,7 @@ export default class UserPreferencedCurrencyDisplay extends PureComponent {
fiatPrefix: PropTypes.string, fiatPrefix: PropTypes.string,
// From container // From container
currency: PropTypes.string, currency: PropTypes.string,
nativeCurrency: PropTypes.string,
} }
renderEthLogo () { renderEthLogo () {

@ -4,15 +4,16 @@ import { preferencesSelector } from '../../selectors'
import { ETH, PRIMARY, SECONDARY } from '../../constants/common' import { ETH, PRIMARY, SECONDARY } from '../../constants/common'
const mapStateToProps = (state, ownProps) => { const mapStateToProps = (state, ownProps) => {
const { useETHAsPrimaryCurrency } = preferencesSelector(state) const { useNativeCurrencyAsPrimaryCurrency } = preferencesSelector(state)
return { return {
useETHAsPrimaryCurrency, useNativeCurrencyAsPrimaryCurrency,
nativeCurrency: state.metamask.nativeCurrency,
} }
} }
const mergeProps = (stateProps, dispatchProps, ownProps) => { const mergeProps = (stateProps, dispatchProps, ownProps) => {
const { useETHAsPrimaryCurrency, ...restStateProps } = stateProps const { useNativeCurrencyAsPrimaryCurrency, nativeCurrency, ...restStateProps } = stateProps
const { const {
type, type,
numberOfDecimals: propsNumberOfDecimals, numberOfDecimals: propsNumberOfDecimals,
@ -26,14 +27,14 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => {
let currency, numberOfDecimals, prefix let currency, numberOfDecimals, prefix
if (type === PRIMARY && useETHAsPrimaryCurrency || if (type === PRIMARY && useNativeCurrencyAsPrimaryCurrency ||
type === SECONDARY && !useETHAsPrimaryCurrency) { type === SECONDARY && !useNativeCurrencyAsPrimaryCurrency) {
// Display ETH // Display ETH
currency = ETH currency = nativeCurrency || ETH
numberOfDecimals = propsNumberOfDecimals || ethNumberOfDecimals || 6 numberOfDecimals = propsNumberOfDecimals || ethNumberOfDecimals || 6
prefix = propsPrefix || ethPrefix prefix = propsPrefix || ethPrefix
} else if (type === SECONDARY && useETHAsPrimaryCurrency || } else if (type === SECONDARY && useNativeCurrencyAsPrimaryCurrency ||
type === PRIMARY && !useETHAsPrimaryCurrency) { type === PRIMARY && !useNativeCurrencyAsPrimaryCurrency) {
// Display Fiat // Display Fiat
numberOfDecimals = propsNumberOfDecimals || fiatNumberOfDecimals || 2 numberOfDecimals = propsNumberOfDecimals || fiatNumberOfDecimals || 2
prefix = propsPrefix || fiatPrefix prefix = propsPrefix || fiatPrefix
@ -43,6 +44,7 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => {
...restStateProps, ...restStateProps,
...dispatchProps, ...dispatchProps,
...restOwnProps, ...restOwnProps,
nativeCurrency,
currency, currency,
numberOfDecimals, numberOfDecimals,
prefix, prefix,

@ -15,17 +15,17 @@ describe('UserPreferencedCurrencyInput Component', () => {
assert.equal(wrapper.find(CurrencyInput).length, 1) assert.equal(wrapper.find(CurrencyInput).length, 1)
}) })
it('should render useFiat for CurrencyInput based on preferences.useETHAsPrimaryCurrency', () => { it('should render useFiat for CurrencyInput based on preferences.useNativeCurrencyAsPrimaryCurrency', () => {
const wrapper = shallow( const wrapper = shallow(
<UserPreferencedCurrencyInput <UserPreferencedCurrencyInput
useETHAsPrimaryCurrency useNativeCurrencyAsPrimaryCurrency
/> />
) )
assert.ok(wrapper) assert.ok(wrapper)
assert.equal(wrapper.find(CurrencyInput).length, 1) assert.equal(wrapper.find(CurrencyInput).length, 1)
assert.equal(wrapper.find(CurrencyInput).props().useFiat, false) assert.equal(wrapper.find(CurrencyInput).props().useFiat, false)
wrapper.setProps({ useETHAsPrimaryCurrency: false }) wrapper.setProps({ useNativeCurrencyAsPrimaryCurrency: false })
assert.equal(wrapper.find(CurrencyInput).props().useFiat, true) assert.equal(wrapper.find(CurrencyInput).props().useFiat, true)
}) })
}) })

@ -18,13 +18,13 @@ describe('UserPreferencedCurrencyInput container', () => {
const mockState = { const mockState = {
metamask: { metamask: {
preferences: { preferences: {
useETHAsPrimaryCurrency: true, useNativeCurrencyAsPrimaryCurrency: true,
}, },
}, },
} }
assert.deepEqual(mapStateToProps(mockState), { assert.deepEqual(mapStateToProps(mockState), {
useETHAsPrimaryCurrency: true, useNativeCurrencyAsPrimaryCurrency: true,
}) })
}) })
}) })

@ -4,16 +4,16 @@ import CurrencyInput from '../currency-input'
export default class UserPreferencedCurrencyInput extends PureComponent { export default class UserPreferencedCurrencyInput extends PureComponent {
static propTypes = { static propTypes = {
useETHAsPrimaryCurrency: PropTypes.bool, useNativeCurrencyAsPrimaryCurrency: PropTypes.bool,
} }
render () { render () {
const { useETHAsPrimaryCurrency, ...restProps } = this.props const { useNativeCurrencyAsPrimaryCurrency, ...restProps } = this.props
return ( return (
<CurrencyInput <CurrencyInput
{...restProps} {...restProps}
useFiat={!useETHAsPrimaryCurrency} useFiat={!useNativeCurrencyAsPrimaryCurrency}
/> />
) )
} }

@ -3,10 +3,10 @@ import UserPreferencedCurrencyInput from './user-preferenced-currency-input.comp
import { preferencesSelector } from '../../selectors' import { preferencesSelector } from '../../selectors'
const mapStateToProps = state => { const mapStateToProps = state => {
const { useETHAsPrimaryCurrency } = preferencesSelector(state) const { useNativeCurrencyAsPrimaryCurrency } = preferencesSelector(state)
return { return {
useETHAsPrimaryCurrency, useNativeCurrencyAsPrimaryCurrency,
} }
} }

@ -15,17 +15,17 @@ describe('UserPreferencedCurrencyInput Component', () => {
assert.equal(wrapper.find(TokenInput).length, 1) assert.equal(wrapper.find(TokenInput).length, 1)
}) })
it('should render showFiat for TokenInput based on preferences.useETHAsPrimaryCurrency', () => { it('should render showFiat for TokenInput based on preferences.useNativeCurrencyAsPrimaryCurrency', () => {
const wrapper = shallow( const wrapper = shallow(
<UserPreferencedTokenInput <UserPreferencedTokenInput
useETHAsPrimaryCurrency useNativeCurrencyAsPrimaryCurrency
/> />
) )
assert.ok(wrapper) assert.ok(wrapper)
assert.equal(wrapper.find(TokenInput).length, 1) assert.equal(wrapper.find(TokenInput).length, 1)
assert.equal(wrapper.find(TokenInput).props().showFiat, false) assert.equal(wrapper.find(TokenInput).props().showFiat, false)
wrapper.setProps({ useETHAsPrimaryCurrency: false }) wrapper.setProps({ useNativeCurrencyAsPrimaryCurrency: false })
assert.equal(wrapper.find(TokenInput).props().showFiat, true) assert.equal(wrapper.find(TokenInput).props().showFiat, true)
}) })
}) })

@ -18,13 +18,13 @@ describe('UserPreferencedTokenInput container', () => {
const mockState = { const mockState = {
metamask: { metamask: {
preferences: { preferences: {
useETHAsPrimaryCurrency: true, useNativeCurrencyAsPrimaryCurrency: true,
}, },
}, },
} }
assert.deepEqual(mapStateToProps(mockState), { assert.deepEqual(mapStateToProps(mockState), {
useETHAsPrimaryCurrency: true, useNativeCurrencyAsPrimaryCurrency: true,
}) })
}) })
}) })

@ -4,16 +4,16 @@ import TokenInput from '../token-input'
export default class UserPreferencedTokenInput extends PureComponent { export default class UserPreferencedTokenInput extends PureComponent {
static propTypes = { static propTypes = {
useETHAsPrimaryCurrency: PropTypes.bool, useNativeCurrencyAsPrimaryCurrency: PropTypes.bool,
} }
render () { render () {
const { useETHAsPrimaryCurrency, ...restProps } = this.props const { useNativeCurrencyAsPrimaryCurrency, ...restProps } = this.props
return ( return (
<TokenInput <TokenInput
{...restProps} {...restProps}
showFiat={!useETHAsPrimaryCurrency} showFiat={!useNativeCurrencyAsPrimaryCurrency}
/> />
) )
} }

@ -3,10 +3,10 @@ import UserPreferencedTokenInput from './user-preferenced-token-input.component'
import { preferencesSelector } from '../../selectors' import { preferencesSelector } from '../../selectors'
const mapStateToProps = state => { const mapStateToProps = state => {
const { useETHAsPrimaryCurrency } = preferencesSelector(state) const { useNativeCurrencyAsPrimaryCurrency } = preferencesSelector(state)
return { return {
useETHAsPrimaryCurrency, useNativeCurrencyAsPrimaryCurrency,
} }
} }

@ -2,6 +2,7 @@ import {
conversionRateSelector, conversionRateSelector,
currentCurrencySelector, currentCurrencySelector,
unconfirmedTransactionsHashSelector, unconfirmedTransactionsHashSelector,
getNativeCurrency,
} from '../selectors/confirm-transaction' } from '../selectors/confirm-transaction'
import { import {
@ -292,16 +293,17 @@ export function updateTxDataAndCalculate (txData) {
const state = getState() const state = getState()
const currentCurrency = currentCurrencySelector(state) const currentCurrency = currentCurrencySelector(state)
const conversionRate = conversionRateSelector(state) const conversionRate = conversionRateSelector(state)
const nativeCurrency = getNativeCurrency(state)
dispatch(updateTxData(txData)) dispatch(updateTxData(txData))
const { txParams: { value = '0x0', gas: gasLimit = '0x0', gasPrice = '0x0' } = {} } = txData const { txParams: { value = '0x0', gas: gasLimit = '0x0', gasPrice = '0x0' } = {} } = txData
const fiatTransactionAmount = getValueFromWeiHex({ const fiatTransactionAmount = getValueFromWeiHex({
value, toCurrency: currentCurrency, conversionRate, numberOfDecimals: 2, value, fromCurrency: nativeCurrency, toCurrency: currentCurrency, conversionRate, numberOfDecimals: 2,
}) })
const ethTransactionAmount = getValueFromWeiHex({ const ethTransactionAmount = getValueFromWeiHex({
value, toCurrency: 'ETH', conversionRate, numberOfDecimals: 6, value, fromCurrency: nativeCurrency, toCurrency: nativeCurrency, conversionRate, numberOfDecimals: 6,
}) })
dispatch(updateTransactionAmounts({ dispatch(updateTransactionAmounts({
@ -314,13 +316,15 @@ export function updateTxDataAndCalculate (txData) {
const fiatTransactionFee = getTransactionFee({ const fiatTransactionFee = getTransactionFee({
value: hexTransactionFee, value: hexTransactionFee,
fromCurrency: nativeCurrency,
toCurrency: currentCurrency, toCurrency: currentCurrency,
numberOfDecimals: 2, numberOfDecimals: 2,
conversionRate, conversionRate,
}) })
const ethTransactionFee = getTransactionFee({ const ethTransactionFee = getTransactionFee({
value: hexTransactionFee, value: hexTransactionFee,
toCurrency: 'ETH', fromCurrency: nativeCurrency,
toCurrency: nativeCurrency,
numberOfDecimals: 6, numberOfDecimals: 6,
conversionRate, conversionRate,
}) })

@ -55,6 +55,7 @@ export function addFiat (...args) {
export function getValueFromWeiHex ({ export function getValueFromWeiHex ({
value, value,
fromCurrency = 'ETH',
toCurrency, toCurrency,
conversionRate, conversionRate,
numberOfDecimals, numberOfDecimals,
@ -63,7 +64,7 @@ export function getValueFromWeiHex ({
return conversionUtil(value, { return conversionUtil(value, {
fromNumericBase: 'hex', fromNumericBase: 'hex',
toNumericBase: 'dec', toNumericBase: 'dec',
fromCurrency: 'ETH', fromCurrency,
toCurrency, toCurrency,
numberOfDecimals, numberOfDecimals,
fromDenomination: 'WEI', fromDenomination: 'WEI',
@ -74,6 +75,7 @@ export function getValueFromWeiHex ({
export function getTransactionFee ({ export function getTransactionFee ({
value, value,
fromCurrency = 'ETH',
toCurrency, toCurrency,
conversionRate, conversionRate,
numberOfDecimals, numberOfDecimals,
@ -82,7 +84,7 @@ export function getTransactionFee ({
fromNumericBase: 'BN', fromNumericBase: 'BN',
toNumericBase: 'dec', toNumericBase: 'dec',
fromDenomination: 'WEI', fromDenomination: 'WEI',
fromCurrency: 'ETH', fromCurrency,
toCurrency, toCurrency,
numberOfDecimals, numberOfDecimals,
conversionRate, conversionRate,
@ -99,6 +101,7 @@ export function formatCurrency (value, currencyCode) {
export function convertTokenToFiat ({ export function convertTokenToFiat ({
value, value,
fromCurrency = 'ETH',
toCurrency, toCurrency,
conversionRate, conversionRate,
contractExchangeRate, contractExchangeRate,
@ -108,6 +111,7 @@ export function convertTokenToFiat ({
return conversionUtil(value, { return conversionUtil(value, {
fromNumericBase: 'dec', fromNumericBase: 'dec',
toNumericBase: 'dec', toNumericBase: 'dec',
fromCurrency,
toCurrency, toCurrency,
numberOfDecimals: 2, numberOfDecimals: 2,
conversionRate: totalExchangeRate, conversionRate: totalExchangeRate,

@ -20,8 +20,8 @@ export function decimalToHex (decimal) {
}) })
} }
export function getEthConversionFromWeiHex ({ value, conversionRate, numberOfDecimals = 6 }) { export function getEthConversionFromWeiHex ({ value, fromCurrency = ETH, conversionRate, numberOfDecimals = 6 }) {
const denominations = [ETH, GWEI, WEI] const denominations = [fromCurrency, GWEI, WEI]
let nonZeroDenomination let nonZeroDenomination
@ -29,7 +29,8 @@ export function getEthConversionFromWeiHex ({ value, conversionRate, numberOfDec
const convertedValue = getValueFromWeiHex({ const convertedValue = getValueFromWeiHex({
value, value,
conversionRate, conversionRate,
toCurrency: ETH, fromCurrency,
toCurrency: fromCurrency,
numberOfDecimals, numberOfDecimals,
toDenomination: denominations[i], toDenomination: denominations[i],
}) })
@ -45,6 +46,7 @@ export function getEthConversionFromWeiHex ({ value, conversionRate, numberOfDec
export function getValueFromWeiHex ({ export function getValueFromWeiHex ({
value, value,
fromCurrency = ETH,
toCurrency, toCurrency,
conversionRate, conversionRate,
numberOfDecimals, numberOfDecimals,
@ -53,7 +55,7 @@ export function getValueFromWeiHex ({
return conversionUtil(value, { return conversionUtil(value, {
fromNumericBase: 'hex', fromNumericBase: 'hex',
toNumericBase: 'dec', toNumericBase: 'dec',
fromCurrency: ETH, fromCurrency,
toCurrency, toCurrency,
numberOfDecimals, numberOfDecimals,
fromDenomination: WEI, fromDenomination: WEI,

@ -52,7 +52,7 @@ function reduceMetamask (state, action) {
welcomeScreenSeen: false, welcomeScreenSeen: false,
currentLocale: '', currentLocale: '',
preferences: { preferences: {
useETHAsPrimaryCurrency: true, useNativeCurrencyAsPrimaryCurrency: true,
}, },
}, state.metamask) }, state.metamask)

@ -26,6 +26,7 @@ const selectors = {
getAddressBook, getAddressBook,
getSendFrom, getSendFrom,
getCurrentCurrency, getCurrentCurrency,
getNativeCurrency,
getSendAmount, getSendAmount,
getSelectedTokenToFiatRate, getSelectedTokenToFiatRate,
getSelectedTokenContract, getSelectedTokenContract,
@ -143,6 +144,10 @@ function getCurrentCurrency (state) {
return state.metamask.currentCurrency return state.metamask.currentCurrency
} }
function getNativeCurrency (state) {
return state.metamask.nativeCurrency
}
function getSelectedTokenToFiatRate (state) { function getSelectedTokenToFiatRate (state) {
const selectedTokenExchangeRate = getSelectedTokenExchangeRate(state) const selectedTokenExchangeRate = getSelectedTokenExchangeRate(state)
const conversionRate = conversionRateSelector(state) const conversionRate = conversionRateSelector(state)

@ -93,6 +93,7 @@ export const unconfirmedTransactionsCountSelector = createSelector(
export const currentCurrencySelector = state => state.metamask.currentCurrency export const currentCurrencySelector = state => state.metamask.currentCurrency
export const conversionRateSelector = state => state.metamask.conversionRate export const conversionRateSelector = state => state.metamask.conversionRate
export const getNativeCurrency = state => state.metamask.nativeCurrency
const txDataSelector = state => state.confirmTransaction.txData const txDataSelector = state => state.confirmTransaction.txData
const tokenDataSelector = state => state.confirmTransaction.tokenData const tokenDataSelector = state => state.confirmTransaction.tokenData

@ -128,7 +128,7 @@ function parseBalance (balance) {
// Takes wei hex, returns an object with three properties. // Takes wei hex, returns an object with three properties.
// Its "formatted" property is what we generally use to render values. // Its "formatted" property is what we generally use to render values.
function formatBalance (balance, decimalsToKeep, needsParse = true) { function formatBalance (balance, decimalsToKeep, needsParse = true, ticker = 'ETH') {
var parsed = needsParse ? parseBalance(balance) : balance.split('.') var parsed = needsParse ? parseBalance(balance) : balance.split('.')
var beforeDecimal = parsed[0] var beforeDecimal = parsed[0]
var afterDecimal = parsed[1] var afterDecimal = parsed[1]
@ -138,14 +138,14 @@ function formatBalance (balance, decimalsToKeep, needsParse = true) {
if (afterDecimal !== '0') { if (afterDecimal !== '0') {
var sigFigs = afterDecimal.match(/^0*(.{2})/) // default: grabs 2 most significant digits var sigFigs = afterDecimal.match(/^0*(.{2})/) // default: grabs 2 most significant digits
if (sigFigs) { afterDecimal = sigFigs[0] } if (sigFigs) { afterDecimal = sigFigs[0] }
formatted = '0.' + afterDecimal + ' ETH' formatted = '0.' + afterDecimal + ` ${ticker}`
} }
} else { } else {
formatted = beforeDecimal + '.' + afterDecimal.slice(0, 3) + ' ETH' formatted = beforeDecimal + '.' + afterDecimal.slice(0, 3) + ` ${ticker}`
} }
} else { } else {
afterDecimal += Array(decimalsToKeep).join('0') afterDecimal += Array(decimalsToKeep).join('0')
formatted = beforeDecimal + '.' + afterDecimal.slice(0, decimalsToKeep) + ' ETH' formatted = beforeDecimal + '.' + afterDecimal.slice(0, decimalsToKeep) + ` ${ticker}`
} }
return formatted return formatted
} }

Loading…
Cancel
Save