From 168b4c5bc034367912f32bad6706cc91987d1085 Mon Sep 17 00:00:00 2001 From: Kevin Serrano Date: Thu, 1 Sep 2016 15:01:45 -0700 Subject: [PATCH 01/12] Install deps. Fix chrome/firefox typo. --- gulpfile.js | 8 +++++--- package.json | 2 ++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index dac6cce3e..40ae6f203 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -16,6 +16,8 @@ var eslint = require('gulp-eslint') var fs = require('fs') var path = require('path') var manifest = require('./app/manifest.json') +var gulpif = require('gulp-if') +var replace = require('gulp-replace') // browser reload @@ -72,8 +74,8 @@ gulp.task('copy:root', copyTask({ pattern: '/*', })) -gulp.task('manifest:cleanup', function() { - return gulp.src('./dist/firefox/manifest.json') +gulp.task('manifest:chrome', function() { + return gulp.src('./dist/chrome/manifest.json') .pipe(jsoneditor(function(json) { delete json.applications return json @@ -81,7 +83,7 @@ gulp.task('manifest:cleanup', function() { .pipe(gulp.dest('./dist/chrome', { overwrite: true })) }) -gulp.task('copy', gulp.series(gulp.parallel('copy:locales','copy:images','copy:fonts','copy:reload','copy:root'), 'manifest:cleanup')) +gulp.task('copy', gulp.series(gulp.parallel('copy:locales','copy:images','copy:fonts','copy:reload','copy:root'), 'manifest:chrome')) gulp.task('copy:watch', function(){ gulp.watch(['./app/{_locales,images}/*', './app/scripts/chromereload.js', './app/*.{html,json}'], gulp.series('copy')) }) diff --git a/package.json b/package.json index fdf6c822e..63754f417 100644 --- a/package.json +++ b/package.json @@ -94,8 +94,10 @@ "del": "^2.2.0", "gulp": "github:gulpjs/gulp#4.0", "gulp-brfs": "^0.1.0", + "gulp-if": "^2.0.1", "gulp-json-editor": "^2.2.1", "gulp-livereload": "^3.8.1", + "gulp-replace": "^0.5.4", "gulp-sourcemaps": "^1.6.0", "gulp-util": "^3.0.7", "gulp-watch": "^4.3.5", From c02be4fa66a037bd23fb5d51759b6d4a7486bf6e Mon Sep 17 00:00:00 2001 From: Kevin Serrano Date: Thu, 1 Sep 2016 15:19:31 -0700 Subject: [PATCH 02/12] Add production flag. Conditionally copy reload based on production. --- gulpfile.js | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/gulpfile.js b/gulpfile.js index 40ae6f203..2f2722a3b 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -19,6 +19,8 @@ var manifest = require('./app/manifest.json') var gulpif = require('gulp-if') var replace = require('gulp-replace') +var production = false + // browser reload gulp.task('dev:reload', function() { @@ -83,7 +85,20 @@ gulp.task('manifest:chrome', function() { .pipe(gulp.dest('./dist/chrome', { overwrite: true })) }) -gulp.task('copy', gulp.series(gulp.parallel('copy:locales','copy:images','copy:fonts','copy:reload','copy:root'), 'manifest:chrome')) +gulp.task('manifest:production', function() { + return gulp.src([ + './dist/firefox/manifest.json', + './dist/chrome/manifest.json', + './dist/edge/manifest.json', + ],{base: './dist/'}) + .pipe(gulpif(production,jsoneditor(function(json) { + json.background.scripts = ["scripts/background.js"] + return json + }))) + .pipe(gulp.dest('./dist/', { overwrite: true })) +}) + +gulp.task('copy', gulp.series(gulp.parallel('copy:locales','copy:images','copy:fonts','copy:reload','copy:root'), 'manifest:production', 'manifest:chrome')) gulp.task('copy:watch', function(){ gulp.watch(['./app/{_locales,images}/*', './app/scripts/chromereload.js', './app/*.{html,json}'], gulp.series('copy')) }) From 8b37bcf16b4bbc6dcfd24e38f8f6f492555b619a Mon Sep 17 00:00:00 2001 From: kumavis Date: Fri, 2 Sep 2016 12:48:50 -0700 Subject: [PATCH 03/12] Version 2.10.1 --- CHANGELOG.md | 3 +++ app/manifest.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e705adef..d25ee284a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,11 @@ ## Current Master +## 2.10.1 2016-09-02 + - Fix bug where provider menu did not allow switching to custom network from a custom network. - Sending a transaction from within MetaMask no longer triggers a popup. +- Fix Ethereum JSON RPC Filters bug. ## 2.10.0 2016-08-29 diff --git a/app/manifest.json b/app/manifest.json index 5444007fa..7d23e7d31 100644 --- a/app/manifest.json +++ b/app/manifest.json @@ -1,7 +1,7 @@ { "name": "MetaMask", "short_name": "Metamask", - "version": "2.10.0", + "version": "2.10.1", "manifest_version": 2, "author": "https://metamask.io", "description": "Ethereum Browser Extension", From 891e17c44c9a8ff8b2c794f534018f727dd25d5d Mon Sep 17 00:00:00 2001 From: kumavis Date: Fri, 2 Sep 2016 13:29:47 -0700 Subject: [PATCH 04/12] notif - use standard err-first callback style --- app/scripts/lib/notifications.js | 42 ++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/app/scripts/lib/notifications.js b/app/scripts/lib/notifications.js index df4fe73dd..4e3f7558c 100644 --- a/app/scripts/lib/notifications.js +++ b/app/scripts/lib/notifications.js @@ -9,20 +9,26 @@ module.exports = notifications window.METAMASK_NOTIFIER = notifications function show () { - getWindows((windows) => { + getPopup((err, popup) => { + if (err) throw err - if (windows.length > 0) { - const win = windows[0] - return extension.windows.update(win.id, { focused: true }) - } + if (popup) { + + // bring focus to existing popup + extension.windows.update(popup.id, { focused: true }) + + } else { - extension.windows.create({ - url: 'notification.html', - type: 'popup', - focused: true, - width: 360, - height: 500, - }) + // create new popup + extension.windows.create({ + url: 'notification.html', + type: 'popup', + focused: true, + width: 360, + height: 500, + }) + + } }) } @@ -38,19 +44,19 @@ function getWindows(cb) { } function getPopup(cb) { - getWindows((windows) => { - cb(getPopupIn(windows)) + getWindows((err, windows) => { + if (err) throw err + cb(null, getPopupIn(windows)) }) } function getPopupIn(windows) { - return windows ? windows.find((win) => { - return win.type === 'popup' - }) : null + return windows ? windows.find((win) => win.type === 'popup') : null } function closePopup() { - getPopup((popup) => { + getPopup((err, popup) => { + if (err) throw err if (!popup) return extension.windows.remove(popup.id, console.error) }) From c2a1b442e9c54b29a6f9a5bcf563a847373f5c4a Mon Sep 17 00:00:00 2001 From: kumavis Date: Fri, 2 Sep 2016 13:51:22 -0700 Subject: [PATCH 05/12] Version 2.10.2 --- CHANGELOG.md | 4 ++++ app/manifest.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d25ee284a..0affb2477 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Current Master +## 2.10.2 2016-09-02 + +- Fix bug where notification popup would not display. + ## 2.10.1 2016-09-02 - Fix bug where provider menu did not allow switching to custom network from a custom network. diff --git a/app/manifest.json b/app/manifest.json index 7d23e7d31..0fee3f051 100644 --- a/app/manifest.json +++ b/app/manifest.json @@ -1,7 +1,7 @@ { "name": "MetaMask", "short_name": "Metamask", - "version": "2.10.1", + "version": "2.10.2", "manifest_version": 2, "author": "https://metamask.io", "description": "Ethereum Browser Extension", From b4334a72b45d18c4985c1882f5da7ad4a1b907df Mon Sep 17 00:00:00 2001 From: Kevin Serrano Date: Fri, 2 Sep 2016 14:00:36 -0700 Subject: [PATCH 06/12] Refactor for spreading. Add conditionals for livereload. --- gulpfile.js | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index 2f2722a3b..ad87586e5 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -19,7 +19,7 @@ var manifest = require('./app/manifest.json') var gulpif = require('gulp-if') var replace = require('gulp-replace') -var production = false +var production = gutil.env.production // browser reload @@ -98,7 +98,20 @@ gulp.task('manifest:production', function() { .pipe(gulp.dest('./dist/', { overwrite: true })) }) -gulp.task('copy', gulp.series(gulp.parallel('copy:locales','copy:images','copy:fonts','copy:reload','copy:root'), 'manifest:production', 'manifest:chrome')) +const staticFiles = [ + 'locales', + 'images', + 'fonts', + 'root' +] + +var copyStrings = staticFiles.map(staticFile => `copy:${staticFile}`) + +if (!production) { + copyStrings.push('copy:`reload`') +} + +gulp.task('copy', gulp.series(gulp.parallel(...copyStrings), 'manifest:production', 'manifest:chrome')) gulp.task('copy:watch', function(){ gulp.watch(['./app/{_locales,images}/*', './app/scripts/chromereload.js', './app/*.{html,json}'], gulp.series('copy')) }) @@ -132,14 +145,18 @@ const jsFiles = [ 'popup', ] +var jsDevStrings = jsFiles.map(jsFile => `dev:js:${jsFile}`) +var jsBuildStrings = jsFiles.map(jsFile => `build:js:${jsFile}`) + jsFiles.forEach((jsFile) => { gulp.task(`dev:js:${jsFile}`, bundleTask({ watch: true, filename: `${jsFile}.js` })) gulp.task(`build:js:${jsFile}`, bundleTask({ watch: false, filename: `${jsFile}.js` })) }) -gulp.task('dev:js', gulp.parallel('dev:js:inpage','dev:js:contentscript','dev:js:background','dev:js:popup')) +gulp.task('dev:js', gulp.parallel(...jsDevStrings)) + +gulp.task('build:js', gulp.parallel(...jsBuildStrings)) -gulp.task('build:js', gulp.parallel('build:js:inpage','build:js:contentscript','build:js:background','build:js:popup')) // clean dist @@ -169,6 +186,7 @@ gulp.task('zip', gulp.parallel('zip:chrome', 'zip:firefox', 'zip:edge')) // high level tasks gulp.task('dev', gulp.series('dev:js', 'copy', gulp.parallel('copy:watch', 'dev:reload'))) + gulp.task('build', gulp.series('clean', gulp.parallel('build:js', 'copy'))) gulp.task('dist', gulp.series('build', 'zip')) @@ -187,7 +205,7 @@ function copyTask(opts){ destinations.forEach(function(destination) { stream = stream.pipe(gulp.dest(destination)) }) - stream.pipe(livereload()) + stream.pipe(gulpif(production,livereload())) return stream } @@ -228,7 +246,7 @@ function bundleTask(opts) { .pipe(gulp.dest('./dist/firefox/scripts')) .pipe(gulp.dest('./dist/chrome/scripts')) .pipe(gulp.dest('./dist/edge/scripts')) - .pipe(livereload()) + .pipe(gulpif(!production,livereload())) ) } From 2d0077d5c67e5861628e865c1477878f3277d10d Mon Sep 17 00:00:00 2001 From: Kevin Serrano Date: Fri, 2 Sep 2016 14:15:00 -0700 Subject: [PATCH 07/12] Changelog add and rename vars. --- CHANGELOG.md | 1 + gulpfile.js | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e705adef..d608fd976 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - Fix bug where provider menu did not allow switching to custom network from a custom network. - Sending a transaction from within MetaMask no longer triggers a popup. +- The ability to build without livereload features (such as for production) can be enabled with the gulp --disableLiveReload ## 2.10.0 2016-08-29 diff --git a/gulpfile.js b/gulpfile.js index ad87586e5..3d28e7198 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -19,7 +19,7 @@ var manifest = require('./app/manifest.json') var gulpif = require('gulp-if') var replace = require('gulp-replace') -var production = gutil.env.production +var disableLiveReload = gutil.env.disableLiveReload // browser reload @@ -91,7 +91,7 @@ gulp.task('manifest:production', function() { './dist/chrome/manifest.json', './dist/edge/manifest.json', ],{base: './dist/'}) - .pipe(gulpif(production,jsoneditor(function(json) { + .pipe(gulpif(disableLiveReload,jsoneditor(function(json) { json.background.scripts = ["scripts/background.js"] return json }))) @@ -107,8 +107,8 @@ const staticFiles = [ var copyStrings = staticFiles.map(staticFile => `copy:${staticFile}`) -if (!production) { - copyStrings.push('copy:`reload`') +if (!disableLiveReload) { + copyStrings.push('copy:reload') } gulp.task('copy', gulp.series(gulp.parallel(...copyStrings), 'manifest:production', 'manifest:chrome')) @@ -205,7 +205,7 @@ function copyTask(opts){ destinations.forEach(function(destination) { stream = stream.pipe(gulp.dest(destination)) }) - stream.pipe(gulpif(production,livereload())) + stream.pipe(gulpif(disableLiveReload,livereload())) return stream } @@ -246,7 +246,7 @@ function bundleTask(opts) { .pipe(gulp.dest('./dist/firefox/scripts')) .pipe(gulp.dest('./dist/chrome/scripts')) .pipe(gulp.dest('./dist/edge/scripts')) - .pipe(gulpif(!production,livereload())) + .pipe(gulpif(!disableLiveReload,livereload())) ) } From f9f821e72650046466b9c1f0694a1cdb0c1e637c Mon Sep 17 00:00:00 2001 From: Kevin Serrano Date: Fri, 2 Sep 2016 14:15:15 -0700 Subject: [PATCH 08/12] Changelog add. --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d608fd976..1f414e0fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ - Fix bug where provider menu did not allow switching to custom network from a custom network. - Sending a transaction from within MetaMask no longer triggers a popup. -- The ability to build without livereload features (such as for production) can be enabled with the gulp --disableLiveReload +- The ability to build without livereload features (such as for production) can be enabled with the gulp --disableLiveReload flag. ## 2.10.0 2016-08-29 From be0c605556a21a824bb841f80da58220b8b32058 Mon Sep 17 00:00:00 2001 From: Kevin Serrano Date: Fri, 2 Sep 2016 14:42:20 -0700 Subject: [PATCH 09/12] Fix boolean. --- gulpfile.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gulpfile.js b/gulpfile.js index 3d28e7198..9f1acbf67 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -205,7 +205,7 @@ function copyTask(opts){ destinations.forEach(function(destination) { stream = stream.pipe(gulp.dest(destination)) }) - stream.pipe(gulpif(disableLiveReload,livereload())) + stream.pipe(gulpif(!disableLiveReload,livereload())) return stream } From 76d63ec4e0f02b7bb91aefb9eb6959198322f42b Mon Sep 17 00:00:00 2001 From: Vincent Serpoul Date: Tue, 6 Sep 2016 11:20:22 +0800 Subject: [PATCH 10/12] Error message more helpful --- app/scripts/lib/inpage-provider.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/scripts/lib/inpage-provider.js b/app/scripts/lib/inpage-provider.js index 3b7d76c7d..e22e5e411 100644 --- a/app/scripts/lib/inpage-provider.js +++ b/app/scripts/lib/inpage-provider.js @@ -80,7 +80,8 @@ MetamaskInpageProvider.prototype.send = function (payload) { // throw not-supported Error default: - var message = 'The MetaMask Web3 object does not support synchronous methods. See https://github.com/MetaMask/faq/blob/master/DEVELOPERS.md#all-async---think-of-metamask-as-a-light-client for details.' + var message = 'The MetaMask Web3 object does not support synchronous methods like '+ payload.method + + '. See https://github.com/MetaMask/faq/blob/master/DEVELOPERS.md#all-async---think-of-metamask-as-a-light-client for details.' throw new Error(message) } @@ -131,4 +132,4 @@ function eachJsonMessage(payload, transformFn){ } else { return transformFn(payload) } -} \ No newline at end of file +} From 4cf12337f2d3ad97a670ddbdc131435415f9626b Mon Sep 17 00:00:00 2001 From: Vincent Serpoul Date: Tue, 6 Sep 2016 11:24:09 +0800 Subject: [PATCH 11/12] linting --- app/scripts/lib/inpage-provider.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/scripts/lib/inpage-provider.js b/app/scripts/lib/inpage-provider.js index e22e5e411..4f9fa1a7d 100644 --- a/app/scripts/lib/inpage-provider.js +++ b/app/scripts/lib/inpage-provider.js @@ -80,7 +80,7 @@ MetamaskInpageProvider.prototype.send = function (payload) { // throw not-supported Error default: - var message = 'The MetaMask Web3 object does not support synchronous methods like '+ payload.method + + var message = 'The MetaMask Web3 object does not support synchronous methods like ' + payload.method + '. See https://github.com/MetaMask/faq/blob/master/DEVELOPERS.md#all-async---think-of-metamask-as-a-light-client for details.' throw new Error(message) From dcc24804a1048e9379ce30233f4ecf540db5a54a Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Tue, 6 Sep 2016 15:23:48 -0700 Subject: [PATCH 12/12] Add negative value validation to tx approval --- app/scripts/metamask-controller.js | 10 +++ test/unit/metamask-controller-test.js | 97 +++++++++++++++++++++++++++ 2 files changed, 107 insertions(+) create mode 100644 test/unit/metamask-controller-test.js diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index e94db2dfd..83827ec76 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -199,6 +199,9 @@ module.exports = class MetamaskController { const idStore = this.idStore var state = idStore.getState() + let err = this.enforceTxValidations(txParams) + if (err) return onTxDoneCb(err) + // It's locked if (!state.isUnlocked) { @@ -216,6 +219,13 @@ module.exports = class MetamaskController { } } + enforceTxValidations (txParams) { + if (txParams.value.indexOf('-') === 0) { + const msg = `Invalid transaction value of ${txParams.value} not a positive number.` + return new Error(msg) + } + } + newUnsignedMessage (msgParams, cb) { var state = this.idStore.getState() if (!state.isUnlocked) { diff --git a/test/unit/metamask-controller-test.js b/test/unit/metamask-controller-test.js new file mode 100644 index 000000000..b87169ca2 --- /dev/null +++ b/test/unit/metamask-controller-test.js @@ -0,0 +1,97 @@ +var assert = require('assert') +var MetaMaskController = require('../../app/scripts/metamask-controller') +var sinon = require('sinon') +var extend = require('xtend') +const STORAGE_KEY = 'metamask-config' + +describe('MetaMaskController', function() { + const noop = () => {} + let controller = new MetaMaskController({ + showUnconfirmedMessage: noop, + unlockAccountMessage: noop, + showUnconfirmedTx: noop, + setData, + loadData, + }) + + beforeEach(function() { + // sinon allows stubbing methods that are easily verified + this.sinon = sinon.sandbox.create() + window.localStorage = {} // Hacking localStorage support into JSDom + }) + + afterEach(function() { + // sinon requires cleanup otherwise it will overwrite context + this.sinon.restore() + }) + + describe('#enforceTxValidations', function () { + it('returns null for positive values', function() { + var sample = { + value: '0x01' + } + var res = controller.enforceTxValidations(sample) + assert.equal(res, null, 'no error') + }) + + + it('returns error for negative values', function() { + var sample = { + value: '-0x01' + } + var res = controller.enforceTxValidations(sample) + assert.ok(res, 'error') + }) + }) +}) + + +function loadData () { + var oldData = getOldStyleData() + var newData + try { + newData = JSON.parse(window.localStorage[STORAGE_KEY]) + } catch (e) {} + + var data = extend({ + meta: { + version: 0, + }, + data: { + config: { + provider: { + type: 'testnet', + }, + }, + }, + }, oldData || null, newData || null) + return data +} + +function getOldStyleData () { + var config, wallet, seedWords + + var result = { + meta: { version: 0 }, + data: {}, + } + + try { + config = JSON.parse(window.localStorage['config']) + result.data.config = config + } catch (e) {} + try { + wallet = JSON.parse(window.localStorage['lightwallet']) + result.data.wallet = wallet + } catch (e) {} + try { + seedWords = window.localStorage['seedWords'] + result.data.seedWords = seedWords + } catch (e) {} + + return result +} + +function setData (data) { + window.localStorage[STORAGE_KEY] = JSON.stringify(data) +}