From e226b10a89d87af07c7c35ff1251a8264f3bb1b8 Mon Sep 17 00:00:00 2001 From: Alexander Tseung Date: Tue, 28 Nov 2017 20:24:35 -0800 Subject: Add react-router to allow use of the browser back button --- app/scripts/lib/is-popup-or-notification.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'app/scripts') diff --git a/app/scripts/lib/is-popup-or-notification.js b/app/scripts/lib/is-popup-or-notification.js index e2999411f..ad3e825c0 100644 --- a/app/scripts/lib/is-popup-or-notification.js +++ b/app/scripts/lib/is-popup-or-notification.js @@ -3,7 +3,8 @@ module.exports = function isPopupOrNotification () { // 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(?:\?.+)*$/)) { + if (url.match(/popup.html(?:\?.+)*$/) || + url.match(/home.html(?:\?.+)*$/) || url.match(/home.html(?:#.*)*$/)) { return 'popup' } else { return 'notification' -- cgit v1.2.3 From 0685381fdcd3ab8cebc76bf73e9678deaf5d15f8 Mon Sep 17 00:00:00 2001 From: kumavis Date: Fri, 6 Apr 2018 12:05:24 -0700 Subject: metamask - attempt recovery from empty vault --- app/scripts/background.js | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'app/scripts') diff --git a/app/scripts/background.js b/app/scripts/background.js index ec586f642..a4d95289b 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -78,6 +78,28 @@ async function loadStateFromPersistence () { diskStore.getState() || migrator.generateInitialState(firstTimeState) + // check if somehow state is empty + // this should never happen but new error reporting suggests that it has + // for a small number of users + // https://github.com/metamask/metamask-extension/issues/3919 + if (versionedData && !versionedData.data) { + // try to recover from diskStore incase only localStore is bad + const diskStoreState = diskStore.getState() + if (diskStoreState && diskStoreState.data) { + // we were able to recover (though it might be old) + versionedData = diskStoreState + const vaultStructure = getObjStructure(versionedData) + raven.captureMessage('MetaMask - Empty vault found - recovered from diskStore', { + // "extra" key is required by Sentry + extra: { vaultStructure }, + }) + } else { + // unable to recover, clear state + versionedData = migrator.generateInitialState(firstTimeState) + raven.captureMessage('MetaMask - Empty vault found - unable to recover') + } + } + // report migration errors to sentry migrator.on('error', (err) => { // get vault structure without secrets -- cgit v1.2.3 From 6f4dbd6d4a27888ccff88d336ae4340cb69c90e7 Mon Sep 17 00:00:00 2001 From: kumavis Date: Fri, 6 Apr 2018 12:08:51 -0700 Subject: metamask - never persist if state is missing data --- app/scripts/background.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'app/scripts') diff --git a/app/scripts/background.js b/app/scripts/background.js index a4d95289b..837fd7133 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -162,9 +162,9 @@ function setupController (initState, initLangCode) { asStream(controller.store), debounce(1000), storeTransform(versionifyData), - storeTransform(syncDataWithExtension), + storeTransform(persistData), (error) => { - log.error('pump hit error', error) + log.error('MetaMask - Persistence pipeline failed', error) } ) @@ -173,7 +173,10 @@ function setupController (initState, initLangCode) { return versionedData } - function syncDataWithExtension(state) { + function persistData(state) { + if (!state.data) { + throw new Error('MetaMask - updated state is missing data', state) + } if (localStore.isSupported) { localStore.set(state) .catch((err) => { -- cgit v1.2.3 From f9a6feb26369048d63cc3794f4f94db4c89ed287 Mon Sep 17 00:00:00 2001 From: kumavis Date: Fri, 6 Apr 2018 12:10:39 -0700 Subject: metamask - never persist if state is missing --- app/scripts/background.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'app/scripts') diff --git a/app/scripts/background.js b/app/scripts/background.js index 837fd7133..5878cd2e8 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -174,8 +174,11 @@ function setupController (initState, initLangCode) { } function persistData(state) { + if (!state) { + throw new Error('MetaMask - updated state is missing', state) + } if (!state.data) { - throw new Error('MetaMask - updated state is missing data', state) + throw new Error('MetaMask - updated state does not have data', state) } if (localStore.isSupported) { localStore.set(state) -- cgit v1.2.3 From 8fb1237d6425655b88d0bca6ef000d7b77939617 Mon Sep 17 00:00:00 2001 From: Dan Date: Thu, 12 Apr 2018 13:17:46 -0230 Subject: Documentation for environemnt-type.js --- app/scripts/lib/environment-type.js | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'app/scripts') diff --git a/app/scripts/lib/environment-type.js b/app/scripts/lib/environment-type.js index 7966926eb..f13a1574d 100644 --- a/app/scripts/lib/environment-type.js +++ b/app/scripts/lib/environment-type.js @@ -1,3 +1,12 @@ +/** + * Used to determine the window type through which the app is being viewed. + * - 'popup' refers to the extension opened through the browser app icon (in top right corner in chrome and firefox) + * - 'responsive' refers to the main browser window + * - 'notification' refers to the popup that appears in its own window when taking action outside of metamask + * + * @returns {string} A single word label that represents the type of window through which the app is being viewed + * + */ module.exports = function environmentType () { const url = window.location.href if (url.match(/popup.html$/)) { -- cgit v1.2.3 From 4780f825b1bfb33f03c60133f293b122b8b43be4 Mon Sep 17 00:00:00 2001 From: bitpshr Date: Thu, 12 Apr 2018 23:26:50 -0400 Subject: Add ComposableObservableStore for subscription management --- app/scripts/lib/ComposableObservableStore.js | 49 +++++++++++++ app/scripts/metamask-controller.js | 106 +++++++++------------------ 2 files changed, 85 insertions(+), 70 deletions(-) create mode 100644 app/scripts/lib/ComposableObservableStore.js (limited to 'app/scripts') diff --git a/app/scripts/lib/ComposableObservableStore.js b/app/scripts/lib/ComposableObservableStore.js new file mode 100644 index 000000000..688594b6d --- /dev/null +++ b/app/scripts/lib/ComposableObservableStore.js @@ -0,0 +1,49 @@ +const ObservableStore = require('obs-store') + +/** + * An ObservableStore that can composes a flat + * structure of child stores based on configuration + */ +class ComposableObservableStore extends ObservableStore { + /** + * Create a new store + * + * @param {Object} [initState] - The initial store state + * @param {Object} [config] - Map of internal state keys to child stores + */ + constructor (initState, config) { + super() + this.updateStructure(config) + } + + /** + * Composes a new internal store subscription structure + * + * @param {Object} [config] - Map of internal state keys to child stores + */ + updateStructure (config) { + this.config = config + this.removeAllListeners() + for (const key in config) { + config[key].subscribe((state) => { + this.updateState({ [key]: state }) + }) + } + } + + /** + * Merges all child store state into a single object rather than + * returning an object keyed by child store class name + * + * @returns {Object} - Object containing merged child store state + */ + getFlatState () { + let flatState = {} + for (const key in this.config) { + flatState = { ...flatState, ...this.config[key].getState() } + } + return flatState + } +} + +module.exports = ComposableObservableStore diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index b96acc9da..fa7890c50 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -5,10 +5,10 @@ */ const EventEmitter = require('events') -const extend = require('xtend') const pump = require('pump') const Dnode = require('dnode') const ObservableStore = require('obs-store') +const ComposableObservableStore = require('./lib/ComposableObservableStore') const asStream = require('obs-store/lib/asStream') const AccountTracker = require('./lib/account-tracker') const RpcEngine = require('json-rpc-engine') @@ -65,7 +65,7 @@ module.exports = class MetamaskController extends EventEmitter { this.platform = opts.platform // observable state store - this.store = new ObservableStore(initState) + this.store = new ComposableObservableStore(initState) // lock to ensure only one vault created at once this.createVaultMutex = new Mutex() @@ -184,53 +184,36 @@ module.exports = class MetamaskController extends EventEmitter { this.typedMessageManager = new TypedMessageManager() this.publicConfigStore = this.initPublicConfigStore() - // manual disk state subscriptions - this.txController.store.subscribe((state) => { - this.store.updateState({ TransactionController: state }) - }) - this.keyringController.store.subscribe((state) => { - this.store.updateState({ KeyringController: state }) - }) - this.preferencesController.store.subscribe((state) => { - this.store.updateState({ PreferencesController: state }) - }) - this.addressBookController.store.subscribe((state) => { - this.store.updateState({ AddressBookController: state }) - }) - this.currencyController.store.subscribe((state) => { - this.store.updateState({ CurrencyController: state }) - }) - this.noticeController.store.subscribe((state) => { - this.store.updateState({ NoticeController: state }) - }) - this.shapeshiftController.store.subscribe((state) => { - this.store.updateState({ ShapeShiftController: state }) - }) - this.networkController.store.subscribe((state) => { - this.store.updateState({ NetworkController: state }) + this.store.updateStructure({ + TransactionController: this.txController.store, + KeyringController: this.keyringController.store, + PreferencesController: this.preferencesController.store, + AddressBookController: this.addressBookController.store, + CurrencyController: this.currencyController.store, + NoticeController: this.noticeController.store, + ShapeShiftController: this.shapeshiftController.store, + NetworkController: this.networkController.store, + InfuraController: this.infuraController.store, }) - this.infuraController.store.subscribe((state) => { - this.store.updateState({ InfuraController: state }) + this.memStore = new ComposableObservableStore(null, { + NetworkController: this.networkController.store, + AccountTracker: this.accountTracker.store, + TxController: this.txController.memStore, + BalancesController: this.balancesController.store, + MessageManager: this.messageManager.memStore, + PersonalMessageManager: this.personalMessageManager.memStore, + TypesMessageManager: this.typedMessageManager.memStore, + KeyringController: this.keyringController.memStore, + PreferencesController: this.preferencesController.store, + RecentBlocksController: this.recentBlocksController.store, + AddressBookController: this.addressBookController.store, + CurrencyController: this.currencyController.store, + NoticeController: this.noticeController.memStore, + ShapeshiftController: this.shapeshiftController.store, + InfuraController: this.infuraController.store, }) - - // manual mem state subscriptions - const sendUpdate = this.sendUpdate.bind(this) - this.networkController.store.subscribe(sendUpdate) - this.accountTracker.store.subscribe(sendUpdate) - this.txController.memStore.subscribe(sendUpdate) - this.balancesController.store.subscribe(sendUpdate) - this.messageManager.memStore.subscribe(sendUpdate) - this.personalMessageManager.memStore.subscribe(sendUpdate) - this.typedMessageManager.memStore.subscribe(sendUpdate) - this.keyringController.memStore.subscribe(sendUpdate) - this.preferencesController.store.subscribe(sendUpdate) - this.recentBlocksController.store.subscribe(sendUpdate) - this.addressBookController.store.subscribe(sendUpdate) - this.currencyController.store.subscribe(sendUpdate) - this.noticeController.memStore.subscribe(sendUpdate) - this.shapeshiftController.store.subscribe(sendUpdate) - this.infuraController.store.subscribe(sendUpdate) + this.memStore.subscribe(this.sendUpdate.bind(this)) } /** @@ -308,33 +291,16 @@ module.exports = class MetamaskController extends EventEmitter { const vault = this.keyringController.store.getState().vault const isInitialized = (!!wallet || !!vault) - return extend( - { - isInitialized, - }, - this.networkController.store.getState(), - this.accountTracker.store.getState(), - this.txController.memStore.getState(), - this.messageManager.memStore.getState(), - this.personalMessageManager.memStore.getState(), - this.typedMessageManager.memStore.getState(), - this.keyringController.memStore.getState(), - this.balancesController.store.getState(), - this.preferencesController.store.getState(), - this.addressBookController.store.getState(), - this.currencyController.store.getState(), - this.noticeController.memStore.getState(), - this.infuraController.store.getState(), - this.recentBlocksController.store.getState(), - // config manager - this.configManager.getConfig(), - this.shapeshiftController.store.getState(), - { + return { + ...{ isInitialized }, + ...this.memStore.getFlatState(), + ...this.configManager.getConfig(), + ...{ lostAccounts: this.configManager.getLostAccounts(), seedWords: this.configManager.getSeedWords(), forgottenPassword: this.configManager.getPasswordForgotten(), - } - ) + }, + } } /** -- cgit v1.2.3 From 8974f933fc97a37f5cd8dcd510ff0e6dc21d6751 Mon Sep 17 00:00:00 2001 From: bitpshr Date: Fri, 13 Apr 2018 13:13:36 -0400 Subject: Add tests for ComposableObservableStore --- app/scripts/lib/ComposableObservableStore.js | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'app/scripts') diff --git a/app/scripts/lib/ComposableObservableStore.js b/app/scripts/lib/ComposableObservableStore.js index 688594b6d..d5ee708a1 100644 --- a/app/scripts/lib/ComposableObservableStore.js +++ b/app/scripts/lib/ComposableObservableStore.js @@ -11,24 +11,24 @@ class ComposableObservableStore extends ObservableStore { * @param {Object} [initState] - The initial store state * @param {Object} [config] - Map of internal state keys to child stores */ - constructor (initState, config) { - super() - this.updateStructure(config) - } + constructor (initState, config) { + super(initState) + this.updateStructure(config) + } /** * Composes a new internal store subscription structure * * @param {Object} [config] - Map of internal state keys to child stores */ - updateStructure (config) { + updateStructure (config) { this.config = config - this.removeAllListeners() - for (const key in config) { - config[key].subscribe((state) => { - this.updateState({ [key]: state }) - }) - } + this.removeAllListeners() + for (const key in config) { + config[key].subscribe((state) => { + this.updateState({ [key]: state }) + }) + } } /** -- cgit v1.2.3 From 7129d7c0f3ce11839f8b8a576eb1e2c093fb96cc Mon Sep 17 00:00:00 2001 From: bitpshr Date: Thu, 12 Apr 2018 17:06:59 -0400 Subject: Require loglevel singleton in each module that uses it --- app/scripts/background.js | 3 +-- app/scripts/controllers/blacklist.js | 1 + app/scripts/controllers/currency.js | 1 + app/scripts/controllers/infura.js | 1 + app/scripts/controllers/network.js | 1 + app/scripts/controllers/recent-blocks.js | 1 + app/scripts/controllers/shapeshift.js | 1 + app/scripts/controllers/transactions.js | 1 + app/scripts/inpage.js | 7 +------ app/scripts/lib/createLoggerMiddleware.js | 2 ++ app/scripts/lib/local-store.js | 1 + app/scripts/lib/personal-message-manager.js | 1 + app/scripts/lib/seed-phrase-verifier.js | 1 + app/scripts/lib/typed-message-manager.js | 2 +- app/scripts/metamask-controller.js | 1 + app/scripts/ui.js | 1 + 16 files changed, 17 insertions(+), 9 deletions(-) (limited to 'app/scripts') diff --git a/app/scripts/background.js b/app/scripts/background.js index 5878cd2e8..451b0d609 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -25,8 +25,7 @@ const getObjStructure = require('./lib/getObjStructure') const STORAGE_KEY = 'metamask-config' const METAMASK_DEBUG = process.env.METAMASK_DEBUG -window.log = log -log.setDefaultLevel(METAMASK_DEBUG ? 'debug' : 'warn') +log.setDefaultLevel(process.env.METAMASK_DEBUG ? 'debug' : 'warn') const platform = new ExtensionPlatform() const notificationManager = new NotificationManager() 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..d9e0a3e34 100644 --- a/app/scripts/controllers/currency.js +++ b/app/scripts/controllers/currency.js @@ -1,5 +1,6 @@ const ObservableStore = require('obs-store') const extend = require('xtend') +const log = require('loglevel') // every ten minutes const POLLING_INTERVAL = 600000 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/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..b38b3812d 100644 --- a/app/scripts/controllers/shapeshift.js +++ b/app/scripts/controllers/shapeshift.js @@ -1,5 +1,6 @@ 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 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 diff --git a/app/scripts/inpage.js b/app/scripts/inpage.js index ec99bfc35..92c732813 100644 --- a/app/scripts/inpage.js +++ b/app/scripts/inpage.js @@ -3,16 +3,11 @@ cleanContextForImports() require('web3/dist/web3.min.js') const log = require('loglevel') const LocalMessageDuplexStream = require('post-message-stream') -// const PingStream = require('ping-pong-stream/ping') -// const endOfStream = require('end-of-stream') const setupDappAutoReload = require('./lib/auto-reload.js') const MetamaskInpageProvider = require('./lib/inpage-provider.js') restoreContextAfterImports() -const METAMASK_DEBUG = process.env.METAMASK_DEBUG -window.log = log -log.setDefaultLevel(METAMASK_DEBUG ? 'debug' : 'warn') - +log.setDefaultLevel(process.env.METAMASK_DEBUG ? 'debug' : 'warn') // // setup plugin communication diff --git a/app/scripts/lib/createLoggerMiddleware.js b/app/scripts/lib/createLoggerMiddleware.js index 2707cbd9e..fc6abf828 100644 --- a/app/scripts/lib/createLoggerMiddleware.js +++ b/app/scripts/lib/createLoggerMiddleware.js @@ -1,3 +1,5 @@ +const log = require('loglevel') + // log rpc activity module.exports = createLoggerMiddleware diff --git a/app/scripts/lib/local-store.js b/app/scripts/lib/local-store.js index 5b47985f6..2dda0ba1f 100644 --- a/app/scripts/lib/local-store.js +++ b/app/scripts/lib/local-store.js @@ -3,6 +3,7 @@ // https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/storage/local const extension = require('extensionizer') +const log = require('loglevel') module.exports = class ExtensionStore { constructor() { diff --git a/app/scripts/lib/personal-message-manager.js b/app/scripts/lib/personal-message-manager.js index 6602f5aa8..43a7d0b42 100644 --- a/app/scripts/lib/personal-message-manager.js +++ b/app/scripts/lib/personal-message-manager.js @@ -3,6 +3,7 @@ const ObservableStore = require('obs-store') const ethUtil = require('ethereumjs-util') const createId = require('./random-id') const hexRe = /^[0-9A-Fa-f]+$/g +const log = require('loglevel') module.exports = class PersonalMessageManager extends EventEmitter { diff --git a/app/scripts/lib/seed-phrase-verifier.js b/app/scripts/lib/seed-phrase-verifier.js index 9cea22029..7ba712c0d 100644 --- a/app/scripts/lib/seed-phrase-verifier.js +++ b/app/scripts/lib/seed-phrase-verifier.js @@ -1,4 +1,5 @@ const KeyringController = require('eth-keyring-controller') +const log = require('loglevel') const seedPhraseVerifier = { diff --git a/app/scripts/lib/typed-message-manager.js b/app/scripts/lib/typed-message-manager.js index 8b760790e..60042155e 100644 --- a/app/scripts/lib/typed-message-manager.js +++ b/app/scripts/lib/typed-message-manager.js @@ -3,7 +3,7 @@ const ObservableStore = require('obs-store') const createId = require('./random-id') const assert = require('assert') const sigUtil = require('eth-sig-util') - +const log = require('loglevel') module.exports = class TypedMessageManager extends EventEmitter { constructor (opts) { diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index fa7890c50..dbb8efc6e 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -44,6 +44,7 @@ const BN = require('ethereumjs-util').BN const GWEI_BN = new BN('1000000000') const percentile = require('percentile') const seedPhraseVerifier = require('./lib/seed-phrase-verifier') +const log = require('loglevel') module.exports = class MetamaskController extends EventEmitter { diff --git a/app/scripts/ui.js b/app/scripts/ui.js index 13c7ac5ec..c326ca1c3 100644 --- a/app/scripts/ui.js +++ b/app/scripts/ui.js @@ -9,6 +9,7 @@ const ExtensionPlatform = require('./platforms/extension') const NotificationManager = require('./lib/notification-manager') const notificationManager = new NotificationManager() const setupRaven = require('./lib/setupRaven') +const log = require('loglevel') start().catch(log.error) -- cgit v1.2.3 From d0447f90583275868bb72aa7ae8f670bf3668173 Mon Sep 17 00:00:00 2001 From: bitpshr Date: Mon, 16 Apr 2018 11:21:06 -0400 Subject: Maintain token prices using a background service --- app/scripts/controllers/token-rates.js | 76 ++++++++++++++++++++++++++++++++++ app/scripts/metamask-controller.js | 7 ++++ 2 files changed, 83 insertions(+) create mode 100644 app/scripts/controllers/token-rates.js (limited to 'app/scripts') diff --git a/app/scripts/controllers/token-rates.js b/app/scripts/controllers/token-rates.js new file mode 100644 index 000000000..85a8ca24e --- /dev/null +++ b/app/scripts/controllers/token-rates.js @@ -0,0 +1,76 @@ +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 () { + 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/metamask-controller.js b/app/scripts/metamask-controller.js index fa7890c50..750a97b86 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -34,6 +34,7 @@ const PersonalMessageManager = require('./lib/personal-message-manager') const TypedMessageManager = require('./lib/typed-message-manager') const TransactionController = require('./controllers/transactions') const BalancesController = require('./controllers/computed-balances') +const TokenRatesController = require('./controllers/token-rates') const ConfigManager = require('./lib/config-manager') const nodeify = require('./lib/nodeify') const accountImporter = require('./account-import-strategies') @@ -104,6 +105,11 @@ module.exports = class MetamaskController extends EventEmitter { this.provider = this.initializeProvider() this.blockTracker = this.provider._blockTracker + // token exchange rate tracker + this.tokenRatesController = new TokenRatesController({ + preferences: this.preferencesController.store, + }) + this.recentBlocksController = new RecentBlocksController({ blockTracker: this.blockTracker, provider: this.provider, @@ -201,6 +207,7 @@ module.exports = class MetamaskController extends EventEmitter { AccountTracker: this.accountTracker.store, TxController: this.txController.memStore, BalancesController: this.balancesController.store, + TokenRatesController: this.tokenRatesController.store, MessageManager: this.messageManager.memStore, PersonalMessageManager: this.personalMessageManager.memStore, TypesMessageManager: this.typedMessageManager.memStore, -- cgit v1.2.3 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(-) (limited to 'app/scripts') 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 From b4912f29cd3b6e0a5df81e9f69acc50b03e31c0c Mon Sep 17 00:00:00 2001 From: bitpshr Date: Mon, 16 Apr 2018 17:45:18 -0400 Subject: Disable token price polling when no client is active --- app/scripts/background.js | 3 +++ app/scripts/controllers/token-rates.js | 1 + app/scripts/metamask-controller.js | 9 +++++++++ 3 files changed, 13 insertions(+) (limited to 'app/scripts') diff --git a/app/scripts/background.js b/app/scripts/background.js index 5878cd2e8..3f0e289c9 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -200,6 +200,7 @@ function setupController (initState, initLangCode) { if (isMetaMaskInternalProcess) { // communication with popup popupIsOpen = popupIsOpen || (remotePort.name === 'popup') + controller.isClientOpen = true controller.setupTrustedCommunication(portStream, 'MetaMask') // record popup as closed if (remotePort.sender.url.match(/home.html$/)) { @@ -211,6 +212,8 @@ function setupController (initState, initLangCode) { if (remotePort.sender.url.match(/home.html$/)) { openMetamaskTabsIDs[remotePort.sender.tab.id] = false } + controller.isClientOpen = popupIsOpen || + Object.keys(openMetamaskTabsIDs).some(key => openMetamaskTabsIDs[key]) }) } if (remotePort.name === 'notification') { diff --git a/app/scripts/controllers/token-rates.js b/app/scripts/controllers/token-rates.js index 85a8ca24e..22e3e8154 100644 --- a/app/scripts/controllers/token-rates.js +++ b/app/scripts/controllers/token-rates.js @@ -23,6 +23,7 @@ class TokenRatesController { * 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 diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 750a97b86..73b7cfbb0 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -269,6 +269,7 @@ module.exports = class MetamaskController extends EventEmitter { // memStore -> transform -> publicConfigStore this.on('update', (memState) => { + this.isClientOpenAndUnlocked = memState.isUnlocked && this._isClientOpen const publicState = selectPublicState(memState) publicConfigStore.putState(publicState) }) @@ -1030,4 +1031,12 @@ module.exports = class MetamaskController extends EventEmitter { } } + set isClientOpen (open) { + this._isClientOpen = open + this.isClientOpenAndUnlocked = this.getState().isUnlocked && open + } + + set isClientOpenAndUnlocked (active) { + this.tokenRatesController.isActive = active + } } -- cgit v1.2.3 From 23acddf8f6fe4cb2d23e9b508c9b95f1f50fe32a Mon Sep 17 00:00:00 2001 From: Dan Date: Mon, 16 Apr 2018 19:45:27 -0230 Subject: @params -> @param fix --- app/scripts/controllers/currency.js | 8 ++++---- app/scripts/controllers/shapeshift.js | 4 ++-- app/scripts/lib/nodeify.js | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) (limited to 'app/scripts') diff --git a/app/scripts/controllers/currency.js b/app/scripts/controllers/currency.js index 275219ce4..6b166c151 100644 --- a/app/scripts/controllers/currency.js +++ b/app/scripts/controllers/currency.js @@ -1,4 +1,4 @@ -const ObservableStore = require('obs-store') + const ObservableStore = require('obs-store') const extend = require('xtend') // every ten minutes @@ -48,7 +48,7 @@ class CurrencyController { /** * A setter for the currentCurrency property * - * @params {string} currentCurrency The new currency to set as the currentCurrency in the store + * @param {string} currentCurrency The new currency to set as the currentCurrency in the store * */ setCurrentCurrency (currentCurrency) { @@ -68,7 +68,7 @@ class CurrencyController { /** * A setter for the conversionRate property * - * @params {number} conversionRate The new rate to set as the conversionRate in the store + * @param {number} conversionRate The new rate to set as the conversionRate in the store * */ setConversionRate (conversionRate) { @@ -89,7 +89,7 @@ class CurrencyController { /** * A setter for the conversionDate property * - * @params {number} conversionDate The date, expressed in milliseconds since midnight of January 1, 1970, that the + * @param {number} conversionDate The date, expressed in milliseconds since midnight of January 1, 1970, that the * conversionRate was set * */ diff --git a/app/scripts/controllers/shapeshift.js b/app/scripts/controllers/shapeshift.js index b24b010ea..10753b722 100644 --- a/app/scripts/controllers/shapeshift.js +++ b/app/scripts/controllers/shapeshift.js @@ -117,7 +117,7 @@ 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 + * @param {ShapeShiftTx} tx The updated tx to save, if it exists in the current shapeShiftTxList * */ saveTx (tx) { @@ -132,7 +132,7 @@ class ShapeshiftController { /** * Removes a ShapeShiftTx from the shapeShiftTxList * - * @params {ShapeShiftTx} tx The tx to remove + * @param {ShapeShiftTx} tx The tx to remove * */ removeShapeShiftTx (tx) { diff --git a/app/scripts/lib/nodeify.js b/app/scripts/lib/nodeify.js index d568035c0..0a3891ac3 100644 --- a/app/scripts/lib/nodeify.js +++ b/app/scripts/lib/nodeify.js @@ -5,8 +5,8 @@ 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 + * @param {Function} fn The function to handle as a callback + * @param {Object} context The context in which the fn is to be called, most often a this reference * */ module.exports = function nodeify (fn, context) { -- cgit v1.2.3 From cc7e71488ea9df9246f1bfcf9db9a4695fd515de Mon Sep 17 00:00:00 2001 From: Dan Date: Mon, 16 Apr 2018 19:47:52 -0230 Subject: Spell deprecated correctly. --- app/scripts/controllers/address-book.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'app/scripts') diff --git a/app/scripts/controllers/address-book.js b/app/scripts/controllers/address-book.js index cd6756e03..62205a430 100644 --- a/app/scripts/controllers/address-book.js +++ b/app/scripts/controllers/address-book.js @@ -13,7 +13,7 @@ class 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 + * @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 @@ -100,7 +100,7 @@ class AddressBookController { * Retrieves identities from the keyring controller in order to avoid * duplication * - * @depricated + * @deprecated * @returns {array} Returns the identies array from the keyringContoller's state * */ -- cgit v1.2.3 From e9ca7199ab9b63ef728fa93f8e98295c3096c553 Mon Sep 17 00:00:00 2001 From: Dan Date: Mon, 16 Apr 2018 21:23:29 -0230 Subject: Typo fixes, type fixes on the return clauses of the buyEthUrl and getPrefferedLangCode functions. --- app/scripts/controllers/address-book.js | 2 +- app/scripts/controllers/currency.js | 2 +- app/scripts/controllers/preferences.js | 2 +- app/scripts/controllers/shapeshift.js | 2 +- app/scripts/lib/buy-eth-url.js | 5 +++-- app/scripts/lib/nodeify.js | 2 +- 6 files changed, 8 insertions(+), 7 deletions(-) (limited to 'app/scripts') diff --git a/app/scripts/controllers/address-book.js b/app/scripts/controllers/address-book.js index 62205a430..222910767 100644 --- a/app/scripts/controllers/address-book.js +++ b/app/scripts/controllers/address-book.js @@ -10,7 +10,7 @@ class AddressBookController { * addresses and all currently owned addresses. * * @typedef {Object} AddressBookController - * @param {object} opts Overides the defaults for the initial state of this.store + * @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 diff --git a/app/scripts/controllers/currency.js b/app/scripts/controllers/currency.js index 6b166c151..c23c7f616 100644 --- a/app/scripts/controllers/currency.js +++ b/app/scripts/controllers/currency.js @@ -10,7 +10,7 @@ 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 + * @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 diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js index 92c016309..cf0ca13a8 100644 --- a/app/scripts/controllers/preferences.js +++ b/app/scripts/controllers/preferences.js @@ -7,7 +7,7 @@ class PreferencesController { /** * * @typedef {Object} PreferencesController - * @param {object} opts Overides the defaults for the initial state of this.store + * @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 diff --git a/app/scripts/controllers/shapeshift.js b/app/scripts/controllers/shapeshift.js index 10753b722..17994d2db 100644 --- a/app/scripts/controllers/shapeshift.js +++ b/app/scripts/controllers/shapeshift.js @@ -11,7 +11,7 @@ class ShapeshiftController { * 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 + * @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 diff --git a/app/scripts/lib/buy-eth-url.js b/app/scripts/lib/buy-eth-url.js index c7c7bc33c..4e2d0bc79 100644 --- a/app/scripts/lib/buy-eth-url.js +++ b/app/scripts/lib/buy-eth-url.js @@ -6,8 +6,9 @@ module.exports = getBuyEthUrl * @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 + * @param {string} opts.address The address the bought ETH should be sent to. Only relevant if network === '1'. + * @returns {string|undefined} The url at which the user can access ETH, while in the given network. If the passed + * network does not match any of the specified cases, or if no network is given, returns undefined. * */ function getBuyEthUrl ({ network, amount, address }) { diff --git a/app/scripts/lib/nodeify.js b/app/scripts/lib/nodeify.js index 0a3891ac3..25be6537b 100644 --- a/app/scripts/lib/nodeify.js +++ b/app/scripts/lib/nodeify.js @@ -3,7 +3,7 @@ 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. + * The prime advantage being that callbacks are better for error handling. * * @param {Function} fn The function to handle as a callback * @param {Object} context The context in which the fn is to be called, most often a this reference -- cgit v1.2.3 From 128cb1af46224da9c0f5158107b6ac2da40b0622 Mon Sep 17 00:00:00 2001 From: Dan Date: Tue, 17 Apr 2018 00:54:16 -0230 Subject: Improve documentation of promises that return undefined. --- app/scripts/controllers/address-book.js | 2 +- app/scripts/controllers/preferences.js | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'app/scripts') diff --git a/app/scripts/controllers/address-book.js b/app/scripts/controllers/address-book.js index 222910767..c91e6b2e4 100644 --- a/app/scripts/controllers/address-book.js +++ b/app/scripts/controllers/address-book.js @@ -37,7 +37,7 @@ class AddressBookController { * * @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 + * @returns {Promise} Promise resolves with undefined * */ setAddressBook (address, name) { diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js index cf0ca13a8..653e6d762 100644 --- a/app/scripts/controllers/preferences.js +++ b/app/scripts/controllers/preferences.js @@ -66,7 +66,7 @@ class PreferencesController { * Setter for the `selectedAddress` property * * @param {string} _address A new hex address for an account - * @returns {Promise} Promises an undefined return value + * @returns {Promise} Promise resolves with undefined * */ setSelectedAddress (_address) { @@ -159,7 +159,7 @@ class PreferencesController { * 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. + * @returns {Promise} Promise resolves with undefined * */ updateFrequentRpcList (_url) { @@ -174,7 +174,7 @@ 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. + * @returns {Promise} Promise resolves with undefined * */ setCurrentAccountTab (currentAccountTab) { -- cgit v1.2.3 From 7b5d506cec7fe182f64e71772c7f17bf697f31d8 Mon Sep 17 00:00:00 2001 From: Dan Date: Tue, 17 Apr 2018 00:55:10 -0230 Subject: Document async function as returning a promise. --- app/scripts/lib/get-first-preferred-lang-code.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/scripts') diff --git a/app/scripts/lib/get-first-preferred-lang-code.js b/app/scripts/lib/get-first-preferred-lang-code.js index 78448ad43..5473fccf0 100644 --- a/app/scripts/lib/get-first-preferred-lang-code.js +++ b/app/scripts/lib/get-first-preferred-lang-code.js @@ -8,7 +8,7 @@ const existingLocaleCodes = allLocales.map(locale => locale.code.toLowerCase().r * 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' + * @returns {Promise} Promises a locale code, either one from the user's preferred list that we have a translation for, or 'en' * */ async function getFirstPreferredLangCode () { -- cgit v1.2.3 From 6da00c4061b4af1bb282c9ad68eaa2deef84093b Mon Sep 17 00:00:00 2001 From: Dan Date: Tue, 17 Apr 2018 00:56:46 -0230 Subject: Add missing descriptions in util.js --- app/scripts/lib/util.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'app/scripts') diff --git a/app/scripts/lib/util.js b/app/scripts/lib/util.js index 11565790f..cb0d7e5c1 100644 --- a/app/scripts/lib/util.js +++ b/app/scripts/lib/util.js @@ -49,8 +49,8 @@ function sufficientBalance (txParams, hexBalance) { /** * Converts a BN object to a hex string with a '0x' prefix * - * @param {BN} inputBn Description - * @returns {string} A hex string + * @param {BN} inputBn The BN to convert to a hex string + * @returns {string} A '0x' prefixed hex string * */ function bnToHex (inputBn) { @@ -72,8 +72,8 @@ function hexToBn (inputHex) { * 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 + * @param {number|string} numerator The numerator of the fraction multiplier + * @param {number|string} denominator The denominator of the fraction multiplier * @returns {BN} The product of the multiplication * */ -- cgit v1.2.3 From b0a105ce809b8b7e5e4431bd1ddecc523586cad0 Mon Sep 17 00:00:00 2001 From: Alexander Tseung Date: Mon, 16 Apr 2018 23:03:47 -0700 Subject: Fix confirmation popup not always opening --- app/scripts/background.js | 66 ++++++++++++++++++++--------- app/scripts/lib/enums.js | 9 ++++ app/scripts/lib/environment-type.js | 10 ----- app/scripts/lib/is-popup-or-notification.js | 12 ------ app/scripts/lib/util.js | 32 ++++++++++---- app/scripts/ui.js | 7 +-- 6 files changed, 83 insertions(+), 53 deletions(-) create mode 100644 app/scripts/lib/enums.js delete mode 100644 app/scripts/lib/environment-type.js delete mode 100644 app/scripts/lib/is-popup-or-notification.js (limited to 'app/scripts') diff --git a/app/scripts/background.js b/app/scripts/background.js index 35c484ec5..6550e8944 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -21,6 +21,11 @@ const setupMetamaskMeshMetrics = require('./lib/setupMetamaskMeshMetrics') const EdgeEncryptor = require('./edge-encryptor') const getFirstPreferredLangCode = require('./lib/get-first-preferred-lang-code') const getObjStructure = require('./lib/getObjStructure') +const { + ENVIRONMENT_TYPE_POPUP, + ENVIRONMENT_TYPE_NOTIFICATION, + ENVIRONMENT_TYPE_FULLSCREEN, +} = require('./lib/enums') const STORAGE_KEY = 'metamask-config' const METAMASK_DEBUG = process.env.METAMASK_DEBUG @@ -43,7 +48,7 @@ const isEdge = !isIE && !!window.StyleMedia let popupIsOpen = false let notificationIsOpen = false -let openMetamaskTabsIDs = {} +const openMetamaskTabsIDs = {} // state persistence const diskStore = new LocalStorageStore({ storageKey: STORAGE_KEY }) @@ -172,7 +177,7 @@ function setupController (initState, initLangCode) { return versionedData } - function persistData(state) { + function persistData (state) { if (!state) { throw new Error('MetaMask - updated state is missing', state) } @@ -191,33 +196,53 @@ function setupController (initState, initLangCode) { // // connect to other contexts // - extension.runtime.onConnect.addListener(connectRemote) + + const metamaskInternalProcessHash = { + [ENVIRONMENT_TYPE_POPUP]: true, + [ENVIRONMENT_TYPE_NOTIFICATION]: true, + [ENVIRONMENT_TYPE_FULLSCREEN]: true, + } + + const isClientOpenStatus = () => { + return popupIsOpen || Boolean(Object.keys(openMetamaskTabsIDs).length) || notificationIsOpen + } + function connectRemote (remotePort) { - const isMetaMaskInternalProcess = remotePort.name === 'popup' || remotePort.name === 'notification' + const processName = remotePort.name + const isMetaMaskInternalProcess = metamaskInternalProcessHash[processName] const portStream = new PortStream(remotePort) + if (isMetaMaskInternalProcess) { // communication with popup - popupIsOpen = popupIsOpen || (remotePort.name === 'popup') controller.isClientOpen = true controller.setupTrustedCommunication(portStream, 'MetaMask') - // record popup as closed - if (remotePort.sender.url.match(/home.html$/)) { - openMetamaskTabsIDs[remotePort.sender.tab.id] = true - } - if (remotePort.name === 'popup') { + + if (processName === ENVIRONMENT_TYPE_POPUP) { + popupIsOpen = true + endOfStream(portStream, () => { popupIsOpen = false - if (remotePort.sender.url.match(/home.html$/)) { - openMetamaskTabsIDs[remotePort.sender.tab.id] = false - } - controller.isClientOpen = popupIsOpen || - Object.keys(openMetamaskTabsIDs).some(key => openMetamaskTabsIDs[key]) + controller.isClientOpen = isClientOpenStatus() }) } - if (remotePort.name === 'notification') { + + if (processName === ENVIRONMENT_TYPE_NOTIFICATION) { + notificationIsOpen = true + endOfStream(portStream, () => { notificationIsOpen = false + controller.isClientOpen = isClientOpenStatus() + }) + } + + if (processName === ENVIRONMENT_TYPE_FULLSCREEN) { + const tabId = remotePort.sender.tab.id + openMetamaskTabsIDs[tabId] = true + + endOfStream(portStream, () => { + delete openMetamaskTabsIDs[tabId] + controller.isClientOpen = isClientOpenStatus() }) } } else { @@ -260,10 +285,11 @@ function setupController (initState, initLangCode) { // popup trigger function triggerUi () { - extension.tabs.query({ active: true }, (tabs) => { - const currentlyActiveMetamaskTab = tabs.find(tab => openMetamaskTabsIDs[tab.id]) - if (!popupIsOpen && !currentlyActiveMetamaskTab && !notificationIsOpen) notificationManager.showPopup() - notificationIsOpen = true + extension.tabs.query({ active: true }, tabs => { + const currentlyActiveMetamaskTab = Boolean(tabs.find(tab => openMetamaskTabsIDs[tab.id])) + if (!popupIsOpen && !currentlyActiveMetamaskTab && !notificationIsOpen) { + notificationManager.showPopup() + } }) } diff --git a/app/scripts/lib/enums.js b/app/scripts/lib/enums.js new file mode 100644 index 000000000..0a3afca47 --- /dev/null +++ b/app/scripts/lib/enums.js @@ -0,0 +1,9 @@ +const ENVIRONMENT_TYPE_POPUP = 'popup' +const ENVIRONMENT_TYPE_NOTIFICATION = 'notification' +const ENVIRONMENT_TYPE_FULLSCREEN = 'fullscreen' + +module.exports = { + ENVIRONMENT_TYPE_POPUP, + ENVIRONMENT_TYPE_NOTIFICATION, + ENVIRONMENT_TYPE_FULLSCREEN, +} diff --git a/app/scripts/lib/environment-type.js b/app/scripts/lib/environment-type.js deleted file mode 100644 index 7966926eb..000000000 --- a/app/scripts/lib/environment-type.js +++ /dev/null @@ -1,10 +0,0 @@ -module.exports = function environmentType () { - const url = window.location.href - if (url.match(/popup.html$/)) { - return 'popup' - } else if (url.match(/home.html$/)) { - return 'responsive' - } else { - return 'notification' - } -} diff --git a/app/scripts/lib/is-popup-or-notification.js b/app/scripts/lib/is-popup-or-notification.js deleted file mode 100644 index ad3e825c0..000000000 --- a/app/scripts/lib/is-popup-or-notification.js +++ /dev/null @@ -1,12 +0,0 @@ -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' - } else { - return 'notification' - } -} diff --git a/app/scripts/lib/util.js b/app/scripts/lib/util.js index 6dee9edf0..df815906f 100644 --- a/app/scripts/lib/util.js +++ b/app/scripts/lib/util.js @@ -1,20 +1,27 @@ const ethUtil = require('ethereumjs-util') const assert = require('assert') const BN = require('bn.js') - -module.exports = { - getStack, - sufficientBalance, - hexToBn, - bnToHex, - BnMultiplyByFraction, -} +const { + ENVIRONMENT_TYPE_POPUP, + ENVIRONMENT_TYPE_NOTIFICATION, + ENVIRONMENT_TYPE_FULLSCREEN, +} = require('./enums') function getStack () { const stack = new Error('Stack trace generator - not an error').stack return stack } +const getEnvironmentType = (url = window.location.href) => { + if (url.match(/popup.html(?:\?.+)*$/)) { + return ENVIRONMENT_TYPE_POPUP + } else if (url.match(/home.html(?:\?.+)*$/) || url.match(/home.html(?:#.*)*$/)) { + return ENVIRONMENT_TYPE_FULLSCREEN + } else { + return ENVIRONMENT_TYPE_NOTIFICATION + } +} + function sufficientBalance (txParams, hexBalance) { // validate hexBalance is a hex string assert.equal(typeof hexBalance, 'string', 'sufficientBalance - hexBalance is not a hex string') @@ -42,3 +49,12 @@ function BnMultiplyByFraction (targetBN, numerator, denominator) { const denomBN = new BN(denominator) return targetBN.mul(numBN).div(denomBN) } + +module.exports = { + getStack, + getEnvironmentType, + sufficientBalance, + hexToBn, + bnToHex, + BnMultiplyByFraction, +} diff --git a/app/scripts/ui.js b/app/scripts/ui.js index c326ca1c3..bdab29c1e 100644 --- a/app/scripts/ui.js +++ b/app/scripts/ui.js @@ -3,7 +3,8 @@ const OldMetaMaskUiCss = require('../../old-ui/css') const NewMetaMaskUiCss = require('../../ui/css') const startPopup = require('./popup-core') const PortStream = require('./lib/port-stream.js') -const isPopupOrNotification = require('./lib/is-popup-or-notification') +const { getEnvironmentType } = require('./lib/util') +const { ENVIRONMENT_TYPE_NOTIFICATION } = require('./lib/enums') const extension = require('extensionizer') const ExtensionPlatform = require('./platforms/extension') const NotificationManager = require('./lib/notification-manager') @@ -27,7 +28,7 @@ async function start() { // injectCss(css) // identify window type (popup, notification) - const windowType = isPopupOrNotification() + const windowType = getEnvironmentType(window.location.href) global.METAMASK_UI_TYPE = windowType closePopupIfOpen(windowType) @@ -69,7 +70,7 @@ async function start() { function closePopupIfOpen (windowType) { - if (windowType !== 'notification') { + if (windowType !== ENVIRONMENT_TYPE_NOTIFICATION) { // should close only chrome popup notificationManager.closePopup() } -- cgit v1.2.3 From 2c8156ebe91941309d49e8f8f1ed8e9d740bb9de Mon Sep 17 00:00:00 2001 From: Alexander Tseung Date: Wed, 18 Apr 2018 00:31:33 -0700 Subject: Fix UI getting stuck in Reveal Seed screen --- app/scripts/lib/config-manager.js | 22 +++++++++++++++++++++- app/scripts/metamask-controller.js | 2 ++ 2 files changed, 23 insertions(+), 1 deletion(-) (limited to 'app/scripts') diff --git a/app/scripts/lib/config-manager.js b/app/scripts/lib/config-manager.js index 34b603b96..63d27c40e 100644 --- a/app/scripts/lib/config-manager.js +++ b/app/scripts/lib/config-manager.js @@ -102,7 +102,6 @@ ConfigManager.prototype.setShowSeedWords = function (should) { this.setData(data) } - ConfigManager.prototype.getShouldShowSeedWords = function () { var data = this.getData() return data.showSeedWords @@ -118,6 +117,27 @@ ConfigManager.prototype.getSeedWords = function () { var data = this.getData() return data.seedWords } + +/** + * Called to set the isRevealingSeedWords flag. This happens only when the user chooses to reveal + * the seed words and not during the first time flow. + * @param {boolean} reveal - Value to set the isRevealingSeedWords flag. + */ +ConfigManager.prototype.setIsRevealingSeedWords = function (reveal = false) { + const data = this.getData() + data.isRevealingSeedWords = reveal + this.setData(data) +} + +/** + * Returns the isRevealingSeedWords flag. + * @returns {boolean|undefined} + */ +ConfigManager.prototype.getIsRevealingSeedWords = function () { + const data = this.getData() + return data.isRevealingSeedWords +} + ConfigManager.prototype.setRpcTarget = function (rpcUrl) { var config = this.getConfig() config.provider = { diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index a12b6776e..782bc50ac 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -308,6 +308,7 @@ module.exports = class MetamaskController extends EventEmitter { lostAccounts: this.configManager.getLostAccounts(), seedWords: this.configManager.getSeedWords(), forgottenPassword: this.configManager.getPasswordForgotten(), + isRevealingSeedWords: Boolean(this.configManager.getIsRevealingSeedWords()), }, } } @@ -347,6 +348,7 @@ module.exports = class MetamaskController extends EventEmitter { clearSeedWordCache: this.clearSeedWordCache.bind(this), resetAccount: nodeify(this.resetAccount, this), importAccountWithStrategy: this.importAccountWithStrategy.bind(this), + setIsRevealingSeedWords: this.configManager.setIsRevealingSeedWords.bind(this.configManager), // vault management submitPassword: nodeify(keyringController.submitPassword, keyringController), -- cgit v1.2.3 From 6d96b1a2ab0ac09ca8e5948ff11d2924faef4cd7 Mon Sep 17 00:00:00 2001 From: Dan Date: Wed, 18 Apr 2018 16:08:08 -0230 Subject: Documentation fix: @constant -> @property --- app/scripts/controllers/shapeshift.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/scripts') diff --git a/app/scripts/controllers/shapeshift.js b/app/scripts/controllers/shapeshift.js index 17994d2db..d4e734ee9 100644 --- a/app/scripts/controllers/shapeshift.js +++ b/app/scripts/controllers/shapeshift.js @@ -31,7 +31,7 @@ class ShapeshiftController { * @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 {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} -- cgit v1.2.3 From e80bd230b9bb6ac9ff05d7095f74dd2fd7ebb3af Mon Sep 17 00:00:00 2001 From: Dan Date: Wed, 18 Apr 2018 16:11:39 -0230 Subject: NO MIXED TABS AND SPACES --- app/scripts/controllers/currency.js | 46 +++++----- app/scripts/controllers/preferences.js | 162 ++++++++++++++++----------------- app/scripts/controllers/shapeshift.js | 66 +++++++------- 3 files changed, 137 insertions(+), 137 deletions(-) (limited to 'app/scripts') diff --git a/app/scripts/controllers/currency.js b/app/scripts/controllers/currency.js index c23c7f616..37a19ff0d 100644 --- a/app/scripts/controllers/currency.js +++ b/app/scripts/controllers/currency.js @@ -6,12 +6,12 @@ const POLLING_INTERVAL = 600000 class CurrencyController { - /** - * Controller responsible for managing data associated with the currently selected currency. - * + /** + * 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 + * @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 @@ -20,8 +20,8 @@ class CurrencyController { * 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', @@ -35,22 +35,22 @@ 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 - * - */ + /** + * 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 - * - */ + /** + * 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 }) } @@ -117,12 +117,12 @@ class CurrencyController { } } - /** - * Creates a new poll, using setInterval, to periodically call updateConversionRate. The id of the interval is + /** + * 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 653e6d762..29214d072 100644 --- a/app/scripts/controllers/preferences.js +++ b/app/scripts/controllers/preferences.js @@ -4,12 +4,12 @@ const extend = require('xtend') class PreferencesController { - /** - * + /** + * * @typedef {Object} PreferencesController - * @param {object} opts Overrides the defaults for the initial state of this.store + * @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 {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 @@ -18,7 +18,7 @@ class PreferencesController { * @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: [], @@ -32,43 +32,43 @@ class PreferencesController { } // PUBLIC METHODS - /** - * Setter for the `useBlockie` property - * - * @param {boolean} val Whether or not the user prefers blockie indicators - * - */ + /** + * 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 - * - */ + /** + * Getter for the `useBlockie` property + * + * @returns {boolean} this.store.useBlockie + * + */ getUseBlockie () { return this.store.getState().useBlockie } - /** - * Setter for the `currentLocale` property + /** + * 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} Promise resolves with undefined - * - */ + /** + * Setter for the `selectedAddress` property + * + * @param {string} _address A new hex address for an account + * @returns {Promise} Promise resolves with undefined + * + */ setSelectedAddress (_address) { return new Promise((resolve, reject) => { const address = normalizeAddress(_address) @@ -129,13 +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 - * - */ + /** + * 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 @@ -145,23 +145,23 @@ class PreferencesController { return Promise.resolve(updatedTokens) } - /** - * A getter for the `tokens` property - * - * @returns {array} The current array of AddedToken objects - * - */ + /** + * 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} Promise resolves with undefined - * - */ + /** + * 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} Promise resolves with undefined + * + */ updateFrequentRpcList (_url) { return this.addToFrequentRpcList(_url) .then((rpcList) => { @@ -170,13 +170,13 @@ class PreferencesController { }) } - /** - * Setter for the `currentAccountTab` property - * - * @param {string} currentAccountTab Specifies the new tab to be marked as current - * @returns {Promise} Promise resolves with undefined - * - */ + /** + * Setter for the `currentAccountTab` property + * + * @param {string} currentAccountTab Specifies the new tab to be marked as current + * @returns {Promise} Promise resolves with undefined + * + */ setCurrentAccountTab (currentAccountTab) { return new Promise((resolve, reject) => { this.store.updateState({ currentAccountTab }) @@ -184,15 +184,15 @@ class PreferencesController { }) } - /** - * Returns an updated rpcList based on the passed url and the current list. + /** + * 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. - * - */ + * + * @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 }) @@ -208,24 +208,24 @@ class PreferencesController { return Promise.resolve(rpcList) } - /** - * Getter for the `frequentRpcList` property. - * - * @returns {array} An array of one or two rpc urls. - * - */ + /** + * 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. + /** + * 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. - * - */ + * @returns {Promise} Promises a new object; the updated featureFlags object. + * + */ setFeatureFlag (feature, activated) { const currentFeatureFlags = this.store.getState().featureFlags const updatedFeatureFlags = { @@ -238,13 +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 + /** + * 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 d4e734ee9..203dd99bd 100644 --- a/app/scripts/controllers/shapeshift.js +++ b/app/scripts/controllers/shapeshift.js @@ -41,38 +41,38 @@ class ShapeshiftController { // PUBLIC METHODS // - /** - * A getter for the shapeShiftTxList property - * - * @returns {array} - * - */ + /** + * 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 - * - */ + /** + * 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 + /** + * 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() @@ -113,13 +113,13 @@ class ShapeshiftController { } } - /** - * Saves an updated to a ShapeShiftTx in the shapeShiftTxList. If the passed ShapeShiftTx is not in the + /** + * 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 - * - */ + * + * @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) @@ -129,12 +129,12 @@ class ShapeshiftController { } } - /** - * Removes a ShapeShiftTx from the shapeShiftTxList - * - * @param {ShapeShiftTx} tx The tx to remove - * - */ + /** + * 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) @@ -144,14 +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 - * + /** + * 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 -- cgit v1.2.3 From 164f9c4662072dc0960ee5dc2c021545a7b14d8a Mon Sep 17 00:00:00 2001 From: Dan Date: Wed, 18 Apr 2018 16:17:06 -0230 Subject: Missing bracket in docblock. --- app/scripts/controllers/preferences.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/scripts') diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js index 29214d072..d4d508026 100644 --- a/app/scripts/controllers/preferences.js +++ b/app/scripts/controllers/preferences.js @@ -133,7 +133,7 @@ class PreferencesController { * 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 + * @returns {Promise} The new array of AddedToken objects * */ removeToken (rawAddress) { -- cgit v1.2.3 From 603c1310ffc0cdb61a66f68b8240e76c2ae7cb04 Mon Sep 17 00:00:00 2001 From: Whymarrh Whitby Date: Mon, 16 Apr 2018 12:59:43 -0230 Subject: Add a few missing docblocks to background files --- app/scripts/lib/createLoggerMiddleware.js | 14 +++++++----- app/scripts/lib/createOriginMiddleware.js | 12 ++++++---- app/scripts/lib/events-proxy.js | 25 +++++++++++++++------ app/scripts/lib/hex-to-bn.js | 7 +++++- app/scripts/lib/local-store.js | 37 ++++++++++++++++++++++++++----- app/scripts/lib/migrator/index.js | 35 +++++++++++++++++++++++++---- app/scripts/lib/stream-utils.js | 18 +++++++++++++-- app/scripts/platforms/sw.js | 27 +++++++++++++--------- app/scripts/platforms/window.js | 23 +++++++++++-------- 9 files changed, 150 insertions(+), 48 deletions(-) (limited to 'app/scripts') diff --git a/app/scripts/lib/createLoggerMiddleware.js b/app/scripts/lib/createLoggerMiddleware.js index fc6abf828..996c3477c 100644 --- a/app/scripts/lib/createLoggerMiddleware.js +++ b/app/scripts/lib/createLoggerMiddleware.js @@ -1,16 +1,20 @@ const log = require('loglevel') -// log rpc activity module.exports = createLoggerMiddleware -function createLoggerMiddleware ({ origin }) { - return function loggerMiddleware (req, res, next, end) { - next((cb) => { +/** + * Returns a middleware that logs RPC activity + * @param {{ origin: string }} opts - The middleware options + * @returns {Function} + */ +function createLoggerMiddleware (opts) { + return function loggerMiddleware (/** @type {any} */ req, /** @type {any} */ res, /** @type {Function} */ next) { + next((/** @type {Function} */ cb) => { if (res.error) { log.error('Error in RPC response:\n', res) } if (req.isMetamaskInternal) return - log.info(`RPC (${origin}):`, req, '->', res) + log.info(`RPC (${opts.origin}):`, req, '->', res) cb() }) } diff --git a/app/scripts/lib/createOriginMiddleware.js b/app/scripts/lib/createOriginMiddleware.js index f8bdb2dc2..98bb0e3b3 100644 --- a/app/scripts/lib/createOriginMiddleware.js +++ b/app/scripts/lib/createOriginMiddleware.js @@ -1,9 +1,13 @@ -// append dapp origin domain to request module.exports = createOriginMiddleware -function createOriginMiddleware ({ origin }) { - return function originMiddleware (req, res, next, end) { - req.origin = origin +/** + * Returns a middleware that appends the DApp origin to request + * @param {{ origin: string }} opts - The middleware options + * @returns {Function} + */ +function createOriginMiddleware (opts) { + return function originMiddleware (/** @type {any} */ req, /** @type {any} */ _, /** @type {Function} */ next) { + req.origin = opts.origin next() } } diff --git a/app/scripts/lib/events-proxy.js b/app/scripts/lib/events-proxy.js index c0a490b05..f83773ccc 100644 --- a/app/scripts/lib/events-proxy.js +++ b/app/scripts/lib/events-proxy.js @@ -1,26 +1,37 @@ +/** + * Returns an EventEmitter that proxies events from the given event emitter + * @param {any} eventEmitter + * @param {object} listeners - The listeners to proxy to + * @returns {any} + */ module.exports = function createEventEmitterProxy (eventEmitter, listeners) { let target = eventEmitter const eventHandlers = listeners || {} - const proxy = new Proxy({}, { - get: (obj, name) => { + const proxy = /** @type {any} */ (new Proxy({}, { + get: (_, name) => { // intercept listeners if (name === 'on') return addListener if (name === 'setTarget') return setTarget if (name === 'proxyEventHandlers') return eventHandlers - return target[name] + return (/** @type {any} */ (target))[name] }, - set: (obj, name, value) => { + set: (_, name, value) => { target[name] = value return true }, - }) - function setTarget (eventEmitter) { + })) + function setTarget (/** @type {EventEmitter} */ eventEmitter) { target = eventEmitter // migrate listeners Object.keys(eventHandlers).forEach((name) => { - eventHandlers[name].forEach((handler) => target.on(name, handler)) + /** @type {Array} */ (eventHandlers[name]).forEach((handler) => target.on(name, handler)) }) } + /** + * Attaches a function to be called whenever the specified event is emitted + * @param {string} name + * @param {Function} handler + */ function addListener (name, handler) { if (!eventHandlers[name]) eventHandlers[name] = [] eventHandlers[name].push(handler) diff --git a/app/scripts/lib/hex-to-bn.js b/app/scripts/lib/hex-to-bn.js index 184217279..b28746920 100644 --- a/app/scripts/lib/hex-to-bn.js +++ b/app/scripts/lib/hex-to-bn.js @@ -1,6 +1,11 @@ -const ethUtil = require('ethereumjs-util') +const ethUtil = (/** @type {object} */ (require('ethereumjs-util'))) const BN = ethUtil.BN +/** + * Returns a [BinaryNumber]{@link BN} representation of the given hex value + * @param {string} hex + * @return {any} + */ module.exports = function hexToBn (hex) { return new BN(ethUtil.stripHexPrefix(hex), 16) } diff --git a/app/scripts/lib/local-store.js b/app/scripts/lib/local-store.js index 2dda0ba1f..139ff86bd 100644 --- a/app/scripts/lib/local-store.js +++ b/app/scripts/lib/local-store.js @@ -1,11 +1,13 @@ -// We should not rely on local storage in an extension! -// We should use this instead! -// https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/storage/local - const extension = require('extensionizer') const log = require('loglevel') +/** + * A wrapper around the extension's storage local API + */ module.exports = class ExtensionStore { + /** + * @constructor + */ constructor() { this.isSupported = !!(extension.storage.local) if (!this.isSupported) { @@ -13,6 +15,10 @@ module.exports = class ExtensionStore { } } + /** + * Returns all of the keys currently saved + * @return {Promise<*>} + */ async get() { if (!this.isSupported) return undefined const result = await this._get() @@ -25,14 +31,24 @@ module.exports = class ExtensionStore { } } + /** + * Sets the key in local state + * @param {object} state - The state to set + * @return {Promise} + */ async set(state) { return this._set(state) } + /** + * Returns all of the keys currently saved + * @private + * @return {object} the key-value map from local storage + */ _get() { const local = extension.storage.local return new Promise((resolve, reject) => { - local.get(null, (result) => { + local.get(null, (/** @type {any} */ result) => { const err = extension.runtime.lastError if (err) { reject(err) @@ -43,6 +59,12 @@ module.exports = class ExtensionStore { }) } + /** + * Sets the key in local state + * @param {object} obj - The key to set + * @return {Promise} + * @private + */ _set(obj) { const local = extension.storage.local return new Promise((resolve, reject) => { @@ -58,6 +80,11 @@ module.exports = class ExtensionStore { } } +/** + * Returns whether or not the given object contains no keys + * @param {object} obj - The object to check + * @returns {boolean} + */ function isEmpty(obj) { return Object.keys(obj).length === 0 } diff --git a/app/scripts/lib/migrator/index.js b/app/scripts/lib/migrator/index.js index 85c2717ea..345ca8001 100644 --- a/app/scripts/lib/migrator/index.js +++ b/app/scripts/lib/migrator/index.js @@ -1,7 +1,23 @@ const EventEmitter = require('events') +/** + * @typedef {object} Migration + * @property {number} version - The migration version + * @property {Function} migrate - Returns a promise of the migrated data + */ + +/** + * @typedef {object} MigratorOptions + * @property {Array} [migrations] - The list of migrations to apply + * @property {number} [defaultVersion] - The version to use in the initial state + */ + class Migrator extends EventEmitter { + /** + * @constructor + * @param {MigratorOptions} opts + */ constructor (opts = {}) { super() const migrations = opts.migrations || [] @@ -42,19 +58,30 @@ class Migrator extends EventEmitter { return versionedData - // migration is "pending" if it has a higher - // version number than currentVersion + /** + * Returns whether or not the migration is pending + * + * A migration is considered "pending" if it has a higher + * version number than the current version. + * @param {Migration} migration + * @returns {boolean} + */ function migrationIsPending (migration) { return migration.version > versionedData.meta.version } } - generateInitialState (initState) { + /** + * Returns the initial state for the migrator + * @param {object} [data] - The data for the initial state + * @returns {{meta: {version: number}, data: any}} + */ + generateInitialState (data) { return { meta: { version: this.defaultVersion, }, - data: initState, + data, } } diff --git a/app/scripts/lib/stream-utils.js b/app/scripts/lib/stream-utils.js index 8bb0b4f3c..3dbc064b5 100644 --- a/app/scripts/lib/stream-utils.js +++ b/app/scripts/lib/stream-utils.js @@ -8,20 +8,34 @@ module.exports = { setupMultiplex: setupMultiplex, } +/** + * Returns a stream transform that parses JSON strings passing through + * @return {stream.Transform} + */ function jsonParseStream () { - return Through.obj(function (serialized, encoding, cb) { + return Through.obj(function (serialized, _, cb) { this.push(JSON.parse(serialized)) cb() }) } +/** + * Returns a stream transform that calls {@code JSON.stringify} + * on objects passing through + * @return {stream.Transform} the stream transform + */ function jsonStringifyStream () { - return Through.obj(function (obj, encoding, cb) { + return Through.obj(function (obj, _, cb) { this.push(JSON.stringify(obj)) cb() }) } +/** + * Sets up stream multiplexing for the given stream + * @param {any} connectionStream - the stream to mux + * @return {stream.Stream} the multiplexed stream + */ function setupMultiplex (connectionStream) { const mux = new ObjectMultiplex() pump( diff --git a/app/scripts/platforms/sw.js b/app/scripts/platforms/sw.js index 007d8dc5b..56c5f2774 100644 --- a/app/scripts/platforms/sw.js +++ b/app/scripts/platforms/sw.js @@ -1,20 +1,25 @@ - class SwPlatform { - - // - // Public - // - + /** + * Reloads the platform + */ reload () { - // you cant actually do this - global.location.reload() + // TODO: you can't actually do this + /** @type {any} */ (global).location.reload() } - openWindow ({ url }) { - // this doesnt actually work - global.open(url, '_blank') + /** + * Opens a window + * @param {{url: string}} opts - The window options + */ + openWindow (opts) { + // TODO: this doesn't actually work + /** @type {any} */ (global).open(opts.url, '_blank') } + /** + * Returns the platform version + * @returns {string} + */ getVersion () { return '' } diff --git a/app/scripts/platforms/window.js b/app/scripts/platforms/window.js index 1527c008b..943b2a703 100644 --- a/app/scripts/platforms/window.js +++ b/app/scripts/platforms/window.js @@ -1,18 +1,23 @@ - class WindowPlatform { - - // - // Public - // - + /** + * Reload the platform + */ reload () { - global.location.reload() + /** @type {any} */ (global).location.reload() } - openWindow ({ url }) { - global.open(url, '_blank') + /** + * Opens a window + * @param {{url: string}} opts - The window options + */ + openWindow (opts) { + /** @type {any} */ (global).open(opts.url, '_blank') } + /** + * Returns the platform version + * @returns {string} + */ getVersion () { return '' } -- cgit v1.2.3