mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 18:00:18 +01:00
Merge branch 'master' of github.com:MetaMask/metamask-extension into filter-leak-fix
This commit is contained in:
commit
671dafea9e
15
CHANGELOG.md
15
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
|
||||
|
||||
|
@ -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",
|
||||
|
@ -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))
|
||||
}
|
||||
|
||||
|
@ -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 reload in progress, no need to check reload logic
|
||||
if (reloadInProgress) return
|
||||
|
||||
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
|
||||
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)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
26
test/unit/tx-state-history-helper-test.js
Normal file
26
test/unit/tx-state-history-helper-test.js
Normal file
@ -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')
|
||||
})
|
||||
})
|
@ -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() })
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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) => {
|
||||
|
@ -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,
|
||||
|
@ -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),
|
||||
|
@ -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`
|
||||
|
@ -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;
|
||||
|
@ -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'),
|
||||
]),
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -80,7 +80,7 @@ UnlockScreen.prototype.render = function () {
|
||||
color: 'rgb(247, 134, 28)',
|
||||
textDecoration: 'underline',
|
||||
},
|
||||
}, 'I forgot my password.'),
|
||||
}, 'Restore from seed phrase'),
|
||||
]),
|
||||
])
|
||||
)
|
||||
|
@ -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 = ''
|
||||
|
Loading…
Reference in New Issue
Block a user