From 5d7c2810a701097ef1a4f9de23948418340f9cb4 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Mon, 18 Jun 2018 15:05:41 -0700 Subject: 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(+) (limited to 'app/scripts') 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 -- cgit v1.2.3 From f14ed329801ab65c31e84f8e9d8d93700ed56670 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Mon, 18 Jun 2018 15:33:50 -0700 Subject: Begin letting UI show suggested tokens --- app/scripts/controllers/preferences.js | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) (limited to 'app/scripts') 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() } } -- cgit v1.2.3 From 5e4f3e430a9057b073cfc82255fe62c5e8550e44 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Mon, 18 Jun 2018 15:37:37 -0700 Subject: 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(-) (limited to 'app/scripts') 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 -- cgit v1.2.3 From 0481335dda447ba4c228d146834952bac0ad641b Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Mon, 18 Jun 2018 15:50:27 -0700 Subject: Improved rpc-engine usage --- app/scripts/controllers/preferences.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'app/scripts') 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() } -- cgit v1.2.3 From 081884bd8095b2027e88fabdfe297f6d2fc8c38e Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Fri, 3 Aug 2018 16:42:13 -0400 Subject: rpc-engine not crashing when eth_watchToken --- app/scripts/controllers/preferences.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'app/scripts') 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() } -- cgit v1.2.3 From 9ac9f53a73357238ed2ee0ce57c65de592cfd968 Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Fri, 3 Aug 2018 19:24:12 -0400 Subject: eth_watchToken working --- app/scripts/controllers/preferences.js | 7 +++++++ app/scripts/metamask-controller.js | 1 + 2 files changed, 8 insertions(+) (limited to 'app/scripts') 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), -- cgit v1.2.3 From 78ad3c38e2c9cfce8b0756c7d0df8264316d1d21 Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Mon, 6 Aug 2018 18:28:47 -0400 Subject: add suggested token params validation --- app/scripts/controllers/preferences.js | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) (limited to 'app/scripts') 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 -- cgit v1.2.3 From 15ea8c04b28a9f89999c96caf188d157e5230a55 Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Tue, 7 Aug 2018 17:53:36 -0400 Subject: fix merge --- app/scripts/controllers/preferences.js | 1 + 1 file changed, 1 insertion(+) (limited to 'app/scripts') 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. -- cgit v1.2.3 From 33357e3538b5157a852323d5f1e2db7f19b3303e Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Tue, 7 Aug 2018 19:12:16 -0400 Subject: refactor unused code --- app/scripts/controllers/preferences.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'app/scripts') 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. * -- cgit v1.2.3 From 9666eaba668f7b288c42366ba04add951e6aa3b5 Mon Sep 17 00:00:00 2001 From: hahnmichaelf Date: Sun, 12 Aug 2018 18:57:58 -0400 Subject: base - working. fixes #4774 --- app/scripts/contentscript.js | 2 +- app/scripts/esdb-replace.js | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 app/scripts/esdb-replace.js (limited to 'app/scripts') diff --git a/app/scripts/contentscript.js b/app/scripts/contentscript.js index e0a2b0061..60ef97e5e 100644 --- a/app/scripts/contentscript.js +++ b/app/scripts/contentscript.js @@ -199,5 +199,5 @@ function blacklistedDomainCheck () { function redirectToPhishingWarning () { console.log('MetaMask - routing to Phishing Warning component') const extensionURL = extension.runtime.getURL('phishing.html') - window.location.href = extensionURL + window.location.href = extensionURL + "#" + window.location.hostname } diff --git a/app/scripts/esdb-replace.js b/app/scripts/esdb-replace.js new file mode 100644 index 000000000..ae5991586 --- /dev/null +++ b/app/scripts/esdb-replace.js @@ -0,0 +1,5 @@ +window.onload = function() { + if (window.location.pathname === "/phishing.html") { + document.getElementById("esdbLink").innerHTML = "To read more about this scam, navigate to: https://etherscamdb.info/domain/" + window.location.hash.substring(1) + "" + } +} -- cgit v1.2.3 From 7d2d71bcbc9779b7a25f56a67264cac876356869 Mon Sep 17 00:00:00 2001 From: hahnmichaelf Date: Mon, 13 Aug 2018 12:40:16 -0400 Subject: change name --- app/scripts/esdb-replace.js | 5 ----- app/scripts/phishing-detect.js | 5 +++++ 2 files changed, 5 insertions(+), 5 deletions(-) delete mode 100644 app/scripts/esdb-replace.js create mode 100644 app/scripts/phishing-detect.js (limited to 'app/scripts') diff --git a/app/scripts/esdb-replace.js b/app/scripts/esdb-replace.js deleted file mode 100644 index ae5991586..000000000 --- a/app/scripts/esdb-replace.js +++ /dev/null @@ -1,5 +0,0 @@ -window.onload = function() { - if (window.location.pathname === "/phishing.html") { - document.getElementById("esdbLink").innerHTML = "To read more about this scam, navigate to: https://etherscamdb.info/domain/" + window.location.hash.substring(1) + "" - } -} diff --git a/app/scripts/phishing-detect.js b/app/scripts/phishing-detect.js new file mode 100644 index 000000000..ae5991586 --- /dev/null +++ b/app/scripts/phishing-detect.js @@ -0,0 +1,5 @@ +window.onload = function() { + if (window.location.pathname === "/phishing.html") { + document.getElementById("esdbLink").innerHTML = "To read more about this scam, navigate to: https://etherscamdb.info/domain/" + window.location.hash.substring(1) + "" + } +} -- cgit v1.2.3 From 4808ce25ebdc566a13acbb9cb1c6815368b7c4a1 Mon Sep 17 00:00:00 2001 From: hahnmichaelf Date: Mon, 13 Aug 2018 13:16:37 -0400 Subject: fix for lint-test --- app/scripts/contentscript.js | 2 +- app/scripts/phishing-detect.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'app/scripts') diff --git a/app/scripts/contentscript.js b/app/scripts/contentscript.js index 60ef97e5e..b6d5cfe9e 100644 --- a/app/scripts/contentscript.js +++ b/app/scripts/contentscript.js @@ -199,5 +199,5 @@ function blacklistedDomainCheck () { function redirectToPhishingWarning () { console.log('MetaMask - routing to Phishing Warning component') const extensionURL = extension.runtime.getURL('phishing.html') - window.location.href = extensionURL + "#" + window.location.hostname + window.location.href = extensionURL + '#' + window.location.hostname } diff --git a/app/scripts/phishing-detect.js b/app/scripts/phishing-detect.js index ae5991586..a66cdb5ac 100644 --- a/app/scripts/phishing-detect.js +++ b/app/scripts/phishing-detect.js @@ -1,5 +1,5 @@ window.onload = function() { - if (window.location.pathname === "/phishing.html") { - document.getElementById("esdbLink").innerHTML = "To read more about this scam, navigate to: https://etherscamdb.info/domain/" + window.location.hash.substring(1) + "" + if (window.location.pathname === '/phishing.html') { + document.getElementById("esdbLink").innerHTML = 'To read more about this scam, navigate to: https://etherscamdb.info/domain/' + window.location.hash.substring(1) + '' } } -- cgit v1.2.3 From e19eb7bc035670d0f05e34840e6565bc1f907de3 Mon Sep 17 00:00:00 2001 From: hahnmichaelf Date: Mon, 13 Aug 2018 13:21:18 -0400 Subject: additional lint-test fix --- app/scripts/phishing-detect.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/scripts') diff --git a/app/scripts/phishing-detect.js b/app/scripts/phishing-detect.js index a66cdb5ac..4168b6618 100644 --- a/app/scripts/phishing-detect.js +++ b/app/scripts/phishing-detect.js @@ -1,5 +1,5 @@ window.onload = function() { if (window.location.pathname === '/phishing.html') { - document.getElementById("esdbLink").innerHTML = 'To read more about this scam, navigate to: https://etherscamdb.info/domain/' + window.location.hash.substring(1) + '' + document.getElementById('esdbLink').innerHTML = 'To read more about this scam, navigate to: https://etherscamdb.info/domain/' + window.location.hash.substring(1) + '' } } -- cgit v1.2.3 From 8f5b80a0fe13c53a602a5b2883ae1cdfba0123e1 Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Tue, 14 Aug 2018 13:58:47 -0300 Subject: update method to metamask_watchToken --- app/scripts/controllers/preferences.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/scripts') 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 = { -- cgit v1.2.3 From a4c3f6b65c9a25da0319b9077d830c23f729b32f Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Tue, 14 Aug 2018 20:08:12 -0300 Subject: add support for images base64 and urls on new ui --- app/scripts/controllers/preferences.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'app/scripts') 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) } -- cgit v1.2.3 From a4b6b2357a2eee7a4286a8490b8d31aac487120d Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Tue, 14 Aug 2018 20:09:56 -0300 Subject: watchToken to watchAsset --- app/scripts/controllers/preferences.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/scripts') 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 = { -- cgit v1.2.3 From b766104c8d8fc4d4b1c5660af54b791243836f30 Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Wed, 15 Aug 2018 18:34:57 -0300 Subject: add suggested tokens objects in metamask state --- app/scripts/controllers/preferences.js | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) (limited to 'app/scripts') 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 }) } /** -- cgit v1.2.3 From 5289a36664f180fae1dc6da07ccc80d307f7408c Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Wed, 15 Aug 2018 20:01:59 -0300 Subject: change watchAsset to new spec for type ERC20 --- app/scripts/controllers/preferences.js | 42 ++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 15 deletions(-) (limited to 'app/scripts') 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 -- cgit v1.2.3 From a36ea0e2328e6ffedd5b526470dc1133c4f2f556 Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Thu, 16 Aug 2018 12:04:43 -0300 Subject: show watch asset image from hide token modal --- app/scripts/controllers/preferences.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/scripts') 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 -- cgit v1.2.3 From bb868f58348962d4a85415380d11f72892a2e28c Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Thu, 16 Aug 2018 20:19:19 -0300 Subject: correct behavior when notification is closed when popup --- app/scripts/controllers/preferences.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/scripts') 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({}) }) } -- cgit v1.2.3 From dbab9a007fc9663427cebdbe1d41c51df67fd1fe Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Thu, 16 Aug 2018 21:17:02 -0300 Subject: delete according image when token added with watchToken deleted --- app/scripts/controllers/preferences.js | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) (limited to 'app/scripts') 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 }) } /** -- cgit v1.2.3 From 68c1b4c17049e3ef18397ae83b0eb9da8cccab2c Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Mon, 20 Aug 2018 22:32:14 -0300 Subject: 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(-) (limited to 'app/scripts') 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 -- cgit v1.2.3 From 6fa889abcb2e907073e227379e1fc930d22bfe2d Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Tue, 21 Aug 2018 12:59:42 -0300 Subject: refactor watchToken related functions --- app/scripts/controllers/preferences.js | 73 +++++++++++++++++----------------- 1 file changed, 36 insertions(+), 37 deletions(-) (limited to 'app/scripts') 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 -- cgit v1.2.3 From 3a3732eb2471c83722d41f2389f34c8759b5e2cb Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Tue, 21 Aug 2018 13:12:45 -0300 Subject: returning error in watchAsset --- app/scripts/controllers/preferences.js | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'app/scripts') 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(() => { -- cgit v1.2.3 From 3ac2b40dcf4b60f13184ff8b97f9099a6a22852e Mon Sep 17 00:00:00 2001 From: kumavis Date: Tue, 21 Aug 2018 16:30:11 -0700 Subject: metamask controller - track active controller connections --- app/scripts/metamask-controller.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'app/scripts') diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 29838ad2d..9082daac9 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -67,6 +67,10 @@ module.exports = class MetamaskController extends EventEmitter { const initState = opts.initState || {} this.recordFirstTimeInfo(initState) + // this keeps track of how many "controllerStream" connections are open + // the only thing that uses controller connections are open metamask UI instances + this.activeControllerConnections = 0 + // platform-specific api this.platform = opts.platform @@ -1209,11 +1213,19 @@ module.exports = class MetamaskController extends EventEmitter { setupControllerConnection (outStream) { const api = this.getApi() const dnode = Dnode(api) + // report new active controller connection + this.activeControllerConnections++ + this.emit('controllerConnectionChanged', this.activeControllerConnections) + // connect dnode api to remote connection pump( outStream, dnode, outStream, (err) => { + // report new active controller connection + this.activeControllerConnections-- + this.emit('controllerConnectionChanged', this.activeControllerConnections) + // report any error if (err) log.error(err) } ) -- cgit v1.2.3 From a2654108bed88db5cd09c22049471c5c3f1199d6 Mon Sep 17 00:00:00 2001 From: kumavis Date: Tue, 21 Aug 2018 16:49:24 -0700 Subject: account-tracker - only track blocks when there are activeControllerConnections --- app/scripts/lib/account-tracker.js | 18 ++++++++++++++++-- app/scripts/metamask-controller.js | 8 ++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) (limited to 'app/scripts') diff --git a/app/scripts/lib/account-tracker.js b/app/scripts/lib/account-tracker.js index b7e2c7cbe..3a52d5e8d 100644 --- a/app/scripts/lib/account-tracker.js +++ b/app/scripts/lib/account-tracker.js @@ -43,10 +43,24 @@ class AccountTracker { this._provider = opts.provider this._query = pify(new EthQuery(this._provider)) this._blockTracker = opts.blockTracker - // subscribe to latest block - this._blockTracker.on('latest', this._updateForBlock.bind(this)) // blockTracker.currentBlock may be null this._currentBlockNumber = this._blockTracker.getCurrentBlock() + // bind function for easier listener syntax + this._updateForBlock = this._updateForBlock.bind(this) + } + + start () { + // remove first to avoid double add + this._blockTracker.removeListener('latest', this._updateForBlock) + // add listener + this._blockTracker.addListener('latest', this._updateForBlock) + // fetch account balances + this._updateAccounts() + } + + stop () { + // remove listener + this._blockTracker.removeListener('latest', this._updateForBlock) } /** diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 9082daac9..71df45ba0 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -131,6 +131,14 @@ module.exports = class MetamaskController extends EventEmitter { provider: this.provider, blockTracker: this.blockTracker, }) + // start and stop polling for balances based on activeControllerConnections + this.on('controllerConnectionChanged', (activeControllerConnections) => { + if (activeControllerConnections > 0) { + this.accountTracker.start() + } else { + this.accountTracker.stop() + } + }) // key mgmt const additionalKeyrings = [TrezorKeyring, LedgerBridgeKeyring] -- cgit v1.2.3 From 003d445a98164dac0c0529dfd69f5e3987d7d05c Mon Sep 17 00:00:00 2001 From: Whymarrh Whitby Date: Thu, 16 Aug 2018 12:59:39 -0230 Subject: Update unlock logic to not overwrite existing selected address --- app/scripts/metamask-controller.js | 42 ++++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 13 deletions(-) (limited to 'app/scripts') diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 29838ad2d..4ee88186a 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -137,19 +137,7 @@ module.exports = class MetamaskController extends EventEmitter { encryptor: opts.encryptor || undefined, }) - // If only one account exists, make sure it is selected. - this.keyringController.memStore.subscribe((state) => { - const addresses = state.keyrings.reduce((res, keyring) => { - return res.concat(keyring.accounts) - }, []) - if (addresses.length === 1) { - const address = addresses[0] - this.preferencesController.setSelectedAddress(address) - } - // ensure preferences + identities controller know about all addresses - this.preferencesController.addAddresses(addresses) - this.accountTracker.syncWithAddresses(addresses) - }) + this.keyringController.memStore.subscribe((s) => this._onKeyringControllerUpdate(s)) // detect tokens controller this.detectTokensController = new DetectTokensController({ @@ -1278,6 +1266,34 @@ module.exports = class MetamaskController extends EventEmitter { ) } + /** + * Handle a KeyringController update + * @param {object} state the KC state + * @return {Promise} + * @private + */ + async _onKeyringControllerUpdate (state) { + const {isUnlocked, keyrings} = state + const addresses = keyrings.reduce((acc, {accounts}) => acc.concat(accounts), []) + + if (!addresses.length) { + return + } + + // Ensure preferences + identities controller know about all addresses + this.preferencesController.addAddresses(addresses) + this.accountTracker.syncWithAddresses(addresses) + + const wasLocked = !isUnlocked + if (wasLocked) { + const oldSelectedAddress = this.preferencesController.getSelectedAddress() + if (!addresses.includes(oldSelectedAddress)) { + const address = addresses[0] + await this.preferencesController.setSelectedAddress(address) + } + } + } + /** * A method for emitting the full MetaMask state to all registered listeners. * @private -- cgit v1.2.3 From bf6d624e769eb5486b5c491165fad1862435669b Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 22 Aug 2018 12:05:41 -0700 Subject: Add todo to dedupe UI tracking in background --- app/scripts/metamask-controller.js | 1 + 1 file changed, 1 insertion(+) (limited to 'app/scripts') diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 71df45ba0..4aa901e31 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -1444,6 +1444,7 @@ module.exports = class MetamaskController extends EventEmitter { } } + // TODO: Replace isClientOpen methods with `controllerConnectionChanged` events. /** * A method for recording whether the MetaMask user interface is open or not. * @private -- cgit v1.2.3 From 56bed3f1bce3cde176784026b10bc3bbe8e819d0 Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Wed, 22 Aug 2018 18:14:10 -0300 Subject: expose web3.metamask.watchAsset --- app/scripts/lib/auto-reload.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'app/scripts') 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 -- cgit v1.2.3 From b23cca14699b6c6a8c843c9cc020ec96fe758822 Mon Sep 17 00:00:00 2001 From: Evgeniy Filatov Date: Sun, 19 Aug 2018 20:25:33 +0300 Subject: implemented improvements to RPC history --- app/scripts/controllers/preferences.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/scripts') diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js index 707fd7de9..7456a7a7c 100644 --- a/app/scripts/controllers/preferences.js +++ b/app/scripts/controllers/preferences.js @@ -338,7 +338,7 @@ class PreferencesController { if (_url !== 'http://localhost:8545') { rpcList.push(_url) } - if (rpcList.length > 2) { + if (rpcList.length > 3) { rpcList.shift() } return Promise.resolve(rpcList) -- cgit v1.2.3 From 9a80d6e8598850fec00471c6101c194e90c30353 Mon Sep 17 00:00:00 2001 From: Evgeniy Filatov Date: Thu, 23 Aug 2018 01:26:30 +0300 Subject: updated docs, small improvement of recent RPC rendering --- app/scripts/controllers/preferences.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/scripts') diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js index 7456a7a7c..1b85e4fd1 100644 --- a/app/scripts/controllers/preferences.js +++ b/app/scripts/controllers/preferences.js @@ -322,7 +322,7 @@ class PreferencesController { /** * Returns an updated rpcList based on the passed url and the current list. - * The returned list will have a max length of 2. If the _url currently exists it the list, it will be moved to the + * The returned list will have a max length of 3. If the _url currently exists it the list, it will be moved to the * end of the list. The current list is modified and returned as a promise. * * @param {string} _url The rpc url to add to the frequentRpcList. -- cgit v1.2.3 From b59a1e91b8f4b595500a0785f325e833fa35407d Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Thu, 23 Aug 2018 15:54:40 -0300 Subject: typo watchAsset imageUrl to image --- app/scripts/controllers/preferences.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'app/scripts') 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)) -- cgit v1.2.3 From 053e262ae738af29cfe67e702350f1f046b4b311 Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Thu, 23 Aug 2018 16:32:04 -0300 Subject: delete web3.metamassk.watchAsset --- app/scripts/lib/auto-reload.js | 17 ----------------- 1 file changed, 17 deletions(-) (limited to 'app/scripts') 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 -- cgit v1.2.3 From 803a79f8367080141e38ed73065b5a3467f5a196 Mon Sep 17 00:00:00 2001 From: bitpshr Date: Fri, 24 Aug 2018 12:40:37 -0400 Subject: Do not resolve .test domains using ENS --- app/scripts/lib/ipfsContent.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/scripts') diff --git a/app/scripts/lib/ipfsContent.js b/app/scripts/lib/ipfsContent.js index 5db63f47d..38682b916 100644 --- a/app/scripts/lib/ipfsContent.js +++ b/app/scripts/lib/ipfsContent.js @@ -34,7 +34,7 @@ module.exports = function (provider) { return { cancel: true } } - extension.webRequest.onErrorOccurred.addListener(ipfsContent, {urls: ['*://*.eth/', '*://*.test/']}) + extension.webRequest.onErrorOccurred.addListener(ipfsContent, {urls: ['*://*.eth/']}) return { remove () { -- cgit v1.2.3 From 3106374cc31b66e5a0faadd657b4430e21aa48b2 Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Mon, 27 Aug 2018 22:10:14 -0300 Subject: watchAsset small changes --- app/scripts/controllers/preferences.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/scripts') 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') -- cgit v1.2.3 From 7c3b69e1e495ed0d44cd1ed43db55828f3e05642 Mon Sep 17 00:00:00 2001 From: Whymarrh Whitby Date: Sat, 8 Sep 2018 19:59:50 -0230 Subject: Attach the RPC error value to txMeta --- app/scripts/controllers/transactions/tx-state-manager.js | 1 + 1 file changed, 1 insertion(+) (limited to 'app/scripts') diff --git a/app/scripts/controllers/transactions/tx-state-manager.js b/app/scripts/controllers/transactions/tx-state-manager.js index 28a18ca2e..daa6cc388 100644 --- a/app/scripts/controllers/transactions/tx-state-manager.js +++ b/app/scripts/controllers/transactions/tx-state-manager.js @@ -353,6 +353,7 @@ class TransactionStateManager extends EventEmitter { const txMeta = this.getTx(txId) txMeta.err = { message: err.toString(), + rpc: err.value, stack: err.stack, } this.updateTx(txMeta) -- cgit v1.2.3 From ee568d5f5a3d04f32969fd2ba3113b9eeb175d63 Mon Sep 17 00:00:00 2001 From: Connor Christie Date: Sun, 9 Sep 2018 19:33:09 -0500 Subject: Upgrade obs-store and fix memory leaks --- app/scripts/metamask-controller.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'app/scripts') diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 98cb62bfa..1060f508a 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -177,7 +177,7 @@ module.exports = class MetamaskController extends EventEmitter { blockTracker: this.blockTracker, getGasPrice: this.getGasPrice.bind(this), }) - this.txController.on('newUnapprovedTx', opts.showUnapprovedTx.bind(opts)) + this.txController.on('newUnapprovedTx', () => opts.showUnapprovedTx()) this.txController.on(`tx:status-update`, (txId, status) => { if (status === 'confirmed' || status === 'failed') { @@ -1229,8 +1229,10 @@ module.exports = class MetamaskController extends EventEmitter { ) dnode.on('remote', (remote) => { // push updates to popup - const sendUpdate = remote.sendUpdate.bind(remote) + const sendUpdate = (update) => remote.sendUpdate(update) this.on('update', sendUpdate) + // remove update listener once the connection ends + dnode.on('end', () => this.removeListener('update', sendUpdate)) }) } @@ -1280,10 +1282,12 @@ module.exports = class MetamaskController extends EventEmitter { * @param {*} outStream - The stream to provide public config over. */ setupPublicConfig (outStream) { + const configStream = asStream(this.publicConfigStore) pump( - asStream(this.publicConfigStore), + configStream, outStream, (err) => { + configStream.destroy() if (err) log.error(err) } ) -- cgit v1.2.3 From 43de189d067f8cf03cdd97380cbe2487319271eb Mon Sep 17 00:00:00 2001 From: Alexander Tseung Date: Sun, 9 Sep 2018 10:07:23 -0700 Subject: Add createCancelTransaction method --- app/scripts/controllers/transactions/enums.js | 12 +++++++ app/scripts/controllers/transactions/index.js | 49 +++++++++++++++++++++++++-- app/scripts/metamask-controller.js | 14 ++++++++ 3 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 app/scripts/controllers/transactions/enums.js (limited to 'app/scripts') diff --git a/app/scripts/controllers/transactions/enums.js b/app/scripts/controllers/transactions/enums.js new file mode 100644 index 000000000..be6f16e0d --- /dev/null +++ b/app/scripts/controllers/transactions/enums.js @@ -0,0 +1,12 @@ +const TRANSACTION_TYPE_CANCEL = 'cancel' +const TRANSACTION_TYPE_RETRY = 'retry' +const TRANSACTION_TYPE_STANDARD = 'standard' + +const TRANSACTION_STATUS_APPROVED = 'approved' + +module.exports = { + TRANSACTION_TYPE_CANCEL, + TRANSACTION_TYPE_RETRY, + TRANSACTION_TYPE_STANDARD, + TRANSACTION_STATUS_APPROVED, +} diff --git a/app/scripts/controllers/transactions/index.js b/app/scripts/controllers/transactions/index.js index 5d7d6d6da..59e30cdde 100644 --- a/app/scripts/controllers/transactions/index.js +++ b/app/scripts/controllers/transactions/index.js @@ -11,6 +11,14 @@ const txUtils = require('./lib/util') const cleanErrorStack = require('../../lib/cleanErrorStack') const log = require('loglevel') const recipientBlacklistChecker = require('./lib/recipient-blacklist-checker') +const { + TRANSACTION_TYPE_CANCEL, + TRANSACTION_TYPE_RETRY, + TRANSACTION_TYPE_STANDARD, + TRANSACTION_STATUS_APPROVED, +} = require('./enums') + +const { hexToBn, bnToHex } = require('../../lib/util') /** Transaction Controller is an aggregate of sub-controllers and trackers @@ -160,7 +168,10 @@ class TransactionController extends EventEmitter { const normalizedTxParams = txUtils.normalizeTxParams(txParams) txUtils.validateTxParams(normalizedTxParams) // construct txMeta - let txMeta = this.txStateManager.generateTxMeta({ txParams: normalizedTxParams }) + let txMeta = this.txStateManager.generateTxMeta({ + txParams: normalizedTxParams, + type: TRANSACTION_TYPE_STANDARD, + }) this.addTx(txMeta) this.emit('newUnapprovedTx', txMeta) @@ -214,12 +225,46 @@ class TransactionController extends EventEmitter { txParams: originalTxMeta.txParams, lastGasPrice, loadingDefaults: false, + type: TRANSACTION_TYPE_RETRY, }) this.addTx(txMeta) this.emit('newUnapprovedTx', txMeta) return txMeta } + /** + * Creates a new approved transaction to attempt to cancel a previously submitted transaction. The + * new transaction contains the same nonce as the previous, is a basic ETH transfer of 0x value to + * the sender's address, and has a higher gasPrice than that of the previous transaction. + * @param {number} originalTxId - the id of the txMeta that you want to attempt to cancel + * @param {string=} customGasPrice - the hex value to use for the cancel transaction + * @returns {txMeta} + */ + async createCancelTransaction (originalTxId, customGasPrice) { + const originalTxMeta = this.txStateManager.getTx(originalTxId) + const { txParams } = originalTxMeta + const { gasPrice: lastGasPrice, from, nonce } = txParams + const newGasPrice = customGasPrice || bnToHex(hexToBn(lastGasPrice).mul(1.1)) + const newTxMeta = this.txStateManager.generateTxMeta({ + txParams: { + from, + to: from, + nonce, + gas: '0x5208', + value: '0x0', + gasPrice: newGasPrice, + }, + lastGasPrice, + loadingDefaults: false, + status: TRANSACTION_STATUS_APPROVED, + type: TRANSACTION_TYPE_CANCEL, + }) + + this.addTx(newTxMeta) + await this.approveTransaction(newTxMeta.id) + return newTxMeta + } + /** updates the txMeta in the txStateManager @param txMeta {Object} - the updated txMeta @@ -393,7 +438,7 @@ class TransactionController extends EventEmitter { }) this.txStateManager.getFilteredTxList({ - status: 'approved', + status: TRANSACTION_STATUS_APPROVED, }).forEach((txMeta) => { const txSignError = new Error('Transaction found as "approved" during boot - possibly stuck during signing') this.txStateManager.setTxStatusFailed(txMeta.id, txSignError) diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 98cb62bfa..9b373de9b 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -407,6 +407,7 @@ module.exports = class MetamaskController extends EventEmitter { updateTransaction: nodeify(txController.updateTransaction, txController), updateAndApproveTransaction: nodeify(txController.updateAndApproveTransaction, txController), retryTransaction: nodeify(this.retryTransaction, this), + createCancelTransaction: nodeify(this.createCancelTransaction, this), getFilteredTxList: nodeify(txController.getFilteredTxList, txController), isNonceTaken: nodeify(txController.isNonceTaken, txController), estimateGas: nodeify(this.estimateGas, this), @@ -1109,6 +1110,19 @@ module.exports = class MetamaskController extends EventEmitter { return state } + /** + * Allows a user to attempt to cancel a previously submitted transaction by creating a new + * transaction. + * @param {number} originalTxId - the id of the txMeta that you want to attempt to cancel + * @param {string=} customGasPrice - the hex value to use for the cancel transaction + * @returns {object} MetaMask state + */ + async createCancelTransaction (originalTxId, customGasPrice, cb) { + await this.txController.createCancelTransaction(originalTxId, customGasPrice) + const state = await this.getState() + return state + } + estimateGas (estimateGasParams) { return new Promise((resolve, reject) => { return this.txController.txGasUtil.query.estimateGas(estimateGasParams, (err, res) => { -- cgit v1.2.3 From eb32ccb0c77c6e480c93f02c29faafec3cd93ec0 Mon Sep 17 00:00:00 2001 From: Dan Miller Date: Tue, 11 Sep 2018 12:51:49 -0230 Subject: Ensure account-tracker currentBlockNumber is set on first block update. --- app/scripts/lib/account-tracker.js | 3 +++ 1 file changed, 3 insertions(+) (limited to 'app/scripts') diff --git a/app/scripts/lib/account-tracker.js b/app/scripts/lib/account-tracker.js index 3a52d5e8d..2e9340018 100644 --- a/app/scripts/lib/account-tracker.js +++ b/app/scripts/lib/account-tracker.js @@ -45,6 +45,9 @@ class AccountTracker { this._blockTracker = opts.blockTracker // blockTracker.currentBlock may be null this._currentBlockNumber = this._blockTracker.getCurrentBlock() + this._blockTracker.once('latest', blockNumber => { + this._currentBlockNumber = blockNumber + }) // bind function for easier listener syntax this._updateForBlock = this._updateForBlock.bind(this) } -- cgit v1.2.3 From d60991ec88fe88be4041eb4a9392df6dfc1aa973 Mon Sep 17 00:00:00 2001 From: Whymarrh Whitby Date: Mon, 10 Sep 2018 17:01:15 -0700 Subject: Delete ConfigManager, replacing its usages with PreferencesController --- app/scripts/controllers/preferences.js | 18 +++ app/scripts/lib/config-manager.js | 254 ------------------------------- app/scripts/metamask-controller.js | 46 ++---- app/scripts/migrations/_multi-keyring.js | 50 ------ 4 files changed, 27 insertions(+), 341 deletions(-) delete mode 100644 app/scripts/lib/config-manager.js delete mode 100644 app/scripts/migrations/_multi-keyring.js (limited to 'app/scripts') diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js index 464a37017..928ebdf1f 100644 --- a/app/scripts/controllers/preferences.js +++ b/app/scripts/controllers/preferences.js @@ -36,6 +36,8 @@ class PreferencesController { currentLocale: opts.initLangCode, identities: {}, lostIdentities: {}, + seedWords: null, + forgottenPassword: false, }, opts.initState) this.diagnostics = opts.diagnostics @@ -46,6 +48,22 @@ class PreferencesController { } // PUBLIC METHODS + /** + * Sets the {@code forgottenPassword} state property + * @param {boolean} forgottenPassword whether or not the user has forgotten their password + */ + setPasswordForgotten (forgottenPassword) { + this.store.updateState({ forgottenPassword }) + } + + /** + * Sets the {@code seedWords} seed words + * @param {string|null} seedWords the seed words + */ + setSeedWords (seedWords) { + this.store.updateState({ seedWords }) + } + /** * Setter for the `useBlockie` property * diff --git a/app/scripts/lib/config-manager.js b/app/scripts/lib/config-manager.js deleted file mode 100644 index 221746467..000000000 --- a/app/scripts/lib/config-manager.js +++ /dev/null @@ -1,254 +0,0 @@ -const ethUtil = require('ethereumjs-util') -const normalize = require('eth-sig-util').normalize -const { - MAINNET_RPC_URL, - ROPSTEN_RPC_URL, - KOVAN_RPC_URL, - RINKEBY_RPC_URL, -} = require('../controllers/network/enums') - -/* The config-manager is a convenience object - * wrapping a pojo-migrator. - * - * It exists mostly to allow the creation of - * convenience methods to access and persist - * particular portions of the state. - */ -module.exports = ConfigManager -function ConfigManager (opts) { - // ConfigManager is observable and will emit updates - this._subs = [] - this.store = opts.store -} - -ConfigManager.prototype.setConfig = function (config) { - var data = this.getData() - data.config = config - this.setData(data) - this._emitUpdates(config) -} - -ConfigManager.prototype.getConfig = function () { - var data = this.getData() - return data.config -} - -ConfigManager.prototype.setData = function (data) { - this.store.putState(data) -} - -ConfigManager.prototype.getData = function () { - return this.store.getState() -} - -ConfigManager.prototype.setPasswordForgotten = function (passwordForgottenState) { - const data = this.getData() - data.forgottenPassword = passwordForgottenState - this.setData(data) -} - -ConfigManager.prototype.getPasswordForgotten = function (passwordForgottenState) { - const data = this.getData() - return data.forgottenPassword -} - -ConfigManager.prototype.setWallet = function (wallet) { - var data = this.getData() - data.wallet = wallet - this.setData(data) -} - -ConfigManager.prototype.setVault = function (encryptedString) { - var data = this.getData() - data.vault = encryptedString - this.setData(data) -} - -ConfigManager.prototype.getVault = function () { - var data = this.getData() - return data.vault -} - -ConfigManager.prototype.getKeychains = function () { - return this.getData().keychains || [] -} - -ConfigManager.prototype.setKeychains = function (keychains) { - var data = this.getData() - data.keychains = keychains - this.setData(data) -} - -ConfigManager.prototype.getSelectedAccount = function () { - var config = this.getConfig() - return config.selectedAccount -} - -ConfigManager.prototype.setSelectedAccount = function (address) { - var config = this.getConfig() - config.selectedAccount = ethUtil.addHexPrefix(address) - this.setConfig(config) -} - -ConfigManager.prototype.getWallet = function () { - return this.getData().wallet -} - -// Takes a boolean -ConfigManager.prototype.setShowSeedWords = function (should) { - var data = this.getData() - data.showSeedWords = should - this.setData(data) -} - - -ConfigManager.prototype.getShouldShowSeedWords = function () { - var data = this.getData() - return data.showSeedWords -} - -ConfigManager.prototype.setSeedWords = function (words) { - var data = this.getData() - data.seedWords = words - this.setData(data) -} - -ConfigManager.prototype.getSeedWords = function () { - var data = this.getData() - return data.seedWords -} -ConfigManager.prototype.setRpcTarget = function (rpcUrl) { - var config = this.getConfig() - config.provider = { - type: 'rpc', - rpcTarget: rpcUrl, - } - this.setConfig(config) -} - -ConfigManager.prototype.setProviderType = function (type) { - var config = this.getConfig() - config.provider = { - type: type, - } - this.setConfig(config) -} - -ConfigManager.prototype.useEtherscanProvider = function () { - var config = this.getConfig() - config.provider = { - type: 'etherscan', - } - this.setConfig(config) -} - -ConfigManager.prototype.getProvider = function () { - var config = this.getConfig() - return config.provider -} - -ConfigManager.prototype.getCurrentRpcAddress = function () { - var provider = this.getProvider() - if (!provider) return null - switch (provider.type) { - - case 'mainnet': - return MAINNET_RPC_URL - - case 'ropsten': - return ROPSTEN_RPC_URL - - case 'kovan': - return KOVAN_RPC_URL - - case 'rinkeby': - return RINKEBY_RPC_URL - - default: - return provider && provider.rpcTarget ? provider.rpcTarget : RINKEBY_RPC_URL - } -} - -// -// Tx -// - -ConfigManager.prototype.getTxList = function () { - var data = this.getData() - if (data.transactions !== undefined) { - return data.transactions - } else { - return [] - } -} - -ConfigManager.prototype.setTxList = function (txList) { - var data = this.getData() - data.transactions = txList - this.setData(data) -} - - -// wallet nickname methods - -ConfigManager.prototype.getWalletNicknames = function () { - var data = this.getData() - const nicknames = ('walletNicknames' in data) ? data.walletNicknames : {} - return nicknames -} - -ConfigManager.prototype.nicknameForWallet = function (account) { - const address = normalize(account) - const nicknames = this.getWalletNicknames() - return nicknames[address] -} - -ConfigManager.prototype.setNicknameForWallet = function (account, nickname) { - const address = normalize(account) - const nicknames = this.getWalletNicknames() - nicknames[address] = nickname - var data = this.getData() - data.walletNicknames = nicknames - this.setData(data) -} - -// observable - -ConfigManager.prototype.getSalt = function () { - var data = this.getData() - return data.salt -} - -ConfigManager.prototype.setSalt = function (salt) { - var data = this.getData() - data.salt = salt - this.setData(data) -} - -ConfigManager.prototype.subscribe = function (fn) { - this._subs.push(fn) - var unsubscribe = this.unsubscribe.bind(this, fn) - return unsubscribe -} - -ConfigManager.prototype.unsubscribe = function (fn) { - var index = this._subs.indexOf(fn) - if (index !== -1) this._subs.splice(index, 1) -} - -ConfigManager.prototype._emitUpdates = function (state) { - this._subs.forEach(function (handler) { - handler(state) - }) -} - -ConfigManager.prototype.setLostAccounts = function (lostAccounts) { - var data = this.getData() - data.lostAccounts = lostAccounts - this.setData(data) -} - -ConfigManager.prototype.getLostAccounts = function () { - var data = this.getData() - return data.lostAccounts || [] -} diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 3ab256314..f9a12628b 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -36,7 +36,6 @@ const TransactionController = require('./controllers/transactions') const BalancesController = require('./controllers/computed-balances') const TokenRatesController = require('./controllers/token-rates') const DetectTokensController = require('./controllers/detect-tokens') -const ConfigManager = require('./lib/config-manager') const nodeify = require('./lib/nodeify') const accountImporter = require('./account-import-strategies') const getBuyEthUrl = require('./lib/buy-eth-url') @@ -83,11 +82,6 @@ module.exports = class MetamaskController extends EventEmitter { // network store this.networkController = new NetworkController(initState.NetworkController) - // config manager - this.configManager = new ConfigManager({ - store: this.store, - }) - // preferences controller this.preferencesController = new PreferencesController({ initState: initState.PreferencesController, @@ -314,18 +308,15 @@ module.exports = class MetamaskController extends EventEmitter { * @returns {Object} status */ getState () { - const wallet = this.configManager.getWallet() const vault = this.keyringController.store.getState().vault - const isInitialized = (!!wallet || !!vault) + const isInitialized = !!vault return { ...{ isInitialized }, ...this.memStore.getFlatState(), - ...this.configManager.getConfig(), ...{ - lostAccounts: this.configManager.getLostAccounts(), - seedWords: this.configManager.getSeedWords(), - forgottenPassword: this.configManager.getPasswordForgotten(), + // TODO: Remove usages of lost accounts + lostAccounts: [], }, } } @@ -727,7 +718,7 @@ module.exports = class MetamaskController extends EventEmitter { this.verifySeedPhrase() .then((seedWords) => { - this.configManager.setSeedWords(seedWords) + this.preferencesController.setSeedWords(seedWords) return cb(null, seedWords) }) .catch((err) => { @@ -776,7 +767,7 @@ module.exports = class MetamaskController extends EventEmitter { * @param {function} cb Callback function called with the current address. */ clearSeedWordCache (cb) { - this.configManager.setSeedWords(null) + this.preferencesController.setSeedWords(null) cb(null, this.preferencesController.getSelectedAddress()) } @@ -1037,35 +1028,16 @@ module.exports = class MetamaskController extends EventEmitter { /** * A legacy method used to record user confirmation that they understand * that some of their accounts have been recovered but should be backed up. + * This function no longer does anything and will be removed. * * @deprecated * @param {Function} cb - A callback function called with a full state update. */ markAccountsFound (cb) { - this.configManager.setLostAccounts([]) - this.sendUpdate() + // TODO Remove me cb(null, this.getState()) } - /** - * A legacy method (probably dead code) that was used when we swapped out our - * key management library that we depended on. - * - * Described in: - * https://medium.com/metamask/metamask-3-migration-guide-914b79533cdd - * - * @deprecated - * @param {} migratorOutput - */ - restoreOldLostAccounts (migratorOutput) { - const { lostAccounts } = migratorOutput - if (lostAccounts) { - this.configManager.setLostAccounts(lostAccounts.map(acct => acct.address)) - return this.importLostAccounts(migratorOutput) - } - return Promise.resolve(migratorOutput) - } - /** * An account object * @typedef Account @@ -1144,7 +1116,7 @@ module.exports = class MetamaskController extends EventEmitter { * @param {Function} cb - A callback function called when complete. */ markPasswordForgotten (cb) { - this.configManager.setPasswordForgotten(true) + this.preferencesController.setPasswordForgotten(true) this.sendUpdate() cb() } @@ -1154,7 +1126,7 @@ module.exports = class MetamaskController extends EventEmitter { * @param {Function} cb - A callback function called when complete. */ unMarkPasswordForgotten (cb) { - this.configManager.setPasswordForgotten(false) + this.preferencesController.setPasswordForgotten(false) this.sendUpdate() cb() } diff --git a/app/scripts/migrations/_multi-keyring.js b/app/scripts/migrations/_multi-keyring.js deleted file mode 100644 index 7a4578ea7..000000000 --- a/app/scripts/migrations/_multi-keyring.js +++ /dev/null @@ -1,50 +0,0 @@ -const version = 5 - -/* - -This is an incomplete migration bc it requires post-decrypted data -which we dont have access to at the time of this writing. - -*/ - -const ObservableStore = require('obs-store') -const ConfigManager = require('../../app/scripts/lib/config-manager') -const IdentityStoreMigrator = require('../../app/scripts/lib/idStore-migrator') -const KeyringController = require('eth-keyring-controller') - -const password = 'obviously not correct' - -module.exports = { - version, - - migrate: function (versionedData) { - versionedData.meta.version = version - - const store = new ObservableStore(versionedData.data) - const configManager = new ConfigManager({ store }) - const idStoreMigrator = new IdentityStoreMigrator({ configManager }) - const keyringController = new KeyringController({ - configManager: configManager, - }) - - // attempt to migrate to multiVault - return idStoreMigrator.migratedVaultForPassword(password) - .then((result) => { - // skip if nothing to migrate - if (!result) return Promise.resolve(versionedData) - delete versionedData.data.wallet - // create new keyrings - const privKeys = result.lostAccounts.map(acct => acct.privateKey) - return Promise.all([ - keyringController.restoreKeyring(result.serialized), - keyringController.restoreKeyring({ type: 'Simple Key Pair', data: privKeys }), - ]).then(() => { - return keyringController.persistAllKeyrings(password) - }).then(() => { - // copy result on to state object - versionedData.data = store.get() - return Promise.resolve(versionedData) - }) - }) - }, -} -- cgit v1.2.3 From 13bc46d8243b434268db04b58500720b4884a969 Mon Sep 17 00:00:00 2001 From: Whymarrh Whitby Date: Tue, 11 Sep 2018 12:12:35 -0700 Subject: Default NoticeController ctor opts to empty obj --- app/scripts/notice-controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/scripts') diff --git a/app/scripts/notice-controller.js b/app/scripts/notice-controller.js index 2def4371e..ce686d9d1 100644 --- a/app/scripts/notice-controller.js +++ b/app/scripts/notice-controller.js @@ -7,7 +7,7 @@ const uniqBy = require('lodash.uniqby') module.exports = class NoticeController extends EventEmitter { - constructor (opts) { + constructor (opts = {}) { super() this.noticePoller = null this.firstVersion = opts.firstVersion -- cgit v1.2.3 From 36dd0354e777e6786ae0d2284ffcb1adbc6d85f7 Mon Sep 17 00:00:00 2001 From: bitpshr Date: Mon, 10 Sep 2018 17:11:57 -0400 Subject: Implement latest EIP-712 protocol --- app/scripts/lib/typed-message-manager.js | 74 +++++++++++++++++++++--------- app/scripts/metamask-controller.js | 77 +++++++++++++++++++++++++------- 2 files changed, 112 insertions(+), 39 deletions(-) (limited to 'app/scripts') diff --git a/app/scripts/lib/typed-message-manager.js b/app/scripts/lib/typed-message-manager.js index e5e1c94b3..3e97023f5 100644 --- a/app/scripts/lib/typed-message-manager.js +++ b/app/scripts/lib/typed-message-manager.js @@ -4,6 +4,7 @@ const createId = require('./random-id') const assert = require('assert') const sigUtil = require('eth-sig-util') const log = require('loglevel') +const jsonschema = require('jsonschema') /** * Represents, and contains data about, an 'eth_signTypedData' type signature request. These are created when a @@ -17,7 +18,7 @@ const log = require('loglevel') * @property {Object} msgParams.from The address that is making the signature request. * @property {string} msgParams.data A hex string conversion of the raw buffer data of the signature request * @property {number} time The epoch time at which the this message was created - * @property {string} status Indicates whether the signature request is 'unapproved', 'approved', 'signed' or 'rejected' + * @property {string} status Indicates whether the signature request is 'unapproved', 'approved', 'signed', 'rejected', or 'errored' * @property {string} type The json-prc signing method for which a signature request has been made. A 'Message' will * always have a 'eth_signTypedData' type. * @@ -26,17 +27,10 @@ const log = require('loglevel') module.exports = class TypedMessageManager extends EventEmitter { /** * Controller in charge of managing - storing, adding, removing, updating - TypedMessage. - * - * @typedef {Object} TypedMessage - * @param {Object} opts @deprecated - * @property {Object} memStore The observable store where TypedMessage are saved. - * @property {Object} memStore.unapprovedTypedMessages A collection of all TypedMessages in the 'unapproved' state - * @property {number} memStore.unapprovedTypedMessagesCount The count of all TypedMessages in this.memStore.unapprobedMsgs - * @property {array} messages Holds all messages that have been created by this TypedMessage - * */ - constructor (opts) { + constructor ({ networkController }) { super() + this.networkController = networkController this.memStore = new ObservableStore({ unapprovedTypedMessages: {}, unapprovedTypedMessagesCount: 0, @@ -76,15 +70,17 @@ module.exports = class TypedMessageManager extends EventEmitter { * @returns {promise} When the message has been signed or rejected * */ - addUnapprovedMessageAsync (msgParams, req) { + addUnapprovedMessageAsync (msgParams, req, version) { return new Promise((resolve, reject) => { - const msgId = this.addUnapprovedMessage(msgParams, req) + const msgId = this.addUnapprovedMessage(msgParams, req, version) this.once(`${msgId}:finished`, (data) => { switch (data.status) { case 'signed': return resolve(data.rawSig) case 'rejected': return reject(new Error('MetaMask Message Signature: User denied message signature.')) + case 'errored': + return reject(new Error(`MetaMask Message Signature: ${data.error}`)) default: return reject(new Error(`MetaMask Message Signature: Unknown problem: ${JSON.stringify(msgParams)}`)) } @@ -102,7 +98,8 @@ module.exports = class TypedMessageManager extends EventEmitter { * @returns {number} The id of the newly created TypedMessage. * */ - addUnapprovedMessage (msgParams, req) { + addUnapprovedMessage (msgParams, req, version) { + msgParams.version = version this.validateParams(msgParams) // add origin from request if (req) msgParams.origin = req.origin @@ -132,14 +129,33 @@ module.exports = class TypedMessageManager extends EventEmitter { * */ validateParams (params) { - assert.equal(typeof params, 'object', 'Params should ben an object.') - assert.ok('data' in params, 'Params must include a data field.') - assert.ok('from' in params, 'Params must include a from field.') - assert.ok(Array.isArray(params.data), 'Data should be an array.') - assert.equal(typeof params.from, 'string', 'From field must be a string.') - assert.doesNotThrow(() => { - sigUtil.typedSignatureHash(params.data) - }, 'Expected EIP712 typed data') + switch (params.version) { + case 'V1': + assert.equal(typeof params, 'object', 'Params should ben an object.') + assert.ok('data' in params, 'Params must include a data field.') + assert.ok('from' in params, 'Params must include a from field.') + assert.ok(Array.isArray(params.data), 'Data should be an array.') + assert.equal(typeof params.from, 'string', 'From field must be a string.') + assert.doesNotThrow(() => { + sigUtil.typedSignatureHash(params.data) + }, 'Expected EIP712 typed data') + break + case 'V2': + let data + assert.equal(typeof params, 'object', 'Params should be an object.') + assert.ok('data' in params, 'Params must include a data field.') + assert.ok('from' in params, 'Params must include a from field.') + assert.equal(typeof params.from, 'string', 'From field must be a string.') + assert.equal(typeof params.data, 'string', 'Data must be passed as a valid JSON string.') + assert.doesNotThrow(() => { data = JSON.parse(params.data) }, 'Data must be passed as a valid JSON string.') + const validation = jsonschema.validate(data, sigUtil.TYPED_MESSAGE_SCHEMA) + assert.ok(data.primaryType in data.types, `Primary type of "${data.primaryType}" has no type definition.`) + assert.equal(validation.errors.length, 0, 'Data must conform to EIP-712 schema. See https://git.io/fNtcx.') + const chainId = data.domain.chainId + const activeChainId = parseInt(this.networkController.getNetworkState()) + chainId && assert.equal(chainId, activeChainId, `Provided chainId (${activeChainId}) must match the active chainId (${activeChainId})`) + break + } } /** @@ -214,6 +230,7 @@ module.exports = class TypedMessageManager extends EventEmitter { */ prepMsgForSigning (msgParams) { delete msgParams.metamaskId + delete msgParams.version return Promise.resolve(msgParams) } @@ -227,6 +244,19 @@ module.exports = class TypedMessageManager extends EventEmitter { this._setMsgStatus(msgId, 'rejected') } + /** + * Sets a TypedMessage status to 'errored' via a call to this._setMsgStatus. + * + * @param {number} msgId The id of the TypedMessage to error + * + */ + errorMessage (msgId, error) { + const msg = this.getMsg(msgId) + msg.error = error + this._updateMsg(msg) + this._setMsgStatus(msgId, 'errored') + } + // // PRIVATE METHODS // @@ -250,7 +280,7 @@ module.exports = class TypedMessageManager extends EventEmitter { msg.status = status this._updateMsg(msg) this.emit(`${msgId}:${status}`, msg) - if (status === 'rejected' || status === 'signed') { + if (status === 'rejected' || status === 'signed' || status === 'errored') { this.emit(`${msgId}:finished`, msg) } } diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index f9a12628b..d5005d977 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -49,6 +49,8 @@ const log = require('loglevel') const TrezorKeyring = require('eth-trezor-keyring') const LedgerBridgeKeyring = require('eth-ledger-bridge-keyring') const EthQuery = require('eth-query') +const ethUtil = require('ethereumjs-util') +const sigUtil = require('eth-sig-util') module.exports = class MetamaskController extends EventEmitter { @@ -205,7 +207,7 @@ module.exports = class MetamaskController extends EventEmitter { this.networkController.lookupNetwork() this.messageManager = new MessageManager() this.personalMessageManager = new PersonalMessageManager() - this.typedMessageManager = new TypedMessageManager() + this.typedMessageManager = new TypedMessageManager({ networkController: this.networkController }) this.publicConfigStore = this.initPublicConfigStore() this.store.updateStructure({ @@ -266,7 +268,6 @@ module.exports = class MetamaskController extends EventEmitter { // msg signing processEthSignMessage: this.newUnsignedMessage.bind(this), processPersonalMessage: this.newUnsignedPersonalMessage.bind(this), - processTypedMessage: this.newUnsignedTypedMessage.bind(this), } const providerProxy = this.networkController.initializeProvider(providerOpts) return providerProxy @@ -975,22 +976,31 @@ module.exports = class MetamaskController extends EventEmitter { * @param {Object} msgParams - The params passed to eth_signTypedData. * @returns {Object} Full state update. */ - signTypedMessage (msgParams) { - log.info('MetaMaskController - signTypedMessage') + async signTypedMessage (msgParams) { + log.info('MetaMaskController - eth_signTypedData') const msgId = msgParams.metamaskId - // sets the status op the message to 'approved' - // and removes the metamaskId for signing - return this.typedMessageManager.approveMessage(msgParams) - .then((cleanMsgParams) => { - // signs the message - return this.keyringController.signTypedMessage(cleanMsgParams) - }) - .then((rawSig) => { - // tells the listener that the message has been signed - // and can be returned to the dapp - this.typedMessageManager.setMsgStatusSigned(msgId, rawSig) - return this.getState() - }) + const version = msgParams.version + try { + const cleanMsgParams = await this.typedMessageManager.approveMessage(msgParams) + const address = sigUtil.normalize(cleanMsgParams.from) + const keyring = await this.keyringController.getKeyringForAccount(address) + const wallet = keyring._getWalletForAccount(address) + const privKey = ethUtil.toBuffer(wallet.getPrivateKey()) + let signature + switch (version) { + case 'V1': + signature = sigUtil.signTypedDataLegacy(privKey, { data: cleanMsgParams.data }) + break + case 'V2': + signature = sigUtil.signTypedData(privKey, { data: JSON.parse(cleanMsgParams.data) }) + break + } + this.typedMessageManager.setMsgStatusSigned(msgId, signature) + return this.getState() + } catch (error) { + log.info('MetaMaskController - eth_signTypedData failed.', error) + this.typedMessageManager.errorMessage(msgId, error) + } } /** @@ -1241,6 +1251,9 @@ module.exports = class MetamaskController extends EventEmitter { engine.push(createLoggerMiddleware({ origin })) engine.push(filterMiddleware) engine.push(this.preferencesController.requestWatchAsset.bind(this.preferencesController)) + engine.push(this.createTypedDataMiddleware('eth_signTypedData', 'V1').bind(this)) + engine.push(this.createTypedDataMiddleware('eth_signTypedData_v1', 'V1').bind(this)) + engine.push(this.createTypedDataMiddleware('eth_signTypedData_v2', 'V2').bind(this)) engine.push(createProviderMiddleware({ provider: this.provider })) // setup connection @@ -1474,4 +1487,34 @@ module.exports = class MetamaskController extends EventEmitter { set isClientOpenAndUnlocked (active) { this.tokenRatesController.isActive = active } + + /** + * Creates RPC engine middleware for processing eth_signTypedData requests + * + * @param {Object} req - request object + * @param {Object} res - response object + * @param {Function} - next + * @param {Function} - end + */ + createTypedDataMiddleware (methodName, version) { + return async (req, res, next, end) => { + const { method, params } = req + if (method === methodName) { + const promise = this.typedMessageManager.addUnapprovedMessageAsync({ + data: params.length >= 1 && params[0], + from: params.length >= 2 && params[1], + }, req, version) + this.sendUpdate() + this.opts.showUnconfirmedMessage() + try { + res.result = await promise + end() + } catch (error) { + end(error) + } + } else { + next() + } + } + } } -- cgit v1.2.3 From 68c25542965ae89badc284bf30e1f426f27aa5ae Mon Sep 17 00:00:00 2001 From: bitpshr Date: Tue, 11 Sep 2018 09:33:37 -0400 Subject: Update error message for chainId mis-match --- app/scripts/lib/typed-message-manager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/scripts') diff --git a/app/scripts/lib/typed-message-manager.js b/app/scripts/lib/typed-message-manager.js index 3e97023f5..01e1c9331 100644 --- a/app/scripts/lib/typed-message-manager.js +++ b/app/scripts/lib/typed-message-manager.js @@ -153,7 +153,7 @@ module.exports = class TypedMessageManager extends EventEmitter { assert.equal(validation.errors.length, 0, 'Data must conform to EIP-712 schema. See https://git.io/fNtcx.') const chainId = data.domain.chainId const activeChainId = parseInt(this.networkController.getNetworkState()) - chainId && assert.equal(chainId, activeChainId, `Provided chainId (${activeChainId}) must match the active chainId (${activeChainId})`) + chainId && assert.equal(chainId, activeChainId, `Provided chainId (${chainId}) must match the active chainId (${activeChainId})`) break } } -- cgit v1.2.3 From 42fdcf6239fc9278cfa85b6ae6cc025cef0e35ae Mon Sep 17 00:00:00 2001 From: bitpshr Date: Thu, 13 Sep 2018 15:12:08 -0400 Subject: Update new method namespace from v2 to v3 --- app/scripts/lib/typed-message-manager.js | 2 +- app/scripts/metamask-controller.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'app/scripts') diff --git a/app/scripts/lib/typed-message-manager.js b/app/scripts/lib/typed-message-manager.js index 01e1c9331..b10145f3b 100644 --- a/app/scripts/lib/typed-message-manager.js +++ b/app/scripts/lib/typed-message-manager.js @@ -140,7 +140,7 @@ module.exports = class TypedMessageManager extends EventEmitter { sigUtil.typedSignatureHash(params.data) }, 'Expected EIP712 typed data') break - case 'V2': + case 'V3': let data assert.equal(typeof params, 'object', 'Params should be an object.') assert.ok('data' in params, 'Params must include a data field.') diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index d5005d977..431a49dde 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -991,7 +991,7 @@ module.exports = class MetamaskController extends EventEmitter { case 'V1': signature = sigUtil.signTypedDataLegacy(privKey, { data: cleanMsgParams.data }) break - case 'V2': + case 'V3': signature = sigUtil.signTypedData(privKey, { data: JSON.parse(cleanMsgParams.data) }) break } @@ -1253,7 +1253,7 @@ module.exports = class MetamaskController extends EventEmitter { engine.push(this.preferencesController.requestWatchAsset.bind(this.preferencesController)) engine.push(this.createTypedDataMiddleware('eth_signTypedData', 'V1').bind(this)) engine.push(this.createTypedDataMiddleware('eth_signTypedData_v1', 'V1').bind(this)) - engine.push(this.createTypedDataMiddleware('eth_signTypedData_v2', 'V2').bind(this)) + engine.push(this.createTypedDataMiddleware('eth_signTypedData_v3', 'V3').bind(this)) engine.push(createProviderMiddleware({ provider: this.provider })) // setup connection -- cgit v1.2.3 From 2ec09362c59958a457939724003312024f97393c Mon Sep 17 00:00:00 2001 From: Paul Bouchon Date: Fri, 14 Sep 2018 19:26:03 -0400 Subject: EIP-1102: Transitionary API (#5256) --- app/scripts/inpage.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'app/scripts') diff --git a/app/scripts/inpage.js b/app/scripts/inpage.js index 1a170c617..d9fda1feb 100644 --- a/app/scripts/inpage.js +++ b/app/scripts/inpage.js @@ -22,6 +22,25 @@ var metamaskStream = new LocalMessageDuplexStream({ // compose the inpage provider var inpageProvider = new MetamaskInpageProvider(metamaskStream) +// Augment the provider with its enable method +inpageProvider.enable = function (options = {}) { + return new Promise((resolve, reject) => { + if (options.mockRejection) { + reject('User rejected account access') + } else { + inpageProvider.sendAsync({ method: 'eth_accounts', params: [] }, (error, response) => { + if (error) { + reject(error) + } else { + resolve(response.result) + } + }) + } + }) +} + +window.ethereum = inpageProvider + // // setup web3 // -- cgit v1.2.3 From 5a6c333506e4000602c1a1106cee6d06fe83afa8 Mon Sep 17 00:00:00 2001 From: Alexander Tseung Date: Mon, 17 Sep 2018 10:34:29 -0700 Subject: Switch existing modals from using Notification to Modal. Remove Notification component. Add CancelTransaction modal --- app/scripts/controllers/transactions/index.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'app/scripts') diff --git a/app/scripts/controllers/transactions/index.js b/app/scripts/controllers/transactions/index.js index 59e30cdde..e2965ceb6 100644 --- a/app/scripts/controllers/transactions/index.js +++ b/app/scripts/controllers/transactions/index.js @@ -18,7 +18,7 @@ const { TRANSACTION_STATUS_APPROVED, } = require('./enums') -const { hexToBn, bnToHex } = require('../../lib/util') +const { hexToBn, bnToHex, BnMultiplyByFraction } = require('../../lib/util') /** Transaction Controller is an aggregate of sub-controllers and trackers @@ -244,7 +244,8 @@ class TransactionController extends EventEmitter { const originalTxMeta = this.txStateManager.getTx(originalTxId) const { txParams } = originalTxMeta const { gasPrice: lastGasPrice, from, nonce } = txParams - const newGasPrice = customGasPrice || bnToHex(hexToBn(lastGasPrice).mul(1.1)) + + const newGasPrice = customGasPrice || bnToHex(BnMultiplyByFraction(hexToBn(lastGasPrice), 11, 10)) const newTxMeta = this.txStateManager.generateTxMeta({ txParams: { from, -- cgit v1.2.3 From 19d72c9b0b4539f55624f6e9d41ded46c31d38d5 Mon Sep 17 00:00:00 2001 From: Dan Miller Date: Fri, 21 Sep 2018 15:04:21 -0230 Subject: Adds getPendingNonce method to provider initialization options in metamask-controller. --- .../controllers/network/createMetamaskMiddleware.js | 2 +- app/scripts/metamask-controller.js | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) (limited to 'app/scripts') diff --git a/app/scripts/controllers/network/createMetamaskMiddleware.js b/app/scripts/controllers/network/createMetamaskMiddleware.js index 8b17829b7..9e6a45888 100644 --- a/app/scripts/controllers/network/createMetamaskMiddleware.js +++ b/app/scripts/controllers/network/createMetamaskMiddleware.js @@ -38,6 +38,6 @@ function createPendingNonceMiddleware ({ getPendingNonce }) { const address = req.params[0] const blockRef = req.params[1] if (blockRef !== 'pending') return next() - req.result = await getPendingNonce(address) + res.result = await getPendingNonce(address) }) } diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 431a49dde..f11626c78 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -268,6 +268,7 @@ module.exports = class MetamaskController extends EventEmitter { // msg signing processEthSignMessage: this.newUnsignedMessage.bind(this), processPersonalMessage: this.newUnsignedPersonalMessage.bind(this), + getPendingNonce: this.getPendingNonce.bind(this), } const providerProxy = this.networkController.initializeProvider(providerOpts) return providerProxy @@ -1362,6 +1363,19 @@ module.exports = class MetamaskController extends EventEmitter { return '0x' + percentileNumBn.mul(GWEI_BN).toString(16) } + /** + * Returns the nonce that will be associated with a transaction once approved + * @param address {string} - The hex string address for the transaction + * @returns Promise + */ + async getPendingNonce (address) { + const { nonceDetails, releaseLock} = await this.txController.nonceTracker.getNonceLock(address) + const pendingNonce = nonceDetails.params.highestSuggested + + releaseLock() + return pendingNonce + } + //============================================================================= // CONFIG //============================================================================= -- cgit v1.2.3 From 4d82df69959ab370ba4bf6acc324ed756322ef4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20Mi=C3=B1o?= Date: Tue, 25 Sep 2018 16:14:37 -0300 Subject: Fix MetaMask web3 version (#5352) --- app/scripts/metamask-controller.js | 1 + 1 file changed, 1 insertion(+) (limited to 'app/scripts') diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index f11626c78..ddc13c918 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -252,6 +252,7 @@ module.exports = class MetamaskController extends EventEmitter { eth_syncing: false, web3_clientVersion: `MetaMask/v${version}`, }, + version, // account mgmt getAccounts: async () => { const isUnlocked = this.keyringController.memStore.getState().isUnlocked -- cgit v1.2.3 From c4caba131776ff7397d3a4071d7cc84907ac9a43 Mon Sep 17 00:00:00 2001 From: bitpshr Date: Wed, 26 Sep 2018 10:00:12 -0400 Subject: bugfix: update eth_signTypedData_v3 parameter order --- app/scripts/metamask-controller.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'app/scripts') diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index ddc13c918..123e17569 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -1255,7 +1255,7 @@ module.exports = class MetamaskController extends EventEmitter { engine.push(this.preferencesController.requestWatchAsset.bind(this.preferencesController)) engine.push(this.createTypedDataMiddleware('eth_signTypedData', 'V1').bind(this)) engine.push(this.createTypedDataMiddleware('eth_signTypedData_v1', 'V1').bind(this)) - engine.push(this.createTypedDataMiddleware('eth_signTypedData_v3', 'V3').bind(this)) + engine.push(this.createTypedDataMiddleware('eth_signTypedData_v3', 'V3', true).bind(this)) engine.push(createProviderMiddleware({ provider: this.provider })) // setup connection @@ -1511,13 +1511,13 @@ module.exports = class MetamaskController extends EventEmitter { * @param {Function} - next * @param {Function} - end */ - createTypedDataMiddleware (methodName, version) { + createTypedDataMiddleware (methodName, version, reverse) { return async (req, res, next, end) => { const { method, params } = req if (method === methodName) { const promise = this.typedMessageManager.addUnapprovedMessageAsync({ - data: params.length >= 1 && params[0], - from: params.length >= 2 && params[1], + data: reverse ? params[1] : params[0], + from: reverse ? params[0] : params[1], }, req, version) this.sendUpdate() this.opts.showUnconfirmedMessage() -- cgit v1.2.3 From 9359fc875df061c39807d90c3ca92356960ec4c3 Mon Sep 17 00:00:00 2001 From: Paul Bouchon Date: Wed, 26 Sep 2018 10:48:17 -0400 Subject: EIP-1102: Add deprecation message (#5353) --- app/scripts/inpage.js | 6 ++++++ app/scripts/lib/auto-reload.js | 6 ------ 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'app/scripts') diff --git a/app/scripts/inpage.js b/app/scripts/inpage.js index d9fda1feb..d924be516 100644 --- a/app/scripts/inpage.js +++ b/app/scripts/inpage.js @@ -9,6 +9,11 @@ restoreContextAfterImports() log.setDefaultLevel(process.env.METAMASK_DEBUG ? 'debug' : 'warn') +console.warn('ATTENTION: In an effort to improve user privacy, MetaMask will ' + +'stop exposing user accounts to dapps by default beginning November 2nd, 2018. ' + +'Dapps should call provider.enable() in order to view and use accounts. Please see ' + +'https://bit.ly/2QQHXvF for complete information and up-to-date example code.') + // // setup plugin communication // @@ -52,6 +57,7 @@ if (typeof window.web3 !== 'undefined') { or MetaMask and another web3 extension. Please remove one and try again.`) } + var web3 = new Web3(inpageProvider) web3.setProvider = function () { log.debug('MetaMask - overrode web3.setProvider') diff --git a/app/scripts/lib/auto-reload.js b/app/scripts/lib/auto-reload.js index cce31c3d2..558391a06 100644 --- a/app/scripts/lib/auto-reload.js +++ b/app/scripts/lib/auto-reload.js @@ -2,18 +2,12 @@ module.exports = setupDappAutoReload function setupDappAutoReload (web3, observable) { // export web3 as a global, checking for usage - let hasBeenWarned = false let reloadInProgress = false let lastTimeUsed let lastSeenNetwork global.web3 = new Proxy(web3, { get: (_web3, key) => { - // show warning once on web3 access - if (!hasBeenWarned && key !== 'currentProvider') { - 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 - } // get the time of use lastTimeUsed = Date.now() // return value normally -- cgit v1.2.3 From 386110ef0fda622e87fef0f82bb6282f35c420e9 Mon Sep 17 00:00:00 2001 From: Phyrex Tsai Date: Thu, 27 Sep 2018 23:21:50 +0800 Subject: fix tld bug (#5250) --- app/scripts/lib/ipfsContent.js | 2 ++ 1 file changed, 2 insertions(+) (limited to 'app/scripts') diff --git a/app/scripts/lib/ipfsContent.js b/app/scripts/lib/ipfsContent.js index 38682b916..62a808b90 100644 --- a/app/scripts/lib/ipfsContent.js +++ b/app/scripts/lib/ipfsContent.js @@ -5,6 +5,8 @@ module.exports = function (provider) { function ipfsContent (details) { const name = details.url.substring(7, details.url.length - 1) let clearTime = null + if (/^.+\.eth$/.test(name) === false) return + extension.tabs.query({active: true}, tab => { extension.tabs.update(tab.id, { url: 'loading.html' }) -- cgit v1.2.3 From 83666e8d28d7bf7aa6cea140be359622ec781bd8 Mon Sep 17 00:00:00 2001 From: Whymarrh Whitby Date: Tue, 25 Sep 2018 11:05:17 -0230 Subject: Update extension badge with correct signTypedData count --- app/scripts/background.js | 1 + 1 file changed, 1 insertion(+) (limited to 'app/scripts') diff --git a/app/scripts/background.js b/app/scripts/background.js index 546fef569..ae450352e 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -406,6 +406,7 @@ function setupController (initState, initLangCode) { controller.txController.on('update:badge', updateBadge) controller.messageManager.on('updateBadge', updateBadge) controller.personalMessageManager.on('updateBadge', updateBadge) + controller.typedMessageManager.on('updateBadge', updateBadge) /** * Updates the Web Extension's "badge" number, on the little fox in the toolbar. -- cgit v1.2.3