diff options
-rw-r--r-- | CHANGELOG.md | 3 | ||||
-rw-r--r-- | app/scripts/controllers/blacklist.js | 1 | ||||
-rw-r--r-- | app/scripts/controllers/transactions.js | 4 | ||||
-rw-r--r-- | app/scripts/lib/tx-gas-utils.js | 11 | ||||
-rw-r--r-- | app/scripts/metamask-controller.js | 38 | ||||
-rw-r--r-- | package.json | 1 | ||||
-rw-r--r-- | test/unit/metamask-controller-test.js | 49 | ||||
-rw-r--r-- | test/unit/tx-gas-util-test.js | 32 | ||||
-rw-r--r-- | ui/app/keychains/hd/restore-vault.js | 4 |
9 files changed, 135 insertions, 8 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index dc96203be..cacc4b522 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,10 +2,13 @@ ## Current Master +- Remove recipient field if application initializes a tx with an empty string, or 0x, and tx data. Throw an error with the same condition, but without tx data. +- Improve gas price suggestion to be closer to the lowest that will be accepted. - 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. +- Fix bug where incorrectly inputting seed phrase would prevent any future attempts from succeeding. ## 3.13.3 2017-12-14 diff --git a/app/scripts/controllers/blacklist.js b/app/scripts/controllers/blacklist.js index dd671943f..33c31dab9 100644 --- a/app/scripts/controllers/blacklist.js +++ b/app/scripts/controllers/blacklist.js @@ -57,3 +57,4 @@ class BlacklistController { } module.exports = BlacklistController + diff --git a/app/scripts/controllers/transactions.js b/app/scripts/controllers/transactions.js index 7c7efb84d..469deb670 100644 --- a/app/scripts/controllers/transactions.js +++ b/app/scripts/controllers/transactions.js @@ -32,6 +32,7 @@ module.exports = class TransactionController extends EventEmitter { this.provider = opts.provider this.blockTracker = opts.blockTracker this.signEthTx = opts.signTransaction + this.getGasPrice = opts.getGasPrice this.memStore = new ObservableStore({}) this.query = new EthQuery(this.provider) @@ -179,7 +180,8 @@ module.exports = class TransactionController extends EventEmitter { // ensure value txMeta.gasPriceSpecified = Boolean(txParams.gasPrice) txMeta.nonceSpecified = Boolean(txParams.nonce) - const gasPrice = txParams.gasPrice || await this.query.gasPrice() + const gasPrice = txParams.gasPrice || this.getGasPrice ? this.getGasPrice() + : await this.query.gasPrice() txParams.gasPrice = ethUtil.addHexPrefix(gasPrice.toString(16)) txParams.value = txParams.value || '0x0' // set gasLimit diff --git a/app/scripts/lib/tx-gas-utils.js b/app/scripts/lib/tx-gas-utils.js index ccf8bb1b1..5e49fdb22 100644 --- a/app/scripts/lib/tx-gas-utils.js +++ b/app/scripts/lib/tx-gas-utils.js @@ -81,6 +81,7 @@ module.exports = class txProvideUtil { } async validateTxParams (txParams) { + this.validateRecipient(txParams) if ('value' in txParams) { const value = txParams.value.toString() if (value.includes('-')) { @@ -92,4 +93,14 @@ module.exports = class txProvideUtil { } } } + validateRecipient (txParams) { + if (txParams.to === '0x') { + if (txParams.data) { + delete txParams.to + } else { + throw new Error('Invalid recipient address') + } + } + return txParams + } } diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 23f2a1598..66738db51 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -35,13 +35,15 @@ const accountImporter = require('./account-import-strategies') const getBuyEthUrl = require('./lib/buy-eth-url') const Mutex = require('await-semaphore').Mutex const version = require('../manifest.json').version +const BN = require('ethereumjs-util').BN +const GWEI_BN = new BN('1000000000') +const percentile = require('percentile') module.exports = class MetamaskController extends EventEmitter { constructor (opts) { super() - this.sendUpdate = debounce(this.privateSendUpdate.bind(this), 200) this.opts = opts @@ -139,6 +141,7 @@ module.exports = class MetamaskController extends EventEmitter { provider: this.provider, blockTracker: this.blockTracker, ethQuery: this.ethQuery, + getGasPrice: this.getGasPrice.bind(this), }) this.txController.on('newUnapprovedTx', opts.showUnapprovedTx.bind(opts)) @@ -484,6 +487,26 @@ module.exports = class MetamaskController extends EventEmitter { this.emit('update', this.getState()) } + getGasPrice () { + const { recentBlocksController } = this + const { recentBlocks } = recentBlocksController.store.getState() + const lowestPrices = recentBlocks.map((block) => { + if (!block.gasPrices) { + return new BN(0) + } + return block.gasPrices + .map(hexPrefix => hexPrefix.substr(2)) + .map(hex => new BN(hex, 16)) + .sort((a, b) => { + return a.gt(b) ? 1 : -1 + })[0] + }) + .map(number => number.div(GWEI_BN).toNumber()) + const percentileNum = percentile(50, lowestPrices) + const percentileNumBn = new BN(percentileNum) + return '0x' + percentileNumBn.mul(GWEI_BN).toString(16) + } + // // Vault Management // @@ -513,10 +536,15 @@ module.exports = class MetamaskController extends EventEmitter { async createNewVaultAndRestore (password, seed) { const release = await this.createVaultMutex.acquire() - const vault = await this.keyringController.createNewVaultAndRestore(password, seed) - this.selectFirstIdentity(vault) - release() - return vault + try { + const vault = await this.keyringController.createNewVaultAndRestore(password, seed) + this.selectFirstIdentity(vault) + release() + return vault + } catch (err) { + release() + throw err + } } selectFirstIdentity (vault) { diff --git a/package.json b/package.json index 1d76585b3..df532b01e 100644 --- a/package.json +++ b/package.json @@ -119,6 +119,7 @@ "obj-multiplex": "^1.0.0", "obs-store": "^3.0.0", "once": "^1.3.3", + "percentile": "^1.2.0", "ping-pong-stream": "^1.0.0", "pojo-migrator": "^2.1.0", "polyfill-crypto.getrandomvalues": "^1.0.0", diff --git a/test/unit/metamask-controller-test.js b/test/unit/metamask-controller-test.js index fd420a70f..9ec7cd0af 100644 --- a/test/unit/metamask-controller-test.js +++ b/test/unit/metamask-controller-test.js @@ -3,6 +3,8 @@ const sinon = require('sinon') const clone = require('clone') const MetaMaskController = require('../../app/scripts/metamask-controller') const firstTimeState = require('../../app/scripts/first-time-state') +const BN = require('ethereumjs-util').BN +const GWEI_BN = new BN('1000000000') describe('MetaMaskController', function () { const noop = () => {} @@ -39,17 +41,44 @@ describe('MetaMaskController', function () { beforeEach(function () { sinon.spy(metamaskController.keyringController, 'createNewVaultAndKeychain') + sinon.spy(metamaskController.keyringController, 'createNewVaultAndRestore') }) afterEach(function () { metamaskController.keyringController.createNewVaultAndKeychain.restore() + metamaskController.keyringController.createNewVaultAndRestore.restore() + }) + + describe('#getGasPrice', function () { + it('gives the 50th percentile lowest accepted gas price from recentBlocksController', async function () { + const realRecentBlocksController = metamaskController.recentBlocksController + metamaskController.recentBlocksController = { + store: { + getState: () => { + return { + recentBlocks: [ + { gasPrices: [ '0x3b9aca00', '0x174876e800'] }, + { gasPrices: [ '0x3b9aca00', '0x174876e800'] }, + { gasPrices: [ '0x174876e800', '0x174876e800' ]}, + { gasPrices: [ '0x174876e800', '0x174876e800' ]}, + ] + } + } + } + } + + const gasPrice = metamaskController.getGasPrice() + assert.equal(gasPrice, '0x3b9aca00', 'accurately estimates 50th percentile accepted gas price') + + metamaskController.recentBlocksController = realRecentBlocksController + }) }) describe('#createNewVaultAndKeychain', function () { it('can only create new vault on keyringController once', async function () { - const selectStub = sinon.stub(metamaskController, 'selectFirstIdentity') + const password = 'a-fake-password' const first = await metamaskController.createNewVaultAndKeychain(password) @@ -60,6 +89,22 @@ describe('MetaMaskController', function () { selectStub.reset() }) }) + + describe('#createNewVaultAndRestore', function () { + it('should be able to call newVaultAndRestore despite a mistake.', async function () { + // const selectStub = sinon.stub(metamaskController, 'selectFirstIdentity') + + const password = 'what-what-what' + const wrongSeed = 'debris dizzy just program just float decrease vacant alarm reduce speak stadiu' + const rightSeed = 'debris dizzy just program just float decrease vacant alarm reduce speak stadium' + const first = await metamaskController.createNewVaultAndRestore(password, wrongSeed) + .catch((e) => { + return + }) + const second = await metamaskController.createNewVaultAndRestore(password, rightSeed) + + assert(metamaskController.keyringController.createNewVaultAndRestore.calledTwice) + }) + }) }) }) - diff --git a/test/unit/tx-gas-util-test.js b/test/unit/tx-gas-util-test.js new file mode 100644 index 000000000..ccef31359 --- /dev/null +++ b/test/unit/tx-gas-util-test.js @@ -0,0 +1,32 @@ +const assert = require('assert') +const TxGasUtils = require('../../app/scripts/lib/tx-gas-utils') +const { createStubedProvider } = require('../stub/provider') + +describe('Tx Gas Util', function () { + let txGasUtil, provider, providerResultStub + beforeEach(function () { + providerResultStub = {} + provider = createStubedProvider(providerResultStub) + txGasUtil = new TxGasUtils({ + provider, + }) + }) + + it('removes recipient for txParams with 0x when contract data is provided', function () { + const zeroRecipientandDataTxParams = { + from: '0x1678a085c290ebd122dc42cba69373b5953b831d', + to: '0x', + data: 'bytecode', + } + const sanitizedTxParams = txGasUtil.validateRecipient(zeroRecipientandDataTxParams) + assert.deepEqual(sanitizedTxParams, { from: '0x1678a085c290ebd122dc42cba69373b5953b831d', data: 'bytecode' }, 'no recipient with 0x') + }) + + it('should error when recipient is 0x', function () { + const zeroRecipientTxParams = { + from: '0x1678a085c290ebd122dc42cba69373b5953b831d', + to: '0x', + } + assert.throws(() => { txGasUtil.validateRecipient(zeroRecipientTxParams) }, Error, 'Invalid recipient address') + }) +}) diff --git a/ui/app/keychains/hd/restore-vault.js b/ui/app/keychains/hd/restore-vault.js index 06e51d9b3..24b37a83d 100644 --- a/ui/app/keychains/hd/restore-vault.js +++ b/ui/app/keychains/hd/restore-vault.js @@ -149,4 +149,8 @@ RestoreVaultScreen.prototype.createNewVaultAndRestore = function () { this.warning = null this.props.dispatch(actions.displayWarning(this.warning)) this.props.dispatch(actions.createNewVaultAndRestore(password, seed)) + .catch((err) => { + log.error(err.message) + }) + } |