From 20a075657f9c8133b65ea9cf6e8f1f633bc8a8e6 Mon Sep 17 00:00:00 2001 From: Dan Date: Mon, 16 Apr 2018 14:38:04 -0230 Subject: Documentation for various controllers and libs --- app/scripts/controllers/address-book.js | 66 +++++++++--- app/scripts/controllers/currency.js | 65 ++++++++++++ app/scripts/controllers/preferences.js | 124 +++++++++++++++++++++++ app/scripts/controllers/shapeshift.js | 74 ++++++++++++++ app/scripts/lib/buy-eth-url.js | 10 ++ app/scripts/lib/get-first-preferred-lang-code.js | 7 ++ app/scripts/lib/is-popup-or-notification.js | 12 ++- app/scripts/lib/nodeify.js | 8 ++ app/scripts/lib/util.js | 40 ++++++++ 9 files changed, 387 insertions(+), 19 deletions(-) diff --git a/app/scripts/controllers/address-book.js b/app/scripts/controllers/address-book.js index 6fb4ee114..cd6756e03 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 Overides 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 depracated) 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} Promises an 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} 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 + * + * @depricated + * @returns {array} Returns the identies array from the keyringContoller's state + * + */ _getIdentities () { return this.keyringController.memStore.getState().identities } diff --git a/app/scripts/controllers/currency.js b/app/scripts/controllers/currency.js index 36b8808aa..275219ce4 100644 --- a/app/scripts/controllers/currency.js +++ b/app/scripts/controllers/currency.js @@ -6,6 +6,22 @@ const POLLING_INTERVAL = 600000 class CurrencyController { + /** + * Controller responsible for managing data associated with the currently selected currency. + * + * @typedef {Object} CurrencyController + * @param {object} opts Overides 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 +35,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 + * + * @params {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 + * + * @params {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 + * + * @params {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 +117,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/preferences.js b/app/scripts/controllers/preferences.js index b4819d951..92c016309 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 Overides 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} Promises an undefined return value + * + */ 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} 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 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} Promises an undefined value. + * + */ 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} Promises an undefined value. + * + */ 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} 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} 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} 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/shapeshift.js b/app/scripts/controllers/shapeshift.js index 3bbfaa1c5..b24b010ea 100644 --- a/app/scripts/controllers/shapeshift.js +++ b/app/scripts/controllers/shapeshift.js @@ -6,6 +6,17 @@ 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 Overides 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 +25,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. + * @constant {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} + * + */ 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} 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 +89,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 +113,13 @@ class ShapeshiftController { } } + /** + * Saves an updated to a ShapeShiftTx in the shapeShiftTxList. If the passed ShapeShiftTx is not in the + * shapeShiftTxList, nothing happens. + * + * @params {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 +129,12 @@ class ShapeshiftController { } } + /** + * Removes a ShapeShiftTx from the shapeShiftTxList + * + * @params {ShapeShiftTx} tx The tx to remove + * + */ removeShapeShiftTx (tx) { const { shapeShiftTxList } = this.store.getState() const index = shapeShiftTxList.indexOf(index) @@ -78,6 +144,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/lib/buy-eth-url.js b/app/scripts/lib/buy-eth-url.js index b9dde3c28..c7c7bc33c 100644 --- a/app/scripts/lib/buy-eth-url.js +++ b/app/scripts/lib/buy-eth-url.js @@ -1,5 +1,15 @@ module.exports = getBuyEthUrl +/** + * Gives the caller a url at which the user can acquire eth, depending on the network they are in + * + * @param {object} opts Options required to determine the correct url + * @param {string} opts.network The network for which to return a url + * @param {string} opts.amount The amount of ETH to buy on coinbase. Only relevant if network === '1'. + * @param {string} opts.address The adderss the bought ETH should be sent to. Only relevant if network === '1'. + * @returns {string} The url at which the user can access ETH, while in the given network + * + */ function getBuyEthUrl ({ network, amount, address }) { let url switch (network) { diff --git a/app/scripts/lib/get-first-preferred-lang-code.js b/app/scripts/lib/get-first-preferred-lang-code.js index e3635434e..78448ad43 100644 --- a/app/scripts/lib/get-first-preferred-lang-code.js +++ b/app/scripts/lib/get-first-preferred-lang-code.js @@ -4,6 +4,13 @@ const allLocales = require('../../_locales/index.json') const existingLocaleCodes = allLocales.map(locale => locale.code.toLowerCase().replace('_', '-')) +/** + * Returns a preferred language code, based on settings within the user's browser. If we have no translations for the + * users preferred locales, 'en' is returned. + * + * @returns {string} A locale code, either one from the user's preferred list that we have a translation for, or 'en' + * + */ async function getFirstPreferredLangCode () { const userPreferredLocaleCodes = await promisify( extension.i18n.getAcceptLanguages, diff --git a/app/scripts/lib/is-popup-or-notification.js b/app/scripts/lib/is-popup-or-notification.js index ad3e825c0..894564def 100644 --- a/app/scripts/lib/is-popup-or-notification.js +++ b/app/scripts/lib/is-popup-or-notification.js @@ -1,8 +1,14 @@ +/** + * Indicates whether the user is viewing the app through an extension like window or through a notification. + * Used to make some style decisions on the frontend, and when deciding whether to close the popup in the backend. + * + * @returns {string} Returns 'popup' if the user is viewing through the browser ('home.html') or popup extension + * ('popup.html'). Otherwise it returns 'notification'. + * + */ module.exports = function isPopupOrNotification () { const url = window.location.href - // if (url.match(/popup.html$/) || url.match(/home.html$/)) { - // Below regexes needed for feature toggles (e.g. see line ~340 in ui/app/app.js) - // Revert below regexes to above commented out regexes before merge to master + if (url.match(/popup.html(?:\?.+)*$/) || url.match(/home.html(?:\?.+)*$/) || url.match(/home.html(?:#.*)*$/)) { return 'popup' diff --git a/app/scripts/lib/nodeify.js b/app/scripts/lib/nodeify.js index 9b595d93c..d568035c0 100644 --- a/app/scripts/lib/nodeify.js +++ b/app/scripts/lib/nodeify.js @@ -1,6 +1,14 @@ const promiseToCallback = require('promise-to-callback') const noop = function () {} +/** + * A generator that returns a function which, when passed a promise, can treat that promise as a node style callback. + * The primse advantage being that callbacks are better for error handling. + * + * @params {Function} fn The function to handle as a callback + * @params {Object} context The context in which the fn is to be called, most often a this reference + * + */ module.exports = function nodeify (fn, context) { return function () { const args = [].slice.call(arguments) diff --git a/app/scripts/lib/util.js b/app/scripts/lib/util.js index 6dee9edf0..11565790f 100644 --- a/app/scripts/lib/util.js +++ b/app/scripts/lib/util.js @@ -10,11 +10,28 @@ module.exports = { BnMultiplyByFraction, } +/** + * Generates an example stack trace + * + * @returns {string} A stack trace + * + */ function getStack () { const stack = new Error('Stack trace generator - not an error').stack return stack } +/** + * Checks whether a given balance of ETH, represented as a hex string, is sufficient to pay a value plus a gas fee + * + * @param {object} txParams Contains data about a transaction + * @param {string} txParams.gas The gas for a transaction + * @param {string} txParams.gasPrice The price per gas for the transaction + * @param {string} txParams.value The value of ETH to send + * @param {string} hexBalance A balance of ETH represented as a hex string + * @returns {boolean} Whether the balance is greater than or equal to the value plus the value of gas times gasPrice + * + */ function sufficientBalance (txParams, hexBalance) { // validate hexBalance is a hex string assert.equal(typeof hexBalance, 'string', 'sufficientBalance - hexBalance is not a hex string') @@ -29,14 +46,37 @@ function sufficientBalance (txParams, hexBalance) { return balance.gte(maxCost) } +/** + * Converts a BN object to a hex string with a '0x' prefix + * + * @param {BN} inputBn Description + * @returns {string} A hex string + * + */ function bnToHex (inputBn) { return ethUtil.addHexPrefix(inputBn.toString(16)) } +/** + * Converts a hex string to a BN object + * + * @param {string} inputHex A number represented as a hex string + * @returns {Object} A BN object + * + */ function hexToBn (inputHex) { return new BN(ethUtil.stripHexPrefix(inputHex), 16) } +/** + * Used to multiply a BN by a fraction + * + * @param {BN} targetBN The number to multiply by a fraction + * @param {number|string} numerator + * @param {number|string} denominator + * @returns {BN} The product of the multiplication + * + */ function BnMultiplyByFraction (targetBN, numerator, denominator) { const numBN = new BN(numerator) const denomBN = new BN(denominator) -- cgit v1.2.3