aboutsummaryrefslogtreecommitdiffstats
path: root/app/scripts/controllers
diff options
context:
space:
mode:
authorfrankiebee <frankie.diamond@gmail.com>2018-04-20 02:49:11 +0800
committerfrankiebee <frankie.diamond@gmail.com>2018-04-20 02:49:11 +0800
commitc20c97ce625207c7afcd48a12f6e7891d582ae99 (patch)
treeb5e063ee7cfd0d0e99fcdb0409fe3106c010bf79 /app/scripts/controllers
parenteeb9390de81ce6fc92247d5c499e991dce8330bd (diff)
parent00efcf9e8ba34d448b628c98d32ad12d5be2ffc9 (diff)
downloadtangerine-wallet-browser-c20c97ce625207c7afcd48a12f6e7891d582ae99.tar.gz
tangerine-wallet-browser-c20c97ce625207c7afcd48a12f6e7891d582ae99.tar.zst
tangerine-wallet-browser-c20c97ce625207c7afcd48a12f6e7891d582ae99.zip
Merge branch 'master' into tx-controller-rewrite-v3
Diffstat (limited to 'app/scripts/controllers')
-rw-r--r--app/scripts/controllers/address-book.js66
-rw-r--r--app/scripts/controllers/blacklist.js1
-rw-r--r--app/scripts/controllers/currency.js68
-rw-r--r--app/scripts/controllers/infura.js1
-rw-r--r--app/scripts/controllers/network.js1
-rw-r--r--app/scripts/controllers/preferences.js124
-rw-r--r--app/scripts/controllers/recent-blocks.js1
-rw-r--r--app/scripts/controllers/shapeshift.js75
-rw-r--r--app/scripts/controllers/token-rates.js77
-rw-r--r--app/scripts/controllers/transactions/index.js1
10 files changed, 398 insertions, 17 deletions
diff --git a/app/scripts/controllers/address-book.js b/app/scripts/controllers/address-book.js
index 6fb4ee114..c91e6b2e4 100644
--- a/app/scripts/controllers/address-book.js
+++ b/app/scripts/controllers/address-book.js
@@ -4,9 +4,22 @@ const extend = require('xtend')
class AddressBookController {
- // Controller in charge of managing the address book functionality from the
- // recipients field on the send screen. Manages a history of all saved
- // addresses and all currently owned addresses.
+ /**
+ * Controller in charge of managing the address book functionality from the
+ * recipients field on the send screen. Manages a history of all saved
+ * addresses and all currently owned addresses.
+ *
+ * @typedef {Object} AddressBookController
+ * @param {object} opts Overrides the defaults for the initial state of this.store
+ * @property {array} opts.initState initializes the the state of the AddressBookController. Can contain an
+ * addressBook property to initialize the addressBook array
+ * @param {KeyringController} keyringController (Soon to be deprecated) The keyringController used in the current
+ * MetamaskController. Contains the identities used in this AddressBookController.
+ * @property {object} store The the store of the current users address book
+ * @property {array} store.addressBook An array of addresses and nicknames. These are set by the user when sending
+ * to a new address.
+ *
+ */
constructor (opts = {}, keyringController) {
const initState = extend({
addressBook: [],
@@ -19,7 +32,14 @@ class AddressBookController {
// PUBLIC METHODS
//
- // Sets a new address book in store by accepting a new address and nickname.
+ /**
+ * Sets a new address book in store by accepting a new address and nickname.
+ *
+ * @param {string} address A hex address of a new account that the user is sending to.
+ * @param {string} name The name the user wishes to associate with the new account
+ * @returns {Promise<void>} Promise resolves with undefined
+ *
+ */
setAddressBook (address, name) {
return this._addToAddressBook(address, name)
.then((addressBook) => {
@@ -30,14 +50,16 @@ class AddressBookController {
})
}
- //
- // PRIVATE METHODS
- //
-
-
- // Performs the logic to add the address and name into the address book. The
- // pushed object is an object of two fields. Current behavior does not set an
- // upper limit to the number of addresses.
+ /**
+ * Performs the logic to add the address and name into the address book. The pushed object is an object of two
+ * fields. Current behavior does not set an upper limit to the number of addresses.
+ *
+ * @private
+ * @param {string} address A hex address of a new account that the user is sending to.
+ * @param {string} name The name the user wishes to associate with the new account
+ * @returns {Promise<array>} Promises the updated addressBook array
+ *
+ */
_addToAddressBook (address, name) {
const addressBook = this._getAddressBook()
const identities = this._getIdentities()
@@ -62,14 +84,26 @@ class AddressBookController {
return Promise.resolve(addressBook)
}
- // Internal method to get the address book. Current persistence behavior
- // should not require that this method be called from the UI directly.
+ /**
+ * Internal method to get the address book. Current persistence behavior should not require that this method be
+ * called from the UI directly.
+ *
+ * @private
+ * @returns {array} The addressBook array from the store.
+ *
+ */
_getAddressBook () {
return this.store.getState().addressBook
}
- // Retrieves identities from the keyring controller in order to avoid
- // duplication
+ /**
+ * Retrieves identities from the keyring controller in order to avoid
+ * duplication
+ *
+ * @deprecated
+ * @returns {array} Returns the identies array from the keyringContoller's state
+ *
+ */
_getIdentities () {
return this.keyringController.memStore.getState().identities
}
diff --git a/app/scripts/controllers/blacklist.js b/app/scripts/controllers/blacklist.js
index df41c90c0..d965f80b8 100644
--- a/app/scripts/controllers/blacklist.js
+++ b/app/scripts/controllers/blacklist.js
@@ -1,6 +1,7 @@
const ObservableStore = require('obs-store')
const extend = require('xtend')
const PhishingDetector = require('eth-phishing-detect/src/detector')
+const log = require('loglevel')
// compute phishing lists
const PHISHING_DETECTION_CONFIG = require('eth-phishing-detect/src/config.json')
diff --git a/app/scripts/controllers/currency.js b/app/scripts/controllers/currency.js
index 36b8808aa..480c08b1c 100644
--- a/app/scripts/controllers/currency.js
+++ b/app/scripts/controllers/currency.js
@@ -1,11 +1,28 @@
-const ObservableStore = require('obs-store')
+ 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.
+ *
+ */
constructor (opts = {}) {
const initState = extend({
currentCurrency: 'usd',
@@ -19,30 +36,73 @@ class CurrencyController {
// PUBLIC METHODS
//
+ /**
+ * 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
try {
@@ -58,6 +118,12 @@ class CurrencyController {
}
}
+ /**
+ * 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)
diff --git a/app/scripts/controllers/infura.js b/app/scripts/controllers/infura.js
index c6b4c9de2..8f6dd837e 100644
--- a/app/scripts/controllers/infura.js
+++ b/app/scripts/controllers/infura.js
@@ -1,5 +1,6 @@
const ObservableStore = require('obs-store')
const extend = require('xtend')
+const log = require('loglevel')
// every ten minutes
const POLLING_INTERVAL = 10 * 60 * 1000
diff --git a/app/scripts/controllers/network.js b/app/scripts/controllers/network.js
index 617456cd7..45574e673 100644
--- a/app/scripts/controllers/network.js
+++ b/app/scripts/controllers/network.js
@@ -9,6 +9,7 @@ const extend = require('xtend')
const EthQuery = require('eth-query')
const createEventEmitterProxy = require('../lib/events-proxy.js')
const networkConfig = require('../config.js')
+const log = require('loglevel')
const { OLD_UI_NETWORK_TYPE, DEFAULT_RPC } = networkConfig.enums
const INFURA_PROVIDER_TYPES = ['ropsten', 'rinkeby', 'kovan', 'mainnet']
diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js
index b4819d951..d4d508026 100644
--- a/app/scripts/controllers/preferences.js
+++ b/app/scripts/controllers/preferences.js
@@ -4,6 +4,21 @@ const extend = require('xtend')
class PreferencesController {
+ /**
+ *
+ * @typedef {Object} PreferencesController
+ * @param {object} opts Overrides the defaults for the initial state of this.store
+ * @property {object} store The an object containing a users preferences, stored in local storage
+ * @property {array} store.frequentRpcList A list of custom rpcs to provide the user
+ * @property {string} store.currentAccountTab Indicates the selected tab in the ui
+ * @property {array} store.tokens The tokens the user wants display in their token lists
+ * @property {boolean} store.useBlockie The users preference for blockie identicons within the UI
+ * @property {object} store.featureFlags A key-boolean map, where keys refer to features and booleans to whether the
+ * user wishes to see that feature
+ * @property {string} store.currentLocale The preferred language locale key
+ * @property {string} store.selectedAddress A hex string that matches the currently selected address in the app
+ *
+ */
constructor (opts = {}) {
const initState = extend({
frequentRpcList: [],
@@ -17,18 +32,43 @@ class PreferencesController {
}
// PUBLIC METHODS
+ /**
+ * Setter for the `useBlockie` property
+ *
+ * @param {boolean} val Whether or not the user prefers blockie indicators
+ *
+ */
setUseBlockie (val) {
this.store.updateState({ useBlockie: val })
}
+ /**
+ * Getter for the `useBlockie` property
+ *
+ * @returns {boolean} this.store.useBlockie
+ *
+ */
getUseBlockie () {
return this.store.getState().useBlockie
}
+ /**
+ * Setter for the `currentLocale` property
+ *
+ * @param {string} key he preferred language locale key
+ *
+ */
setCurrentLocale (key) {
this.store.updateState({ currentLocale: key })
}
+ /**
+ * Setter for the `selectedAddress` property
+ *
+ * @param {string} _address A new hex address for an account
+ * @returns {Promise<void>} Promise resolves with undefined
+ *
+ */
setSelectedAddress (_address) {
return new Promise((resolve, reject) => {
const address = normalizeAddress(_address)
@@ -37,10 +77,37 @@ class PreferencesController {
})
}
+ /**
+ * Getter for the `selectedAddress` property
+ *
+ * @returns {string} The hex address for the currently selected account
+ *
+ */
getSelectedAddress () {
return this.store.getState().selectedAddress
}
+ /**
+ * Contains data about tokens users add to their account.
+ * @typedef {Object} AddedToken
+ * @property {string} address - The hex address for the token contract. Will be all lower cased and hex-prefixed.
+ * @property {string} symbol - The symbol of the token, usually 3 or 4 capitalized letters
+ * {@link https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md#symbol}
+ * @property {boolean} decimals - The number of decimals the token uses.
+ * {@link https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md#decimals}
+ */
+
+ /**
+ * Adds a new token to the token array, or updates the token if passed an address that already exists.
+ * Modifies the existing tokens array from the store. All objects in the tokens array array AddedToken objects.
+ * @see AddedToken {@link AddedToken}
+ *
+ * @param {string} rawAddress Hex address of the token contract. May or may not be a checksum address.
+ * @param {string} symbol The symbol of the token
+ * @param {number} decimals The number of decimals the token uses.
+ * @returns {Promise<array>} Promises the new array of AddedToken objects.
+ *
+ */
async addToken (rawAddress, symbol, decimals) {
const address = normalizeAddress(rawAddress)
const newEntry = { address, symbol, decimals }
@@ -62,6 +129,13 @@ class PreferencesController {
return Promise.resolve(tokens)
}
+ /**
+ * Removes a specified token from the tokens array.
+ *
+ * @param {string} rawAddress Hex address of the token contract to remove.
+ * @returns {Promise<array>} The new array of AddedToken objects
+ *
+ */
removeToken (rawAddress) {
const tokens = this.store.getState().tokens
@@ -71,10 +145,23 @@ class PreferencesController {
return Promise.resolve(updatedTokens)
}
+ /**
+ * A getter for the `tokens` property
+ *
+ * @returns {array} The current array of AddedToken objects
+ *
+ */
getTokens () {
return this.store.getState().tokens
}
+ /**
+ * Gets an updated rpc list from this.addToFrequentRpcList() and sets the `frequentRpcList` to this update list.
+ *
+ * @param {string} _url The the new rpc url to add to the updated list
+ * @returns {Promise<void>} Promise resolves with undefined
+ *
+ */
updateFrequentRpcList (_url) {
return this.addToFrequentRpcList(_url)
.then((rpcList) => {
@@ -83,6 +170,13 @@ class PreferencesController {
})
}
+ /**
+ * Setter for the `currentAccountTab` property
+ *
+ * @param {string} currentAccountTab Specifies the new tab to be marked as current
+ * @returns {Promise<void>} Promise resolves with undefined
+ *
+ */
setCurrentAccountTab (currentAccountTab) {
return new Promise((resolve, reject) => {
this.store.updateState({ currentAccountTab })
@@ -90,6 +184,15 @@ class PreferencesController {
})
}
+ /**
+ * Returns an updated rpcList based on the passed url and the current list.
+ * The returned list will have a max length of 2. If the _url currently exists it the list, it will be moved to the
+ * end of the list. The current list is modified and returned as a promise.
+ *
+ * @param {string} _url The rpc url to add to the frequentRpcList.
+ * @returns {Promise<array>} The updated frequentRpcList.
+ *
+ */
addToFrequentRpcList (_url) {
const rpcList = this.getFrequentRpcList()
const index = rpcList.findIndex((element) => { return element === _url })
@@ -105,10 +208,24 @@ class PreferencesController {
return Promise.resolve(rpcList)
}
+ /**
+ * Getter for the `frequentRpcList` property.
+ *
+ * @returns {array<string>} An array of one or two rpc urls.
+ *
+ */
getFrequentRpcList () {
return this.store.getState().frequentRpcList
}
+ /**
+ * Updates the `featureFlags` property, which is an object. One property within that object will be set to a boolean.
+ *
+ * @param {string} feature A key that corresponds to a UI feature.
+ * @param {boolean} activated Indicates whether or not the UI feature should be displayed
+ * @returns {Promise<object>} Promises a new object; the updated featureFlags object.
+ *
+ */
setFeatureFlag (feature, activated) {
const currentFeatureFlags = this.store.getState().featureFlags
const updatedFeatureFlags = {
@@ -121,6 +238,13 @@ class PreferencesController {
return Promise.resolve(updatedFeatureFlags)
}
+ /**
+ * A getter for the `featureFlags` property
+ *
+ * @returns {object} A key-boolean map, where keys refer to features and booleans to whether the
+ * user wishes to see that feature
+ *
+ */
getFeatureFlags () {
return this.store.getState().featureFlags
}
diff --git a/app/scripts/controllers/recent-blocks.js b/app/scripts/controllers/recent-blocks.js
index 4ae3810eb..0c1ee4e38 100644
--- a/app/scripts/controllers/recent-blocks.js
+++ b/app/scripts/controllers/recent-blocks.js
@@ -2,6 +2,7 @@ const ObservableStore = require('obs-store')
const extend = require('xtend')
const BN = require('ethereumjs-util').BN
const EthQuery = require('eth-query')
+const log = require('loglevel')
class RecentBlocksController {
diff --git a/app/scripts/controllers/shapeshift.js b/app/scripts/controllers/shapeshift.js
index 3bbfaa1c5..b2a1462c2 100644
--- a/app/scripts/controllers/shapeshift.js
+++ b/app/scripts/controllers/shapeshift.js
@@ -1,11 +1,23 @@
const ObservableStore = require('obs-store')
const extend = require('xtend')
+const log = require('loglevel')
// every three seconds when an incomplete tx is waiting
const POLLING_INTERVAL = 3000
class ShapeshiftController {
+ /**
+ * Controller responsible for managing the list of shapeshift transactions. On construction, it initiates a poll
+ * that queries a shapeshift.io API for updates to any pending shapeshift transactions
+ *
+ * @typedef {Object} ShapeshiftController
+ * @param {object} opts Overrides the defaults for the initial state of this.store
+ * @property {array} opts.initState initializes the the state of the ShapeshiftController. Can contain an
+ * shapeShiftTxList array.
+ * @property {array} shapeShiftTxList An array of ShapeShiftTx objects
+ *
+ */
constructor (opts = {}) {
const initState = extend({
shapeShiftTxList: [],
@@ -14,21 +26,54 @@ class ShapeshiftController {
this.pollForUpdates()
}
+ /**
+ * Represents, and contains data about, a single shapeshift transaction.
+ * @typedef {Object} ShapeShiftTx
+ * @property {string} depositAddress - An address at which to send a crypto deposit, so that eth can be sent to the
+ * user's Metamask account
+ * @property {string} depositType - An abbreviation of the type of crypto currency to be deposited.
+ * @property {string} key - The 'shapeshift' key differentiates this from other types of txs in Metamask
+ * @property {number} time - The time at which the tx was created
+ * @property {object} response - Initiated as an empty object, which will be replaced by a Response object. @see {@link
+ * https://developer.mozilla.org/en-US/docs/Web/API/Response}
+ */
+
//
// PUBLIC METHODS
//
+ /**
+ * A getter for the shapeShiftTxList property
+ *
+ * @returns {array<ShapeShiftTx>}
+ *
+ */
getShapeShiftTxList () {
const shapeShiftTxList = this.store.getState().shapeShiftTxList
return shapeShiftTxList
}
+ /**
+ * A getter for all ShapeShiftTx in the shapeShiftTxList that have not successfully completed a deposit.
+ *
+ * @returns {array<ShapeShiftTx>} Only includes ShapeShiftTx which has a response property with a status !== complete
+ *
+ */
getPendingTxs () {
const txs = this.getShapeShiftTxList()
const pending = txs.filter(tx => tx.response && tx.response.status !== 'complete')
return pending
}
+ /**
+ * A poll that exists as long as there are pending transactions. Each call attempts to update the data of any
+ * pendingTxs, and then calls itself again. If there are no pending txs, the recursive call is not made and
+ * the polling stops.
+ *
+ * this.updateTx is used to attempt the update to the pendingTxs in the ShapeShiftTxList, and that updated data
+ * is saved with saveTx.
+ *
+ */
pollForUpdates () {
const pendingTxs = this.getPendingTxs()
@@ -45,6 +90,15 @@ class ShapeshiftController {
})
}
+ /**
+ * Attempts to update a ShapeShiftTx with data from a shapeshift.io API. Both the response and time properties
+ * can be updated. The response property is updated with every call, but the time property is only updated when
+ * the response status updates to 'complete'. This will occur once the user makes a deposit as the ShapeShiftTx
+ * depositAddress
+ *
+ * @param {ShapeShiftTx} tx The tx to update
+ *
+ */
async updateTx (tx) {
try {
const url = `https://shapeshift.io/txStat/${tx.depositAddress}`
@@ -60,6 +114,13 @@ class ShapeshiftController {
}
}
+ /**
+ * Saves an updated to a ShapeShiftTx in the shapeShiftTxList. If the passed ShapeShiftTx is not in the
+ * shapeShiftTxList, nothing happens.
+ *
+ * @param {ShapeShiftTx} tx The updated tx to save, if it exists in the current shapeShiftTxList
+ *
+ */
saveTx (tx) {
const { shapeShiftTxList } = this.store.getState()
const index = shapeShiftTxList.indexOf(tx)
@@ -69,6 +130,12 @@ class ShapeshiftController {
}
}
+ /**
+ * Removes a ShapeShiftTx from the shapeShiftTxList
+ *
+ * @param {ShapeShiftTx} tx The tx to remove
+ *
+ */
removeShapeShiftTx (tx) {
const { shapeShiftTxList } = this.store.getState()
const index = shapeShiftTxList.indexOf(index)
@@ -78,6 +145,14 @@ class ShapeshiftController {
this.updateState({ shapeShiftTxList })
}
+ /**
+ * Creates a new ShapeShiftTx, adds it to the shapeShiftTxList, and initiates a new poll for updates of pending txs
+ *
+ * @param {string} depositAddress - An address at which to send a crypto deposit, so that eth can be sent to the
+ * user's Metamask account
+ * @param {string} depositType - An abbreviation of the type of crypto currency to be deposited.
+ *
+ */
createShapeShiftTx (depositAddress, depositType) {
const state = this.store.getState()
let { shapeShiftTxList } = state
diff --git a/app/scripts/controllers/token-rates.js b/app/scripts/controllers/token-rates.js
new file mode 100644
index 000000000..22e3e8154
--- /dev/null
+++ b/app/scripts/controllers/token-rates.js
@@ -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
diff --git a/app/scripts/controllers/transactions/index.js b/app/scripts/controllers/transactions/index.js
index d7287450b..6e47816f8 100644
--- a/app/scripts/controllers/transactions/index.js
+++ b/app/scripts/controllers/transactions/index.js
@@ -8,6 +8,7 @@ const TxGasUtil = require('./tx-gas-utils')
const PendingTransactionTracker = require('./pending-tx-tracker')
const NonceTracker = require('./nonce-tracker')
const txUtils = require('./lib/util')
+const log = require('loglevel')
/**
Transaction Controller is an aggregate of sub-controllers and trackers