From 147b81068a643074a1fd7aa3e0488263a64961ad Mon Sep 17 00:00:00 2001 From: Kevin Serrano Date: Wed, 4 Oct 2017 09:56:18 -0700 Subject: [PATCH 01/46] Include OS version --- app/scripts/platforms/extension.js | 4 ++++ ui/app/reducers.js | 2 ++ 2 files changed, 6 insertions(+) diff --git a/app/scripts/platforms/extension.js b/app/scripts/platforms/extension.js index 0afe04b74..83c77a77f 100644 --- a/app/scripts/platforms/extension.js +++ b/app/scripts/platforms/extension.js @@ -17,6 +17,10 @@ class ExtensionPlatform { return extension.runtime.getManifest().version } + getPlatformInfo () { + return extension.runtime.getPlatformInfo() + } + } module.exports = ExtensionPlatform diff --git a/ui/app/reducers.js b/ui/app/reducers.js index 6a2f44534..99cbda8aa 100644 --- a/ui/app/reducers.js +++ b/ui/app/reducers.js @@ -44,7 +44,9 @@ function rootReducer (state, action) { window.logState = function () { let state = window.METAMASK_CACHED_LOG_STATE const version = global.platform.getVersion() + const platform = global.platform.getPlatformInfo() state.version = version + state.platform = platform let stateString = JSON.stringify(state, removeSeedWords, 2) return stateString } From 2f135c2e68ae58e009212be862bf2318b55a1180 Mon Sep 17 00:00:00 2001 From: Kevin Serrano Date: Wed, 4 Oct 2017 09:56:40 -0700 Subject: [PATCH 02/46] Include browser version --- ui/app/reducers.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ui/app/reducers.js b/ui/app/reducers.js index 99cbda8aa..6307998c2 100644 --- a/ui/app/reducers.js +++ b/ui/app/reducers.js @@ -44,9 +44,11 @@ function rootReducer (state, action) { window.logState = function () { let state = window.METAMASK_CACHED_LOG_STATE const version = global.platform.getVersion() + const browser = global.navigator.userAgent() const platform = global.platform.getPlatformInfo() state.version = version state.platform = platform + state.browser = browser let stateString = JSON.stringify(state, removeSeedWords, 2) return stateString } From e64c64a0492aba377db6ef2c406bdb0bd1d10469 Mon Sep 17 00:00:00 2001 From: Kevin Serrano Date: Wed, 4 Oct 2017 10:00:07 -0700 Subject: [PATCH 03/46] change global to window --- ui/app/reducers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/app/reducers.js b/ui/app/reducers.js index 6307998c2..1224b4e92 100644 --- a/ui/app/reducers.js +++ b/ui/app/reducers.js @@ -44,7 +44,7 @@ function rootReducer (state, action) { window.logState = function () { let state = window.METAMASK_CACHED_LOG_STATE const version = global.platform.getVersion() - const browser = global.navigator.userAgent() + const browser = window.navigator.userAgent() const platform = global.platform.getPlatformInfo() state.version = version state.platform = platform From 3d80565339f02e1fa3e4bc0f8aaedbeea663a712 Mon Sep 17 00:00:00 2001 From: Kevin Serrano Date: Wed, 4 Oct 2017 10:55:10 -0700 Subject: [PATCH 04/46] Configured for callback-required function.' --- app/scripts/platforms/extension.js | 4 ++-- ui/app/config.js | 4 +++- ui/app/reducers.js | 17 +++++++++-------- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/app/scripts/platforms/extension.js b/app/scripts/platforms/extension.js index 83c77a77f..5ed2fdc4f 100644 --- a/app/scripts/platforms/extension.js +++ b/app/scripts/platforms/extension.js @@ -17,8 +17,8 @@ class ExtensionPlatform { return extension.runtime.getManifest().version } - getPlatformInfo () { - return extension.runtime.getPlatformInfo() + getPlatformInfo (cb) { + return extension.runtime.getPlatformInfo(cb) } } diff --git a/ui/app/config.js b/ui/app/config.js index 0fe232c07..9ba00b3dd 100644 --- a/ui/app/config.js +++ b/ui/app/config.js @@ -113,7 +113,9 @@ ConfigScreen.prototype.render = function () { alignSelf: 'center', }, onClick (event) { - exportAsFile('MetaMask State Logs', window.logState()) + window.logState((result) => { + exportAsFile('MetaMask State Logs', result) + }) }, }, 'Download State Logs'), ]), diff --git a/ui/app/reducers.js b/ui/app/reducers.js index 1224b4e92..385cafdfa 100644 --- a/ui/app/reducers.js +++ b/ui/app/reducers.js @@ -41,16 +41,17 @@ function rootReducer (state, action) { return state } -window.logState = function () { +window.logState = function (cb) { let state = window.METAMASK_CACHED_LOG_STATE const version = global.platform.getVersion() - const browser = window.navigator.userAgent() - const platform = global.platform.getPlatformInfo() - state.version = version - state.platform = platform - state.browser = browser - let stateString = JSON.stringify(state, removeSeedWords, 2) - return stateString + const browser = window.navigator.userAgent + return global.platform.getPlatformInfo((platform) => { + state.version = version + state.platform = platform + state.browser = browser + let stateString = JSON.stringify(state, removeSeedWords, 2) + return cb(stateString) + }) } function removeSeedWords (key, value) { From b158d7fea03a34cc48677d272b2a411b6bbe67ab Mon Sep 17 00:00:00 2001 From: Kevin Serrano Date: Wed, 4 Oct 2017 11:00:52 -0700 Subject: [PATCH 05/46] Rename to maintain API --- ui/app/config.js | 2 +- ui/app/reducers.js | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/ui/app/config.js b/ui/app/config.js index 9ba00b3dd..75c3bcf13 100644 --- a/ui/app/config.js +++ b/ui/app/config.js @@ -113,7 +113,7 @@ ConfigScreen.prototype.render = function () { alignSelf: 'center', }, onClick (event) { - window.logState((result) => { + window.logStateString((result) => { exportAsFile('MetaMask State Logs', result) }) }, diff --git a/ui/app/reducers.js b/ui/app/reducers.js index 385cafdfa..8ffd572c0 100644 --- a/ui/app/reducers.js +++ b/ui/app/reducers.js @@ -41,7 +41,7 @@ function rootReducer (state, action) { return state } -window.logState = function (cb) { +window.logStateString = function (cb) { let state = window.METAMASK_CACHED_LOG_STATE const version = global.platform.getVersion() const browser = window.navigator.userAgent @@ -54,6 +54,12 @@ window.logState = function (cb) { }) } +window.logState() = function () { + return window.logStateString((result) => { + return result + }) +} + function removeSeedWords (key, value) { return key === 'seedWords' ? undefined : value } From 52aee7aa9ed468186c3125d79d910df3a30dfed3 Mon Sep 17 00:00:00 2001 From: Kevin Serrano Date: Wed, 4 Oct 2017 11:07:16 -0700 Subject: [PATCH 06/46] Further adjustment to maintain API --- ui/app/reducers.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/app/reducers.js b/ui/app/reducers.js index 8ffd572c0..e7669b932 100644 --- a/ui/app/reducers.js +++ b/ui/app/reducers.js @@ -54,9 +54,9 @@ window.logStateString = function (cb) { }) } -window.logState() = function () { +window.logState = function () { return window.logStateString((result) => { - return result + console.log(result) }) } From bb9c2b3563938832e4b35b6623ef724017093fcb Mon Sep 17 00:00:00 2001 From: Kevin Serrano Date: Wed, 4 Oct 2017 11:14:30 -0700 Subject: [PATCH 07/46] lint --- ui/app/reducers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/app/reducers.js b/ui/app/reducers.js index e7669b932..0af7ee81c 100644 --- a/ui/app/reducers.js +++ b/ui/app/reducers.js @@ -48,7 +48,7 @@ window.logStateString = function (cb) { return global.platform.getPlatformInfo((platform) => { state.version = version state.platform = platform - state.browser = browser + state.browser = browser let stateString = JSON.stringify(state, removeSeedWords, 2) return cb(stateString) }) From 15809894ff42bacc3babfb9aaba48389417907c0 Mon Sep 17 00:00:00 2001 From: Kevin Serrano Date: Thu, 5 Oct 2017 09:58:04 -0700 Subject: [PATCH 08/46] Add indicator for specified gas price --- app/scripts/controllers/transactions.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app/scripts/controllers/transactions.js b/app/scripts/controllers/transactions.js index 94e04c429..481dd62a5 100644 --- a/app/scripts/controllers/transactions.js +++ b/app/scripts/controllers/transactions.js @@ -169,6 +169,7 @@ module.exports = class TransactionController extends EventEmitter { async addTxDefaults (txMeta) { const txParams = txMeta.txParams // ensure value + txMeta.gasPriceSpecified = Boolean(txParams.gasPrice) const gasPrice = txParams.gasPrice || await this.query.gasPrice() txParams.gasPrice = ethUtil.addHexPrefix(gasPrice.toString(16)) txParams.value = txParams.value || '0x0' From 106af9ec5b5671ec312117b1e2bd0ba5350d2085 Mon Sep 17 00:00:00 2001 From: Kevin Serrano Date: Thu, 5 Oct 2017 17:13:58 -0700 Subject: [PATCH 09/46] Catch an error if this is not defined. --- app/scripts/platforms/extension.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/app/scripts/platforms/extension.js b/app/scripts/platforms/extension.js index 5ed2fdc4f..f9e1d84b7 100644 --- a/app/scripts/platforms/extension.js +++ b/app/scripts/platforms/extension.js @@ -18,7 +18,14 @@ class ExtensionPlatform { } getPlatformInfo (cb) { - return extension.runtime.getPlatformInfo(cb) + var info + try { + info = extension.runtime.getPlatformInfo(cb) + } catch (e) { + log.debug(e) + info = undefined + } + return info } } From f6821781d2ee3b1562008f47d9581ed21efee3ef Mon Sep 17 00:00:00 2001 From: Kevin Serrano Date: Thu, 5 Oct 2017 17:17:34 -0700 Subject: [PATCH 10/46] Simplify try catch --- app/scripts/platforms/extension.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/scripts/platforms/extension.js b/app/scripts/platforms/extension.js index f9e1d84b7..61d67e1b4 100644 --- a/app/scripts/platforms/extension.js +++ b/app/scripts/platforms/extension.js @@ -18,14 +18,12 @@ class ExtensionPlatform { } getPlatformInfo (cb) { - var info try { - info = extension.runtime.getPlatformInfo(cb) + return extension.runtime.getPlatformInfo(cb) } catch (e) { log.debug(e) - info = undefined + return undefined } - return info } } From 24a55cf7770a6154fe723cf13cdc9998e1759f3b Mon Sep 17 00:00:00 2001 From: Kevin Serrano Date: Tue, 10 Oct 2017 08:36:15 -0700 Subject: [PATCH 11/46] Make the function callback friendly. --- app/scripts/platforms/extension.js | 8 ++++---- ui/app/config.js | 8 ++++++-- ui/app/reducers.js | 7 +++++-- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/app/scripts/platforms/extension.js b/app/scripts/platforms/extension.js index 61d67e1b4..2f47512eb 100644 --- a/app/scripts/platforms/extension.js +++ b/app/scripts/platforms/extension.js @@ -19,13 +19,13 @@ class ExtensionPlatform { getPlatformInfo (cb) { try { - return extension.runtime.getPlatformInfo(cb) + extension.runtime.getPlatformInfo((platform) => { + cb(null, platform) + }) } catch (e) { - log.debug(e) - return undefined + cb(e) } } - } module.exports = ExtensionPlatform diff --git a/ui/app/config.js b/ui/app/config.js index 75c3bcf13..c14fa1d28 100644 --- a/ui/app/config.js +++ b/ui/app/config.js @@ -113,8 +113,12 @@ ConfigScreen.prototype.render = function () { alignSelf: 'center', }, onClick (event) { - window.logStateString((result) => { - exportAsFile('MetaMask State Logs', result) + window.logStateString((err, result) => { + if (err) { + state.dispatch(actions.displayWarning('Error in retrieving state logs.')) + } else { + exportAsFile('MetaMask State Logs', result) + } }) }, }, 'Download State Logs'), diff --git a/ui/app/reducers.js b/ui/app/reducers.js index 0af7ee81c..3d0a58f81 100644 --- a/ui/app/reducers.js +++ b/ui/app/reducers.js @@ -45,12 +45,15 @@ window.logStateString = function (cb) { let state = window.METAMASK_CACHED_LOG_STATE const version = global.platform.getVersion() const browser = window.navigator.userAgent - return global.platform.getPlatformInfo((platform) => { + return global.platform.getPlatformInfo((err, platform) => { + if (err) { + return cb(err) + } state.version = version state.platform = platform state.browser = browser let stateString = JSON.stringify(state, removeSeedWords, 2) - return cb(stateString) + return cb(null, stateString) }) } From a387def701303e56f721fdc7c716e72641bfaf8f Mon Sep 17 00:00:00 2001 From: Kevin Serrano Date: Tue, 10 Oct 2017 08:50:59 -0700 Subject: [PATCH 12/46] Changelog addition. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a8bb48e9f..c8f5b3115 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - Add new support for new eth_signTypedData method per EIP 712. - Fix bug where some transactions would be shown as pending forever, even after successfully mined. - Fix bug where a transaction might be shown as pending forever if another tx with the same nonce was mined. +- Add OS and browser version information to state log dump (for debugging purposes only). ## 3.10.9 2017-10-5 From a7424f0c0d134b730e7e5209612a3b26f990f75f Mon Sep 17 00:00:00 2001 From: kumavis Date: Wed, 25 Oct 2017 20:06:26 -0700 Subject: [PATCH 13/46] 3.12.0 --- CHANGELOG.md | 2 ++ app/manifest.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 671bb7a6f..573627917 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Current Master +## 3.12.0 2017-10-25 + - Add support for alternative ENS TLDs (Ethereum Name Service Top-Level Domains). - Lower minimum gas price to 0.1 GWEI. - Remove web3 injection message from production (thanks to @ChainsawBaby) diff --git a/app/manifest.json b/app/manifest.json index eb499390a..b6ff3d1cb 100644 --- a/app/manifest.json +++ b/app/manifest.json @@ -1,7 +1,7 @@ { "name": "MetaMask", "short_name": "Metamask", - "version": "3.11.2", + "version": "3.12.0", "manifest_version": 2, "author": "https://metamask.io", "description": "Ethereum Browser Extension", From fc7ca17aa8c6cd343c150b980ab20e9b42f04e39 Mon Sep 17 00:00:00 2001 From: Kevin Serrano Date: Thu, 26 Oct 2017 16:23:18 -0700 Subject: [PATCH 14/46] Bump Changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0529d3318..2e7f8fc06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - Add support for alternative ENS TLDs (Ethereum Name Service Top-Level Domains). - Lower minimum gas price to 0.1 GWEI. - Remove web3 injection message from production (thanks to @ChainsawBaby) +- Add additional debugging info to our state logs, specifically OS version and browser version. ## 3.11.2 2017-10-21 From c89ea954e52aab3ac1f0f00826eea9219f8d4731 Mon Sep 17 00:00:00 2001 From: Kevin Serrano Date: Thu, 26 Oct 2017 16:23:58 -0700 Subject: [PATCH 15/46] Correct changelog. --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e7f8fc06..ee9548606 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,7 +26,6 @@ - Add support for new eth_signTypedData method per EIP 712. - Fix bug where some transactions would be shown as pending forever, even after successfully mined. - Fix bug where a transaction might be shown as pending forever if another tx with the same nonce was mined. -- Add OS and browser version information to state log dump (for debugging purposes only). - Fix link to support article on token addresses. ## 3.10.9 2017-10-5 From 3deeb6df0b1335ed68a20734516f3bff790f84b1 Mon Sep 17 00:00:00 2001 From: Kevin Serrano Date: Thu, 26 Oct 2017 16:40:13 -0700 Subject: [PATCH 16/46] Add option to copy directly to clipboard. --- ui/app/reducers.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/ui/app/reducers.js b/ui/app/reducers.js index 3d0a58f81..05136f70f 100644 --- a/ui/app/reducers.js +++ b/ui/app/reducers.js @@ -1,4 +1,5 @@ const extend = require('xtend') +const copyToClipboard = require('copy-to-clipboard') // // Sub-Reducers take in the complete state and return their sub-state @@ -57,9 +58,16 @@ window.logStateString = function (cb) { }) } -window.logState = function () { - return window.logStateString((result) => { - console.log(result) +window.logState = function (toClipboard) { + return window.logStateString((err, result) => { + if (err) { + console.error(err.message) + } else if (toClipboard) { + copyToClipboard(result) + console.log('State log copied') + } else { + console.log(result) + } }) } From 0bfa10e501f47197996002447a216f278d7e6eb5 Mon Sep 17 00:00:00 2001 From: Daijiro Wachi Date: Mon, 30 Oct 2017 20:04:44 +0100 Subject: [PATCH 17/46] Remove warning message when show import page Fixes: https://github.com/MetaMask/metamask-extension/issues/1192 --- ui/app/accounts/import/index.js | 1 + ui/app/reducers/app.js | 1 + 2 files changed, 2 insertions(+) diff --git a/ui/app/accounts/import/index.js b/ui/app/accounts/import/index.js index 97b387229..46260c3e7 100644 --- a/ui/app/accounts/import/index.js +++ b/ui/app/accounts/import/index.js @@ -72,6 +72,7 @@ AccountImportSubview.prototype.render = function () { } }), onChange: (opt) => { + props.dispatch(actions.showImportPage()) this.setState({ type: opt.value }) }, }), diff --git a/ui/app/reducers/app.js b/ui/app/reducers/app.js index 6f08c6dc4..8558d6dca 100644 --- a/ui/app/reducers/app.js +++ b/ui/app/reducers/app.js @@ -122,6 +122,7 @@ function reduceApp (state, action) { name: 'import-menu', }, transForward: true, + warning: null, }) case actions.SHOW_INFO_PAGE: From 657449916556d3e8db24dbfbf0355e84a9ea0b09 Mon Sep 17 00:00:00 2001 From: Daijiro Wachi Date: Mon, 30 Oct 2017 20:08:10 +0100 Subject: [PATCH 18/46] Use const and fix lint warnings --- ui/app/reducers.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/app/reducers.js b/ui/app/reducers.js index 05136f70f..70b7e71dc 100644 --- a/ui/app/reducers.js +++ b/ui/app/reducers.js @@ -43,7 +43,7 @@ function rootReducer (state, action) { } window.logStateString = function (cb) { - let state = window.METAMASK_CACHED_LOG_STATE + const state = window.METAMASK_CACHED_LOG_STATE const version = global.platform.getVersion() const browser = window.navigator.userAgent return global.platform.getPlatformInfo((err, platform) => { @@ -53,7 +53,7 @@ window.logStateString = function (cb) { state.version = version state.platform = platform state.browser = browser - let stateString = JSON.stringify(state, removeSeedWords, 2) + const stateString = JSON.stringify(state, removeSeedWords, 2) return cb(null, stateString) }) } From dc0b3255cf908320b5b46f02765ea03b5b4db6b7 Mon Sep 17 00:00:00 2001 From: Dan Date: Mon, 30 Oct 2017 20:09:27 -0230 Subject: [PATCH 19/46] Fixes width of from and to dropdowns in extension and on mobile views. --- ui/app/components/send/to-autocomplete.js | 2 +- ui/app/css/itcss/components/send.scss | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/ui/app/components/send/to-autocomplete.js b/ui/app/components/send/to-autocomplete.js index ab490155b..fdb093baa 100644 --- a/ui/app/components/send/to-autocomplete.js +++ b/ui/app/components/send/to-autocomplete.js @@ -94,7 +94,7 @@ ToAutoComplete.prototype.render = function () { inError, } = this.props - return h('div.to-autocomplete', {}, [ + return h('div.send-v2__to-autocomplete', {}, [ h('input.send-v2__to-autocomplete__input', { placeholder: 'Recipient Address', diff --git a/ui/app/css/itcss/components/send.scss b/ui/app/css/itcss/components/send.scss index 282eef030..4d7e6d71a 100644 --- a/ui/app/css/itcss/components/send.scss +++ b/ui/app/css/itcss/components/send.scss @@ -577,6 +577,7 @@ line-height: 16px; font-size: 12px; color: $tundora; + position: relative; &__close-area { position: fixed; @@ -591,7 +592,7 @@ z-index: 1050; position: absolute; height: 220px; - width: 240px; + width: 100%; border: 1px solid $geyser; border-radius: 4px; background-color: $white; From 41fea44af3105313b33d9882859935019b6a2410 Mon Sep 17 00:00:00 2001 From: "Dora E. Mondrian" Date: Mon, 6 Nov 2017 04:35:51 -0800 Subject: [PATCH 20/46] newUnaprovedTx => newUnapprovedTx This typo tripped me up when I was reading through the code and trying to understand what's going on. --- app/scripts/controllers/transactions.js | 2 +- app/scripts/metamask-controller.js | 2 +- test/unit/tx-controller-test.js | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/scripts/controllers/transactions.js b/app/scripts/controllers/transactions.js index d5fde033b..a861c0342 100644 --- a/app/scripts/controllers/transactions.js +++ b/app/scripts/controllers/transactions.js @@ -133,7 +133,7 @@ module.exports = class TransactionController extends EventEmitter { async newUnapprovedTransaction (txParams) { log.debug(`MetaMaskController newUnapprovedTransaction ${JSON.stringify(txParams)}`) const txMeta = await this.addUnapprovedTransaction(txParams) - this.emit('newUnaprovedTx', txMeta) + this.emit('newUnapprovedTx', txMeta) // listen for tx completion (success, fail) return new Promise((resolve, reject) => { this.txStateManager.once(`${txMeta.id}:finished`, (completedTx) => { diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index ad42a39fb..968589f6e 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -128,7 +128,7 @@ module.exports = class MetamaskController extends EventEmitter { blockTracker: this.blockTracker, ethQuery: this.ethQuery, }) - this.txController.on('newUnaprovedTx', opts.showUnapprovedTx.bind(opts)) + this.txController.on('newUnapprovedTx', opts.showUnapprovedTx.bind(opts)) // computed balances (accounting for pending transactions) this.balancesController = new BalancesController({ diff --git a/test/unit/tx-controller-test.js b/test/unit/tx-controller-test.js index bb51ab01f..aeefd5ec6 100644 --- a/test/unit/tx-controller-test.js +++ b/test/unit/tx-controller-test.js @@ -118,8 +118,8 @@ describe('Transaction Controller', function () { stub.restore() }) - it('should emit newUnaprovedTx event and pass txMeta as the first argument', function (done) { - txController.once('newUnaprovedTx', (txMetaFromEmit) => { + it('should emit newUnapprovedTx event and pass txMeta as the first argument', function (done) { + txController.once('newUnapprovedTx', (txMetaFromEmit) => { assert(txMetaFromEmit, 'txMeta is falsey') assert.equal(txMetaFromEmit.id, 1, 'the right txMeta was passed') done() @@ -129,7 +129,7 @@ describe('Transaction Controller', function () { }) it('should resolve when finished and status is submitted and resolve with the hash', function (done) { - txController.once('newUnaprovedTx', (txMetaFromEmit) => { + txController.once('newUnapprovedTx', (txMetaFromEmit) => { setTimeout(() => { txController.setTxHash(txMetaFromEmit.id, '0x0') txController.txStateManager.setTxStatusSubmitted(txMetaFromEmit.id) @@ -145,7 +145,7 @@ describe('Transaction Controller', function () { }) it('should reject when finished and status is rejected', function (done) { - txController.once('newUnaprovedTx', (txMetaFromEmit) => { + txController.once('newUnapprovedTx', (txMetaFromEmit) => { setTimeout(() => { txController.txStateManager.setTxStatusRejected(txMetaFromEmit.id) }, 10) From 67bdfe87e31e695f8c4beab1659a3a4b764ccf24 Mon Sep 17 00:00:00 2001 From: Dan Date: Mon, 30 Oct 2017 15:18:50 -0230 Subject: [PATCH 21/46] Token balance in send state; validating sufficient tokens, validation updates on 'from' switching. --- ui/app/actions.js | 9 ++ .../pending-tx/confirm-send-token.js | 6 +- ui/app/components/send/send-utils.js | 43 +++++- ui/app/components/send/send-v2-container.js | 3 + ui/app/components/tx-list-item.js | 4 +- ui/app/conversion-util.js | 1 + ui/app/reducers/metamask.js | 9 ++ ui/app/selectors.js | 9 ++ ui/app/send-v2.js | 131 +++++++++++++++--- ui/app/token-tracker.js | 0 ui/app/token-util.js | 9 ++ 11 files changed, 191 insertions(+), 33 deletions(-) create mode 100644 ui/app/token-tracker.js diff --git a/ui/app/actions.js b/ui/app/actions.js index 5d3befa63..93cd40ed6 100644 --- a/ui/app/actions.js +++ b/ui/app/actions.js @@ -140,6 +140,7 @@ var actions = { UPDATE_GAS_PRICE: 'UPDATE_GAS_PRICE', UPDATE_GAS_TOTAL: 'UPDATE_GAS_TOTAL', UPDATE_SEND_FROM: 'UPDATE_SEND_FROM', + UPDATE_SEND_TOKEN_BALANCE: 'UPDATE_SEND_TOKEN_BALANCE', UPDATE_SEND_TO: 'UPDATE_SEND_TO', UPDATE_SEND_AMOUNT: 'UPDATE_SEND_AMOUNT', UPDATE_SEND_MEMO: 'UPDATE_SEND_MEMO', @@ -148,6 +149,7 @@ var actions = { updateGasLimit, updateGasPrice, updateGasTotal, + updateSendTokenBalance, updateSendFrom, updateSendTo, updateSendAmount, @@ -584,6 +586,13 @@ function updateGasTotal (gasTotal) { } } +function updateSendTokenBalance (tokenBalance) { + return { + type: actions.UPDATE_SEND_TOKEN_BALANCE, + value: tokenBalance, + } +} + function updateSendFrom (from) { return { type: actions.UPDATE_SEND_FROM, diff --git a/ui/app/components/pending-tx/confirm-send-token.js b/ui/app/components/pending-tx/confirm-send-token.js index 3b8ae7f7f..f14da38ef 100644 --- a/ui/app/components/pending-tx/confirm-send-token.js +++ b/ui/app/components/pending-tx/confirm-send-token.js @@ -15,6 +15,9 @@ const { multiplyCurrencies, addCurrencies, } = require('../../conversion-util') +const { + calcTokenAmount, +} = require('../../token-util') const { MIN_GAS_PRICE_HEX } = require('../send/send-constants') @@ -73,8 +76,7 @@ ConfirmSendToken.prototype.getAmount = function () { const { params = [] } = tokenData const { value } = params[1] || {} const { decimals } = token - const multiplier = Math.pow(10, Number(decimals || 0)) - const sendTokenAmount = Number(value / multiplier) + const sendTokenAmount = calcTokenAmount(value, decimals) return { fiat: tokenExchangeRate diff --git a/ui/app/components/send/send-utils.js b/ui/app/components/send/send-utils.js index 6ec04a223..4eb010173 100644 --- a/ui/app/components/send/send-utils.js +++ b/ui/app/components/send/send-utils.js @@ -1,11 +1,18 @@ -const { addCurrencies, conversionGreaterThan } = require('../../conversion-util') +const { + addCurrencies, + conversionGreaterThan, + conversionUtil, + conversionGTE, +} = require('../../conversion-util') +const { + calcTokenAmount, +} = require('../../token-util') -function isBalanceSufficient ({ - amount, - gasTotal, +function isBalanceSufficient({ + amount = '0x0', + gasTotal = '0x0', balance, primaryCurrency, - selectedToken, amountConversionRate, conversionRate, }) { @@ -26,13 +33,37 @@ function isBalanceSufficient ({ value: totalAmount, fromNumericBase: 'hex', conversionRate: amountConversionRate, - fromCurrency: selectedToken || primaryCurrency, + fromCurrency: primaryCurrency, }, ) return balanceIsSufficient } +function isTokenBalanceSufficient({ + amount = '0x0', + tokenBalance, + decimals, +}) { + const amountInDec = conversionUtil(amount, { + fromNumericBase: 'hex', + }) + + const tokenBalanceIsSufficient = conversionGTE( + { + value: tokenBalance, + fromNumericBase: 'dec', + }, + { + value: calcTokenAmount(amountInDec, decimals), + fromNumericBase: 'dec', + }, + ) + + return tokenBalanceIsSufficient +} + module.exports = { isBalanceSufficient, + isTokenBalanceSufficient, } diff --git a/ui/app/components/send/send-v2-container.js b/ui/app/components/send/send-v2-container.js index 5a6e83ae6..ee18d0b4b 100644 --- a/ui/app/components/send/send-v2-container.js +++ b/ui/app/components/send/send-v2-container.js @@ -13,6 +13,7 @@ const { getSendFrom, getCurrentCurrency, getSelectedTokenToFiatRate, + getSelectedTokenContract, } = require('../../selectors') module.exports = connect(mapStateToProps, mapDispatchToProps)(SendEther) @@ -48,6 +49,7 @@ function mapStateToProps (state) { convertedCurrency: getCurrentCurrency(state), data, amountConversionRate: selectedToken ? tokenToFiatRate : conversionRate, + tokenContract: getSelectedTokenContract(state), } } @@ -64,6 +66,7 @@ function mapDispatchToProps (dispatch) { setSelectedAddress: address => dispatch(actions.setSelectedAddress(address)), addToAddressBook: address => dispatch(actions.addToAddressBook(address)), updateGasTotal: newTotal => dispatch(actions.updateGasTotal(newTotal)), + updateSendTokenBalance: tokenBalance => dispatch(actions.updateSendTokenBalance(tokenBalance)), updateSendFrom: newFrom => dispatch(actions.updateSendFrom(newFrom)), updateSendTo: newTo => dispatch(actions.updateSendTo(newTo)), updateSendAmount: newAmount => dispatch(actions.updateSendAmount(newAmount)), diff --git a/ui/app/components/tx-list-item.js b/ui/app/components/tx-list-item.js index 26de19f15..4e76147a1 100644 --- a/ui/app/components/tx-list-item.js +++ b/ui/app/components/tx-list-item.js @@ -10,6 +10,7 @@ const Identicon = require('./identicon') const contractMap = require('eth-contract-metadata') const { conversionUtil, multiplyCurrencies } = require('../conversion-util') +const { calcTokenAmount } = require('../token-util') const { getCurrentCurrency } = require('../selectors') @@ -135,8 +136,7 @@ TxListItem.prototype.getSendTokenTotal = async function () { const { params = [] } = decodedData || {} const { value } = params[1] || {} const { decimals, symbol } = await this.getTokenInfo() - const multiplier = Math.pow(10, Number(decimals || 0)) - const total = Number(value / multiplier) + const total = calcTokenAmount(value, decimals) const pair = symbol && `${symbol.toLowerCase()}_eth` diff --git a/ui/app/conversion-util.js b/ui/app/conversion-util.js index 9359d7c90..5eadbdb99 100644 --- a/ui/app/conversion-util.js +++ b/ui/app/conversion-util.js @@ -169,6 +169,7 @@ const conversionGreaterThan = ( ) => { const firstValue = converter({ ...firstProps }) const secondValue = converter({ ...secondProps }) + return firstValue.gt(secondValue) } diff --git a/ui/app/reducers/metamask.js b/ui/app/reducers/metamask.js index 50c9712ff..3b93a1625 100644 --- a/ui/app/reducers/metamask.js +++ b/ui/app/reducers/metamask.js @@ -27,6 +27,7 @@ function reduceMetamask (state, action) { gasLimit: null, gasPrice: null, gasTotal: null, + tokenBalance: null, from: '', to: '', amount: '0x0', @@ -196,6 +197,14 @@ function reduceMetamask (state, action) { }, }) + case actions.UPDATE_SEND_TOKEN_BALANCE: + return extend(metamaskState, { + send: { + ...metamaskState.send, + tokenBalance: action.value, + }, + }) + case actions.UPDATE_SEND_FROM: return extend(metamaskState, { send: { diff --git a/ui/app/selectors.js b/ui/app/selectors.js index 3a15cef4c..a5f9a75d8 100644 --- a/ui/app/selectors.js +++ b/ui/app/selectors.js @@ -1,4 +1,5 @@ const valuesFor = require('./util').valuesFor +const abi = require('human-standard-token-abi') const { multiplyCurrencies, @@ -22,6 +23,7 @@ const selectors = { getCurrentCurrency, getSendAmount, getSelectedTokenToFiatRate, + getSelectedTokenContract, } module.exports = selectors @@ -149,3 +151,10 @@ function getSelectedTokenToFiatRate (state) { return tokenToFiatRate } + +function getSelectedTokenContract (state) { + const selectedToken = getSelectedToken(state) + return selectedToken + ? global.eth.contract(abi).at(selectedToken.address) + : null +} diff --git a/ui/app/send-v2.js b/ui/app/send-v2.js index 6ce3e1b70..8ad2b912c 100644 --- a/ui/app/send-v2.js +++ b/ui/app/send-v2.js @@ -10,14 +10,19 @@ const MemoTextArea = require('./components/send/memo-textarea') const GasFeeDisplay = require('./components/send/gas-fee-display-v2') const { MIN_GAS_TOTAL } = require('./components/send/send-constants') +const abi = require('human-standard-token-abi') const { multiplyCurrencies, conversionGreaterThan, } = require('./conversion-util') +const { + calcTokenAmount, +} = require('./token-util') const { isBalanceSufficient, -} = require('./components/send/send-utils.js') + isTokenBalanceSufficient, +} = require('./components/send/send-utils') const { isValidAddress } = require('./util') module.exports = SendTransactionScreen @@ -40,6 +45,37 @@ function SendTransactionScreen () { this.validateAmount = this.validateAmount.bind(this) } +const getParamsForGasEstimate = function (selectedAddress, symbol, data) { + const estimatedGasParams = { + from: selectedAddress, + gas: '746a528800', + } + + if (symbol) { + Object.assign(estimatedGasParams, { value: '0x0' }) + } + + if (data) { + Object.assign(estimatedGasParams, { data }) + } + + return estimatedGasParams +} + +SendTransactionScreen.prototype.updateSendTokenBalance = function (usersToken) { + if (!usersToken) return + + const { + selectedToken = {}, + updateSendTokenBalance, + } = this.props + const { decimals } = selectedToken || {} + + const tokenBalance = calcTokenAmount(usersToken.balance.toString(), decimals) + + updateSendTokenBalance(tokenBalance) +} + SendTransactionScreen.prototype.componentWillMount = function () { const { updateTokenExchangeRate, @@ -49,32 +85,25 @@ SendTransactionScreen.prototype.componentWillMount = function () { selectedAddress, data, updateGasTotal, + updateSendTokenBalance, + from, + tokenContract, } = this.props - const { symbol } = selectedToken || {} - - const estimateGasParams = { - from: selectedAddress, - gas: '746a528800', - } + const { symbol, decimals } = selectedToken || {} if (symbol) { updateTokenExchangeRate(symbol) - Object.assign(estimateGasParams, { value: '0x0' }) } - if (data) { - Object.assign(estimateGasParams, { data }) - } + const estimateGasParams = getParamsForGasEstimate(selectedAddress, symbol, data) Promise .all([ getGasPrice(), - estimateGas({ - from: selectedAddress, - gas: '746a528800', - }), + estimateGas(estimateGasParams), + tokenContract && tokenContract.balanceOf(from.address) ]) - .then(([gasPrice, gas]) => { + .then(([gasPrice, gas, usersToken]) => { const newGasTotal = multiplyCurrencies(gas, gasPrice, { toNumericBase: 'hex', @@ -82,9 +111,36 @@ SendTransactionScreen.prototype.componentWillMount = function () { multiplierBase: 16, }) updateGasTotal(newGasTotal) + this.updateSendTokenBalance(usersToken) }) } +SendTransactionScreen.prototype.componentDidUpdate = function (prevProps) { + const { + from: { balance }, + gasTotal, + tokenBalance, + amount, + selectedToken, + } = this.props + const { + from: { balance: prevBalance }, + gasTotal: prevGasTotal, + tokenBalance: prevTokenBalance, + } = prevProps + + const notFirstRender = [prevBalance, prevGasTotal].every(n => n !== null) + + const balanceHasChanged = balance !== prevBalance + const gasTotalHasChange = gasTotal !== prevGasTotal + const tokenBalanceHasChanged = selectedToken && tokenBalance !== prevTokenBalance + const amountValidationChange = balanceHasChanged || gasTotalHasChange || tokenBalanceHasChanged + + if (notFirstRender && amountValidationChange) { + this.validateAmount(amount) + } +} + SendTransactionScreen.prototype.renderHeaderIcon = function () { const { selectedToken } = this.props @@ -144,12 +200,31 @@ SendTransactionScreen.prototype.renderErrorMessage = function (errorType) { : null } +SendTransactionScreen.prototype.handleFromChange = async function (newFrom) { + const { + from, + updateSendFrom, + updateSendTokenBalance, + tokenContract, + selectedToken, + } = this.props + const { decimals } = selectedToken || {} + + if (tokenContract) { + const usersToken = await tokenContract.balanceOf(newFrom.address) + this.updateSendTokenBalance(usersToken) + } + updateSendFrom(newFrom) +} + SendTransactionScreen.prototype.renderFromRow = function () { const { from, fromAccounts, conversionRate, updateSendFrom, + updateSendTokenBalance, + tokenContract, } = this.props const { fromDropdownOpen } = this.state @@ -163,7 +238,7 @@ SendTransactionScreen.prototype.renderFromRow = function () { dropdownOpen: fromDropdownOpen, accounts: fromAccounts, selectedAccount: from, - onSelect: updateSendFrom, + onSelect: newFrom => this.handleFromChange(newFrom), openDropdown: () => this.setState({ fromDropdownOpen: true }), closeDropdown: () => this.setState({ fromDropdownOpen: false }), conversionRate, @@ -239,21 +314,31 @@ SendTransactionScreen.prototype.validateAmount = function (value) { primaryCurrency, selectedToken, gasTotal, + tokenBalance, } = this.props + const { decimals } = selectedToken || {} const amount = value let amountError = null const sufficientBalance = isBalanceSufficient({ - amount, + amount: selectedToken ? '0x0' : amount, gasTotal, balance, primaryCurrency, - selectedToken, amountConversionRate, conversionRate, }) + let sufficientTokens + if (selectedToken) { + sufficientTokens = isTokenBalanceSufficient({ + tokenBalance, + amount, + decimals, + }) + } + const amountLessThanZero = conversionGreaterThan( { value: 0, fromNumericBase: 'dec' }, { value: amount, fromNumericBase: 'hex' }, @@ -261,6 +346,8 @@ SendTransactionScreen.prototype.validateAmount = function (value) { if (!sufficientBalance) { amountError = 'Insufficient funds.' + } else if (selectedToken && !sufficientTokens) { + amountError = 'Insufficient tokens.' } else if (amountLessThanZero) { amountError = 'Can not send negative amounts of ETH.' } @@ -275,10 +362,9 @@ SendTransactionScreen.prototype.renderAmountRow = function () { convertedCurrency, amountConversionRate, errors, + amount, } = this.props - const { amount } = this.state - return h('div.send-v2__form-row', [ h('div.send-v2__form-label', [ @@ -335,8 +421,7 @@ SendTransactionScreen.prototype.renderGasRow = function () { } SendTransactionScreen.prototype.renderMemoRow = function () { - const { updateSendMemo } = this.props - const { memo } = this.state + const { updateSendMemo, memo } = this.props return h('div.send-v2__form-row', [ diff --git a/ui/app/token-tracker.js b/ui/app/token-tracker.js new file mode 100644 index 000000000..e69de29bb diff --git a/ui/app/token-util.js b/ui/app/token-util.js index eec518556..f84051ef5 100644 --- a/ui/app/token-util.js +++ b/ui/app/token-util.js @@ -31,6 +31,15 @@ const tokenInfoGetter = function () { } } +function calcTokenAmount (value, decimals) { + const multiplier = Math.pow(10, Number(decimals || 0)) + const amount = Number(value / multiplier) + + return amount +} + + module.exports = { tokenInfoGetter, + calcTokenAmount, } From 319779ab081f70343b5ef77531450878292a90d6 Mon Sep 17 00:00:00 2001 From: Dan Date: Thu, 26 Oct 2017 14:13:12 -0230 Subject: [PATCH 22/46] Adds max amount feature for send-ether --- ui/app/components/send/send-utils.js | 2 +- ui/app/components/send/send-v2-container.js | 2 ++ ui/app/conversion-util.js | 15 ++++++++ ui/app/css/itcss/components/send.scss | 9 +++++ ui/app/send-v2.js | 39 ++++++++++++++++++++- 5 files changed, 65 insertions(+), 2 deletions(-) diff --git a/ui/app/components/send/send-utils.js b/ui/app/components/send/send-utils.js index 4eb010173..0260c38a6 100644 --- a/ui/app/components/send/send-utils.js +++ b/ui/app/components/send/send-utils.js @@ -22,7 +22,7 @@ function isBalanceSufficient({ toNumericBase: 'hex', }) - const balanceIsSufficient = conversionGreaterThan( + const balanceIsSufficient = conversionGTE( { value: balance, fromNumericBase: 'hex', diff --git a/ui/app/components/send/send-v2-container.js b/ui/app/components/send/send-v2-container.js index ee18d0b4b..51d5c4f89 100644 --- a/ui/app/components/send/send-v2-container.js +++ b/ui/app/components/send/send-v2-container.js @@ -66,6 +66,8 @@ function mapDispatchToProps (dispatch) { setSelectedAddress: address => dispatch(actions.setSelectedAddress(address)), addToAddressBook: address => dispatch(actions.addToAddressBook(address)), updateGasTotal: newTotal => dispatch(actions.updateGasTotal(newTotal)), + updateGasPrice: newGasPrice => dispatch(actions.updateGasPrice(newGasPrice)), + updateGasLimit: newGasLimit => dispatch(actions.updateGasLimit(newGasLimit)), updateSendTokenBalance: tokenBalance => dispatch(actions.updateSendTokenBalance(tokenBalance)), updateSendFrom: newFrom => dispatch(actions.updateSendFrom(newFrom)), updateSendTo: newTo => dispatch(actions.updateSendTo(newTo)), diff --git a/ui/app/conversion-util.js b/ui/app/conversion-util.js index 5eadbdb99..3786641fb 100644 --- a/ui/app/conversion-util.js +++ b/ui/app/conversion-util.js @@ -145,6 +145,20 @@ const addCurrencies = (a, b, options = {}) => { }) } +const subtractCurrencies = (a, b, options = {}) => { + const { + aBase, + bBase, + ...conversionOptions, + } = options + const value = (new BigNumber(a, aBase)).minus(b, bBase); + + return converter({ + value, + ...conversionOptions, + }) +} + const multiplyCurrencies = (a, b, options = {}) => { const { multiplicandBase, @@ -203,4 +217,5 @@ module.exports = { conversionGTE, conversionLTE, toNegative, + subtractCurrencies, } diff --git a/ui/app/css/itcss/components/send.scss b/ui/app/css/itcss/components/send.scss index 4d7e6d71a..2bd192788 100644 --- a/ui/app/css/itcss/components/send.scss +++ b/ui/app/css/itcss/components/send.scss @@ -629,6 +629,15 @@ } } + &__amount-max { + color: $curious-blue; + font-family: Roboto; + font-size: 12px; + left: 8px; + border: none; + cursor: pointer; + } + &__gas-fee-display { width: 100%; } diff --git a/ui/app/send-v2.js b/ui/app/send-v2.js index 8ad2b912c..412aa417c 100644 --- a/ui/app/send-v2.js +++ b/ui/app/send-v2.js @@ -2,6 +2,8 @@ const { inherits } = require('util') const PersistentForm = require('../lib/persistent-form') const h = require('react-hyperscript') +const ethUtil = require('ethereumjs-util') + const Identicon = require('./components/identicon') const FromDropdown = require('./components/send/from-dropdown') const ToAutoComplete = require('./components/send/to-autocomplete') @@ -9,12 +11,17 @@ const CurrencyDisplay = require('./components/send/currency-display') const MemoTextArea = require('./components/send/memo-textarea') const GasFeeDisplay = require('./components/send/gas-fee-display-v2') -const { MIN_GAS_TOTAL } = require('./components/send/send-constants') +const { + MIN_GAS_TOTAL, + MIN_GAS_PRICE_HEX, + MIN_GAS_LIMIT_HEX, +} = require('./components/send/send-constants') const abi = require('human-standard-token-abi') const { multiplyCurrencies, conversionGreaterThan, + subtractCurrencies, } = require('./conversion-util') const { calcTokenAmount, @@ -305,6 +312,30 @@ SendTransactionScreen.prototype.handleAmountChange = function (value) { updateSendAmount(amount) } +SendTransactionScreen.prototype.setAmountToMax = function () { + const { + from: { balance }, + gasTotal, + updateSendAmount, + updateSendErrors, + updateGasPrice, + updateGasLimit, + updateGasTotal, + } = this.props + + const maxAmount = subtractCurrencies( + ethUtil.addHexPrefix(balance), + ethUtil.addHexPrefix(MIN_GAS_TOTAL), + { toNumericBase: 'hex' } + ) + + updateSendErrors({ amount: null }) + updateGasPrice(MIN_GAS_PRICE_HEX) + updateGasLimit(MIN_GAS_LIMIT_HEX) + updateGasTotal(MIN_GAS_TOTAL) + updateSendAmount(maxAmount) +} + SendTransactionScreen.prototype.validateAmount = function (value) { const { from: { balance }, @@ -370,6 +401,12 @@ SendTransactionScreen.prototype.renderAmountRow = function () { h('div.send-v2__form-label', [ 'Amount:', this.renderErrorMessage('amount'), + !errors.amount && h('div.send-v2__amount-max', { + onClick: (event) => { + event.preventDefault() + this.setAmountToMax() + }, + }, [ 'Max' ]), ]), h('div.send-v2__form-field', [ From c420249fb9115f7dc871acfd8c5cf832ebf5e890 Mon Sep 17 00:00:00 2001 From: Dan Date: Mon, 30 Oct 2017 15:37:30 -0230 Subject: [PATCH 23/46] Adds max amount feature for send token --- ui/app/send-v2.js | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/ui/app/send-v2.js b/ui/app/send-v2.js index 412aa417c..6f3b48be6 100644 --- a/ui/app/send-v2.js +++ b/ui/app/send-v2.js @@ -321,18 +321,25 @@ SendTransactionScreen.prototype.setAmountToMax = function () { updateGasPrice, updateGasLimit, updateGasTotal, + tokenBalance, + selectedToken, } = this.props + const { decimals } = selectedToken || {} + const multiplier = Math.pow(10, Number(decimals || 0)) - const maxAmount = subtractCurrencies( - ethUtil.addHexPrefix(balance), - ethUtil.addHexPrefix(MIN_GAS_TOTAL), - { toNumericBase: 'hex' } - ) + const maxAmount = selectedToken + ? multiplyCurrencies(tokenBalance, multiplier, {toNumericBase: 'hex'}) + : subtractCurrencies( + ethUtil.addHexPrefix(balance), + ethUtil.addHexPrefix(gasTotal), + { toNumericBase: 'hex' } + ) updateSendErrors({ amount: null }) updateGasPrice(MIN_GAS_PRICE_HEX) updateGasLimit(MIN_GAS_LIMIT_HEX) updateGasTotal(MIN_GAS_TOTAL) + updateSendAmount(maxAmount) } From b16946723f94b855ab70469dca5899ad80f129b3 Mon Sep 17 00:00:00 2001 From: Dan Date: Mon, 30 Oct 2017 16:11:05 -0230 Subject: [PATCH 24/46] Insufficient balance warning in gas customizer works for send token --- ui/app/components/customize-gas-modal/index.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ui/app/components/customize-gas-modal/index.js b/ui/app/components/customize-gas-modal/index.js index 101a19d9f..5bd6c54cb 100644 --- a/ui/app/components/customize-gas-modal/index.js +++ b/ui/app/components/customize-gas-modal/index.js @@ -107,19 +107,17 @@ CustomizeGasModal.prototype.validate = function ({ gasTotal, gasLimit }) { const { amount, balance, - primaryCurrency, selectedToken, amountConversionRate, conversionRate, } = this.props let error = null - + const balanceIsSufficient = isBalanceSufficient({ amount, gasTotal, balance, - primaryCurrency, selectedToken, amountConversionRate, conversionRate, From 716bbf67d7180ffe0f59d07484d30231ed5f5e49 Mon Sep 17 00:00:00 2001 From: Dan Date: Mon, 30 Oct 2017 19:16:56 -0230 Subject: [PATCH 25/46] Set gas price allows for WEI precision. --- .../components/customize-gas-modal/index.js | 20 +++++++++++++++---- ui/app/components/input-number.js | 17 +++++++++++----- ui/app/components/send/send-constants.js | 1 + ui/app/conversion-util.js | 2 +- 4 files changed, 30 insertions(+), 10 deletions(-) diff --git a/ui/app/components/customize-gas-modal/index.js b/ui/app/components/customize-gas-modal/index.js index 5bd6c54cb..dcb058690 100644 --- a/ui/app/components/customize-gas-modal/index.js +++ b/ui/app/components/customize-gas-modal/index.js @@ -73,6 +73,8 @@ function getOriginalState (props) { gasLimit, gasTotal, error: null, + priceSigZeros: '', + priceSigDec: '', } } @@ -167,7 +169,15 @@ CustomizeGasModal.prototype.convertAndSetGasLimit = function (newGasLimit) { } CustomizeGasModal.prototype.convertAndSetGasPrice = function (newGasPrice) { - const { gasLimit } = this.state + const { gasLimit, priceSigZeros } = this.state + const priceStrLength = newGasPrice.length + const sigZeros = String(newGasPrice).match(/^\d+[.]\d*?(0+)$/) + const sigDec = String(newGasPrice).match(/^\d+([.])0*$/) + + this.setState({ + priceSigZeros: sigZeros && sigZeros[1] || '', + priceSigDec: sigDec && sigDec[1] || '', + }) const gasPrice = conversionUtil(newGasPrice, { fromNumericBase: 'dec', @@ -189,15 +199,17 @@ CustomizeGasModal.prototype.convertAndSetGasPrice = function (newGasPrice) { CustomizeGasModal.prototype.render = function () { const { hideModal } = this.props - const { gasPrice, gasLimit, gasTotal, error } = this.state + const { gasPrice, gasLimit, gasTotal, error, priceSigZeros, priceSigDec } = this.state - const convertedGasPrice = conversionUtil(gasPrice, { + let convertedGasPrice = conversionUtil(gasPrice, { fromNumericBase: 'hex', toNumericBase: 'dec', fromDenomination: 'WEI', toDenomination: 'GWEI', }) + convertedGasPrice += convertedGasPrice.match(/[.]/) ? priceSigZeros : `${priceSigDec}${priceSigZeros}` + const convertedGasLimit = conversionUtil(gasLimit, { fromNumericBase: 'hex', toNumericBase: 'dec', @@ -222,7 +234,7 @@ CustomizeGasModal.prototype.render = function () { value: convertedGasPrice, min: MIN_GAS_PRICE_GWEI, // max: 1000, - step: MIN_GAS_PRICE_GWEI, + step: multiplyCurrencies(MIN_GAS_PRICE_GWEI, 10), onChange: value => this.convertAndSetGasPrice(value), title: 'Gas Price (GWEI)', copy: 'We calculate the suggested gas prices based on network success rates.', diff --git a/ui/app/components/input-number.js b/ui/app/components/input-number.js index e28807c13..da4d739aa 100644 --- a/ui/app/components/input-number.js +++ b/ui/app/components/input-number.js @@ -5,6 +5,7 @@ const { addCurrencies, conversionGTE, conversionLTE, + subtractCurrencies, toNegative, } = require('../conversion-util') @@ -17,18 +18,24 @@ function InputNumber () { this.setValue = this.setValue.bind(this) } +function isValidInput (text) { + const re = /^([1-9]\d*|0)(\.|\.\d*)?$/ + return re.test(text) +} + InputNumber.prototype.setValue = function (newValue) { + if (newValue && !isValidInput(newValue)) return const { fixed, min = -1, max = Infinity, onChange } = this.props - newValue = Number(fixed ? newValue.toFixed(4) : newValue) + newValue = fixed ? newValue.toFixed(4) : newValue const newValueGreaterThanMin = conversionGTE( - { value: newValue, fromNumericBase: 'dec' }, + { value: newValue || '0', fromNumericBase: 'dec' }, { value: min, fromNumericBase: 'hex' }, ) const newValueLessThanMax = conversionLTE( - { value: newValue, fromNumericBase: 'dec' }, + { value: newValue || '0', fromNumericBase: 'dec' }, { value: max, fromNumericBase: 'hex' }, ) if (newValueGreaterThanMin && newValueLessThanMax) { @@ -46,8 +53,8 @@ InputNumber.prototype.render = function () { return h('div.customize-gas-input-wrapper', {}, [ h('input.customize-gas-input', { placeholder, - type: 'number', value, + step, onChange: (e) => this.setValue(e.target.value), }), h('span.gas-tooltip-input-detail', {}, [unitLabel]), @@ -57,7 +64,7 @@ InputNumber.prototype.render = function () { }), h('i.fa.fa-angle-down', { style: { cursor: 'pointer' }, - onClick: () => this.setValue(addCurrencies(value, toNegative(step))), + onClick: () => this.setValue(subtractCurrencies(value, step)), }), ]), ]) diff --git a/ui/app/components/send/send-constants.js b/ui/app/components/send/send-constants.js index 0a4f85bc9..a961ffcd8 100644 --- a/ui/app/components/send/send-constants.js +++ b/ui/app/components/send/send-constants.js @@ -11,6 +11,7 @@ const MIN_GAS_PRICE_GWEI = ethUtil.addHexPrefix(conversionUtil(MIN_GAS_PRICE_HEX toDenomination: 'GWEI', fromNumericBase: 'hex', toNumericBase: 'hex', + numberOfDecimals: 1, })) const MIN_GAS_TOTAL = multiplyCurrencies(MIN_GAS_LIMIT_HEX, MIN_GAS_PRICE_HEX, { diff --git a/ui/app/conversion-util.js b/ui/app/conversion-util.js index 3786641fb..ee831262b 100644 --- a/ui/app/conversion-util.js +++ b/ui/app/conversion-util.js @@ -53,7 +53,7 @@ const toNormalizedDenomination = { } const toSpecifiedDenomination = { WEI: bigNumber => bigNumber.times(BIG_NUMBER_WEI_MULTIPLIER).round(), - GWEI: bigNumber => bigNumber.times(BIG_NUMBER_GWEI_MULTIPLIER).round(1), + GWEI: bigNumber => bigNumber.times(BIG_NUMBER_GWEI_MULTIPLIER).round(9), } const baseChange = { hex: n => n.toString(16), From e5391cf9fdc7dfc25318caaed5dd56270946ddf8 Mon Sep 17 00:00:00 2001 From: Dan Date: Mon, 30 Oct 2017 19:34:21 -0230 Subject: [PATCH 26/46] Allow sending 0 eth or tokens --- ui/app/send-v2.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/app/send-v2.js b/ui/app/send-v2.js index 6f3b48be6..951af3d2c 100644 --- a/ui/app/send-v2.js +++ b/ui/app/send-v2.js @@ -422,7 +422,7 @@ SendTransactionScreen.prototype.renderAmountRow = function () { primaryCurrency, convertedCurrency, selectedToken, - value: amount, + value: amount || '0x0', conversionRate: amountConversionRate, handleChange: this.handleAmountChange, validate: this.validateAmount, @@ -512,7 +512,7 @@ SendTransactionScreen.prototype.renderFooter = function () { errors: { amount: amountError, to: toError }, } = this.props - const noErrors = amountError === null && toError === null + const noErrors = !amountError && toError === null const errorClass = noErrors ? '' : '__disabled' return h('div.send-v2__footer', [ @@ -566,7 +566,7 @@ SendTransactionScreen.prototype.onSubmit = function (event) { errors: { amount: amountError, to: toError }, } = this.props - const noErrors = amountError === null && toError === null + const noErrors = !amountError && toError === null if (!noErrors) { return From 8c6e1232e417f5a2974b5aa1cc479dac4925df63 Mon Sep 17 00:00:00 2001 From: Dan Date: Mon, 6 Nov 2017 16:14:46 -0330 Subject: [PATCH 27/46] Lint fixes. --- ui/app/components/customize-gas-modal/index.js | 3 +-- ui/app/components/input-number.js | 1 - ui/app/components/send/send-utils.js | 1 - ui/app/conversion-util.js | 2 +- ui/app/send-v2.js | 11 +---------- 5 files changed, 3 insertions(+), 15 deletions(-) diff --git a/ui/app/components/customize-gas-modal/index.js b/ui/app/components/customize-gas-modal/index.js index dcb058690..b77e1990f 100644 --- a/ui/app/components/customize-gas-modal/index.js +++ b/ui/app/components/customize-gas-modal/index.js @@ -169,8 +169,7 @@ CustomizeGasModal.prototype.convertAndSetGasLimit = function (newGasLimit) { } CustomizeGasModal.prototype.convertAndSetGasPrice = function (newGasPrice) { - const { gasLimit, priceSigZeros } = this.state - const priceStrLength = newGasPrice.length + const { gasLimit } = this.state const sigZeros = String(newGasPrice).match(/^\d+[.]\d*?(0+)$/) const sigDec = String(newGasPrice).match(/^\d+([.])0*$/) diff --git a/ui/app/components/input-number.js b/ui/app/components/input-number.js index da4d739aa..12dec2957 100644 --- a/ui/app/components/input-number.js +++ b/ui/app/components/input-number.js @@ -6,7 +6,6 @@ const { conversionGTE, conversionLTE, subtractCurrencies, - toNegative, } = require('../conversion-util') module.exports = InputNumber diff --git a/ui/app/components/send/send-utils.js b/ui/app/components/send/send-utils.js index 0260c38a6..bd1197950 100644 --- a/ui/app/components/send/send-utils.js +++ b/ui/app/components/send/send-utils.js @@ -1,6 +1,5 @@ const { addCurrencies, - conversionGreaterThan, conversionUtil, conversionGTE, } = require('../../conversion-util') diff --git a/ui/app/conversion-util.js b/ui/app/conversion-util.js index ee831262b..ee2950071 100644 --- a/ui/app/conversion-util.js +++ b/ui/app/conversion-util.js @@ -149,7 +149,7 @@ const subtractCurrencies = (a, b, options = {}) => { const { aBase, bBase, - ...conversionOptions, + ...conversionOptions } = options const value = (new BigNumber(a, aBase)).minus(b, bBase); diff --git a/ui/app/send-v2.js b/ui/app/send-v2.js index 951af3d2c..bb4c592e8 100644 --- a/ui/app/send-v2.js +++ b/ui/app/send-v2.js @@ -16,7 +16,6 @@ const { MIN_GAS_PRICE_HEX, MIN_GAS_LIMIT_HEX, } = require('./components/send/send-constants') -const abi = require('human-standard-token-abi') const { multiplyCurrencies, @@ -92,11 +91,10 @@ SendTransactionScreen.prototype.componentWillMount = function () { selectedAddress, data, updateGasTotal, - updateSendTokenBalance, from, tokenContract, } = this.props - const { symbol, decimals } = selectedToken || {} + const { symbol } = selectedToken || {} if (symbol) { updateTokenExchangeRate(symbol) @@ -209,13 +207,9 @@ SendTransactionScreen.prototype.renderErrorMessage = function (errorType) { SendTransactionScreen.prototype.handleFromChange = async function (newFrom) { const { - from, updateSendFrom, - updateSendTokenBalance, tokenContract, - selectedToken, } = this.props - const { decimals } = selectedToken || {} if (tokenContract) { const usersToken = await tokenContract.balanceOf(newFrom.address) @@ -229,9 +223,6 @@ SendTransactionScreen.prototype.renderFromRow = function () { from, fromAccounts, conversionRate, - updateSendFrom, - updateSendTokenBalance, - tokenContract, } = this.props const { fromDropdownOpen } = this.state From c57d504794b6020d42dcdabe08a13ed412450fc1 Mon Sep 17 00:00:00 2001 From: Dan Date: Tue, 7 Nov 2017 20:06:26 -0330 Subject: [PATCH 28/46] Add currency-input component to correct send amount behaviour and move currency display value state to parent component. --- ui/app/components/currency-input.js | 93 ++++++++++++++++++++++ ui/app/components/send/currency-display.js | 36 ++------- ui/app/send-v2.js | 6 +- 3 files changed, 103 insertions(+), 32 deletions(-) create mode 100644 ui/app/components/currency-input.js diff --git a/ui/app/components/currency-input.js b/ui/app/components/currency-input.js new file mode 100644 index 000000000..5e534d87b --- /dev/null +++ b/ui/app/components/currency-input.js @@ -0,0 +1,93 @@ +const Component = require('react').Component +const h = require('react-hyperscript') +const inherits = require('util').inherits + +module.exports = CurrencyInput + +inherits(CurrencyInput, Component) +function CurrencyInput (props) { + Component.call(this) + + this.state = { + value: sanitizeValue(props.value), + } +} + +function removeNonDigits (str) { + return str.match(/\d|$/g).join('') +} + +// Removes characters that are not digits, then removes leading zeros +function sanitizeInteger (val) { + return String(parseInt(removeNonDigits(val) || '0', 10)) +} + +function sanitizeDecimal (val) { + return removeNonDigits(val) +} + +// Take a single string param and returns a non-negative integer or float as a string. +// Breaks the input into three parts: the integer, the decimal point, and the decimal/fractional part. +// Removes leading zeros from the integer, and non-digits from the integer and decimal +// The integer is returned as '0' in cases where it would be empty. A decimal point is +// included in the returned string if one is included in the param +// Examples: +// sanitizeValue('0') -> '0' +// sanitizeValue('a') -> '0' +// sanitizeValue('010.') -> '10.' +// sanitizeValue('0.005') -> '0.005' +// sanitizeValue('22.200') -> '22.200' +// sanitizeValue('.200') -> '0.200' +// sanitizeValue('a.b.1.c,89.123') -> '0.189123' +function sanitizeValue (value) { + let [,integer, point, decimal] = (/([^.]*)([.]?)([^.]*)/).exec(value) + + integer = sanitizeInteger(integer) || '0' + decimal = sanitizeDecimal(decimal) + + return `${integer}${point}${decimal}` +} + +CurrencyInput.prototype.handleChange = function (newValue) { + const { onInputChange } = this.props + + this.setState({ value: sanitizeValue(newValue) }) + + onInputChange(sanitizeValue(newValue)) +} + +// If state.value === props.value plus a decimal point, or at least one +// zero or a decimal point and at least one zero, then this returns state.value +// after it is sanitized with getValueParts +CurrencyInput.prototype.getValueToRender = function () { + const { value } = this.props + const { value: stateValue } = this.state + + const trailingStateString = (new RegExp(`^${value}(.+)`)).exec(stateValue) + const trailingDecimalAndZeroes = trailingStateString && (/^[.0]0*/).test(trailingStateString[1]) + + return sanitizeValue(trailingDecimalAndZeroes + ? stateValue + : value) +} + +CurrencyInput.prototype.render = function () { + const { + className, + placeholder, + readOnly, + } = this.props + + const inputSizeMultiplier = readOnly ? 1 : 1.2 + + const valueToRender = this.getValueToRender() + + return h('input', { + className, + value: valueToRender, + placeholder, + size: valueToRender.length * inputSizeMultiplier, + readOnly, + onChange: e => this.handleChange(e.target.value), + }) +} diff --git a/ui/app/components/send/currency-display.js b/ui/app/components/send/currency-display.js index 45986e371..8b72b3e6d 100644 --- a/ui/app/components/send/currency-display.js +++ b/ui/app/components/send/currency-display.js @@ -1,6 +1,7 @@ const Component = require('react').Component const h = require('react-hyperscript') const inherits = require('util').inherits +const CurrencyInput = require('../currency-input') const { conversionUtil, multiplyCurrencies } = require('../../conversion-util') module.exports = CurrencyDisplay @@ -8,10 +9,6 @@ module.exports = CurrencyDisplay inherits(CurrencyDisplay, Component) function CurrencyDisplay () { Component.call(this) - - this.state = { - value: null, - } } function isValidInput (text) { @@ -49,13 +46,11 @@ CurrencyDisplay.prototype.render = function () { convertedCurrency, readOnly = false, inError = false, - value: initValue, + value, handleChange, - validate, } = this.props - const { value } = this.state - const initValueToRender = conversionUtil(initValue, { + const valueToRender = conversionUtil(value, { fromNumericBase: 'hex', toNumericBase: 'dec', fromDenomination: 'WEI', @@ -63,7 +58,7 @@ CurrencyDisplay.prototype.render = function () { conversionRate, }) - const convertedValue = conversionUtil(value || initValueToRender, { + const convertedValue = conversionUtil(valueToRender, { fromNumericBase: 'dec', fromCurrency: primaryCurrency, toCurrency: convertedCurrency, @@ -84,29 +79,14 @@ CurrencyDisplay.prototype.render = function () { h('div.currency-display__input-wrapper', [ - h('input', { + h(CurrencyInput, { className: primaryBalanceClassName, - value: `${value || initValueToRender}`, + value: `${valueToRender}`, placeholder: '0', - size: (value || initValueToRender).length * inputSizeMultiplier, readOnly, - onChange: (event) => { - let newValue = event.target.value - - if (newValue === '') { - newValue = '0' - } else if (newValue.match(/^0[1-9]$/)) { - newValue = newValue.match(/[1-9]/)[0] - } - - if (newValue && !isValidInput(newValue)) { - event.preventDefault() - } else { - validate(this.getAmount(newValue)) - this.setState({ value: newValue }) - } + onInputChange: newValue => { + handleChange(this.getAmount(newValue)) }, - onBlur: event => !readOnly && handleChange(this.getAmount(event.target.value)), }), h('span.currency-display__currency-symbol', primaryCurrency), diff --git a/ui/app/send-v2.js b/ui/app/send-v2.js index bb4c592e8..8c8b97a6d 100644 --- a/ui/app/send-v2.js +++ b/ui/app/send-v2.js @@ -300,6 +300,7 @@ SendTransactionScreen.prototype.handleAmountChange = function (value) { const amount = value const { updateSendAmount } = this.props + this.validateAmount(amount) updateSendAmount(amount) } @@ -330,7 +331,6 @@ SendTransactionScreen.prototype.setAmountToMax = function () { updateGasPrice(MIN_GAS_PRICE_HEX) updateGasLimit(MIN_GAS_LIMIT_HEX) updateGasTotal(MIN_GAS_TOTAL) - updateSendAmount(maxAmount) } @@ -393,9 +393,8 @@ SendTransactionScreen.prototype.renderAmountRow = function () { errors, amount, } = this.props - return h('div.send-v2__form-row', [ - + h('div.send-v2__form-label', [ 'Amount:', this.renderErrorMessage('amount'), @@ -416,7 +415,6 @@ SendTransactionScreen.prototype.renderAmountRow = function () { value: amount || '0x0', conversionRate: amountConversionRate, handleChange: this.handleAmountChange, - validate: this.validateAmount, }), ]), From c156c85eaa064b971ce0202df0bb1681eb0e22dd Mon Sep 17 00:00:00 2001 From: Dan Date: Tue, 7 Nov 2017 21:47:14 -0330 Subject: [PATCH 29/46] Fix amount max for sending token. --- ui/app/components/send/currency-display.js | 30 +++++++++++++++++----- ui/app/send-v2.js | 8 +++--- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/ui/app/components/send/currency-display.js b/ui/app/components/send/currency-display.js index 8b72b3e6d..49df5b0b7 100644 --- a/ui/app/components/send/currency-display.js +++ b/ui/app/components/send/currency-display.js @@ -36,6 +36,28 @@ CurrencyDisplay.prototype.getAmount = function (value) { : toHexWei(value) } +CurrencyDisplay.prototype.getValueToRender = function () { + const { selectedToken, conversionRate, value } = this.props + + const { decimals, symbol } = selectedToken || {} + const multiplier = Math.pow(10, Number(decimals || 0)) + + return selectedToken + ? conversionUtil(value, { + fromNumericBase: 'hex', + toCurrency: symbol, + conversionRate: multiplier, + invertConversionRate: true, + }) + : conversionUtil(value, { + fromNumericBase: 'hex', + toNumericBase: 'dec', + fromDenomination: 'WEI', + numberOfDecimals: 6, + conversionRate, + }) +} + CurrencyDisplay.prototype.render = function () { const { className = 'currency-display', @@ -50,13 +72,7 @@ CurrencyDisplay.prototype.render = function () { handleChange, } = this.props - const valueToRender = conversionUtil(value, { - fromNumericBase: 'hex', - toNumericBase: 'dec', - fromDenomination: 'WEI', - numberOfDecimals: 6, - conversionRate, - }) + const valueToRender = this.getValueToRender() const convertedValue = conversionUtil(valueToRender, { fromNumericBase: 'dec', diff --git a/ui/app/send-v2.js b/ui/app/send-v2.js index 8c8b97a6d..ffc9accc5 100644 --- a/ui/app/send-v2.js +++ b/ui/app/send-v2.js @@ -328,9 +328,11 @@ SendTransactionScreen.prototype.setAmountToMax = function () { ) updateSendErrors({ amount: null }) - updateGasPrice(MIN_GAS_PRICE_HEX) - updateGasLimit(MIN_GAS_LIMIT_HEX) - updateGasTotal(MIN_GAS_TOTAL) + if (!selectedToken) { + updateGasPrice(MIN_GAS_PRICE_HEX) + updateGasLimit(MIN_GAS_LIMIT_HEX) + updateGasTotal(MIN_GAS_TOTAL) + } updateSendAmount(maxAmount) } From d1977225a48f381f3a6bda4a54ce9e0e0914357e Mon Sep 17 00:00:00 2001 From: Dan Date: Tue, 7 Nov 2017 21:49:14 -0330 Subject: [PATCH 30/46] Calculate max amount for send ether based on minimum gas total. --- ui/app/components/send/currency-display.js | 8 -------- ui/app/send-v2.js | 3 +-- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/ui/app/components/send/currency-display.js b/ui/app/components/send/currency-display.js index 49df5b0b7..870fbb42a 100644 --- a/ui/app/components/send/currency-display.js +++ b/ui/app/components/send/currency-display.js @@ -11,11 +11,6 @@ function CurrencyDisplay () { Component.call(this) } -function isValidInput (text) { - const re = /^([1-9]\d*|0)(\.|\.\d*)?$/ - return re.test(text) -} - function toHexWei (value) { return conversionUtil(value, { fromNumericBase: 'dec', @@ -68,7 +63,6 @@ CurrencyDisplay.prototype.render = function () { convertedCurrency, readOnly = false, inError = false, - value, handleChange, } = this.props @@ -82,8 +76,6 @@ CurrencyDisplay.prototype.render = function () { conversionRate, }) - const inputSizeMultiplier = readOnly ? 1 : 1.2 - return h('div', { className, style: { diff --git a/ui/app/send-v2.js b/ui/app/send-v2.js index ffc9accc5..0cef47b27 100644 --- a/ui/app/send-v2.js +++ b/ui/app/send-v2.js @@ -307,7 +307,6 @@ SendTransactionScreen.prototype.handleAmountChange = function (value) { SendTransactionScreen.prototype.setAmountToMax = function () { const { from: { balance }, - gasTotal, updateSendAmount, updateSendErrors, updateGasPrice, @@ -323,7 +322,7 @@ SendTransactionScreen.prototype.setAmountToMax = function () { ? multiplyCurrencies(tokenBalance, multiplier, {toNumericBase: 'hex'}) : subtractCurrencies( ethUtil.addHexPrefix(balance), - ethUtil.addHexPrefix(gasTotal), + ethUtil.addHexPrefix(MIN_GAS_TOTAL), { toNumericBase: 'hex' } ) From 424c1f23c91b06b687ddb3a3cbc79610584dc23f Mon Sep 17 00:00:00 2001 From: cjeria Date: Wed, 8 Nov 2017 15:41:29 -0800 Subject: [PATCH 31/46] darker backdrop style for modal --- ui/app/components/modals/modal.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/app/components/modals/modal.js b/ui/app/components/modals/modal.js index 842081f40..f2909f3c3 100644 --- a/ui/app/components/modals/modal.js +++ b/ui/app/components/modals/modal.js @@ -189,7 +189,7 @@ const MODALS = { } const BACKDROPSTYLE = { - backgroundColor: 'rgba(245, 245, 245, 0.85)', + backgroundColor: 'rgba(0, 0, 0, 0.5)', } function mapStateToProps (state) { From 62f2aebe1d9c3efd6ace8785fc96bb43ae08afe8 Mon Sep 17 00:00:00 2001 From: Dan Date: Thu, 9 Nov 2017 13:17:10 -0330 Subject: [PATCH 32/46] Network loading does not block network loading. --- .../dropdowns/components/dropdown.js | 2 +- .../components/dropdowns/network-dropdown.js | 3 ++- ui/app/components/loading.js | 15 +------------ ui/app/css/itcss/components/index.scss | 2 ++ .../css/itcss/components/loading-overlay.scss | 21 +++++++++++++++++++ 5 files changed, 27 insertions(+), 16 deletions(-) create mode 100644 ui/app/css/itcss/components/loading-overlay.scss diff --git a/ui/app/components/dropdowns/components/dropdown.js b/ui/app/components/dropdowns/components/dropdown.js index ddcb7998f..15d064be8 100644 --- a/ui/app/components/dropdowns/components/dropdown.js +++ b/ui/app/components/dropdowns/components/dropdown.js @@ -31,7 +31,7 @@ class Dropdown extends Component { containerClassName, useCssTransition, isOpen, - zIndex: 30, + zIndex: 55, onClickOutside, style, innerStyle: innerStyleDefaults, diff --git a/ui/app/components/dropdowns/network-dropdown.js b/ui/app/components/dropdowns/network-dropdown.js index 20dfca590..0908faf01 100644 --- a/ui/app/components/dropdowns/network-dropdown.js +++ b/ui/app/components/dropdowns/network-dropdown.js @@ -75,11 +75,12 @@ NetworkDropdown.prototype.render = function () { } }, containerClassName: 'network-droppo', - zIndex: 11, + zIndex: 55, style: { position: 'absolute', top: '58px', minWidth: '309px', + zIndex: '55px', }, innerStyle: { padding: '18px 8px', diff --git a/ui/app/components/loading.js b/ui/app/components/loading.js index 587212015..9442121fe 100644 --- a/ui/app/components/loading.js +++ b/ui/app/components/loading.js @@ -10,20 +10,7 @@ class LoadingIndicator extends Component { render () { return ( - h('.full-flex-height', { - style: { - left: '0px', - zIndex: 50, - position: 'absolute', - flexDirection: 'column', - display: 'flex', - justifyContent: 'center', - alignItems: 'center', - height: '100%', - width: '100%', - background: 'rgba(255, 255, 255, 0.8)', - }, - }, [ + h('.full-flex-height.loading-overlay', {}, [ h('img', { src: 'images/loading.svg', }), diff --git a/ui/app/css/itcss/components/index.scss b/ui/app/css/itcss/components/index.scss index 4ba02be67..dfb4f23f0 100644 --- a/ui/app/css/itcss/components/index.scss +++ b/ui/app/css/itcss/components/index.scss @@ -16,6 +16,8 @@ @import './confirm.scss'; +@import './loading-overlay.scss'; + // Balances @import './hero-balance.scss'; diff --git a/ui/app/css/itcss/components/loading-overlay.scss b/ui/app/css/itcss/components/loading-overlay.scss new file mode 100644 index 000000000..15009c1e6 --- /dev/null +++ b/ui/app/css/itcss/components/loading-overlay.scss @@ -0,0 +1,21 @@ +.loading-overlay { + left: 0px; + z-index: 50; + position: absolute; + flex-direction: column; + display: flex; + justify-content: center; + align-items: center; + width: 100%; + background: rgba(255, 255, 255, 0.8); + + @media screen and (max-width: 575px) { + margin-top: 56px; + height: calc(100% - 56px); + } + + @media screen and (min-width: 576px) { + margin-top: 75px; + height: calc(100% - 75px); + } +} From 5120cfdff3047e4bf88cec544895cc713d063cdd Mon Sep 17 00:00:00 2001 From: Thomas Huang Date: Thu, 9 Nov 2017 14:23:10 -0800 Subject: [PATCH 33/46] Linting --- ui/app/components/currency-input.js | 2 +- ui/app/components/customize-gas-modal/index.js | 2 +- ui/app/components/send/send-utils.js | 4 ++-- ui/app/conversion-util.js | 4 ++-- ui/app/send-v2.js | 6 +++--- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ui/app/components/currency-input.js b/ui/app/components/currency-input.js index 5e534d87b..f192ee531 100644 --- a/ui/app/components/currency-input.js +++ b/ui/app/components/currency-input.js @@ -40,7 +40,7 @@ function sanitizeDecimal (val) { // sanitizeValue('.200') -> '0.200' // sanitizeValue('a.b.1.c,89.123') -> '0.189123' function sanitizeValue (value) { - let [,integer, point, decimal] = (/([^.]*)([.]?)([^.]*)/).exec(value) + let [integer, point, decimal] = (/([^.]*)([.]?)([^.]*)/).exec(value) integer = sanitizeInteger(integer) || '0' decimal = sanitizeDecimal(decimal) diff --git a/ui/app/components/customize-gas-modal/index.js b/ui/app/components/customize-gas-modal/index.js index b77e1990f..f01f42fe0 100644 --- a/ui/app/components/customize-gas-modal/index.js +++ b/ui/app/components/customize-gas-modal/index.js @@ -115,7 +115,7 @@ CustomizeGasModal.prototype.validate = function ({ gasTotal, gasLimit }) { } = this.props let error = null - + const balanceIsSufficient = isBalanceSufficient({ amount, gasTotal, diff --git a/ui/app/components/send/send-utils.js b/ui/app/components/send/send-utils.js index bd1197950..d8211930d 100644 --- a/ui/app/components/send/send-utils.js +++ b/ui/app/components/send/send-utils.js @@ -7,7 +7,7 @@ const { calcTokenAmount, } = require('../../token-util') -function isBalanceSufficient({ +function isBalanceSufficient ({ amount = '0x0', gasTotal = '0x0', balance, @@ -39,7 +39,7 @@ function isBalanceSufficient({ return balanceIsSufficient } -function isTokenBalanceSufficient({ +function isTokenBalanceSufficient ({ amount = '0x0', tokenBalance, decimals, diff --git a/ui/app/conversion-util.js b/ui/app/conversion-util.js index ee2950071..de37bd595 100644 --- a/ui/app/conversion-util.js +++ b/ui/app/conversion-util.js @@ -151,7 +151,7 @@ const subtractCurrencies = (a, b, options = {}) => { bBase, ...conversionOptions } = options - const value = (new BigNumber(a, aBase)).minus(b, bBase); + const value = (new BigNumber(a, aBase)).minus(b, bBase) return converter({ value, @@ -183,7 +183,7 @@ const conversionGreaterThan = ( ) => { const firstValue = converter({ ...firstProps }) const secondValue = converter({ ...secondProps }) - + return firstValue.gt(secondValue) } diff --git a/ui/app/send-v2.js b/ui/app/send-v2.js index 0cef47b27..690af7e5c 100644 --- a/ui/app/send-v2.js +++ b/ui/app/send-v2.js @@ -106,7 +106,7 @@ SendTransactionScreen.prototype.componentWillMount = function () { .all([ getGasPrice(), estimateGas(estimateGasParams), - tokenContract && tokenContract.balanceOf(from.address) + tokenContract && tokenContract.balanceOf(from.address), ]) .then(([gasPrice, gas, usersToken]) => { @@ -352,7 +352,7 @@ SendTransactionScreen.prototype.validateAmount = function (value) { let amountError = null const sufficientBalance = isBalanceSufficient({ - amount: selectedToken ? '0x0' : amount, + amount: selectedToken ? '0x0' : amount, gasTotal, balance, primaryCurrency, @@ -395,7 +395,7 @@ SendTransactionScreen.prototype.renderAmountRow = function () { amount, } = this.props return h('div.send-v2__form-row', [ - + h('div.send-v2__form-label', [ 'Amount:', this.renderErrorMessage('amount'), From 5a0126f17b3513fa7fa1513f2c52abff19535ac0 Mon Sep 17 00:00:00 2001 From: Dan Date: Fri, 10 Nov 2017 07:07:53 -0330 Subject: [PATCH 34/46] Rounding of vals < 0.01 in currency display consistent with master. --- ui/app/components/send/currency-display.js | 3 ++- ui/app/conversion-util.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/ui/app/components/send/currency-display.js b/ui/app/components/send/currency-display.js index 870fbb42a..5057c413c 100644 --- a/ui/app/components/send/currency-display.js +++ b/ui/app/components/send/currency-display.js @@ -68,13 +68,14 @@ CurrencyDisplay.prototype.render = function () { const valueToRender = this.getValueToRender() - const convertedValue = conversionUtil(valueToRender, { + let convertedValue = conversionUtil(valueToRender, { fromNumericBase: 'dec', fromCurrency: primaryCurrency, toCurrency: convertedCurrency, numberOfDecimals: 2, conversionRate, }) + convertedValue = Number(convertedValue).toFixed(2) return h('div', { className, diff --git a/ui/app/conversion-util.js b/ui/app/conversion-util.js index de37bd595..ee42ebea1 100644 --- a/ui/app/conversion-util.js +++ b/ui/app/conversion-util.js @@ -37,7 +37,7 @@ const BIG_NUMBER_GWEI_MULTIPLIER = new BigNumber('1000000000') // Individual Setters const convert = R.invoker(1, 'times') -const round = R.invoker(2, 'round')(R.__, BigNumber.ROUND_DOWN) +const round = R.invoker(2, 'round')(R.__, BigNumber.ROUND_HALF_DOWN) const invertConversionRate = conversionRate => () => new BigNumber(1.0).div(conversionRate) const decToBigNumberViaString = n => R.pipe(String, toBigNumber['dec']) From a33ced39946c1448b56aef22ab3573b00d1faeca Mon Sep 17 00:00:00 2001 From: Dan Date: Fri, 10 Nov 2017 15:17:02 -0330 Subject: [PATCH 35/46] Focus amount input when click anywhere in amount field container --- ui/app/components/currency-input.js | 2 ++ ui/app/components/send/currency-display.js | 2 ++ 2 files changed, 4 insertions(+) diff --git a/ui/app/components/currency-input.js b/ui/app/components/currency-input.js index f192ee531..016f14d3e 100644 --- a/ui/app/components/currency-input.js +++ b/ui/app/components/currency-input.js @@ -76,6 +76,7 @@ CurrencyInput.prototype.render = function () { className, placeholder, readOnly, + inputRef, } = this.props const inputSizeMultiplier = readOnly ? 1 : 1.2 @@ -89,5 +90,6 @@ CurrencyInput.prototype.render = function () { size: valueToRender.length * inputSizeMultiplier, readOnly, onChange: e => this.handleChange(e.target.value), + ref: inputRef, }) } diff --git a/ui/app/components/send/currency-display.js b/ui/app/components/send/currency-display.js index 5057c413c..5bf8d6aa0 100644 --- a/ui/app/components/send/currency-display.js +++ b/ui/app/components/send/currency-display.js @@ -82,6 +82,7 @@ CurrencyDisplay.prototype.render = function () { style: { borderColor: inError ? 'red' : null, }, + onClick: () => this.currencyInput.focus(), }, [ h('div.currency-display__primary-row', [ @@ -96,6 +97,7 @@ CurrencyDisplay.prototype.render = function () { onInputChange: newValue => { handleChange(this.getAmount(newValue)) }, + inputRef: input => { this.currencyInput = input; }, }), h('span.currency-display__currency-symbol', primaryCurrency), From 08d9ecc0454c729356f3f7a6d91156ee96c66959 Mon Sep 17 00:00:00 2001 From: Dan Date: Fri, 10 Nov 2017 16:15:43 -0330 Subject: [PATCH 36/46] Cursor pointer and hover background on from and to dropdown items. --- ui/app/components/send/account-list-item.js | 2 ++ ui/app/components/send/from-dropdown.js | 1 + ui/app/components/send/to-autocomplete.js | 1 + ui/app/css/itcss/components/account-dropdown.scss | 11 +++++++++++ 4 files changed, 15 insertions(+) diff --git a/ui/app/components/send/account-list-item.js b/ui/app/components/send/account-list-item.js index 2378a4671..1ad3f69c1 100644 --- a/ui/app/components/send/account-list-item.js +++ b/ui/app/components/send/account-list-item.js @@ -22,6 +22,7 @@ module.exports = connect(mapStateToProps)(AccountListItem) AccountListItem.prototype.render = function () { const { + className, account, handleClick, icon = null, @@ -34,6 +35,7 @@ AccountListItem.prototype.render = function () { const { name, address, balance } = account || {} return h('div.account-list-item', { + className, onClick: () => handleClick({ name, address, balance }), }, [ diff --git a/ui/app/components/send/from-dropdown.js b/ui/app/components/send/from-dropdown.js index bcae5ede8..0686fbe73 100644 --- a/ui/app/components/send/from-dropdown.js +++ b/ui/app/components/send/from-dropdown.js @@ -35,6 +35,7 @@ FromDropdown.prototype.renderDropdown = function () { h('div.send-v2__from-dropdown__list', {}, [ ...accounts.map(account => h(AccountListItem, { + className: 'account-list-item__dropdown', account, handleClick: () => { onSelect(account) diff --git a/ui/app/components/send/to-autocomplete.js b/ui/app/components/send/to-autocomplete.js index fef8d5ccb..e0cdd0a58 100644 --- a/ui/app/components/send/to-autocomplete.js +++ b/ui/app/components/send/to-autocomplete.js @@ -38,6 +38,7 @@ ToAutoComplete.prototype.renderDropdown = function () { ...accountsToRender.map(account => h(AccountListItem, { account, + className: 'account-list-item__dropdown', handleClick: () => { onChange(account.address) closeDropdown() diff --git a/ui/app/css/itcss/components/account-dropdown.scss b/ui/app/css/itcss/components/account-dropdown.scss index c298c4019..725da9d39 100644 --- a/ui/app/css/itcss/components/account-dropdown.scss +++ b/ui/app/css/itcss/components/account-dropdown.scss @@ -69,4 +69,15 @@ overflow: hidden; text-overflow: ellipsis; } + + &__dropdown { + &:hover { + background: rgba($alto, .2); + cursor: pointer; + + input { + background: rgba($alto, .1); + } + } + } } From 544166437a0a41ce25d3d47814409a7ce01b4e07 Mon Sep 17 00:00:00 2001 From: Dan Date: Fri, 10 Nov 2017 00:02:53 -0330 Subject: [PATCH 37/46] Deposit button shows link to faucet on testnet networks. --- ui/app/components/modals/buy-options-modal.js | 38 +++++++++++-------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/ui/app/components/modals/buy-options-modal.js b/ui/app/components/modals/buy-options-modal.js index 33615c483..53e40ba92 100644 --- a/ui/app/components/modals/buy-options-modal.js +++ b/ui/app/components/modals/buy-options-modal.js @@ -3,6 +3,7 @@ const h = require('react-hyperscript') const inherits = require('util').inherits const connect = require('react-redux').connect const actions = require('../../actions') +const networkNames = require('../../../../app/scripts/config.js').networkNames function mapStateToProps (state) { return { @@ -22,6 +23,7 @@ function mapDispatchToProps (dispatch) { showAccountDetailModal: () => { dispatch(actions.showModal({ name: 'ACCOUNT_DETAILS' })) }, + toFaucet: network => dispatch(actions.buyEth({ network })), } } @@ -32,7 +34,20 @@ function BuyOptions () { module.exports = connect(mapStateToProps, mapDispatchToProps)(BuyOptions) +BuyOptions.prototype.renderModalContentOption = function (title, header, onClick) { + return h('div.buy-modal-content-option', { + onClick, + }, [ + h('div.buy-modal-content-option-title', {}, title), + h('div.buy-modal-content-option-subtitle', {}, header), + ]) +} + BuyOptions.prototype.render = function () { + const { network, toCoinbase, address, toFaucet } = this.props + const networkIsTest = ['3', '4', '42'].find(n => n === network) + const networkName = networkNames[network] + return h('div', {}, [ h('div.buy-modal-content.transfers-subview', { }, [ @@ -47,27 +62,20 @@ BuyOptions.prototype.render = function () { h('div.buy-modal-content-options.flex-column.flex-center', {}, [ - h('div.buy-modal-content-option', { - onClick: () => { - const { toCoinbase, address } = this.props - toCoinbase(address) - }, - }, [ - h('div.buy-modal-content-option-title', {}, 'Coinbase'), - h('div.buy-modal-content-option-subtitle', {}, 'Deposit with Fiat'), - ]), + networkIsTest + ? this.renderModalContentOption(networkName, 'Test Faucet', () => toFaucet(network)) + : this.renderModalContentOption('Coinbase', 'Deposit with Fiat', () => toCoinbase(address)), // h('div.buy-modal-content-option', {}, [ // h('div.buy-modal-content-option-title', {}, 'Shapeshift'), // h('div.buy-modal-content-option-subtitle', {}, 'Trade any digital asset for any other'), // ]), - h('div.buy-modal-content-option', { - onClick: () => this.goToAccountDetailsModal(), - }, [ - h('div.buy-modal-content-option-title', {}, 'Direct Deposit'), - h('div.buy-modal-content-option-subtitle', {}, 'Deposit from another account'), - ]), + this.renderModalContentOption( + 'Direct Deposit', + 'Deposit from another account', + () => this.goToAccountDetailsModal() + ), ]), From 7eb083bd9f1a8ce0e9c3e83e8b6bfb4d5a7b59cc Mon Sep 17 00:00:00 2001 From: Dan Date: Fri, 10 Nov 2017 21:06:00 -0330 Subject: [PATCH 38/46] Improve variable name. --- ui/app/components/modals/buy-options-modal.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/app/components/modals/buy-options-modal.js b/ui/app/components/modals/buy-options-modal.js index 53e40ba92..d735983f9 100644 --- a/ui/app/components/modals/buy-options-modal.js +++ b/ui/app/components/modals/buy-options-modal.js @@ -45,7 +45,7 @@ BuyOptions.prototype.renderModalContentOption = function (title, header, onClick BuyOptions.prototype.render = function () { const { network, toCoinbase, address, toFaucet } = this.props - const networkIsTest = ['3', '4', '42'].find(n => n === network) + const isTestNetwork = ['3', '4', '42'].find(n => n === network) const networkName = networkNames[network] return h('div', {}, [ @@ -62,7 +62,7 @@ BuyOptions.prototype.render = function () { h('div.buy-modal-content-options.flex-column.flex-center', {}, [ - networkIsTest + isTestNetwork ? this.renderModalContentOption(networkName, 'Test Faucet', () => toFaucet(network)) : this.renderModalContentOption('Coinbase', 'Deposit with Fiat', () => toCoinbase(address)), From 34ca7290c593d6fb27faa98a660c8c0bca7e1457 Mon Sep 17 00:00:00 2001 From: Dan Date: Wed, 8 Nov 2017 13:14:48 -0330 Subject: [PATCH 39/46] Allow editing of send ether. --- ui/app/actions.js | 21 ++++++++ .../pending-tx/confirm-send-ether.js | 54 ++++++++++++++++--- ui/app/components/send/send-v2-container.js | 2 + ui/app/reducers/metamask.js | 23 ++++++++ ui/app/send-v2.js | 52 ++++++++++++------ 5 files changed, 130 insertions(+), 22 deletions(-) diff --git a/ui/app/actions.js b/ui/app/actions.js index 93cd40ed6..0d7f03d0e 100644 --- a/ui/app/actions.js +++ b/ui/app/actions.js @@ -115,6 +115,7 @@ var actions = { TRANSACTION_ERROR: 'TRANSACTION_ERROR', NEXT_TX: 'NEXT_TX', PREVIOUS_TX: 'PREV_TX', + EDIT_TX: 'EDIT_TX', signMsg: signMsg, cancelMsg: cancelMsg, signPersonalMsg, @@ -129,10 +130,13 @@ var actions = { completedTx: completedTx, txError: txError, nextTx: nextTx, + editTx, previousTx: previousTx, cancelAllTx: cancelAllTx, viewPendingTx: viewPendingTx, VIEW_PENDING_TX: 'VIEW_PENDING_TX', + updateTransactionParams, + UPDATE_TRANSACTION_PARAMS: 'UPDATE_TRANSACTION_PARAMS', // send screen estimateGas, getGasPrice, @@ -668,6 +672,8 @@ function updateAndApproveTx (txData) { log.debug(`actions calling background.updateAndApproveTx`) background.updateAndApproveTransaction(txData, (err) => { dispatch(actions.hideLoadingIndication()) + dispatch(actions.updateTransactionParams(txData.id, txData.txParams)) + dispatch(actions.clearSend()) if (err) { dispatch(actions.txError(err)) dispatch(actions.goHome()) @@ -685,6 +691,14 @@ function completedTx (id) { } } +function updateTransactionParams (id, txParams) { + return { + type: actions.UPDATE_TRANSACTION_PARAMS, + id, + value: txParams, + } +} + function txError (err) { return { type: actions.TRANSACTION_ERROR, @@ -948,6 +962,13 @@ function previousTx () { } } +function editTx (txId) { + return { + type: actions.EDIT_TX, + value: txId, + } +} + function showConfigPage (transitionForward = true) { return { type: actions.SHOW_CONFIG_PAGE, diff --git a/ui/app/components/pending-tx/confirm-send-ether.js b/ui/app/components/pending-tx/confirm-send-ether.js index d12bc499b..8b5801aec 100644 --- a/ui/app/components/pending-tx/confirm-send-ether.js +++ b/ui/app/components/pending-tx/confirm-send-ether.js @@ -19,6 +19,7 @@ function mapStateToProps (state) { conversionRate, identities, currentCurrency, + send, } = state.metamask const accounts = state.metamask.accounts const selectedAddress = state.metamask.selectedAddress || Object.keys(accounts)[0] @@ -27,12 +28,30 @@ function mapStateToProps (state) { identities, selectedAddress, currentCurrency, + send, } } function mapDispatchToProps (dispatch) { return { - backToAccountDetail: address => dispatch(actions.backToAccountDetail(address)), + clearSend: () => dispatch(actions.clearSend()), + editTransaction: txMeta => { + const { id, txParams } = txMeta + const { + gas: gasLimit, + gasPrice, + from, + to, + value: amount + } = txParams + dispatch(actions.editTx(id)) + dispatch(actions.updateGasLimit(gasLimit)), + dispatch(actions.updateGasPrice(gasPrice)), + dispatch(actions.updateSendTo(to)), + dispatch(actions.updateSendAmount(amount)), + dispatch(actions.updateSendErrors({ to: null, amount: null })), + dispatch(actions.showSendPage()) + }, cancelTransaction: ({ id }) => dispatch(actions.cancelTx({ id })), } } @@ -157,7 +176,7 @@ ConfirmSendEther.prototype.getData = function () { } ConfirmSendEther.prototype.render = function () { - const { backToAccountDetail, selectedAddress, currentCurrency } = this.props + const { editTransaction, selectedAddress, currentCurrency, clearSend } = this.props const txMeta = this.gatherTxMeta() const txParams = txMeta.txParams || {} @@ -199,8 +218,8 @@ ConfirmSendEther.prototype.render = function () { h('div.confirm-screen-wrapper.flex-column.flex-grow', [ h('h3.flex-center.confirm-screen-header', [ h('button.confirm-screen-back-button', { - onClick: () => backToAccountDetail(selectedAddress), - }, 'BACK'), + onClick: () => editTransaction(txMeta), + }, 'EDIT'), h('div.confirm-screen-title', 'Confirm Transaction'), h('div.confirm-screen-header-tip'), ]), @@ -371,7 +390,10 @@ ConfirmSendEther.prototype.render = function () { }, [ // Cancel Button h('div.cancel.btn-light.confirm-screen-cancel-button', { - onClick: (event) => this.cancel(event, txMeta), + onClick: (event) => { + clearSend() + this.cancel(event, txMeta) + }, }, 'CANCEL'), // Accept Button @@ -419,7 +441,27 @@ ConfirmSendEther.prototype.getFormEl = function () { ConfirmSendEther.prototype.gatherTxMeta = function () { const props = this.props const state = this.state - const txData = clone(state.txData) || clone(props.txData) + let txData = clone(state.txData) || clone(props.txData) + + if (props.send.editingTransactionId) { + const { + send: { + memo, + amount: value, + gasLimit: gas, + gasPrice, + } + } = props + const { txParams: { from, to } } = txData + txData.txParams = { + from: ethUtil.addHexPrefix(from), + to: ethUtil.addHexPrefix(to), + memo: memo && ethUtil.addHexPrefix(memo), + value: ethUtil.addHexPrefix(value), + gas: ethUtil.addHexPrefix(gas), + gasPrice: ethUtil.addHexPrefix(gasPrice), + } + } // log.debug(`UI has defaulted to tx meta ${JSON.stringify(txData)}`) return txData diff --git a/ui/app/components/send/send-v2-container.js b/ui/app/components/send/send-v2-container.js index 51d5c4f89..4451a6113 100644 --- a/ui/app/components/send/send-v2-container.js +++ b/ui/app/components/send/send-v2-container.js @@ -63,6 +63,7 @@ function mapDispatchToProps (dispatch) { dispatch(actions.signTokenTx(tokenAddress, toAddress, amount, txData)) ), signTx: txParams => dispatch(actions.signTx(txParams)), + updateAndApproveTx: txParams => dispatch(actions.updateAndApproveTx(txParams)), setSelectedAddress: address => dispatch(actions.setSelectedAddress(address)), addToAddressBook: address => dispatch(actions.addToAddressBook(address)), updateGasTotal: newTotal => dispatch(actions.updateGasTotal(newTotal)), @@ -76,5 +77,6 @@ function mapDispatchToProps (dispatch) { updateSendErrors: newError => dispatch(actions.updateSendErrors(newError)), goHome: () => dispatch(actions.goHome()), clearSend: () => dispatch(actions.clearSend()), + backToConfirmScreen: editingTransactionId => dispatch(actions.showConfTxPage({ id: editingTransactionId })), } } diff --git a/ui/app/reducers/metamask.js b/ui/app/reducers/metamask.js index 3b93a1625..bc0d0a4b3 100644 --- a/ui/app/reducers/metamask.js +++ b/ui/app/reducers/metamask.js @@ -33,6 +33,7 @@ function reduceMetamask (state, action) { amount: '0x0', memo: '', errors: {}, + editingTransactionId: null, }, coinOptions: {}, }, state.metamask) @@ -108,6 +109,14 @@ function reduceMetamask (state, action) { } return newState + case actions.EDIT_TX: + return extend(metamaskState, { + send: { + ...metamaskState.send, + editingTransactionId: action.value, + }, + }) + case actions.SHOW_NEW_VAULT_SEED: return extend(metamaskState, { isUnlocked: true, @@ -262,6 +271,20 @@ function reduceMetamask (state, action) { }, }) + case actions.UPDATE_TRANSACTION_PARAMS: + const { id, value } = action + let { selectedAddressTxList } = metamaskState + selectedAddressTxList = selectedAddressTxList.map(tx => { + if (tx.id === id) { + tx.txParams = value + } + return tx + }) + + return extend(metamaskState, { + selectedAddressTxList, + }) + case actions.PAIR_UPDATE: const { value: { marketinfo: pairMarketInfo } } = action return extend(metamaskState, { diff --git a/ui/app/send-v2.js b/ui/app/send-v2.js index 690af7e5c..b7e904ea8 100644 --- a/ui/app/send-v2.js +++ b/ui/app/send-v2.js @@ -93,6 +93,9 @@ SendTransactionScreen.prototype.componentWillMount = function () { updateGasTotal, from, tokenContract, + editingTransactionId, + gasPrice, + gasLimit, } = this.props const { symbol } = selectedToken || {} @@ -102,22 +105,32 @@ SendTransactionScreen.prototype.componentWillMount = function () { const estimateGasParams = getParamsForGasEstimate(selectedAddress, symbol, data) - Promise - .all([ - getGasPrice(), - estimateGas(estimateGasParams), - tokenContract && tokenContract.balanceOf(from.address), - ]) - .then(([gasPrice, gas, usersToken]) => { + let newGasTotal + if (!editingTransactionId) { + Promise + .all([ + getGasPrice(), + estimateGas(estimateGasParams), + tokenContract && tokenContract.balanceOf(from.address) + ]) + .then(([gasPrice, gas, usersToken]) => { - const newGasTotal = multiplyCurrencies(gas, gasPrice, { - toNumericBase: 'hex', - multiplicandBase: 16, - multiplierBase: 16, + const newGasTotal = multiplyCurrencies(gas, gasPrice, { + toNumericBase: 'hex', + multiplicandBase: 16, + multiplierBase: 16, + }) + updateGasTotal(newGasTotal) + this.updateSendTokenBalance(usersToken) }) - updateGasTotal(newGasTotal) - this.updateSendTokenBalance(usersToken) + } else { + newGasTotal = multiplyCurrencies(gasLimit, gasPrice, { + toNumericBase: 'hex', + multiplicandBase: 16, + multiplierBase: 16, }) + updateGasTotal(newGasTotal) + } } SendTransactionScreen.prototype.componentDidUpdate = function (prevProps) { @@ -394,6 +407,7 @@ SendTransactionScreen.prototype.renderAmountRow = function () { errors, amount, } = this.props + return h('div.send-v2__form-row', [ h('div.send-v2__form-label', [ @@ -551,9 +565,12 @@ SendTransactionScreen.prototype.onSubmit = function (event) { gasPrice, signTokenTx, signTx, + updateAndApproveTx, selectedToken, - clearSend, + toAccounts, + editingTransactionId, errors: { amount: amountError, to: toError }, + backToConfirmScreen, } = this.props const noErrors = !amountError && toError === null @@ -564,6 +581,11 @@ SendTransactionScreen.prototype.onSubmit = function (event) { this.addToAddressBookIfNew(to) + if (editingTransactionId) { + backToConfirmScreen(editingTransactionId) + return + } + const txParams = { from, value: '0', @@ -576,8 +598,6 @@ SendTransactionScreen.prototype.onSubmit = function (event) { txParams.to = to } - clearSend() - selectedToken ? signTokenTx(selectedToken.address, to, amount, txParams) : signTx(txParams) From 0a91671ff69957596abbcffb7d20c89f144d7a69 Mon Sep 17 00:00:00 2001 From: Dan Date: Wed, 8 Nov 2017 15:48:27 -0330 Subject: [PATCH 40/46] Fix lint errors. --- ui/app/components/currency-input.js | 2 +- .../pending-tx/confirm-send-ether.js | 19 +++++++++---------- ui/app/reducers/metamask.js | 8 ++++---- ui/app/send-v2.js | 4 +--- 4 files changed, 15 insertions(+), 18 deletions(-) diff --git a/ui/app/components/currency-input.js b/ui/app/components/currency-input.js index 016f14d3e..66880091f 100644 --- a/ui/app/components/currency-input.js +++ b/ui/app/components/currency-input.js @@ -40,7 +40,7 @@ function sanitizeDecimal (val) { // sanitizeValue('.200') -> '0.200' // sanitizeValue('a.b.1.c,89.123') -> '0.189123' function sanitizeValue (value) { - let [integer, point, decimal] = (/([^.]*)([.]?)([^.]*)/).exec(value) + let [ , integer, point, decimal] = (/([^.]*)([.]?)([^.]*)/).exec(value) integer = sanitizeInteger(integer) || '0' decimal = sanitizeDecimal(decimal) diff --git a/ui/app/components/pending-tx/confirm-send-ether.js b/ui/app/components/pending-tx/confirm-send-ether.js index 8b5801aec..b4d955b80 100644 --- a/ui/app/components/pending-tx/confirm-send-ether.js +++ b/ui/app/components/pending-tx/confirm-send-ether.js @@ -40,16 +40,15 @@ function mapDispatchToProps (dispatch) { const { gas: gasLimit, gasPrice, - from, to, - value: amount + value: amount, } = txParams dispatch(actions.editTx(id)) - dispatch(actions.updateGasLimit(gasLimit)), - dispatch(actions.updateGasPrice(gasPrice)), - dispatch(actions.updateSendTo(to)), - dispatch(actions.updateSendAmount(amount)), - dispatch(actions.updateSendErrors({ to: null, amount: null })), + dispatch(actions.updateGasLimit(gasLimit)) + dispatch(actions.updateGasPrice(gasPrice)) + dispatch(actions.updateSendTo(to)) + dispatch(actions.updateSendAmount(amount)) + dispatch(actions.updateSendErrors({ to: null, amount: null })) dispatch(actions.showSendPage()) }, cancelTransaction: ({ id }) => dispatch(actions.cancelTx({ id })), @@ -176,7 +175,7 @@ ConfirmSendEther.prototype.getData = function () { } ConfirmSendEther.prototype.render = function () { - const { editTransaction, selectedAddress, currentCurrency, clearSend } = this.props + const { editTransaction, currentCurrency, clearSend } = this.props const txMeta = this.gatherTxMeta() const txParams = txMeta.txParams || {} @@ -441,7 +440,7 @@ ConfirmSendEther.prototype.getFormEl = function () { ConfirmSendEther.prototype.gatherTxMeta = function () { const props = this.props const state = this.state - let txData = clone(state.txData) || clone(props.txData) + const txData = clone(state.txData) || clone(props.txData) if (props.send.editingTransactionId) { const { @@ -450,7 +449,7 @@ ConfirmSendEther.prototype.gatherTxMeta = function () { amount: value, gasLimit: gas, gasPrice, - } + }, } = props const { txParams: { from, to } } = txData txData.txParams = { diff --git a/ui/app/reducers/metamask.js b/ui/app/reducers/metamask.js index bc0d0a4b3..56bf1fba6 100644 --- a/ui/app/reducers/metamask.js +++ b/ui/app/reducers/metamask.js @@ -150,9 +150,9 @@ function reduceMetamask (state, action) { case actions.SAVE_ACCOUNT_LABEL: const account = action.value.account const name = action.value.label - var id = {} + const id = {} id[account] = extend(metamaskState.identities[account], { name }) - var identities = extend(metamaskState.identities, id) + const identities = extend(metamaskState.identities, id) return extend(metamaskState, { identities }) case actions.SET_CURRENT_FIAT: @@ -272,10 +272,10 @@ function reduceMetamask (state, action) { }) case actions.UPDATE_TRANSACTION_PARAMS: - const { id, value } = action + const { id: txId, value } = action let { selectedAddressTxList } = metamaskState selectedAddressTxList = selectedAddressTxList.map(tx => { - if (tx.id === id) { + if (tx.id === txId) { tx.txParams = value } return tx diff --git a/ui/app/send-v2.js b/ui/app/send-v2.js index b7e904ea8..0d745c66e 100644 --- a/ui/app/send-v2.js +++ b/ui/app/send-v2.js @@ -111,7 +111,7 @@ SendTransactionScreen.prototype.componentWillMount = function () { .all([ getGasPrice(), estimateGas(estimateGasParams), - tokenContract && tokenContract.balanceOf(from.address) + tokenContract && tokenContract.balanceOf(from.address), ]) .then(([gasPrice, gas, usersToken]) => { @@ -565,9 +565,7 @@ SendTransactionScreen.prototype.onSubmit = function (event) { gasPrice, signTokenTx, signTx, - updateAndApproveTx, selectedToken, - toAccounts, editingTransactionId, errors: { amount: amountError, to: toError }, backToConfirmScreen, From 4671f28476165fec43785ae23352c1e9a0776abc Mon Sep 17 00:00:00 2001 From: Dan Date: Thu, 9 Nov 2017 11:44:32 -0330 Subject: [PATCH 41/46] Allow editing of token transactions. --- .../pending-tx/confirm-send-token.js | 95 +++++++++++++++++-- ui/app/send-v2.js | 7 +- 2 files changed, 91 insertions(+), 11 deletions(-) diff --git a/ui/app/components/pending-tx/confirm-send-token.js b/ui/app/components/pending-tx/confirm-send-token.js index f14da38ef..aab45f2a4 100644 --- a/ui/app/components/pending-tx/confirm-send-token.js +++ b/ui/app/components/pending-tx/confirm-send-token.js @@ -2,9 +2,10 @@ const Component = require('react').Component const { connect } = require('react-redux') const h = require('react-hyperscript') const inherits = require('util').inherits -const abi = require('human-standard-token-abi') +const ethAbi = require('ethereumjs-abi') +const tokenAbi = require('human-standard-token-abi') const abiDecoder = require('abi-decoder') -abiDecoder.addABI(abi) +abiDecoder.addABI(tokenAbi) const actions = require('../../actions') const clone = require('clone') const Identicon = require('../identicon') @@ -24,6 +25,7 @@ const { MIN_GAS_PRICE_HEX } = require('../send/send-constants') const { getTokenExchangeRate, getSelectedAddress, + getSelectedTokenContract, } = require('../../selectors') module.exports = connect(mapStateToProps, mapDispatchToProps)(ConfirmSendToken) @@ -32,6 +34,7 @@ function mapStateToProps (state, ownProps) { const { token: { symbol }, txData } = ownProps const { txParams } = txData || {} const tokenData = txParams.data && abiDecoder.decodeMethod(txParams.data) + const { conversionRate, identities, @@ -47,6 +50,8 @@ function mapStateToProps (state, ownProps) { tokenExchangeRate, tokenData: tokenData || {}, currentCurrency: currentCurrency.toUpperCase(), + send: state.metamask.send, + tokenContract: getSelectedTokenContract(state), } } @@ -57,6 +62,30 @@ function mapDispatchToProps (dispatch, ownProps) { backToAccountDetail: address => dispatch(actions.backToAccountDetail(address)), cancelTransaction: ({ id }) => dispatch(actions.cancelTx({ id })), updateTokenExchangeRate: () => dispatch(actions.updateTokenExchangeRate(symbol)), + editTransaction: txMeta => { + const { token: { address } } = ownProps + const { txParams, id } = txMeta + const tokenData = txParams.data && abiDecoder.decodeMethod(txParams.data) + const { params = [] } = tokenData + const { value } = params[1] || {} + const amount = conversionUtil(value, { + fromNumericBase: 'dec', + toNumericBase: 'hex', + }) + const { + gas: gasLimit, + gasPrice, + to, + } = txParams + dispatch(actions.editTx(id)) + dispatch(actions.updateGasLimit(gasLimit)) + dispatch(actions.updateGasPrice(gasPrice)) + dispatch(actions.updateSendTo(to)) + dispatch(actions.updateSendAmount(amount)) + dispatch(actions.updateSendErrors({ to: null, amount: null })) + dispatch(actions.setSelectedToken(address)) + dispatch(actions.showSendTokenPage()) + }, } } @@ -68,14 +97,33 @@ function ConfirmSendToken () { } ConfirmSendToken.prototype.componentWillMount = function () { + const { tokenContract, selectedAddress } = this.props + tokenContract && tokenContract + .balanceOf(selectedAddress) + .then(usersToken => { + }) this.props.updateTokenExchangeRate() } ConfirmSendToken.prototype.getAmount = function () { - const { conversionRate, tokenExchangeRate, token, tokenData } = this.props + const { + conversionRate, + tokenExchangeRate, + token, + tokenData, + send: { amount, editingTransactionId }, + } = this.props const { params = [] } = tokenData - const { value } = params[1] || {} + let { value } = params[1] || {} const { decimals } = token + + if (editingTransactionId) { + value = conversionUtil(amount, { + fromNumericBase: 'hex', + toNumericBase: 'dec', + }) + } + const sendTokenAmount = calcTokenAmount(value, decimals) return { @@ -242,9 +290,8 @@ ConfirmSendToken.prototype.renderTotalPlusGas = function () { } ConfirmSendToken.prototype.render = function () { - const { backToAccountDetail, selectedAddress } = this.props + const { editTransaction } = this.props const txMeta = this.gatherTxMeta() - const { from: { address: fromAddress, @@ -266,8 +313,8 @@ ConfirmSendToken.prototype.render = function () { h('div.confirm-screen-wrapper.flex-column.flex-grow', [ h('h3.flex-center.confirm-screen-header', [ h('button.confirm-screen-back-button', { - onClick: () => backToAccountDetail(selectedAddress), - }, 'BACK'), + onClick: () => editTransaction(txMeta), + }, 'EDIT'), h('div.confirm-screen-title', 'Confirm Transaction'), h('div.confirm-screen-header-tip'), ]), @@ -389,6 +436,38 @@ ConfirmSendToken.prototype.gatherTxMeta = function () { const state = this.state const txData = clone(state.txData) || clone(props.txData) + if (props.send.editingTransactionId) { + const { + send: { + memo, + amount, + gasLimit: gas, + gasPrice, + }, + } = props + + const { txParams: { from, to } } = txData + + const tokenParams = { + from: ethUtil.addHexPrefix(from), + value: '0', + gas: ethUtil.addHexPrefix(gas), + gasPrice: ethUtil.addHexPrefix(gasPrice), + } + + const data = '0xa9059cbb' + Array.prototype.map.call( + ethAbi.rawEncode(['address', 'uint256'], [to, ethUtil.addHexPrefix(amount)]), + x => ('00' + x.toString(16)).slice(-2) + ).join('') + + txData.txParams = { + ...tokenParams, + to: ethUtil.addHexPrefix(to), + memo: memo && ethUtil.addHexPrefix(memo), + data, + } + } + // log.debug(`UI has defaulted to tx meta ${JSON.stringify(txData)}`) return txData } diff --git a/ui/app/send-v2.js b/ui/app/send-v2.js index 0d745c66e..788ae87b4 100644 --- a/ui/app/send-v2.js +++ b/ui/app/send-v2.js @@ -76,7 +76,6 @@ SendTransactionScreen.prototype.updateSendTokenBalance = function (usersToken) { updateSendTokenBalance, } = this.props const { decimals } = selectedToken || {} - const tokenBalance = calcTokenAmount(usersToken.balance.toString(), decimals) updateSendTokenBalance(tokenBalance) @@ -105,13 +104,14 @@ SendTransactionScreen.prototype.componentWillMount = function () { const estimateGasParams = getParamsForGasEstimate(selectedAddress, symbol, data) + const tokenBalancePromise = tokenContract && tokenContract.balanceOf(from.address) let newGasTotal if (!editingTransactionId) { Promise .all([ getGasPrice(), estimateGas(estimateGasParams), - tokenContract && tokenContract.balanceOf(from.address), + tokenBalancePromise, ]) .then(([gasPrice, gas, usersToken]) => { @@ -130,6 +130,8 @@ SendTransactionScreen.prototype.componentWillMount = function () { multiplierBase: 16, }) updateGasTotal(newGasTotal) + tokenBalancePromise && tokenBalancePromise.then( + usersToken => this.updateSendTokenBalance(usersToken)) } } @@ -363,7 +365,6 @@ SendTransactionScreen.prototype.validateAmount = function (value) { const amount = value let amountError = null - const sufficientBalance = isBalanceSufficient({ amount: selectedToken ? '0x0' : amount, gasTotal, From 9e3f921ba928a948c04b4156daa0a3f752ee2dde Mon Sep 17 00:00:00 2001 From: Dan Date: Fri, 10 Nov 2017 00:19:16 -0330 Subject: [PATCH 42/46] Create single action for updating all of send in redux state. --- ui/app/actions.js | 9 +++++++++ .../components/pending-tx/confirm-send-ether.js | 15 +++++++++------ .../components/pending-tx/confirm-send-token.js | 15 +++++++++------ ui/app/components/send/currency-display.js | 2 +- ui/app/reducers/metamask.js | 10 ++++++++++ 5 files changed, 38 insertions(+), 13 deletions(-) diff --git a/ui/app/actions.js b/ui/app/actions.js index 0d7f03d0e..2ca62c41f 100644 --- a/ui/app/actions.js +++ b/ui/app/actions.js @@ -149,6 +149,7 @@ var actions = { UPDATE_SEND_AMOUNT: 'UPDATE_SEND_AMOUNT', UPDATE_SEND_MEMO: 'UPDATE_SEND_MEMO', UPDATE_SEND_ERRORS: 'UPDATE_SEND_ERRORS', + UPDATE_SEND: 'UPDATE_SEND', CLEAR_SEND: 'CLEAR_SEND', updateGasLimit, updateGasPrice, @@ -159,6 +160,7 @@ var actions = { updateSendAmount, updateSendMemo, updateSendErrors, + updateSend, clearSend, setSelectedAddress, // app messages @@ -632,6 +634,13 @@ function updateSendErrors (error) { } } +function updateSend (newSend) { + return { + type: actions.UPDATE_SEND, + value: newSend, + } +} + function clearSend () { return { type: actions.CLEAR_SEND, diff --git a/ui/app/components/pending-tx/confirm-send-ether.js b/ui/app/components/pending-tx/confirm-send-ether.js index b4d955b80..1264da153 100644 --- a/ui/app/components/pending-tx/confirm-send-ether.js +++ b/ui/app/components/pending-tx/confirm-send-ether.js @@ -43,12 +43,15 @@ function mapDispatchToProps (dispatch) { to, value: amount, } = txParams - dispatch(actions.editTx(id)) - dispatch(actions.updateGasLimit(gasLimit)) - dispatch(actions.updateGasPrice(gasPrice)) - dispatch(actions.updateSendTo(to)) - dispatch(actions.updateSendAmount(amount)) - dispatch(actions.updateSendErrors({ to: null, amount: null })) + dispatch(actions.updateSend({ + gasLimit, + gasPrice, + gasTotal: null, + to, + amount, + errors: { to: null, amount: null }, + editingTransactionId: id, + })) dispatch(actions.showSendPage()) }, cancelTransaction: ({ id }) => dispatch(actions.cancelTx({ id })), diff --git a/ui/app/components/pending-tx/confirm-send-token.js b/ui/app/components/pending-tx/confirm-send-token.js index aab45f2a4..cc2df8299 100644 --- a/ui/app/components/pending-tx/confirm-send-token.js +++ b/ui/app/components/pending-tx/confirm-send-token.js @@ -77,13 +77,16 @@ function mapDispatchToProps (dispatch, ownProps) { gasPrice, to, } = txParams - dispatch(actions.editTx(id)) - dispatch(actions.updateGasLimit(gasLimit)) - dispatch(actions.updateGasPrice(gasPrice)) - dispatch(actions.updateSendTo(to)) - dispatch(actions.updateSendAmount(amount)) - dispatch(actions.updateSendErrors({ to: null, amount: null })) dispatch(actions.setSelectedToken(address)) + dispatch(actions.updateSend({ + gasLimit, + gasPrice, + gasTotal: null, + to, + amount, + errors: { to: null, amount: null }, + editingTransactionId: id, + })) dispatch(actions.showSendTokenPage()) }, } diff --git a/ui/app/components/send/currency-display.js b/ui/app/components/send/currency-display.js index 5bf8d6aa0..819fee0a0 100644 --- a/ui/app/components/send/currency-display.js +++ b/ui/app/components/send/currency-display.js @@ -97,7 +97,7 @@ CurrencyDisplay.prototype.render = function () { onInputChange: newValue => { handleChange(this.getAmount(newValue)) }, - inputRef: input => { this.currencyInput = input; }, + inputRef: input => { this.currencyInput = input }, }), h('span.currency-display__currency-symbol', primaryCurrency), diff --git a/ui/app/reducers/metamask.js b/ui/app/reducers/metamask.js index 56bf1fba6..83161320e 100644 --- a/ui/app/reducers/metamask.js +++ b/ui/app/reducers/metamask.js @@ -257,17 +257,27 @@ function reduceMetamask (state, action) { }, }) + case actions.UPDATE_SEND: + return extend(metamaskState, { + send: { + ...metamaskState.send, + ...action.value, + }, + }) + case actions.CLEAR_SEND: return extend(metamaskState, { send: { gasLimit: null, gasPrice: null, gasTotal: null, + tokenBalance: null, from: '', to: '', amount: '0x0', memo: '', errors: {}, + editingTransactionId: null, }, }) From 59e46e7cb25b7355bd969fe9ab04090f52ec67bc Mon Sep 17 00:00:00 2001 From: Alexander Tseung Date: Mon, 13 Nov 2017 14:20:42 -0800 Subject: [PATCH 43/46] Show tokens with zero balance --- ui/app/components/token-list.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/ui/app/components/token-list.js b/ui/app/components/token-list.js index b6a27fd5a..8e06e0f27 100644 --- a/ui/app/components/token-list.js +++ b/ui/app/components/token-list.js @@ -151,10 +151,7 @@ TokenList.prototype.componentDidUpdate = function (nextProps) { } TokenList.prototype.updateBalances = function (tokens) { - const heldTokens = tokens.filter(token => { - return token.balance !== '0' && token.string !== '0.000' - }) - this.setState({ tokens: heldTokens, isLoading: false }) + this.setState({ tokens, isLoading: false }) } TokenList.prototype.componentWillUnmount = function () { From bbdb35c35a03c42eb4a950756bf280e6e15513b5 Mon Sep 17 00:00:00 2001 From: Dan Date: Wed, 15 Nov 2017 15:00:35 -0330 Subject: [PATCH 44/46] Use currency input component in input number, to improve input behaviour in gas estimator --- ui/app/components/input-number.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/ui/app/components/input-number.js b/ui/app/components/input-number.js index 12dec2957..fd8c5c309 100644 --- a/ui/app/components/input-number.js +++ b/ui/app/components/input-number.js @@ -1,6 +1,7 @@ const Component = require('react').Component const h = require('react-hyperscript') const inherits = require('util').inherits +const CurrencyInput = require('./currency-input') const { addCurrencies, conversionGTE, @@ -50,11 +51,13 @@ InputNumber.prototype.render = function () { const { unitLabel, step = 1, placeholder, value = 0 } = this.props return h('div.customize-gas-input-wrapper', {}, [ - h('input.customize-gas-input', { - placeholder, + h(CurrencyInput, { + className: 'customize-gas-input', value, - step, - onChange: (e) => this.setValue(e.target.value), + placeholder, + onInputChange: newValue => { + this.setValue(newValue) + }, }), h('span.gas-tooltip-input-detail', {}, [unitLabel]), h('div.gas-tooltip-input-arrows', {}, [ From 960cc8abcbdc82ae2b73a82f0faf1658f113a9d3 Mon Sep 17 00:00:00 2001 From: Dan Date: Wed, 15 Nov 2017 15:29:57 -0330 Subject: [PATCH 45/46] Gas customzier does not consider amount when sending tokens. --- ui/app/components/customize-gas-modal/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/app/components/customize-gas-modal/index.js b/ui/app/components/customize-gas-modal/index.js index f01f42fe0..485dacf90 100644 --- a/ui/app/components/customize-gas-modal/index.js +++ b/ui/app/components/customize-gas-modal/index.js @@ -117,7 +117,7 @@ CustomizeGasModal.prototype.validate = function ({ gasTotal, gasLimit }) { let error = null const balanceIsSufficient = isBalanceSufficient({ - amount, + amount: selectedToken ? '0' : amount, gasTotal, balance, selectedToken, From 03968ffafe27e64dcdf58764057cee1d67aa4168 Mon Sep 17 00:00:00 2001 From: Chi Kei Chan Date: Wed, 15 Nov 2017 12:23:39 -0800 Subject: [PATCH 46/46] Version Bump --- app/manifest.json | 2 +- yarn.lock | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/app/manifest.json b/app/manifest.json index 65d7a4811..ff595c717 100644 --- a/app/manifest.json +++ b/app/manifest.json @@ -1,7 +1,7 @@ { "name": "MetaMask", "short_name": "Metamask", - "version": "4.0.3", + "version": "4.0.4", "manifest_version": 2, "author": "https://metamask.io", "description": "Ethereum Browser Extension", diff --git a/yarn.lock b/yarn.lock index 8f63c1fff..f8bd06cab 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8379,6 +8379,10 @@ redux-logger@^3.0.6: dependencies: deep-diff "^0.3.5" +redux-test-utils@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/redux-test-utils/-/redux-test-utils-0.1.3.tgz#0d89100f100f86c7c7214976eaece88e7e45bf74" + redux-thunk@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.2.0.tgz#e615a16e16b47a19a515766133d1e3e99b7852e5"