aboutsummaryrefslogtreecommitdiffstats
path: root/app/scripts/controllers
diff options
context:
space:
mode:
Diffstat (limited to 'app/scripts/controllers')
-rw-r--r--app/scripts/controllers/preferences.js107
1 files changed, 101 insertions, 6 deletions
diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js
index 707fd7de9..611d2d067 100644
--- a/app/scripts/controllers/preferences.js
+++ b/app/scripts/controllers/preferences.js
@@ -1,5 +1,6 @@
const ObservableStore = require('obs-store')
const normalizeAddress = require('eth-sig-util').normalize
+const isValidAddress = require('ethereumjs-util').isValidAddress
const extend = require('xtend')
@@ -14,6 +15,7 @@ class PreferencesController {
* @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 {object} store.accountTokens The tokens stored per account and then per network type
+ * @property {object} store.imageObjects Contains assets objects related to assets added
* @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
@@ -26,7 +28,9 @@ class PreferencesController {
frequentRpcList: [],
currentAccountTab: 'history',
accountTokens: {},
+ imageObjects: {},
tokens: [],
+ suggestedTokens: {},
useBlockie: false,
featureFlags: {},
currentLocale: opts.initLangCode,
@@ -37,6 +41,7 @@ class PreferencesController {
this.diagnostics = opts.diagnostics
this.network = opts.network
this.store = new ObservableStore(initState)
+ this.showAddTokenUi = opts.showAddTokenUi
this._subscribeProviderType()
}
// PUBLIC METHODS
@@ -51,6 +56,50 @@ class PreferencesController {
this.store.updateState({ useBlockie: val })
}
+ getSuggestedTokens () {
+ return this.store.getState().suggestedTokens
+ }
+
+ getImageObjects () {
+ return this.store.getState().imageObjects
+ }
+
+ addSuggestedToken (tokenOpts) {
+ this._validateSuggestedTokenParams(tokenOpts)
+ const suggested = this.getSuggestedTokens()
+ const { rawAddress, symbol, decimals, imageUrl } = tokenOpts
+ const address = normalizeAddress(rawAddress)
+ const newEntry = { address, symbol, decimals, imageUrl }
+ suggested[address] = newEntry
+ this.store.updateState({ suggestedTokens: suggested })
+ }
+
+ /**
+ * RPC engine middleware for requesting new asset added
+ *
+ * @param req
+ * @param res
+ * @param {Function} - next
+ * @param {Function} - end
+ */
+ requestAddToken (req, res, next, end) {
+ if (req.method === 'metamask_watchAsset') {
+ const { type, options } = req.params
+ switch (type) {
+ case 'ERC20':
+ this._handleWatchAssetERC20(options, res)
+ res.result = options.address
+ end()
+ break
+ default:
+ // TODO return promise for not handled assets
+ end(new Error(`Asset of type ${type} not supported`))
+ }
+ } else {
+ next()
+ }
+ }
+
/**
* Getter for the `useBlockie` property
*
@@ -186,6 +235,13 @@ class PreferencesController {
return selected
}
+ removeSuggestedTokens () {
+ return new Promise((resolve, reject) => {
+ this.store.updateState({ suggestedTokens: {} })
+ resolve({})
+ })
+ }
+
/**
* Setter for the `selectedAddress` property
*
@@ -232,11 +288,11 @@ class PreferencesController {
* @returns {Promise<array>} Promises the new array of AddedToken objects.
*
*/
- async addToken (rawAddress, symbol, decimals) {
+ async addToken (rawAddress, symbol, decimals, imageUrl) {
const address = normalizeAddress(rawAddress)
const newEntry = { address, symbol, decimals }
-
const tokens = this.store.getState().tokens
+ const imageObjects = this.getImageObjects()
const previousEntry = tokens.find((token, index) => {
return token.address === address
})
@@ -247,7 +303,8 @@ class PreferencesController {
} else {
tokens.push(newEntry)
}
- this._updateAccountTokens(tokens)
+ imageObjects[address] = imageUrl
+ this._updateAccountTokens(tokens, imageObjects)
return Promise.resolve(tokens)
}
@@ -260,8 +317,10 @@ class PreferencesController {
*/
removeToken (rawAddress) {
const tokens = this.store.getState().tokens
+ const imageObjects = this.getImageObjects()
const updatedTokens = tokens.filter(token => token.address !== rawAddress)
- this._updateAccountTokens(updatedTokens)
+ delete imageObjects[rawAddress]
+ this._updateAccountTokens(updatedTokens, imageObjects)
return Promise.resolve(updatedTokens)
}
@@ -387,6 +446,26 @@ class PreferencesController {
//
// PRIVATE METHODS
//
+
+ /**
+ * Validates that the passed options for suggested token have all required properties.
+ *
+ * @param {Object} opts The options object to validate
+ * @throws {string} Throw a custom error indicating that address, symbol and/or decimals
+ * doesn't fulfill requirements
+ *
+ */
+ _validateSuggestedTokenParams (opts) {
+ const { rawAddress, symbol, decimals } = opts
+ if (!rawAddress || !symbol || !decimals) throw new Error(`Cannot suggest token without address, symbol, and decimals`)
+ if (!(symbol.length < 5)) throw new Error(`Invalid symbol ${symbol} more than four characters`)
+ const numDecimals = parseInt(decimals, 10)
+ if (isNaN(numDecimals) || numDecimals > 36 || numDecimals < 0) {
+ throw new Error(`Invalid decimals ${decimals} must be at least 0, and not over 36`)
+ }
+ if (!isValidAddress(rawAddress)) throw new Error(`Invalid address ${rawAddress}`)
+ }
+
/**
* Subscription to network provider type.
*
@@ -405,10 +484,10 @@ class PreferencesController {
* @param {array} tokens Array of tokens to be updated.
*
*/
- _updateAccountTokens (tokens) {
+ _updateAccountTokens (tokens, imageObjects) {
const { accountTokens, providerType, selectedAddress } = this._getTokenRelatedStates()
accountTokens[selectedAddress][providerType] = tokens
- this.store.updateState({ accountTokens, tokens })
+ this.store.updateState({ accountTokens, tokens, imageObjects })
}
/**
@@ -438,6 +517,22 @@ class PreferencesController {
const tokens = accountTokens[selectedAddress][providerType]
return { tokens, accountTokens, providerType, selectedAddress }
}
+
+ /**
+ * Handle the suggestion of an ERC20 asset through `watchAsset`
+ * *
+ * @param {Object} options Parameters according to addition of ERC20 token
+ *
+ */
+ _handleWatchAssetERC20 (options) {
+ // TODO handle bad parameters
+ const { address, symbol, decimals, imageUrl } = options
+ const rawAddress = address
+ this._validateSuggestedTokenParams({ rawAddress, symbol, decimals })
+ const tokenOpts = { rawAddress, decimals, symbol, imageUrl }
+ this.addSuggestedToken(tokenOpts)
+ this.showAddTokenUi()
+ }
}
module.exports = PreferencesController