feature: integrate gaba/CurrencyRateController (#6570)

feature/default_network_editable
Paul Bouchon 6 years ago committed by GitHub
parent 44616befc7
commit 47024fd9a5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 205
      app/scripts/controllers/currency.js
  2. 2
      app/scripts/controllers/token-rates.js
  3. 32
      app/scripts/metamask-controller.js
  4. 15603
      package-lock.json
  5. 2
      package.json
  6. 78
      test/unit/app/controllers/currency-controller-test.js
  7. 9
      test/unit/app/controllers/metamask-controller-test.js

@ -1,205 +0,0 @@
const ObservableStore = require('obs-store')
const extend = require('xtend')
const log = require('loglevel')
// every ten minutes
const POLLING_INTERVAL = 600000
class CurrencyController {
/**
* Controller responsible for managing data associated with the currently selected currency.
*
* @typedef {Object} CurrencyController
* @param {object} opts Overrides the defaults for the initial state of this.store
* @property {array} opts.initState initializes the the state of the CurrencyController. Can contain an
* currentCurrency, conversionRate and conversionDate properties
* @property {string} currentCurrency A 2-4 character shorthand that describes a specific currency, currently
* selected by the user
* @property {number} conversionRate The conversion rate from ETH to the selected currency.
* @property {string} conversionDate The date at which the conversion rate was set. Expressed in in milliseconds
* since midnight of January 1, 1970
* @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.
* @property {string} nativeCurrency The ticker/symbol of the native chain currency
*
*/
constructor (opts = {}) {
const initState = extend({
currentCurrency: 'usd',
conversionRate: 0,
conversionDate: 'N/A',
nativeCurrency: 'ETH',
}, opts.initState)
this.store = new ObservableStore(initState)
}
//
// 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
*
* @returns {string} A 2-4 character shorthand that describes a specific currency, currently selected by the user
*
*/
getCurrentCurrency () {
return this.store.getState().currentCurrency
}
/**
* A setter for the currentCurrency property
*
* @param {string} currentCurrency The new currency to set as the currentCurrency in the store
*
*/
setCurrentCurrency (currentCurrency) {
this.store.updateState({ currentCurrency })
}
/**
* A getter for the conversionRate property
*
* @returns {string} The conversion rate from ETH to the selected currency.
*
*/
getConversionRate () {
return this.store.getState().conversionRate
}
/**
* A setter for the conversionRate property
*
* @param {number} conversionRate The new rate to set as the conversionRate in the store
*
*/
setConversionRate (conversionRate) {
this.store.updateState({ conversionRate })
}
/**
* A getter for the conversionDate property
*
* @returns {string} The date at which the conversion rate was set. Expressed in milliseconds since midnight of
* January 1, 1970
*
*/
getConversionDate () {
return this.store.getState().conversionDate
}
/**
* A setter for the conversionDate property
*
* @param {number} conversionDate The date, expressed in milliseconds since midnight of January 1, 1970, that the
* conversionRate was set
*
*/
setConversionDate (conversionDate) {
this.store.updateState({ conversionDate })
}
/**
* Updates the conversionRate and conversionDate properties associated with the currentCurrency. Updated info is
* fetched from an external API
*
*/
async updateConversionRate () {
let currentCurrency, nativeCurrency
try {
currentCurrency = this.getCurrentCurrency()
nativeCurrency = this.getNativeCurrency()
// select api
let apiUrl
if (nativeCurrency === 'ETH') {
// ETH
apiUrl = `https://api.infura.io/v1/ticker/eth${currentCurrency.toLowerCase()}`
} else {
// ETC
apiUrl = `https://min-api.cryptocompare.com/data/price?fsym=${nativeCurrency.toUpperCase()}&tsyms=${currentCurrency.toUpperCase()}`
}
// attempt request
let response
try {
response = await fetch(apiUrl)
} catch (err) {
log.error(new Error(`CurrencyController - Failed to request currency from Infura:\n${err.stack}`))
return
}
// parse response
let rawResponse
let parsedResponse
try {
rawResponse = await response.text()
parsedResponse = JSON.parse(rawResponse)
} catch (err) {
log.error(new Error(`CurrencyController - Failed to parse response "${rawResponse}"`))
return
}
// set conversion rate
if (nativeCurrency === 'ETH') {
// ETH
this.setConversionRate(Number(parsedResponse.bid))
this.setConversionDate(Number(parsedResponse.timestamp))
} else {
// ETC
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) {
// reset current conversion rate
log.warn(`MetaMask - Failed to query currency conversion:`, nativeCurrency, currentCurrency, err)
this.setConversionRate(0)
this.setConversionDate('N/A')
// throw error
log.error(new Error(`CurrencyController - Failed to query rate for currency "${currentCurrency}":\n${err.stack}`))
return
}
}
/**
* Creates a new poll, using setInterval, to periodically call updateConversionRate. The id of the interval is
* stored at the controller's conversionInterval property. If it is called and such an id already exists, the
* previous interval is clear and a new one is created.
*
*/
scheduleConversionInterval () {
if (this.conversionInterval) {
clearInterval(this.conversionInterval)
}
this.conversionInterval = setInterval(() => {
this.updateConversionRate()
}, POLLING_INTERVAL)
}
}
module.exports = CurrencyController

@ -30,7 +30,7 @@ class TokenRatesController {
async updateExchangeRates () {
if (!this.isActive) { return }
const contractExchangeRates = {}
const nativeCurrency = this.currency ? this.currency.getState().nativeCurrency.toLowerCase() : 'eth'
const nativeCurrency = this.currency ? this.currency.state.nativeCurrency.toLowerCase() : 'eth'
const pairs = this._tokens.map(token => token.address).join(',')
const query = `contract_addresses=${pairs}&vs_currencies=${nativeCurrency}`
if (this._tokens.length > 0) {

@ -26,7 +26,6 @@ const KeyringController = require('eth-keyring-controller')
const NetworkController = require('./controllers/network')
const PreferencesController = require('./controllers/preferences')
const AppStateController = require('./controllers/app-state')
const CurrencyController = require('./controllers/currency')
const InfuraController = require('./controllers/infura')
const CachedBalancesController = require('./controllers/cached-balances')
const RecentBlocksController = require('./controllers/recent-blocks')
@ -56,6 +55,7 @@ const ethUtil = require('ethereumjs-util')
const sigUtil = require('eth-sig-util')
const {
AddressBookController,
CurrencyRateController,
ShapeShiftController,
PhishingController,
} = require('gaba')
@ -109,11 +109,7 @@ module.exports = class MetamaskController extends EventEmitter {
})
// currency controller
this.currencyController = new CurrencyController({
initState: initState.CurrencyController,
})
this.currencyController.updateConversionRate()
this.currencyController.scheduleConversionInterval()
this.currencyRateController = new CurrencyRateController(undefined, initState.CurrencyController)
// infura controller
this.infuraController = new InfuraController({
@ -130,7 +126,7 @@ module.exports = class MetamaskController extends EventEmitter {
// token exchange rate tracker
this.tokenRatesController = new TokenRatesController({
currency: this.currencyController.store,
currency: this.currencyRateController,
preferences: this.preferencesController.store,
})
@ -232,8 +228,7 @@ module.exports = class MetamaskController extends EventEmitter {
})
this.networkController.on('networkDidChange', () => {
this.balancesController.updateAllBalances()
const currentCurrency = this.currencyController.getCurrentCurrency()
this.setCurrentCurrency(currentCurrency, function () {})
this.setCurrentCurrency(this.currencyRateController.state.currentCurrency, function () {})
})
this.balancesController.updateAllBalances()
@ -262,7 +257,7 @@ module.exports = class MetamaskController extends EventEmitter {
KeyringController: this.keyringController.store,
PreferencesController: this.preferencesController.store,
AddressBookController: this.addressBookController,
CurrencyController: this.currencyController.store,
CurrencyController: this.currencyRateController,
ShapeShiftController: this.shapeshiftController,
NetworkController: this.networkController.store,
InfuraController: this.infuraController.store,
@ -284,7 +279,7 @@ module.exports = class MetamaskController extends EventEmitter {
PreferencesController: this.preferencesController.store,
RecentBlocksController: this.recentBlocksController.store,
AddressBookController: this.addressBookController,
CurrencyController: this.currencyController.store,
CurrencyController: this.currencyRateController,
ShapeshiftController: this.shapeshiftController,
InfuraController: this.infuraController.store,
ProviderApprovalController: this.providerApprovalController.store,
@ -1596,16 +1591,13 @@ module.exports = class MetamaskController extends EventEmitter {
setCurrentCurrency (currencyCode, cb) {
const { ticker } = this.networkController.getNetworkConfig()
try {
this.currencyController.setNativeCurrency(ticker)
this.currencyController.setCurrentCurrency(currencyCode)
this.currencyController.updateConversionRate()
const data = {
nativeCurrency: ticker || 'ETH',
conversionRate: this.currencyController.getConversionRate(),
currentCurrency: this.currencyController.getCurrentCurrency(),
conversionDate: this.currencyController.getConversionDate(),
const currencyState = {
nativeCurrency: ticker,
currentCurrency: currencyCode,
}
cb(null, data)
this.currencyRateController.update(currencyState)
this.currencyRateController.configure(currencyState)
cb(null, this.currencyRateController.state)
} catch (err) {
cb(err)
}

15603
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -230,7 +230,7 @@
"file-loader": "^1.1.11",
"fs-extra": "^6.0.1",
"fs-promise": "^2.0.3",
"gaba": "^1.0.1",
"gaba": "^1.3.0",
"ganache-cli": "^6.1.0",
"ganache-core": "^2.5.3",
"geckodriver": "^1.14.1",

@ -1,78 +0,0 @@
const assert = require('assert')
const nock = require('nock')
const CurrencyController = require('../../../../app/scripts/controllers/currency')
describe('currency-controller', function () {
var currencyController
beforeEach(function () {
currencyController = new CurrencyController()
})
describe('currency conversions', function () {
describe('#setCurrentCurrency', function () {
it('should return USD as default', function () {
assert.equal(currencyController.getCurrentCurrency(), 'usd')
})
it('should be able to set to other currency', function () {
assert.equal(currencyController.getCurrentCurrency(), 'usd')
currencyController.setCurrentCurrency('JPY')
var result = currencyController.getCurrentCurrency()
assert.equal(result, 'JPY')
})
})
describe('#getConversionRate', function () {
it('should return undefined if non-existent', function () {
var result = currencyController.getConversionRate()
assert.ok(!result)
})
})
describe('#updateConversionRate', function () {
it('should retrieve an update for ETH to USD and set it in memory', function (done) {
this.timeout(15000)
nock('https://api.infura.io')
.get('/v1/ticker/ethusd')
.reply(200, '{"base": "ETH", "quote": "USD", "bid": 288.45, "ask": 288.46, "volume": 112888.17569277, "exchange": "bitfinex", "total_volume": 272175.00106721005, "num_exchanges": 8, "timestamp": 1506444677}')
assert.equal(currencyController.getConversionRate(), 0)
currencyController.setCurrentCurrency('usd')
currencyController.updateConversionRate()
.then(function () {
var result = currencyController.getConversionRate()
assert.equal(typeof result, 'number')
done()
}).catch(function (err) {
done(err)
})
})
it('should work for JPY as well.', function () {
this.timeout(15000)
assert.equal(currencyController.getConversionRate(), 0)
nock('https://api.infura.io')
.get('/v1/ticker/ethjpy')
.reply(200, '{"base": "ETH", "quote": "JPY", "bid": 32300.0, "ask": 32400.0, "volume": 247.4616071, "exchange": "kraken", "total_volume": 247.4616071, "num_exchanges": 1, "timestamp": 1506444676}')
var promise = new Promise(
function (resolve) {
currencyController.setCurrentCurrency('jpy')
currencyController.updateConversionRate().then(function () {
resolve()
})
})
promise.then(function () {
var result = currencyController.getConversionRate()
assert.equal(typeof result, 'number')
}).catch(function (done, err) {
done(err)
})
})
})
})
})

@ -45,6 +45,11 @@ describe('MetaMaskController', function () {
.get(/.*/)
.reply(200)
nock('https://min-api.cryptocompare.com')
.persist()
.get(/.*/)
.reply(200, '{"JPY":12415.9}')
metamaskController = new MetaMaskController({
showUnapprovedTx: noop,
showUnconfirmedMessage: noop,
@ -441,7 +446,7 @@ describe('MetaMaskController', function () {
let defaultMetaMaskCurrency
beforeEach(function () {
defaultMetaMaskCurrency = metamaskController.currencyController.getCurrentCurrency()
defaultMetaMaskCurrency = metamaskController.currencyRateController.state.currentCurrency
})
it('defaults to usd', function () {
@ -450,7 +455,7 @@ describe('MetaMaskController', function () {
it('sets currency to JPY', function () {
metamaskController.setCurrentCurrency('JPY', noop)
assert.equal(metamaskController.currencyController.getCurrentCurrency(), 'JPY')
assert.equal(metamaskController.currencyRateController.state.currentCurrency, 'JPY')
})
})

Loading…
Cancel
Save