diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c1c1dcb4..dc96203be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,10 @@ ## Current Master +- Throw an error if a application tries to submit a tx whose value is a decimal, and inform that it should be in wei. - Fix bug that prevented updating custom token details. - No longer mark long-pending transactions as failed, since we now have button to retry with higher gas. +- Fix rounding error when specifying an ether amount that has too much precision. ## 3.13.3 2017-12-14 diff --git a/app/scripts/lib/pending-tx-tracker.js b/app/scripts/lib/pending-tx-tracker.js index 7956a3329..e8869e6b8 100644 --- a/app/scripts/lib/pending-tx-tracker.js +++ b/app/scripts/lib/pending-tx-tracker.js @@ -178,7 +178,8 @@ module.exports = class PendingTransactionTracker extends EventEmitter { } async _checkIfNonceIsTaken (txMeta) { - const completed = this.getCompletedTransactions() + const address = txMeta.txParams.from + const completed = this.getCompletedTransactions(address) const sameNonce = completed.filter((otherMeta) => { return otherMeta.txParams.nonce === txMeta.txParams.nonce }) diff --git a/app/scripts/lib/tx-gas-utils.js b/app/scripts/lib/tx-gas-utils.js index 56bee19f7..ccf8bb1b1 100644 --- a/app/scripts/lib/tx-gas-utils.js +++ b/app/scripts/lib/tx-gas-utils.js @@ -26,7 +26,7 @@ module.exports = class txProvideUtil { err.message.includes('Transaction execution error.') || err.message.includes('gas required exceeds allowance or always failing transaction') ) - if ( simulationFailed ) { + if (simulationFailed) { txMeta.simulationFails = true return txMeta } @@ -81,8 +81,15 @@ module.exports = class txProvideUtil { } async validateTxParams (txParams) { - if (('value' in txParams) && txParams.value.indexOf('-') === 0) { - throw new Error(`Invalid transaction value of ${txParams.value} not a positive number.`) + if ('value' in txParams) { + const value = txParams.value.toString() + if (value.includes('-')) { + throw new Error(`Invalid transaction value of ${txParams.value} not a positive number.`) + } + + if (value.includes('.')) { + throw new Error(`Invalid transaction value of ${txParams.value} number must be in wei`) + } } } } diff --git a/app/scripts/notice-controller.js b/app/scripts/notice-controller.js index db2b8c4f4..14a63eae7 100644 --- a/app/scripts/notice-controller.js +++ b/app/scripts/notice-controller.js @@ -77,7 +77,7 @@ module.exports = class NoticeController extends EventEmitter { return uniqBy(oldNotices.concat(newNotices), 'id') } - _filterNotices(notices) { + _filterNotices (notices) { return notices.filter((newNotice) => { if ('version' in newNotice) { const satisfied = semver.satisfies(this.version, newNotice.version) diff --git a/old-ui/app/send.js b/old-ui/app/send.js index 9ab064aea..b40910236 100644 --- a/old-ui/app/send.js +++ b/old-ui/app/send.js @@ -247,10 +247,26 @@ SendTransactionScreen.prototype.onSubmit = function () { const recipient = state.recipient || document.querySelector('input[name="address"]').value.replace(/^[.\s]+|[.\s]+$/g, '') const nickname = state.nickname || ' ' const input = document.querySelector('input[name="amount"]').value + const parts = input.split('') + + let message + + if (isNaN(input) || input === '') { + message = 'Invalid ether value.' + return this.props.dispatch(actions.displayWarning(message)) + } + + if (parts[1]) { + var decimal = parts[1] + if (decimal.length > 18) { + message = 'Ether amount is too precise.' + return this.props.dispatch(actions.displayWarning(message)) + } + } + const value = util.normalizeEthStringToWei(input) const txData = document.querySelector('input[name="txData"]').value const balance = this.props.balance - let message if (value.gt(balance)) { message = 'Insufficient funds.' diff --git a/package.json b/package.json index c0d938484..5c56d2e1b 100644 --- a/package.json +++ b/package.json @@ -239,7 +239,7 @@ "testem": "^1.10.3", "uglifyify": "^4.0.2", "vinyl-buffer": "^1.0.0", - "vinyl-source-stream": "^1.1.0", + "vinyl-source-stream": "^2.0.0", "watchify": "^3.9.0" }, "engines": { diff --git a/test/unit/pending-tx-test.js b/test/unit/pending-tx-test.js index 961fa6baf..64547a505 100644 --- a/test/unit/pending-tx-test.js +++ b/test/unit/pending-tx-test.js @@ -329,7 +329,7 @@ describe('PendingTransactionTracker', function () { it('should publish the transaction if the number of blocks since last retry exceeds the last set limit', function (done) { const enoughBalance = '0x100000' const mockLatestBlockNumber = '0x11' - + pendingTxTracker._resubmitTx(txMetaToTestExponentialBackoff, mockLatestBlockNumber) .then(() => done()) .catch((err) => { @@ -339,5 +339,64 @@ describe('PendingTransactionTracker', function () { assert.equal(pendingTxTracker.publishTransaction.callCount, 1, 'Should call publish transaction') }) - }) + }) + + describe('#_checkIfNonceIsTaken', function () { + beforeEach ( function () { + let confirmedTxList = [{ + id: 1, + hash: '0x0593ee121b92e10d63150ad08b4b8f9c7857d1bd160195ee648fb9a0f8d00eeb', + status: 'confirmed', + txParams: { + from: '0x1678a085c290ebd122dc42cba69373b5953b831d', + nonce: '0x1', + value: '0xfffff', + }, + rawTx: '0xf86c808504a817c800827b0d940c62bb85faa3311a998d3aba8098c1235c564966880de0b6b3a7640000802aa08ff665feb887a25d4099e40e11f0fef93ee9608f404bd3f853dd9e84ed3317a6a02ec9d3d1d6e176d4d2593dd760e74ccac753e6a0ea0d00cc9789d0d7ff1f471d', + }, { + id: 2, + hash: '0x0593ee121b92e10d63150ad08b4b8f9c7857d1bd160195ee648fb9a0f8d00eeb', + status: 'confirmed', + txParams: { + from: '0x1678a085c290ebd122dc42cba69373b5953b831d', + nonce: '0x2', + value: '0xfffff', + }, + rawTx: '0xf86c808504a817c800827b0d940c62bb85faa3311a998d3aba8098c1235c564966880de0b6b3a7640000802aa08ff665feb887a25d4099e40e11f0fef93ee9608f404bd3f853dd9e84ed3317a6a02ec9d3d1d6e176d4d2593dd760e74ccac753e6a0ea0d00cc9789d0d7ff1f471d', + }] + pendingTxTracker.getCompletedTransactions = (address) => { + if (!address) throw new Error('unless behavior has changed #_checkIfNonceIsTaken needs a filtered list of transactions to see if the nonce is taken') + return confirmedTxList + } + }) + + it('should return false if nonce has not been taken', function (done) { + pendingTxTracker._checkIfNonceIsTaken({ + txParams: { + from: '0x1678a085c290ebd122dc42cba69373b5953b831d', + nonce: '0x3', + value: '0xfffff', + }, + }) + .then((taken) => { + assert.ok(!taken) + done() + }) + .catch(done) + }) + + it('should return true if nonce has been taken', function (done) { + pendingTxTracker._checkIfNonceIsTaken({ + txParams: { + from: '0x1678a085c290ebd122dc42cba69373b5953b831d', + nonce: '0x2', + value: '0xfffff', + }, + }).then((taken) => { + assert.ok(taken) + done() + }) + .catch(done) + }) + }) }) diff --git a/test/unit/util_test.js b/test/unit/util_test.js index 3a8b6bdfd..59048975a 100644 --- a/test/unit/util_test.js +++ b/test/unit/util_test.js @@ -201,6 +201,18 @@ describe('util', function () { var output = util.normalizeEthStringToWei(input) assert.equal(output.toString(10), ethInWei) }) + + it('should account for overflow numbers gracefully by dropping extra precision.', function () { + var input = '1.11111111111111111111' + var output = util.normalizeEthStringToWei(input) + assert.equal(output.toString(10), '1111111111111111111') + }) + + it('should not truncate very exact wei values that do not have extra precision.', function () { + var input = '1.100000000000000001' + var output = util.normalizeEthStringToWei(input) + assert.equal(output.toString(10), '1100000000000000001') + }) }) describe('#normalizeNumberToWei', function () { diff --git a/ui/app/app.js b/ui/app/app.js index 1f40eccbe..c6fce0e47 100644 --- a/ui/app/app.js +++ b/ui/app/app.js @@ -360,11 +360,6 @@ App.prototype.renderPrimary = function () { }) } - if (props.seedWords) { - log.debug('rendering seed words') - return h(HDCreateVaultComplete, {key: 'HDCreateVaultComplete'}) - } - // show initialize screen if (!props.isInitialized || props.forgottenPassword) { // show current view @@ -389,6 +384,12 @@ App.prototype.renderPrimary = function () { }) } + // show seed words screen + if (props.seedWords) { + log.debug('rendering seed words') + return h(HDCreateVaultComplete, {key: 'HDCreateVaultComplete'}) + } + // show current view switch (props.currentView.name) { diff --git a/ui/app/util.js b/ui/app/util.js index 82a5f9f29..70c503550 100644 --- a/ui/app/util.js +++ b/ui/app/util.js @@ -211,6 +211,9 @@ function normalizeEthStringToWei (str) { while (decimal.length < 18) { decimal += '0' } + if (decimal.length > 18) { + decimal = decimal.slice(0, 18) + } const decimalBN = new ethUtil.BN(decimal, 10) eth = eth.add(decimalBN) }