aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md3
-rw-r--r--app/scripts/controllers/blacklist.js1
-rw-r--r--app/scripts/controllers/transactions.js4
-rw-r--r--app/scripts/lib/tx-gas-utils.js11
-rw-r--r--app/scripts/metamask-controller.js38
-rw-r--r--package.json1
-rw-r--r--test/unit/metamask-controller-test.js49
-rw-r--r--test/unit/tx-gas-util-test.js32
-rw-r--r--ui/app/keychains/hd/restore-vault.js4
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)
+ })
+
}