mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-22 17:33:23 +01:00
Merge branch 'master' into uat
This commit is contained in:
commit
d8896a8c31
17
CHANGELOG.md
17
CHANGELOG.md
@ -2,6 +2,23 @@
|
||||
|
||||
## Current Master
|
||||
|
||||
## 3.14.1 2018-2-1
|
||||
|
||||
- Further fix scrolling for Firefox.
|
||||
|
||||
## 3.14.0 2018-2-1
|
||||
|
||||
- Removed unneeded data from storage
|
||||
- Add a "reset account" feature to Settings
|
||||
- Add warning for importing some kinds of files.
|
||||
- Scrollable Setting view for Firefox.
|
||||
|
||||
## 3.13.8 2018-1-29
|
||||
|
||||
- Fix provider for Kovan network.
|
||||
- Bump limit for EventEmitter listeners before warning.
|
||||
- Display Error when empty string is entered as a token address.
|
||||
|
||||
## 3.13.7 2018-1-22
|
||||
|
||||
- Add ability to bypass gas estimation loading indicator.
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "MetaMask",
|
||||
"short_name": "Metamask",
|
||||
"version": "3.13.7",
|
||||
"version": "3.14.1",
|
||||
"manifest_version": 2,
|
||||
"author": "https://metamask.io",
|
||||
"description": "Ethereum Browser Extension",
|
||||
|
@ -152,6 +152,10 @@ module.exports = class TransactionController extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
wipeTransactions (address) {
|
||||
this.txStateManager.wipeTransactions(address)
|
||||
}
|
||||
|
||||
// Adds a tx to the txlist
|
||||
addTx (txMeta) {
|
||||
this.txStateManager.addTx(txMeta)
|
||||
|
@ -221,6 +221,17 @@ module.exports = class TransactionStateManger extends EventEmitter {
|
||||
this._setTxStatus(txId, 'failed')
|
||||
}
|
||||
|
||||
wipeTransactions (address) {
|
||||
// network only tx
|
||||
const txs = this.getFullTxList()
|
||||
const network = this.getNetwork()
|
||||
|
||||
// Filter out the ones from the current account and network
|
||||
const otherAccountTxs = txs.filter((txMeta) => !(txMeta.txParams.from === address && txMeta.metamaskNetworkId === network))
|
||||
|
||||
// Update state
|
||||
this._saveTxList(otherAccountTxs)
|
||||
}
|
||||
//
|
||||
// PRIVATE METHODS
|
||||
//
|
||||
|
@ -43,6 +43,8 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
constructor (opts) {
|
||||
super()
|
||||
|
||||
this.defaultMaxListeners = 20
|
||||
|
||||
this.sendUpdate = debounce(this.privateSendUpdate.bind(this), 200)
|
||||
|
||||
this.opts = opts
|
||||
@ -84,9 +86,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
})
|
||||
this.infuraController.scheduleInfuraNetworkCheck()
|
||||
|
||||
this.blacklistController = new BlacklistController({
|
||||
initState: initState.BlacklistController,
|
||||
})
|
||||
this.blacklistController = new BlacklistController()
|
||||
this.blacklistController.scheduleUpdates()
|
||||
|
||||
// rpc provider
|
||||
@ -198,12 +198,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
this.networkController.store.subscribe((state) => {
|
||||
this.store.updateState({ NetworkController: state })
|
||||
})
|
||||
this.blacklistController.store.subscribe((state) => {
|
||||
this.store.updateState({ BlacklistController: state })
|
||||
})
|
||||
this.recentBlocksController.store.subscribe((state) => {
|
||||
this.store.updateState({ RecentBlocks: state })
|
||||
})
|
||||
|
||||
this.infuraController.store.subscribe((state) => {
|
||||
this.store.updateState({ InfuraController: state })
|
||||
})
|
||||
@ -347,6 +342,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
addNewAccount: nodeify(this.addNewAccount, this),
|
||||
placeSeedWords: this.placeSeedWords.bind(this),
|
||||
clearSeedWordCache: this.clearSeedWordCache.bind(this),
|
||||
resetAccount: this.resetAccount.bind(this),
|
||||
importAccountWithStrategy: this.importAccountWithStrategy.bind(this),
|
||||
|
||||
// vault management
|
||||
@ -607,6 +603,13 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
cb(null, this.preferencesController.getSelectedAddress())
|
||||
}
|
||||
|
||||
resetAccount (cb) {
|
||||
const selectedAddress = this.preferencesController.getSelectedAddress()
|
||||
this.txController.wipeTransactions(selectedAddress)
|
||||
cb(null, selectedAddress)
|
||||
}
|
||||
|
||||
|
||||
importAccountWithStrategy (strategy, args, cb) {
|
||||
accountImporter.importAccount(strategy, args)
|
||||
.then((privateKey) => {
|
||||
|
34
app/scripts/migrations/021.js
Normal file
34
app/scripts/migrations/021.js
Normal file
@ -0,0 +1,34 @@
|
||||
const version = 21
|
||||
|
||||
/*
|
||||
|
||||
This migration removes the BlackListController from disk state
|
||||
|
||||
*/
|
||||
|
||||
const clone = require('clone')
|
||||
|
||||
module.exports = {
|
||||
version,
|
||||
|
||||
migrate: function (originalVersionedData) {
|
||||
const versionedData = clone(originalVersionedData)
|
||||
versionedData.meta.version = version
|
||||
try {
|
||||
const state = versionedData.data
|
||||
const newState = transformState(state)
|
||||
versionedData.data = newState
|
||||
} catch (err) {
|
||||
console.warn(`MetaMask Migration #${version}` + err.stack)
|
||||
}
|
||||
return Promise.resolve(versionedData)
|
||||
},
|
||||
}
|
||||
|
||||
function transformState (state) {
|
||||
const newState = state
|
||||
delete newState.BlacklistController
|
||||
delete newState.RecentBlocks
|
||||
return newState
|
||||
}
|
||||
|
@ -31,4 +31,5 @@ module.exports = [
|
||||
require('./018'),
|
||||
require('./019'),
|
||||
require('./020'),
|
||||
require('./021'),
|
||||
]
|
||||
|
@ -303,7 +303,7 @@ gulp.task('apply-prod-environment', function(done) {
|
||||
|
||||
gulp.task('dev', gulp.series('build:scss', 'dev:js', 'copy', gulp.parallel('watch:scss', 'copy:watch', 'dev:reload')))
|
||||
|
||||
gulp.task('build', gulp.series('clean', 'build:scss', gulp.parallel('build:js', 'copy', 'deps')))
|
||||
gulp.task('build', gulp.series('clean', 'build:scss', gulp.parallel('build:js', 'copy')))
|
||||
gulp.task('dist', gulp.series('apply-prod-environment', 'build', 'zip'))
|
||||
|
||||
// task generators
|
||||
|
@ -25,7 +25,7 @@ inherits(AddTokenScreen, Component)
|
||||
function AddTokenScreen () {
|
||||
this.state = {
|
||||
warning: null,
|
||||
address: null,
|
||||
address: '',
|
||||
symbol: 'TOKEN',
|
||||
decimals: 18,
|
||||
}
|
||||
@ -190,7 +190,7 @@ AddTokenScreen.prototype.validateInputs = function () {
|
||||
|
||||
const validAddress = ethUtil.isValidAddress(address)
|
||||
if (!validAddress) {
|
||||
msg += 'Address is invalid. '
|
||||
msg += 'Address is invalid.'
|
||||
}
|
||||
|
||||
const validDecimals = decimals >= 0 && decimals < 36
|
||||
|
@ -30,7 +30,12 @@ ConfigScreen.prototype.render = function () {
|
||||
var warning = state.warning
|
||||
|
||||
return (
|
||||
h('.flex-column.flex-grow', [
|
||||
h('.flex-column.flex-grow', {
|
||||
style:{
|
||||
maxHeight: '465px',
|
||||
overflowY: 'auto',
|
||||
},
|
||||
}, [
|
||||
|
||||
h(Modal, {}, []),
|
||||
|
||||
@ -57,6 +62,7 @@ ConfigScreen.prototype.render = function () {
|
||||
h('.flex-space-around', {
|
||||
style: {
|
||||
padding: '20px',
|
||||
overflow: 'auto',
|
||||
},
|
||||
}, [
|
||||
|
||||
@ -144,6 +150,40 @@ ConfigScreen.prototype.render = function () {
|
||||
}, 'Reveal Seed Words'),
|
||||
]),
|
||||
|
||||
h('hr.horizontal-line'),
|
||||
|
||||
h('div', {
|
||||
style: {
|
||||
marginTop: '20px',
|
||||
},
|
||||
}, [
|
||||
|
||||
h('p', {
|
||||
style: {
|
||||
fontFamily: 'Montserrat Light',
|
||||
fontSize: '13px',
|
||||
},
|
||||
}, [
|
||||
'Resetting is for developer use only. ',
|
||||
h('a', {
|
||||
href: 'http://metamask.helpscoutdocs.com/article/36-resetting-an-account',
|
||||
target: '_blank',
|
||||
onClick (event) { this.navigateTo(event.target.href) },
|
||||
}, 'Read more.'),
|
||||
]),
|
||||
h('br'),
|
||||
|
||||
h('button', {
|
||||
style: {
|
||||
alignSelf: 'center',
|
||||
},
|
||||
onClick (event) {
|
||||
event.preventDefault()
|
||||
state.dispatch(actions.resetAccount())
|
||||
},
|
||||
}, 'Reset Account'),
|
||||
]),
|
||||
|
||||
]),
|
||||
]),
|
||||
])
|
||||
@ -220,3 +260,7 @@ function currentProviderDisplay (metamaskState) {
|
||||
h('span', value),
|
||||
])
|
||||
}
|
||||
|
||||
ConfigScreen.prototype.navigateTo = function (url) {
|
||||
global.platform.openWindow({ url })
|
||||
}
|
||||
|
14
package.json
14
package.json
@ -78,7 +78,7 @@
|
||||
"eth-bin-to-ops": "^1.0.1",
|
||||
"eth-block-tracker": "^2.3.0",
|
||||
"eth-json-rpc-filters": "^1.2.5",
|
||||
"eth-json-rpc-infura": "^2.0.11",
|
||||
"eth-json-rpc-infura": "^3.0.0",
|
||||
"eth-keyring-controller": "^2.1.4",
|
||||
"eth-contract-metadata": "^1.1.5",
|
||||
"eth-hd-keyring": "^1.2.1",
|
||||
@ -192,7 +192,7 @@
|
||||
"deep-freeze-strict": "^1.1.1",
|
||||
"del": "^3.0.0",
|
||||
"envify": "^4.0.0",
|
||||
"enzyme": "^3.2.0",
|
||||
"enzyme": "^3.3.0",
|
||||
"enzyme-adapter-react-15": "^1.0.5",
|
||||
"eslint-plugin-chai": "0.0.1",
|
||||
"eslint-plugin-mocha": "^4.9.0",
|
||||
@ -201,6 +201,7 @@
|
||||
"fs-promise": "^2.0.3",
|
||||
"gulp": "github:gulpjs/gulp#6d71a658c61edb3090221579d8f97dbe086ba2ed",
|
||||
"gulp-babel": "^7.0.0",
|
||||
"gulp-eslint": "^4.0.0",
|
||||
"gulp-if": "^2.0.2",
|
||||
"gulp-json-editor": "^2.2.1",
|
||||
"gulp-livereload": "^3.8.1",
|
||||
@ -211,9 +212,8 @@
|
||||
"gulp-uglify": "^3.0.0",
|
||||
"gulp-uglify-es": "^1.0.0",
|
||||
"gulp-util": "^3.0.7",
|
||||
"gulp-watch": "^4.3.5",
|
||||
"gulp-watch": "^5.0.0",
|
||||
"gulp-zip": "^4.0.0",
|
||||
"gulp-eslint": "^4.0.0",
|
||||
"isomorphic-fetch": "^2.2.1",
|
||||
"jsdom": "^11.1.0",
|
||||
"jsdom-global": "^3.0.2",
|
||||
@ -224,7 +224,7 @@
|
||||
"karma-firefox-launcher": "^1.0.1",
|
||||
"karma-qunit": "^1.2.1",
|
||||
"lodash.assign": "^4.0.6",
|
||||
"mocha": "^4.0.0",
|
||||
"mocha": "^5.0.0",
|
||||
"mocha-eslint": "^4.0.0",
|
||||
"mocha-jsdom": "^1.1.0",
|
||||
"mocha-sinon": "^2.0.0",
|
||||
@ -237,11 +237,11 @@
|
||||
"react-addons-test-utils": "^15.5.1",
|
||||
"react-test-renderer": "^15.6.2",
|
||||
"react-testutils-additions": "^15.2.0",
|
||||
"redux-test-utils": "^0.1.3",
|
||||
"redux-test-utils": "^0.2.2",
|
||||
"sinon": "^4.0.0",
|
||||
"stylelint-config-standard": "^17.0.0",
|
||||
"tape": "^4.5.1",
|
||||
"testem": "^1.10.3",
|
||||
"testem": "^2.0.0",
|
||||
"uglifyify": "^4.0.2",
|
||||
"vinyl-buffer": "^1.0.1",
|
||||
"vinyl-source-stream": "^2.0.0",
|
||||
|
1
test/lib/migrations/002.json
Normal file
1
test/lib/migrations/002.json
Normal file
File diff suppressed because one or more lines are too long
@ -1,16 +1,20 @@
|
||||
const { shallow, mount } = require('enzyme')
|
||||
|
||||
exports.shallowWithStore = function shallowWithStore (component, store) {
|
||||
module.exports = {
|
||||
shallowWithStore,
|
||||
mountWithStore,
|
||||
}
|
||||
|
||||
function shallowWithStore (component, store) {
|
||||
const context = {
|
||||
store,
|
||||
}
|
||||
|
||||
return shallow(component, { context })
|
||||
return shallow(component, {context})
|
||||
}
|
||||
|
||||
exports.mountWithStore = function mountWithStore (component, store) {
|
||||
function mountWithStore (component, store) {
|
||||
const context = {
|
||||
store,
|
||||
}
|
||||
return mount(component, { context })
|
||||
return mount(component, {context})
|
||||
}
|
||||
|
16
test/unit/migrations/021-test.js
Normal file
16
test/unit/migrations/021-test.js
Normal file
@ -0,0 +1,16 @@
|
||||
const assert = require('assert')
|
||||
|
||||
const wallet2 = require('../../lib/migrations/002.json')
|
||||
const migration21 = require('../../../app/scripts/migrations/021')
|
||||
|
||||
describe('wallet2 is migrated successfully with out the BlacklistController', () => {
|
||||
it('should delete BlacklistController key', (done) => {
|
||||
migration21.migrate(wallet2)
|
||||
.then((migratedData) => {
|
||||
assert.equal(migratedData.meta.version, 21)
|
||||
assert(!migratedData.data.BlacklistController)
|
||||
assert(!migratedData.data.RecentBlocks)
|
||||
done()
|
||||
}).catch(done)
|
||||
})
|
||||
})
|
@ -238,4 +238,47 @@ describe('TransactionStateManger', function () {
|
||||
assert.equal(txStateManager.getFilteredTxList(filterParams).length, 5, `getFilteredTxList - ${JSON.stringify(filterParams)}`)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#wipeTransactions', function () {
|
||||
|
||||
const specificAddress = '0xaa'
|
||||
const otherAddress = '0xbb'
|
||||
|
||||
it('should remove only the transactions from a specific address', function () {
|
||||
|
||||
const txMetas = [
|
||||
{ id: 0, status: 'unapproved', txParams: { from: specificAddress, to: otherAddress }, metamaskNetworkId: currentNetworkId },
|
||||
{ id: 1, status: 'confirmed', txParams: { from: otherAddress, to: specificAddress }, metamaskNetworkId: currentNetworkId },
|
||||
{ id: 2, status: 'confirmed', txParams: { from: otherAddress, to: specificAddress }, metamaskNetworkId: currentNetworkId },
|
||||
]
|
||||
txMetas.forEach((txMeta) => txStateManager.addTx(txMeta, noop))
|
||||
|
||||
txStateManager.wipeTransactions(specificAddress)
|
||||
|
||||
const transactionsFromCurrentAddress = txStateManager.getTxList().filter((txMeta) => txMeta.txParams.from === specificAddress)
|
||||
const transactionsFromOtherAddresses = txStateManager.getTxList().filter((txMeta) => txMeta.txParams.from !== specificAddress)
|
||||
|
||||
assert.equal(transactionsFromCurrentAddress.length, 0)
|
||||
assert.equal(transactionsFromOtherAddresses.length, 2)
|
||||
})
|
||||
|
||||
it('should not remove the transactions from other networks', function () {
|
||||
const txMetas = [
|
||||
{ id: 0, status: 'unapproved', txParams: { from: specificAddress, to: otherAddress }, metamaskNetworkId: currentNetworkId },
|
||||
{ id: 1, status: 'confirmed', txParams: { from: specificAddress, to: otherAddress }, metamaskNetworkId: otherNetworkId },
|
||||
{ id: 2, status: 'confirmed', txParams: { from: specificAddress, to: otherAddress }, metamaskNetworkId: otherNetworkId },
|
||||
]
|
||||
|
||||
txMetas.forEach((txMeta) => txStateManager.addTx(txMeta, noop))
|
||||
|
||||
txStateManager.wipeTransactions(specificAddress)
|
||||
|
||||
const txsFromCurrentNetworkAndAddress = txStateManager.getTxList().filter((txMeta) => txMeta.txParams.from === specificAddress)
|
||||
const txFromOtherNetworks = txStateManager.getFullTxList().filter((txMeta) => txMeta.metamaskNetworkId === otherNetworkId)
|
||||
|
||||
assert.equal(txsFromCurrentNetworkAndAddress.length, 0)
|
||||
assert.equal(txFromOtherNetworks.length, 2)
|
||||
|
||||
})
|
||||
})
|
||||
})
|
43
test/unit/ui/add-token.spec.js
Normal file
43
test/unit/ui/add-token.spec.js
Normal file
@ -0,0 +1,43 @@
|
||||
const assert = require('assert')
|
||||
const { createMockStore } = require('redux-test-utils')
|
||||
const h = require('react-hyperscript')
|
||||
const { shallowWithStore } = require('../../lib/shallow-with-store')
|
||||
const AddTokenScreen = require('../../../ui/app/add-token')
|
||||
|
||||
describe('Add Token Screen', function () {
|
||||
let addTokenComponent, store, component
|
||||
const mockState = {
|
||||
metamask: {
|
||||
identities: {
|
||||
'0x7d3517b0d011698406d6e0aed8453f0be2697926': {
|
||||
'address': '0x7d3517b0d011698406d6e0aed8453f0be2697926',
|
||||
'name': 'Add Token Name',
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
beforeEach(function () {
|
||||
store = createMockStore(mockState)
|
||||
component = shallowWithStore(h(AddTokenScreen), store)
|
||||
addTokenComponent = component.dive()
|
||||
})
|
||||
|
||||
describe('#ValidateInputs', function () {
|
||||
|
||||
it('Default State', function () {
|
||||
addTokenComponent.instance().validateInputs()
|
||||
const state = addTokenComponent.state()
|
||||
assert.equal(state.warning, 'Address is invalid.')
|
||||
})
|
||||
|
||||
it('Address is a Metamask Identity', function () {
|
||||
addTokenComponent.setState({
|
||||
address: '0x7d3517b0d011698406d6e0aed8453f0be2697926',
|
||||
})
|
||||
addTokenComponent.instance().validateInputs()
|
||||
const state = addTokenComponent.state()
|
||||
assert.equal(state.warning, 'Personal address detected. Input the token contract address.')
|
||||
})
|
||||
|
||||
})
|
||||
})
|
@ -81,6 +81,12 @@ JsonImportSubview.prototype.createKeyringOnEnter = function (event) {
|
||||
|
||||
JsonImportSubview.prototype.createNewKeychain = function () {
|
||||
const state = this.state
|
||||
|
||||
if (!state) {
|
||||
const message = 'You must select a valid file to import.'
|
||||
return this.props.dispatch(actions.displayWarning(message))
|
||||
}
|
||||
|
||||
const { fileContents } = state
|
||||
|
||||
if (!fileContents) {
|
||||
|
@ -69,12 +69,14 @@ var actions = {
|
||||
addNewAccount,
|
||||
NEW_ACCOUNT_SCREEN: 'NEW_ACCOUNT_SCREEN',
|
||||
navigateToNewAccountScreen,
|
||||
resetAccount,
|
||||
showNewVaultSeed: showNewVaultSeed,
|
||||
showInfoPage: showInfoPage,
|
||||
// seed recovery actions
|
||||
REVEAL_SEED_CONFIRMATION: 'REVEAL_SEED_CONFIRMATION',
|
||||
revealSeedConfirmation: revealSeedConfirmation,
|
||||
requestRevealSeed: requestRevealSeed,
|
||||
|
||||
// unlock screen
|
||||
UNLOCK_IN_PROGRESS: 'UNLOCK_IN_PROGRESS',
|
||||
UNLOCK_FAILED: 'UNLOCK_FAILED',
|
||||
@ -396,6 +398,20 @@ function requestRevealSeed (password) {
|
||||
}
|
||||
}
|
||||
|
||||
function resetAccount () {
|
||||
return (dispatch) => {
|
||||
background.resetAccount((err, account) => {
|
||||
dispatch(actions.hideLoadingIndication())
|
||||
if (err) {
|
||||
dispatch(actions.displayWarning(err.message))
|
||||
}
|
||||
|
||||
log.info('Transaction history reset for ' + account)
|
||||
dispatch(actions.showAccountsPage())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function addNewKeyring (type, opts) {
|
||||
return (dispatch) => {
|
||||
dispatch(actions.showLoadingIndication())
|
||||
|
Loading…
Reference in New Issue
Block a user