1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-12-23 09:52:26 +01:00

Merge pull request #3987 from MetaMask/dm-docs-1

Documentation for various controllers and and lib utils
This commit is contained in:
Dan Finlay 2018-04-18 13:39:31 -07:00 committed by GitHub
commit 6742a5b272
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 389 additions and 17 deletions

View File

@ -4,9 +4,22 @@ const extend = require('xtend')
class AddressBookController {
// Controller in charge of managing the address book functionality from the
// recipients field on the send screen. Manages a history of all saved
// addresses and all currently owned addresses.
/**
* Controller in charge of managing the address book functionality from the
* recipients field on the send screen. Manages a history of all saved
* addresses and all currently owned addresses.
*
* @typedef {Object} AddressBookController
* @param {object} opts Overrides the defaults for the initial state of this.store
* @property {array} opts.initState initializes the the state of the AddressBookController. Can contain an
* addressBook property to initialize the addressBook array
* @param {KeyringController} keyringController (Soon to be deprecated) The keyringController used in the current
* MetamaskController. Contains the identities used in this AddressBookController.
* @property {object} store The the store of the current users address book
* @property {array} store.addressBook An array of addresses and nicknames. These are set by the user when sending
* to a new address.
*
*/
constructor (opts = {}, keyringController) {
const initState = extend({
addressBook: [],
@ -19,7 +32,14 @@ class AddressBookController {
// PUBLIC METHODS
//
// Sets a new address book in store by accepting a new address and nickname.
/**
* Sets a new address book in store by accepting a new address and nickname.
*
* @param {string} address A hex address of a new account that the user is sending to.
* @param {string} name The name the user wishes to associate with the new account
* @returns {Promise<void>} Promise resolves with undefined
*
*/
setAddressBook (address, name) {
return this._addToAddressBook(address, name)
.then((addressBook) => {
@ -30,14 +50,16 @@ class AddressBookController {
})
}
//
// PRIVATE METHODS
//
// Performs the logic to add the address and name into the address book. The
// pushed object is an object of two fields. Current behavior does not set an
// upper limit to the number of addresses.
/**
* Performs the logic to add the address and name into the address book. The pushed object is an object of two
* fields. Current behavior does not set an upper limit to the number of addresses.
*
* @private
* @param {string} address A hex address of a new account that the user is sending to.
* @param {string} name The name the user wishes to associate with the new account
* @returns {Promise<array>} Promises the updated addressBook array
*
*/
_addToAddressBook (address, name) {
const addressBook = this._getAddressBook()
const identities = this._getIdentities()
@ -62,14 +84,26 @@ class AddressBookController {
return Promise.resolve(addressBook)
}
// Internal method to get the address book. Current persistence behavior
// should not require that this method be called from the UI directly.
/**
* Internal method to get the address book. Current persistence behavior should not require that this method be
* called from the UI directly.
*
* @private
* @returns {array} The addressBook array from the store.
*
*/
_getAddressBook () {
return this.store.getState().addressBook
}
// Retrieves identities from the keyring controller in order to avoid
// duplication
/**
* Retrieves identities from the keyring controller in order to avoid
* duplication
*
* @deprecated
* @returns {array} Returns the identies array from the keyringContoller's state
*
*/
_getIdentities () {
return this.keyringController.memStore.getState().identities
}

View File

@ -1,4 +1,4 @@
const ObservableStore = require('obs-store')
const ObservableStore = require('obs-store')
const extend = require('xtend')
const log = require('loglevel')
@ -7,6 +7,22 @@ const POLLING_INTERVAL = 600000
class CurrencyController {
/**
* Controller responsible for managing data associated with the currently selected currency.
*
* @typedef {Object} CurrencyController
* @param {object} opts Overrides the defaults for the initial state of this.store
* @property {array} opts.initState initializes the the state of the CurrencyController. Can contain an
* currentCurrency, conversionRate and conversionDate properties
* @property {string} currentCurrency A 2-4 character shorthand that describes a specific currency, currently
* selected by the user
* @property {number} conversionRate The conversion rate from ETH to the selected currency.
* @property {string} conversionDate The date at which the conversion rate was set. Expressed in in milliseconds
* since midnight of January 1, 1970
* @property {number} conversionInterval The id of the interval created by the scheduleConversionInterval method.
* Used to clear an existing interval on subsequent calls of that method.
*
*/
constructor (opts = {}) {
const initState = extend({
currentCurrency: 'usd',
@ -20,30 +36,73 @@ class CurrencyController {
// PUBLIC METHODS
//
/**
* A getter for the currentCurrency property
*
* @returns {string} A 2-4 character shorthand that describes a specific currency, currently selected by the user
*
*/
getCurrentCurrency () {
return this.store.getState().currentCurrency
}
/**
* A setter for the currentCurrency property
*
* @param {string} currentCurrency The new currency to set as the currentCurrency in the store
*
*/
setCurrentCurrency (currentCurrency) {
this.store.updateState({ currentCurrency })
}
/**
* A getter for the conversionRate property
*
* @returns {string} The conversion rate from ETH to the selected currency.
*
*/
getConversionRate () {
return this.store.getState().conversionRate
}
/**
* A setter for the conversionRate property
*
* @param {number} conversionRate The new rate to set as the conversionRate in the store
*
*/
setConversionRate (conversionRate) {
this.store.updateState({ conversionRate })
}
/**
* A getter for the conversionDate property
*
* @returns {string} The date at which the conversion rate was set. Expressed in milliseconds since midnight of
* January 1, 1970
*
*/
getConversionDate () {
return this.store.getState().conversionDate
}
/**
* A setter for the conversionDate property
*
* @param {number} conversionDate The date, expressed in milliseconds since midnight of January 1, 1970, that the
* conversionRate was set
*
*/
setConversionDate (conversionDate) {
this.store.updateState({ conversionDate })
}
/**
* Updates the conversionRate and conversionDate properties associated with the currentCurrency. Updated info is
* fetched from an external API
*
*/
async updateConversionRate () {
let currentCurrency
try {
@ -59,6 +118,12 @@ class CurrencyController {
}
}
/**
* Creates a new poll, using setInterval, to periodically call updateConversionRate. The id of the interval is
* stored at the controller's conversionInterval property. If it is called and such an id already exists, the
* previous interval is clear and a new one is created.
*
*/
scheduleConversionInterval () {
if (this.conversionInterval) {
clearInterval(this.conversionInterval)

View File

@ -4,6 +4,21 @@ const extend = require('xtend')
class PreferencesController {
/**
*
* @typedef {Object} PreferencesController
* @param {object} opts Overrides the defaults for the initial state of this.store
* @property {object} store The an object containing a users preferences, stored in local storage
* @property {array} store.frequentRpcList A list of custom rpcs to provide the user
* @property {string} store.currentAccountTab Indicates the selected tab in the ui
* @property {array} store.tokens The tokens the user wants display in their token lists
* @property {boolean} store.useBlockie The users preference for blockie identicons within the UI
* @property {object} store.featureFlags A key-boolean map, where keys refer to features and booleans to whether the
* user wishes to see that feature
* @property {string} store.currentLocale The preferred language locale key
* @property {string} store.selectedAddress A hex string that matches the currently selected address in the app
*
*/
constructor (opts = {}) {
const initState = extend({
frequentRpcList: [],
@ -17,18 +32,43 @@ class PreferencesController {
}
// PUBLIC METHODS
/**
* Setter for the `useBlockie` property
*
* @param {boolean} val Whether or not the user prefers blockie indicators
*
*/
setUseBlockie (val) {
this.store.updateState({ useBlockie: val })
}
/**
* Getter for the `useBlockie` property
*
* @returns {boolean} this.store.useBlockie
*
*/
getUseBlockie () {
return this.store.getState().useBlockie
}
/**
* Setter for the `currentLocale` property
*
* @param {string} key he preferred language locale key
*
*/
setCurrentLocale (key) {
this.store.updateState({ currentLocale: key })
}
/**
* Setter for the `selectedAddress` property
*
* @param {string} _address A new hex address for an account
* @returns {Promise<void>} Promise resolves with undefined
*
*/
setSelectedAddress (_address) {
return new Promise((resolve, reject) => {
const address = normalizeAddress(_address)
@ -37,10 +77,37 @@ class PreferencesController {
})
}
/**
* Getter for the `selectedAddress` property
*
* @returns {string} The hex address for the currently selected account
*
*/
getSelectedAddress () {
return this.store.getState().selectedAddress
}
/**
* Contains data about tokens users add to their account.
* @typedef {Object} AddedToken
* @property {string} address - The hex address for the token contract. Will be all lower cased and hex-prefixed.
* @property {string} symbol - The symbol of the token, usually 3 or 4 capitalized letters
* {@link https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md#symbol}
* @property {boolean} decimals - The number of decimals the token uses.
* {@link https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md#decimals}
*/
/**
* Adds a new token to the token array, or updates the token if passed an address that already exists.
* Modifies the existing tokens array from the store. All objects in the tokens array array AddedToken objects.
* @see AddedToken {@link AddedToken}
*
* @param {string} rawAddress Hex address of the token contract. May or may not be a checksum address.
* @param {string} symbol The symbol of the token
* @param {number} decimals The number of decimals the token uses.
* @returns {Promise<array>} Promises the new array of AddedToken objects.
*
*/
async addToken (rawAddress, symbol, decimals) {
const address = normalizeAddress(rawAddress)
const newEntry = { address, symbol, decimals }
@ -62,6 +129,13 @@ class PreferencesController {
return Promise.resolve(tokens)
}
/**
* Removes a specified token from the tokens array.
*
* @param {string} rawAddress Hex address of the token contract to remove.
* @returns {Promise<array>} The new array of AddedToken objects
*
*/
removeToken (rawAddress) {
const tokens = this.store.getState().tokens
@ -71,10 +145,23 @@ class PreferencesController {
return Promise.resolve(updatedTokens)
}
/**
* A getter for the `tokens` property
*
* @returns {array} The current array of AddedToken objects
*
*/
getTokens () {
return this.store.getState().tokens
}
/**
* Gets an updated rpc list from this.addToFrequentRpcList() and sets the `frequentRpcList` to this update list.
*
* @param {string} _url The the new rpc url to add to the updated list
* @returns {Promise<void>} Promise resolves with undefined
*
*/
updateFrequentRpcList (_url) {
return this.addToFrequentRpcList(_url)
.then((rpcList) => {
@ -83,6 +170,13 @@ class PreferencesController {
})
}
/**
* Setter for the `currentAccountTab` property
*
* @param {string} currentAccountTab Specifies the new tab to be marked as current
* @returns {Promise<void>} Promise resolves with undefined
*
*/
setCurrentAccountTab (currentAccountTab) {
return new Promise((resolve, reject) => {
this.store.updateState({ currentAccountTab })
@ -90,6 +184,15 @@ class PreferencesController {
})
}
/**
* Returns an updated rpcList based on the passed url and the current list.
* The returned list will have a max length of 2. If the _url currently exists it the list, it will be moved to the
* end of the list. The current list is modified and returned as a promise.
*
* @param {string} _url The rpc url to add to the frequentRpcList.
* @returns {Promise<array>} The updated frequentRpcList.
*
*/
addToFrequentRpcList (_url) {
const rpcList = this.getFrequentRpcList()
const index = rpcList.findIndex((element) => { return element === _url })
@ -105,10 +208,24 @@ class PreferencesController {
return Promise.resolve(rpcList)
}
/**
* Getter for the `frequentRpcList` property.
*
* @returns {array<string>} An array of one or two rpc urls.
*
*/
getFrequentRpcList () {
return this.store.getState().frequentRpcList
}
/**
* Updates the `featureFlags` property, which is an object. One property within that object will be set to a boolean.
*
* @param {string} feature A key that corresponds to a UI feature.
* @param {boolean} activated Indicates whether or not the UI feature should be displayed
* @returns {Promise<object>} Promises a new object; the updated featureFlags object.
*
*/
setFeatureFlag (feature, activated) {
const currentFeatureFlags = this.store.getState().featureFlags
const updatedFeatureFlags = {
@ -121,6 +238,13 @@ class PreferencesController {
return Promise.resolve(updatedFeatureFlags)
}
/**
* A getter for the `featureFlags` property
*
* @returns {object} A key-boolean map, where keys refer to features and booleans to whether the
* user wishes to see that feature
*
*/
getFeatureFlags () {
return this.store.getState().featureFlags
}

View File

@ -7,6 +7,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 Overrides the defaults for the initial state of this.store
* @property {array} opts.initState initializes the the state of the ShapeshiftController. Can contain an
* shapeShiftTxList array.
* @property {array} shapeShiftTxList An array of ShapeShiftTx objects
*
*/
constructor (opts = {}) {
const initState = extend({
shapeShiftTxList: [],
@ -15,21 +26,54 @@ class ShapeshiftController {
this.pollForUpdates()
}
/**
* Represents, and contains data about, a single shapeshift transaction.
* @typedef {Object} ShapeShiftTx
* @property {string} depositAddress - An address at which to send a crypto deposit, so that eth can be sent to the
* user's Metamask account
* @property {string} depositType - An abbreviation of the type of crypto currency to be deposited.
* @property {string} key - The 'shapeshift' key differentiates this from other types of txs in Metamask
* @property {number} time - The time at which the tx was created
* @property {object} response - Initiated as an empty object, which will be replaced by a Response object. @see {@link
* https://developer.mozilla.org/en-US/docs/Web/API/Response}
*/
//
// PUBLIC METHODS
//
/**
* A getter for the shapeShiftTxList property
*
* @returns {array<ShapeShiftTx>}
*
*/
getShapeShiftTxList () {
const shapeShiftTxList = this.store.getState().shapeShiftTxList
return shapeShiftTxList
}
/**
* A getter for all ShapeShiftTx in the shapeShiftTxList that have not successfully completed a deposit.
*
* @returns {array<ShapeShiftTx>} Only includes ShapeShiftTx which has a response property with a status !== complete
*
*/
getPendingTxs () {
const txs = this.getShapeShiftTxList()
const pending = txs.filter(tx => tx.response && tx.response.status !== 'complete')
return pending
}
/**
* A poll that exists as long as there are pending transactions. Each call attempts to update the data of any
* pendingTxs, and then calls itself again. If there are no pending txs, the recursive call is not made and
* the polling stops.
*
* this.updateTx is used to attempt the update to the pendingTxs in the ShapeShiftTxList, and that updated data
* is saved with saveTx.
*
*/
pollForUpdates () {
const pendingTxs = this.getPendingTxs()
@ -46,6 +90,15 @@ class ShapeshiftController {
})
}
/**
* Attempts to update a ShapeShiftTx with data from a shapeshift.io API. Both the response and time properties
* can be updated. The response property is updated with every call, but the time property is only updated when
* the response status updates to 'complete'. This will occur once the user makes a deposit as the ShapeShiftTx
* depositAddress
*
* @param {ShapeShiftTx} tx The tx to update
*
*/
async updateTx (tx) {
try {
const url = `https://shapeshift.io/txStat/${tx.depositAddress}`
@ -61,6 +114,13 @@ class ShapeshiftController {
}
}
/**
* Saves an updated to a ShapeShiftTx in the shapeShiftTxList. If the passed ShapeShiftTx is not in the
* shapeShiftTxList, nothing happens.
*
* @param {ShapeShiftTx} tx The updated tx to save, if it exists in the current shapeShiftTxList
*
*/
saveTx (tx) {
const { shapeShiftTxList } = this.store.getState()
const index = shapeShiftTxList.indexOf(tx)
@ -70,6 +130,12 @@ class ShapeshiftController {
}
}
/**
* Removes a ShapeShiftTx from the shapeShiftTxList
*
* @param {ShapeShiftTx} tx The tx to remove
*
*/
removeShapeShiftTx (tx) {
const { shapeShiftTxList } = this.store.getState()
const index = shapeShiftTxList.indexOf(index)
@ -79,6 +145,14 @@ class ShapeshiftController {
this.updateState({ shapeShiftTxList })
}
/**
* Creates a new ShapeShiftTx, adds it to the shapeShiftTxList, and initiates a new poll for updates of pending txs
*
* @param {string} depositAddress - An address at which to send a crypto deposit, so that eth can be sent to the
* user's Metamask account
* @param {string} depositType - An abbreviation of the type of crypto currency to be deposited.
*
*/
createShapeShiftTx (depositAddress, depositType) {
const state = this.store.getState()
let { shapeShiftTxList } = state

View File

@ -1,5 +1,16 @@
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 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 }) {
let url
switch (network) {

View File

@ -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 {Promise<string>} Promises 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,

View File

@ -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 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
*
*/
module.exports = function nodeify (fn, context) {
return function () {
const args = [].slice.call(arguments)

View File

@ -7,11 +7,26 @@ const {
ENVIRONMENT_TYPE_FULLSCREEN,
} = require('./enums')
/**
* 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
}
/**
* 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
@ -22,6 +37,17 @@ const getEnvironmentType = (url = window.location.href) => {
}
}
/**
* 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')
@ -36,14 +62,37 @@ function sufficientBalance (txParams, hexBalance) {
return balance.gte(maxCost)
}
/**
* Converts a BN object to a hex string with a '0x' prefix
*
* @param {BN} inputBn The BN to convert to a hex string
* @returns {string} A '0x' prefixed 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 The numerator of the fraction multiplier
* @param {number|string} denominator The denominator of the fraction multiplier
* @returns {BN} The product of the multiplication
*
*/
function BnMultiplyByFraction (targetBN, numerator, denominator) {
const numBN = new BN(numerator)
const denomBN = new BN(denominator)