aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md12
-rw-r--r--app/manifest.json7
-rw-r--r--app/scripts/keyring-controller.js3
-rw-r--r--app/scripts/lib/inpage-provider.js52
-rw-r--r--circle.yml4
-rw-r--r--test/unit/nonce-tracker-test.js19
-rw-r--r--ui/app/actions.js11
-rw-r--r--ui/app/add-token.js31
-rw-r--r--ui/app/app.js3
-rw-r--r--ui/app/components/account-dropdowns.js21
-rw-r--r--ui/app/components/dropdown.js2
-rw-r--r--ui/app/components/network.js11
-rw-r--r--ui/app/components/pending-tx.js36
-rw-r--r--ui/app/conf-tx.js10
-rw-r--r--ui/app/css/lib.css5
-rw-r--r--ui/app/unlock.js2
16 files changed, 166 insertions, 63 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c738ac375..0d41742be 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,18 @@
## Current Master
+## 3.10.0 2017-9-11
+
+- Readded loose keyring label back into the account list.
+- Remove cryptonator from chrome permissions.
+- Add info on token contract addresses.
+- Add validation preventing users from inputting their own addresses as token tracking addresses.
+- Added button to reject all transactions (thanks to davidp94! https://github.com/davidp94)
+
+## 3.9.13 2017-9-8
+
+- Changed the way we initialize the inpage provider to fix a bug affecting some developers.
+
## 3.9.12 2017-9-6
- Fix bug that prevented Web3 1.0 compatibility
diff --git a/app/manifest.json b/app/manifest.json
index 256737c89..bd25c1f6f 100644
--- a/app/manifest.json
+++ b/app/manifest.json
@@ -1,7 +1,7 @@
{
"name": "MetaMask",
"short_name": "Metamask",
- "version": "3.9.12",
+ "version": "3.10.0",
"manifest_version": 2,
"author": "https://metamask.io",
"description": "Ethereum Browser Extension",
@@ -57,9 +57,8 @@
"permissions": [
"storage",
"clipboardWrite",
- "http://localhost:8545/",
- "https://api.cryptonator.com/"
- ],
+ "http://localhost:8545/"
+ ],
"web_accessible_resources": [
"scripts/inpage.js"
],
diff --git a/app/scripts/keyring-controller.js b/app/scripts/keyring-controller.js
index 2edc8060e..fd57fac70 100644
--- a/app/scripts/keyring-controller.js
+++ b/app/scripts/keyring-controller.js
@@ -171,9 +171,9 @@ class KeyringController extends EventEmitter {
return this.setupAccounts(checkedAccounts)
})
.then(() => this.persistAllKeyrings())
+ .then(() => this._updateMemStoreKeyrings())
.then(() => this.fullUpdate())
.then(() => {
- this._updateMemStoreKeyrings()
return keyring
})
}
@@ -208,6 +208,7 @@ class KeyringController extends EventEmitter {
return selectedKeyring.addAccounts(1)
.then(this.setupAccounts.bind(this))
.then(this.persistAllKeyrings.bind(this))
+ .then(this._updateMemStoreKeyrings.bind(this))
.then(this.fullUpdate.bind(this))
}
diff --git a/app/scripts/lib/inpage-provider.js b/app/scripts/lib/inpage-provider.js
index c63af06dc..13888dc67 100644
--- a/app/scripts/lib/inpage-provider.js
+++ b/app/scripts/lib/inpage-provider.js
@@ -40,31 +40,37 @@ function MetamaskInpageProvider (connectionStream) {
// start and stop polling to unblock first block lock
self.idMap = {}
- // handle sendAsync requests via asyncProvider
- self.sendAsync = function (payload, cb) {
- // rewrite request ids
- var request = eachJsonMessage(payload, (_message) => {
- const message = Object.assign({}, _message)
- const newId = createRandomId()
- self.idMap[newId] = message.id
- message.id = newId
+}
+
+// handle sendAsync requests via asyncProvider
+// also remap ids inbound and outbound
+MetamaskInpageProvider.prototype.sendAsync = function (payload, cb) {
+ const self = this
+
+ // rewrite request ids
+ const request = eachJsonMessage(payload, (_message) => {
+ const message = Object.assign({}, _message)
+ const newId = createRandomId()
+ self.idMap[newId] = message.id
+ message.id = newId
+ return message
+ })
+
+ // forward to asyncProvider
+ self.asyncProvider.sendAsync(request, (err, _res) => {
+ if (err) return cb(err)
+ // transform messages to original ids
+ const res = eachJsonMessage(_res, (message) => {
+ const oldId = self.idMap[message.id]
+ delete self.idMap[message.id]
+ message.id = oldId
return message
})
- // forward to asyncProvider
- asyncProvider.sendAsync(request, function (err, res) {
- if (err) return cb(err)
- // transform messages to original ids
- eachJsonMessage(res, (message) => {
- var oldId = self.idMap[message.id]
- delete self.idMap[message.id]
- message.id = oldId
- return message
- })
- cb(null, res)
- })
- }
+ cb(null, res)
+ })
}
+
MetamaskInpageProvider.prototype.send = function (payload) {
const self = this
@@ -110,10 +116,6 @@ MetamaskInpageProvider.prototype.send = function (payload) {
}
}
-MetamaskInpageProvider.prototype.sendAsync = function () {
- throw new Error('MetamaskInpageProvider - sendAsync not overwritten')
-}
-
MetamaskInpageProvider.prototype.isConnected = function () {
return true
}
diff --git a/circle.yml b/circle.yml
index 2ea60bb9d..e81e1bcaa 100644
--- a/circle.yml
+++ b/circle.yml
@@ -1,10 +1,6 @@
machine:
node:
version: 8.1.4
-dependencies:
- pre:
- - "npm i -g testem"
- - "npm i -g mocha"
test:
override:
- "npm run ci" \ No newline at end of file
diff --git a/test/unit/nonce-tracker-test.js b/test/unit/nonce-tracker-test.js
index 3312a3bd0..8970cf84d 100644
--- a/test/unit/nonce-tracker-test.js
+++ b/test/unit/nonce-tracker-test.js
@@ -162,6 +162,25 @@ describe('Nonce Tracker', function () {
await nonceLock.releaseLock()
})
})
+
+ describe('Faq issue 67', function () {
+ beforeEach(function () {
+ const txGen = new MockTxGen()
+ const confirmedTxs = txGen.generate({ status: 'confirmed' }, { count: 64 })
+ const pendingTxs = txGen.generate({
+ status: 'submitted',
+ }, { count: 10 })
+ // 0x40 is 64 in hex:
+ nonceTracker = generateNonceTrackerWith(pendingTxs, [], '0x40')
+ })
+
+ it('should return nonce after network nonce', async function () {
+ this.timeout(15000)
+ const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926')
+ assert.equal(nonceLock.nextNonce, '74', `nonce should be 74 got ${nonceLock.nextNonce}`)
+ await nonceLock.releaseLock()
+ })
+ })
})
})
diff --git a/ui/app/actions.js b/ui/app/actions.js
index eebe65ba2..e793e6a21 100644
--- a/ui/app/actions.js
+++ b/ui/app/actions.js
@@ -104,6 +104,7 @@ var actions = {
txError: txError,
nextTx: nextTx,
previousTx: previousTx,
+ cancelAllTx: cancelAllTx,
viewPendingTx: viewPendingTx,
VIEW_PENDING_TX: 'VIEW_PENDING_TX',
// app messages
@@ -457,6 +458,16 @@ function cancelTx (txData) {
}
}
+function cancelAllTx (txsData) {
+ return (dispatch) => {
+ txsData.forEach((txData, i) => {
+ background.cancelTransaction(txData.id, () => {
+ dispatch(actions.completedTx(txData.id))
+ i === txsData.length - 1 ? dispatch(actions.goHome()) : null
+ })
+ })
+ }
+}
//
// initialize screen
//
diff --git a/ui/app/add-token.js b/ui/app/add-token.js
index 15ef7a852..18adc7eb5 100644
--- a/ui/app/add-token.js
+++ b/ui/app/add-token.js
@@ -3,6 +3,8 @@ const Component = require('react').Component
const h = require('react-hyperscript')
const connect = require('react-redux').connect
const actions = require('./actions')
+const Tooltip = require('./components/tooltip.js')
+
const ethUtil = require('ethereumjs-util')
const abi = require('human-standard-token-abi')
@@ -15,6 +17,7 @@ module.exports = connect(mapStateToProps)(AddTokenScreen)
function mapStateToProps (state) {
return {
+ identities: state.metamask.identities,
}
}
@@ -64,15 +67,25 @@ AddTokenScreen.prototype.render = function () {
}, [
h('div', [
- h('span', {
- style: { fontWeight: 'bold', paddingRight: '10px'},
- }, 'Token Address'),
+ h(Tooltip, {
+ position: 'top',
+ title: 'The contract of the actual token contract. Click for more info.',
+ }, [
+ h('a', {
+ style: { fontWeight: 'bold', paddingRight: '10px'},
+ href: 'https://consensyssupport.happyfox.com/staff/kb/article/24-what-is-a-token-contract-address',
+ target: '_blank',
+ }, [
+ h('span', 'Token Contract Address '),
+ h('i.fa.fa-question-circle'),
+ ]),
+ ]),
]),
h('section.flex-row.flex-center', [
h('input#token-address', {
name: 'address',
- placeholder: 'Token Address',
+ placeholder: 'Token Contract Address',
onChange: this.tokenAddressDidChange.bind(this),
style: {
width: 'inherit',
@@ -171,7 +184,9 @@ AddTokenScreen.prototype.tokenAddressDidChange = function (event) {
AddTokenScreen.prototype.validateInputs = function () {
let msg = ''
const state = this.state
+ const identitiesList = Object.keys(this.props.identities)
const { address, symbol, decimals } = state
+ const standardAddress = ethUtil.addHexPrefix(address).toLowerCase()
const validAddress = ethUtil.isValidAddress(address)
if (!validAddress) {
@@ -189,7 +204,12 @@ AddTokenScreen.prototype.validateInputs = function () {
msg += 'Symbol must be between 0 and 10 characters.'
}
- const isValid = validAddress && validDecimals
+ const ownAddress = identitiesList.includes(standardAddress)
+ if (ownAddress) {
+ msg = 'Personal address detected. Input the token contract address.'
+ }
+
+ const isValid = validAddress && validDecimals && !ownAddress
if (!isValid) {
this.setState({
@@ -216,4 +236,3 @@ AddTokenScreen.prototype.attemptToAutoFillTokenParams = async function (address)
this.setState({ symbol: symbol[0], decimals: decimals[0].toString() })
}
}
-
diff --git a/ui/app/app.js b/ui/app/app.js
index 1f3d5b0f8..ee800ea90 100644
--- a/ui/app/app.js
+++ b/ui/app/app.js
@@ -42,6 +42,7 @@ function mapStateToProps (state) {
identities,
accounts,
address,
+ keyrings,
} = state.metamask
const selected = address || Object.keys(accounts)[0]
@@ -69,6 +70,7 @@ function mapStateToProps (state) {
// state needed to get account dropdown temporarily rendering from app bar
identities,
selected,
+ keyrings,
}
}
@@ -187,6 +189,7 @@ App.prototype.renderAppBar = function () {
identities: this.props.identities,
selected: this.props.currentView.context,
network: this.props.network,
+ keyrings: this.props.keyrings,
}, []),
// hamburger
diff --git a/ui/app/components/account-dropdowns.js b/ui/app/components/account-dropdowns.js
index 7c24e70bd..b087a40d4 100644
--- a/ui/app/components/account-dropdowns.js
+++ b/ui/app/components/account-dropdowns.js
@@ -22,12 +22,19 @@ class AccountDropdowns extends Component {
}
renderAccounts () {
- const { identities, selected } = this.props
+ const { identities, selected, keyrings } = this.props
return Object.keys(identities).map((key, index) => {
const identity = identities[key]
const isSelected = identity.address === selected
+ const simpleAddress = identity.address.substring(2).toLowerCase()
+
+ const keyring = keyrings.find((kr) => {
+ return kr.accounts.includes(simpleAddress) ||
+ kr.accounts.includes(identity.address)
+ })
+
return h(
DropdownMenuItem,
{
@@ -51,6 +58,7 @@ class AccountDropdowns extends Component {
},
},
),
+ this.indicateIfLoose(keyring),
h('span', {
style: {
marginLeft: '20px',
@@ -67,6 +75,14 @@ class AccountDropdowns extends Component {
})
}
+ indicateIfLoose (keyring) {
+ try { // Sometimes keyrings aren't loaded yet:
+ const type = keyring.type
+ const isLoose = type !== 'HD Key Tree'
+ return isLoose ? h('.keyring-label', 'LOOSE') : null
+ } catch (e) { return }
+ }
+
renderAccountSelector () {
const { actions } = this.props
const { accountSelectorActive } = this.state
@@ -145,6 +161,8 @@ class AccountDropdowns extends Component {
)
}
+
+
renderAccountOptions () {
const { actions } = this.props
const { optionsMenuActive } = this.state
@@ -278,6 +296,7 @@ AccountDropdowns.defaultProps = {
AccountDropdowns.propTypes = {
identities: PropTypes.objectOf(PropTypes.object),
selected: PropTypes.string,
+ keyrings: PropTypes.array,
}
const mapDispatchToProps = (dispatch) => {
diff --git a/ui/app/components/dropdown.js b/ui/app/components/dropdown.js
index 34c7149ee..73710acc2 100644
--- a/ui/app/components/dropdown.js
+++ b/ui/app/components/dropdown.js
@@ -32,7 +32,7 @@ class Dropdown extends Component {
'style',
`
li.dropdown-menu-item:hover { color:rgb(225, 225, 225); }
- li.dropdown-menu-item { color: rgb(185, 185, 185); }
+ li.dropdown-menu-item { color: rgb(185, 185, 185); position: relative }
`
),
...children,
diff --git a/ui/app/components/network.js b/ui/app/components/network.js
index 698a0bbb9..0dbe37cdd 100644
--- a/ui/app/components/network.js
+++ b/ui/app/components/network.js
@@ -22,7 +22,7 @@ Network.prototype.render = function () {
let iconName, hoverText
if (networkNumber === 'loading') {
- return h('span', {
+ return h('span.pointer', {
style: {
display: 'flex',
alignItems: 'center',
@@ -37,7 +37,7 @@ Network.prototype.render = function () {
},
src: 'images/loading.svg',
}),
- h('i.fa.fa-sort-desc'),
+ h('i.fa.fa-caret-down'),
])
} else if (providerName === 'mainnet') {
hoverText = 'Main Ethereum Network'
@@ -73,7 +73,8 @@ Network.prototype.render = function () {
style: {
color: '#039396',
}},
- 'Ethereum Main Net'),
+ 'Main Network'),
+ h('i.fa.fa-caret-down.fa-lg'),
])
case 'ropsten-test-network':
return h('.network-indicator', [
@@ -83,6 +84,7 @@ Network.prototype.render = function () {
color: '#ff6666',
}},
'Ropsten Test Net'),
+ h('i.fa.fa-caret-down.fa-lg'),
])
case 'kovan-test-network':
return h('.network-indicator', [
@@ -92,6 +94,7 @@ Network.prototype.render = function () {
color: '#690496',
}},
'Kovan Test Net'),
+ h('i.fa.fa-caret-down.fa-lg'),
])
case 'rinkeby-test-network':
return h('.network-indicator', [
@@ -101,6 +104,7 @@ Network.prototype.render = function () {
color: '#e7a218',
}},
'Rinkeby Test Net'),
+ h('i.fa.fa-caret-down.fa-lg'),
])
default:
return h('.network-indicator', [
@@ -116,6 +120,7 @@ Network.prototype.render = function () {
color: '#AEAEAE',
}},
'Private Network'),
+ h('i.fa.fa-caret-down.fa-lg'),
])
}
})(),
diff --git a/ui/app/components/pending-tx.js b/ui/app/components/pending-tx.js
index 5324ccd64..3e53d47f9 100644
--- a/ui/app/components/pending-tx.js
+++ b/ui/app/components/pending-tx.js
@@ -66,6 +66,8 @@ PendingTx.prototype.render = function () {
const balanceBn = hexToBn(balance)
const insufficientBalance = balanceBn.lt(maxCost)
+ const buyDisabled = insufficientBalance || !this.state.valid || !isValidAddress || this.state.submitting
+ const showRejectAll = props.unconfTxListLength > 1
this.inputs = []
@@ -297,14 +299,6 @@ PendingTx.prototype.render = function () {
margin: '14px 25px',
},
}, [
-
-
- insufficientBalance ?
- h('button.btn-green', {
- onClick: props.buyEth,
- }, 'Buy Ether')
- : null,
-
h('button', {
onClick: (event) => {
this.resetGasFields()
@@ -312,18 +306,30 @@ PendingTx.prototype.render = function () {
},
}, 'Reset'),
- // Accept Button
- h('input.confirm.btn-green', {
- type: 'submit',
- value: 'SUBMIT',
- style: { marginLeft: '10px' },
- disabled: insufficientBalance || !this.state.valid || !isValidAddress || this.state.submitting,
- }),
+ // Accept Button or Buy Button
+ insufficientBalance ? h('button.btn-green', { onClick: props.buyEth }, 'Buy Ether') :
+ h('input.confirm.btn-green', {
+ type: 'submit',
+ value: 'SUBMIT',
+ style: { marginLeft: '10px' },
+ disabled: buyDisabled,
+ }),
h('button.cancel.btn-red', {
onClick: props.cancelTransaction,
}, 'Reject'),
]),
+ showRejectAll ? h('.flex-row.flex-space-around.conf-buttons', {
+ style: {
+ display: 'flex',
+ justifyContent: 'flex-end',
+ margin: '14px 25px',
+ },
+ }, [
+ h('button.cancel.btn-red', {
+ onClick: props.cancelAllTransactions,
+ }, 'Reject All'),
+ ]) : null,
]),
])
)
diff --git a/ui/app/conf-tx.js b/ui/app/conf-tx.js
index 34727ff78..1ee4166f7 100644
--- a/ui/app/conf-tx.js
+++ b/ui/app/conf-tx.js
@@ -52,6 +52,8 @@ ConfirmTxScreen.prototype.render = function () {
log.info(`rendering a combined ${unconfTxList.length} unconf msg & txs`)
if (unconfTxList.length === 0) return h(Loading, { isLoading: true })
+ const unconfTxListLength = unconfTxList.length
+
return (
h('.flex-column.flex-grow', [
@@ -101,10 +103,12 @@ ConfirmTxScreen.prototype.render = function () {
conversionRate,
currentCurrency,
blockGasLimit,
+ unconfTxListLength,
// Actions
buyEth: this.buyEth.bind(this, txParams.from || props.selectedAddress),
sendTransaction: this.sendTransaction.bind(this),
cancelTransaction: this.cancelTransaction.bind(this, txData),
+ cancelAllTransactions: this.cancelAllTransactions.bind(this, unconfTxList),
signMessage: this.signMessage.bind(this, txData),
signPersonalMessage: this.signPersonalMessage.bind(this, txData),
cancelMessage: this.cancelMessage.bind(this, txData),
@@ -151,6 +155,12 @@ ConfirmTxScreen.prototype.cancelTransaction = function (txData, event) {
this.props.dispatch(actions.cancelTx(txData))
}
+ConfirmTxScreen.prototype.cancelAllTransactions = function (unconfTxList, event) {
+ this.stopPropagation(event)
+ event.preventDefault()
+ this.props.dispatch(actions.cancelAllTx(unconfTxList))
+}
+
ConfirmTxScreen.prototype.signMessage = function (msgData, event) {
log.info('conf-tx.js: signing message')
var params = msgData.msgParams
diff --git a/ui/app/css/lib.css b/ui/app/css/lib.css
index 81647d1c1..f3acbee76 100644
--- a/ui/app/css/lib.css
+++ b/ui/app/css/lib.css
@@ -215,12 +215,13 @@ hr.horizontal-line {
z-index: 1;
font-size: 11px;
background: rgba(255,0,0,0.8);
- bottom: -47px;
color: white;
+ bottom: 0px;
+ left: -8px;
border-radius: 10px;
height: 20px;
min-width: 20px;
- position: relative;
+ position: absolute;
display: flex;
align-items: center;
justify-content: center;
diff --git a/ui/app/unlock.js b/ui/app/unlock.js
index 9bacd5124..4180791c4 100644
--- a/ui/app/unlock.js
+++ b/ui/app/unlock.js
@@ -80,7 +80,7 @@ UnlockScreen.prototype.render = function () {
color: 'rgb(247, 134, 28)',
textDecoration: 'underline',
},
- }, 'I forgot my password.'),
+ }, 'Restore from seed phrase'),
]),
])
)