Merge branch 'master' into tos

feature/default_network_editable
Kevin Serrano 8 years ago
commit 05b960cace
No known key found for this signature in database
GPG Key ID: 7CC862A58D2889B4
  1. 4
      .eslintrc
  2. 4
      CHANGELOG.md
  3. 2
      app/scripts/metamask-controller.js
  4. 16
      test/unit/extension-test.js
  5. 3
      ui/app/account-detail.js
  6. 3
      ui/app/actions.js
  7. 11
      ui/app/components/buy-button-subview.js
  8. 4
      ui/app/components/pending-tx-details.js
  9. 23
      ui/app/components/pending-tx.js
  10. 19
      ui/app/components/transaction-list-item.js
  11. 27
      ui/app/conf-tx.js
  12. 26
      ui/app/css/index.css
  13. 4
      ui/app/reducers/app.js

@ -127,14 +127,14 @@
"no-whitespace-before-property": 2, "no-whitespace-before-property": 2,
"no-with": 2, "no-with": 2,
"one-var": [2, { "initialized": "never" }], "one-var": [2, { "initialized": "never" }],
"operator-linebreak": [2, "after", { "overrides": { "?": "before", ":": "before" } }], "operator-linebreak": [1, "after", { "overrides": { "?": "before", ":": "before" } }],
"padded-blocks": [1, "never"], "padded-blocks": [1, "never"],
"quotes": [2, "single", "avoid-escape"], "quotes": [2, "single", "avoid-escape"],
"semi": [2, "never"], "semi": [2, "never"],
"semi-spacing": [2, { "before": false, "after": true }], "semi-spacing": [2, { "before": false, "after": true }],
"space-before-blocks": [1, "always"], "space-before-blocks": [1, "always"],
"space-before-function-paren": [1, "always"], "space-before-function-paren": [1, "always"],
"space-in-parens": [2, "never"], "space-in-parens": [1, "never"],
"space-infix-ops": 2, "space-infix-ops": 2,
"space-unary-ops": [2, { "words": true, "nonwords": false }], "space-unary-ops": [2, { "words": true, "nonwords": false }],
"spaced-comment": [2, "always", { "markers": ["global", "globals", "eslint", "eslint-disable", "*package", "!", ","] }], "spaced-comment": [2, "always", { "markers": ["global", "globals", "eslint", "eslint-disable", "*package", "!", ","] }],

@ -2,6 +2,10 @@
## Current Master ## Current Master
- Add a link to the transaction in history that goes to https://metamask.github.io/eth-tx-viz
too help visualize transactions and to where they are going.
- Show "Buy Ether" button and warning on tx confirmation when sender balance is insufficient
## 2.12.1 2016-09-14 ## 2.12.1 2016-09-14
- Fixed bug where if you send a transaction from within MetaMask extension the - Fixed bug where if you send a transaction from within MetaMask extension the

@ -334,7 +334,7 @@ module.exports = class MetamaskController {
var url = `https://buy.coinbase.com/?code=9ec56d01-7e81-5017-930c-513daa27bb6a&amount=${amount}&address=${address}&crypto_currency=ETH` var url = `https://buy.coinbase.com/?code=9ec56d01-7e81-5017-930c-513daa27bb6a&amount=${amount}&address=${address}&crypto_currency=ETH`
if (network === '2') { if (network === '2') {
url = 'https://testfaucet.metamask.io/' url = 'https://faucet.metamask.io/'
} }
extension.tabs.create({ extension.tabs.create({

@ -1,8 +1,8 @@
var assert = require('assert') var assert = require('assert')
var sinon = require('sinon') var sinon = require('sinon')
const ethUtil = require('ethereumjs-util') const ethUtil = require('ethereumjs-util')
GLOBAL.chrome = {} global.chrome = {}
GLOBAL.browser = {} global.browser = {}
var path = require('path') var path = require('path')
var Extension = require(path.join(__dirname, '..', '..', 'app', 'scripts', 'lib', 'extension-instance.js')) var Extension = require(path.join(__dirname, '..', '..', 'app', 'scripts', 'lib', 'extension-instance.js'))
@ -13,7 +13,7 @@ describe('extension', function() {
const desiredResult = 'http://the-desired-result.io' const desiredResult = 'http://the-desired-result.io'
describe('in Chrome or Firefox', function() { describe('in Chrome or Firefox', function() {
GLOBAL.chrome.extension = { global.chrome.extension = {
getURL: () => desiredResult getURL: () => desiredResult
} }
@ -25,7 +25,7 @@ describe('extension', function() {
}) })
describe('in Microsoft Edge', function() { describe('in Microsoft Edge', function() {
GLOBAL.browser.extension = { global.browser.extension = {
getURL: () => desiredResult getURL: () => desiredResult
} }
@ -41,7 +41,7 @@ describe('extension', function() {
let extension let extension
beforeEach(function() { beforeEach(function() {
GLOBAL.chrome = { global.chrome = {
alarms: 'foo' alarms: 'foo'
} }
extension = new Extension() extension = new Extension()
@ -58,9 +58,9 @@ describe('extension', function() {
beforeEach(function() { beforeEach(function() {
realWindow = window realWindow = window
window = GLOBAL window = global
GLOBAL.chrome = undefined global.chrome = undefined
GLOBAL.alarms = 'foo' global.alarms = 'foo'
extension = new Extension() extension = new Extension()
}) })

@ -279,10 +279,11 @@ AccountDetailScreen.prototype.requestAccountExport = function () {
AccountDetailScreen.prototype.buyButtonDeligator = function () { AccountDetailScreen.prototype.buyButtonDeligator = function () {
var props = this.props var props = this.props
var selected = props.address || Object.keys(props.accounts)[0]
if (this.props.accountDetail.subview === 'buyForm') { if (this.props.accountDetail.subview === 'buyForm') {
props.dispatch(actions.backToAccountDetail(props.address)) props.dispatch(actions.backToAccountDetail(props.address))
} else { } else {
props.dispatch(actions.buyEthView()) props.dispatch(actions.buyEthView(selected))
} }
} }

@ -479,9 +479,10 @@ function showAccountsPage () {
} }
} }
function showConfTxPage () { function showConfTxPage (transForward = true) {
return { return {
type: actions.SHOW_CONF_TX_PAGE, type: actions.SHOW_CONF_TX_PAGE,
transForward: transForward,
} }
} }

@ -16,6 +16,7 @@ function mapStateToProps (state) {
buyView: state.appState.buyView, buyView: state.appState.buyView,
network: state.metamask.network, network: state.metamask.network,
provider: state.metamask.provider, provider: state.metamask.provider,
context: state.appState.currentView.context,
} }
} }
@ -38,7 +39,7 @@ BuyButtonSubview.prototype.render = function () {
}, },
}, [ }, [
h('i.fa.fa-arrow-left.fa-lg.cursor-pointer.color-orange', { h('i.fa.fa-arrow-left.fa-lg.cursor-pointer.color-orange', {
onClick: () => props.dispatch(actions.backToAccountDetail(props.selectedAccount)), onClick: this.backButtonContext.bind(this),
style: { style: {
position: 'absolute', position: 'absolute',
left: '10px', left: '10px',
@ -121,3 +122,11 @@ BuyButtonSubview.prototype.formVersionSubview = function () {
BuyButtonSubview.prototype.navigateTo = function (url) { BuyButtonSubview.prototype.navigateTo = function (url) {
extension.tabs.create({ url }) extension.tabs.create({ url })
} }
BuyButtonSubview.prototype.backButtonContext = function () {
if (this.props.context === 'confTx') {
this.props.dispatch(actions.showConfTxPage(false))
} else {
this.props.dispatch(actions.goHome())
}
}

@ -4,12 +4,12 @@ const inherits = require('util').inherits
const MiniAccountPanel = require('./mini-account-panel') const MiniAccountPanel = require('./mini-account-panel')
const EthBalance = require('./eth-balance') const EthBalance = require('./eth-balance')
const addressSummary = require('../util').addressSummary const util = require('../util')
const addressSummary = util.addressSummary
const nameForAddress = require('../../lib/contract-namer') const nameForAddress = require('../../lib/contract-namer')
const ethUtil = require('ethereumjs-util') const ethUtil = require('ethereumjs-util')
const BN = ethUtil.BN const BN = ethUtil.BN
module.exports = PendingTxDetails module.exports = PendingTxDetails
inherits(PendingTxDetails, Component) inherits(PendingTxDetails, Component)

@ -3,7 +3,6 @@ const h = require('react-hyperscript')
const inherits = require('util').inherits const inherits = require('util').inherits
const PendingTxDetails = require('./pending-tx-details') const PendingTxDetails = require('./pending-tx-details')
module.exports = PendingTx module.exports = PendingTx
inherits(PendingTx, Component) inherits(PendingTx, Component)
@ -31,6 +30,15 @@ PendingTx.prototype.render = function () {
} }
`), `),
state.insufficientBalance ?
h('span.error', {
style: {
marginLeft: 50,
fontSize: '0.9em',
},
}, 'Insufficient balance for transaction')
: null,
// send + cancel // send + cancel
h('.flex-row.flex-space-around.conf-buttons', { h('.flex-row.flex-space-around.conf-buttons', {
style: { style: {
@ -39,17 +47,22 @@ PendingTx.prototype.render = function () {
margin: '14px 25px', margin: '14px 25px',
}, },
}, [ }, [
state.insufficientBalance ?
h('button.btn-green', {
onClick: state.buyEth,
}, 'Buy Ether')
: null,
h('button.confirm', { h('button.confirm', {
disabled: state.insufficientBalance,
onClick: state.sendTransaction, onClick: state.sendTransaction,
style: { background: 'rgb(251,117,1)' },
}, 'Accept'), }, 'Accept'),
h('button.cancel', { h('button.cancel.btn-red', {
onClick: state.cancelTransaction, onClick: state.cancelTransaction,
style: { background: 'rgb(254,35,17)' },
}, 'Reject'), }, 'Reject'),
]), ]),
]) ])
) )
} }

@ -48,7 +48,7 @@ TransactionListItem.prototype.render = function () {
if (isPending) { if (isPending) {
this.props.showTx(transaction.id) this.props.showTx(transaction.id)
} }
event.stopPropagation()
if (!transaction.hash || !isLinkable) return if (!transaction.hash || !isLinkable) return
var url = explorerLink(transaction.hash, parseInt(network)) var url = explorerLink(transaction.hash, parseInt(network))
extension.tabs.create({ url }) extension.tabs.create({ url })
@ -58,10 +58,21 @@ TransactionListItem.prototype.render = function () {
}, },
}, [ }, [
// large identicon
h('.identicon-wrapper.flex-column.flex-center.select-none', [ h('.identicon-wrapper.flex-column.flex-center.select-none', [
transaction.status === 'unconfirmed' ? h('i.fa.fa-ellipsis-h', {style: { fontSize: '27px' }}) transaction.status === 'unconfirmed' ? h('i.fa.fa-ellipsis-h', {
: h(TransactionIcon, { txParams, transaction, isTx, isMsg }), style: {
fontSize: '27px',
},
}) : h( '.pop-hover', {
onClick: (event) => {
event.stopPropagation()
if (!isTx || isPending) return
var url = `https://metamask.github.io/eth-tx-viz/?tx=${transaction.hash}`
extension.tabs.create({ url })
},
}, [
h(TransactionIcon, { txParams, transaction, isTx, isMsg }),
]),
]), ]),
h('.flex-column', {style: {width: '200px', overflow: 'hidden'}}, [ h('.flex-column', {style: {width: '200px', overflow: 'hidden'}}, [

@ -6,6 +6,8 @@ const connect = require('react-redux').connect
const actions = require('./actions') const actions = require('./actions')
const txHelper = require('../lib/tx-helper') const txHelper = require('../lib/tx-helper')
const isPopupOrNotification = require('../../app/scripts/lib/is-popup-or-notification') const isPopupOrNotification = require('../../app/scripts/lib/is-popup-or-notification')
const ethUtil = require('ethereumjs-util')
const BN = ethUtil.BN
const PendingTx = require('./components/pending-tx') const PendingTx = require('./components/pending-tx')
const PendingMsg = require('./components/pending-msg') const PendingMsg = require('./components/pending-msg')
@ -39,6 +41,7 @@ ConfirmTxScreen.prototype.render = function () {
var unconfTxList = txHelper(unconfTxs, unconfMsgs, network) var unconfTxList = txHelper(unconfTxs, unconfMsgs, network)
var index = state.index !== undefined ? state.index : 0 var index = state.index !== undefined ? state.index : 0
var txData = unconfTxList[index] || unconfTxList[0] || {} var txData = unconfTxList[index] || unconfTxList[0] || {}
var txParams = txData.txParams || {}
var isNotification = isPopupOrNotification() === 'notification' var isNotification = isPopupOrNotification() === 'notification'
return ( return (
@ -90,7 +93,9 @@ ConfirmTxScreen.prototype.render = function () {
selectedAddress: state.selectedAddress, selectedAddress: state.selectedAddress,
accounts: state.accounts, accounts: state.accounts,
identities: state.identities, identities: state.identities,
insufficientBalance: this.checkBalnceAgainstTx(txData),
// Actions // Actions
buyEth: this.buyEth.bind(this, txParams.from || state.selectedAddress),
sendTransaction: this.sendTransaction.bind(this, txData), sendTransaction: this.sendTransaction.bind(this, txData),
cancelTransaction: this.cancelTransaction.bind(this, txData), cancelTransaction: this.cancelTransaction.bind(this, txData),
signMessage: this.signMessage.bind(this, txData), signMessage: this.signMessage.bind(this, txData),
@ -111,6 +116,28 @@ function currentTxView (opts) {
return h(PendingMsg, opts) return h(PendingMsg, opts)
} }
} }
ConfirmTxScreen.prototype.checkBalnceAgainstTx = function (txData) {
var state = this.props
var txParams = txData.txParams || {}
var address = txParams.from || state.selectedAddress
var account = state.accounts[address]
var balance = account ? account.balance : '0x0'
var gasCost = new BN(ethUtil.stripHexPrefix(txParams.gas || txData.estimatedGas), 16)
var gasPrice = new BN(ethUtil.stripHexPrefix(txParams.gasPrice || '0x4a817c800'), 16)
var txFee = gasCost.mul(gasPrice)
var txValue = new BN(ethUtil.stripHexPrefix(txParams.value || '0x0'), 16)
var maxCost = txValue.add(txFee)
var balanceBn = new BN(ethUtil.stripHexPrefix(balance), 16)
return maxCost.gt(balanceBn)
}
ConfirmTxScreen.prototype.buyEth = function (address, event) {
event.stopPropagation()
this.props.dispatch(actions.buyEthView(address))
}
ConfirmTxScreen.prototype.sendTransaction = function (txData, event) { ConfirmTxScreen.prototype.sendTransaction = function (txData, event) {
event.stopPropagation() event.stopPropagation()

@ -36,24 +36,40 @@ button {
font-family: 'Montserrat Bold'; font-family: 'Montserrat Bold';
outline: none; outline: none;
cursor: pointer; cursor: pointer;
box-shadow: 0px 3px 6px rgba(247, 134, 28, 0.36);
/*margin: 10px;*/
padding: 8px 12px; padding: 8px 12px;
border: none; border: none;
background: #F7861C;
color: white; color: white;
transform-origin: center center; transform-origin: center center;
transition: transform 50ms ease-in; transition: transform 50ms ease-in;
/* default orange */
background: rgba(247, 134, 28, 1);
box-shadow: 0px 3px 6px rgba(247, 134, 28, 0.36);
}
button.btn-green {
background: rgba(106, 195, 96, 1);
box-shadow: 0px 3px 6px rgba(106, 195, 96, 0.36);
}
button.btn-red {
background: rgba(254, 35, 17, 1);
box-shadow: 0px 3px 6px rgba(254, 35, 17, 0.36);
}
button[disabled] {
cursor: not-allowed;
background: rgba(197, 197, 197, 1);
box-shadow: 0px 3px 6px rgba(197, 197, 197, 0.36);
} }
button.spaced { button.spaced {
margin: 2px; margin: 2px;
} }
button:hover { button:not([disabled]):hover {
transform: scale(1.1); transform: scale(1.1);
} }
button:active { button:not([disabled]):active {
transform: scale(0.95); transform: scale(0.95);
} }

@ -240,7 +240,7 @@ function reduceApp (state, action) {
name: 'confTx', name: 'confTx',
context: 0, context: 0,
}, },
transForward: true, transForward: action.transForward,
warning: null, warning: null,
}) })
@ -408,7 +408,7 @@ function reduceApp (state, action) {
transForward: true, transForward: true,
currentView: { currentView: {
name: 'buyEth', name: 'buyEth',
context: appState.currentView.context, context: appState.currentView.name,
}, },
buyView: { buyView: {
subview: 'buyForm', subview: 'buyForm',

Loading…
Cancel
Save