aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDan Finlay <dan@danfinlay.com>2016-10-21 00:49:49 +0800
committerDan Finlay <dan@danfinlay.com>2016-10-21 00:49:49 +0800
commit331bb735c7e9db0aa3af66e4a994e6eb9762af81 (patch)
tree6d51948907cbfc0c002bfdc4698ec99fe100e831
parent94f25543691b1a26f1b33a9074ba65cb722eff48 (diff)
parent12698b946221c1145d1fe966406adb7265e6b9d2 (diff)
downloadtangerine-wallet-browser-331bb735c7e9db0aa3af66e4a994e6eb9762af81.tar.gz
tangerine-wallet-browser-331bb735c7e9db0aa3af66e4a994e6eb9762af81.tar.zst
tangerine-wallet-browser-331bb735c7e9db0aa3af66e4a994e6eb9762af81.zip
Merge branch 'i328-MultiVault' of github.com:MetaMask/metamask-plugin into i328-MultiVault
-rw-r--r--CHANGELOG.md11
-rw-r--r--app/manifest.json2
-rw-r--r--app/scripts/contentscript.js36
-rw-r--r--app/scripts/inpage.js21
-rw-r--r--app/scripts/keyring-controller.js44
-rw-r--r--app/scripts/lib/auto-reload.js22
-rw-r--r--app/scripts/lib/config-manager.js44
-rw-r--r--app/scripts/lib/id-management.js7
-rw-r--r--app/scripts/lib/idStore.js14
-rw-r--r--app/scripts/lib/inpage-provider.js17
-rw-r--r--app/scripts/lib/obj-multiplex.js8
-rw-r--r--app/scripts/lib/port-stream.js3
-rw-r--r--app/scripts/metamask-controller.js13
-rw-r--r--mock-dev.js2
-rw-r--r--package.json5
-rw-r--r--test/unit/actions/restore_vault_test.js2
-rw-r--r--test/unit/actions/tx_test.js8
-rw-r--r--test/unit/idStore-test.js18
-rw-r--r--ui-dev.js2
-rw-r--r--ui/app/actions.js72
-rw-r--r--ui/app/app.js4
-rw-r--r--ui/app/components/pending-tx-details.js2
-rw-r--r--ui/app/components/range-slider.js58
-rw-r--r--ui/app/new-keychain.js33
-rw-r--r--ui/app/reducers/app.js10
-rw-r--r--ui/app/send.js143
-rw-r--r--ui/index.js2
27 files changed, 483 insertions, 120 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8aafb47df..d6d400b66 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,17 @@
## Current Master
+## 2.13.5 2016-10-18
+
+- Increase default max gas to `100000` over the RPC's `estimateGas` response.
+- Fix bug where slow-loading dapps would sometimes trigger infinite reload loops.
+
+## 2.13.4 2016-10-17
+
+- Add custom transaction fee field to send form.
+- Fix bug where web3 was being injected into XML files.
+- Fix bug where changing network would not reload current Dapps.
+
## 2.13.3 2016-10-4
- Fix bug where log queries were filtered out.
diff --git a/app/manifest.json b/app/manifest.json
index badeb7cb2..8f5a34ea6 100644
--- a/app/manifest.json
+++ b/app/manifest.json
@@ -1,7 +1,7 @@
{
"name": "MetaMask",
"short_name": "Metamask",
- "version": "2.13.3",
+ "version": "2.13.5",
"manifest_version": 2,
"author": "https://metamask.io",
"description": "Ethereum Browser Extension",
diff --git a/app/scripts/contentscript.js b/app/scripts/contentscript.js
index b3a560c88..e2a968ac9 100644
--- a/app/scripts/contentscript.js
+++ b/app/scripts/contentscript.js
@@ -1,4 +1,5 @@
const LocalMessageDuplexStream = require('post-message-stream')
+const PongStream = require('ping-pong-stream/pong')
const PortStream = require('./lib/port-stream.js')
const ObjectMultiplex = require('./lib/obj-multiplex')
const extension = require('./lib/extension')
@@ -51,20 +52,35 @@ function setupStreams(){
// forward communication plugin->inpage
pageStream.pipe(pluginStream).pipe(pageStream)
- // connect contentscript->inpage reload stream
+ // setup local multistream channels
var mx = ObjectMultiplex()
mx.on('error', console.error)
- mx.pipe(pageStream)
- var reloadStream = mx.createStream('reload')
- reloadStream.on('error', console.error)
+ mx.pipe(pageStream).pipe(mx)
+
+ // connect ping stream
+ var pongStream = new PongStream({ objectMode: true })
+ pongStream.pipe(mx.createStream('pingpong')).pipe(pongStream)
+
+ // ignore unused channels (handled by background)
+ mx.ignoreStream('provider')
+ mx.ignoreStream('publicConfig')
+ mx.ignoreStream('reload')
- // if we lose connection with the plugin, trigger tab refresh
- pluginStream.on('close', function () {
- reloadStream.write({ method: 'reset' })
- })
}
function shouldInjectWeb3(){
- var shouldInject = (window.location.href.indexOf('.pdf') === -1)
- return shouldInject
+ return isAllowedSuffix(window.location.href)
+}
+
+function isAllowedSuffix(testCase) {
+ var prohibitedTypes = ['xml', 'pdf']
+ var currentUrl = window.location.href
+ var currentRegex
+ for (let i = 0; i < prohibitedTypes.length; i++) {
+ currentRegex = new RegExp(`\.${prohibitedTypes[i]}$`)
+ if (currentRegex.test(currentUrl)) {
+ return false
+ }
+ }
+ return true
}
diff --git a/app/scripts/inpage.js b/app/scripts/inpage.js
index 28a1223ac..85dd70b4d 100644
--- a/app/scripts/inpage.js
+++ b/app/scripts/inpage.js
@@ -2,6 +2,8 @@
cleanContextForImports()
require('web3/dist/web3.min.js')
const LocalMessageDuplexStream = require('post-message-stream')
+const PingStream = require('ping-pong-stream/ping')
+const endOfStream = require('end-of-stream')
const setupDappAutoReload = require('./lib/auto-reload.js')
const MetamaskInpageProvider = require('./lib/inpage-provider.js')
restoreContextAfterImports()
@@ -29,13 +31,22 @@ web3.setProvider = function () {
console.log('MetaMask - overrode web3.setProvider')
}
console.log('MetaMask - injected web3')
+// export global web3, with usage-detection reload fn
+var triggerReload = setupDappAutoReload(web3)
-//
-// export global web3 with auto dapp reload
-//
-
+// listen for reset requests from metamask
var reloadStream = inpageProvider.multiStream.createStream('reload')
-setupDappAutoReload(web3, reloadStream)
+reloadStream.once('data', triggerReload)
+
+// setup ping timeout autoreload
+// LocalMessageDuplexStream does not self-close, so reload if pingStream fails
+var pingChannel = inpageProvider.multiStream.createStream('pingpong')
+var pingStream = new PingStream({ objectMode: true })
+// wait for first successful reponse
+metamaskStream.once('_data', function(){
+ pingStream.pipe(pingChannel).pipe(pingStream)
+})
+endOfStream(pingStream, triggerReload)
// set web3 defaultAcount
inpageProvider.publicConfigStore.subscribe(function (state) {
diff --git a/app/scripts/keyring-controller.js b/app/scripts/keyring-controller.js
index d96b9c101..db7e5e61e 100644
--- a/app/scripts/keyring-controller.js
+++ b/app/scripts/keyring-controller.js
@@ -1,7 +1,7 @@
-const scrypt = require('scrypt-async')
-const bitcore = require('bitcore-lib')
-const configManager = require('./lib/config-manager')
const EventEmitter = require('events').EventEmitter
+const encryptor = require('./lib/encryptor')
+const messageManager = require('./lib/message-manager')
+
module.exports = class KeyringController extends EventEmitter {
@@ -12,12 +12,17 @@ module.exports = class KeyringController extends EventEmitter {
this.keyChains = []
}
+ keyFromPassword(password, callback) {
+ deriveKeyFromPassword(password, callback);
+ }
+
+ // Takes a pw and callback, returns a password-dervied key
getKeyForPassword(password, callback) {
let salt = this.configManager.getSalt()
if (!salt) {
salt = generateSalt(32)
- configManager.setSalt(salt)
+ this.configManager.setSalt(salt)
}
var logN = 14
@@ -39,7 +44,21 @@ module.exports = class KeyringController extends EventEmitter {
}
getState() {
- return {}
+ return {
+ isInitialized: !!this.key,
+ isUnlocked: !!this.key,
+ isConfirmed: true, // this.configManager.getConfirmed(),
+ isEthConfirmed: this.configManager.getShouldntShowWarning(),
+ unconfTxs: this.configManager.unconfirmedTxs(),
+ transactions: this.configManager.getTxList(),
+ unconfMsgs: messageManager.unconfirmedMsgs(),
+ messages: messageManager.getMsgList(),
+ selectedAddress: this.configManager.getSelectedAccount(),
+ shapeShiftTxList: this.configManager.getShapeShiftTxList(),
+ currentFiat: this.configManager.getCurrentFiat(),
+ conversionRate: this.configManager.getConversionRate(),
+ conversionDate: this.configManager.getConversionDate(),
+ }
}
setStore(ethStore) {
@@ -47,9 +66,22 @@ module.exports = class KeyringController extends EventEmitter {
}
createNewVault(password, entropy, cb) {
- cb()
+ encryptor.keyFromPassword(password)
+ .then((key) => {
+ this.key = key
+ return encryptor.encryptWithKey(key, {})
+ })
+ .then((encryptedString) => {
+ this.configManager.setVault(encryptedString)
+ cb(null, [])
+ })
+ .catch((err) => {
+ cb(err)
+ })
}
+
+
submitPassword(password, cb) {
cb()
}
diff --git a/app/scripts/lib/auto-reload.js b/app/scripts/lib/auto-reload.js
index c4c8053f0..3c90905db 100644
--- a/app/scripts/lib/auto-reload.js
+++ b/app/scripts/lib/auto-reload.js
@@ -3,7 +3,7 @@ const ensnare = require('ensnare')
module.exports = setupDappAutoReload
-function setupDappAutoReload (web3, controlStream) {
+function setupDappAutoReload (web3) {
// export web3 as a global, checking for usage
var pageIsUsingWeb3 = false
var resetWasRequested = false
@@ -16,19 +16,19 @@ function setupDappAutoReload (web3, controlStream) {
global.web3 = web3
}))
- // listen for reset requests from metamask
- controlStream.once('data', function () {
+ return handleResetRequest
+
+ function handleResetRequest() {
resetWasRequested = true
// ignore if web3 was not used
if (!pageIsUsingWeb3) return
// reload after short timeout
- triggerReset()
- })
-
- // reload the page
- function triggerReset () {
- setTimeout(function () {
- global.location.reload()
- }, 500)
+ setTimeout(triggerReset, 500)
}
+
}
+
+// reload the page
+function triggerReset () {
+ global.location.reload()
+} \ No newline at end of file
diff --git a/app/scripts/lib/config-manager.js b/app/scripts/lib/config-manager.js
index ecc9bc5f7..d12304c46 100644
--- a/app/scripts/lib/config-manager.js
+++ b/app/scripts/lib/config-manager.js
@@ -110,6 +110,27 @@ ConfigManager.prototype.setWallet = function (wallet) {
this.setData(data)
}
+ConfigManager.prototype.setVault = function (encryptedString) {
+ var data = this.getData()
+ data.vault = encryptedString
+ this.setData(data)
+}
+
+ConfigManager.prototype.getVault = function () {
+ var data = this.getData()
+ return ('vault' in data) && data.vault
+}
+
+ConfigManager.prototype.getKeychains = function () {
+ return this.migrator.getData().keychains || []
+}
+
+ConfigManager.prototype.setKeychains = function (keychains) {
+ var data = this.migrator.getData()
+ data.keychains = keychains
+ this.setData(data)
+}
+
ConfigManager.prototype.getSelectedAccount = function () {
var config = this.getConfig()
return config.selectedAccount
@@ -249,6 +270,17 @@ ConfigManager.prototype.setNicknameForWallet = function (account, nickname) {
// observable
+ConfigManager.prototype.getSalt = function () {
+ var data = this.getData()
+ return ('salt' in data) && data.salt
+}
+
+ConfigManager.prototype.setSalt = function(salt) {
+ var data = this.getData()
+ data.salt = salt
+ this.setData(data)
+}
+
ConfigManager.prototype.subscribe = function (fn) {
this._subs.push(fn)
var unsubscribe = this.unsubscribe.bind(this, fn)
@@ -384,3 +416,15 @@ ConfigManager.prototype.createShapeShiftTx = function (depositAddress, depositTy
}
this.setData(data)
}
+
+ConfigManager.prototype.getGasMultiplier = function () {
+ var data = this.getData()
+ return ('gasMultiplier' in data) && data.gasMultiplier
+}
+
+ConfigManager.prototype.setGasMultiplier = function (gasMultiplier) {
+ var data = this.getData()
+
+ data.gasMultiplier = gasMultiplier
+ this.setData(data)
+}
diff --git a/app/scripts/lib/id-management.js b/app/scripts/lib/id-management.js
index 2d42e1e30..421f2105f 100644
--- a/app/scripts/lib/id-management.js
+++ b/app/scripts/lib/id-management.js
@@ -7,6 +7,7 @@
*/
const ethUtil = require('ethereumjs-util')
+const BN = ethUtil.BN
const Transaction = require('ethereumjs-tx')
module.exports = IdManagement
@@ -24,7 +25,13 @@ function IdManagement (opts) {
}
this.signTx = function (txParams) {
+ // calculate gas with custom gas multiplier
+ var gasMultiplier = this.configManager.getGasMultiplier() || 1
+ var gasPrice = new BN(ethUtil.stripHexPrefix(txParams.gasPrice), 16)
+ gasPrice = gasPrice.mul(new BN(gasMultiplier * 100, 10)).div(new BN(100, 10))
+ txParams.gasPrice = ethUtil.intToHex(gasPrice.toNumber())
// normalize values
+
txParams.to = ethUtil.addHexPrefix(txParams.to)
txParams.from = ethUtil.addHexPrefix(txParams.from.toLowerCase())
txParams.value = ethUtil.addHexPrefix(txParams.value)
diff --git a/app/scripts/lib/idStore.js b/app/scripts/lib/idStore.js
index 6837a1e8d..9d0ca7f19 100644
--- a/app/scripts/lib/idStore.js
+++ b/app/scripts/lib/idStore.js
@@ -2,6 +2,7 @@ const EventEmitter = require('events').EventEmitter
const inherits = require('util').inherits
const async = require('async')
const ethUtil = require('ethereumjs-util')
+const BN = ethUtil.BN
const EthQuery = require('eth-query')
const KeyStore = require('eth-lightwallet').keystore
const clone = require('clone')
@@ -112,6 +113,8 @@ IdentityStore.prototype.getState = function () {
currentFiat: configManager.getCurrentFiat(),
conversionRate: configManager.getConversionRate(),
conversionDate: configManager.getConversionDate(),
+ gasMultiplier: configManager.getGasMultiplier(),
+
}))
}
@@ -211,6 +214,7 @@ IdentityStore.prototype.exportAccount = function (address, cb) {
// comes from dapp via zero-client hooked-wallet provider
IdentityStore.prototype.addUnconfirmedTransaction = function (txParams, onTxDoneCb, cb) {
const configManager = this.configManager
+
var self = this
// create txData obj with parameters and meta data
var time = (new Date()).getTime()
@@ -222,6 +226,7 @@ IdentityStore.prototype.addUnconfirmedTransaction = function (txParams, onTxDone
txParams: txParams,
time: time,
status: 'unconfirmed',
+ gasMultiplier: configManager.getGasMultiplier() || 1,
}
console.log('addUnconfirmedTransaction:', txData)
@@ -262,7 +267,7 @@ IdentityStore.prototype.addUnconfirmedTransaction = function (txParams, onTxDone
function estimateGas(cb){
query.estimateGas(txParams, function(err, result){
if (err) return cb(err)
- txData.estimatedGas = result
+ txData.estimatedGas = self.addGasBuffer(result)
cb()
})
}
@@ -277,6 +282,13 @@ IdentityStore.prototype.addUnconfirmedTransaction = function (txParams, onTxDone
}
}
+IdentityStore.prototype.addGasBuffer = function (gasHex) {
+ var gas = new BN(gasHex, 16)
+ var buffer = new BN('100000', 10)
+ var result = gas.add(buffer)
+ return ethUtil.addHexPrefix(result.toString(16))
+}
+
// comes from metamask ui
IdentityStore.prototype.approveTransaction = function (txId, cb) {
const configManager = this.configManager
diff --git a/app/scripts/lib/inpage-provider.js b/app/scripts/lib/inpage-provider.js
index bcde333d0..c6bfdb4da 100644
--- a/app/scripts/lib/inpage-provider.js
+++ b/app/scripts/lib/inpage-provider.js
@@ -1,6 +1,6 @@
const Streams = require('mississippi')
-const ObjectMultiplex = require('./obj-multiplex')
const StreamProvider = require('web3-stream-provider')
+const ObjectMultiplex = require('./obj-multiplex')
const RemoteStore = require('./remote-store.js').RemoteStore
module.exports = MetamaskInpageProvider
@@ -11,8 +11,9 @@ function MetamaskInpageProvider (connectionStream) {
// setup connectionStream multiplexing
var multiStream = ObjectMultiplex()
Streams.pipe(connectionStream, multiStream, connectionStream, function (err) {
- console.warn('MetamaskInpageProvider - lost connection to MetaMask')
- if (err) throw err
+ let warningMsg = 'MetamaskInpageProvider - lost connection to MetaMask'
+ if (err) warningMsg += '\n' + err.stack
+ console.warn(warningMsg)
})
self.multiStream = multiStream
@@ -20,16 +21,18 @@ function MetamaskInpageProvider (connectionStream) {
var publicConfigStore = remoteStoreWithLocalStorageCache('MetaMask-Config')
var storeStream = publicConfigStore.createStream()
Streams.pipe(storeStream, multiStream.createStream('publicConfig'), storeStream, function (err) {
- console.warn('MetamaskInpageProvider - lost connection to MetaMask publicConfig')
- if (err) throw err
+ let warningMsg = 'MetamaskInpageProvider - lost connection to MetaMask publicConfig'
+ if (err) warningMsg += '\n' + err.stack
+ console.warn(warningMsg)
})
self.publicConfigStore = publicConfigStore
// connect to async provider
var asyncProvider = new StreamProvider()
Streams.pipe(asyncProvider, multiStream.createStream('provider'), asyncProvider, function (err) {
- console.warn('MetamaskInpageProvider - lost connection to MetaMask provider')
- if (err) throw err
+ let warningMsg = 'MetamaskInpageProvider - lost connection to MetaMask provider'
+ if (err) warningMsg += '\n' + err.stack
+ console.warn(warningMsg)
})
asyncProvider.on('error', console.error.bind(console))
self.asyncProvider = asyncProvider
diff --git a/app/scripts/lib/obj-multiplex.js b/app/scripts/lib/obj-multiplex.js
index f54ff7653..bd114c394 100644
--- a/app/scripts/lib/obj-multiplex.js
+++ b/app/scripts/lib/obj-multiplex.js
@@ -10,9 +10,9 @@ function ObjectMultiplex (opts) {
var data = chunk.data
var substream = mx.streams[name]
if (!substream) {
- console.warn('orphaned data for stream ' + name)
+ console.warn(`orphaned data for stream "${name}"`)
} else {
- substream.push(data)
+ if (substream.push) substream.push(data)
}
return cb()
})
@@ -36,5 +36,9 @@ function ObjectMultiplex (opts) {
}
return substream
}
+ // ignore streams (dont display orphaned data warning)
+ mx.ignoreStream = function (name) {
+ mx.streams[name] = true
+ }
return mx
}
diff --git a/app/scripts/lib/port-stream.js b/app/scripts/lib/port-stream.js
index 6f59d4485..6f4ccc6ab 100644
--- a/app/scripts/lib/port-stream.js
+++ b/app/scripts/lib/port-stream.js
@@ -53,8 +53,7 @@ PortDuplexStream.prototype._write = function (msg, encoding, cb) {
}
cb()
} catch (err) {
- console.error(err)
- // this.emit('error', err)
+ // console.error(err)
cb(new Error('PortDuplexStream - disconnected'))
}
}
diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js
index 462c132e6..92551d633 100644
--- a/app/scripts/metamask-controller.js
+++ b/app/scripts/metamask-controller.js
@@ -57,6 +57,7 @@ module.exports = class MetamaskController {
agreeToEthWarning: this.agreeToEthWarning.bind(this),
setTOSHash: this.setTOSHash.bind(this),
checkTOSChange: this.checkTOSChange.bind(this),
+ setGasMultiplier: this.setGasMultiplier.bind(this),
// forward directly to idStore
createNewVault: idStore.createNewVault.bind(idStore),
@@ -278,9 +279,9 @@ module.exports = class MetamaskController {
checkTOSChange () {
try {
const storedHash = this.configManager.getTOSHash() || 0
- if (storedHash !== global.newTOSHash) {
+ if (storedHash !== global.TOS_HASH) {
this.resetDisclaimer()
- this.setTOSHash(global.newTOSHash)
+ this.setTOSHash(global.TOS_HASH)
}
} catch (e) {
console.error('Error in checking TOS change.')
@@ -395,4 +396,12 @@ module.exports = class MetamaskController {
})
}
+ setGasMultiplier (gasMultiplier, cb) {
+ try {
+ this.configManager.setGasMultiplier(gasMultiplier)
+ cb()
+ } catch (e) {
+ cb(e)
+ }
+ }
}
diff --git a/mock-dev.js b/mock-dev.js
index 99b846e51..d3f364d69 100644
--- a/mock-dev.js
+++ b/mock-dev.js
@@ -107,7 +107,7 @@ function getOldStyleData () {
return result
}
-actions._setAccountManager(controller.getApi())
+actions._setKeyringController(controller.getApi())
actions.update = function(stateName) {
selectedView = stateName
updateQueryParams(stateName)
diff --git a/package.json b/package.json
index 8453a2549..70ca1c7d4 100644
--- a/package.json
+++ b/package.json
@@ -6,9 +6,9 @@
"scripts": {
"start": "npm run dev",
"lint": "gulp lint",
- "dev": "gulp dev --debug",
- "dist": "gulp dist",
"buildCiUnits": "node test/integration/index.js",
+ "dev": "gulp dev --debug",
+ "dist": "gulp dist --disableLiveReload",
"test": "npm run fastTest && npm run ci && npm run lint",
"fastTest": "mocha --require test/helper.js --compilers js:babel-register --recursive \"test/unit/**/*.js\"",
"watch": "mocha watch --compilers js:babel-register --recursive \"test/unit/**/*.js\"",
@@ -60,6 +60,7 @@
"mississippi": "^1.2.0",
"multiplex": "^6.7.0",
"once": "^1.3.3",
+ "ping-pong-stream": "^1.0.0",
"pojo-migrator": "^2.1.0",
"polyfill-crypto.getrandomvalues": "^1.0.0",
"post-message-stream": "^1.0.0",
diff --git a/test/unit/actions/restore_vault_test.js b/test/unit/actions/restore_vault_test.js
index 609f5429e..7202abb70 100644
--- a/test/unit/actions/restore_vault_test.js
+++ b/test/unit/actions/restore_vault_test.js
@@ -20,7 +20,7 @@ describe('#recoverFromSeed(password, seed)', function() {
})
// stub out account manager
- actions._setAccountManager({
+ actions._setKeyringController({
recoverFromSeed(pw, seed, cb) {
cb(null, {
identities: {
diff --git a/test/unit/actions/tx_test.js b/test/unit/actions/tx_test.js
index c08a8aa26..e365ee3a3 100644
--- a/test/unit/actions/tx_test.js
+++ b/test/unit/actions/tx_test.js
@@ -46,7 +46,7 @@ describe('tx confirmation screen', function() {
describe('cancelTx', function() {
before(function(done) {
- actions._setAccountManager({
+ actions._setKeyringController({
approveTransaction(txId, cb) { cb('An error!') },
cancelTransaction(txId) { /* noop */ },
clearSeedWordCache(cb) { cb() },
@@ -75,7 +75,7 @@ describe('tx confirmation screen', function() {
before(function(done) {
alert = () => {/* noop */}
- actions._setAccountManager({
+ actions._setKeyringController({
approveTransaction(txId, cb) { cb({message: 'An error!'}) },
})
@@ -96,7 +96,7 @@ describe('tx confirmation screen', function() {
describe('when there is success', function() {
it('should complete tx and go home', function() {
- actions._setAccountManager({
+ actions._setKeyringController({
approveTransaction(txId, cb) { cb() },
})
@@ -135,7 +135,7 @@ describe('tx confirmation screen', function() {
}
freeze(initialState)
- actions._setAccountManager({
+ actions._setKeyringController({
approveTransaction(txId, cb) { cb() },
})
diff --git a/test/unit/idStore-test.js b/test/unit/idStore-test.js
index 31da2cd3d..0a57d2121 100644
--- a/test/unit/idStore-test.js
+++ b/test/unit/idStore-test.js
@@ -2,6 +2,7 @@ var assert = require('assert')
var IdentityStore = require('../../app/scripts/lib/idStore')
var configManagerGen = require('../lib/mock-config-manager')
const ethUtil = require('ethereumjs-util')
+const BN = ethUtil.BN
const async = require('async')
describe('IdentityStore', function() {
@@ -138,4 +139,21 @@ describe('IdentityStore', function() {
})
})
})
+
+ describe('#addGasBuffer', function() {
+ const idStore = new IdentityStore({
+ configManager: configManagerGen(),
+ ethStore: {
+ addAccount(acct) { accounts.push(ethUtil.addHexPrefix(acct)) },
+ },
+ })
+
+ const gas = '0x01'
+ const bnGas = new BN(gas, 16)
+ const result = idStore.addGasBuffer(gas)
+ const bnResult = new BN(result, 16)
+
+ assert.ok(bnResult.gt(gas), 'added more gas as buffer.')
+ assert.equal(result.indexOf('0x'), 0, 'include hex prefix')
+ })
})
diff --git a/ui-dev.js b/ui-dev.js
index bfc84d415..9fc344f98 100644
--- a/ui-dev.js
+++ b/ui-dev.js
@@ -41,7 +41,7 @@ function updateQueryParams(newView) {
}
const actions = {
- _setAccountManager(){},
+ _setKeyringController(){},
update: function(stateName) {
selectedView = stateName
updateQueryParams(stateName)
diff --git a/ui/app/actions.js b/ui/app/actions.js
index 4f3083707..a6601cd0e 100644
--- a/ui/app/actions.js
+++ b/ui/app/actions.js
@@ -95,7 +95,7 @@ var actions = {
setRpcTarget: setRpcTarget,
setProviderType: setProviderType,
// hacky - need a way to get a reference to account manager
- _setAccountManager: _setAccountManager,
+ _setKeyringController: _setKeyringController,
// loading overlay
SHOW_LOADING: 'SHOW_LOADING_INDICATION',
HIDE_LOADING: 'HIDE_LOADING_INDICATION',
@@ -132,13 +132,17 @@ var actions = {
RECOVERY_IN_PROGRESS: 'RECOVERY_IN_PROGRESS',
BACK_TO_UNLOCK_VIEW: 'BACK_TO_UNLOCK_VIEW',
backToUnlockView: backToUnlockView,
+ // SHOWING KEYCHAIN
+ SHOW_NEW_KEYCHAIN: 'SHOW_NEW_KEYCHAIN',
+ showNewKeychain: showNewKeychain,
+
}
module.exports = actions
-var _accountManager = null
-function _setAccountManager (accountManager) {
- _accountManager = accountManager
+var _keyringController = null
+function _setKeyringController (accountManager) {
+ _keyringController = accountManager
}
function goHome () {
@@ -153,7 +157,7 @@ function tryUnlockMetamask (password) {
return (dispatch) => {
dispatch(actions.showLoadingIndication())
dispatch(actions.unlockInProgress())
- _accountManager.submitPassword(password, (err, selectedAccount) => {
+ _keyringController.submitPassword(password, (err, selectedAccount) => {
dispatch(actions.hideLoadingIndication())
if (err) {
dispatch(actions.unlockFailed())
@@ -167,11 +171,11 @@ function tryUnlockMetamask (password) {
function createNewVault (password, entropy) {
return (dispatch) => {
dispatch(actions.createNewVaultInProgress())
- _accountManager.createNewVault(password, entropy, (err, result) => {
+ _keyringController.createNewVault(password, entropy, (err, result) => {
if (err) {
return dispatch(actions.showWarning(err.message))
}
- dispatch(this.goHome())
+ dispatch(this.showAccountsPage())
dispatch(this.hideLoadingIndication())
})
}
@@ -185,14 +189,14 @@ function showInfoPage () {
function setSelectedAddress (address) {
return (dispatch) => {
- _accountManager.setSelectedAddress(address)
+ _keyringController.setSelectedAddress(address)
}
}
function setCurrentFiat (fiat) {
return (dispatch) => {
dispatch(this.showLoadingIndication())
- _accountManager.setCurrentFiat(fiat, (data, err) => {
+ _keyringController.setCurrentFiat(fiat, (data, err) => {
dispatch(this.hideLoadingIndication())
dispatch({
type: this.SET_CURRENT_FIAT,
@@ -210,7 +214,7 @@ function signMsg (msgData) {
return (dispatch) => {
dispatch(actions.showLoadingIndication())
- _accountManager.signMessage(msgData, (err) => {
+ _keyringController.signMessage(msgData, (err) => {
dispatch(actions.hideLoadingIndication())
if (err) return dispatch(actions.displayWarning(err.message))
@@ -221,20 +225,22 @@ function signMsg (msgData) {
function signTx (txData) {
return (dispatch) => {
- web3.eth.sendTransaction(txData, (err, data) => {
- dispatch(actions.hideLoadingIndication())
-
+ _accountManager.setGasMultiplier(txData.gasMultiplier, (err) => {
if (err) return dispatch(actions.displayWarning(err.message))
- dispatch(actions.hideWarning())
- dispatch(actions.goHome())
+ web3.eth.sendTransaction(txData, (err, data) => {
+ dispatch(actions.hideLoadingIndication())
+ if (err) return dispatch(actions.displayWarning(err.message))
+ dispatch(actions.hideWarning())
+ dispatch(actions.goHome())
+ })
+ dispatch(this.showConfTxPage())
})
- dispatch(this.showConfTxPage())
}
}
function sendTx (txData) {
return (dispatch) => {
- _accountManager.approveTransaction(txData.id, (err) => {
+ _keyringController.approveTransaction(txData.id, (err) => {
if (err) {
alert(err.message)
dispatch(actions.txError(err))
@@ -260,12 +266,12 @@ function txError (err) {
}
function cancelMsg (msgData) {
- _accountManager.cancelMessage(msgData.id)
+ _keyringController.cancelMessage(msgData.id)
return actions.completedTx(msgData.id)
}
function cancelTx (txData) {
- _accountManager.cancelTransaction(txData.id)
+ _keyringController.cancelTransaction(txData.id)
return actions.completedTx(txData.id)
}
@@ -294,7 +300,7 @@ function showInitializeMenu () {
function agreeToDisclaimer () {
return (dispatch) => {
dispatch(this.showLoadingIndication())
- _accountManager.agreeToDisclaimer((err) => {
+ _keyringController.agreeToDisclaimer((err) => {
if (err) {
return dispatch(actions.showWarning(err.message))
}
@@ -326,6 +332,12 @@ function backToUnlockView () {
}
}
+function showNewKeychain () {
+ return {
+ type: actions.SHOW_NEW_KEYCHAIN
+ }
+}
+
//
// unlock screen
//
@@ -358,7 +370,7 @@ function updateMetamaskState (newState) {
function lockMetamask () {
return (dispatch) => {
- _accountManager.setLocked((err) => {
+ _keyringController.setLocked((err) => {
dispatch(actions.hideLoadingIndication())
if (err) {
return dispatch(actions.showWarning(err.message))
@@ -374,7 +386,7 @@ function lockMetamask () {
function showAccountDetail (address) {
return (dispatch) => {
dispatch(actions.showLoadingIndication())
- _accountManager.setSelectedAddress(address, (err, address) => {
+ _keyringController.setSelectedAddress(address, (err, address) => {
dispatch(actions.hideLoadingIndication())
if (err) {
return dispatch(actions.showWarning(err.message))
@@ -445,7 +457,7 @@ function goBackToInitView () {
//
function setRpcTarget (newRpc) {
- _accountManager.setRpcTarget(newRpc)
+ _keyringController.setRpcTarget(newRpc)
return {
type: actions.SET_RPC_TARGET,
value: newRpc,
@@ -453,7 +465,7 @@ function setRpcTarget (newRpc) {
}
function setProviderType (type) {
- _accountManager.setProviderType(type)
+ _keyringController.setProviderType(type)
return {
type: actions.SET_PROVIDER_TYPE,
value: type,
@@ -461,7 +473,7 @@ function setProviderType (type) {
}
function useEtherscanProvider () {
- _accountManager.useEtherscanProvider()
+ _keyringController.useEtherscanProvider()
return {
type: actions.USE_ETHERSCAN_PROVIDER,
}
@@ -520,7 +532,7 @@ function exportAccount (address) {
return function (dispatch) {
dispatch(self.showLoadingIndication())
- _accountManager.exportAccount(address, function (err, result) {
+ _keyringController.exportAccount(address, function (err, result) {
dispatch(self.hideLoadingIndication())
if (err) {
@@ -543,7 +555,7 @@ function showPrivateKey (key) {
function saveAccountLabel (account, label) {
return (dispatch) => {
dispatch(actions.showLoadingIndication())
- _accountManager.saveAccountLabel(account, label, (err) => {
+ _keyringController.saveAccountLabel(account, label, (err) => {
dispatch(actions.hideLoadingIndication())
if (err) {
return dispatch(actions.showWarning(err.message))
@@ -564,7 +576,7 @@ function showSendPage () {
function agreeToEthWarning () {
return (dispatch) => {
- _accountManager.agreeToEthWarning((err) => {
+ _keyringController.agreeToEthWarning((err) => {
if (err) {
return dispatch(actions.showEthWarning(err.message))
}
@@ -583,7 +595,7 @@ function showEthWarning () {
function buyEth (address, amount) {
return (dispatch) => {
- _accountManager.buyEth(address, amount)
+ _keyringController.buyEth(address, amount)
dispatch({
type: actions.BUY_ETH,
})
@@ -661,7 +673,7 @@ function coinShiftRquest (data, marketData) {
if (response.error) return dispatch(actions.showWarning(response.error))
var message = `
Deposit your ${response.depositType} to the address bellow:`
- _accountManager.createShapeShiftTx(response.deposit, response.depositType)
+ _keyringController.createShapeShiftTx(response.deposit, response.depositType)
dispatch(actions.showQrView(response.deposit, [message].concat(marketData)))
})
}
diff --git a/ui/app/app.js b/ui/app/app.js
index 3266ced51..7392e275d 100644
--- a/ui/app/app.js
+++ b/ui/app/app.js
@@ -8,6 +8,7 @@ const ReactCSSTransitionGroup = require('react-addons-css-transition-group')
const DisclaimerScreen = require('./first-time/disclaimer')
const InitializeMenuScreen = require('./first-time/init-menu')
const CreateVaultScreen = require('./first-time/create-vault')
+const NewKeychainScreen = require('./new-keychain')
// unlock
const UnlockScreen = require('./unlock')
// accounts
@@ -432,6 +433,9 @@ App.prototype.renderPrimary = function () {
case 'sendTransaction':
return h(SendTransactionScreen, {key: 'send-transaction'})
+ case 'newKeychain':
+ return h(NewKeyChainScreen, {key: 'new-keychain'})
+
case 'confTx':
return h(ConfirmTxScreen, {key: 'confirm-tx'})
diff --git a/ui/app/components/pending-tx-details.js b/ui/app/components/pending-tx-details.js
index d8e8524bf..545302098 100644
--- a/ui/app/components/pending-tx-details.js
+++ b/ui/app/components/pending-tx-details.js
@@ -29,8 +29,10 @@ PTXP.render = function () {
var account = props.accounts[address]
var balance = account ? account.balance : '0x0'
+ var gasMultiplier = txData.gasMultiplier
var gasCost = new BN(ethUtil.stripHexPrefix(txParams.gas || txData.estimatedGas), 16)
var gasPrice = new BN(ethUtil.stripHexPrefix(txParams.gasPrice || '0x4a817c800'), 16)
+ gasPrice = gasPrice.mul(new BN(gasMultiplier * 100), 10).div(new BN(100, 10))
var txFee = gasCost.mul(gasPrice)
var txValue = new BN(ethUtil.stripHexPrefix(txParams.value || '0x0'), 16)
var maxCost = txValue.add(txFee)
diff --git a/ui/app/components/range-slider.js b/ui/app/components/range-slider.js
new file mode 100644
index 000000000..823f5eb01
--- /dev/null
+++ b/ui/app/components/range-slider.js
@@ -0,0 +1,58 @@
+const Component = require('react').Component
+const h = require('react-hyperscript')
+const inherits = require('util').inherits
+
+module.exports = RangeSlider
+
+inherits(RangeSlider, Component)
+function RangeSlider () {
+ Component.call(this)
+}
+
+RangeSlider.prototype.render = function () {
+ const state = this.state || {}
+ const props = this.props
+ const onInput = props.onInput || function () {}
+ const name = props.name
+ const {
+ min = 0,
+ max = 100,
+ increment = 1,
+ defaultValue = 50,
+ mirrorInput = false,
+ } = this.props.options
+ const {container, input, range} = props.style
+
+ return (
+ h('.flex-row', {
+ style: container,
+ }, [
+ h('input', {
+ type: 'range',
+ name: name,
+ min: min,
+ max: max,
+ step: increment,
+ style: range,
+ value: state.value || defaultValue,
+ onChange: mirrorInput ? this.mirrorInputs.bind(this, event) : onInput,
+ }),
+
+ // Mirrored input for range
+ mirrorInput ? h('input.large-input', {
+ type: 'number',
+ name: `${name}Mirror`,
+ min: min,
+ max: max,
+ value: state.value || defaultValue,
+ step: increment,
+ style: input,
+ onChange: this.mirrorInputs.bind(this, event),
+ }) : null,
+ ])
+ )
+}
+
+RangeSlider.prototype.mirrorInputs = function (event) {
+ this.setState({value: event.target.value})
+}
diff --git a/ui/app/new-keychain.js b/ui/app/new-keychain.js
new file mode 100644
index 000000000..d6fefd0c7
--- /dev/null
+++ b/ui/app/new-keychain.js
@@ -0,0 +1,33 @@
+const inherits = require('util').inherits
+const Component = require('react').Component
+const h = require('react-hyperscript')
+const connect = require('react-redux').connect
+
+module.exports = connect(mapStateToProps)(NewKeychain)
+
+function mapStateToProps (state) {
+ return {}
+}
+
+inherits(NewKeychain, Component)
+function NewKeychain () {
+ Component.call(this)
+}
+
+NewKeychain.prototype.render = function () {
+ const props = this.props
+
+ return (
+ h('div', {
+ style: {
+ background: 'blue',
+ },
+ }, [
+ h('h1',`Here's a list!!!!`),
+ h('button',
+ {
+ onClick: () => this.props.dispatch(actions.goHome())
+ })
+ ])
+ )
+}
diff --git a/ui/app/reducers/app.js b/ui/app/reducers/app.js
index c2ac099a6..2bfb2567a 100644
--- a/ui/app/reducers/app.js
+++ b/ui/app/reducers/app.js
@@ -119,6 +119,15 @@ function reduceApp (state, action) {
warning: null,
})
+ case actions.SHOW_NEW_KEYCHAIN:
+ return extend(appState, {
+ currentView: {
+ name: 'newKeychain',
+ context: appState.currentView.context
+ },
+ transForward: true,
+ })
+
// unlock
case actions.UNLOCK_METAMASK:
@@ -540,4 +549,3 @@ function indexForPending (state, txId) {
})
return idx
}
-
diff --git a/ui/app/send.js b/ui/app/send.js
index 009866cf7..97ed29e4a 100644
--- a/ui/app/send.js
+++ b/ui/app/send.js
@@ -9,7 +9,8 @@ const numericBalance = require('./util').numericBalance
const addressSummary = require('./util').addressSummary
const EthBalance = require('./components/eth-balance')
const ethUtil = require('ethereumjs-util')
-
+const RangeSlider = require('./components/range-slider')
+const Tooltip = require('./components/tooltip')
module.exports = connect(mapStateToProps)(SendTransactionScreen)
function mapStateToProps (state) {
@@ -50,7 +51,7 @@ SendTransactionScreen.prototype.render = function () {
// Sender Profile
//
- h('.account-data-subsection.flex-column.flex-grow', {
+ h('.account-data-subsection.flex-row.flex-grow', {
style: {
margin: '0 20px',
},
@@ -59,10 +60,9 @@ SendTransactionScreen.prototype.render = function () {
// header - identicon + nav
h('.flex-row.flex-space-between', {
style: {
- marginTop: 28,
+ marginTop: '15px',
},
}, [
-
// back button
h('i.fa.fa-arrow-left.fa-lg.cursor-pointer.color-orange', {
onClick: this.back.bind(this),
@@ -77,42 +77,53 @@ SendTransactionScreen.prototype.render = function () {
]),
// invisible place holder
- h('i.fa.fa-users.fa-lg.invisible'),
+ h('i.fa.fa-users.fa-lg.invisible', {
+ style: {
+ marginTop: '28px',
+ },
+ }),
]),
// account label
- h('h2.font-medium.color-forest.flex-center', {
- style: {
- paddingTop: 8,
- marginBottom: 8,
- },
- }, identity && identity.name),
- // address and getter actions
- h('.flex-row.flex-center', {
+ h('.flex-column', {
style: {
- marginBottom: 8,
+ marginTop: '10px',
+ alignItems: 'flex-start',
},
}, [
+ h('h2.font-medium.color-forest.flex-center', {
+ style: {
+ paddingTop: '8px',
+ marginBottom: '8px',
+ },
+ }, identity && identity.name),
- h('div', {
+ // address and getter actions
+ h('.flex-row.flex-center', {
style: {
- lineHeight: '16px',
+ marginBottom: '8px',
},
- }, addressSummary(address)),
+ }, [
- ]),
+ h('div', {
+ style: {
+ lineHeight: '16px',
+ },
+ }, addressSummary(address)),
- // balance
- h('.flex-row.flex-center', [
+ ]),
- h(EthBalance, {
- value: account && account.balance,
- }),
+ // balance
+ h('.flex-row.flex-center', [
- ]),
+ h(EthBalance, {
+ value: account && account.balance,
+ }),
+ ]),
+ ]),
]),
//
@@ -123,8 +134,8 @@ SendTransactionScreen.prototype.render = function () {
style: {
background: '#EBEBEB',
color: '#AEAEAE',
- marginTop: 32,
- marginBottom: 16,
+ marginTop: '15px',
+ marginBottom: '16px',
},
}, [
'Send Transaction',
@@ -152,7 +163,7 @@ SendTransactionScreen.prototype.render = function () {
placeholder: 'Amount',
type: 'number',
style: {
- marginRight: 6,
+ marginRight: '6px',
},
dataset: {
persistentFormId: 'tx-amount',
@@ -171,20 +182,19 @@ SendTransactionScreen.prototype.render = function () {
//
// Optional Fields
//
-
h('h3.flex-center.text-transform-uppercase', {
style: {
background: '#EBEBEB',
color: '#AEAEAE',
- marginTop: 16,
- marginBottom: 16,
+ marginTop: '16px',
+ marginBottom: '16px',
},
}, [
'Transactional Data (optional)',
]),
// 'data' field
- h('section.flex-row.flex-center', [
+ h('section.flex-column.flex-center', [
h('input.large-input', {
name: 'txData',
placeholder: '0x01234',
@@ -197,6 +207,73 @@ SendTransactionScreen.prototype.render = function () {
},
}),
]),
+ // custom gasPrice field
+ h('h3.flex-center.text-transform-uppercase', {
+ style: {
+ background: '#EBEBEB',
+ color: '#AEAEAE',
+ marginBottom: '5px',
+ },
+ }, [
+ 'Transaction Fee (optional)',
+ h(Tooltip, {
+ title: `
+ This is used to set the transaction's gas price.
+ Setting it to 100% will use the full recommended value. `,
+ }, [
+ h('i.fa.fa-question-circle', {
+ style: {
+ marginLeft: '5px',
+ },
+ }),
+ ]),
+ ]),
+
+ h('section.flex-column.flex-center', [
+ h('.flex-row', [
+ h(RangeSlider, {
+ name: 'gasInput',
+ options: {
+ mirrorInput: true,
+ defaultValue: 100,
+ min: 80,
+ max: 220,
+ },
+ style: {
+ container: {
+ marginBottom: '16px',
+ },
+ range: {
+ width: '68vw',
+ },
+ input: {
+ width: '5em',
+ marginLeft: '5px',
+ },
+ },
+ }),
+
+ h('div', {
+ style: {
+ fontSize: '12px',
+ paddingTop: '8px',
+ paddingLeft: '5px',
+ },
+ }, '%'),
+ ]),
+ h('.flex-row', {
+ style: {
+ justifyContent: 'space-between',
+ width: '243px',
+ position: 'relative',
+ fontSize: '12px',
+ right: '42px',
+ bottom: '30px',
+ },
+ }, [
+ h('span', 'Cheaper'), h('span', 'Faster'),
+ ]),
+ ]),
])
)
}
@@ -211,11 +288,12 @@ SendTransactionScreen.prototype.back = function () {
this.props.dispatch(actions.backToAccountDetail(address))
}
-SendTransactionScreen.prototype.onSubmit = function () {
+SendTransactionScreen.prototype.onSubmit = function (gasPrice) {
const recipient = document.querySelector('input[name="address"]').value
const input = document.querySelector('input[name="amount"]').value
const value = util.normalizeEthStringToWei(input)
const txData = document.querySelector('input[name="txData"]').value
+ const gasMultiplier = document.querySelector('input[name="gasInput"]').value
const balance = this.props.balance
let message
@@ -239,6 +317,7 @@ SendTransactionScreen.prototype.onSubmit = function () {
var txParams = {
from: this.props.address,
value: '0x' + value.toString(16),
+ gasMultiplier: gasMultiplier * 0.01,
}
if (recipient) txParams.to = ethUtil.addHexPrefix(recipient)
diff --git a/ui/index.js b/ui/index.js
index a6905b639..f07a4006c 100644
--- a/ui/index.js
+++ b/ui/index.js
@@ -8,7 +8,7 @@ module.exports = launchApp
function launchApp (opts) {
var accountManager = opts.accountManager
- actions._setAccountManager(accountManager)
+ actions._setKeyringController(accountManager)
// check if we are unlocked first
accountManager.getState(function (err, metamaskState) {