Merge pull request #3982 from MetaMask/i3981-contract-rates
Fetch token prices based on contract addressfeature/default_network_editable
commit
e4eb69dcc2
@ -0,0 +1,77 @@ |
||||
const ObservableStore = require('obs-store') |
||||
|
||||
// By default, poll every 3 minutes
|
||||
const DEFAULT_INTERVAL = 180 * 1000 |
||||
|
||||
/** |
||||
* A controller that polls for token exchange |
||||
* rates based on a user's current token list |
||||
*/ |
||||
class TokenRatesController { |
||||
/** |
||||
* Creates a TokenRatesController |
||||
* |
||||
* @param {Object} [config] - Options to configure controller |
||||
*/ |
||||
constructor ({ interval = DEFAULT_INTERVAL, preferences } = {}) { |
||||
this.store = new ObservableStore() |
||||
this.preferences = preferences |
||||
this.interval = interval |
||||
} |
||||
|
||||
/** |
||||
* Updates exchange rates for all tokens |
||||
*/ |
||||
async updateExchangeRates () { |
||||
if (!this.isActive) { return } |
||||
const contractExchangeRates = {} |
||||
for (const i in this._tokens) { |
||||
const address = this._tokens[i].address |
||||
contractExchangeRates[address] = await this.fetchExchangeRate(address) |
||||
} |
||||
this.store.putState({ contractExchangeRates }) |
||||
} |
||||
|
||||
/** |
||||
* Fetches a token exchange rate by address |
||||
* |
||||
* @param {String} address - Token contract address |
||||
*/ |
||||
async fetchExchangeRate (address) { |
||||
try { |
||||
const response = await fetch(`https://exchanges.balanc3.net/prices?from=${address}&to=ETH&autoConversion=false&summaryOnly=true`) |
||||
const json = await response.json() |
||||
return json && json.length ? json[0].averagePrice : 0 |
||||
} catch (error) { } |
||||
} |
||||
|
||||
/** |
||||
* @type {Number} - Interval used to poll for exchange rates |
||||
*/ |
||||
set interval (interval) { |
||||
this._handle && clearInterval(this._handle) |
||||
if (!interval) { return } |
||||
this._handle = setInterval(() => { this.updateExchangeRates() }, interval) |
||||
} |
||||
|
||||
/** |
||||
* @type {Object} - Preferences controller instance |
||||
*/ |
||||
set preferences (preferences) { |
||||
this._preferences && this._preferences.unsubscribe() |
||||
if (!preferences) { return } |
||||
this._preferences = preferences |
||||
this.tokens = preferences.getState().tokens |
||||
preferences.subscribe(({ tokens = [] }) => { this.tokens = tokens }) |
||||
} |
||||
|
||||
/** |
||||
* @type {Array} - Array of token objects with contract addresses |
||||
*/ |
||||
set tokens (tokens) { |
||||
this._tokens = tokens |
||||
this.updateExchangeRates() |
||||
} |
||||
} |
||||
|
||||
module.exports = TokenRatesController |
@ -0,0 +1,29 @@ |
||||
const assert = require('assert') |
||||
const sinon = require('sinon') |
||||
const TokenRatesController = require('../../app/scripts/controllers/token-rates') |
||||
const ObservableStore = require('obs-store') |
||||
|
||||
describe('TokenRatesController', () => { |
||||
it('should listen for preferences store updates', () => { |
||||
const preferences = new ObservableStore({ tokens: [] }) |
||||
const controller = new TokenRatesController({ preferences }) |
||||
preferences.putState({ tokens: ['foo'] }) |
||||
assert.deepEqual(controller._tokens, ['foo']) |
||||
}) |
||||
|
||||
it('should poll on correct interval', async () => { |
||||
const stub = sinon.stub(global, 'setInterval') |
||||
new TokenRatesController({ interval: 1337 }) // eslint-disable-line no-new
|
||||
assert.strictEqual(stub.getCall(0).args[1], 1337) |
||||
stub.restore() |
||||
}) |
||||
|
||||
it('should fetch each token rate based on address', async () => { |
||||
const controller = new TokenRatesController() |
||||
controller.isActive = true |
||||
controller.fetchExchangeRate = address => address |
||||
controller.tokens = [{ address: 'foo' }, { address: 'bar' }] |
||||
await controller.updateExchangeRates() |
||||
assert.deepEqual(controller.store.getState().contractExchangeRates, { foo: 'foo', bar: 'bar' }) |
||||
}) |
||||
}) |
Loading…
Reference in new issue