diff options
author | frankiebee <frankie.diamond@gmail.com> | 2018-04-20 02:49:11 +0800 |
---|---|---|
committer | frankiebee <frankie.diamond@gmail.com> | 2018-04-20 02:49:11 +0800 |
commit | c20c97ce625207c7afcd48a12f6e7891d582ae99 (patch) | |
tree | b5e063ee7cfd0d0e99fcdb0409fe3106c010bf79 /app/scripts/controllers | |
parent | eeb9390de81ce6fc92247d5c499e991dce8330bd (diff) | |
parent | 00efcf9e8ba34d448b628c98d32ad12d5be2ffc9 (diff) | |
download | tangerine-wallet-browser-c20c97ce625207c7afcd48a12f6e7891d582ae99.tar tangerine-wallet-browser-c20c97ce625207c7afcd48a12f6e7891d582ae99.tar.gz tangerine-wallet-browser-c20c97ce625207c7afcd48a12f6e7891d582ae99.tar.bz2 tangerine-wallet-browser-c20c97ce625207c7afcd48a12f6e7891d582ae99.tar.lz tangerine-wallet-browser-c20c97ce625207c7afcd48a12f6e7891d582ae99.tar.xz 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.js | 66 | ||||
-rw-r--r-- | app/scripts/controllers/blacklist.js | 1 | ||||
-rw-r--r-- | app/scripts/controllers/currency.js | 68 | ||||
-rw-r--r-- | app/scripts/controllers/infura.js | 1 | ||||
-rw-r--r-- | app/scripts/controllers/network.js | 1 | ||||
-rw-r--r-- | app/scripts/controllers/preferences.js | 124 | ||||
-rw-r--r-- | app/scripts/controllers/recent-blocks.js | 1 | ||||
-rw-r--r-- | app/scripts/controllers/shapeshift.js | 75 | ||||
-rw-r--r-- | app/scripts/controllers/token-rates.js | 77 | ||||
-rw-r--r-- | app/scripts/controllers/transactions/index.js | 1 |
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 |