mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Merge branch 'NewUI-flat' into MM-333-auto-add-users-to-new-UI
This commit is contained in:
commit
845aec82b9
@ -2,6 +2,12 @@
|
|||||||
|
|
||||||
## Current Master
|
## Current Master
|
||||||
|
|
||||||
|
## 3.12.1 2017-11-29
|
||||||
|
|
||||||
|
- Fix bug where a user could be shown two different seed phrases.
|
||||||
|
- Detect when multiple web3 extensions are active, and provide useful error.
|
||||||
|
- Adds notice about seed phrase backup.
|
||||||
|
|
||||||
## 3.12.0 2017-10-25
|
## 3.12.0 2017-10-25
|
||||||
|
|
||||||
- Add support for alternative ENS TLDs (Ethereum Name Service Top-Level Domains).
|
- Add support for alternative ENS TLDs (Ethereum Name Service Top-Level Domains).
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<!--
|
<!--
|
||||||
FAQ:
|
|
||||||
BEFORE SUBMITTING, please make sure your question hasn't been answered in our FAQ: https://github.com/MetaMask/faq
|
BEFORE SUBMITTING, please make sure your question hasn't been answered in our support center: https://support.metamask.io
|
||||||
Common questions such as "Where is my ether?" or "Where did my tokens go?" are answered in the FAQ.
|
Common questions such as "Where is my ether?" or "Where did my tokens go?" are answered there.
|
||||||
|
|
||||||
Bug Reports:
|
Bug Reports:
|
||||||
|
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
const urlUtil = require('url')
|
const urlUtil = require('url')
|
||||||
const endOfStream = require('end-of-stream')
|
const endOfStream = require('end-of-stream')
|
||||||
const pipe = require('pump')
|
const pump = require('pump')
|
||||||
const log = require('loglevel')
|
const log = require('loglevel')
|
||||||
const extension = require('extensionizer')
|
const extension = require('extensionizer')
|
||||||
const LocalStorageStore = require('obs-store/lib/localStorage')
|
const LocalStorageStore = require('obs-store/lib/localStorage')
|
||||||
const storeTransform = require('obs-store/lib/transform')
|
const storeTransform = require('obs-store/lib/transform')
|
||||||
|
const asStream = require('obs-store/lib/asStream')
|
||||||
const ExtensionPlatform = require('./platforms/extension')
|
const ExtensionPlatform = require('./platforms/extension')
|
||||||
const Migrator = require('./lib/migrator/')
|
const Migrator = require('./lib/migrator/')
|
||||||
const migrations = require('./migrations/')
|
const migrations = require('./migrations/')
|
||||||
@ -72,10 +73,10 @@ function setupController (initState) {
|
|||||||
global.metamaskController = controller
|
global.metamaskController = controller
|
||||||
|
|
||||||
// setup state persistence
|
// setup state persistence
|
||||||
pipe(
|
pump(
|
||||||
controller.store,
|
asStream(controller.store),
|
||||||
storeTransform(versionifyData),
|
storeTransform(versionifyData),
|
||||||
diskStore
|
asStream(diskStore)
|
||||||
)
|
)
|
||||||
|
|
||||||
function versionifyData (state) {
|
function versionifyData (state) {
|
||||||
|
@ -53,7 +53,7 @@ module.exports = class NetworkController extends EventEmitter {
|
|||||||
lookupNetwork () {
|
lookupNetwork () {
|
||||||
// Prevent firing when provider is not defined.
|
// Prevent firing when provider is not defined.
|
||||||
if (!this.ethQuery || !this.ethQuery.sendAsync) {
|
if (!this.ethQuery || !this.ethQuery.sendAsync) {
|
||||||
return
|
return log.warn('NetworkController - lookupNetwork aborted due to missing ethQuery')
|
||||||
}
|
}
|
||||||
this.ethQuery.sendAsync({ method: 'net_version' }, (err, network) => {
|
this.ethQuery.sendAsync({ method: 'net_version' }, (err, network) => {
|
||||||
if (err) return this.setNetworkState('loading')
|
if (err) return this.setNetworkState('loading')
|
||||||
|
@ -72,6 +72,12 @@ module.exports = class TransactionController extends EventEmitter {
|
|||||||
})
|
})
|
||||||
this.pendingTxTracker.on('tx:failed', this.txStateManager.setTxStatusFailed.bind(this.txStateManager))
|
this.pendingTxTracker.on('tx:failed', this.txStateManager.setTxStatusFailed.bind(this.txStateManager))
|
||||||
this.pendingTxTracker.on('tx:confirmed', this.txStateManager.setTxStatusConfirmed.bind(this.txStateManager))
|
this.pendingTxTracker.on('tx:confirmed', this.txStateManager.setTxStatusConfirmed.bind(this.txStateManager))
|
||||||
|
this.pendingTxTracker.on('tx:block-update', (txMeta, latestBlockNumber) => {
|
||||||
|
if (!txMeta.firstRetryBlockNumber) {
|
||||||
|
txMeta.firstRetryBlockNumber = latestBlockNumber
|
||||||
|
this.txStateManager.updateTx(txMeta, 'transactions/pending-tx-tracker#event: tx:block-update')
|
||||||
|
}
|
||||||
|
})
|
||||||
this.pendingTxTracker.on('tx:retry', (txMeta) => {
|
this.pendingTxTracker.on('tx:retry', (txMeta) => {
|
||||||
if (!('retryCount' in txMeta)) txMeta.retryCount = 0
|
if (!('retryCount' in txMeta)) txMeta.retryCount = 0
|
||||||
txMeta.retryCount++
|
txMeta.retryCount++
|
||||||
|
@ -31,6 +31,13 @@ var inpageProvider = new MetamaskInpageProvider(metamaskStream)
|
|||||||
// setup web3
|
// setup web3
|
||||||
//
|
//
|
||||||
|
|
||||||
|
if (typeof window.web3 !== 'undefined') {
|
||||||
|
throw new Error(`MetaMask detected another web3.
|
||||||
|
MetaMask will not work reliably with another web3 extension.
|
||||||
|
This usually happens if you have two MetaMasks installed,
|
||||||
|
or MetaMask and another web3 extension. Please remove one
|
||||||
|
and try again.`)
|
||||||
|
}
|
||||||
var web3 = new Web3(inpageProvider)
|
var web3 = new Web3(inpageProvider)
|
||||||
web3.setProvider = function () {
|
web3.setProvider = function () {
|
||||||
log.debug('MetaMask - overrode web3.setProvider')
|
log.debug('MetaMask - overrode web3.setProvider')
|
||||||
|
@ -3,6 +3,7 @@ const RpcEngine = require('json-rpc-engine')
|
|||||||
const createIdRemapMiddleware = require('json-rpc-engine/src/idRemapMiddleware')
|
const createIdRemapMiddleware = require('json-rpc-engine/src/idRemapMiddleware')
|
||||||
const createStreamMiddleware = require('json-rpc-middleware-stream')
|
const createStreamMiddleware = require('json-rpc-middleware-stream')
|
||||||
const LocalStorageStore = require('obs-store')
|
const LocalStorageStore = require('obs-store')
|
||||||
|
const asStream = require('obs-store/lib/asStream')
|
||||||
const ObjectMultiplex = require('obj-multiplex')
|
const ObjectMultiplex = require('obj-multiplex')
|
||||||
|
|
||||||
module.exports = MetamaskInpageProvider
|
module.exports = MetamaskInpageProvider
|
||||||
@ -21,9 +22,10 @@ function MetamaskInpageProvider (connectionStream) {
|
|||||||
|
|
||||||
// subscribe to metamask public config (one-way)
|
// subscribe to metamask public config (one-way)
|
||||||
self.publicConfigStore = new LocalStorageStore({ storageKey: 'MetaMask-Config' })
|
self.publicConfigStore = new LocalStorageStore({ storageKey: 'MetaMask-Config' })
|
||||||
|
|
||||||
pump(
|
pump(
|
||||||
mux.createStream('publicConfig'),
|
mux.createStream('publicConfig'),
|
||||||
self.publicConfigStore,
|
asStream(self.publicConfigStore),
|
||||||
(err) => logStreamDisconnectWarning('MetaMask PublicConfigStore', err)
|
(err) => logStreamDisconnectWarning('MetaMask PublicConfigStore', err)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -65,11 +65,11 @@ module.exports = class PendingTransactionTracker extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
resubmitPendingTxs () {
|
resubmitPendingTxs (block) {
|
||||||
const pending = this.getPendingTransactions()
|
const pending = this.getPendingTransactions()
|
||||||
// only try resubmitting if their are transactions to resubmit
|
// only try resubmitting if their are transactions to resubmit
|
||||||
if (!pending.length) return
|
if (!pending.length) return
|
||||||
pending.forEach((txMeta) => this._resubmitTx(txMeta).catch((err) => {
|
pending.forEach((txMeta) => this._resubmitTx(txMeta, block.number).catch((err) => {
|
||||||
/*
|
/*
|
||||||
Dont marked as failed if the error is a "known" transaction warning
|
Dont marked as failed if the error is a "known" transaction warning
|
||||||
"there is already a transaction with the same sender-nonce
|
"there is already a transaction with the same sender-nonce
|
||||||
@ -101,13 +101,25 @@ module.exports = class PendingTransactionTracker extends EventEmitter {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
async _resubmitTx (txMeta) {
|
async _resubmitTx (txMeta, latestBlockNumber) {
|
||||||
|
if (!txMeta.firstRetryBlockNumber) {
|
||||||
|
this.emit('tx:block-update', txMeta, latestBlockNumber)
|
||||||
|
}
|
||||||
|
|
||||||
if (Date.now() > txMeta.time + this.retryTimePeriod) {
|
if (Date.now() > txMeta.time + this.retryTimePeriod) {
|
||||||
const hours = (this.retryTimePeriod / 3.6e+6).toFixed(1)
|
const hours = (this.retryTimePeriod / 3.6e+6).toFixed(1)
|
||||||
const err = new Error(`Gave up submitting after ${hours} hours.`)
|
const err = new Error(`Gave up submitting after ${hours} hours.`)
|
||||||
return this.emit('tx:failed', txMeta.id, err)
|
return this.emit('tx:failed', txMeta.id, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const firstRetryBlockNumber = txMeta.firstRetryBlockNumber || latestBlockNumber
|
||||||
|
const txBlockDistance = Number.parseInt(latestBlockNumber, 16) - Number.parseInt(firstRetryBlockNumber, 16)
|
||||||
|
|
||||||
|
const retryCount = txMeta.retryCount || 0
|
||||||
|
|
||||||
|
// Exponential backoff to limit retries at publishing
|
||||||
|
if (txBlockDistance <= Math.pow(2, retryCount) - 1) return
|
||||||
|
|
||||||
// Only auto-submit already-signed txs:
|
// Only auto-submit already-signed txs:
|
||||||
if (!('rawTx' in txMeta)) return
|
if (!('rawTx' in txMeta)) return
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ const extend = require('xtend')
|
|||||||
const pump = require('pump')
|
const pump = require('pump')
|
||||||
const Dnode = require('dnode')
|
const Dnode = require('dnode')
|
||||||
const ObservableStore = require('obs-store')
|
const ObservableStore = require('obs-store')
|
||||||
|
const asStream = require('obs-store/lib/asStream')
|
||||||
const AccountTracker = require('./lib/account-tracker')
|
const AccountTracker = require('./lib/account-tracker')
|
||||||
const EthQuery = require('eth-query')
|
const EthQuery = require('eth-query')
|
||||||
const RpcEngine = require('json-rpc-engine')
|
const RpcEngine = require('json-rpc-engine')
|
||||||
@ -31,6 +32,7 @@ const ConfigManager = require('./lib/config-manager')
|
|||||||
const nodeify = require('./lib/nodeify')
|
const nodeify = require('./lib/nodeify')
|
||||||
const accountImporter = require('./account-import-strategies')
|
const accountImporter = require('./account-import-strategies')
|
||||||
const getBuyEthUrl = require('./lib/buy-eth-url')
|
const getBuyEthUrl = require('./lib/buy-eth-url')
|
||||||
|
const Mutex = require('await-semaphore').Mutex
|
||||||
const version = require('../manifest.json').version
|
const version = require('../manifest.json').version
|
||||||
|
|
||||||
module.exports = class MetamaskController extends EventEmitter {
|
module.exports = class MetamaskController extends EventEmitter {
|
||||||
@ -38,10 +40,12 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||||||
constructor (opts) {
|
constructor (opts) {
|
||||||
super()
|
super()
|
||||||
|
|
||||||
|
|
||||||
this.sendUpdate = debounce(this.privateSendUpdate.bind(this), 200)
|
this.sendUpdate = debounce(this.privateSendUpdate.bind(this), 200)
|
||||||
|
|
||||||
this.opts = opts
|
this.opts = opts
|
||||||
const initState = opts.initState || {}
|
const initState = opts.initState || {}
|
||||||
|
this.recordFirstTimeInfo(initState)
|
||||||
|
|
||||||
// platform-specific api
|
// platform-specific api
|
||||||
this.platform = opts.platform
|
this.platform = opts.platform
|
||||||
@ -49,6 +53,9 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||||||
// observable state store
|
// observable state store
|
||||||
this.store = new ObservableStore(initState)
|
this.store = new ObservableStore(initState)
|
||||||
|
|
||||||
|
// lock to ensure only one vault created at once
|
||||||
|
this.createVaultMutex = new Mutex()
|
||||||
|
|
||||||
// network store
|
// network store
|
||||||
this.networkController = new NetworkController(initState.NetworkController)
|
this.networkController = new NetworkController(initState.NetworkController)
|
||||||
|
|
||||||
@ -144,6 +151,8 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||||||
// notices
|
// notices
|
||||||
this.noticeController = new NoticeController({
|
this.noticeController = new NoticeController({
|
||||||
initState: initState.NoticeController,
|
initState: initState.NoticeController,
|
||||||
|
version,
|
||||||
|
firstVersion: initState.firstTimeInfo.version,
|
||||||
})
|
})
|
||||||
this.noticeController.updateNoticesList()
|
this.noticeController.updateNoticesList()
|
||||||
// to be uncommented when retrieving notices from a remote server.
|
// to be uncommented when retrieving notices from a remote server.
|
||||||
@ -454,7 +463,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||||||
|
|
||||||
setupPublicConfig (outStream) {
|
setupPublicConfig (outStream) {
|
||||||
pump(
|
pump(
|
||||||
this.publicConfigStore,
|
asStream(this.publicConfigStore),
|
||||||
outStream,
|
outStream,
|
||||||
(err) => {
|
(err) => {
|
||||||
if (err) log.error(err)
|
if (err) log.error(err)
|
||||||
@ -470,15 +479,34 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||||||
// Vault Management
|
// Vault Management
|
||||||
//
|
//
|
||||||
|
|
||||||
async createNewVaultAndKeychain (password, cb) {
|
async createNewVaultAndKeychain (password) {
|
||||||
const vault = await this.keyringController.createNewVaultAndKeychain(password)
|
const release = await this.createVaultMutex.acquire()
|
||||||
this.selectFirstIdentity(vault)
|
let vault
|
||||||
|
|
||||||
|
try {
|
||||||
|
const accounts = await this.keyringController.getAccounts()
|
||||||
|
|
||||||
|
if (accounts.length > 0) {
|
||||||
|
vault = await this.keyringController.fullUpdate()
|
||||||
|
|
||||||
|
} else {
|
||||||
|
vault = await this.keyringController.createNewVaultAndKeychain(password)
|
||||||
|
this.selectFirstIdentity(vault)
|
||||||
|
}
|
||||||
|
release()
|
||||||
|
} catch (err) {
|
||||||
|
release()
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
|
||||||
return vault
|
return vault
|
||||||
}
|
}
|
||||||
|
|
||||||
async createNewVaultAndRestore (password, seed, cb) {
|
async createNewVaultAndRestore (password, seed) {
|
||||||
|
const release = await this.createVaultMutex.acquire()
|
||||||
const vault = await this.keyringController.createNewVaultAndRestore(password, seed)
|
const vault = await this.keyringController.createNewVaultAndRestore(password, seed)
|
||||||
this.selectFirstIdentity(vault)
|
this.selectFirstIdentity(vault)
|
||||||
|
release()
|
||||||
return vault
|
return vault
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -785,4 +813,13 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
recordFirstTimeInfo (initState) {
|
||||||
|
if (!('firstTimeInfo' in initState)) {
|
||||||
|
initState.firstTimeInfo = {
|
||||||
|
version,
|
||||||
|
date: Date.now(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
41
app/scripts/migrations/020.js
Normal file
41
app/scripts/migrations/020.js
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
const version = 20
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
This migration ensures previous installations
|
||||||
|
get a `firstTimeInfo` key on the metamask state,
|
||||||
|
so that we can version notices in the future.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
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
|
||||||
|
if ('metamask' in newState &&
|
||||||
|
!('firstTimeInfo' in newState.metamask)) {
|
||||||
|
newState.metamask.firstTimeInfo = {
|
||||||
|
version: '3.12.0',
|
||||||
|
date: Date.now(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newState
|
||||||
|
}
|
||||||
|
|
@ -30,4 +30,5 @@ module.exports = [
|
|||||||
require('./017'),
|
require('./017'),
|
||||||
require('./018'),
|
require('./018'),
|
||||||
require('./019'),
|
require('./019'),
|
||||||
|
require('./020'),
|
||||||
]
|
]
|
||||||
|
@ -1,13 +1,17 @@
|
|||||||
const EventEmitter = require('events').EventEmitter
|
const EventEmitter = require('events').EventEmitter
|
||||||
|
const semver = require('semver')
|
||||||
const extend = require('xtend')
|
const extend = require('xtend')
|
||||||
const ObservableStore = require('obs-store')
|
const ObservableStore = require('obs-store')
|
||||||
const hardCodedNotices = require('../../notices/notices.json')
|
const hardCodedNotices = require('../../notices/notices.json')
|
||||||
|
const uniqBy = require('lodash.uniqby')
|
||||||
|
|
||||||
module.exports = class NoticeController extends EventEmitter {
|
module.exports = class NoticeController extends EventEmitter {
|
||||||
|
|
||||||
constructor (opts) {
|
constructor (opts) {
|
||||||
super()
|
super()
|
||||||
this.noticePoller = null
|
this.noticePoller = null
|
||||||
|
this.firstVersion = opts.firstVersion
|
||||||
|
this.version = opts.version
|
||||||
const initState = extend({
|
const initState = extend({
|
||||||
noticesList: [],
|
noticesList: [],
|
||||||
}, opts.initState)
|
}, opts.initState)
|
||||||
@ -30,9 +34,9 @@ module.exports = class NoticeController extends EventEmitter {
|
|||||||
return unreadNotices[unreadNotices.length - 1]
|
return unreadNotices[unreadNotices.length - 1]
|
||||||
}
|
}
|
||||||
|
|
||||||
setNoticesList (noticesList) {
|
async setNoticesList (noticesList) {
|
||||||
this.store.updateState({ noticesList })
|
this.store.updateState({ noticesList })
|
||||||
return Promise.resolve(true)
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
markNoticeRead (noticeToMark, cb) {
|
markNoticeRead (noticeToMark, cb) {
|
||||||
@ -50,12 +54,14 @@ module.exports = class NoticeController extends EventEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateNoticesList () {
|
async updateNoticesList () {
|
||||||
return this._retrieveNoticeData().then((newNotices) => {
|
const newNotices = await this._retrieveNoticeData()
|
||||||
var oldNotices = this.getNoticesList()
|
const oldNotices = this.getNoticesList()
|
||||||
var combinedNotices = this._mergeNotices(oldNotices, newNotices)
|
const combinedNotices = this._mergeNotices(oldNotices, newNotices)
|
||||||
return Promise.resolve(this.setNoticesList(combinedNotices))
|
const filteredNotices = this._filterNotices(combinedNotices)
|
||||||
})
|
const result = this.setNoticesList(filteredNotices)
|
||||||
|
this._updateMemstore()
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
startPolling () {
|
startPolling () {
|
||||||
@ -68,22 +74,30 @@ module.exports = class NoticeController extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_mergeNotices (oldNotices, newNotices) {
|
_mergeNotices (oldNotices, newNotices) {
|
||||||
var noticeMap = this._mapNoticeIds(oldNotices)
|
return uniqBy(oldNotices.concat(newNotices), 'id')
|
||||||
newNotices.forEach((notice) => {
|
}
|
||||||
if (noticeMap.indexOf(notice.id) === -1) {
|
|
||||||
oldNotices.push(notice)
|
_filterNotices(notices) {
|
||||||
|
return notices.filter((newNotice) => {
|
||||||
|
if ('version' in newNotice) {
|
||||||
|
const satisfied = semver.satisfies(this.version, newNotice.version)
|
||||||
|
return satisfied
|
||||||
}
|
}
|
||||||
|
if ('firstVersion' in newNotice) {
|
||||||
|
const satisfied = semver.satisfies(this.firstVersion, newNotice.firstVersion)
|
||||||
|
return satisfied
|
||||||
|
}
|
||||||
|
return true
|
||||||
})
|
})
|
||||||
return oldNotices
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_mapNoticeIds (notices) {
|
_mapNoticeIds (notices) {
|
||||||
return notices.map((notice) => notice.id)
|
return notices.map((notice) => notice.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
_retrieveNoticeData () {
|
async _retrieveNoticeData () {
|
||||||
// Placeholder for the API.
|
// Placeholder for the API.
|
||||||
return Promise.resolve(hardCodedNotices)
|
return hardCodedNotices
|
||||||
}
|
}
|
||||||
|
|
||||||
_updateMemstore () {
|
_updateMemstore () {
|
||||||
|
@ -126,11 +126,17 @@ gulp.task('manifest:production', function() {
|
|||||||
'./dist/firefox/manifest.json',
|
'./dist/firefox/manifest.json',
|
||||||
'./dist/chrome/manifest.json',
|
'./dist/chrome/manifest.json',
|
||||||
'./dist/edge/manifest.json',
|
'./dist/edge/manifest.json',
|
||||||
|
'./dist/opera/manifest.json',
|
||||||
],{base: './dist/'})
|
],{base: './dist/'})
|
||||||
|
|
||||||
|
// Exclude chromereload script in production:
|
||||||
.pipe(gulpif(!debug,jsoneditor(function(json) {
|
.pipe(gulpif(!debug,jsoneditor(function(json) {
|
||||||
json.background.scripts = ["scripts/background.js"]
|
json.background.scripts = json.background.scripts.filter((script) => {
|
||||||
|
return !script.includes('chromereload')
|
||||||
|
})
|
||||||
return json
|
return json
|
||||||
})))
|
})))
|
||||||
|
|
||||||
.pipe(gulp.dest('./dist/', { overwrite: true }))
|
.pipe(gulp.dest('./dist/', { overwrite: true }))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ class NoticeScreen extends Component {
|
|||||||
<Identicon address={address} diameter={70} />
|
<Identicon address={address} diameter={70} />
|
||||||
<div className="tou__title">{title}</div>
|
<div className="tou__title">{title}</div>
|
||||||
<Markdown
|
<Markdown
|
||||||
className="tou__body"
|
className="tou__body markdown"
|
||||||
source={body}
|
source={body}
|
||||||
skipHtml
|
skipHtml
|
||||||
/>
|
/>
|
||||||
|
11
notices/archive/notice_3.md
Normal file
11
notices/archive/notice_3.md
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
Please take a moment to [back up your seed phrase again](https://support.metamask.io/kb/article/28-abbu-always-be-backed-up-how-to-make-sure-your-12-word-metamask-seed-phrase-is-backed-up).
|
||||||
|
|
||||||
|
MetaMask has become aware of a previous issue where a very small number of users were shown the wrong seed phrase to back up. The only way to protect yourself from this issue, is to back up your seed phrase again now.
|
||||||
|
|
||||||
|
You can follow the guide at this link:
|
||||||
|
|
||||||
|
[https://support.metamask.io/kb/article/28-abbu-always-be-backed-up-how-to-make-sure-your-12-word-metamask-seed-phrase-is-backed-up](https://support.metamask.io/kb/article/28-abbu-always-be-backed-up-how-to-make-sure-your-12-word-metamask-seed-phrase-is-backed-up)
|
||||||
|
|
||||||
|
We have fixed the known issue, but will be issuing ongoing bug bounties to help prevent this kind of problem in the future.
|
||||||
|
|
||||||
|
For more information on this issue, [see this blog post](https://medium.com/metamask/seed-phrase-issue-bounty-awarded-e1986e811021)
|
@ -1 +1 @@
|
|||||||
3
|
4
|
File diff suppressed because one or more lines are too long
@ -118,6 +118,7 @@
|
|||||||
"lodash.debounce": "^4.0.8",
|
"lodash.debounce": "^4.0.8",
|
||||||
"lodash.memoize": "^4.1.2",
|
"lodash.memoize": "^4.1.2",
|
||||||
"lodash.shuffle": "^4.2.0",
|
"lodash.shuffle": "^4.2.0",
|
||||||
|
"lodash.uniqby": "^4.7.0",
|
||||||
"loglevel": "^1.4.1",
|
"loglevel": "^1.4.1",
|
||||||
"metamascara": "^1.3.1",
|
"metamascara": "^1.3.1",
|
||||||
"metamask-logo": "^2.1.2",
|
"metamask-logo": "^2.1.2",
|
||||||
@ -126,7 +127,7 @@
|
|||||||
"multiplex": "^6.7.0",
|
"multiplex": "^6.7.0",
|
||||||
"number-to-bn": "^1.7.0",
|
"number-to-bn": "^1.7.0",
|
||||||
"obj-multiplex": "^1.0.0",
|
"obj-multiplex": "^1.0.0",
|
||||||
"obs-store": "^2.3.1",
|
"obs-store": "^3.0.0",
|
||||||
"once": "^1.3.3",
|
"once": "^1.3.3",
|
||||||
"ping-pong-stream": "^1.0.0",
|
"ping-pong-stream": "^1.0.0",
|
||||||
"pojo-migrator": "^2.1.0",
|
"pojo-migrator": "^2.1.0",
|
||||||
@ -142,7 +143,7 @@
|
|||||||
"react-addons-css-transition-group": "^15.6.0",
|
"react-addons-css-transition-group": "^15.6.0",
|
||||||
"react-dom": "^15.6.2",
|
"react-dom": "^15.6.2",
|
||||||
"react-hyperscript": "^3.0.0",
|
"react-hyperscript": "^3.0.0",
|
||||||
"react-markdown": "^2.3.0",
|
"react-markdown": "^3.0.0",
|
||||||
"react-redux": "^5.0.5",
|
"react-redux": "^5.0.5",
|
||||||
"react-select": "^1.0.0",
|
"react-select": "^1.0.0",
|
||||||
"react-simple-file-input": "^2.0.0",
|
"react-simple-file-input": "^2.0.0",
|
||||||
@ -160,6 +161,7 @@
|
|||||||
"sandwich-expando": "^1.1.3",
|
"sandwich-expando": "^1.1.3",
|
||||||
"semaphore": "^1.0.5",
|
"semaphore": "^1.0.5",
|
||||||
"shallow-copy": "0.0.1",
|
"shallow-copy": "0.0.1",
|
||||||
|
"semver": "^5.4.1",
|
||||||
"sw-stream": "^2.0.0",
|
"sw-stream": "^2.0.0",
|
||||||
"textarea-caret": "^3.0.1",
|
"textarea-caret": "^3.0.1",
|
||||||
"through2": "^2.0.3",
|
"through2": "^2.0.3",
|
||||||
|
@ -6,23 +6,7 @@ async function runFirstTimeUsageTest (assert, done) {
|
|||||||
|
|
||||||
const app = $('#app-content')
|
const app = $('#app-content')
|
||||||
|
|
||||||
// recurse notices
|
await skipNotices(app)
|
||||||
while (true) {
|
|
||||||
const button = app.find('button')
|
|
||||||
if (button.html() === 'Accept') {
|
|
||||||
// still notices to accept
|
|
||||||
const termsPage = app.find('.markdown')[0]
|
|
||||||
termsPage.scrollTop = termsPage.scrollHeight
|
|
||||||
await timeout()
|
|
||||||
console.log('Clearing notice')
|
|
||||||
button.click()
|
|
||||||
await timeout()
|
|
||||||
} else {
|
|
||||||
// exit loop
|
|
||||||
console.log('No more notices...')
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await timeout()
|
await timeout()
|
||||||
|
|
||||||
@ -51,28 +35,13 @@ async function runFirstTimeUsageTest (assert, done) {
|
|||||||
assert.equal(created.textContent, 'Your unique account image', 'unique image screen')
|
assert.equal(created.textContent, 'Your unique account image', 'unique image screen')
|
||||||
|
|
||||||
// Agree button
|
// Agree button
|
||||||
const button = app.find('button')[0]
|
let button = app.find('button')[0]
|
||||||
assert.ok(button, 'button present')
|
assert.ok(button, 'button present')
|
||||||
button.click()
|
button.click()
|
||||||
|
|
||||||
await timeout(1000)
|
await timeout(1000)
|
||||||
|
|
||||||
// Privacy Screen
|
await skipNotices(app)
|
||||||
const detail = app.find('.tou__title')[0]
|
|
||||||
assert.equal(detail.textContent, 'Privacy Notice', 'privacy notice screen')
|
|
||||||
app.find('button').click()
|
|
||||||
|
|
||||||
await timeout(1000)
|
|
||||||
|
|
||||||
|
|
||||||
// terms of service screen
|
|
||||||
const tou = app.find('.tou__title')[0]
|
|
||||||
assert.equal(tou.textContent, 'Terms of Use', 'terms of use screen')
|
|
||||||
app.find('.tou__body').scrollTop(100000)
|
|
||||||
await timeout(1000)
|
|
||||||
|
|
||||||
app.find('.first-time-flow__button').click()
|
|
||||||
await timeout(1000)
|
|
||||||
|
|
||||||
// secret backup phrase
|
// secret backup phrase
|
||||||
const seedTitle = app.find('.backup-phrase__title')[0]
|
const seedTitle = app.find('.backup-phrase__title')[0]
|
||||||
@ -156,4 +125,24 @@ function timeout (time) {
|
|||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
setTimeout(resolve, time || 1500)
|
setTimeout(resolve, time || 1500)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function skipNotices (app) {
|
||||||
|
while (true) {
|
||||||
|
const button = app.find('button')
|
||||||
|
if (button && button.html() === 'Accept') {
|
||||||
|
// still notices to accept
|
||||||
|
const termsPage = app.find('.markdown')[0]
|
||||||
|
if (!termsPage) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
termsPage.scrollTop = termsPage.scrollHeight
|
||||||
|
await timeout()
|
||||||
|
button.click()
|
||||||
|
await timeout()
|
||||||
|
} else {
|
||||||
|
console.log('No more notices...')
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -11,6 +11,15 @@ describe('MetaMaskController', function () {
|
|||||||
unlockAccountMessage: noop,
|
unlockAccountMessage: noop,
|
||||||
showUnapprovedTx: noop,
|
showUnapprovedTx: noop,
|
||||||
platform: {},
|
platform: {},
|
||||||
|
encryptor: {
|
||||||
|
encrypt: function(password, object) {
|
||||||
|
this.object = object
|
||||||
|
return Promise.resolve()
|
||||||
|
},
|
||||||
|
decrypt: function () {
|
||||||
|
return Promise.resolve(this.object)
|
||||||
|
}
|
||||||
|
},
|
||||||
// initial state
|
// initial state
|
||||||
initState: clone(firstTimeState),
|
initState: clone(firstTimeState),
|
||||||
})
|
})
|
||||||
@ -27,6 +36,30 @@ describe('MetaMaskController', function () {
|
|||||||
|
|
||||||
describe('Metamask Controller', function () {
|
describe('Metamask Controller', function () {
|
||||||
assert(metamaskController)
|
assert(metamaskController)
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
sinon.spy(metamaskController.keyringController, 'createNewVaultAndKeychain')
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(function () {
|
||||||
|
metamaskController.keyringController.createNewVaultAndKeychain.restore()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('#createNewVaultAndKeychain', function () {
|
||||||
|
it('can only create new vault on keyringController once', async function () {
|
||||||
|
|
||||||
|
const selectStub = sinon.stub(metamaskController, 'selectFirstIdentity')
|
||||||
|
|
||||||
|
const password = 'a-fake-password'
|
||||||
|
|
||||||
|
const first = await metamaskController.createNewVaultAndKeychain(password)
|
||||||
|
const second = await metamaskController.createNewVaultAndKeychain(password)
|
||||||
|
|
||||||
|
assert(metamaskController.keyringController.createNewVaultAndKeychain.calledOnce)
|
||||||
|
|
||||||
|
selectStub.reset()
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -207,6 +207,7 @@ describe('PendingTransactionTracker', function () {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('#resubmitPendingTxs', function () {
|
describe('#resubmitPendingTxs', function () {
|
||||||
|
const blockStub = { number: '0x0' };
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
const txMeta2 = txMeta3 = txMeta
|
const txMeta2 = txMeta3 = txMeta
|
||||||
txList = [txMeta, txMeta2, txMeta3].map((tx) => {
|
txList = [txMeta, txMeta2, txMeta3].map((tx) => {
|
||||||
@ -224,7 +225,7 @@ describe('PendingTransactionTracker', function () {
|
|||||||
Promise.all(txList.map((tx) => tx.processed))
|
Promise.all(txList.map((tx) => tx.processed))
|
||||||
.then((txCompletedList) => done())
|
.then((txCompletedList) => done())
|
||||||
.catch(done)
|
.catch(done)
|
||||||
pendingTxTracker.resubmitPendingTxs()
|
pendingTxTracker.resubmitPendingTxs(blockStub)
|
||||||
})
|
})
|
||||||
it('should not emit \'tx:failed\' if the txMeta throws a known txError', function (done) {
|
it('should not emit \'tx:failed\' if the txMeta throws a known txError', function (done) {
|
||||||
knownErrors =[
|
knownErrors =[
|
||||||
@ -251,7 +252,7 @@ describe('PendingTransactionTracker', function () {
|
|||||||
.then((txCompletedList) => done())
|
.then((txCompletedList) => done())
|
||||||
.catch(done)
|
.catch(done)
|
||||||
|
|
||||||
pendingTxTracker.resubmitPendingTxs()
|
pendingTxTracker.resubmitPendingTxs(blockStub)
|
||||||
})
|
})
|
||||||
it('should emit \'tx:warning\' if it encountered a real error', function (done) {
|
it('should emit \'tx:warning\' if it encountered a real error', function (done) {
|
||||||
pendingTxTracker.once('tx:warning', (txMeta, err) => {
|
pendingTxTracker.once('tx:warning', (txMeta, err) => {
|
||||||
@ -269,28 +270,74 @@ describe('PendingTransactionTracker', function () {
|
|||||||
.then((txCompletedList) => done())
|
.then((txCompletedList) => done())
|
||||||
.catch(done)
|
.catch(done)
|
||||||
|
|
||||||
pendingTxTracker.resubmitPendingTxs()
|
pendingTxTracker.resubmitPendingTxs(blockStub)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
describe('#_resubmitTx', function () {
|
describe('#_resubmitTx', function () {
|
||||||
it('should publishing the transaction', function (done) {
|
const mockFirstRetryBlockNumber = '0x1'
|
||||||
const enoughBalance = '0x100000'
|
let txMetaToTestExponentialBackoff
|
||||||
pendingTxTracker.getBalance = (address) => {
|
|
||||||
assert.equal(address, txMeta.txParams.from, 'Should pass the address')
|
|
||||||
return enoughBalance
|
|
||||||
}
|
|
||||||
pendingTxTracker.publishTransaction = async (rawTx) => {
|
|
||||||
assert.equal(rawTx, txMeta.rawTx, 'Should pass the rawTx')
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stubbing out current account state:
|
beforeEach(() => {
|
||||||
// Adding the fake tx:
|
pendingTxTracker.getBalance = (address) => {
|
||||||
pendingTxTracker._resubmitTx(txMeta)
|
assert.equal(address, txMeta.txParams.from, 'Should pass the address')
|
||||||
.then(() => done())
|
return enoughBalance
|
||||||
.catch((err) => {
|
}
|
||||||
assert.ifError(err, 'should not throw an error')
|
pendingTxTracker.publishTransaction = async (rawTx) => {
|
||||||
done(err)
|
assert.equal(rawTx, txMeta.rawTx, 'Should pass the rawTx')
|
||||||
|
}
|
||||||
|
sinon.spy(pendingTxTracker, 'publishTransaction')
|
||||||
|
|
||||||
|
txMetaToTestExponentialBackoff = Object.assign({}, txMeta, {
|
||||||
|
retryCount: 4,
|
||||||
|
firstRetryBlockNumber: mockFirstRetryBlockNumber,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
pendingTxTracker.publishTransaction.reset()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should publish the transaction', function (done) {
|
||||||
|
const enoughBalance = '0x100000'
|
||||||
|
|
||||||
|
// Stubbing out current account state:
|
||||||
|
// Adding the fake tx:
|
||||||
|
pendingTxTracker._resubmitTx(txMeta)
|
||||||
|
.then(() => done())
|
||||||
|
.catch((err) => {
|
||||||
|
assert.ifError(err, 'should not throw an error')
|
||||||
|
done(err)
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.equal(pendingTxTracker.publishTransaction.callCount, 1, 'Should call publish transaction')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should not publish the transaction if the limit of retries has been exceeded', function (done) {
|
||||||
|
const enoughBalance = '0x100000'
|
||||||
|
const mockLatestBlockNumber = '0x5'
|
||||||
|
|
||||||
|
pendingTxTracker._resubmitTx(txMetaToTestExponentialBackoff, mockLatestBlockNumber)
|
||||||
|
.then(() => done())
|
||||||
|
.catch((err) => {
|
||||||
|
assert.ifError(err, 'should not throw an error')
|
||||||
|
done(err)
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.equal(pendingTxTracker.publishTransaction.callCount, 0, 'Should NOT call publish transaction')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should publish the transaction if the number of blocks since last retry exceeds the last set limit', function (done) {
|
||||||
|
const enoughBalance = '0x100000'
|
||||||
|
const mockLatestBlockNumber = '0x11'
|
||||||
|
|
||||||
|
pendingTxTracker._resubmitTx(txMetaToTestExponentialBackoff, mockLatestBlockNumber)
|
||||||
|
.then(() => done())
|
||||||
|
.catch((err) => {
|
||||||
|
assert.ifError(err, 'should not throw an error')
|
||||||
|
done(err)
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.equal(pendingTxTracker.publishTransaction.callCount, 1, 'Should call publish transaction')
|
||||||
})
|
})
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -149,6 +149,7 @@ var actions = {
|
|||||||
UPDATE_SEND_AMOUNT: 'UPDATE_SEND_AMOUNT',
|
UPDATE_SEND_AMOUNT: 'UPDATE_SEND_AMOUNT',
|
||||||
UPDATE_SEND_MEMO: 'UPDATE_SEND_MEMO',
|
UPDATE_SEND_MEMO: 'UPDATE_SEND_MEMO',
|
||||||
UPDATE_SEND_ERRORS: 'UPDATE_SEND_ERRORS',
|
UPDATE_SEND_ERRORS: 'UPDATE_SEND_ERRORS',
|
||||||
|
UPDATE_MAX_MODE: 'UPDATE_MAX_MODE',
|
||||||
UPDATE_SEND: 'UPDATE_SEND',
|
UPDATE_SEND: 'UPDATE_SEND',
|
||||||
CLEAR_SEND: 'CLEAR_SEND',
|
CLEAR_SEND: 'CLEAR_SEND',
|
||||||
updateGasLimit,
|
updateGasLimit,
|
||||||
@ -160,6 +161,7 @@ var actions = {
|
|||||||
updateSendAmount,
|
updateSendAmount,
|
||||||
updateSendMemo,
|
updateSendMemo,
|
||||||
updateSendErrors,
|
updateSendErrors,
|
||||||
|
setMaxModeTo,
|
||||||
updateSend,
|
updateSend,
|
||||||
clearSend,
|
clearSend,
|
||||||
setSelectedAddress,
|
setSelectedAddress,
|
||||||
@ -642,6 +644,13 @@ function updateSendErrors (error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setMaxModeTo (bool) {
|
||||||
|
return {
|
||||||
|
type: actions.UPDATE_MAX_MODE,
|
||||||
|
value: bool,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function updateSend (newSend) {
|
function updateSend (newSend) {
|
||||||
return {
|
return {
|
||||||
type: actions.UPDATE_SEND,
|
type: actions.UPDATE_SEND,
|
||||||
|
@ -5,6 +5,8 @@ const connect = require('react-redux').connect
|
|||||||
const actions = require('../../actions')
|
const actions = require('../../actions')
|
||||||
const GasModalCard = require('./gas-modal-card')
|
const GasModalCard = require('./gas-modal-card')
|
||||||
|
|
||||||
|
const ethUtil = require('ethereumjs-util')
|
||||||
|
|
||||||
const {
|
const {
|
||||||
MIN_GAS_PRICE_DEC,
|
MIN_GAS_PRICE_DEC,
|
||||||
MIN_GAS_LIMIT_DEC,
|
MIN_GAS_LIMIT_DEC,
|
||||||
@ -19,6 +21,7 @@ const {
|
|||||||
conversionUtil,
|
conversionUtil,
|
||||||
multiplyCurrencies,
|
multiplyCurrencies,
|
||||||
conversionGreaterThan,
|
conversionGreaterThan,
|
||||||
|
subtractCurrencies,
|
||||||
} = require('../../conversion-util')
|
} = require('../../conversion-util')
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@ -30,6 +33,7 @@ const {
|
|||||||
getSendFrom,
|
getSendFrom,
|
||||||
getCurrentAccountWithSendEtherInfo,
|
getCurrentAccountWithSendEtherInfo,
|
||||||
getSelectedTokenToFiatRate,
|
getSelectedTokenToFiatRate,
|
||||||
|
getSendMaxModeState,
|
||||||
} = require('../../selectors')
|
} = require('../../selectors')
|
||||||
|
|
||||||
function mapStateToProps (state) {
|
function mapStateToProps (state) {
|
||||||
@ -42,6 +46,7 @@ function mapStateToProps (state) {
|
|||||||
gasLimit: getGasLimit(state),
|
gasLimit: getGasLimit(state),
|
||||||
conversionRate,
|
conversionRate,
|
||||||
amount: getSendAmount(state),
|
amount: getSendAmount(state),
|
||||||
|
maxModeOn: getSendMaxModeState(state),
|
||||||
balance: currentAccount.balance,
|
balance: currentAccount.balance,
|
||||||
primaryCurrency: selectedToken && selectedToken.symbol,
|
primaryCurrency: selectedToken && selectedToken.symbol,
|
||||||
selectedToken,
|
selectedToken,
|
||||||
@ -55,6 +60,7 @@ function mapDispatchToProps (dispatch) {
|
|||||||
updateGasPrice: newGasPrice => dispatch(actions.updateGasPrice(newGasPrice)),
|
updateGasPrice: newGasPrice => dispatch(actions.updateGasPrice(newGasPrice)),
|
||||||
updateGasLimit: newGasLimit => dispatch(actions.updateGasLimit(newGasLimit)),
|
updateGasLimit: newGasLimit => dispatch(actions.updateGasLimit(newGasLimit)),
|
||||||
updateGasTotal: newGasTotal => dispatch(actions.updateGasTotal(newGasTotal)),
|
updateGasTotal: newGasTotal => dispatch(actions.updateGasTotal(newGasTotal)),
|
||||||
|
updateSendAmount: newAmount => dispatch(actions.updateSendAmount(newAmount)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,8 +99,21 @@ CustomizeGasModal.prototype.save = function (gasPrice, gasLimit, gasTotal) {
|
|||||||
updateGasLimit,
|
updateGasLimit,
|
||||||
hideModal,
|
hideModal,
|
||||||
updateGasTotal,
|
updateGasTotal,
|
||||||
|
maxModeOn,
|
||||||
|
selectedToken,
|
||||||
|
balance,
|
||||||
|
updateSendAmount,
|
||||||
} = this.props
|
} = this.props
|
||||||
|
|
||||||
|
if (maxModeOn && !selectedToken) {
|
||||||
|
const maxAmount = subtractCurrencies(
|
||||||
|
ethUtil.addHexPrefix(balance),
|
||||||
|
ethUtil.addHexPrefix(gasTotal),
|
||||||
|
{ toNumericBase: 'hex' }
|
||||||
|
)
|
||||||
|
updateSendAmount(maxAmount)
|
||||||
|
}
|
||||||
|
|
||||||
updateGasPrice(gasPrice)
|
updateGasPrice(gasPrice)
|
||||||
updateGasLimit(gasLimit)
|
updateGasLimit(gasLimit)
|
||||||
updateGasTotal(gasTotal)
|
updateGasTotal(gasTotal)
|
||||||
@ -112,12 +131,13 @@ CustomizeGasModal.prototype.validate = function ({ gasTotal, gasLimit }) {
|
|||||||
selectedToken,
|
selectedToken,
|
||||||
amountConversionRate,
|
amountConversionRate,
|
||||||
conversionRate,
|
conversionRate,
|
||||||
|
maxModeOn,
|
||||||
} = this.props
|
} = this.props
|
||||||
|
|
||||||
let error = null
|
let error = null
|
||||||
|
|
||||||
const balanceIsSufficient = isBalanceSufficient({
|
const balanceIsSufficient = isBalanceSufficient({
|
||||||
amount: selectedToken ? '0' : amount,
|
amount: selectedToken || maxModeOn ? '0' : amount,
|
||||||
gasTotal,
|
gasTotal,
|
||||||
balance,
|
balance,
|
||||||
selectedToken,
|
selectedToken,
|
||||||
|
@ -78,5 +78,6 @@ function mapDispatchToProps (dispatch) {
|
|||||||
goHome: () => dispatch(actions.goHome()),
|
goHome: () => dispatch(actions.goHome()),
|
||||||
clearSend: () => dispatch(actions.clearSend()),
|
clearSend: () => dispatch(actions.clearSend()),
|
||||||
backToConfirmScreen: editingTransactionId => dispatch(actions.showConfTxPage({ id: editingTransactionId })),
|
backToConfirmScreen: editingTransactionId => dispatch(actions.showConfTxPage({ id: editingTransactionId })),
|
||||||
|
setMaxModeTo: bool => dispatch(actions.setMaxModeTo(bool)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,8 @@ const actions = require('../actions')
|
|||||||
const Tooltip = require('../components/tooltip')
|
const Tooltip = require('../components/tooltip')
|
||||||
const getCaretCoordinates = require('textarea-caret')
|
const getCaretCoordinates = require('textarea-caret')
|
||||||
|
|
||||||
|
let isSubmitting = false
|
||||||
|
|
||||||
module.exports = connect(mapStateToProps)(InitializeMenuScreen)
|
module.exports = connect(mapStateToProps)(InitializeMenuScreen)
|
||||||
|
|
||||||
inherits(InitializeMenuScreen, Component)
|
inherits(InitializeMenuScreen, Component)
|
||||||
@ -164,7 +166,10 @@ InitializeMenuScreen.prototype.createNewVaultAndKeychain = function () {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.props.dispatch(actions.createNewVaultAndKeychain(password))
|
if (!isSubmitting) {
|
||||||
|
isSubmitting = true
|
||||||
|
this.props.dispatch(actions.createNewVaultAndKeychain(password))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
InitializeMenuScreen.prototype.inputChanged = function (event) {
|
InitializeMenuScreen.prototype.inputChanged = function (event) {
|
||||||
|
@ -33,6 +33,7 @@ function reduceMetamask (state, action) {
|
|||||||
amount: '0x0',
|
amount: '0x0',
|
||||||
memo: '',
|
memo: '',
|
||||||
errors: {},
|
errors: {},
|
||||||
|
maxModeOn: false,
|
||||||
editingTransactionId: null,
|
editingTransactionId: null,
|
||||||
},
|
},
|
||||||
coinOptions: {},
|
coinOptions: {},
|
||||||
@ -259,6 +260,14 @@ function reduceMetamask (state, action) {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
case actions.UPDATE_MAX_MODE:
|
||||||
|
return extend(metamaskState, {
|
||||||
|
send: {
|
||||||
|
...metamaskState.send,
|
||||||
|
maxModeOn: action.value,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
case actions.UPDATE_SEND:
|
case actions.UPDATE_SEND:
|
||||||
return extend(metamaskState, {
|
return extend(metamaskState, {
|
||||||
send: {
|
send: {
|
||||||
|
@ -25,6 +25,7 @@ const selectors = {
|
|||||||
getSelectedTokenToFiatRate,
|
getSelectedTokenToFiatRate,
|
||||||
getSelectedTokenContract,
|
getSelectedTokenContract,
|
||||||
autoAddToBetaUI,
|
autoAddToBetaUI,
|
||||||
|
getSendMaxModeState,
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = selectors
|
module.exports = selectors
|
||||||
@ -136,6 +137,10 @@ function getSendAmount (state) {
|
|||||||
return state.metamask.send.amount
|
return state.metamask.send.amount
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getSendMaxModeState (state) {
|
||||||
|
return state.metamask.send.maxModeOn
|
||||||
|
}
|
||||||
|
|
||||||
function getCurrentCurrency (state) {
|
function getCurrentCurrency (state) {
|
||||||
return state.metamask.currentCurrency
|
return state.metamask.currentCurrency
|
||||||
}
|
}
|
||||||
|
@ -13,8 +13,6 @@ const GasFeeDisplay = require('./components/send/gas-fee-display-v2')
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
MIN_GAS_TOTAL,
|
MIN_GAS_TOTAL,
|
||||||
MIN_GAS_PRICE_HEX,
|
|
||||||
MIN_GAS_LIMIT_HEX,
|
|
||||||
} = require('./components/send/send-constants')
|
} = require('./components/send/send-constants')
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@ -313,8 +311,9 @@ SendTransactionScreen.prototype.renderToRow = function () {
|
|||||||
|
|
||||||
SendTransactionScreen.prototype.handleAmountChange = function (value) {
|
SendTransactionScreen.prototype.handleAmountChange = function (value) {
|
||||||
const amount = value
|
const amount = value
|
||||||
const { updateSendAmount } = this.props
|
const { updateSendAmount, setMaxModeTo } = this.props
|
||||||
|
|
||||||
|
setMaxModeTo(false)
|
||||||
this.validateAmount(amount)
|
this.validateAmount(amount)
|
||||||
updateSendAmount(amount)
|
updateSendAmount(amount)
|
||||||
}
|
}
|
||||||
@ -324,11 +323,9 @@ SendTransactionScreen.prototype.setAmountToMax = function () {
|
|||||||
from: { balance },
|
from: { balance },
|
||||||
updateSendAmount,
|
updateSendAmount,
|
||||||
updateSendErrors,
|
updateSendErrors,
|
||||||
updateGasPrice,
|
|
||||||
updateGasLimit,
|
|
||||||
updateGasTotal,
|
|
||||||
tokenBalance,
|
tokenBalance,
|
||||||
selectedToken,
|
selectedToken,
|
||||||
|
gasTotal,
|
||||||
} = this.props
|
} = this.props
|
||||||
const { decimals } = selectedToken || {}
|
const { decimals } = selectedToken || {}
|
||||||
const multiplier = Math.pow(10, Number(decimals || 0))
|
const multiplier = Math.pow(10, Number(decimals || 0))
|
||||||
@ -337,16 +334,12 @@ SendTransactionScreen.prototype.setAmountToMax = function () {
|
|||||||
? multiplyCurrencies(tokenBalance, multiplier, {toNumericBase: 'hex'})
|
? multiplyCurrencies(tokenBalance, multiplier, {toNumericBase: 'hex'})
|
||||||
: subtractCurrencies(
|
: subtractCurrencies(
|
||||||
ethUtil.addHexPrefix(balance),
|
ethUtil.addHexPrefix(balance),
|
||||||
ethUtil.addHexPrefix(MIN_GAS_TOTAL),
|
ethUtil.addHexPrefix(gasTotal),
|
||||||
{ toNumericBase: 'hex' }
|
{ toNumericBase: 'hex' }
|
||||||
)
|
)
|
||||||
|
|
||||||
updateSendErrors({ amount: null })
|
updateSendErrors({ amount: null })
|
||||||
if (!selectedToken) {
|
|
||||||
updateGasPrice(MIN_GAS_PRICE_HEX)
|
|
||||||
updateGasLimit(MIN_GAS_LIMIT_HEX)
|
|
||||||
updateGasTotal(MIN_GAS_TOTAL)
|
|
||||||
}
|
|
||||||
updateSendAmount(maxAmount)
|
updateSendAmount(maxAmount)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -407,19 +400,22 @@ SendTransactionScreen.prototype.renderAmountRow = function () {
|
|||||||
amountConversionRate,
|
amountConversionRate,
|
||||||
errors,
|
errors,
|
||||||
amount,
|
amount,
|
||||||
|
setMaxModeTo,
|
||||||
|
maxModeOn,
|
||||||
} = this.props
|
} = this.props
|
||||||
|
|
||||||
return h('div.send-v2__form-row', [
|
return h('div.send-v2__form-row', [
|
||||||
|
|
||||||
h('div.send-v2__form-label', [
|
h('div.send-v2__form-label', [
|
||||||
'Amount:',
|
'Amount:',
|
||||||
this.renderErrorMessage('amount'),
|
this.renderErrorMessage('amount'),
|
||||||
!errors.amount && h('div.send-v2__amount-max', {
|
!errors.amount && h('div.send-v2__amount-max', {
|
||||||
onClick: (event) => {
|
onClick: (event) => {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
|
setMaxModeTo(true)
|
||||||
this.setAmountToMax()
|
this.setAmountToMax()
|
||||||
},
|
},
|
||||||
}, [ 'Max' ]),
|
}, [ !maxModeOn ? 'Max' : '' ]),
|
||||||
]),
|
]),
|
||||||
|
|
||||||
h('div.send-v2__form-field', [
|
h('div.send-v2__form-field', [
|
||||||
|
Loading…
Reference in New Issue
Block a user