aboutsummaryrefslogtreecommitdiffstats
path: root/app/scripts/controllers
diff options
context:
space:
mode:
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/computed-balances.js45
-rw-r--r--app/scripts/controllers/currency.js68
-rw-r--r--app/scripts/controllers/infura.js1
-rw-r--r--app/scripts/controllers/network/enums.js56
-rw-r--r--app/scripts/controllers/network/index.js2
-rw-r--r--app/scripts/controllers/network/network.js (renamed from app/scripts/controllers/network.js)28
-rw-r--r--app/scripts/controllers/network/util.js65
-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.js1
14 files changed, 580 insertions, 30 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/computed-balances.js b/app/scripts/controllers/computed-balances.js
index 907b087cf..1a6802f9a 100644
--- a/app/scripts/controllers/computed-balances.js
+++ b/app/scripts/controllers/computed-balances.js
@@ -2,8 +2,24 @@ const ObservableStore = require('obs-store')
const extend = require('xtend')
const BalanceController = require('./balance')
-class ComputedbalancesController {
+/**
+ * @typedef {Object} ComputedBalancesOptions
+ * @property {Object} accountTracker Account tracker store reference
+ * @property {Object} txController Token controller reference
+ * @property {Object} blockTracker Block tracker reference
+ * @property {Object} initState Initial state to populate this internal store with
+ */
+/**
+ * Background controller responsible for syncing
+ * and computing ETH balances for all accounts
+ */
+class ComputedbalancesController {
+ /**
+ * Creates a new controller instance
+ *
+ * @param {ComputedBalancesOptions} [opts] Controller configuration parameters
+ */
constructor (opts = {}) {
const { accountTracker, txController, blockTracker } = opts
this.accountTracker = accountTracker
@@ -19,6 +35,9 @@ class ComputedbalancesController {
this._initBalanceUpdating()
}
+ /**
+ * Updates balances associated with each internal address
+ */
updateAllBalances () {
Object.keys(this.balances).forEach((balance) => {
const address = balance.address
@@ -26,12 +45,23 @@ class ComputedbalancesController {
})
}
+ /**
+ * Initializes internal address tracking
+ *
+ * @private
+ */
_initBalanceUpdating () {
const store = this.accountTracker.store.getState()
this.syncAllAccountsFromStore(store)
this.accountTracker.store.subscribe(this.syncAllAccountsFromStore.bind(this))
}
+ /**
+ * Uses current account state to sync and track all
+ * addresses associated with the current account
+ *
+ * @param {{ accounts: Object }} store Account tracking state
+ */
syncAllAccountsFromStore (store) {
const upstream = Object.keys(store.accounts)
const balances = Object.keys(this.balances)
@@ -50,6 +80,13 @@ class ComputedbalancesController {
})
}
+ /**
+ * Conditionally establishes a new subscription
+ * to track an address associated with the current
+ * account
+ *
+ * @param {string} address Address to conditionally subscribe to
+ */
trackAddressIfNotAlready (address) {
const state = this.store.getState()
if (!(address in state.computedBalances)) {
@@ -57,6 +94,12 @@ class ComputedbalancesController {
}
}
+ /**
+ * Establishes a new subscription to track an
+ * address associated with the current account
+ *
+ * @param {string} address Address to conditionally subscribe to
+ */
trackAddress (address) {
const updater = new BalanceController({
address,
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/enums.js b/app/scripts/controllers/network/enums.js
new file mode 100644
index 000000000..4f29e301b
--- /dev/null
+++ b/app/scripts/controllers/network/enums.js
@@ -0,0 +1,56 @@
+const ROPSTEN = 'ropsten'
+const RINKEBY = 'rinkeby'
+const KOVAN = 'kovan'
+const MAINNET = 'mainnet'
+const LOCALHOST = 'localhost'
+
+const ROPSTEN_CODE = 3
+const RINKEYBY_CODE = 4
+const KOVAN_CODE = 42
+
+const ROPSTEN_DISPLAY_NAME = 'Ropsten'
+const RINKEBY_DISPLAY_NAME = 'Rinkeby'
+const KOVAN_DISPLAY_NAME = 'Kovan'
+const MAINNET_DISPLAY_NAME = 'Main Ethereum Network'
+
+const MAINNET_RPC_URL = 'https://mainnet.infura.io/metamask'
+const ROPSTEN_RPC_URL = 'https://ropsten.infura.io/metamask'
+const KOVAN_RPC_URL = 'https://kovan.infura.io/metamask'
+const RINKEBY_RPC_URL = 'https://rinkeby.infura.io/metamask'
+const LOCALHOST_RPC_URL = 'http://localhost:8545'
+
+const MAINNET_RPC_URL_BETA = 'https://mainnet.infura.io/metamask2'
+const ROPSTEN_RPC_URL_BETA = 'https://ropsten.infura.io/metamask2'
+const KOVAN_RPC_URL_BETA = 'https://kovan.infura.io/metamask2'
+const RINKEBY_RPC_URL_BETA = 'https://rinkeby.infura.io/metamask2'
+
+const DEFAULT_NETWORK = 'rinkeby'
+const OLD_UI_NETWORK_TYPE = 'network'
+const BETA_UI_NETWORK_TYPE = 'networkBeta'
+
+module.exports = {
+ ROPSTEN,
+ RINKEBY,
+ KOVAN,
+ MAINNET,
+ LOCALHOST,
+ ROPSTEN_CODE,
+ RINKEYBY_CODE,
+ KOVAN_CODE,
+ ROPSTEN_DISPLAY_NAME,
+ RINKEBY_DISPLAY_NAME,
+ KOVAN_DISPLAY_NAME,
+ MAINNET_DISPLAY_NAME,
+ MAINNET_RPC_URL,
+ ROPSTEN_RPC_URL,
+ KOVAN_RPC_URL,
+ RINKEBY_RPC_URL,
+ LOCALHOST_RPC_URL,
+ MAINNET_RPC_URL_BETA,
+ ROPSTEN_RPC_URL_BETA,
+ KOVAN_RPC_URL_BETA,
+ RINKEBY_RPC_URL_BETA,
+ DEFAULT_NETWORK,
+ OLD_UI_NETWORK_TYPE,
+ BETA_UI_NETWORK_TYPE,
+}
diff --git a/app/scripts/controllers/network/index.js b/app/scripts/controllers/network/index.js
new file mode 100644
index 000000000..fb095bf33
--- /dev/null
+++ b/app/scripts/controllers/network/index.js
@@ -0,0 +1,2 @@
+const NetworkController = require('./network')
+module.exports = NetworkController
diff --git a/app/scripts/controllers/network.js b/app/scripts/controllers/network/network.js
index 617456cd7..6fd983bb2 100644
--- a/app/scripts/controllers/network.js
+++ b/app/scripts/controllers/network/network.js
@@ -7,10 +7,18 @@ const ObservableStore = require('obs-store')
const ComposedStore = require('obs-store/lib/composed')
const extend = require('xtend')
const EthQuery = require('eth-query')
-const createEventEmitterProxy = require('../lib/events-proxy.js')
-const networkConfig = require('../config.js')
-const { OLD_UI_NETWORK_TYPE, DEFAULT_RPC } = networkConfig.enums
-const INFURA_PROVIDER_TYPES = ['ropsten', 'rinkeby', 'kovan', 'mainnet']
+const createEventEmitterProxy = require('../../lib/events-proxy.js')
+const log = require('loglevel')
+const {
+ ROPSTEN,
+ RINKEBY,
+ KOVAN,
+ MAINNET,
+ OLD_UI_NETWORK_TYPE,
+ DEFAULT_NETWORK,
+} = require('./enums')
+const { getNetworkEndpoints } = require('./util')
+const INFURA_PROVIDER_TYPES = [ROPSTEN, RINKEBY, KOVAN, MAINNET]
module.exports = class NetworkController extends EventEmitter {
@@ -18,8 +26,8 @@ module.exports = class NetworkController extends EventEmitter {
super()
this._networkEndpointVersion = OLD_UI_NETWORK_TYPE
- this._networkEndpoints = this.getNetworkEndpoints(OLD_UI_NETWORK_TYPE)
- this._defaultRpc = this._networkEndpoints[DEFAULT_RPC]
+ this._networkEndpoints = getNetworkEndpoints(OLD_UI_NETWORK_TYPE)
+ this._defaultRpc = this._networkEndpoints[DEFAULT_NETWORK]
config.provider.rpcTarget = this.getRpcAddressForType(config.provider.type, config.provider)
this.networkStore = new ObservableStore('loading')
@@ -36,17 +44,13 @@ module.exports = class NetworkController extends EventEmitter {
}
this._networkEndpointVersion = version
- this._networkEndpoints = this.getNetworkEndpoints(version)
- this._defaultRpc = this._networkEndpoints[DEFAULT_RPC]
+ this._networkEndpoints = getNetworkEndpoints(version)
+ this._defaultRpc = this._networkEndpoints[DEFAULT_NETWORK]
const { type } = this.getProviderConfig()
return this.setProviderType(type, true)
}
- getNetworkEndpoints (version = OLD_UI_NETWORK_TYPE) {
- return networkConfig[version]
- }
-
initializeProvider (_providerParams) {
this._baseProviderParams = _providerParams
const { type, rpcTarget } = this.providerStore.getState()
diff --git a/app/scripts/controllers/network/util.js b/app/scripts/controllers/network/util.js
new file mode 100644
index 000000000..4f38ccda4
--- /dev/null
+++ b/app/scripts/controllers/network/util.js
@@ -0,0 +1,65 @@
+const {
+ ROPSTEN,
+ RINKEBY,
+ KOVAN,
+ MAINNET,
+ LOCALHOST,
+ ROPSTEN_CODE,
+ RINKEYBY_CODE,
+ KOVAN_CODE,
+ ROPSTEN_DISPLAY_NAME,
+ RINKEBY_DISPLAY_NAME,
+ KOVAN_DISPLAY_NAME,
+ MAINNET_DISPLAY_NAME,
+ MAINNET_RPC_URL,
+ ROPSTEN_RPC_URL,
+ KOVAN_RPC_URL,
+ RINKEBY_RPC_URL,
+ LOCALHOST_RPC_URL,
+ MAINNET_RPC_URL_BETA,
+ ROPSTEN_RPC_URL_BETA,
+ KOVAN_RPC_URL_BETA,
+ RINKEBY_RPC_URL_BETA,
+ OLD_UI_NETWORK_TYPE,
+ BETA_UI_NETWORK_TYPE,
+} = require('./enums')
+
+const networkToNameMap = {
+ [ROPSTEN]: ROPSTEN_DISPLAY_NAME,
+ [RINKEBY]: RINKEBY_DISPLAY_NAME,
+ [KOVAN]: KOVAN_DISPLAY_NAME,
+ [MAINNET]: MAINNET_DISPLAY_NAME,
+ [ROPSTEN_CODE]: ROPSTEN_DISPLAY_NAME,
+ [RINKEYBY_CODE]: RINKEBY_DISPLAY_NAME,
+ [KOVAN_CODE]: KOVAN_DISPLAY_NAME,
+}
+
+const networkEndpointsMap = {
+ [OLD_UI_NETWORK_TYPE]: {
+ [LOCALHOST]: LOCALHOST_RPC_URL,
+ [MAINNET]: MAINNET_RPC_URL,
+ [ROPSTEN]: ROPSTEN_RPC_URL,
+ [KOVAN]: KOVAN_RPC_URL,
+ [RINKEBY]: RINKEBY_RPC_URL,
+ },
+ [BETA_UI_NETWORK_TYPE]: {
+ [LOCALHOST]: LOCALHOST_RPC_URL,
+ [MAINNET]: MAINNET_RPC_URL_BETA,
+ [ROPSTEN]: ROPSTEN_RPC_URL_BETA,
+ [KOVAN]: KOVAN_RPC_URL_BETA,
+ [RINKEBY]: RINKEBY_RPC_URL_BETA,
+ },
+}
+
+const getNetworkDisplayName = key => networkToNameMap[key]
+
+const getNetworkEndpoints = (networkType = OLD_UI_NETWORK_TYPE) => {
+ return {
+ ...networkEndpointsMap[networkType],
+ }
+}
+
+module.exports = {
+ getNetworkDisplayName,
+ getNetworkEndpoints,
+}
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..21384f262
--- /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://metamask.dev.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}
+ */
+ set interval (interval) {
+ this._handle && clearInterval(this._handle)
+ if (!interval) { return }
+ this._handle = setInterval(() => { this.updateExchangeRates() }, interval)
+ }
+
+ /**
+ * @type {Object}
+ */
+ 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}
+ */
+ set tokens (tokens) {
+ this._tokens = tokens
+ this.updateExchangeRates()
+ }
+}
+
+module.exports = TokenRatesController
diff --git a/app/scripts/controllers/transactions.js b/app/scripts/controllers/transactions.js
index 336b0d8f7..c8211ebd7 100644
--- a/app/scripts/controllers/transactions.js
+++ b/app/scripts/controllers/transactions.js
@@ -7,6 +7,7 @@ const TransactionStateManager = require('../lib/tx-state-manager')
const TxGasUtil = require('../lib/tx-gas-utils')
const PendingTransactionTracker = require('../lib/pending-tx-tracker')
const NonceTracker = require('../lib/nonce-tracker')
+const log = require('loglevel')
/*
Transaction Controller is an aggregate of sub-controllers and trackers