feature: integrate gaba/CurrencyRateController (#6570)
parent
44616befc7
commit
47024fd9a5
@ -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 |
File diff suppressed because it is too large
Load Diff
@ -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) |
||||
}) |
||||
}) |
||||
}) |
||||
}) |
||||
}) |
Loading…
Reference in new issue