aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md15
-rw-r--r--app/manifest.json2
-rw-r--r--app/scripts/keyring-controller.js3
-rw-r--r--app/scripts/lib/auto-reload.js62
-rw-r--r--app/scripts/lib/inpage-provider.js6
-rw-r--r--development/uiStore.js4
-rw-r--r--mascara/src/lib/setup-iframe.js4
-rw-r--r--mascara/src/proxy.js4
-rw-r--r--package.json2
-rw-r--r--test/lib/mock-store.js4
-rw-r--r--test/unit/nonce-tracker-test.js19
-rw-r--r--test/unit/tx-state-history-helper-test.js26
-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/pending-msg.js15
-rw-r--r--ui/app/components/token-list.js2
-rw-r--r--ui/app/css/lib.css5
-rw-r--r--ui/app/info.js2
-rw-r--r--ui/app/reducers.js5
-rw-r--r--ui/app/unlock.js2
-rw-r--r--ui/lib/account-link.js10
23 files changed, 193 insertions, 56 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index cab45914e..a63a8604a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,21 @@
# Changelog
## Current Master
+- Readded loose keyring label back into the account list.
+- Add info on token contract addresses.
+- Add validation preventing users from inputting their own addresses as token tracking addresses.
+
+## 3.9.12 2017-9-6
+
+- Fix bug that prevented Web3 1.0 compatibility
+- Make eth_sign deprecation warning less noisy
+- Add useful link to eth_sign deprecation warning.
+- Fix bug with network version serialization over synchronous RPC
+- Add MetaMask version to state logs.
+- Add the total amount of tokens when multiple tokens are added under the token list
+- Use HTTPS links for Etherscan.
+- Update Support center link to new one with HTTPS.
+- Make web3 deprecation notice more useful by linking to a descriptive article.
## 3.9.11 2017-8-24
diff --git a/app/manifest.json b/app/manifest.json
index 7ec32ea78..256737c89 100644
--- a/app/manifest.json
+++ b/app/manifest.json
@@ -1,7 +1,7 @@
{
"name": "MetaMask",
"short_name": "Metamask",
- "version": "3.9.11",
+ "version": "3.9.12",
"manifest_version": 2,
"author": "https://metamask.io",
"description": "Ethereum Browser Extension",
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/auto-reload.js b/app/scripts/lib/auto-reload.js
index 6abce73ea..cce31c3d2 100644
--- a/app/scripts/lib/auto-reload.js
+++ b/app/scripts/lib/auto-reload.js
@@ -2,33 +2,55 @@ module.exports = setupDappAutoReload
function setupDappAutoReload (web3, observable) {
// export web3 as a global, checking for usage
+ let hasBeenWarned = false
+ let reloadInProgress = false
+ let lastTimeUsed
+ let lastSeenNetwork
+
global.web3 = new Proxy(web3, {
- get: (_web3, name) => {
- // get the time of use
- if (name !== '_used') {
- console.warn('MetaMask: web3 will be deprecated in the near future in favor of the ethereumProvider \nhttps://github.com/ethereum/mist/releases/tag/v0.9.0')
- _web3._used = Date.now()
+ get: (_web3, key) => {
+ // show warning once on web3 access
+ if (!hasBeenWarned && key !== 'currentProvider') {
+ console.warn('MetaMask: web3 will be deprecated in the near future in favor of the ethereumProvider \nhttps://github.com/MetaMask/faq/blob/master/detecting_metamask.md#web3-deprecation')
+ hasBeenWarned = true
}
- return _web3[name]
+ // get the time of use
+ lastTimeUsed = Date.now()
+ // return value normally
+ return _web3[key]
},
- set: (_web3, name, value) => {
- _web3[name] = value
+ set: (_web3, key, value) => {
+ // set value normally
+ _web3[key] = value
},
})
- var networkVersion
observable.subscribe(function (state) {
- // get the initial network
- const curentNetVersion = state.networkVersion
- if (!networkVersion) networkVersion = curentNetVersion
-
- if (curentNetVersion !== networkVersion && web3._used) {
- const timeSinceUse = Date.now() - web3._used
- // if web3 was recently used then delay the reloading of the page
- timeSinceUse > 500 ? triggerReset() : setTimeout(triggerReset, 500)
- // prevent reentry into if statement if state updates again before
- // reload
- networkVersion = curentNetVersion
+ // if reload in progress, no need to check reload logic
+ if (reloadInProgress) return
+
+ const currentNetwork = state.networkVersion
+
+ // set the initial network
+ if (!lastSeenNetwork) {
+ lastSeenNetwork = currentNetwork
+ return
+ }
+
+ // skip reload logic if web3 not used
+ if (!lastTimeUsed) return
+
+ // if network did not change, exit
+ if (currentNetwork === lastSeenNetwork) return
+
+ // initiate page reload
+ reloadInProgress = true
+ const timeSinceUse = Date.now() - lastTimeUsed
+ // if web3 was recently used then delay the reloading of the page
+ if (timeSinceUse > 500) {
+ triggerReset()
+ } else {
+ setTimeout(triggerReset, 500)
}
})
}
diff --git a/app/scripts/lib/inpage-provider.js b/app/scripts/lib/inpage-provider.js
index eb24dfcab..db46e4f17 100644
--- a/app/scripts/lib/inpage-provider.js
+++ b/app/scripts/lib/inpage-provider.js
@@ -38,8 +38,6 @@ function MetamaskInpageProvider (connectionStream) {
streamMiddleware.stream,
(err) => logStreamDisconnectWarning('MetaMask RpcProvider', err)
)
- // start and stop polling to unblock first block lock
-
// handle sendAsync requests via dapp-side rpc engine
const engine = new RpcEngine()
engine.push(createIdRemapMiddleware())
@@ -64,7 +62,7 @@ MetamaskInpageProvider.prototype.send = function (payload) {
case 'eth_coinbase':
// read from localStorage
selectedAddress = self.publicConfigStore.getState().selectedAddress
- result = selectedAddress
+ result = selectedAddress || null
break
case 'eth_uninstallFilter':
@@ -74,7 +72,7 @@ MetamaskInpageProvider.prototype.send = function (payload) {
case 'net_version':
const networkVersion = self.publicConfigStore.getState().networkVersion
- result = networkVersion
+ result = networkVersion || null
break
// throw not-supported Error
diff --git a/development/uiStore.js b/development/uiStore.js
index 1299ee1dc..c71d66d3b 100644
--- a/development/uiStore.js
+++ b/development/uiStore.js
@@ -1,7 +1,7 @@
const createStore = require('redux').createStore
const applyMiddleware = require('redux').applyMiddleware
-const thunkMiddleware = require('redux-thunk')
-const createLogger = require('redux-logger')
+const thunkMiddleware = require('redux-thunk').default
+const createLogger = require('redux-logger').createLogger
const rootReducer = require('../ui/app/reducers')
module.exports = configureStore
diff --git a/mascara/src/lib/setup-iframe.js b/mascara/src/lib/setup-iframe.js
index db67163df..dcf404574 100644
--- a/mascara/src/lib/setup-iframe.js
+++ b/mascara/src/lib/setup-iframe.js
@@ -1,5 +1,5 @@
const Iframe = require('iframe')
-const IframeStream = require('iframe-stream').IframeStream
+const createIframeStream = require('iframe-stream').IframeStream
module.exports = setupIframe
@@ -13,7 +13,7 @@ function setupIframe(opts) {
})
var iframe = frame.iframe
iframe.style.setProperty('display', 'none')
- var iframeStream = new IframeStream(iframe)
+ var iframeStream = createIframeStream(iframe)
return iframeStream
}
diff --git a/mascara/src/proxy.js b/mascara/src/proxy.js
index eabc547b4..5b95175f1 100644
--- a/mascara/src/proxy.js
+++ b/mascara/src/proxy.js
@@ -1,4 +1,4 @@
-const ParentStream = require('iframe-stream').ParentStream
+const createParentStream = require('iframe-stream').ParentStream
const SWcontroller = require('client-sw-ready-event/lib/sw-client.js')
const SwStream = require('sw-stream/lib/sw-stream.js')
const SetupUntrustedComunication = ('./lib/setup-untrusted-connection.js')
@@ -11,7 +11,7 @@ const background = new SWcontroller({
intervalDelay,
})
-const pageStream = new ParentStream()
+const pageStream = createParentStream()
background.on('ready', (_) => {
let swStream = SwStream({
serviceWorker: background.controller,
diff --git a/package.json b/package.json
index 3fede273c..571c76485 100644
--- a/package.json
+++ b/package.json
@@ -190,7 +190,7 @@
"react-addons-test-utils": "^15.5.1",
"react-test-renderer": "^15.5.4",
"react-testutils-additions": "^15.2.0",
- "sinon": "^2.3.8",
+ "sinon": "^3.2.0",
"tape": "^4.5.1",
"testem": "^1.10.3",
"uglifyify": "^4.0.2",
diff --git a/test/lib/mock-store.js b/test/lib/mock-store.js
index 0d50e2d9c..8af8f6d23 100644
--- a/test/lib/mock-store.js
+++ b/test/lib/mock-store.js
@@ -1,7 +1,7 @@
const createStore = require('redux').createStore
const applyMiddleware = require('redux').applyMiddleware
-const thunkMiddleware = require('redux-thunk')
-const createLogger = require('redux-logger')
+const thunkMiddleware = require('redux-thunk').default
+const createLogger = require('redux-logger').createLogger
const rootReducer = function () {}
module.exports = configureStore
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/test/unit/tx-state-history-helper-test.js b/test/unit/tx-state-history-helper-test.js
new file mode 100644
index 000000000..90cb10713
--- /dev/null
+++ b/test/unit/tx-state-history-helper-test.js
@@ -0,0 +1,26 @@
+const assert = require('assert')
+const clone = require('clone')
+const txStateHistoryHelper = require('../../app/scripts/lib/tx-state-history-helper')
+
+describe('deepCloneFromTxMeta', function () {
+ it('should clone deep', function () {
+ const input = {
+ foo: {
+ bar: {
+ bam: 'baz'
+ }
+ }
+ }
+ const output = txStateHistoryHelper.snapshotFromTxMeta(input)
+ assert('foo' in output, 'has a foo key')
+ assert('bar' in output.foo, 'has a bar key')
+ assert('bam' in output.foo.bar, 'has a bar key')
+ assert.equal(output.foo.bar.bam, 'baz', 'has a baz value')
+ })
+
+ it('should remove the history key', function () {
+ const input = { foo: 'bar', history: 'remembered' }
+ const output = txStateHistoryHelper.snapshotFromTxMeta(input)
+ assert(typeof output.history, 'undefined', 'should remove history')
+ })
+})
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/pending-msg.js b/ui/app/components/pending-msg.js
index b7133cda8..834719c53 100644
--- a/ui/app/components/pending-msg.js
+++ b/ui/app/components/pending-msg.js
@@ -35,10 +35,21 @@ PendingMsg.prototype.render = function () {
style: {
margin: '10px',
},
- }, `Signing this message can have
+ }, [
+ `Signing this message can have
dangerous side effects. Only sign messages from
sites you fully trust with your entire account.
- This dangerous method will be removed in a future version.`),
+ This dangerous method will be removed in a future version. `,
+ h('a', {
+ href: 'https://medium.com/metamask/the-new-secure-way-to-sign-data-in-your-browser-6af9dd2a1527',
+ style: { color: 'rgb(247, 134, 28)' },
+ onClick: (event) => {
+ event.preventDefault()
+ const url = 'https://medium.com/metamask/the-new-secure-way-to-sign-data-in-your-browser-6af9dd2a1527'
+ global.platform.openWindow({ url })
+ },
+ }, 'Read more here.'),
+ ]),
// message details
h(PendingTxDetails, state),
diff --git a/ui/app/components/token-list.js b/ui/app/components/token-list.js
index 2346568bc..998ec901d 100644
--- a/ui/app/components/token-list.js
+++ b/ui/app/components/token-list.js
@@ -95,7 +95,7 @@ TokenList.prototype.renderTokenStatusBar = function () {
let msg
if (tokens.length === 1) {
msg = `You own 1 token`
- } else if (tokens.length === 1) {
+ } else if (tokens.length > 1) {
msg = `You own ${tokens.length} tokens`
} else {
msg = `No tokens found`
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/info.js b/ui/app/info.js
index 899841c83..c69d83715 100644
--- a/ui/app/info.js
+++ b/ui/app/info.js
@@ -103,7 +103,7 @@ InfoScreen.prototype.render = function () {
[
h('div.fa.fa-support', [
h('a.info', {
- href: 'http://metamask.consensyssupport.happyfox.com',
+ href: 'https://support.metamask.com',
target: '_blank',
}, 'Visit our Support Center'),
]),
diff --git a/ui/app/reducers.js b/ui/app/reducers.js
index 36045772f..6a2f44534 100644
--- a/ui/app/reducers.js
+++ b/ui/app/reducers.js
@@ -42,7 +42,10 @@ function rootReducer (state, action) {
}
window.logState = function () {
- var stateString = JSON.stringify(window.METAMASK_CACHED_LOG_STATE, removeSeedWords, 2)
+ let state = window.METAMASK_CACHED_LOG_STATE
+ const version = global.platform.getVersion()
+ state.version = version
+ let stateString = JSON.stringify(state, removeSeedWords, 2)
return stateString
}
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'),
]),
])
)
diff --git a/ui/lib/account-link.js b/ui/lib/account-link.js
index d061d0ad1..037d990fa 100644
--- a/ui/lib/account-link.js
+++ b/ui/lib/account-link.js
@@ -3,19 +3,19 @@ module.exports = function (address, network) {
let link
switch (net) {
case 1: // main net
- link = `http://etherscan.io/address/${address}`
+ link = `https://etherscan.io/address/${address}`
break
case 2: // morden test net
- link = `http://morden.etherscan.io/address/${address}`
+ link = `https://morden.etherscan.io/address/${address}`
break
case 3: // ropsten test net
- link = `http://ropsten.etherscan.io/address/${address}`
+ link = `https://ropsten.etherscan.io/address/${address}`
break
case 4: // rinkeby test net
- link = `http://rinkeby.etherscan.io/address/${address}`
+ link = `https://rinkeby.etherscan.io/address/${address}`
break
case 42: // kovan test net
- link = `http://kovan.etherscan.io/address/${address}`
+ link = `https://kovan.etherscan.io/address/${address}`
break
default:
link = ''