aboutsummaryrefslogtreecommitdiffstats
path: root/app/scripts/lib
diff options
context:
space:
mode:
authorDan <danjm.com@gmail.com>2018-04-20 23:53:17 +0800
committerDan <danjm.com@gmail.com>2018-04-20 23:53:17 +0800
commit71b0de76ffdbdfc0ae696a009d5ee34971541e0b (patch)
treed0f2f4fd891a20eb7cbe5e7bfb82a375286fbc43 /app/scripts/lib
parent9f12c26d44a0d78f28af25056857b993f80bbd95 (diff)
parent00efcf9e8ba34d448b628c98d32ad12d5be2ffc9 (diff)
downloadtangerine-wallet-browser-71b0de76ffdbdfc0ae696a009d5ee34971541e0b.tar.gz
tangerine-wallet-browser-71b0de76ffdbdfc0ae696a009d5ee34971541e0b.tar.zst
tangerine-wallet-browser-71b0de76ffdbdfc0ae696a009d5ee34971541e0b.zip
Merge branch 'master' into dm-docs-2
Diffstat (limited to 'app/scripts/lib')
-rw-r--r--app/scripts/lib/ComposableObservableStore.js49
-rw-r--r--app/scripts/lib/config-manager.js22
-rw-r--r--app/scripts/lib/createLoggerMiddleware.js16
-rw-r--r--app/scripts/lib/createOriginMiddleware.js12
-rw-r--r--app/scripts/lib/enums.js9
-rw-r--r--app/scripts/lib/environment-type.js19
-rw-r--r--app/scripts/lib/events-proxy.js25
-rw-r--r--app/scripts/lib/hex-to-bn.js7
-rw-r--r--app/scripts/lib/is-popup-or-notification.js18
-rw-r--r--app/scripts/lib/local-store.js38
-rw-r--r--app/scripts/lib/migrator/index.js35
-rw-r--r--app/scripts/lib/personal-message-manager.js1
-rw-r--r--app/scripts/lib/seed-phrase-verifier.js1
-rw-r--r--app/scripts/lib/stream-utils.js18
-rw-r--r--app/scripts/lib/typed-message-manager.js1
-rw-r--r--app/scripts/lib/util.js41
16 files changed, 238 insertions, 74 deletions
diff --git a/app/scripts/lib/ComposableObservableStore.js b/app/scripts/lib/ComposableObservableStore.js
new file mode 100644
index 000000000..d5ee708a1
--- /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(initState)
+ 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/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/lib/createLoggerMiddleware.js b/app/scripts/lib/createLoggerMiddleware.js
index 2707cbd9e..996c3477c 100644
--- a/app/scripts/lib/createLoggerMiddleware.js
+++ b/app/scripts/lib/createLoggerMiddleware.js
@@ -1,14 +1,20 @@
-// log rpc activity
+const log = require('loglevel')
+
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/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 f13a1574d..000000000
--- a/app/scripts/lib/environment-type.js
+++ /dev/null
@@ -1,19 +0,0 @@
-/**
- * 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$/)) {
- return 'popup'
- } else if (url.match(/home.html$/)) {
- return 'responsive'
- } else {
- return 'notification'
- }
-}
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<Function>} */ (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/is-popup-or-notification.js b/app/scripts/lib/is-popup-or-notification.js
deleted file mode 100644
index 894564def..000000000
--- a/app/scripts/lib/is-popup-or-notification.js
+++ /dev/null
@@ -1,18 +0,0 @@
-/**
- * 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(?:\?.+)*$/) || url.match(/home.html(?:#.*)*$/)) {
- return 'popup'
- } else {
- return 'notification'
- }
-}
diff --git a/app/scripts/lib/local-store.js b/app/scripts/lib/local-store.js
index 5b47985f6..139ff86bd 100644
--- a/app/scripts/lib/local-store.js
+++ b/app/scripts/lib/local-store.js
@@ -1,10 +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) {
@@ -12,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()
@@ -24,14 +31,24 @@ module.exports = class ExtensionStore {
}
}
+ /**
+ * Sets the key in local state
+ * @param {object} state - The state to set
+ * @return {Promise<void>}
+ */
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)
@@ -42,6 +59,12 @@ module.exports = class ExtensionStore {
})
}
+ /**
+ * Sets the key in local state
+ * @param {object} obj - The key to set
+ * @return {Promise<void>}
+ * @private
+ */
_set(obj) {
const local = extension.storage.local
return new Promise((resolve, reject) => {
@@ -57,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<Migration>} [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/personal-message-manager.js b/app/scripts/lib/personal-message-manager.js
index 3c502329c..4f19876db 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')
/**
* Represents, and contains data about, an 'personal_sign' type signature request. These are created when a
diff --git a/app/scripts/lib/seed-phrase-verifier.js b/app/scripts/lib/seed-phrase-verifier.js
index 826e73306..3b5afb800 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/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/lib/typed-message-manager.js b/app/scripts/lib/typed-message-manager.js
index 8716ebf9a..367c6ecb9 100644
--- a/app/scripts/lib/typed-message-manager.js
+++ b/app/scripts/lib/typed-message-manager.js
@@ -3,6 +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')
/**
* Represents, and contains data about, an 'eth_signTypedData' type signature request. These are created when a
diff --git a/app/scripts/lib/util.js b/app/scripts/lib/util.js
index cb0d7e5c1..431d1e59c 100644
--- a/app/scripts/lib/util.js
+++ b/app/scripts/lib/util.js
@@ -1,14 +1,11 @@
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')
/**
* Generates an example stack trace
@@ -22,6 +19,25 @@ function getStack () {
}
/**
+ * 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
+ *
+ */
+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
+ }
+}
+
+/**
* 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
@@ -82,3 +98,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,
+}