From 5d7c2810a701097ef1a4f9de23948418340f9cb4 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Mon, 18 Jun 2018 15:05:41 -0700 Subject: [PATCH 01/36] Begin adding eth_watchToken --- app/scripts/background.js | 2 ++ app/scripts/controllers/preferences.js | 25 +++++++++++++++++++++++++ app/scripts/metamask-controller.js | 1 + 3 files changed, 28 insertions(+) diff --git a/app/scripts/background.js b/app/scripts/background.js index 2451cddb6..2be600c4b 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -248,6 +248,7 @@ function setupController (initState, initLangCode) { showUnconfirmedMessage: triggerUi, unlockAccountMessage: triggerUi, showUnapprovedTx: triggerUi, + showAddTokenUi: triggerUi, // initial state initState, // initial locale code @@ -436,3 +437,4 @@ extension.runtime.onInstalled.addListener(function (details) { extension.tabs.create({url: 'https://metamask.io/#how-it-works'}) } }) + diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js index 8411e3a28..8a8b9a335 100644 --- a/app/scripts/controllers/preferences.js +++ b/app/scripts/controllers/preferences.js @@ -25,6 +25,7 @@ class PreferencesController { frequentRpcList: [], currentAccountTab: 'history', tokens: [], + suggestedTokens: [], useBlockie: false, featureFlags: {}, currentLocale: opts.initLangCode, @@ -48,6 +49,30 @@ class PreferencesController { this.store.updateState({ useBlockie: val }) } + getSuggestedTokens () { + return this.store.getState().suggestedTokens + } + + /** + * RPC engine middleware for requesting new token added + * + * @param req + * @param res + * @param {Function} - next + * @param {Function} - end + */ + requestAddToken(req, res, next, end) { + if (req.method === 'eth_watchToken') { + // Validate params! + // this.suggestedTokens.push(req.params) + const [ rawAddress, symbol, decimals ] = req.params + this.addToken(rawAddress, symbol, decimals) + end(null, rawAddress) + } else { + next() + } + } + /** * Getter for the `useBlockie` property * diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index d40a351a5..0af7c5051 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -1077,6 +1077,7 @@ module.exports = class MetamaskController extends EventEmitter { engine.push(createOriginMiddleware({ origin })) engine.push(createLoggerMiddleware({ origin })) engine.push(filterMiddleware) + engine.push(this.preferencesController.requestAddToken.bind(this.preferencesController)) engine.push(createProviderMiddleware({ provider: this.provider })) // setup connection From f14ed329801ab65c31e84f8e9d8d93700ed56670 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Mon, 18 Jun 2018 15:33:50 -0700 Subject: [PATCH 02/36] Begin letting UI show suggested tokens --- app/scripts/controllers/preferences.js | 30 +++++++++++++++---- .../confirm-add-token.container.js | 8 +++-- ui/app/components/pages/home.js | 7 +++++ 3 files changed, 37 insertions(+), 8 deletions(-) diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js index 8a8b9a335..b76141be4 100644 --- a/app/scripts/controllers/preferences.js +++ b/app/scripts/controllers/preferences.js @@ -25,7 +25,7 @@ class PreferencesController { frequentRpcList: [], currentAccountTab: 'history', tokens: [], - suggestedTokens: [], + suggestedTokens: {}, useBlockie: false, featureFlags: {}, currentLocale: opts.initLangCode, @@ -53,6 +53,13 @@ class PreferencesController { return this.store.getState().suggestedTokens } + addSuggestedToken (tokenOpts) { + // TODO: Validate params + const suggested = this.getSuggestedTokens() + suggested[tokenOpts.address] = suggested + this.store.updateState({ suggestedTokens: suggested }) + } + /** * RPC engine middleware for requesting new token added * @@ -63,13 +70,24 @@ class PreferencesController { */ requestAddToken(req, res, next, end) { if (req.method === 'eth_watchToken') { - // Validate params! - // this.suggestedTokens.push(req.params) + // TODO: Validate params! const [ rawAddress, symbol, decimals ] = req.params - this.addToken(rawAddress, symbol, decimals) - end(null, rawAddress) + + const tokenOpts = { + address: rawAddress, + decimals, + symbol, + } + + this.suggestWatchToken() + + return end(null, { + result: rawAddress, + "jsonrpc": "2.0", + id: req.id, + }) } else { - next() + return next() } } diff --git a/ui/app/components/pages/confirm-add-token/confirm-add-token.container.js b/ui/app/components/pages/confirm-add-token/confirm-add-token.container.js index 0190024d9..500b406bb 100644 --- a/ui/app/components/pages/confirm-add-token/confirm-add-token.container.js +++ b/ui/app/components/pages/confirm-add-token/confirm-add-token.container.js @@ -1,12 +1,16 @@ import { connect } from 'react-redux' import ConfirmAddToken from './confirm-add-token.component' +const extend = require('xtend') + const { addTokens, clearPendingTokens } = require('../../../actions') const mapStateToProps = ({ metamask }) => { - const { pendingTokens } = metamask + const { pendingTokens, suggestedTokens } = metamask + const params = extend(pendingTokens, suggestedTokens) + return { - pendingTokens, + pendingTokens: params, } } diff --git a/ui/app/components/pages/home.js b/ui/app/components/pages/home.js index c53413d3b..f37366396 100644 --- a/ui/app/components/pages/home.js +++ b/ui/app/components/pages/home.js @@ -25,6 +25,7 @@ const { RESTORE_VAULT_ROUTE, CONFIRM_TRANSACTION_ROUTE, NOTICE_ROUTE, + CONFIRM_ADD_TOKEN_ROUTE, } = require('../../routes') class Home extends Component { @@ -35,8 +36,14 @@ class Home extends Component { unapprovedMsgCount = 0, unapprovedPersonalMsgCount = 0, unapprovedTypedMessagesCount = 0, + suggestedTokens = {}, } = this.props + // suggested new tokens + if (suggestedTokens.length > 0) { + history.push(CONFIRM_ADD_TOKEN_ROUTE) + } + // unapprovedTxs and unapproved messages if (Object.keys(unapprovedTxs).length || unapprovedTypedMessagesCount + unapprovedMsgCount + unapprovedPersonalMsgCount > 0) { From 5e4f3e430a9057b073cfc82255fe62c5e8550e44 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Mon, 18 Jun 2018 15:37:37 -0700 Subject: [PATCH 03/36] Get popup appearing when suggesting new token --- app/scripts/controllers/preferences.js | 4 +++- app/scripts/metamask-controller.js | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js index b76141be4..e33501cd0 100644 --- a/app/scripts/controllers/preferences.js +++ b/app/scripts/controllers/preferences.js @@ -36,6 +36,7 @@ class PreferencesController { this.diagnostics = opts.diagnostics this.store = new ObservableStore(initState) + this.showAddTokenUi = opts.showAddTokenUi } // PUBLIC METHODS @@ -79,7 +80,8 @@ class PreferencesController { symbol, } - this.suggestWatchToken() + this.addSuggestedToken(tokenOpts) + this.showAddTokenUi() return end(null, { result: rawAddress, diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 0af7c5051..d5627a0d1 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -85,6 +85,7 @@ module.exports = class MetamaskController extends EventEmitter { this.preferencesController = new PreferencesController({ initState: initState.PreferencesController, initLangCode: opts.initLangCode, + showAddTokenUi: opts.showAddTokenUi, }) // currency controller From d26ce807fb9d21c29122d912d8dc1afc805c72ad Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Mon, 18 Jun 2018 15:44:43 -0700 Subject: [PATCH 04/36] Linted --- ui/app/components/pages/home.js | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/app/components/pages/home.js b/ui/app/components/pages/home.js index f37366396..3dcf63c44 100644 --- a/ui/app/components/pages/home.js +++ b/ui/app/components/pages/home.js @@ -268,6 +268,7 @@ Home.propTypes = { isPopup: PropTypes.bool, isMouseUser: PropTypes.bool, t: PropTypes.func, + suggestedTokens: PropTypes.object, } function mapStateToProps (state) { From 0481335dda447ba4c228d146834952bac0ad641b Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Mon, 18 Jun 2018 15:50:27 -0700 Subject: [PATCH 05/36] Improved rpc-engine usage --- app/scripts/controllers/preferences.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js index e33501cd0..f1bd66889 100644 --- a/app/scripts/controllers/preferences.js +++ b/app/scripts/controllers/preferences.js @@ -83,11 +83,7 @@ class PreferencesController { this.addSuggestedToken(tokenOpts) this.showAddTokenUi() - return end(null, { - result: rawAddress, - "jsonrpc": "2.0", - id: req.id, - }) + return end(rawAddress) } else { return next() } From 081884bd8095b2027e88fabdfe297f6d2fc8c38e Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Fri, 3 Aug 2018 16:42:13 -0400 Subject: [PATCH 06/36] rpc-engine not crashing when eth_watchToken --- app/scripts/controllers/preferences.js | 9 ++++----- ui/app/components/pages/home.js | 4 ---- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js index 8a4a63bb6..50f716852 100644 --- a/app/scripts/controllers/preferences.js +++ b/app/scripts/controllers/preferences.js @@ -57,7 +57,7 @@ class PreferencesController { addSuggestedToken (tokenOpts) { // TODO: Validate params const suggested = this.getSuggestedTokens() - suggested[tokenOpts.address] = suggested + suggested[tokenOpts.address] = tokenOpts this.store.updateState({ suggestedTokens: suggested }) } @@ -69,11 +69,10 @@ class PreferencesController { * @param {Function} - next * @param {Function} - end */ - requestAddToken(req, res, next, end) { + requestAddToken (req, res, next, end) { if (req.method === 'eth_watchToken') { // TODO: Validate params! const [ rawAddress, symbol, decimals ] = req.params - const tokenOpts = { address: rawAddress, decimals, @@ -82,8 +81,8 @@ class PreferencesController { this.addSuggestedToken(tokenOpts) this.showAddTokenUi() - - return end(rawAddress) + res.result = rawAddress + return end() } else { return next() } diff --git a/ui/app/components/pages/home.js b/ui/app/components/pages/home.js index cd4bf9033..97aaedd37 100644 --- a/ui/app/components/pages/home.js +++ b/ui/app/components/pages/home.js @@ -40,7 +40,6 @@ class Home extends Component { unapprovedTypedMessagesCount = 0, suggestedTokens = {}, unconfirmedTransactionsCount = 0, - } = this.props // suggested new tokens @@ -177,11 +176,8 @@ Home.propTypes = { isPopup: PropTypes.bool, isMouseUser: PropTypes.bool, t: PropTypes.func, -<<<<<<< HEAD suggestedTokens: PropTypes.object, -======= unconfirmedTransactionsCount: PropTypes.number, ->>>>>>> develop } function mapStateToProps (state) { From 12dd7a72323bf82d60e99502104e375864c0fbab Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Fri, 3 Aug 2018 18:01:40 -0400 Subject: [PATCH 07/36] popup initializing with suggested tokens --- ui/app/components/pages/home.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/ui/app/components/pages/home.js b/ui/app/components/pages/home.js index 97aaedd37..3b0a34f97 100644 --- a/ui/app/components/pages/home.js +++ b/ui/app/components/pages/home.js @@ -34,16 +34,12 @@ class Home extends Component { componentDidMount () { const { history, - unapprovedTxs = {}, - unapprovedMsgCount = 0, - unapprovedPersonalMsgCount = 0, - unapprovedTypedMessagesCount = 0, suggestedTokens = {}, unconfirmedTransactionsCount = 0, } = this.props // suggested new tokens - if (suggestedTokens.length > 0) { + if (Object.keys(suggestedTokens).length > 0) { history.push(CONFIRM_ADD_TOKEN_ROUTE) } @@ -238,7 +234,7 @@ function mapStateToProps (state) { isRevealingSeedWords: state.metamask.isRevealingSeedWords, Qr: state.appState.Qr, welcomeScreenSeen: state.metamask.welcomeScreenSeen, - + suggestedTokens: state.metamask.suggestedTokens, // state needed to get account dropdown temporarily rendering from app bar selected, unconfirmedTransactionsCount: unconfirmedTransactionsCountSelector(state), From 9ac9f53a73357238ed2ee0ce57c65de592cfd968 Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Fri, 3 Aug 2018 19:24:12 -0400 Subject: [PATCH 08/36] eth_watchToken working --- app/scripts/controllers/preferences.js | 7 ++++++ app/scripts/metamask-controller.js | 1 + ui/app/actions.js | 24 +++++++++++++++++++ .../confirm-add-token.component.js | 9 +++++-- .../confirm-add-token.container.js | 3 ++- ui/app/reducers/metamask.js | 1 + 6 files changed, 42 insertions(+), 3 deletions(-) diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js index 50f716852..521a68a66 100644 --- a/app/scripts/controllers/preferences.js +++ b/app/scripts/controllers/preferences.js @@ -211,6 +211,13 @@ class PreferencesController { return selected } + removeSuggestedTokens () { + return new Promise((resolve, reject) => { + this.store.updateState({ suggestedTokens: {} }) + resolve() + }) + } + /** * Setter for the `selectedAddress` property * diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index e843ec660..801363cb0 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -392,6 +392,7 @@ module.exports = class MetamaskController extends EventEmitter { setSelectedAddress: nodeify(preferencesController.setSelectedAddress, preferencesController), addToken: nodeify(preferencesController.addToken, preferencesController), removeToken: nodeify(preferencesController.removeToken, preferencesController), + removeSuggestedTokens: nodeify(preferencesController.removeSuggestedTokens, preferencesController), setCurrentAccountTab: nodeify(preferencesController.setCurrentAccountTab, preferencesController), setAccountLabel: nodeify(preferencesController.setAccountLabel, preferencesController), setFeatureFlag: nodeify(preferencesController.setFeatureFlag, preferencesController), diff --git a/ui/app/actions.js b/ui/app/actions.js index 7a8d9667d..c15dd05a5 100644 --- a/ui/app/actions.js +++ b/ui/app/actions.js @@ -226,6 +226,7 @@ var actions = { addTokens, removeToken, updateTokens, + removeSuggestedTokens, UPDATE_TOKENS: 'UPDATE_TOKENS', setRpcTarget: setRpcTarget, setProviderType: setProviderType, @@ -1612,6 +1613,29 @@ function addTokens (tokens) { } } +function removeSuggestedTokens () { + return (dispatch) => { + dispatch(actions.showLoadingIndication()) + return new Promise((resolve, reject) => { + background.removeSuggestedTokens((err) => { + dispatch(actions.hideLoadingIndication()) + if (err) { + dispatch(actions.displayWarning(err.message)) + reject(err) + } + dispatch(actions.clearPendingTokens()) + resolve() + }) + }) + } +} + +function clearPendingTokens () { + return { + type: actions.CLEAR_PENDING_TOKENS, + } +} + function updateTokens (newTokens) { return { type: actions.UPDATE_TOKENS, diff --git a/ui/app/components/pages/confirm-add-token/confirm-add-token.component.js b/ui/app/components/pages/confirm-add-token/confirm-add-token.component.js index 65d654b92..ad5264b30 100644 --- a/ui/app/components/pages/confirm-add-token/confirm-add-token.component.js +++ b/ui/app/components/pages/confirm-add-token/confirm-add-token.component.js @@ -15,6 +15,7 @@ export default class ConfirmAddToken extends Component { clearPendingTokens: PropTypes.func, addTokens: PropTypes.func, pendingTokens: PropTypes.object, + removeSuggestedTokens: PropTypes.func, } componentDidMount () { @@ -32,7 +33,7 @@ export default class ConfirmAddToken extends Component { } render () { - const { history, addTokens, clearPendingTokens, pendingTokens } = this.props + const { history, addTokens, clearPendingTokens, pendingTokens, removeSuggestedTokens } = this.props return (
@@ -90,7 +91,11 @@ export default class ConfirmAddToken extends Component { type="default" large className="page-container__footer-button" - onClick={() => history.push(ADD_TOKEN_ROUTE)} + onClick={() => { + // TODO find the right pace to removeSuggestedTokens + removeSuggestedTokens() + history.push(ADD_TOKEN_ROUTE) + }} > { this.context.t('back') } diff --git a/ui/app/components/pages/confirm-add-token/confirm-add-token.container.js b/ui/app/components/pages/confirm-add-token/confirm-add-token.container.js index 500b406bb..47d7a105b 100644 --- a/ui/app/components/pages/confirm-add-token/confirm-add-token.container.js +++ b/ui/app/components/pages/confirm-add-token/confirm-add-token.container.js @@ -3,7 +3,7 @@ import ConfirmAddToken from './confirm-add-token.component' const extend = require('xtend') -const { addTokens, clearPendingTokens } = require('../../../actions') +const { addTokens, clearPendingTokens, removeSuggestedTokens } = require('../../../actions') const mapStateToProps = ({ metamask }) => { const { pendingTokens, suggestedTokens } = metamask @@ -18,6 +18,7 @@ const mapDispatchToProps = dispatch => { return { addTokens: tokens => dispatch(addTokens(tokens)), clearPendingTokens: () => dispatch(clearPendingTokens()), + removeSuggestedTokens: () => dispatch(removeSuggestedTokens()), } } diff --git a/ui/app/reducers/metamask.js b/ui/app/reducers/metamask.js index 3f1d3394f..9e472bc6f 100644 --- a/ui/app/reducers/metamask.js +++ b/ui/app/reducers/metamask.js @@ -29,6 +29,7 @@ function reduceMetamask (state, action) { tokenExchangeRates: {}, tokens: [], pendingTokens: {}, + suggestedTokens: {}, send: { gasLimit: null, gasPrice: null, From 78ad3c38e2c9cfce8b0756c7d0df8264316d1d21 Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Mon, 6 Aug 2018 18:28:47 -0400 Subject: [PATCH 09/36] add suggested token params validation --- app/scripts/controllers/preferences.js | 29 ++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js index 521a68a66..3bbd48f06 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') @@ -55,9 +56,12 @@ class PreferencesController { } addSuggestedToken (tokenOpts) { - // TODO: Validate params + this._validateSuggestedTokenParams(tokenOpts) const suggested = this.getSuggestedTokens() - suggested[tokenOpts.address] = tokenOpts + const { rawAddress, symbol, decimals } = tokenOpts + const address = normalizeAddress(rawAddress) + const newEntry = { address, symbol, decimals } + suggested[address] = newEntry this.store.updateState({ suggestedTokens: suggested }) } @@ -71,10 +75,10 @@ class PreferencesController { */ requestAddToken (req, res, next, end) { if (req.method === 'eth_watchToken') { - // TODO: Validate params! const [ rawAddress, symbol, decimals ] = req.params + this._validateSuggestedTokenParams({ rawAddress, symbol, decimals }) const tokenOpts = { - address: rawAddress, + rawAddress, decimals, symbol, } @@ -423,6 +427,23 @@ 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 > 18 || numDecimals < 0) throw new Error(`Invalid decimals ${decimals}`) + if (!isValidAddress(rawAddress)) throw new Error(`Invalid address ${rawAddress}`) + } } module.exports = PreferencesController From 88933f3a6678a8e3a6d18083696506f99cf489f1 Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Mon, 6 Aug 2018 18:48:18 -0400 Subject: [PATCH 10/36] fix duplicated action --- ui/app/actions.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/ui/app/actions.js b/ui/app/actions.js index c15dd05a5..81e4d2d0c 100644 --- a/ui/app/actions.js +++ b/ui/app/actions.js @@ -2267,9 +2267,3 @@ function setPendingTokens (pendingTokens) { payload: tokens, } } - -function clearPendingTokens () { - return { - type: actions.CLEAR_PENDING_TOKENS, - } -} From af35b415ab0b1a48e77bb253c851afa070933c08 Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Mon, 6 Aug 2018 20:03:49 -0400 Subject: [PATCH 11/36] new confirm add suggested token component --- app/_locales/en/messages.json | 3 + ui/app/actions.js | 19 ++- ui/app/app.js | 3 + .../confirm-add-suggested-token.component.js | 118 ++++++++++++++++++ .../confirm-add-suggested-token.container.js | 25 ++++ .../confirm-add-suggested-token/index.js | 2 + ui/app/components/pages/home.js | 4 +- ui/app/routes.js | 2 + 8 files changed, 164 insertions(+), 12 deletions(-) create mode 100644 ui/app/components/pages/confirm-add-suggested-token/confirm-add-suggested-token.component.js create mode 100644 ui/app/components/pages/confirm-add-suggested-token/confirm-add-suggested-token.container.js create mode 100644 ui/app/components/pages/confirm-add-suggested-token/index.js diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 8d65bc596..6f315bb7a 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -26,6 +26,9 @@ "addTokens": { "message": "Add Tokens" }, + "addSuggestedTokens": { + "message": "Add Suggested Tokens" + }, "addAcquiredTokens": { "message": "Add the tokens you've acquired using MetaMask" }, diff --git a/ui/app/actions.js b/ui/app/actions.js index 81e4d2d0c..7b414a038 100644 --- a/ui/app/actions.js +++ b/ui/app/actions.js @@ -1616,16 +1616,15 @@ function addTokens (tokens) { function removeSuggestedTokens () { return (dispatch) => { dispatch(actions.showLoadingIndication()) - return new Promise((resolve, reject) => { - background.removeSuggestedTokens((err) => { - dispatch(actions.hideLoadingIndication()) - if (err) { - dispatch(actions.displayWarning(err.message)) - reject(err) - } - dispatch(actions.clearPendingTokens()) - resolve() - }) + background.removeSuggestedTokens((err) => { + dispatch(actions.hideLoadingIndication()) + if (err) { + dispatch(actions.displayWarning(err.message)) + } + dispatch(actions.clearPendingTokens()) + if (global.METAMASK_UI_TYPE === ENVIRONMENT_TYPE_NOTIFICATION) { + return global.platform.closeCurrentWindow() + } }) } } diff --git a/ui/app/app.js b/ui/app/app.js index dbb6146d1..83c063c3f 100644 --- a/ui/app/app.js +++ b/ui/app/app.js @@ -26,6 +26,7 @@ const RestoreVaultPage = require('./components/pages/keychains/restore-vault').d const RevealSeedConfirmation = require('./components/pages/keychains/reveal-seed') const AddTokenPage = require('./components/pages/add-token') const ConfirmAddTokenPage = require('./components/pages/confirm-add-token') +const ConfirmAddSuggestedTokenPage = require('./components/pages/confirm-add-suggested-token') const CreateAccountPage = require('./components/pages/create-account') const NoticeScreen = require('./components/pages/notice') @@ -52,6 +53,7 @@ const { RESTORE_VAULT_ROUTE, ADD_TOKEN_ROUTE, CONFIRM_ADD_TOKEN_ROUTE, + CONFIRM_ADD_SUGGESTED_TOKEN_ROUTE, NEW_ACCOUNT_ROUTE, SEND_ROUTE, CONFIRM_TRANSACTION_ROUTE, @@ -86,6 +88,7 @@ class App extends Component { h(Authenticated, { path: SEND_ROUTE, exact, component: SendTransactionScreen }), h(Authenticated, { path: ADD_TOKEN_ROUTE, exact, component: AddTokenPage }), h(Authenticated, { path: CONFIRM_ADD_TOKEN_ROUTE, exact, component: ConfirmAddTokenPage }), + h(Authenticated, { path: CONFIRM_ADD_SUGGESTED_TOKEN_ROUTE, exact, component: ConfirmAddSuggestedTokenPage }), h(Authenticated, { path: NEW_ACCOUNT_ROUTE, component: CreateAccountPage }), h(Authenticated, { path: DEFAULT_ROUTE, exact, component: Home }), ]) diff --git a/ui/app/components/pages/confirm-add-suggested-token/confirm-add-suggested-token.component.js b/ui/app/components/pages/confirm-add-suggested-token/confirm-add-suggested-token.component.js new file mode 100644 index 000000000..2220ae685 --- /dev/null +++ b/ui/app/components/pages/confirm-add-suggested-token/confirm-add-suggested-token.component.js @@ -0,0 +1,118 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import { DEFAULT_ROUTE } from '../../../routes' +import Button from '../../button' +import Identicon from '../../../components/identicon' +import TokenBalance from '../confirm-add-token/token-balance' + +export default class ConfirmAddSuggestedToken extends Component { + static contextTypes = { + t: PropTypes.func, + } + + static propTypes = { + history: PropTypes.object, + clearPendingTokens: PropTypes.func, + addTokens: PropTypes.func, + pendingTokens: PropTypes.object, + removeSuggestedTokens: PropTypes.func, + } + + componentDidMount () { + const { pendingTokens = {}, history } = this.props + + if (Object.keys(pendingTokens).length === 0) { + history.push(DEFAULT_ROUTE) + } + } + + getTokenName (name, symbol) { + return typeof name === 'undefined' + ? symbol + : `${name} (${symbol})` + } + + render () { + const { addTokens, clearPendingTokens, pendingTokens, removeSuggestedTokens } = this.props + + return ( +
+
+
+ { this.context.t('addSuggestedTokens') } +
+
+ { this.context.t('likeToAddTokens') } +
+
+
+
+
+
+ { this.context.t('token') } +
+
+ { this.context.t('balance') } +
+
+
+ { + Object.entries(pendingTokens) + .map(([ address, token ]) => { + const { name, symbol } = token + + return ( +
+
+ +
+ { this.getTokenName(name, symbol) } +
+
+
+ +
+
+ ) + }) + } +
+
+
+
+ + +
+
+ ) + } +} diff --git a/ui/app/components/pages/confirm-add-suggested-token/confirm-add-suggested-token.container.js b/ui/app/components/pages/confirm-add-suggested-token/confirm-add-suggested-token.container.js new file mode 100644 index 000000000..938c363b4 --- /dev/null +++ b/ui/app/components/pages/confirm-add-suggested-token/confirm-add-suggested-token.container.js @@ -0,0 +1,25 @@ +import { connect } from 'react-redux' +import ConfirmAddSuggestedToken from './confirm-add-suggested-token.component' + +const extend = require('xtend') + +const { addTokens, clearPendingTokens, removeSuggestedTokens } = require('../../../actions') + +const mapStateToProps = ({ metamask }) => { + const { pendingTokens, suggestedTokens } = metamask + const params = extend(pendingTokens, suggestedTokens) + + return { + pendingTokens: params, + } +} + +const mapDispatchToProps = dispatch => { + return { + addTokens: tokens => dispatch(addTokens(tokens)), + clearPendingTokens: () => dispatch(clearPendingTokens()), + removeSuggestedTokens: () => dispatch(removeSuggestedTokens()), + } +} + +export default connect(mapStateToProps, mapDispatchToProps)(ConfirmAddSuggestedToken) diff --git a/ui/app/components/pages/confirm-add-suggested-token/index.js b/ui/app/components/pages/confirm-add-suggested-token/index.js new file mode 100644 index 000000000..2ca56b43c --- /dev/null +++ b/ui/app/components/pages/confirm-add-suggested-token/index.js @@ -0,0 +1,2 @@ +import ConfirmAddSuggestedToken from './confirm-add-suggested-token.container' +module.exports = ConfirmAddSuggestedToken diff --git a/ui/app/components/pages/home.js b/ui/app/components/pages/home.js index 3b0a34f97..6ee083579 100644 --- a/ui/app/components/pages/home.js +++ b/ui/app/components/pages/home.js @@ -25,7 +25,7 @@ const { RESTORE_VAULT_ROUTE, CONFIRM_TRANSACTION_ROUTE, NOTICE_ROUTE, - CONFIRM_ADD_TOKEN_ROUTE, + CONFIRM_ADD_SUGGESTED_TOKEN_ROUTE, } = require('../../routes') const { unconfirmedTransactionsCountSelector } = require('../../selectors/confirm-transaction') @@ -40,7 +40,7 @@ class Home extends Component { // suggested new tokens if (Object.keys(suggestedTokens).length > 0) { - history.push(CONFIRM_ADD_TOKEN_ROUTE) + history.push(CONFIRM_ADD_SUGGESTED_TOKEN_ROUTE) } // unapprovedTxs and unapproved messages diff --git a/ui/app/routes.js b/ui/app/routes.js index f6b2a7a55..76afed5db 100644 --- a/ui/app/routes.js +++ b/ui/app/routes.js @@ -7,6 +7,7 @@ const CONFIRM_SEED_ROUTE = '/confirm-seed' const RESTORE_VAULT_ROUTE = '/restore-vault' const ADD_TOKEN_ROUTE = '/add-token' const CONFIRM_ADD_TOKEN_ROUTE = '/confirm-add-token' +const CONFIRM_ADD_SUGGESTED_TOKEN_ROUTE = '/confirm-add-suggested-token' const NEW_ACCOUNT_ROUTE = '/new-account' const IMPORT_ACCOUNT_ROUTE = '/new-account/import' const CONNECT_HARDWARE_ROUTE = '/new-account/connect' @@ -41,6 +42,7 @@ module.exports = { RESTORE_VAULT_ROUTE, ADD_TOKEN_ROUTE, CONFIRM_ADD_TOKEN_ROUTE, + CONFIRM_ADD_SUGGESTED_TOKEN_ROUTE, NEW_ACCOUNT_ROUTE, IMPORT_ACCOUNT_ROUTE, CONNECT_HARDWARE_ROUTE, From a57f56fdca3414fdee3b6d7fd8d7b6e8b3f919dc Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Mon, 6 Aug 2018 20:06:13 -0400 Subject: [PATCH 12/36] clean confirm add token component --- .../pages/confirm-add-token/confirm-add-token.component.js | 5 +---- .../pages/confirm-add-token/confirm-add-token.container.js | 3 +-- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/ui/app/components/pages/confirm-add-token/confirm-add-token.component.js b/ui/app/components/pages/confirm-add-token/confirm-add-token.component.js index ad5264b30..0f27ceb53 100644 --- a/ui/app/components/pages/confirm-add-token/confirm-add-token.component.js +++ b/ui/app/components/pages/confirm-add-token/confirm-add-token.component.js @@ -15,7 +15,6 @@ export default class ConfirmAddToken extends Component { clearPendingTokens: PropTypes.func, addTokens: PropTypes.func, pendingTokens: PropTypes.object, - removeSuggestedTokens: PropTypes.func, } componentDidMount () { @@ -33,7 +32,7 @@ export default class ConfirmAddToken extends Component { } render () { - const { history, addTokens, clearPendingTokens, pendingTokens, removeSuggestedTokens } = this.props + const { history, addTokens, clearPendingTokens, pendingTokens } = this.props return (
@@ -92,8 +91,6 @@ export default class ConfirmAddToken extends Component { large className="page-container__footer-button" onClick={() => { - // TODO find the right pace to removeSuggestedTokens - removeSuggestedTokens() history.push(ADD_TOKEN_ROUTE) }} > diff --git a/ui/app/components/pages/confirm-add-token/confirm-add-token.container.js b/ui/app/components/pages/confirm-add-token/confirm-add-token.container.js index 47d7a105b..500b406bb 100644 --- a/ui/app/components/pages/confirm-add-token/confirm-add-token.container.js +++ b/ui/app/components/pages/confirm-add-token/confirm-add-token.container.js @@ -3,7 +3,7 @@ import ConfirmAddToken from './confirm-add-token.component' const extend = require('xtend') -const { addTokens, clearPendingTokens, removeSuggestedTokens } = require('../../../actions') +const { addTokens, clearPendingTokens } = require('../../../actions') const mapStateToProps = ({ metamask }) => { const { pendingTokens, suggestedTokens } = metamask @@ -18,7 +18,6 @@ const mapDispatchToProps = dispatch => { return { addTokens: tokens => dispatch(addTokens(tokens)), clearPendingTokens: () => dispatch(clearPendingTokens()), - removeSuggestedTokens: () => dispatch(removeSuggestedTokens()), } } From 1f8a808a008fc4caaf7cb399d28bcbce3fa58e25 Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Tue, 7 Aug 2018 14:16:54 -0400 Subject: [PATCH 13/36] wip watch token old ui --- old-ui/app/account-detail.js | 1 + old-ui/app/add-suggested-token.js | 0 2 files changed, 1 insertion(+) create mode 100644 old-ui/app/add-suggested-token.js diff --git a/old-ui/app/account-detail.js b/old-ui/app/account-detail.js index c67f0cf71..808619e46 100644 --- a/old-ui/app/account-detail.js +++ b/old-ui/app/account-detail.js @@ -32,6 +32,7 @@ function mapStateToProps (state) { currentCurrency: state.metamask.currentCurrency, currentAccountTab: state.metamask.currentAccountTab, tokens: state.metamask.tokens, + suggestedTokens: state.metamask.suggestedTokens, computedBalances: state.metamask.computedBalances, } } diff --git a/old-ui/app/add-suggested-token.js b/old-ui/app/add-suggested-token.js new file mode 100644 index 000000000..e69de29bb From 00d1f6fec5457f05fb2e6aec1300dd2dbef51ec1 Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Tue, 7 Aug 2018 17:40:45 -0400 Subject: [PATCH 14/36] watch token on old ui --- old-ui/app/account-detail.js | 4 + old-ui/app/add-suggested-token.js | 193 ++++++++++++++++++++++++++++++ old-ui/app/app.js | 6 + ui/app/actions.js | 9 ++ ui/app/reducers/app.js | 9 ++ 5 files changed, 221 insertions(+) diff --git a/old-ui/app/account-detail.js b/old-ui/app/account-detail.js index 808619e46..d240fc38e 100644 --- a/old-ui/app/account-detail.js +++ b/old-ui/app/account-detail.js @@ -50,6 +50,10 @@ AccountDetailScreen.prototype.render = function () { var account = props.accounts[selected] const { network, conversionRate, currentCurrency } = props + if (Object.keys(props.suggestedTokens).length > 0) { + this.props.dispatch(actions.showAddSuggestedTokenPage()) + } + return ( h('.account-detail-section.full-flex-height', [ diff --git a/old-ui/app/add-suggested-token.js b/old-ui/app/add-suggested-token.js index e69de29bb..1a0fc0461 100644 --- a/old-ui/app/add-suggested-token.js +++ b/old-ui/app/add-suggested-token.js @@ -0,0 +1,193 @@ +const inherits = require('util').inherits +const Component = require('react').Component +const h = require('react-hyperscript') +const connect = require('react-redux').connect +const actions = require('../../ui/app/actions') +const Tooltip = require('./components/tooltip.js') +const ethUtil = require('ethereumjs-util') + +module.exports = connect(mapStateToProps)(AddSuggestedTokenScreen) + +function mapStateToProps (state) { + return { + identities: state.metamask.identities, + suggestedTokens: state.metamask.suggestedTokens, + } +} + +inherits(AddSuggestedTokenScreen, Component) +function AddSuggestedTokenScreen () { + this.state = { + warning: null, + } + Component.call(this) +} + +AddSuggestedTokenScreen.prototype.render = function () { + const state = this.state + const props = this.props + const { warning } = state + const key = Object.keys(props.suggestedTokens)[0] + const { address, symbol, decimals } = props.suggestedTokens[key] + + return ( + h('.flex-column.flex-grow', [ + + // subtitle and nav + h('.section-title.flex-row.flex-center', [ + h('h2.page-subtitle', 'Add Suggested Token'), + ]), + + h('.error', { + style: { + display: warning ? 'block' : 'none', + padding: '0 20px', + textAlign: 'center', + }, + }, warning), + + // conf view + h('.flex-column.flex-justify-center.flex-grow.select-none', [ + h('.flex-space-around', { + style: { + padding: '20px', + }, + }, [ + + h('div', [ + h(Tooltip, { + position: 'top', + title: 'The contract of the actual token contract. Click for more info.', + }, [ + h('a', { + style: { fontWeight: 'bold', paddingRight: '10px'}, + href: 'https://support.metamask.io/kb/article/24-what-is-a-token-contract-address', + target: '_blank', + }, [ + h('span', 'Token Contract Address '), + h('i.fa.fa-question-circle'), + ]), + ]), + ]), + + h('section.flex-row.flex-center', [ + h('p#token-address', { + name: 'address', + style: { + width: 'inherit', + flex: '1 0 auto', + height: '30px', + margin: '8px', + }, + }, address), + ]), + + h('div', [ + h('span', { + style: { fontWeight: 'bold', paddingRight: '10px'}, + }, 'Token Symbol'), + ]), + + h('div', { style: {display: 'flex'} }, [ + h('p#token_symbol', { + style: { + width: 'inherit', + flex: '1 0 auto', + height: '30px', + margin: '8px', + }, + }, symbol), + ]), + + h('div', [ + h('span', { + style: { fontWeight: 'bold', paddingRight: '10px'}, + }, 'Decimals of Precision'), + ]), + + h('div', { style: {display: 'flex'} }, [ + h('p#token_decimals', { + type: 'number', + style: { + width: 'inherit', + flex: '1 0 auto', + height: '30px', + margin: '8px', + }, + }, decimals), + ]), + + h('button', { + style: { + alignSelf: 'center', + margin: '8px', + }, + onClick: (event) => { + this.props.dispatch(actions.removeSuggestedTokens()) + }, + }, 'Cancel'), + + h('button', { + style: { + alignSelf: 'center', + margin: '8px', + }, + onClick: (event) => { + const valid = this.validateInputs({ address, symbol, decimals }) + if (!valid) return + + this.props.dispatch(actions.addToken(address.trim(), symbol.trim(), decimals)) + .then(() => { + this.props.dispatch(actions.removeSuggestedTokens()) + }) + }, + }, 'Add'), + ]), + ]), + ]) + ) +} + +AddSuggestedTokenScreen.prototype.componentWillMount = function () { + if (typeof global.ethereumProvider === 'undefined') return +} + +AddSuggestedTokenScreen.prototype.validateInputs = function (opts) { + let msg = '' + const identitiesList = Object.keys(this.props.identities) + const { address, symbol, decimals } = opts + const standardAddress = ethUtil.addHexPrefix(address).toLowerCase() + + const validAddress = ethUtil.isValidAddress(address) + if (!validAddress) { + msg += 'Address is invalid.' + } + + const validDecimals = decimals >= 0 && decimals < 36 + if (!validDecimals) { + msg += 'Decimals must be at least 0, and not over 36. ' + } + + const symbolLen = symbol.trim().length + const validSymbol = symbolLen > 0 && symbolLen < 10 + if (!validSymbol) { + msg += 'Symbol must be between 0 and 10 characters.' + } + + const ownAddress = identitiesList.includes(standardAddress) + if (ownAddress) { + msg = 'Personal address detected. Input the token contract address.' + } + + const isValid = validAddress && validDecimals && !ownAddress + + if (!isValid) { + this.setState({ + warning: msg, + }) + } else { + this.setState({ warning: null }) + } + + return isValid +} diff --git a/old-ui/app/app.js b/old-ui/app/app.js index 0637e3b5b..fb15d414d 100644 --- a/old-ui/app/app.js +++ b/old-ui/app/app.js @@ -22,6 +22,7 @@ const generateLostAccountsNotice = require('../lib/lost-accounts-notice') // other views const ConfigScreen = require('./config') const AddTokenScreen = require('./add-token') +const AddSuggestedTokenScreen = require('./add-suggested-token') const Import = require('./accounts/import') const InfoScreen = require('./info') const Loading = require('./components/loading') @@ -77,6 +78,7 @@ function mapStateToProps (state) { lostAccounts: state.metamask.lostAccounts, frequentRpcList: state.metamask.frequentRpcList || [], featureFlags, + suggestedTokens: state.metamask.suggestedTokens, // state needed to get account dropdown temporarily rendering from app bar identities, @@ -554,6 +556,10 @@ App.prototype.renderPrimary = function () { log.debug('rendering add-token screen from unlock screen.') return h(AddTokenScreen, {key: 'add-token'}) + case 'add-suggested-token': + log.debug('rendering add-token screen from unlock screen.') + return h(AddSuggestedTokenScreen, {key: 'add-suggested-token'}) + case 'config': log.debug('rendering config screen') return h(ConfigScreen, {key: 'config'}) diff --git a/ui/app/actions.js b/ui/app/actions.js index 7b414a038..04ad344cf 100644 --- a/ui/app/actions.js +++ b/ui/app/actions.js @@ -221,7 +221,9 @@ var actions = { SET_PROVIDER_TYPE: 'SET_PROVIDER_TYPE', showConfigPage, SHOW_ADD_TOKEN_PAGE: 'SHOW_ADD_TOKEN_PAGE', + SHOW_ADD_SUGGESTED_TOKEN_PAGE: 'SHOW_ADD_SUGGESTED_TOKEN_PAGE', showAddTokenPage, + showAddSuggestedTokenPage, addToken, addTokens, removeToken, @@ -1559,6 +1561,13 @@ function showAddTokenPage (transitionForward = true) { } } +function showAddSuggestedTokenPage (transitionForward = true) { + return { + type: actions.SHOW_ADD_SUGGESTED_TOKEN_PAGE, + value: transitionForward, + } +} + function addToken (address, symbol, decimals) { return (dispatch) => { dispatch(actions.showLoadingIndication()) diff --git a/ui/app/reducers/app.js b/ui/app/reducers/app.js index 50d8bcba7..f76b73132 100644 --- a/ui/app/reducers/app.js +++ b/ui/app/reducers/app.js @@ -196,6 +196,15 @@ function reduceApp (state, action) { transForward: action.value, }) + case actions.SHOW_ADD_SUGGESTED_TOKEN_PAGE: + return extend(appState, { + currentView: { + name: 'add-suggested-token', + context: appState.currentView.context, + }, + transForward: action.value, + }) + case actions.SHOW_IMPORT_PAGE: return extend(appState, { currentView: { From 15ea8c04b28a9f89999c96caf188d157e5230a55 Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Tue, 7 Aug 2018 17:53:36 -0400 Subject: [PATCH 15/36] fix merge --- app/scripts/controllers/preferences.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js index a42bb77b3..a6530424d 100644 --- a/app/scripts/controllers/preferences.js +++ b/app/scripts/controllers/preferences.js @@ -454,6 +454,7 @@ class PreferencesController { const numDecimals = parseInt(decimals, 10) if (isNaN(numDecimals) || numDecimals > 18 || numDecimals < 0) throw new Error(`Invalid decimals ${decimals}`) if (!isValidAddress(rawAddress)) throw new Error(`Invalid address ${rawAddress}`) + } /** * Subscription to network provider type. From 33357e3538b5157a852323d5f1e2db7f19b3303e Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Tue, 7 Aug 2018 19:12:16 -0400 Subject: [PATCH 16/36] refactor unused code --- app/scripts/controllers/preferences.js | 6 ++++-- old-ui/app/app.js | 2 +- ui/app/actions.js | 12 ++++++------ .../confirm-add-token/confirm-add-token.component.js | 4 +--- .../confirm-add-token/confirm-add-token.container.js | 8 ++------ ui/app/reducers/metamask.js | 1 - 6 files changed, 14 insertions(+), 19 deletions(-) diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js index a6530424d..4aa91534d 100644 --- a/app/scripts/controllers/preferences.js +++ b/app/scripts/controllers/preferences.js @@ -452,10 +452,12 @@ class PreferencesController { 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 > 18 || numDecimals < 0) throw new Error(`Invalid decimals ${decimals}`) + 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. * diff --git a/old-ui/app/app.js b/old-ui/app/app.js index f60ee5beb..9be21ebad 100644 --- a/old-ui/app/app.js +++ b/old-ui/app/app.js @@ -239,7 +239,7 @@ App.prototype.renderPrimary = function () { return h(AddTokenScreen, {key: 'add-token'}) case 'add-suggested-token': - log.debug('rendering add-token screen from unlock screen.') + log.debug('rendering add-suggested-token screen from unlock screen.') return h(AddSuggestedTokenScreen, {key: 'add-suggested-token'}) case 'config': diff --git a/ui/app/actions.js b/ui/app/actions.js index 5cc7dc2fa..d1168c168 100644 --- a/ui/app/actions.js +++ b/ui/app/actions.js @@ -1639,12 +1639,6 @@ function removeSuggestedTokens () { } } -function clearPendingTokens () { - return { - type: actions.CLEAR_PENDING_TOKENS, - } -} - function updateTokens (newTokens) { return { type: actions.UPDATE_TOKENS, @@ -1652,6 +1646,12 @@ function updateTokens (newTokens) { } } +function clearPendingTokens () { + return { + type: actions.CLEAR_PENDING_TOKENS, + } +} + function goBackToInitView () { return { type: actions.BACK_TO_INIT_MENU, diff --git a/ui/app/components/pages/confirm-add-token/confirm-add-token.component.js b/ui/app/components/pages/confirm-add-token/confirm-add-token.component.js index 0f27ceb53..65d654b92 100644 --- a/ui/app/components/pages/confirm-add-token/confirm-add-token.component.js +++ b/ui/app/components/pages/confirm-add-token/confirm-add-token.component.js @@ -90,9 +90,7 @@ export default class ConfirmAddToken extends Component { type="default" large className="page-container__footer-button" - onClick={() => { - history.push(ADD_TOKEN_ROUTE) - }} + onClick={() => history.push(ADD_TOKEN_ROUTE)} > { this.context.t('back') } diff --git a/ui/app/components/pages/confirm-add-token/confirm-add-token.container.js b/ui/app/components/pages/confirm-add-token/confirm-add-token.container.js index 500b406bb..0190024d9 100644 --- a/ui/app/components/pages/confirm-add-token/confirm-add-token.container.js +++ b/ui/app/components/pages/confirm-add-token/confirm-add-token.container.js @@ -1,16 +1,12 @@ import { connect } from 'react-redux' import ConfirmAddToken from './confirm-add-token.component' -const extend = require('xtend') - const { addTokens, clearPendingTokens } = require('../../../actions') const mapStateToProps = ({ metamask }) => { - const { pendingTokens, suggestedTokens } = metamask - const params = extend(pendingTokens, suggestedTokens) - + const { pendingTokens } = metamask return { - pendingTokens: params, + pendingTokens, } } diff --git a/ui/app/reducers/metamask.js b/ui/app/reducers/metamask.js index 9e472bc6f..3f1d3394f 100644 --- a/ui/app/reducers/metamask.js +++ b/ui/app/reducers/metamask.js @@ -29,7 +29,6 @@ function reduceMetamask (state, action) { tokenExchangeRates: {}, tokens: [], pendingTokens: {}, - suggestedTokens: {}, send: { gasLimit: null, gasPrice: null, From c245405acb2323239d079d574cbf4230ec068c35 Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Tue, 7 Aug 2018 20:07:26 -0400 Subject: [PATCH 17/36] show token address summary on old ui --- old-ui/app/add-suggested-token.js | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/old-ui/app/add-suggested-token.js b/old-ui/app/add-suggested-token.js index 1a0fc0461..51be4c5f2 100644 --- a/old-ui/app/add-suggested-token.js +++ b/old-ui/app/add-suggested-token.js @@ -5,6 +5,9 @@ const connect = require('react-redux').connect const actions = require('../../ui/app/actions') const Tooltip = require('./components/tooltip.js') const ethUtil = require('ethereumjs-util') +const Copyable = require('./components/copyable') +const addressSummary = require('./util').addressSummary + module.exports = connect(mapStateToProps)(AddSuggestedTokenScreen) @@ -70,16 +73,22 @@ AddSuggestedTokenScreen.prototype.render = function () { ]), ]), - h('section.flex-row.flex-center', [ - h('p#token-address', { - name: 'address', - style: { - width: 'inherit', - flex: '1 0 auto', - height: '30px', - margin: '8px', - }, - }, address), + h('div', { + style: { display: 'flex' }, + }, [ + h(Copyable, { + value: ethUtil.toChecksumAddress(address), + }, [ + h('span#token-address', { + style: { + width: 'inherit', + flex: '1 0 auto', + height: '30px', + margin: '8px', + display: 'flex', + }, + }, addressSummary(address, 24, 4, false)), + ]), ]), h('div', [ From 8f5b80a0fe13c53a602a5b2883ae1cdfba0123e1 Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Tue, 14 Aug 2018 13:58:47 -0300 Subject: [PATCH 18/36] update method to metamask_watchToken --- app/scripts/controllers/preferences.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js index 4aa91534d..4cc08a9af 100644 --- a/app/scripts/controllers/preferences.js +++ b/app/scripts/controllers/preferences.js @@ -77,7 +77,7 @@ class PreferencesController { * @param {Function} - end */ requestAddToken (req, res, next, end) { - if (req.method === 'eth_watchToken') { + if (req.method === 'metamask_watchToken') { const [ rawAddress, symbol, decimals ] = req.params this._validateSuggestedTokenParams({ rawAddress, symbol, decimals }) const tokenOpts = { From a4c3f6b65c9a25da0319b9077d830c23f729b32f Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Tue, 14 Aug 2018 20:08:12 -0300 Subject: [PATCH 19/36] add support for images base64 and urls on new ui --- app/scripts/controllers/preferences.js | 13 ++-- ui/app/actions.js | 4 +- ui/app/components/balance-component.js | 3 + ui/app/components/identicon.js | 65 ++++++++++--------- .../confirm-add-suggested-token.component.js | 13 ++-- .../confirm-add-suggested-token.container.js | 4 +- ui/app/components/token-cell.js | 3 +- ui/app/components/token-list.js | 13 +++- 8 files changed, 70 insertions(+), 48 deletions(-) diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js index 4cc08a9af..a92db15c7 100644 --- a/app/scripts/controllers/preferences.js +++ b/app/scripts/controllers/preferences.js @@ -61,9 +61,9 @@ class PreferencesController { addSuggestedToken (tokenOpts) { this._validateSuggestedTokenParams(tokenOpts) const suggested = this.getSuggestedTokens() - const { rawAddress, symbol, decimals } = tokenOpts + const { rawAddress, symbol, decimals, imageUrl } = tokenOpts const address = normalizeAddress(rawAddress) - const newEntry = { address, symbol, decimals } + const newEntry = { address, symbol, decimals, imageUrl } suggested[address] = newEntry this.store.updateState({ suggestedTokens: suggested }) } @@ -78,12 +78,13 @@ class PreferencesController { */ requestAddToken (req, res, next, end) { if (req.method === 'metamask_watchToken') { - const [ rawAddress, symbol, decimals ] = req.params + const [ rawAddress, symbol, decimals, imageUrl ] = req.params this._validateSuggestedTokenParams({ rawAddress, symbol, decimals }) const tokenOpts = { rawAddress, decimals, symbol, + imageUrl, } this.addSuggestedToken(tokenOpts) @@ -283,10 +284,9 @@ class PreferencesController { * @returns {Promise} 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 newEntry = { address, symbol, decimals, imageUrl } const tokens = this.store.getState().tokens const previousEntry = tokens.find((token, index) => { return token.address === address @@ -299,6 +299,7 @@ class PreferencesController { tokens.push(newEntry) } this._updateAccountTokens(tokens) + return Promise.resolve(tokens) } diff --git a/ui/app/actions.js b/ui/app/actions.js index d1168c168..4e3c87756 100644 --- a/ui/app/actions.js +++ b/ui/app/actions.js @@ -1569,11 +1569,11 @@ function showAddSuggestedTokenPage (transitionForward = true) { } } -function addToken (address, symbol, decimals) { +function addToken (address, symbol, decimals, imageUrl) { return (dispatch) => { dispatch(actions.showLoadingIndication()) return new Promise((resolve, reject) => { - background.addToken(address, symbol, decimals, (err, tokens) => { + background.addToken(address, symbol, decimals, imageUrl, (err, tokens) => { dispatch(actions.hideLoadingIndication()) if (err) { dispatch(actions.displayWarning(err.message)) diff --git a/ui/app/components/balance-component.js b/ui/app/components/balance-component.js index e31552f2d..5e2919fca 100644 --- a/ui/app/components/balance-component.js +++ b/ui/app/components/balance-component.js @@ -33,6 +33,8 @@ function BalanceComponent () { BalanceComponent.prototype.render = function () { const props = this.props const { token, network } = props + let imageUrl + if (token) imageUrl = token.imageUrl return h('div.balance-container', {}, [ @@ -45,6 +47,7 @@ BalanceComponent.prototype.render = function () { diameter: 50, address: token && token.address, network, + imageUrl, }), token ? this.renderTokenBalance() : this.renderBalance(), diff --git a/ui/app/components/identicon.js b/ui/app/components/identicon.js index 424048745..eadd6d5a2 100644 --- a/ui/app/components/identicon.js +++ b/ui/app/components/identicon.js @@ -26,36 +26,43 @@ function mapStateToProps (state) { IdenticonComponent.prototype.render = function () { var props = this.props - const { className = '', address } = props + const { className = '', address, imageUrl } = props var diameter = props.diameter || this.defaultDiameter - - return address - ? ( - h('div', { - className: `${className} identicon`, - key: 'identicon-' + address, - style: { - display: 'flex', - flexShrink: 0, - alignItems: 'center', - justifyContent: 'center', - height: diameter, - width: diameter, - borderRadius: diameter / 2, - overflow: 'hidden', - }, - }) - ) - : ( - h('img.balance-icon', { - src: './images/eth_logo.svg', - style: { - height: diameter, - width: diameter, - borderRadius: diameter / 2, - }, - }) - ) + // for tokens added with `watchToken` we need to render the given image + if (imageUrl) { + return h('img.balance-icon', { + src: imageUrl, + style: { + height: diameter, + width: diameter, + borderRadius: diameter / 2, + }, + }) + } else if (address) { + return h('div', { + className: `${className} identicon`, + key: 'identicon-' + address, + style: { + display: 'flex', + flexShrink: 0, + alignItems: 'center', + justifyContent: 'center', + height: diameter, + width: diameter, + borderRadius: diameter / 2, + overflow: 'hidden', + }, + }) + } else { + return h('img.balance-icon', { + src: './images/eth_logo.svg', + style: { + height: diameter, + width: diameter, + borderRadius: diameter / 2, + }, + }) + } } IdenticonComponent.prototype.componentDidMount = function () { diff --git a/ui/app/components/pages/confirm-add-suggested-token/confirm-add-suggested-token.component.js b/ui/app/components/pages/confirm-add-suggested-token/confirm-add-suggested-token.component.js index 2220ae685..5de0859cd 100644 --- a/ui/app/components/pages/confirm-add-suggested-token/confirm-add-suggested-token.component.js +++ b/ui/app/components/pages/confirm-add-suggested-token/confirm-add-suggested-token.component.js @@ -13,7 +13,7 @@ export default class ConfirmAddSuggestedToken extends Component { static propTypes = { history: PropTypes.object, clearPendingTokens: PropTypes.func, - addTokens: PropTypes.func, + addToken: PropTypes.func, pendingTokens: PropTypes.object, removeSuggestedTokens: PropTypes.func, } @@ -33,7 +33,9 @@ export default class ConfirmAddSuggestedToken extends Component { } render () { - const { addTokens, clearPendingTokens, pendingTokens, removeSuggestedTokens } = this.props + const { addToken, clearPendingTokens, pendingTokens, removeSuggestedTokens } = this.props + const pendingTokenKey = Object.keys(pendingTokens)[0] + const pendingToken = pendingTokens[pendingTokenKey] return (
@@ -59,7 +61,7 @@ export default class ConfirmAddSuggestedToken extends Component { { Object.entries(pendingTokens) .map(([ address, token ]) => { - const { name, symbol } = token + const { name, symbol, imageUrl } = token return (
{ this.getTokenName(name, symbol) } @@ -102,14 +105,14 @@ export default class ConfirmAddSuggestedToken extends Component { large className="page-container__footer-button" onClick={() => { - addTokens(pendingTokens) + addToken(pendingToken) .then(() => { clearPendingTokens() removeSuggestedTokens() }) }} > - { this.context.t('addTokens') } + { this.context.t('addToken') }
diff --git a/ui/app/components/pages/confirm-add-suggested-token/confirm-add-suggested-token.container.js b/ui/app/components/pages/confirm-add-suggested-token/confirm-add-suggested-token.container.js index 938c363b4..fba33222e 100644 --- a/ui/app/components/pages/confirm-add-suggested-token/confirm-add-suggested-token.container.js +++ b/ui/app/components/pages/confirm-add-suggested-token/confirm-add-suggested-token.container.js @@ -3,7 +3,7 @@ import ConfirmAddSuggestedToken from './confirm-add-suggested-token.component' const extend = require('xtend') -const { addTokens, clearPendingTokens, removeSuggestedTokens } = require('../../../actions') +const { addToken, clearPendingTokens, removeSuggestedTokens } = require('../../../actions') const mapStateToProps = ({ metamask }) => { const { pendingTokens, suggestedTokens } = metamask @@ -16,7 +16,7 @@ const mapStateToProps = ({ metamask }) => { const mapDispatchToProps = dispatch => { return { - addTokens: tokens => dispatch(addTokens(tokens)), + addToken: ({address, symbol, decimals, imageUrl}) => dispatch(addToken(address, symbol, decimals, imageUrl)), clearPendingTokens: () => dispatch(clearPendingTokens()), removeSuggestedTokens: () => dispatch(removeSuggestedTokens()), } diff --git a/ui/app/components/token-cell.js b/ui/app/components/token-cell.js index 4100d76a5..a84d8eda0 100644 --- a/ui/app/components/token-cell.js +++ b/ui/app/components/token-cell.js @@ -56,8 +56,8 @@ TokenCell.prototype.render = function () { sidebarOpen, currentCurrency, // userAddress, + imageUrl, } = props - let currentTokenToFiatRate let currentTokenInFiat let formattedFiat = '' @@ -97,6 +97,7 @@ TokenCell.prototype.render = function () { diameter: 50, address, network, + imageUrl, }), h('div.token-list-item__balance-ellipsis', null, [ diff --git a/ui/app/components/token-list.js b/ui/app/components/token-list.js index 42351cf89..a59c2e4e0 100644 --- a/ui/app/components/token-list.js +++ b/ui/app/components/token-list.js @@ -9,10 +9,15 @@ const selectors = require('../selectors') const log = require('loglevel') function mapStateToProps (state) { + // In order to get `imageUrl` from token added with `eth_watchToken` + // TODO do this with cache memory for browsers, add support for image object, var names + const tokenImagesHashes = {} + state.metamask.tokens.forEach((token) => { tokenImagesHashes[token.address] = token.imageUrl }) return { network: state.metamask.network, tokens: state.metamask.tokens, userAddress: selectors.getSelectedAddress(state), + tokenImagesHashes: tokenImagesHashes, } } @@ -44,10 +49,9 @@ function TokenList () { } TokenList.prototype.render = function () { - const { userAddress } = this.props + const { userAddress, tokenImagesHashes } = this.props const state = this.state const { tokens, isLoading, error } = state - if (isLoading) { return this.message(this.context.t('loadingTokens')) } @@ -74,7 +78,10 @@ TokenList.prototype.render = function () { ]) } - return h('div', tokens.map((tokenData) => h(TokenCell, tokenData))) + return h('div', tokens.map((tokenData) => { + tokenData.imageUrl = tokenImagesHashes[tokenData.address] + return h(TokenCell, tokenData) + })) } From a4b6b2357a2eee7a4286a8490b8d31aac487120d Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Tue, 14 Aug 2018 20:09:56 -0300 Subject: [PATCH 20/36] watchToken to watchAsset --- app/scripts/controllers/preferences.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js index a92db15c7..04c9a3254 100644 --- a/app/scripts/controllers/preferences.js +++ b/app/scripts/controllers/preferences.js @@ -77,7 +77,7 @@ class PreferencesController { * @param {Function} - end */ requestAddToken (req, res, next, end) { - if (req.method === 'metamask_watchToken') { + if (req.method === 'metamask_watchAsset') { const [ rawAddress, symbol, decimals, imageUrl ] = req.params this._validateSuggestedTokenParams({ rawAddress, symbol, decimals }) const tokenOpts = { From b766104c8d8fc4d4b1c5660af54b791243836f30 Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Wed, 15 Aug 2018 18:34:57 -0300 Subject: [PATCH 21/36] add suggested tokens objects in metamask state --- app/scripts/controllers/preferences.js | 22 ++++++++++++++++------ ui/app/components/balance-component.js | 5 +++-- ui/app/components/token-list.js | 4 +--- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js index 04c9a3254..bda521bdd 100644 --- a/app/scripts/controllers/preferences.js +++ b/app/scripts/controllers/preferences.js @@ -15,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.objects Contains assets objects related to * @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 @@ -28,6 +29,7 @@ class PreferencesController { currentAccountTab: 'history', accountTokens: {}, tokens: [], + objects: {}, suggestedTokens: {}, useBlockie: false, featureFlags: {}, @@ -58,6 +60,10 @@ class PreferencesController { return this.store.getState().suggestedTokens } + getObjects () { + return this.store.getState().objects + } + addSuggestedToken (tokenOpts) { this._validateSuggestedTokenParams(tokenOpts) const suggested = this.getSuggestedTokens() @@ -286,8 +292,9 @@ class PreferencesController { */ async addToken (rawAddress, symbol, decimals, imageUrl) { const address = normalizeAddress(rawAddress) - const newEntry = { address, symbol, decimals, imageUrl } + const newEntry = { address, symbol, decimals } const tokens = this.store.getState().tokens + const objects = this.getObjects() const previousEntry = tokens.find((token, index) => { return token.address === address }) @@ -298,8 +305,9 @@ class PreferencesController { } else { tokens.push(newEntry) } - this._updateAccountTokens(tokens) - + objects[address] = imageUrl + this._updateAccountTokens(tokens, objects) + console.log('OBJECTS OBJET', this.getObjects()) return Promise.resolve(tokens) } @@ -312,8 +320,10 @@ class PreferencesController { */ removeToken (rawAddress) { const tokens = this.store.getState().tokens + const objects = this.getObjects() const updatedTokens = tokens.filter(token => token.address !== rawAddress) - this._updateAccountTokens(updatedTokens) + const updatedObjects = Object.keys(objects).filter(key => key !== rawAddress) + this._updateAccountTokens(updatedTokens, updatedObjects) return Promise.resolve(updatedTokens) } @@ -477,10 +487,10 @@ class PreferencesController { * @param {array} tokens Array of tokens to be updated. * */ - _updateAccountTokens (tokens) { + _updateAccountTokens (tokens, objects) { const { accountTokens, providerType, selectedAddress } = this._getTokenRelatedStates() accountTokens[selectedAddress][providerType] = tokens - this.store.updateState({ accountTokens, tokens }) + this.store.updateState({ accountTokens, tokens, objects }) } /** diff --git a/ui/app/components/balance-component.js b/ui/app/components/balance-component.js index 5e2919fca..1a55a1f5b 100644 --- a/ui/app/components/balance-component.js +++ b/ui/app/components/balance-component.js @@ -22,6 +22,7 @@ function mapStateToProps (state) { network, conversionRate: state.metamask.conversionRate, currentCurrency: state.metamask.currentCurrency, + objects: state.metamask.objects, } } @@ -32,9 +33,9 @@ function BalanceComponent () { BalanceComponent.prototype.render = function () { const props = this.props - const { token, network } = props + const { token, network, objects } = props let imageUrl - if (token) imageUrl = token.imageUrl + if (token) imageUrl = objects[token.address] return h('div.balance-container', {}, [ diff --git a/ui/app/components/token-list.js b/ui/app/components/token-list.js index a59c2e4e0..0f88a7703 100644 --- a/ui/app/components/token-list.js +++ b/ui/app/components/token-list.js @@ -11,13 +11,11 @@ const log = require('loglevel') function mapStateToProps (state) { // In order to get `imageUrl` from token added with `eth_watchToken` // TODO do this with cache memory for browsers, add support for image object, var names - const tokenImagesHashes = {} - state.metamask.tokens.forEach((token) => { tokenImagesHashes[token.address] = token.imageUrl }) return { network: state.metamask.network, tokens: state.metamask.tokens, userAddress: selectors.getSelectedAddress(state), - tokenImagesHashes: tokenImagesHashes, + tokenImagesHashes: state.metamask.objects, } } From 5289a36664f180fae1dc6da07ccc80d307f7408c Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Wed, 15 Aug 2018 20:01:59 -0300 Subject: [PATCH 22/36] change watchAsset to new spec for type ERC20 --- app/scripts/controllers/preferences.js | 42 +++++++++++++++++--------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js index bda521bdd..9f5826dd9 100644 --- a/app/scripts/controllers/preferences.js +++ b/app/scripts/controllers/preferences.js @@ -75,7 +75,7 @@ class PreferencesController { } /** - * RPC engine middleware for requesting new token added + * RPC engine middleware for requesting new asset added * * @param req * @param res @@ -84,21 +84,18 @@ class PreferencesController { */ requestAddToken (req, res, next, end) { if (req.method === 'metamask_watchAsset') { - const [ rawAddress, symbol, decimals, imageUrl ] = req.params - this._validateSuggestedTokenParams({ rawAddress, symbol, decimals }) - const tokenOpts = { - rawAddress, - decimals, - symbol, - imageUrl, + const { type, options } = req.params + switch (type) { + case 'ERC20': + this._handleWatchAssetERC20(options, res) + res.result = options.address + break + default: + // TODO return promise for not handled assets } - - this.addSuggestedToken(tokenOpts) - this.showAddTokenUi() - res.result = rawAddress - return end() + end() } else { - return next() + next() } } @@ -307,7 +304,6 @@ class PreferencesController { } objects[address] = imageUrl this._updateAccountTokens(tokens, objects) - console.log('OBJECTS OBJET', this.getObjects()) return Promise.resolve(tokens) } @@ -520,6 +516,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 From a36ea0e2328e6ffedd5b526470dc1133c4f2f556 Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Thu, 16 Aug 2018 12:04:43 -0300 Subject: [PATCH 23/36] show watch asset image from hide token modal --- app/scripts/controllers/preferences.js | 2 +- ui/app/components/identicon.js | 21 +++++++++---------- .../modals/hide-token-confirmation-modal.js | 5 ++++- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js index 9f5826dd9..59c24f987 100644 --- a/app/scripts/controllers/preferences.js +++ b/app/scripts/controllers/preferences.js @@ -15,7 +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.objects Contains assets objects related to + * @property {object} store.objects 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 diff --git a/ui/app/components/identicon.js b/ui/app/components/identicon.js index eadd6d5a2..6b632352f 100644 --- a/ui/app/components/identicon.js +++ b/ui/app/components/identicon.js @@ -28,14 +28,17 @@ IdenticonComponent.prototype.render = function () { var props = this.props const { className = '', address, imageUrl } = props var diameter = props.diameter || this.defaultDiameter - // for tokens added with `watchToken` we need to render the given image + const style = { + height: diameter, + width: diameter, + borderRadius: diameter / 2, + } if (imageUrl) { - return h('img.balance-icon', { + return h('img', { + className: `${className} identicon`, src: imageUrl, style: { - height: diameter, - width: diameter, - borderRadius: diameter / 2, + ...style, }, }) } else if (address) { @@ -47,9 +50,7 @@ IdenticonComponent.prototype.render = function () { flexShrink: 0, alignItems: 'center', justifyContent: 'center', - height: diameter, - width: diameter, - borderRadius: diameter / 2, + ...style, overflow: 'hidden', }, }) @@ -57,9 +58,7 @@ IdenticonComponent.prototype.render = function () { return h('img.balance-icon', { src: './images/eth_logo.svg', style: { - height: diameter, - width: diameter, - borderRadius: diameter / 2, + ...style, }, }) } diff --git a/ui/app/components/modals/hide-token-confirmation-modal.js b/ui/app/components/modals/hide-token-confirmation-modal.js index 1518fa9a0..b5f396e6f 100644 --- a/ui/app/components/modals/hide-token-confirmation-modal.js +++ b/ui/app/components/modals/hide-token-confirmation-modal.js @@ -10,6 +10,7 @@ function mapStateToProps (state) { return { network: state.metamask.network, token: state.appState.modal.modalState.props.token, + tokenImagesHashes: state.metamask.objects, } } @@ -40,8 +41,9 @@ module.exports = connect(mapStateToProps, mapDispatchToProps)(HideTokenConfirmat HideTokenConfirmationModal.prototype.render = function () { - const { token, network, hideToken, hideModal } = this.props + const { token, network, hideToken, hideModal, tokenImagesHashes } = this.props const { symbol, address } = token + const imageUrl = tokenImagesHashes[address] return h('div.hide-token-confirmation', {}, [ h('div.hide-token-confirmation__container', { @@ -55,6 +57,7 @@ HideTokenConfirmationModal.prototype.render = function () { diameter: 45, address, network, + imageUrl, }), h('div.hide-token-confirmation__symbol', {}, symbol), From 2ace30bcd1d8280b440a4ce2a7156f94cf4f78f2 Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Thu, 16 Aug 2018 18:28:07 -0300 Subject: [PATCH 24/36] WIP --- .../confirm-add-suggested-token.component.js | 5 +++-- .../confirm-add-suggested-token.container.js | 7 ++++++- ui/app/components/pages/home.js | 1 + 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/ui/app/components/pages/confirm-add-suggested-token/confirm-add-suggested-token.component.js b/ui/app/components/pages/confirm-add-suggested-token/confirm-add-suggested-token.component.js index 5de0859cd..e8155b5f0 100644 --- a/ui/app/components/pages/confirm-add-suggested-token/confirm-add-suggested-token.component.js +++ b/ui/app/components/pages/confirm-add-suggested-token/confirm-add-suggested-token.component.js @@ -33,7 +33,7 @@ export default class ConfirmAddSuggestedToken extends Component { } render () { - const { addToken, clearPendingTokens, pendingTokens, removeSuggestedTokens } = this.props + const { addToken, clearPendingTokens, pendingTokens, removeSuggestedTokens, history } = this.props const pendingTokenKey = Object.keys(pendingTokens)[0] const pendingToken = pendingTokens[pendingTokenKey] @@ -96,6 +96,7 @@ export default class ConfirmAddSuggestedToken extends Component { className="page-container__footer-button" onClick={() => { removeSuggestedTokens() + history.push(DEFAULT_ROUTE) }} > { this.context.t('cancel') } @@ -107,8 +108,8 @@ export default class ConfirmAddSuggestedToken extends Component { onClick={() => { addToken(pendingToken) .then(() => { - clearPendingTokens() removeSuggestedTokens() + history.push(DEFAULT_ROUTE) }) }} > diff --git a/ui/app/components/pages/confirm-add-suggested-token/confirm-add-suggested-token.container.js b/ui/app/components/pages/confirm-add-suggested-token/confirm-add-suggested-token.container.js index fba33222e..97e07afda 100644 --- a/ui/app/components/pages/confirm-add-suggested-token/confirm-add-suggested-token.container.js +++ b/ui/app/components/pages/confirm-add-suggested-token/confirm-add-suggested-token.container.js @@ -1,5 +1,7 @@ import { connect } from 'react-redux' +import { compose } from 'recompose' import ConfirmAddSuggestedToken from './confirm-add-suggested-token.component' +import { withRouter } from 'react-router-dom' const extend = require('xtend') @@ -22,4 +24,7 @@ const mapDispatchToProps = dispatch => { } } -export default connect(mapStateToProps, mapDispatchToProps)(ConfirmAddSuggestedToken) +export default compose( + withRouter, + connect(mapStateToProps, mapDispatchToProps) +)(ConfirmAddSuggestedToken) diff --git a/ui/app/components/pages/home.js b/ui/app/components/pages/home.js index 6ee083579..629dc11fc 100644 --- a/ui/app/components/pages/home.js +++ b/ui/app/components/pages/home.js @@ -40,6 +40,7 @@ class Home extends Component { // suggested new tokens if (Object.keys(suggestedTokens).length > 0) { + console.log('CALLING CONFIRM ADD SUGGESTED') history.push(CONFIRM_ADD_SUGGESTED_TOKEN_ROUTE) } From bb868f58348962d4a85415380d11f72892a2e28c Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Thu, 16 Aug 2018 20:19:19 -0300 Subject: [PATCH 25/36] correct behavior when notification is closed when popup --- app/scripts/controllers/preferences.js | 2 +- ui/app/actions.js | 23 +++++++++++-------- .../confirm-add-suggested-token.component.js | 12 ++++++---- .../confirm-add-suggested-token.container.js | 3 +-- ui/app/components/pages/home.js | 1 - 5 files changed, 24 insertions(+), 17 deletions(-) diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js index 59c24f987..1438d6f7f 100644 --- a/app/scripts/controllers/preferences.js +++ b/app/scripts/controllers/preferences.js @@ -237,7 +237,7 @@ class PreferencesController { removeSuggestedTokens () { return new Promise((resolve, reject) => { this.store.updateState({ suggestedTokens: {} }) - resolve() + resolve({}) }) } diff --git a/ui/app/actions.js b/ui/app/actions.js index 4e3c87756..0760377c2 100644 --- a/ui/app/actions.js +++ b/ui/app/actions.js @@ -1626,16 +1626,21 @@ function addTokens (tokens) { function removeSuggestedTokens () { return (dispatch) => { dispatch(actions.showLoadingIndication()) - background.removeSuggestedTokens((err) => { - dispatch(actions.hideLoadingIndication()) - if (err) { - dispatch(actions.displayWarning(err.message)) - } - dispatch(actions.clearPendingTokens()) - if (global.METAMASK_UI_TYPE === ENVIRONMENT_TYPE_NOTIFICATION) { - return global.platform.closeCurrentWindow() - } + return new Promise((resolve, reject) => { + background.removeSuggestedTokens((err, suggestedTokens) => { + dispatch(actions.hideLoadingIndication()) + if (err) { + dispatch(actions.displayWarning(err.message)) + } + dispatch(actions.clearPendingTokens()) + if (global.METAMASK_UI_TYPE === ENVIRONMENT_TYPE_NOTIFICATION) { + return global.platform.closeCurrentWindow() + } + resolve(suggestedTokens) + }) }) + .then(() => updateMetamaskStateFromBackground()) + .then(suggestedTokens => dispatch(actions.updateMetamaskState({...suggestedTokens}))) } } diff --git a/ui/app/components/pages/confirm-add-suggested-token/confirm-add-suggested-token.component.js b/ui/app/components/pages/confirm-add-suggested-token/confirm-add-suggested-token.component.js index e8155b5f0..37d9aca02 100644 --- a/ui/app/components/pages/confirm-add-suggested-token/confirm-add-suggested-token.component.js +++ b/ui/app/components/pages/confirm-add-suggested-token/confirm-add-suggested-token.component.js @@ -33,7 +33,7 @@ export default class ConfirmAddSuggestedToken extends Component { } render () { - const { addToken, clearPendingTokens, pendingTokens, removeSuggestedTokens, history } = this.props + const { addToken, pendingTokens, removeSuggestedTokens, history } = this.props const pendingTokenKey = Object.keys(pendingTokens)[0] const pendingToken = pendingTokens[pendingTokenKey] @@ -96,8 +96,10 @@ export default class ConfirmAddSuggestedToken extends Component { className="page-container__footer-button" onClick={() => { removeSuggestedTokens() - history.push(DEFAULT_ROUTE) - }} + .then(() => { + history.push(DEFAULT_ROUTE) + }) + }} > { this.context.t('cancel') } @@ -109,7 +111,9 @@ export default class ConfirmAddSuggestedToken extends Component { addToken(pendingToken) .then(() => { removeSuggestedTokens() - history.push(DEFAULT_ROUTE) + .then(() => { + history.push(DEFAULT_ROUTE) + }) }) }} > diff --git a/ui/app/components/pages/confirm-add-suggested-token/confirm-add-suggested-token.container.js b/ui/app/components/pages/confirm-add-suggested-token/confirm-add-suggested-token.container.js index 97e07afda..89291ff4f 100644 --- a/ui/app/components/pages/confirm-add-suggested-token/confirm-add-suggested-token.container.js +++ b/ui/app/components/pages/confirm-add-suggested-token/confirm-add-suggested-token.container.js @@ -5,7 +5,7 @@ import { withRouter } from 'react-router-dom' const extend = require('xtend') -const { addToken, clearPendingTokens, removeSuggestedTokens } = require('../../../actions') +const { addToken, removeSuggestedTokens } = require('../../../actions') const mapStateToProps = ({ metamask }) => { const { pendingTokens, suggestedTokens } = metamask @@ -19,7 +19,6 @@ const mapStateToProps = ({ metamask }) => { const mapDispatchToProps = dispatch => { return { addToken: ({address, symbol, decimals, imageUrl}) => dispatch(addToken(address, symbol, decimals, imageUrl)), - clearPendingTokens: () => dispatch(clearPendingTokens()), removeSuggestedTokens: () => dispatch(removeSuggestedTokens()), } } diff --git a/ui/app/components/pages/home.js b/ui/app/components/pages/home.js index 629dc11fc..6ee083579 100644 --- a/ui/app/components/pages/home.js +++ b/ui/app/components/pages/home.js @@ -40,7 +40,6 @@ class Home extends Component { // suggested new tokens if (Object.keys(suggestedTokens).length > 0) { - console.log('CALLING CONFIRM ADD SUGGESTED') history.push(CONFIRM_ADD_SUGGESTED_TOKEN_ROUTE) } From dbab9a007fc9663427cebdbe1d41c51df67fd1fe Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Thu, 16 Aug 2018 21:17:02 -0300 Subject: [PATCH 26/36] delete according image when token added with watchToken deleted --- app/scripts/controllers/preferences.js | 27 ++++++++++--------- ui/app/components/balance-component.js | 6 ++--- .../modals/hide-token-confirmation-modal.js | 6 ++--- ui/app/components/token-list.js | 6 ++--- 4 files changed, 23 insertions(+), 22 deletions(-) diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js index 1438d6f7f..611d2d067 100644 --- a/app/scripts/controllers/preferences.js +++ b/app/scripts/controllers/preferences.js @@ -15,7 +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.objects Contains assets objects related to assets added + * @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 @@ -28,8 +28,8 @@ class PreferencesController { frequentRpcList: [], currentAccountTab: 'history', accountTokens: {}, + imageObjects: {}, tokens: [], - objects: {}, suggestedTokens: {}, useBlockie: false, featureFlags: {}, @@ -60,8 +60,8 @@ class PreferencesController { return this.store.getState().suggestedTokens } - getObjects () { - return this.store.getState().objects + getImageObjects () { + return this.store.getState().imageObjects } addSuggestedToken (tokenOpts) { @@ -89,11 +89,12 @@ class PreferencesController { 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`)) } - end() } else { next() } @@ -291,7 +292,7 @@ class PreferencesController { const address = normalizeAddress(rawAddress) const newEntry = { address, symbol, decimals } const tokens = this.store.getState().tokens - const objects = this.getObjects() + const imageObjects = this.getImageObjects() const previousEntry = tokens.find((token, index) => { return token.address === address }) @@ -302,8 +303,8 @@ class PreferencesController { } else { tokens.push(newEntry) } - objects[address] = imageUrl - this._updateAccountTokens(tokens, objects) + imageObjects[address] = imageUrl + this._updateAccountTokens(tokens, imageObjects) return Promise.resolve(tokens) } @@ -316,10 +317,10 @@ class PreferencesController { */ removeToken (rawAddress) { const tokens = this.store.getState().tokens - const objects = this.getObjects() + const imageObjects = this.getImageObjects() const updatedTokens = tokens.filter(token => token.address !== rawAddress) - const updatedObjects = Object.keys(objects).filter(key => key !== rawAddress) - this._updateAccountTokens(updatedTokens, updatedObjects) + delete imageObjects[rawAddress] + this._updateAccountTokens(updatedTokens, imageObjects) return Promise.resolve(updatedTokens) } @@ -483,10 +484,10 @@ class PreferencesController { * @param {array} tokens Array of tokens to be updated. * */ - _updateAccountTokens (tokens, objects) { + _updateAccountTokens (tokens, imageObjects) { const { accountTokens, providerType, selectedAddress } = this._getTokenRelatedStates() accountTokens[selectedAddress][providerType] = tokens - this.store.updateState({ accountTokens, tokens, objects }) + this.store.updateState({ accountTokens, tokens, imageObjects }) } /** diff --git a/ui/app/components/balance-component.js b/ui/app/components/balance-component.js index 1a55a1f5b..042420789 100644 --- a/ui/app/components/balance-component.js +++ b/ui/app/components/balance-component.js @@ -22,7 +22,7 @@ function mapStateToProps (state) { network, conversionRate: state.metamask.conversionRate, currentCurrency: state.metamask.currentCurrency, - objects: state.metamask.objects, + imageObjects: state.metamask.imageObjects, } } @@ -33,9 +33,9 @@ function BalanceComponent () { BalanceComponent.prototype.render = function () { const props = this.props - const { token, network, objects } = props + const { token, network, imageObjects } = props let imageUrl - if (token) imageUrl = objects[token.address] + if (token) imageUrl = imageObjects[token.address] return h('div.balance-container', {}, [ diff --git a/ui/app/components/modals/hide-token-confirmation-modal.js b/ui/app/components/modals/hide-token-confirmation-modal.js index b5f396e6f..4ed09d2ee 100644 --- a/ui/app/components/modals/hide-token-confirmation-modal.js +++ b/ui/app/components/modals/hide-token-confirmation-modal.js @@ -10,7 +10,7 @@ function mapStateToProps (state) { return { network: state.metamask.network, token: state.appState.modal.modalState.props.token, - tokenImagesHashes: state.metamask.objects, + imageObjects: state.metamask.imageObjects, } } @@ -41,9 +41,9 @@ module.exports = connect(mapStateToProps, mapDispatchToProps)(HideTokenConfirmat HideTokenConfirmationModal.prototype.render = function () { - const { token, network, hideToken, hideModal, tokenImagesHashes } = this.props + const { token, network, hideToken, hideModal, imageObjects } = this.props const { symbol, address } = token - const imageUrl = tokenImagesHashes[address] + const imageUrl = imageObjects[address] return h('div.hide-token-confirmation', {}, [ h('div.hide-token-confirmation__container', { diff --git a/ui/app/components/token-list.js b/ui/app/components/token-list.js index 0f88a7703..7ee8b5faa 100644 --- a/ui/app/components/token-list.js +++ b/ui/app/components/token-list.js @@ -15,7 +15,7 @@ function mapStateToProps (state) { network: state.metamask.network, tokens: state.metamask.tokens, userAddress: selectors.getSelectedAddress(state), - tokenImagesHashes: state.metamask.objects, + imageObjects: state.metamask.imageObjects, } } @@ -47,7 +47,7 @@ function TokenList () { } TokenList.prototype.render = function () { - const { userAddress, tokenImagesHashes } = this.props + const { userAddress, imageObjects } = this.props const state = this.state const { tokens, isLoading, error } = state if (isLoading) { @@ -77,7 +77,7 @@ TokenList.prototype.render = function () { } return h('div', tokens.map((tokenData) => { - tokenData.imageUrl = tokenImagesHashes[tokenData.address] + tokenData.imageUrl = imageObjects[tokenData.address] return h(TokenCell, tokenData) })) From 68c1b4c17049e3ef18397ae83b0eb9da8cccab2c Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Mon, 20 Aug 2018 22:32:14 -0300 Subject: [PATCH 27/36] watchAsset returns result wether token was added or not --- app/scripts/background.js | 20 +++++++++++++++++++- app/scripts/controllers/preferences.js | 16 +++++++++------- app/scripts/metamask-controller.js | 4 ++-- 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/app/scripts/background.js b/app/scripts/background.js index 029ad139a..1913d35dd 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -256,7 +256,7 @@ function setupController (initState, initLangCode) { showUnconfirmedMessage: triggerUi, unlockAccountMessage: triggerUi, showUnapprovedTx: triggerUi, - showAddTokenUi: triggerUi, + showWatchAssetUi: showWatchAssetUi, // initial state initState, // initial locale code @@ -444,6 +444,24 @@ function triggerUi () { }) } +/** + * Opens the browser popup for user confirmation of watchAsset + * then it waits until user interact with the UI + */ +function showWatchAssetUi () { + triggerUi() + return new Promise( + (resolve) => { + var interval = setInterval(() => { + if (!notificationIsOpen) { + clearInterval(interval) + resolve() + } + }, 1000) + } + ) +} + // On first install, open a window to MetaMask website to how-it-works. extension.runtime.onInstalled.addListener(function (details) { if ((details.reason === 'install') && (!METAMASK_DEBUG)) { diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js index 611d2d067..11f36e284 100644 --- a/app/scripts/controllers/preferences.js +++ b/app/scripts/controllers/preferences.js @@ -41,7 +41,7 @@ class PreferencesController { this.diagnostics = opts.diagnostics this.network = opts.network this.store = new ObservableStore(initState) - this.showAddTokenUi = opts.showAddTokenUi + this.showWatchAssetUi = opts.showWatchAssetUi this._subscribeProviderType() } // PUBLIC METHODS @@ -82,13 +82,12 @@ class PreferencesController { * @param {Function} - next * @param {Function} - end */ - requestAddToken (req, res, next, end) { + async requestWatchAsset (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 + res.result = await this._handleWatchAssetERC20(options) end() break default: @@ -521,17 +520,20 @@ class PreferencesController { /** * Handle the suggestion of an ERC20 asset through `watchAsset` * * - * @param {Object} options Parameters according to addition of ERC20 token + * @param {Boolean} assetAdded Boolean according to addition of ERC20 token * */ - _handleWatchAssetERC20 (options) { + async _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() + return this.showWatchAssetUi().then(() => { + const tokenAddresses = this.getTokens().filter(token => token.address === normalizeAddress(rawAddress)) + return tokenAddresses.length > 0 + }) } } diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 57001fdff..0ee9d730c 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -88,7 +88,7 @@ module.exports = class MetamaskController extends EventEmitter { this.preferencesController = new PreferencesController({ initState: initState.PreferencesController, initLangCode: opts.initLangCode, - showAddTokenUi: opts.showAddTokenUi, + showWatchAssetUi: opts.showWatchAssetUi, network: this.networkController, }) @@ -1241,7 +1241,7 @@ module.exports = class MetamaskController extends EventEmitter { engine.push(createOriginMiddleware({ origin })) engine.push(createLoggerMiddleware({ origin })) engine.push(filterMiddleware) - engine.push(this.preferencesController.requestAddToken.bind(this.preferencesController)) + engine.push(this.preferencesController.requestWatchAsset.bind(this.preferencesController)) engine.push(createProviderMiddleware({ provider: this.provider })) // setup connection From 6fa889abcb2e907073e227379e1fc930d22bfe2d Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Tue, 21 Aug 2018 12:59:42 -0300 Subject: [PATCH 28/36] refactor watchToken related functions --- app/scripts/controllers/preferences.js | 73 +++++++++---------- ui/app/components/balance-component.js | 6 +- .../modals/hide-token-confirmation-modal.js | 6 +- ui/app/components/token-list.js | 6 +- 4 files changed, 45 insertions(+), 46 deletions(-) diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js index 11f36e284..04a9f2e75 100644 --- a/app/scripts/controllers/preferences.js +++ b/app/scripts/controllers/preferences.js @@ -15,7 +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 {object} store.assetImages 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 @@ -28,7 +28,7 @@ class PreferencesController { frequentRpcList: [], currentAccountTab: 'history', accountTokens: {}, - imageObjects: {}, + assetImages: {}, tokens: [], suggestedTokens: {}, useBlockie: false, @@ -60,12 +60,12 @@ class PreferencesController { return this.store.getState().suggestedTokens } - getImageObjects () { - return this.store.getState().imageObjects + getAssetImages () { + return this.store.getState().assetImages } - addSuggestedToken (tokenOpts) { - this._validateSuggestedTokenParams(tokenOpts) + addSuggestedERC20Asset (tokenOpts) { + this._validateERC20AssetParams(tokenOpts) const suggested = this.getSuggestedTokens() const { rawAddress, symbol, decimals, imageUrl } = tokenOpts const address = normalizeAddress(rawAddress) @@ -291,7 +291,7 @@ class PreferencesController { const address = normalizeAddress(rawAddress) const newEntry = { address, symbol, decimals } const tokens = this.store.getState().tokens - const imageObjects = this.getImageObjects() + const assetImages = this.getAssetImages() const previousEntry = tokens.find((token, index) => { return token.address === address }) @@ -302,8 +302,8 @@ class PreferencesController { } else { tokens.push(newEntry) } - imageObjects[address] = imageUrl - this._updateAccountTokens(tokens, imageObjects) + assetImages[address] = imageUrl + this._updateAccountTokens(tokens, assetImages) return Promise.resolve(tokens) } @@ -316,10 +316,10 @@ class PreferencesController { */ removeToken (rawAddress) { const tokens = this.store.getState().tokens - const imageObjects = this.getImageObjects() + const assetImages = this.getAssetImages() const updatedTokens = tokens.filter(token => token.address !== rawAddress) - delete imageObjects[rawAddress] - this._updateAccountTokens(updatedTokens, imageObjects) + delete assetImages[rawAddress] + this._updateAccountTokens(updatedTokens, assetImages) return Promise.resolve(updatedTokens) } @@ -446,25 +446,6 @@ 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. * @@ -483,10 +464,10 @@ class PreferencesController { * @param {array} tokens Array of tokens to be updated. * */ - _updateAccountTokens (tokens, imageObjects) { + _updateAccountTokens (tokens, assetImages) { const { accountTokens, providerType, selectedAddress } = this._getTokenRelatedStates() accountTokens[selectedAddress][providerType] = tokens - this.store.updateState({ accountTokens, tokens, imageObjects }) + this.store.updateState({ accountTokens, tokens, assetImages }) } /** @@ -520,21 +501,39 @@ class PreferencesController { /** * Handle the suggestion of an ERC20 asset through `watchAsset` * * - * @param {Boolean} assetAdded Boolean according to addition of ERC20 token + * @param {Promise} promise Promise according to addition of ERC20 token * */ async _handleWatchAssetERC20 (options) { - // TODO handle bad parameters const { address, symbol, decimals, imageUrl } = options const rawAddress = address - this._validateSuggestedTokenParams({ rawAddress, symbol, decimals }) + this._validateERC20AssetParams({ rawAddress, symbol, decimals }) const tokenOpts = { rawAddress, decimals, symbol, imageUrl } - this.addSuggestedToken(tokenOpts) + this.addSuggestedERC20Asset(tokenOpts) return this.showWatchAssetUi().then(() => { const tokenAddresses = this.getTokens().filter(token => token.address === normalizeAddress(rawAddress)) return tokenAddresses.length > 0 }) } + + /** + * 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 + * + */ + _validateERC20AssetParams (opts) { + const { rawAddress, symbol, decimals } = opts + if (!rawAddress || !symbol || !decimals) throw new Error(`Cannot suggest token without address, symbol, and decimals`) + if (!(symbol.length < 6)) throw new Error(`Invalid symbol ${symbol} more than five 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}`) + } } module.exports = PreferencesController diff --git a/ui/app/components/balance-component.js b/ui/app/components/balance-component.js index 042420789..9af27f4ec 100644 --- a/ui/app/components/balance-component.js +++ b/ui/app/components/balance-component.js @@ -22,7 +22,7 @@ function mapStateToProps (state) { network, conversionRate: state.metamask.conversionRate, currentCurrency: state.metamask.currentCurrency, - imageObjects: state.metamask.imageObjects, + assetImages: state.metamask.assetImages, } } @@ -33,9 +33,9 @@ function BalanceComponent () { BalanceComponent.prototype.render = function () { const props = this.props - const { token, network, imageObjects } = props + const { token, network, assetImages } = props let imageUrl - if (token) imageUrl = imageObjects[token.address] + if (token) imageUrl = assetImages[token.address] return h('div.balance-container', {}, [ diff --git a/ui/app/components/modals/hide-token-confirmation-modal.js b/ui/app/components/modals/hide-token-confirmation-modal.js index 4ed09d2ee..bdecc0593 100644 --- a/ui/app/components/modals/hide-token-confirmation-modal.js +++ b/ui/app/components/modals/hide-token-confirmation-modal.js @@ -10,7 +10,7 @@ function mapStateToProps (state) { return { network: state.metamask.network, token: state.appState.modal.modalState.props.token, - imageObjects: state.metamask.imageObjects, + assetImages: state.metamask.assetImages, } } @@ -41,9 +41,9 @@ module.exports = connect(mapStateToProps, mapDispatchToProps)(HideTokenConfirmat HideTokenConfirmationModal.prototype.render = function () { - const { token, network, hideToken, hideModal, imageObjects } = this.props + const { token, network, hideToken, hideModal, assetImages } = this.props const { symbol, address } = token - const imageUrl = imageObjects[address] + const imageUrl = assetImages[address] return h('div.hide-token-confirmation', {}, [ h('div.hide-token-confirmation__container', { diff --git a/ui/app/components/token-list.js b/ui/app/components/token-list.js index 7ee8b5faa..bbdb71c7e 100644 --- a/ui/app/components/token-list.js +++ b/ui/app/components/token-list.js @@ -15,7 +15,7 @@ function mapStateToProps (state) { network: state.metamask.network, tokens: state.metamask.tokens, userAddress: selectors.getSelectedAddress(state), - imageObjects: state.metamask.imageObjects, + assetImages: state.metamask.assetImages, } } @@ -47,7 +47,7 @@ function TokenList () { } TokenList.prototype.render = function () { - const { userAddress, imageObjects } = this.props + const { userAddress, assetImages } = this.props const state = this.state const { tokens, isLoading, error } = state if (isLoading) { @@ -77,7 +77,7 @@ TokenList.prototype.render = function () { } return h('div', tokens.map((tokenData) => { - tokenData.imageUrl = imageObjects[tokenData.address] + tokenData.imageUrl = assetImages[tokenData.address] return h(TokenCell, tokenData) })) From 3a3732eb2471c83722d41f2389f34c8759b5e2cb Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Tue, 21 Aug 2018 13:12:45 -0300 Subject: [PATCH 29/36] returning error in watchAsset --- app/scripts/controllers/preferences.js | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js index 04a9f2e75..a03abbf79 100644 --- a/app/scripts/controllers/preferences.js +++ b/app/scripts/controllers/preferences.js @@ -87,11 +87,15 @@ class PreferencesController { const { type, options } = req.params switch (type) { case 'ERC20': - res.result = await this._handleWatchAssetERC20(options) - end() + const result = await this._handleWatchAssetERC20(options) + if (result instanceof Error) { + end(result) + } else { + res.result = result + end() + } break default: - // TODO return promise for not handled assets end(new Error(`Asset of type ${type} not supported`)) } } else { @@ -507,7 +511,11 @@ class PreferencesController { async _handleWatchAssetERC20 (options) { const { address, symbol, decimals, imageUrl } = options const rawAddress = address - this._validateERC20AssetParams({ rawAddress, symbol, decimals }) + try { + this._validateERC20AssetParams({ rawAddress, symbol, decimals }) + } catch (err) { + return err + } const tokenOpts = { rawAddress, decimals, symbol, imageUrl } this.addSuggestedERC20Asset(tokenOpts) return this.showWatchAssetUi().then(() => { From 6ccf2811e75924d7e92793df7e4ec915770cf6b4 Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Tue, 21 Aug 2018 19:17:57 -0300 Subject: [PATCH 30/36] unit tests for watchAsset --- .../preferences-controller-test.js | 110 ++++++++++++++++++ ui/app/components/token-list.js | 2 - 2 files changed, 110 insertions(+), 2 deletions(-) diff --git a/test/unit/app/controllers/preferences-controller-test.js b/test/unit/app/controllers/preferences-controller-test.js index 9b2c846bd..58fc3d9c5 100644 --- a/test/unit/app/controllers/preferences-controller-test.js +++ b/test/unit/app/controllers/preferences-controller-test.js @@ -1,6 +1,7 @@ const assert = require('assert') const ObservableStore = require('obs-store') const PreferencesController = require('../../../../app/scripts/controllers/preferences') +const sinon = require('sinon') describe('preferences controller', function () { let preferencesController @@ -339,5 +340,114 @@ describe('preferences controller', function () { assert.deepEqual(tokensSecond, initialTokensSecond, 'tokens equal for same network') }) }) + + describe('on watchAsset', function () { + var stubNext, stubEnd, stubHandleWatchAssetERC20, asy, req, res + const sandbox = sinon.createSandbox() + + beforeEach(() => { + req = {params: {}} + res = {} + asy = {next: () => {}, end: () => {}} + stubNext = sandbox.stub(asy, 'next') + stubEnd = sandbox.stub(asy, 'end').returns(0) + stubHandleWatchAssetERC20 = sandbox.stub(preferencesController, '_handleWatchAssetERC20') + }) + after(() => { + sandbox.restore() + }) + + it('should do anything if method not corresponds', async function () { + const asy = {next: () => {}, end: () => {}} + var stubNext = sandbox.stub(asy, 'next') + var stubEnd = sandbox.stub(asy, 'end').returns(0) + req.method = 'metamask' + await preferencesController.requestWatchAsset(req, res, asy.next, asy.end) + sandbox.assert.notCalled(stubEnd) + sandbox.assert.called(stubNext) + }) + it('should do something if method is supported', async function () { + const asy = {next: () => {}, end: () => {}} + var stubNext = sandbox.stub(asy, 'next') + var stubEnd = sandbox.stub(asy, 'end').returns(0) + req.method = 'metamask_watchAsset' + req.params.type = 'someasset' + await preferencesController.requestWatchAsset(req, res, asy.next, asy.end) + sandbox.assert.called(stubEnd) + sandbox.assert.notCalled(stubNext) + }) + it('should through error if method is supported but asset type is not', async function () { + req.method = 'metamask_watchAsset' + req.params.type = 'someasset' + await preferencesController.requestWatchAsset(req, res, asy.next, asy.end) + sandbox.assert.called(stubEnd) + sandbox.assert.notCalled(stubHandleWatchAssetERC20) + sandbox.assert.notCalled(stubNext) + assert.deepEqual(res, {}) + }) + it('should trigger handle add asset if type supported', async function () { + const asy = {next: () => {}, end: () => {}} + req.method = 'metamask_watchAsset' + req.params.type = 'ERC20' + await preferencesController.requestWatchAsset(req, res, asy.next, asy.end) + sandbox.assert.called(stubHandleWatchAssetERC20) + }) + }) + + describe('on watchAsset of type ERC20', function () { + var req + + const sandbox = sinon.createSandbox() + beforeEach(() => { + req = {params: {type: 'ERC20'}} + }) + after(() => { + sandbox.restore() + }) + + it('should add suggested token', async function () { + const address = '0xabcdef1234567' + const symbol = 'ABBR' + const decimals = 5 + const imageUrl = 'someimageurl' + req.params.options = { address, symbol, decimals, imageUrl } + + sandbox.stub(preferencesController, '_validateERC20AssetParams').returns(true) + preferencesController.showWatchAssetUi = async () => {} + + await preferencesController._handleWatchAssetERC20(req.params.options) + const suggested = preferencesController.getSuggestedTokens() + assert.equal(Object.keys(suggested).length, 1, `one token added ${Object.keys(suggested)}`) + + assert.equal(suggested[address].address, address, 'set address correctly') + assert.equal(suggested[address].symbol, symbol, 'set symbol correctly') + assert.equal(suggested[address].decimals, decimals, 'set decimals correctly') + assert.equal(suggested[address].imageUrl, imageUrl, 'set imageUrl correctly') + }) + + it('should add token correctly if user confirms', async function () { + const address = '0xabcdef1234567' + const symbol = 'ABBR' + const decimals = 5 + const imageUrl = 'someimageurl' + req.params.options = { address, symbol, decimals, imageUrl } + + sandbox.stub(preferencesController, '_validateERC20AssetParams').returns(true) + preferencesController.showWatchAssetUi = async () => { + await preferencesController.addToken(address, symbol, decimals, imageUrl) + } + + await preferencesController._handleWatchAssetERC20(req.params.options) + const tokens = preferencesController.getTokens() + assert.equal(tokens.length, 1, `one token added`) + const added = tokens[0] + assert.equal(added.address, address, 'set address correctly') + assert.equal(added.symbol, symbol, 'set symbol correctly') + assert.equal(added.decimals, decimals, 'set decimals correctly') + + const assetImages = preferencesController.getAssetImages() + assert.ok(assetImages[address], `set imageurl correctly`) + }) + }) }) diff --git a/ui/app/components/token-list.js b/ui/app/components/token-list.js index bbdb71c7e..907793026 100644 --- a/ui/app/components/token-list.js +++ b/ui/app/components/token-list.js @@ -9,8 +9,6 @@ const selectors = require('../selectors') const log = require('loglevel') function mapStateToProps (state) { - // In order to get `imageUrl` from token added with `eth_watchToken` - // TODO do this with cache memory for browsers, add support for image object, var names return { network: state.metamask.network, tokens: state.metamask.tokens, From 153731e46285563f834cf29d154efec2cf9077e5 Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Wed, 22 Aug 2018 12:06:11 -0300 Subject: [PATCH 31/36] fix integration tests on balance component with new watchAsset --- ui/app/components/balance-component.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/app/components/balance-component.js b/ui/app/components/balance-component.js index 9af27f4ec..f85d1cdcd 100644 --- a/ui/app/components/balance-component.js +++ b/ui/app/components/balance-component.js @@ -34,8 +34,8 @@ function BalanceComponent () { BalanceComponent.prototype.render = function () { const props = this.props const { token, network, assetImages } = props - let imageUrl - if (token) imageUrl = assetImages[token.address] + const address = token && token.address + const imageUrl = assetImages && address ? assetImages[token.address] : undefined return h('div.balance-container', {}, [ @@ -46,7 +46,7 @@ BalanceComponent.prototype.render = function () { // }), h(Identicon, { diameter: 50, - address: token && token.address, + address, network, imageUrl, }), From 56bed3f1bce3cde176784026b10bc3bbe8e819d0 Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Wed, 22 Aug 2018 18:14:10 -0300 Subject: [PATCH 32/36] expose web3.metamask.watchAsset --- app/scripts/lib/auto-reload.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/app/scripts/lib/auto-reload.js b/app/scripts/lib/auto-reload.js index cce31c3d2..f3c89ecdb 100644 --- a/app/scripts/lib/auto-reload.js +++ b/app/scripts/lib/auto-reload.js @@ -14,6 +14,23 @@ function setupDappAutoReload (web3, observable) { console.warn('MetaMask: web3 will be deprecated in the near future in favor of the ethereumProvider \nhttps://github.com/MetaMask/faq/blob/master/detecting_metamask.md#web3-deprecation') hasBeenWarned = true } + // setup wallet + if (key === 'metamask') { + return { + watchAsset: (params) => { + return new Promise((resolve, reject) => { + web3.currentProvider.sendAsync({ + jsonrpc: '2.0', + method: 'metamask_watchAsset', + params, + }, (err, res) => { + if (err) reject(err) + resolve(res) + }) + }) + }, + } + } // get the time of use lastTimeUsed = Date.now() // return value normally From b59a1e91b8f4b595500a0785f325e833fa35407d Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Thu, 23 Aug 2018 15:54:40 -0300 Subject: [PATCH 33/36] typo watchAsset imageUrl to image --- app/scripts/controllers/preferences.js | 12 ++++++------ .../app/controllers/preferences-controller-test.js | 14 +++++++------- ui/app/actions.js | 4 ++-- ui/app/components/balance-component.js | 4 ++-- ui/app/components/identicon.js | 6 +++--- .../modals/hide-token-confirmation-modal.js | 4 ++-- .../confirm-add-suggested-token.component.js | 4 ++-- .../confirm-add-suggested-token.container.js | 2 +- ui/app/components/token-cell.js | 4 ++-- ui/app/components/token-list.js | 2 +- 10 files changed, 28 insertions(+), 28 deletions(-) diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js index a03abbf79..d57aec71a 100644 --- a/app/scripts/controllers/preferences.js +++ b/app/scripts/controllers/preferences.js @@ -67,9 +67,9 @@ class PreferencesController { addSuggestedERC20Asset (tokenOpts) { this._validateERC20AssetParams(tokenOpts) const suggested = this.getSuggestedTokens() - const { rawAddress, symbol, decimals, imageUrl } = tokenOpts + const { rawAddress, symbol, decimals, image } = tokenOpts const address = normalizeAddress(rawAddress) - const newEntry = { address, symbol, decimals, imageUrl } + const newEntry = { address, symbol, decimals, image } suggested[address] = newEntry this.store.updateState({ suggestedTokens: suggested }) } @@ -291,7 +291,7 @@ class PreferencesController { * @returns {Promise} Promises the new array of AddedToken objects. * */ - async addToken (rawAddress, symbol, decimals, imageUrl) { + async addToken (rawAddress, symbol, decimals, image) { const address = normalizeAddress(rawAddress) const newEntry = { address, symbol, decimals } const tokens = this.store.getState().tokens @@ -306,7 +306,7 @@ class PreferencesController { } else { tokens.push(newEntry) } - assetImages[address] = imageUrl + assetImages[address] = image this._updateAccountTokens(tokens, assetImages) return Promise.resolve(tokens) } @@ -509,14 +509,14 @@ class PreferencesController { * */ async _handleWatchAssetERC20 (options) { - const { address, symbol, decimals, imageUrl } = options + const { address, symbol, decimals, image } = options const rawAddress = address try { this._validateERC20AssetParams({ rawAddress, symbol, decimals }) } catch (err) { return err } - const tokenOpts = { rawAddress, decimals, symbol, imageUrl } + const tokenOpts = { rawAddress, decimals, symbol, image } this.addSuggestedERC20Asset(tokenOpts) return this.showWatchAssetUi().then(() => { const tokenAddresses = this.getTokens().filter(token => token.address === normalizeAddress(rawAddress)) diff --git a/test/unit/app/controllers/preferences-controller-test.js b/test/unit/app/controllers/preferences-controller-test.js index 58fc3d9c5..d63356215 100644 --- a/test/unit/app/controllers/preferences-controller-test.js +++ b/test/unit/app/controllers/preferences-controller-test.js @@ -409,8 +409,8 @@ describe('preferences controller', function () { const address = '0xabcdef1234567' const symbol = 'ABBR' const decimals = 5 - const imageUrl = 'someimageurl' - req.params.options = { address, symbol, decimals, imageUrl } + const image = 'someimage' + req.params.options = { address, symbol, decimals, image } sandbox.stub(preferencesController, '_validateERC20AssetParams').returns(true) preferencesController.showWatchAssetUi = async () => {} @@ -422,19 +422,19 @@ describe('preferences controller', function () { assert.equal(suggested[address].address, address, 'set address correctly') assert.equal(suggested[address].symbol, symbol, 'set symbol correctly') assert.equal(suggested[address].decimals, decimals, 'set decimals correctly') - assert.equal(suggested[address].imageUrl, imageUrl, 'set imageUrl correctly') + assert.equal(suggested[address].image, image, 'set image correctly') }) it('should add token correctly if user confirms', async function () { const address = '0xabcdef1234567' const symbol = 'ABBR' const decimals = 5 - const imageUrl = 'someimageurl' - req.params.options = { address, symbol, decimals, imageUrl } + const image = 'someimage' + req.params.options = { address, symbol, decimals, image } sandbox.stub(preferencesController, '_validateERC20AssetParams').returns(true) preferencesController.showWatchAssetUi = async () => { - await preferencesController.addToken(address, symbol, decimals, imageUrl) + await preferencesController.addToken(address, symbol, decimals, image) } await preferencesController._handleWatchAssetERC20(req.params.options) @@ -446,7 +446,7 @@ describe('preferences controller', function () { assert.equal(added.decimals, decimals, 'set decimals correctly') const assetImages = preferencesController.getAssetImages() - assert.ok(assetImages[address], `set imageurl correctly`) + assert.ok(assetImages[address], `set image correctly`) }) }) }) diff --git a/ui/app/actions.js b/ui/app/actions.js index b5f97d374..870ba42be 100644 --- a/ui/app/actions.js +++ b/ui/app/actions.js @@ -1599,11 +1599,11 @@ function showAddSuggestedTokenPage (transitionForward = true) { } } -function addToken (address, symbol, decimals, imageUrl) { +function addToken (address, symbol, decimals, image) { return (dispatch) => { dispatch(actions.showLoadingIndication()) return new Promise((resolve, reject) => { - background.addToken(address, symbol, decimals, imageUrl, (err, tokens) => { + background.addToken(address, symbol, decimals, image, (err, tokens) => { dispatch(actions.hideLoadingIndication()) if (err) { dispatch(actions.displayWarning(err.message)) diff --git a/ui/app/components/balance-component.js b/ui/app/components/balance-component.js index f85d1cdcd..9b6f13c80 100644 --- a/ui/app/components/balance-component.js +++ b/ui/app/components/balance-component.js @@ -35,7 +35,7 @@ BalanceComponent.prototype.render = function () { const props = this.props const { token, network, assetImages } = props const address = token && token.address - const imageUrl = assetImages && address ? assetImages[token.address] : undefined + const image = assetImages && address ? assetImages[token.address] : undefined return h('div.balance-container', {}, [ @@ -48,7 +48,7 @@ BalanceComponent.prototype.render = function () { diameter: 50, address, network, - imageUrl, + image, }), token ? this.renderTokenBalance() : this.renderBalance(), diff --git a/ui/app/components/identicon.js b/ui/app/components/identicon.js index 6b632352f..076e65b81 100644 --- a/ui/app/components/identicon.js +++ b/ui/app/components/identicon.js @@ -26,17 +26,17 @@ function mapStateToProps (state) { IdenticonComponent.prototype.render = function () { var props = this.props - const { className = '', address, imageUrl } = props + const { className = '', address, image } = props var diameter = props.diameter || this.defaultDiameter const style = { height: diameter, width: diameter, borderRadius: diameter / 2, } - if (imageUrl) { + if (image) { return h('img', { className: `${className} identicon`, - src: imageUrl, + src: image, style: { ...style, }, diff --git a/ui/app/components/modals/hide-token-confirmation-modal.js b/ui/app/components/modals/hide-token-confirmation-modal.js index bdecc0593..fb38516d3 100644 --- a/ui/app/components/modals/hide-token-confirmation-modal.js +++ b/ui/app/components/modals/hide-token-confirmation-modal.js @@ -43,7 +43,7 @@ module.exports = connect(mapStateToProps, mapDispatchToProps)(HideTokenConfirmat HideTokenConfirmationModal.prototype.render = function () { const { token, network, hideToken, hideModal, assetImages } = this.props const { symbol, address } = token - const imageUrl = assetImages[address] + const image = assetImages[address] return h('div.hide-token-confirmation', {}, [ h('div.hide-token-confirmation__container', { @@ -57,7 +57,7 @@ HideTokenConfirmationModal.prototype.render = function () { diameter: 45, address, network, - imageUrl, + image, }), h('div.hide-token-confirmation__symbol', {}, symbol), diff --git a/ui/app/components/pages/confirm-add-suggested-token/confirm-add-suggested-token.component.js b/ui/app/components/pages/confirm-add-suggested-token/confirm-add-suggested-token.component.js index 37d9aca02..025435a3b 100644 --- a/ui/app/components/pages/confirm-add-suggested-token/confirm-add-suggested-token.component.js +++ b/ui/app/components/pages/confirm-add-suggested-token/confirm-add-suggested-token.component.js @@ -61,7 +61,7 @@ export default class ConfirmAddSuggestedToken extends Component { { Object.entries(pendingTokens) .map(([ address, token ]) => { - const { name, symbol, imageUrl } = token + const { name, symbol, image } = token return (
{ this.getTokenName(name, symbol) } diff --git a/ui/app/components/pages/confirm-add-suggested-token/confirm-add-suggested-token.container.js b/ui/app/components/pages/confirm-add-suggested-token/confirm-add-suggested-token.container.js index 89291ff4f..1f2737e52 100644 --- a/ui/app/components/pages/confirm-add-suggested-token/confirm-add-suggested-token.container.js +++ b/ui/app/components/pages/confirm-add-suggested-token/confirm-add-suggested-token.container.js @@ -18,7 +18,7 @@ const mapStateToProps = ({ metamask }) => { const mapDispatchToProps = dispatch => { return { - addToken: ({address, symbol, decimals, imageUrl}) => dispatch(addToken(address, symbol, decimals, imageUrl)), + addToken: ({address, symbol, decimals, image}) => dispatch(addToken(address, symbol, decimals, image)), removeSuggestedTokens: () => dispatch(removeSuggestedTokens()), } } diff --git a/ui/app/components/token-cell.js b/ui/app/components/token-cell.js index a84d8eda0..58a30228d 100644 --- a/ui/app/components/token-cell.js +++ b/ui/app/components/token-cell.js @@ -56,7 +56,7 @@ TokenCell.prototype.render = function () { sidebarOpen, currentCurrency, // userAddress, - imageUrl, + image, } = props let currentTokenToFiatRate let currentTokenInFiat @@ -97,7 +97,7 @@ TokenCell.prototype.render = function () { diameter: 50, address, network, - imageUrl, + image, }), h('div.token-list-item__balance-ellipsis', null, [ diff --git a/ui/app/components/token-list.js b/ui/app/components/token-list.js index 907793026..6a88f30bf 100644 --- a/ui/app/components/token-list.js +++ b/ui/app/components/token-list.js @@ -75,7 +75,7 @@ TokenList.prototype.render = function () { } return h('div', tokens.map((tokenData) => { - tokenData.imageUrl = assetImages[tokenData.address] + tokenData.image = assetImages[tokenData.address] return h(TokenCell, tokenData) })) From 053e262ae738af29cfe67e702350f1f046b4b311 Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Thu, 23 Aug 2018 16:32:04 -0300 Subject: [PATCH 34/36] delete web3.metamassk.watchAsset --- app/scripts/lib/auto-reload.js | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/app/scripts/lib/auto-reload.js b/app/scripts/lib/auto-reload.js index f3c89ecdb..cce31c3d2 100644 --- a/app/scripts/lib/auto-reload.js +++ b/app/scripts/lib/auto-reload.js @@ -14,23 +14,6 @@ function setupDappAutoReload (web3, observable) { console.warn('MetaMask: web3 will be deprecated in the near future in favor of the ethereumProvider \nhttps://github.com/MetaMask/faq/blob/master/detecting_metamask.md#web3-deprecation') hasBeenWarned = true } - // setup wallet - if (key === 'metamask') { - return { - watchAsset: (params) => { - return new Promise((resolve, reject) => { - web3.currentProvider.sendAsync({ - jsonrpc: '2.0', - method: 'metamask_watchAsset', - params, - }, (err, res) => { - if (err) reject(err) - resolve(res) - }) - }) - }, - } - } // get the time of use lastTimeUsed = Date.now() // return value normally From 8af45d50cf345ae0f5cecf5314f73603e7cc73a7 Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Thu, 23 Aug 2018 16:44:21 -0300 Subject: [PATCH 35/36] add watchAsset CHANGELOG --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ddaa496dd..097d57cdf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Current Develop Branch +- (#4606)[https://github.com/MetaMask/metamask-extension/pull/4606]: Add new metamask_watchAsset method. + ## 4.9.3 Wed Aug 15 2018 - (#4897)[https://github.com/MetaMask/metamask-extension/pull/4897]: QR code scan for recipient addresses. From 3106374cc31b66e5a0faadd657b4430e21aa48b2 Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Mon, 27 Aug 2018 22:10:14 -0300 Subject: [PATCH 36/36] watchAsset small changes --- app/scripts/controllers/preferences.js | 2 +- test/unit/app/controllers/preferences-controller-test.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js index d57aec71a..4798b2ad6 100644 --- a/app/scripts/controllers/preferences.js +++ b/app/scripts/controllers/preferences.js @@ -1,6 +1,6 @@ const ObservableStore = require('obs-store') const normalizeAddress = require('eth-sig-util').normalize -const isValidAddress = require('ethereumjs-util').isValidAddress +const { isValidAddress } = require('ethereumjs-util') const extend = require('xtend') diff --git a/test/unit/app/controllers/preferences-controller-test.js b/test/unit/app/controllers/preferences-controller-test.js index d63356215..2c261be90 100644 --- a/test/unit/app/controllers/preferences-controller-test.js +++ b/test/unit/app/controllers/preferences-controller-test.js @@ -357,7 +357,7 @@ describe('preferences controller', function () { sandbox.restore() }) - it('should do anything if method not corresponds', async function () { + it('shouldn not do anything if method not corresponds', async function () { const asy = {next: () => {}, end: () => {}} var stubNext = sandbox.stub(asy, 'next') var stubEnd = sandbox.stub(asy, 'end').returns(0)