aboutsummaryrefslogtreecommitdiffstats
path: root/app/scripts/lib
diff options
context:
space:
mode:
authorDan <danjm.com@gmail.com>2018-04-19 23:38:56 +0800
committerDan <danjm.com@gmail.com>2018-04-19 23:38:56 +0800
commit9f12c26d44a0d78f28af25056857b993f80bbd95 (patch)
treea8a9c102eed9d0c972be80142a2087917b16c511 /app/scripts/lib
parent6da00c4061b4af1bb282c9ad68eaa2deef84093b (diff)
downloadtangerine-wallet-browser-9f12c26d44a0d78f28af25056857b993f80bbd95.tar
tangerine-wallet-browser-9f12c26d44a0d78f28af25056857b993f80bbd95.tar.gz
tangerine-wallet-browser-9f12c26d44a0d78f28af25056857b993f80bbd95.tar.bz2
tangerine-wallet-browser-9f12c26d44a0d78f28af25056857b993f80bbd95.tar.lz
tangerine-wallet-browser-9f12c26d44a0d78f28af25056857b993f80bbd95.tar.xz
tangerine-wallet-browser-9f12c26d44a0d78f28af25056857b993f80bbd95.tar.zst
tangerine-wallet-browser-9f12c26d44a0d78f28af25056857b993f80bbd95.zip
Even more documentation for various controllers and libs.
Diffstat (limited to 'app/scripts/lib')
-rw-r--r--app/scripts/lib/account-tracker.js81
-rw-r--r--app/scripts/lib/extractEthjsErrorMessage.js23
-rw-r--r--app/scripts/lib/getObjStructure.js17
-rw-r--r--app/scripts/lib/message-manager.js145
-rw-r--r--app/scripts/lib/notification-manager.js9
-rw-r--r--app/scripts/lib/pending-balance-calculator.js38
-rw-r--r--app/scripts/lib/personal-message-manager.js142
-rw-r--r--app/scripts/lib/seed-phrase-verifier.js18
-rw-r--r--app/scripts/lib/typed-message-manager.js138
9 files changed, 571 insertions, 40 deletions
diff --git a/app/scripts/lib/account-tracker.js b/app/scripts/lib/account-tracker.js
index 8c3dd8c71..1b707b2bb 100644
--- a/app/scripts/lib/account-tracker.js
+++ b/app/scripts/lib/account-tracker.js
@@ -16,6 +16,24 @@ function noop () {}
class AccountTracker extends EventEmitter {
+ /**
+ * This module is responsible for tracking any number of accounts and caching their current balances & transaction
+ * counts.
+ *
+ * It also tracks transaction hashes, and checks their inclusion status on each new block.
+ *
+ * @typedef {Object} AccountTracker
+ * @param {Object} opts Initialize various properties of the class.
+ * @property {Object} store The stored object containing all accounts to track, as well as the current block's gas limit.
+ * @property {Object} store.accounts The accounts currently stored in this AccountTracker
+ * @property {string} store.currentBlockGasLimit A hex string indicating the gas limit of the current block
+ * @property {Object} _provider A provider needed to create the EthQuery instance used within this AccountTracker.
+ * @property {EthQuery} _query An EthQuery instance used to access account information from the blockchain
+ * @property {BlockTracker} _blockTracker A BlockTracker instance. Needed to ensure that accounts and their info updates
+ * when a new block is created.
+ * @property {Object} _currentBlockNumber Reference to a property on the _blockTracker: the number (i.e. an id) of the the current block
+ *
+ */
constructor (opts = {}) {
super()
@@ -34,10 +52,17 @@ class AccountTracker extends EventEmitter {
this._currentBlockNumber = this._blockTracker.currentBlock
}
- //
- // public
- //
-
+ /**
+ * Ensures that the locally stored accounts are in sync with a set of accounts stored externally to this
+ * AccountTracker.
+ *
+ * Once this AccountTracker's accounts are up to date with those referenced by the passed addresses, each
+ * of these accounts are given an updated balance via Ethquery.
+ *
+ * @param {array} address The array of hex addresses for accounts with which this AccountTracker's accounts should be
+ * in sync
+ *
+ */
syncWithAddresses (addresses) {
const accounts = this.store.getState().accounts
const locals = Object.keys(accounts)
@@ -61,6 +86,13 @@ class AccountTracker extends EventEmitter {
this._updateAccounts()
}
+ /**
+ * Adds a new address to this AccountTracker's accounts object, which points to an empty object. This object will be
+ * given a balance as long this._currentBlockNumber is defined.
+ *
+ * @param {string} address A hex address of a new account to store in this AccountTracker's accounts object
+ *
+ */
addAccount (address) {
const accounts = this.store.getState().accounts
accounts[address] = {}
@@ -69,16 +101,27 @@ class AccountTracker extends EventEmitter {
this._updateAccount(address)
}
+ /**
+ * Removes an account from this AccountTracker's accounts object
+ *
+ * @param {string} address A hex address of a the account to remove
+ *
+ */
removeAccount (address) {
const accounts = this.store.getState().accounts
delete accounts[address]
this.store.updateState({ accounts })
}
- //
- // private
- //
-
+ /**
+ * Given a block, updates this AccountTracker's currentBlockGasLimit, and then updates each local account's balance
+ * via EthQuery
+ *
+ * @private
+ * @param {object} block Data about the block that contains the data to update to.
+ * @fires 'block' The updated state, if all account updates are successfull
+ *
+ */
_updateForBlock (block) {
this._currentBlockNumber = block.number
const currentBlockGasLimit = block.gasLimit
@@ -93,12 +136,26 @@ class AccountTracker extends EventEmitter {
})
}
+ /**
+ * Calls this._updateAccount for each account in this.store
+ *
+ * @param {Function} cb A callback to pass to this._updateAccount, called after each account is succesfully updated
+ *
+ */
_updateAccounts (cb = noop) {
const accounts = this.store.getState().accounts
const addresses = Object.keys(accounts)
async.each(addresses, this._updateAccount.bind(this), cb)
}
+ /**
+ * Updates the current balance of an account. Gets an updated balance via this._getAccount.
+ *
+ * @private
+ * @param {string} address A hex address of a the account to be updated
+ * @param {Function} cb A callback to call once the account at address is successfully update
+ *
+ */
_updateAccount (address, cb = noop) {
this._getAccount(address, (err, result) => {
if (err) return cb(err)
@@ -113,6 +170,14 @@ class AccountTracker extends EventEmitter {
})
}
+ /**
+ * Gets the current balance of an account via EthQuery.
+ *
+ * @private
+ * @param {string} address A hex address of a the account to query
+ * @param {Function} cb A callback to call once the account at address is successfully update
+ *
+ */
_getAccount (address, cb = noop) {
const query = this._query
async.parallel({
diff --git a/app/scripts/lib/extractEthjsErrorMessage.js b/app/scripts/lib/extractEthjsErrorMessage.js
index bac541735..3f0fb0232 100644
--- a/app/scripts/lib/extractEthjsErrorMessage.js
+++ b/app/scripts/lib/extractEthjsErrorMessage.js
@@ -4,17 +4,18 @@ const errorLabelPrefix = 'Error: '
module.exports = extractEthjsErrorMessage
-//
-// ethjs-rpc provides overly verbose error messages
-// if we detect this type of message, we extract the important part
-// Below is an example input and output
-//
-// Error: [ethjs-rpc] rpc error with payload {"id":3947817945380,"jsonrpc":"2.0","params":["0xf8eb8208708477359400830398539406012c8cf97bead5deae237070f9587f8e7a266d80b8843d7d3f5a0000000000000000000000000000000000000000000000000000000000081d1a000000000000000000000000000000000000000000000000001ff973cafa800000000000000000000000000000000000000000000000000000038d7ea4c68000000000000000000000000000000000000000000000000000000000000003f48025a04c32a9b630e0d9e7ff361562d850c86b7a884908135956a7e4a336fa0300d19ca06830776423f25218e8d19b267161db526e66895567147015b1f3fc47aef9a3c7"],"method":"eth_sendRawTransaction"} Error: replacement transaction underpriced
-//
-// Transaction Failed: replacement transaction underpriced
-//
-
-
+/**
+ * Extracts the important part of an ethjs-rpc error message. If the passed error is not anis isEthjsRpcError, the error
+ * is returned unchanged.
+ *
+ * @param {string} errorMessage The error message to parse
+ * @returns {string} Returns an error message, either the same as was passed, or the ending message portion of an isEthjsRpcError
+ *
+ * @example
+ * // returns 'Transaction Failed: replacement transaction underpriced'
+ * extractEthjsErrorMessage(`Error: [ethjs-rpc] rpc error with payload {"id":3947817945380,"jsonrpc":"2.0","params":["0xf8eb8208708477359400830398539406012c8cf97bead5deae237070f9587f8e7a266d80b8843d7d3f5a0000000000000000000000000000000000000000000000000000000000081d1a000000000000000000000000000000000000000000000000001ff973cafa800000000000000000000000000000000000000000000000000000038d7ea4c68000000000000000000000000000000000000000000000000000000000000003f48025a04c32a9b630e0d9e7ff361562d850c86b7a884908135956a7e4a336fa0300d19ca06830776423f25218e8d19b267161db526e66895567147015b1f3fc47aef9a3c7"],"method":"eth_sendRawTransaction"} Error: replacement transaction underpriced`)
+ *
+*/
function extractEthjsErrorMessage(errorMessage) {
const isEthjsRpcError = errorMessage.includes(ethJsRpcSlug)
if (isEthjsRpcError) {
diff --git a/app/scripts/lib/getObjStructure.js b/app/scripts/lib/getObjStructure.js
index 3db389507..52250d3fb 100644
--- a/app/scripts/lib/getObjStructure.js
+++ b/app/scripts/lib/getObjStructure.js
@@ -14,6 +14,15 @@ module.exports = getObjStructure
// }
// }
+/**
+ * Creates an object that represents the structure of the given object. It replaces all values with the result of their
+ * type.
+ *
+ * @param {object} obj The object for which a 'structure' will be returned. Usually a plain object and not a class.
+ * @returns {object} The "mapped" version of a deep clone of the passed object, with each non-object property value
+ * replaced with the javascript type of that value.
+ *
+ */
function getObjStructure(obj) {
const structure = clone(obj)
return deepMap(structure, (value) => {
@@ -21,6 +30,14 @@ function getObjStructure(obj) {
})
}
+/**
+ * Modifies all the properties and deeply nested of a passed object. Iterates recursively over all nested objects and
+ * their properties, and covers the entire depth of the object. At each property value which is not an object is modified.
+ *
+ * @param {object} target The object to modify
+ * @param {Function} visit The modifier to apply to each non-object property value
+ * @returns {object} The modified object
+ */
function deepMap(target = {}, visit) {
Object.entries(target).forEach(([key, value]) => {
if (typeof value === 'object' && value !== null) {
diff --git a/app/scripts/lib/message-manager.js b/app/scripts/lib/message-manager.js
index f52e048e0..52b897720 100644
--- a/app/scripts/lib/message-manager.js
+++ b/app/scripts/lib/message-manager.js
@@ -3,8 +3,45 @@ const ObservableStore = require('obs-store')
const ethUtil = require('ethereumjs-util')
const createId = require('./random-id')
+var msgData = {
+ id: msgId,
+ msgParams: msgParams,
+ time: time,
+ status: 'unapproved',
+ type: 'eth_sign',
+}
+
+/**
+ * Represents, and contains data about, an 'eth_sign' type signature request. These are created when a signature for
+ * an eth_sign call is requested.
+ *
+ * @see {@link https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign}
+ *
+ * @typedef {Object} Message
+ * @property {number} id An id to track and identify the message object
+ * @property {object} msgParams The parameters to pass to the eth_sign method once the signature request is approved.
+ * @property {object} msgParams.metamaskId Added to msgParams for tracking and identification within Metamask.
+ * @property {string} msgParams.data A hex string conversion of the raw buffer data of the signature request
+ * @property {number} time The epoch time at which the this message was created
+ * @property {string} status Indicates whether the signature request is 'unapproved', 'approved', 'signed' or 'rejected'
+ * @property {string} type The json-prc signing method for which a signature request has been made. A 'Message' with
+ * always have a 'eth_sign' type.
+ *
+ */
module.exports = class MessageManager extends EventEmitter {
+
+ /**
+ * Controller in charge of managing - storing, adding, removing, updating - Messages.
+ *
+ * @typedef {Object} MessageManager
+ * @param {object} opts @deprecated
+ * @property {object} memStore The observable store where Messages are saved with persistance.
+ * @property {object} memStore.unapprovedMsgs A collection of all Messages in the 'unapproved' state
+ * @property {number} memStore.unapprovedMsgCount The count of all Messages in this.memStore.unapprobedMsgs
+ * @property {array} messages Holds all messages that have been created by this MessageManager
+ *
+ */
constructor (opts) {
super()
this.memStore = new ObservableStore({
@@ -14,15 +51,35 @@ module.exports = class MessageManager extends EventEmitter {
this.messages = []
}
+ /**
+ * A getter for the number of 'unapproved' Messages in this.messages
+ *
+ * @returns {number} The number of 'unapproved' Messages in this.messages
+ *
+ */
get unapprovedMsgCount () {
return Object.keys(this.getUnapprovedMsgs()).length
}
+ /**
+ * A getter for the 'unapproved' Messages in this.messages
+ *
+ * @returns {object} An index of Message ids to Messages, for all 'unapproved' Messages in this.messages
+ *
+ */
getUnapprovedMsgs () {
return this.messages.filter(msg => msg.status === 'unapproved')
.reduce((result, msg) => { result[msg.id] = msg; return result }, {})
}
+ /**
+ * Creates a new Message with an 'unapproved' status using the passed msgParams. this.addMsg is called to add the
+ * new Message to this.messages, and to save the unapproved Messages from that list to this.memStore.
+ *
+ * @param {object} msgParams The params for the eth_sign call to be made after the message is approved.
+ * @returns {number} The id of the newly created message.
+ *
+ */
addUnapprovedMessage (msgParams) {
msgParams.data = normalizeMsgData(msgParams.data)
// create txData obj with parameters and meta data
@@ -42,24 +99,61 @@ module.exports = class MessageManager extends EventEmitter {
return msgId
}
+ /**
+ * Adds a passed Message to this.messages, and calls this._saveMsgList() to save the unapproved Messages from that
+ * list to this.memStore.
+ *
+ * @param {Message} msg The Message to add to this.messages
+ *
+ */
addMsg (msg) {
this.messages.push(msg)
this._saveMsgList()
}
+ /**
+ * Returns a specified Message.
+ *
+ * @param {number} msgId The id of the Message to get
+ * @returns {Message|undefined} The Message with the id that matches the passed msgId, or undefined if no Message has that id.
+ *
+ */
getMsg (msgId) {
return this.messages.find(msg => msg.id === msgId)
}
+ /**
+ * Approves a Message. Sets the message status via a call to this.setMsgStatusApproved, and returns a promise with
+ * any the message params modified for proper signing.
+ *
+ * @param {object} msgParams The msgParams to be used when eth_sign is called, plus data added by Metamask.
+ * @param {object} msgParams.metamaskId Added to msgParams for tracking and identification within Metamask.
+ * @returns {Promise<object>} Promises the msgParams object with metamaskId removed.
+ *
+ */
approveMessage (msgParams) {
this.setMsgStatusApproved(msgParams.metamaskId)
return this.prepMsgForSigning(msgParams)
}
+ /**
+ * Sets a Message status to 'approved' via a call to this._setMsgStatus.
+ *
+ * @param {number} msgId The id of the Message to approve.
+ *
+ */
setMsgStatusApproved (msgId) {
this._setMsgStatus(msgId, 'approved')
}
+ /**
+ * Sets a Message status to 'signed' via a call to this._setMsgStatus and updates that Message in this.messages by
+ * adding the raw signature data of the signature request to the Message
+ *
+ * @param {number} msgId The id of the Message to sign.
+ * @param {buffer} rawSig The raw data of the signature request
+ *
+ */
setMsgStatusSigned (msgId, rawSig) {
const msg = this.getMsg(msgId)
msg.rawSig = rawSig
@@ -67,19 +161,40 @@ module.exports = class MessageManager extends EventEmitter {
this._setMsgStatus(msgId, 'signed')
}
+ /**
+ * Removes the metamaskId property from passed msgParams and returns a promise which resolves the updated msgParams
+ *
+ * @param {object} msgParams The msgParams to modify
+ * @returns {Promise<object>} Promises the msgParams with the metamaskId property removed
+ *
+ */
prepMsgForSigning (msgParams) {
delete msgParams.metamaskId
return Promise.resolve(msgParams)
}
+ /**
+ * Sets a Message status to 'rejected' via a call to this._setMsgStatus.
+ *
+ * @param {number} msgId The id of the Message to reject.
+ *
+ */
rejectMsg (msgId) {
this._setMsgStatus(msgId, 'rejected')
}
- //
- // PRIVATE METHODS
- //
-
+ /**
+ * Updates the status of a Message in this.messages via a call to this._updateMsg
+ *
+ * @private
+ * @param {number} msgId The id of the Message to update.
+ * @param {string} status The new status of the Message.
+ * @throws A 'MessageManager - Message not found for id: "${msgId}".' if there is no Message in this.messages with an
+ * id equal to the passed msgId
+ * @fires An event with a name equal to `${msgId}:${status}`. The Message is also fired.
+ * @fires If status is 'rejected' or 'signed', an event with a name equal to `${msgId}:finished` is fired along with the message
+ *
+ */
_setMsgStatus (msgId, status) {
const msg = this.getMsg(msgId)
if (!msg) throw new Error('MessageManager - Message not found for id: "${msgId}".')
@@ -91,6 +206,14 @@ module.exports = class MessageManager extends EventEmitter {
}
}
+ /**
+ * Sets a Message in this.messages to the passed Message if the ids are equal. Then saves the unapprovedMsg list to
+ * storage via this._saveMsgList
+ *
+ * @private
+ * @param {msg} Message A Message that will replace an existing Message (with the same id) in this.messages
+ *
+ */
_updateMsg (msg) {
const index = this.messages.findIndex((message) => message.id === msg.id)
if (index !== -1) {
@@ -99,6 +222,13 @@ module.exports = class MessageManager extends EventEmitter {
this._saveMsgList()
}
+ /**
+ * Saves the unapproved messages, and their count, to this.memStore
+ *
+ * @private
+ * @fires 'updateBadge'
+ *
+ */
_saveMsgList () {
const unapprovedMsgs = this.getUnapprovedMsgs()
const unapprovedMsgCount = Object.keys(unapprovedMsgs).length
@@ -108,6 +238,13 @@ module.exports = class MessageManager extends EventEmitter {
}
+/**
+ * A helper function that converts raw buffer data to a hex, or just returns the data if it is already formatted as a hex.
+ *
+ * @param {any} data The buffer data to convert to a hex
+ * @returns {string} A hex string conversion of the buffer data
+ *
+ */
function normalizeMsgData (data) {
if (data.slice(0, 2) === '0x') {
// data is already hex
diff --git a/app/scripts/lib/notification-manager.js b/app/scripts/lib/notification-manager.js
index 1fcb7cf69..ce4825d5b 100644
--- a/app/scripts/lib/notification-manager.js
+++ b/app/scripts/lib/notification-manager.js
@@ -5,9 +5,12 @@ const width = 360
class NotificationManager {
- //
- // Public
- //
+ /**
+ * A collection of methods for controlling the showing and hiding of the notification popup.
+ *
+ * @typedef {Object} NotificationManager
+ *
+ */
showPopup () {
this._getPopup((err, popup) => {
diff --git a/app/scripts/lib/pending-balance-calculator.js b/app/scripts/lib/pending-balance-calculator.js
index 6ae526463..0f1dc19a9 100644
--- a/app/scripts/lib/pending-balance-calculator.js
+++ b/app/scripts/lib/pending-balance-calculator.js
@@ -3,16 +3,28 @@ const normalize = require('eth-sig-util').normalize
class PendingBalanceCalculator {
- // Must be initialized with two functions:
- // getBalance => Returns a promise of a BN of the current balance in Wei
- // getPendingTransactions => Returns an array of TxMeta Objects,
- // which have txParams properties, which include value, gasPrice, and gas,
- // all in a base=16 hex format.
+ /**
+ * Used for calculating a users "pending balance": their current balance minus the total possible cost of all their
+ * pending transactions.
+ *
+ * @typedef {Object} PendingBalanceCalculator
+ * @param {Function} getBalance Returns a promise of a BN of the current balance in Wei
+ * @param {Function} getPendingTransactions Returns an array of TxMeta Objects, which have txParams properties,
+ * which include value, gasPrice, and gas, all in a base=16 hex format.
+ *
+ */
constructor ({ getBalance, getPendingTransactions }) {
this.getPendingTransactions = getPendingTransactions
this.getNetworkBalance = getBalance
}
+ /**
+ * Returns the users "pending balance": their current balance minus the total possible cost of all their
+ * pending transactions.
+ *
+ * @returns {Promise<string>} Promises a base 16 hex string that contains the user's "pending balance"
+ *
+ */
async getBalance () {
const results = await Promise.all([
this.getNetworkBalance(),
@@ -29,6 +41,15 @@ class PendingBalanceCalculator {
return `0x${balance.sub(pendingValue).toString(16)}`
}
+ /**
+ * Calculates the maximum possible cost of a single transaction, based on the value, gas price and gas limit.
+ *
+ * @param {object} tx Contains all that data about a transaction.
+ * @property {object} tx.txParams Contains data needed to calculate the maximum cost of the transaction: gas,
+ * gasLimit and value.
+ *
+ * @returns {string} Returns a base 16 hex string that contains the maximum possible cost of the transaction.
+ */
calculateMaxCost (tx) {
const txValue = tx.txParams.value
const value = this.hexToBn(txValue)
@@ -42,6 +63,13 @@ class PendingBalanceCalculator {
return value.add(gasCost)
}
+ /**
+ * Converts a hex string to a BN object
+ *
+ * @param {string} hex A number represented as a hex string
+ * @returns {Object} A BN object
+ *
+ */
hexToBn (hex) {
return new BN(normalize(hex).substring(2), 16)
}
diff --git a/app/scripts/lib/personal-message-manager.js b/app/scripts/lib/personal-message-manager.js
index 6602f5aa8..3c502329c 100644
--- a/app/scripts/lib/personal-message-manager.js
+++ b/app/scripts/lib/personal-message-manager.js
@@ -4,8 +4,37 @@ const ethUtil = require('ethereumjs-util')
const createId = require('./random-id')
const hexRe = /^[0-9A-Fa-f]+$/g
+/**
+ * Represents, and contains data about, an 'personal_sign' type signature request. These are created when a
+ * signature for an personal_sign call is requested.
+ *
+ * @see {@link https://web3js.readthedocs.io/en/1.0/web3-eth-personal.html#sign}
+ *
+ * @typedef {Object} PersonalMessage
+ * @property {number} id An id to track and identify the message object
+ * @property {object} msgParams The parameters to pass to the personal_sign method once the signature request is
+ * approved.
+ * @property {object} msgParams.metamaskId Added to msgParams for tracking and identification within Metamask.
+ * @property {string} msgParams.data A hex string conversion of the raw buffer data of the signature request
+ * @property {number} time The epoch time at which the this message was created
+ * @property {string} status Indicates whether the signature request is 'unapproved', 'approved', 'signed' or 'rejected'
+ * @property {string} type The json-prc signing method for which a signature request has been made. A 'Message' will
+ * always have a 'personal_sign' type.
+ *
+ */
module.exports = class PersonalMessageManager extends EventEmitter {
+ /**
+ * Controller in charge of managing - storing, adding, removing, updating - PersonalMessage.
+ *
+ * @typedef {Object} PersonalMessageManager
+ * @param {object} opts @deprecated
+ * @property {object} memStore The observable store where PersonalMessage are saved with persistance.
+ * @property {object} memStore.unapprovedPersonalMsgs A collection of all PersonalMessages in the 'unapproved' state
+ * @property {number} memStore.unapprovedPersonalMsgCount The count of all PersonalMessages in this.memStore.unapprobedMsgs
+ * @property {array} messages Holds all messages that have been created by this PersonalMessageManager
+ *
+ */
constructor (opts) {
super()
this.memStore = new ObservableStore({
@@ -15,15 +44,37 @@ module.exports = class PersonalMessageManager extends EventEmitter {
this.messages = []
}
+ /**
+ * A getter for the number of 'unapproved' PersonalMessages in this.messages
+ *
+ * @returns {number} The number of 'unapproved' PersonalMessages in this.messages
+ *
+ */
get unapprovedPersonalMsgCount () {
return Object.keys(this.getUnapprovedMsgs()).length
}
+ /**
+ * A getter for the 'unapproved' PersonalMessages in this.messages
+ *
+ * @returns {object} An index of PersonalMessage ids to PersonalMessages, for all 'unapproved' PersonalMessages in
+ * this.messages
+ *
+ */
getUnapprovedMsgs () {
return this.messages.filter(msg => msg.status === 'unapproved')
.reduce((result, msg) => { result[msg.id] = msg; return result }, {})
}
+ /**
+ * Creates a new PersonalMessage with an 'unapproved' status using the passed msgParams. this.addMsg is called to add
+ * the new PersonalMessage to this.messages, and to save the unapproved PersonalMessages from that list to
+ * this.memStore.
+ *
+ * @param {object} msgParams The params for the eth_sign call to be made after the message is approved.
+ * @returns {number} The id of the newly created PersonalMessage.
+ *
+ */
addUnapprovedMessage (msgParams) {
log.debug(`PersonalMessageManager addUnapprovedMessage: ${JSON.stringify(msgParams)}`)
msgParams.data = this.normalizeMsgData(msgParams.data)
@@ -44,24 +95,62 @@ module.exports = class PersonalMessageManager extends EventEmitter {
return msgId
}
+ /**
+ * Adds a passed PersonalMessage to this.messages, and calls this._saveMsgList() to save the unapproved PersonalMessages from that
+ * list to this.memStore.
+ *
+ * @param {Message} msg The PersonalMessage to add to this.messages
+ *
+ */
addMsg (msg) {
this.messages.push(msg)
this._saveMsgList()
}
+ /**
+ * Returns a specified PersonalMessage.
+ *
+ * @param {number} msgId The id of the PersonalMessage to get
+ * @returns {PersonalMessage|undefined} The PersonalMessage with the id that matches the passed msgId, or undefined
+ * if no PersonalMessage has that id.
+ *
+ */
getMsg (msgId) {
return this.messages.find(msg => msg.id === msgId)
}
+ /**
+ * Approves a PersonalMessage. Sets the message status via a call to this.setMsgStatusApproved, and returns a promise
+ * with any the message params modified for proper signing.
+ *
+ * @param {object} msgParams The msgParams to be used when eth_sign is called, plus data added by Metamask.
+ * @param {object} msgParams.metamaskId Added to msgParams for tracking and identification within Metamask.
+ * @returns {Promise<object>} Promises the msgParams object with metamaskId removed.
+ *
+ */
approveMessage (msgParams) {
this.setMsgStatusApproved(msgParams.metamaskId)
return this.prepMsgForSigning(msgParams)
}
+ /**
+ * Sets a PersonalMessage status to 'approved' via a call to this._setMsgStatus.
+ *
+ * @param {number} msgId The id of the PersonalMessage to approve.
+ *
+ */
setMsgStatusApproved (msgId) {
this._setMsgStatus(msgId, 'approved')
}
+ /**
+ * Sets a PersonalMessage status to 'signed' via a call to this._setMsgStatus and updates that PersonalMessage in
+ * this.messages by adding the raw signature data of the signature request to the PersonalMessage
+ *
+ * @param {number} msgId The id of the PersonalMessage to sign.
+ * @param {buffer} rawSig The raw data of the signature request
+ *
+ */
setMsgStatusSigned (msgId, rawSig) {
const msg = this.getMsg(msgId)
msg.rawSig = rawSig
@@ -69,19 +158,41 @@ module.exports = class PersonalMessageManager extends EventEmitter {
this._setMsgStatus(msgId, 'signed')
}
+ /**
+ * Removes the metamaskId property from passed msgParams and returns a promise which resolves the updated msgParams
+ *
+ * @param {object} msgParams The msgParams to modify
+ * @returns {Promise<object>} Promises the msgParams with the metamaskId property removed
+ *
+ */
prepMsgForSigning (msgParams) {
delete msgParams.metamaskId
return Promise.resolve(msgParams)
}
+ /**
+ * Sets a PersonalMessage status to 'rejected' via a call to this._setMsgStatus.
+ *
+ * @param {number} msgId The id of the PersonalMessage to reject.
+ *
+ */
rejectMsg (msgId) {
this._setMsgStatus(msgId, 'rejected')
}
- //
- // PRIVATE METHODS
- //
-
+ /**
+ * Updates the status of a PersonalMessage in this.messages via a call to this._updateMsg
+ *
+ * @private
+ * @param {number} msgId The id of the PersonalMessage to update.
+ * @param {string} status The new status of the PersonalMessage.
+ * @throws A 'PersonalMessageManager - PersonalMessage not found for id: "${msgId}".' if there is no PersonalMessage
+ * in this.messages with an id equal to the passed msgId
+ * @fires An event with a name equal to `${msgId}:${status}`. The PersonalMessage is also fired.
+ * @fires If status is 'rejected' or 'signed', an event with a name equal to `${msgId}:finished` is fired along
+ * with the PersonalMessage
+ *
+ */
_setMsgStatus (msgId, status) {
const msg = this.getMsg(msgId)
if (!msg) throw new Error('PersonalMessageManager - Message not found for id: "${msgId}".')
@@ -93,6 +204,15 @@ module.exports = class PersonalMessageManager extends EventEmitter {
}
}
+ /**
+ * Sets a PersonalMessage in this.messages to the passed PersonalMessage if the ids are equal. Then saves the
+ * unapprovedPersonalMsgs index to storage via this._saveMsgList
+ *
+ * @private
+ * @param {msg} PersonalMessage A PersonalMessage that will replace an existing PersonalMessage (with the same
+ * id) in this.messages
+ *
+ */
_updateMsg (msg) {
const index = this.messages.findIndex((message) => message.id === msg.id)
if (index !== -1) {
@@ -101,6 +221,13 @@ module.exports = class PersonalMessageManager extends EventEmitter {
this._saveMsgList()
}
+ /**
+ * Saves the unapproved PersonalMessages, and their count, to this.memStore
+ *
+ * @private
+ * @fires 'updateBadge'
+ *
+ */
_saveMsgList () {
const unapprovedPersonalMsgs = this.getUnapprovedMsgs()
const unapprovedPersonalMsgCount = Object.keys(unapprovedPersonalMsgs).length
@@ -108,6 +235,13 @@ module.exports = class PersonalMessageManager extends EventEmitter {
this.emit('updateBadge')
}
+ /**
+ * A helper function that converts raw buffer data to a hex, or just returns the data if it is already formatted as a hex.
+ *
+ * @param {any} data The buffer data to convert to a hex
+ * @returns {string} A hex string conversion of the buffer data
+ *
+ */
normalizeMsgData (data) {
try {
const stripped = ethUtil.stripHexPrefix(data)
diff --git a/app/scripts/lib/seed-phrase-verifier.js b/app/scripts/lib/seed-phrase-verifier.js
index 9cea22029..826e73306 100644
--- a/app/scripts/lib/seed-phrase-verifier.js
+++ b/app/scripts/lib/seed-phrase-verifier.js
@@ -2,11 +2,19 @@ const KeyringController = require('eth-keyring-controller')
const seedPhraseVerifier = {
- // Verifies if the seed words can restore the accounts.
- //
- // The seed words can recreate the primary keyring and the accounts belonging to it.
- // The created accounts in the primary keyring are always the same.
- // The keyring always creates the accounts in the same sequence.
+ /**
+ * Verifies if the seed words can restore the accounts.
+ *
+ * Key notes:
+ * - The seed words can recreate the primary keyring and the accounts belonging to it.
+ * - The created accounts in the primary keyring are always the same.
+ * - The keyring always creates the accounts in the same sequence.
+ *
+ * @param {array} createdAccounts The accounts to restore
+ * @param {string} seedWords The seed words to verify
+ * @returns {Promise<void>} Promises undefined
+ *
+ */
verifyAccounts (createdAccounts, seedWords) {
return new Promise((resolve, reject) => {
diff --git a/app/scripts/lib/typed-message-manager.js b/app/scripts/lib/typed-message-manager.js
index 8b760790e..8716ebf9a 100644
--- a/app/scripts/lib/typed-message-manager.js
+++ b/app/scripts/lib/typed-message-manager.js
@@ -4,8 +4,38 @@ const createId = require('./random-id')
const assert = require('assert')
const sigUtil = require('eth-sig-util')
+/**
+ * Represents, and contains data about, an 'eth_signTypedData' type signature request. These are created when a
+ * signature for an eth_signTypedData call is requested.
+ *
+ * @see {@link }
+ *
+ * @typedef {Object} TypedMessage
+ * @property {number} id An id to track and identify the message object
+ * @property {object} msgParams The parameters to pass to the eth_signTypedData method once the signature request is
+ * approved.
+ * @property {object} msgParams.metamaskId Added to msgParams for tracking and identification within Metamask.
+ * @property {object} msgParams.from The address that is making the signature request.
+ * @property {string} msgParams.data A hex string conversion of the raw buffer data of the signature request
+ * @property {number} time The epoch time at which the this message was created
+ * @property {string} status Indicates whether the signature request is 'unapproved', 'approved', 'signed' or 'rejected'
+ * @property {string} type The json-prc signing method for which a signature request has been made. A 'Message' will
+ * always have a 'eth_signTypedData' type.
+ *
+ */
module.exports = class TypedMessageManager extends EventEmitter {
+ /**
+ * Controller in charge of managing - storing, adding, removing, updating - TypedMessage.
+ *
+ * @typedef {Object} TypedMessage
+ * @param {object} opts @deprecated
+ * @property {object} memStore The observable store where TypedMessage are saved with persistance.
+ * @property {object} memStore.unapprovedTypedMessages A collection of all TypedMessages in the 'unapproved' state
+ * @property {number} memStore.unapprovedTypedMessagesCount The count of all TypedMessages in this.memStore.unapprobedMsgs
+ * @property {array} messages Holds all messages that have been created by this TypedMessage
+ *
+ */
constructor (opts) {
super()
this.memStore = new ObservableStore({
@@ -15,15 +45,37 @@ module.exports = class TypedMessageManager extends EventEmitter {
this.messages = []
}
+ /**
+ * A getter for the number of 'unapproved' TypedMessages in this.messages
+ *
+ * @returns {number} The number of 'unapproved' TypedMessages in this.messages
+ *
+ */
get unapprovedTypedMessagesCount () {
return Object.keys(this.getUnapprovedMsgs()).length
}
+ /**
+ * A getter for the 'unapproved' TypedMessages in this.messages
+ *
+ * @returns {object} An index of TypedMessage ids to TypedMessages, for all 'unapproved' TypedMessages in
+ * this.messages
+ *
+ */
getUnapprovedMsgs () {
return this.messages.filter(msg => msg.status === 'unapproved')
.reduce((result, msg) => { result[msg.id] = msg; return result }, {})
}
+ /**
+ * Creates a new TypedMessage with an 'unapproved' status using the passed msgParams. this.addMsg is called to add
+ * the new TypedMessage to this.messages, and to save the unapproved TypedMessages from that list to
+ * this.memStore. Before any of this is done, msgParams are validated
+ *
+ * @param {object} msgParams The params for the eth_sign call to be made after the message is approved.
+ * @returns {number} The id of the newly created TypedMessage.
+ *
+ */
addUnapprovedMessage (msgParams) {
this.validateParams(msgParams)
@@ -45,6 +97,12 @@ module.exports = class TypedMessageManager extends EventEmitter {
return msgId
}
+ /**
+ * Helper method for this.addUnapprovedMessage. Validates that the passed params have the required properties.
+ *
+ * @param {object} params The params to validate
+ *
+ */
validateParams (params) {
assert.equal(typeof params, 'object', 'Params should ben an object.')
assert.ok('data' in params, 'Params must include a data field.')
@@ -56,24 +114,62 @@ module.exports = class TypedMessageManager extends EventEmitter {
}, 'Expected EIP712 typed data')
}
+ /**
+ * Adds a passed TypedMessage to this.messages, and calls this._saveMsgList() to save the unapproved TypedMessages from that
+ * list to this.memStore.
+ *
+ * @param {Message} msg The TypedMessage to add to this.messages
+ *
+ */
addMsg (msg) {
this.messages.push(msg)
this._saveMsgList()
}
+ /**
+ * Returns a specified TypedMessage.
+ *
+ * @param {number} msgId The id of the TypedMessage to get
+ * @returns {TypedMessage|undefined} The TypedMessage with the id that matches the passed msgId, or undefined
+ * if no TypedMessage has that id.
+ *
+ */
getMsg (msgId) {
return this.messages.find(msg => msg.id === msgId)
}
+ /**
+ * Approves a TypedMessage. Sets the message status via a call to this.setMsgStatusApproved, and returns a promise
+ * with any the message params modified for proper signing.
+ *
+ * @param {object} msgParams The msgParams to be used when eth_sign is called, plus data added by Metamask.
+ * @param {object} msgParams.metamaskId Added to msgParams for tracking and identification within Metamask.
+ * @returns {Promise<object>} Promises the msgParams object with metamaskId removed.
+ *
+ */
approveMessage (msgParams) {
this.setMsgStatusApproved(msgParams.metamaskId)
return this.prepMsgForSigning(msgParams)
}
+ /**
+ * Sets a TypedMessage status to 'approved' via a call to this._setMsgStatus.
+ *
+ * @param {number} msgId The id of the TypedMessage to approve.
+ *
+ */
setMsgStatusApproved (msgId) {
this._setMsgStatus(msgId, 'approved')
}
+ /**
+ * Sets a TypedMessage status to 'signed' via a call to this._setMsgStatus and updates that TypedMessage in
+ * this.messages by adding the raw signature data of the signature request to the TypedMessage
+ *
+ * @param {number} msgId The id of the TypedMessage to sign.
+ * @param {buffer} rawSig The raw data of the signature request
+ *
+ */
setMsgStatusSigned (msgId, rawSig) {
const msg = this.getMsg(msgId)
msg.rawSig = rawSig
@@ -81,11 +177,24 @@ module.exports = class TypedMessageManager extends EventEmitter {
this._setMsgStatus(msgId, 'signed')
}
+ /**
+ * Removes the metamaskId property from passed msgParams and returns a promise which resolves the updated msgParams
+ *
+ * @param {object} msgParams The msgParams to modify
+ * @returns {Promise<object>} Promises the msgParams with the metamaskId property removed
+ *
+ */
prepMsgForSigning (msgParams) {
delete msgParams.metamaskId
return Promise.resolve(msgParams)
}
+ /**
+ * Sets a TypedMessage status to 'rejected' via a call to this._setMsgStatus.
+ *
+ * @param {number} msgId The id of the TypedMessage to reject.
+ *
+ */
rejectMsg (msgId) {
this._setMsgStatus(msgId, 'rejected')
}
@@ -94,6 +203,19 @@ module.exports = class TypedMessageManager extends EventEmitter {
// PRIVATE METHODS
//
+ /**
+ * Updates the status of a TypedMessage in this.messages via a call to this._updateMsg
+ *
+ * @private
+ * @param {number} msgId The id of the TypedMessage to update.
+ * @param {string} status The new status of the TypedMessage.
+ * @throws A 'TypedMessageManager - TypedMessage not found for id: "${msgId}".' if there is no TypedMessage
+ * in this.messages with an id equal to the passed msgId
+ * @fires An event with a name equal to `${msgId}:${status}`. The TypedMessage is also fired.
+ * @fires If status is 'rejected' or 'signed', an event with a name equal to `${msgId}:finished` is fired along
+ * with the TypedMessage
+ *
+ */
_setMsgStatus (msgId, status) {
const msg = this.getMsg(msgId)
if (!msg) throw new Error('TypedMessageManager - Message not found for id: "${msgId}".')
@@ -105,6 +227,15 @@ module.exports = class TypedMessageManager extends EventEmitter {
}
}
+ /**
+ * Sets a TypedMessage in this.messages to the passed TypedMessage if the ids are equal. Then saves the
+ * unapprovedTypedMsgs index to storage via this._saveMsgList
+ *
+ * @private
+ * @param {msg} TypedMessage A TypedMessage that will replace an existing TypedMessage (with the same
+ * id) in this.messages
+ *
+ */
_updateMsg (msg) {
const index = this.messages.findIndex((message) => message.id === msg.id)
if (index !== -1) {
@@ -113,6 +244,13 @@ module.exports = class TypedMessageManager extends EventEmitter {
this._saveMsgList()
}
+ /**
+ * Saves the unapproved TypedMessages, and their count, to this.memStore
+ *
+ * @private
+ * @fires 'updateBadge'
+ *
+ */
_saveMsgList () {
const unapprovedTypedMessages = this.getUnapprovedMsgs()
const unapprovedTypedMessagesCount = Object.keys(unapprovedTypedMessages).length