mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Merge branch 'develop' into testing
This commit is contained in:
commit
770379c3da
@ -13,20 +13,6 @@ const RINKEBY_DISPLAY_NAME = 'Rinkeby'
|
|||||||
const KOVAN_DISPLAY_NAME = 'Kovan'
|
const KOVAN_DISPLAY_NAME = 'Kovan'
|
||||||
const MAINNET_DISPLAY_NAME = 'Main Ethereum Network'
|
const MAINNET_DISPLAY_NAME = 'Main Ethereum Network'
|
||||||
|
|
||||||
const MAINNET_RPC_URL = 'https://mainnet.infura.io/metamask'
|
|
||||||
const ROPSTEN_RPC_URL = 'https://ropsten.infura.io/metamask'
|
|
||||||
const KOVAN_RPC_URL = 'https://kovan.infura.io/metamask'
|
|
||||||
const RINKEBY_RPC_URL = 'https://rinkeby.infura.io/metamask'
|
|
||||||
const LOCALHOST_RPC_URL = 'http://localhost:8545'
|
|
||||||
|
|
||||||
const MAINNET_RPC_URL_BETA = 'https://mainnet.infura.io/metamask2'
|
|
||||||
const ROPSTEN_RPC_URL_BETA = 'https://ropsten.infura.io/metamask2'
|
|
||||||
const KOVAN_RPC_URL_BETA = 'https://kovan.infura.io/metamask2'
|
|
||||||
const RINKEBY_RPC_URL_BETA = 'https://rinkeby.infura.io/metamask2'
|
|
||||||
|
|
||||||
const DEFAULT_NETWORK = 'rinkeby'
|
|
||||||
const OLD_UI_NETWORK_TYPE = 'network'
|
|
||||||
const BETA_UI_NETWORK_TYPE = 'networkBeta'
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
ROPSTEN,
|
ROPSTEN,
|
||||||
@ -41,16 +27,4 @@ module.exports = {
|
|||||||
RINKEBY_DISPLAY_NAME,
|
RINKEBY_DISPLAY_NAME,
|
||||||
KOVAN_DISPLAY_NAME,
|
KOVAN_DISPLAY_NAME,
|
||||||
MAINNET_DISPLAY_NAME,
|
MAINNET_DISPLAY_NAME,
|
||||||
MAINNET_RPC_URL,
|
|
||||||
ROPSTEN_RPC_URL,
|
|
||||||
KOVAN_RPC_URL,
|
|
||||||
RINKEBY_RPC_URL,
|
|
||||||
LOCALHOST_RPC_URL,
|
|
||||||
MAINNET_RPC_URL_BETA,
|
|
||||||
ROPSTEN_RPC_URL_BETA,
|
|
||||||
KOVAN_RPC_URL_BETA,
|
|
||||||
RINKEBY_RPC_URL_BETA,
|
|
||||||
DEFAULT_NETWORK,
|
|
||||||
OLD_UI_NETWORK_TYPE,
|
|
||||||
BETA_UI_NETWORK_TYPE,
|
|
||||||
}
|
}
|
||||||
|
@ -14,52 +14,40 @@ const {
|
|||||||
RINKEBY,
|
RINKEBY,
|
||||||
KOVAN,
|
KOVAN,
|
||||||
MAINNET,
|
MAINNET,
|
||||||
OLD_UI_NETWORK_TYPE,
|
LOCALHOST,
|
||||||
DEFAULT_NETWORK,
|
|
||||||
} = require('./enums')
|
} = require('./enums')
|
||||||
const { getNetworkEndpoints } = require('./util')
|
const LOCALHOST_RPC_URL = 'http://localhost:8545'
|
||||||
const INFURA_PROVIDER_TYPES = [ROPSTEN, RINKEBY, KOVAN, MAINNET]
|
const INFURA_PROVIDER_TYPES = [ROPSTEN, RINKEBY, KOVAN, MAINNET]
|
||||||
|
|
||||||
|
const env = process.env.METAMASK_ENV
|
||||||
|
const METAMASK_DEBUG = process.env.METAMASK_DEBUG
|
||||||
|
const testMode = (METAMASK_DEBUG || env === 'test')
|
||||||
|
|
||||||
|
const defaultProviderConfig = {
|
||||||
|
type: testMode ? RINKEBY : MAINNET,
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = class NetworkController extends EventEmitter {
|
module.exports = class NetworkController extends EventEmitter {
|
||||||
|
|
||||||
constructor (config) {
|
constructor (opts = {}) {
|
||||||
super()
|
super()
|
||||||
|
|
||||||
this._networkEndpointVersion = OLD_UI_NETWORK_TYPE
|
// parse options
|
||||||
this._networkEndpoints = getNetworkEndpoints(OLD_UI_NETWORK_TYPE)
|
const providerConfig = opts.provider || defaultProviderConfig
|
||||||
this._defaultRpc = this._networkEndpoints[DEFAULT_NETWORK]
|
// create stores
|
||||||
|
this.providerStore = new ObservableStore(providerConfig)
|
||||||
config.provider.rpcTarget = this.getRpcAddressForType(config.provider.type, config.provider)
|
|
||||||
this.networkStore = new ObservableStore('loading')
|
this.networkStore = new ObservableStore('loading')
|
||||||
this.providerStore = new ObservableStore(config.provider)
|
|
||||||
this.store = new ComposedStore({ provider: this.providerStore, network: this.networkStore })
|
this.store = new ComposedStore({ provider: this.providerStore, network: this.networkStore })
|
||||||
|
// create event emitter proxy
|
||||||
this._proxy = createEventEmitterProxy()
|
this._proxy = createEventEmitterProxy()
|
||||||
|
|
||||||
this.on('networkDidChange', this.lookupNetwork)
|
this.on('networkDidChange', this.lookupNetwork)
|
||||||
}
|
}
|
||||||
|
|
||||||
async setNetworkEndpoints (version) {
|
|
||||||
if (version === this._networkEndpointVersion) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this._networkEndpointVersion = version
|
|
||||||
this._networkEndpoints = getNetworkEndpoints(version)
|
|
||||||
this._defaultRpc = this._networkEndpoints[DEFAULT_NETWORK]
|
|
||||||
const { type } = this.getProviderConfig()
|
|
||||||
|
|
||||||
return this.setProviderType(type, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
initializeProvider (_providerParams) {
|
initializeProvider (_providerParams) {
|
||||||
this._baseProviderParams = _providerParams
|
this._baseProviderParams = _providerParams
|
||||||
const { type, rpcTarget } = this.providerStore.getState()
|
const { type, rpcTarget } = this.providerStore.getState()
|
||||||
// map rpcTarget to rpcUrl
|
this._configureProvider({ type, rpcTarget })
|
||||||
const opts = {
|
|
||||||
type,
|
|
||||||
rpcUrl: rpcTarget,
|
|
||||||
}
|
|
||||||
this._configureProvider(opts)
|
|
||||||
this._proxy.on('block', this._logBlock.bind(this))
|
this._proxy.on('block', this._logBlock.bind(this))
|
||||||
this._proxy.on('error', this.verifyNetwork.bind(this))
|
this._proxy.on('error', this.verifyNetwork.bind(this))
|
||||||
this.ethQuery = new EthQuery(this._proxy)
|
this.ethQuery = new EthQuery(this._proxy)
|
||||||
@ -96,45 +84,27 @@ module.exports = class NetworkController extends EventEmitter {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
setRpcTarget (rpcUrl) {
|
setRpcTarget (rpcTarget) {
|
||||||
this.providerStore.updateState({
|
const providerConfig = {
|
||||||
type: 'rpc',
|
type: 'rpc',
|
||||||
rpcTarget: rpcUrl,
|
rpcTarget,
|
||||||
})
|
|
||||||
this._switchNetwork({ rpcUrl })
|
|
||||||
}
|
|
||||||
|
|
||||||
getCurrentRpcAddress () {
|
|
||||||
const provider = this.getProviderConfig()
|
|
||||||
if (!provider) return null
|
|
||||||
return this.getRpcAddressForType(provider.type)
|
|
||||||
}
|
|
||||||
|
|
||||||
async setProviderType (type, forceUpdate = false) {
|
|
||||||
assert(type !== 'rpc', `NetworkController.setProviderType - cannot connect by type "rpc"`)
|
|
||||||
// skip if type already matches
|
|
||||||
if (type === this.getProviderConfig().type && !forceUpdate) {
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
this.providerStore.updateState(providerConfig)
|
||||||
|
this._switchNetwork(providerConfig)
|
||||||
|
}
|
||||||
|
|
||||||
const rpcTarget = this.getRpcAddressForType(type)
|
async setProviderType (type) {
|
||||||
assert(rpcTarget, `NetworkController - unknown rpc address for type "${type}"`)
|
assert.notEqual(type, 'rpc', `NetworkController - cannot call "setProviderType" with type 'rpc'. use "setRpcTarget"`)
|
||||||
this.providerStore.updateState({ type, rpcTarget })
|
assert(INFURA_PROVIDER_TYPES.includes(type) || type === LOCALHOST, `NetworkController - Unknown rpc type "${type}"`)
|
||||||
this._switchNetwork({ type })
|
const providerConfig = { type }
|
||||||
|
this.providerStore.updateState(providerConfig)
|
||||||
|
this._switchNetwork(providerConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
getProviderConfig () {
|
getProviderConfig () {
|
||||||
return this.providerStore.getState()
|
return this.providerStore.getState()
|
||||||
}
|
}
|
||||||
|
|
||||||
getRpcAddressForType (type, provider = this.getProviderConfig()) {
|
|
||||||
if (this._networkEndpoints[type]) {
|
|
||||||
return this._networkEndpoints[type]
|
|
||||||
}
|
|
||||||
|
|
||||||
return provider && provider.rpcTarget ? provider.rpcTarget : this._defaultRpc
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Private
|
// Private
|
||||||
//
|
//
|
||||||
@ -146,32 +116,27 @@ module.exports = class NetworkController extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_configureProvider (opts) {
|
_configureProvider (opts) {
|
||||||
// type-based rpc endpoints
|
const { type, rpcTarget } = opts
|
||||||
const { type } = opts
|
// infura type-based endpoints
|
||||||
if (type) {
|
const isInfura = INFURA_PROVIDER_TYPES.includes(type)
|
||||||
// type-based infura rpc endpoints
|
if (isInfura) {
|
||||||
const isInfura = INFURA_PROVIDER_TYPES.includes(type)
|
this._configureInfuraProvider(opts)
|
||||||
opts.rpcUrl = this.getRpcAddressForType(type)
|
// other type-based rpc endpoints
|
||||||
if (isInfura) {
|
} else if (type === LOCALHOST) {
|
||||||
this._configureInfuraProvider(opts)
|
this._configureStandardProvider({ rpcUrl: LOCALHOST_RPC_URL })
|
||||||
// other type-based rpc endpoints
|
|
||||||
} else {
|
|
||||||
this._configureStandardProvider(opts)
|
|
||||||
}
|
|
||||||
// url-based rpc endpoints
|
// url-based rpc endpoints
|
||||||
|
} else if (type === 'rpc'){
|
||||||
|
this._configureStandardProvider({ rpcUrl: rpcTarget })
|
||||||
} else {
|
} else {
|
||||||
this._configureStandardProvider(opts)
|
throw new Error(`NetworkController - _configureProvider - unknown type "${type}"`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_configureInfuraProvider (opts) {
|
_configureInfuraProvider ({ type }) {
|
||||||
log.info('_configureInfuraProvider', opts)
|
log.info('_configureInfuraProvider', type)
|
||||||
const infuraProvider = createInfuraProvider({
|
const infuraProvider = createInfuraProvider({ network: type })
|
||||||
network: opts.type,
|
|
||||||
})
|
|
||||||
const infuraSubprovider = new SubproviderFromProvider(infuraProvider)
|
const infuraSubprovider = new SubproviderFromProvider(infuraProvider)
|
||||||
const providerParams = extend(this._baseProviderParams, {
|
const providerParams = extend(this._baseProviderParams, {
|
||||||
rpcUrl: opts.rpcUrl,
|
|
||||||
engineParams: {
|
engineParams: {
|
||||||
pollingInterval: 8000,
|
pollingInterval: 8000,
|
||||||
blockTrackerProvider: infuraProvider,
|
blockTrackerProvider: infuraProvider,
|
||||||
|
@ -3,7 +3,6 @@ const {
|
|||||||
RINKEBY,
|
RINKEBY,
|
||||||
KOVAN,
|
KOVAN,
|
||||||
MAINNET,
|
MAINNET,
|
||||||
LOCALHOST,
|
|
||||||
ROPSTEN_CODE,
|
ROPSTEN_CODE,
|
||||||
RINKEYBY_CODE,
|
RINKEYBY_CODE,
|
||||||
KOVAN_CODE,
|
KOVAN_CODE,
|
||||||
@ -11,17 +10,6 @@ const {
|
|||||||
RINKEBY_DISPLAY_NAME,
|
RINKEBY_DISPLAY_NAME,
|
||||||
KOVAN_DISPLAY_NAME,
|
KOVAN_DISPLAY_NAME,
|
||||||
MAINNET_DISPLAY_NAME,
|
MAINNET_DISPLAY_NAME,
|
||||||
MAINNET_RPC_URL,
|
|
||||||
ROPSTEN_RPC_URL,
|
|
||||||
KOVAN_RPC_URL,
|
|
||||||
RINKEBY_RPC_URL,
|
|
||||||
LOCALHOST_RPC_URL,
|
|
||||||
MAINNET_RPC_URL_BETA,
|
|
||||||
ROPSTEN_RPC_URL_BETA,
|
|
||||||
KOVAN_RPC_URL_BETA,
|
|
||||||
RINKEBY_RPC_URL_BETA,
|
|
||||||
OLD_UI_NETWORK_TYPE,
|
|
||||||
BETA_UI_NETWORK_TYPE,
|
|
||||||
} = require('./enums')
|
} = require('./enums')
|
||||||
|
|
||||||
const networkToNameMap = {
|
const networkToNameMap = {
|
||||||
@ -34,32 +22,8 @@ const networkToNameMap = {
|
|||||||
[KOVAN_CODE]: KOVAN_DISPLAY_NAME,
|
[KOVAN_CODE]: KOVAN_DISPLAY_NAME,
|
||||||
}
|
}
|
||||||
|
|
||||||
const networkEndpointsMap = {
|
|
||||||
[OLD_UI_NETWORK_TYPE]: {
|
|
||||||
[LOCALHOST]: LOCALHOST_RPC_URL,
|
|
||||||
[MAINNET]: MAINNET_RPC_URL,
|
|
||||||
[ROPSTEN]: ROPSTEN_RPC_URL,
|
|
||||||
[KOVAN]: KOVAN_RPC_URL,
|
|
||||||
[RINKEBY]: RINKEBY_RPC_URL,
|
|
||||||
},
|
|
||||||
[BETA_UI_NETWORK_TYPE]: {
|
|
||||||
[LOCALHOST]: LOCALHOST_RPC_URL,
|
|
||||||
[MAINNET]: MAINNET_RPC_URL_BETA,
|
|
||||||
[ROPSTEN]: ROPSTEN_RPC_URL_BETA,
|
|
||||||
[KOVAN]: KOVAN_RPC_URL_BETA,
|
|
||||||
[RINKEBY]: RINKEBY_RPC_URL_BETA,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
const getNetworkDisplayName = key => networkToNameMap[key]
|
const getNetworkDisplayName = key => networkToNameMap[key]
|
||||||
|
|
||||||
const getNetworkEndpoints = (networkType = OLD_UI_NETWORK_TYPE) => {
|
|
||||||
return {
|
|
||||||
...networkEndpointsMap[networkType],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
getNetworkDisplayName,
|
getNetworkDisplayName,
|
||||||
getNetworkEndpoints,
|
|
||||||
}
|
}
|
||||||
|
@ -25,26 +25,31 @@ function migrateFromSnapshotsToDiffs (longHistory) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
generates an array of history objects sense the previous state.
|
Generates an array of history objects sense the previous state.
|
||||||
The object has the keys opp(the operation preformed),
|
The object has the keys
|
||||||
path(the key and if a nested object then each key will be seperated with a `/`)
|
op (the operation performed),
|
||||||
value
|
path (the key and if a nested object then each key will be seperated with a `/`)
|
||||||
with the first entry having the note
|
value
|
||||||
|
with the first entry having the note and a timestamp when the change took place
|
||||||
@param previousState {object} - the previous state of the object
|
@param previousState {object} - the previous state of the object
|
||||||
@param newState {object} - the update object
|
@param newState {object} - the update object
|
||||||
@param note {string} - a optional note for the state change
|
@param note {string} - a optional note for the state change
|
||||||
@reurns {array}
|
@returns {array}
|
||||||
*/
|
*/
|
||||||
function generateHistoryEntry (previousState, newState, note) {
|
function generateHistoryEntry (previousState, newState, note) {
|
||||||
const entry = jsonDiffer.compare(previousState, newState)
|
const entry = jsonDiffer.compare(previousState, newState)
|
||||||
// Add a note to the first op, since it breaks if we append it to the entry
|
// Add a note to the first op, since it breaks if we append it to the entry
|
||||||
if (note && entry[0]) entry[0].note = note
|
if (entry[0]) {
|
||||||
|
if (note) entry[0].note = note
|
||||||
|
|
||||||
|
entry[0].timestamp = Date.now()
|
||||||
|
}
|
||||||
return entry
|
return entry
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Recovers previous txMeta state obj
|
Recovers previous txMeta state obj
|
||||||
@return {object}
|
@returns {object}
|
||||||
*/
|
*/
|
||||||
function replayHistory (_shortHistory) {
|
function replayHistory (_shortHistory) {
|
||||||
const shortHistory = clone(_shortHistory)
|
const shortHistory = clone(_shortHistory)
|
||||||
|
@ -158,7 +158,7 @@ class TransactionStateManager extends EventEmitter {
|
|||||||
/**
|
/**
|
||||||
updates the txMeta in the list and adds a history entry
|
updates the txMeta in the list and adds a history entry
|
||||||
@param txMeta {Object} - the txMeta to update
|
@param txMeta {Object} - the txMeta to update
|
||||||
@param [note] {string} - a not about the update for history
|
@param [note] {string} - a note about the update for history
|
||||||
*/
|
*/
|
||||||
updateTx (txMeta, note) {
|
updateTx (txMeta, note) {
|
||||||
// validate txParams
|
// validate txParams
|
||||||
|
@ -1,7 +1,3 @@
|
|||||||
// test and development environment variables
|
|
||||||
const env = process.env.METAMASK_ENV
|
|
||||||
const METAMASK_DEBUG = process.env.METAMASK_DEBUG
|
|
||||||
const { DEFAULT_NETWORK, MAINNET } = require('./controllers/network/enums')
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} FirstTimeState
|
* @typedef {Object} FirstTimeState
|
||||||
@ -14,11 +10,6 @@ const { DEFAULT_NETWORK, MAINNET } = require('./controllers/network/enums')
|
|||||||
*/
|
*/
|
||||||
const initialState = {
|
const initialState = {
|
||||||
config: {},
|
config: {},
|
||||||
NetworkController: {
|
|
||||||
provider: {
|
|
||||||
type: (METAMASK_DEBUG || env === 'test') ? DEFAULT_NETWORK : MAINNET,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = initialState
|
module.exports = initialState
|
||||||
|
@ -25,7 +25,7 @@ function setupRaven(opts) {
|
|||||||
const report = opts.data
|
const report = opts.data
|
||||||
try {
|
try {
|
||||||
// handle error-like non-error exceptions
|
// handle error-like non-error exceptions
|
||||||
nonErrorException(report)
|
rewriteErrorLikeExceptions(report)
|
||||||
// simplify certain complex error messages (e.g. Ethjs)
|
// simplify certain complex error messages (e.g. Ethjs)
|
||||||
simplifyErrorMessages(report)
|
simplifyErrorMessages(report)
|
||||||
// modify report urls
|
// modify report urls
|
||||||
@ -42,27 +42,35 @@ function setupRaven(opts) {
|
|||||||
return Raven
|
return Raven
|
||||||
}
|
}
|
||||||
|
|
||||||
function nonErrorException(report) {
|
function rewriteErrorLikeExceptions(report) {
|
||||||
// handle errors that lost their error-ness in serialization
|
// handle errors that lost their error-ness in serialization (e.g. dnode)
|
||||||
if (report.message.includes('Non-Error exception captured with keys: message')) {
|
rewriteErrorMessages(report, (errorMessage) => {
|
||||||
if (!(report.extra && report.extra.__serialized__)) return
|
if (!errorMessage.includes('Non-Error exception captured with keys:')) return errorMessage
|
||||||
report.message = `Non-Error Exception: ${report.extra.__serialized__.message}`
|
if (!(report.extra && report.extra.__serialized__ && report.extra.__serialized__.message)) return errorMessage
|
||||||
}
|
return `Non-Error Exception: ${report.extra.__serialized__.message}`
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function simplifyErrorMessages(report) {
|
function simplifyErrorMessages(report) {
|
||||||
|
rewriteErrorMessages(report, (errorMessage) => {
|
||||||
|
// simplify ethjs error messages
|
||||||
|
errorMessage = extractEthjsErrorMessage(errorMessage)
|
||||||
|
// simplify 'Transaction Failed: known transaction'
|
||||||
|
if (errorMessage.indexOf('Transaction Failed: known transaction') === 0) {
|
||||||
|
// cut the hash from the error message
|
||||||
|
errorMessage = 'Transaction Failed: known transaction'
|
||||||
|
}
|
||||||
|
return errorMessage
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function rewriteErrorMessages(report, rewriteFn) {
|
||||||
|
// rewrite top level message
|
||||||
|
report.message = rewriteFn(report.message)
|
||||||
|
// rewrite each exception message
|
||||||
if (report.exception && report.exception.values) {
|
if (report.exception && report.exception.values) {
|
||||||
report.exception.values.forEach(item => {
|
report.exception.values.forEach(item => {
|
||||||
let errorMessage = item.value
|
item.value = rewriteFn(item.value)
|
||||||
// simplify ethjs error messages
|
|
||||||
errorMessage = extractEthjsErrorMessage(errorMessage)
|
|
||||||
// simplify 'Transaction Failed: known transaction'
|
|
||||||
if (errorMessage.indexOf('Transaction Failed: known transaction') === 0) {
|
|
||||||
// cut the hash from the error message
|
|
||||||
errorMessage = 'Transaction Failed: known transaction'
|
|
||||||
}
|
|
||||||
// finalize
|
|
||||||
item.value = errorMessage
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -355,7 +355,6 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||||||
submitPassword: nodeify(keyringController.submitPassword, keyringController),
|
submitPassword: nodeify(keyringController.submitPassword, keyringController),
|
||||||
|
|
||||||
// network management
|
// network management
|
||||||
setNetworkEndpoints: nodeify(networkController.setNetworkEndpoints, networkController),
|
|
||||||
setProviderType: nodeify(networkController.setProviderType, networkController),
|
setProviderType: nodeify(networkController.setProviderType, networkController),
|
||||||
setCustomRpc: nodeify(this.setCustomRpc, this),
|
setCustomRpc: nodeify(this.setCustomRpc, this),
|
||||||
|
|
||||||
|
96
docs/send-screen-QA-checklist.md
Normal file
96
docs/send-screen-QA-checklist.md
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
# Send screen QA checklist:
|
||||||
|
|
||||||
|
This checklist can be to guide QA of the send screen. It can also be used to guide e2e tests for the send screen.
|
||||||
|
|
||||||
|
Once all of these are QA verified on master, resolutions to any bugs related to the send screen should include and update to this list.
|
||||||
|
|
||||||
|
Additional features or functionality on the send screen should include an update to this list.
|
||||||
|
|
||||||
|
## Send Eth mode
|
||||||
|
- [ ] **Header** _It should:_
|
||||||
|
- [ ] have title "Send ETH"
|
||||||
|
- [ ] have sub title "Only send ETH to an Ethereum address."
|
||||||
|
- [ ] return user to main screen when top right X is clicked
|
||||||
|
- [ ] **From row** _It should:_
|
||||||
|
- [ ] show the currently selected account by default
|
||||||
|
- [ ] show a dropdown with all of the users accounts
|
||||||
|
- [ ] contain the following info for each account: identicon, account name, balance in ETH, balance in current currency
|
||||||
|
- [ ] change the account selected in the dropdown (but not the app-wide selected account) when one in the dropdown is clicked
|
||||||
|
- [ ] close the dropdown, without changing the dropdown selected account, when the dropdown is open and then a click happens outside it
|
||||||
|
- [ ] **To row** _It should:_
|
||||||
|
- [ ] Show a placeholder with the text 'Recipient Address' by default
|
||||||
|
- [ ] Show, when clicked, a dropdown list of all 'to accounts': the users accounts, plus any other accounts they have previously sent to
|
||||||
|
- [ ] Show account address, and account name if it exists, of each item in the dropdown list
|
||||||
|
- [ ] Show a dropdown list of all to accounts (see above) whose address matches an address currently being typed in
|
||||||
|
- [ ] Set the input text to the address of an account clicked in the dropdown list, and also hide the dropdown
|
||||||
|
- [ ] Hide the dropdown without changing what is in the input if the user clicks outside the dropdown list while it is open
|
||||||
|
- [ ] Select the text in the input (i.e. the address) if an address is displayed and then clicked
|
||||||
|
- [ ] Show a 'required' error if the dropdown is opened but no account is selected
|
||||||
|
- [ ] Show an 'invalid address' error if text is entered in the input that cannot be a valid hex address or ens address
|
||||||
|
- [ ] Support ens names. (enter dinodan.eth on mainnet) After entering the plain text address, the hex address should appear in the input with a green checkmark beside
|
||||||
|
- [ ] Should show a 'no such address' error if a non-existent ens address is entered
|
||||||
|
- [ ] **Amount row** _It should:_
|
||||||
|
- [ ] allow user to enter any rational number >= 0
|
||||||
|
- [ ] allow user to copy and paste into the field
|
||||||
|
- [ ] show an insufficient funds error if an amount > balance - gas fee
|
||||||
|
- [ ] display 'ETH' after the number amount. The position of 'ETH' should change as the length of the input amount text changes
|
||||||
|
- [ ] display the value of the amount of ETH in the current currency, formatted in that currency
|
||||||
|
- [ ] show a 'max' but if amount < balance - gas fee
|
||||||
|
- [ ] show no max button or error if amount === balance - gas fee
|
||||||
|
- [ ] set the amount to balance - gas fee if the 'max' button is clicked
|
||||||
|
- [ ] **Gas Fee Display row** _It should:_
|
||||||
|
- [ ] Default to the fee given by the estimated gas price
|
||||||
|
- [ ] display the fee in ETH and the current currency
|
||||||
|
- [ ] update when changes are made using the customize gas modal
|
||||||
|
- [ ] **Cancel button** _It should:_
|
||||||
|
- [ ] Take the user back to the main screen
|
||||||
|
- [ ] **submit button** _It should:_
|
||||||
|
- [ ] be disabled if no recipient address is provided or if any field is in error
|
||||||
|
- [ ] sign a transaction with the info in the above form, and display the details of that transaction on the confirm screen
|
||||||
|
|
||||||
|
## Send token mode
|
||||||
|
- [ ] **Header** _It should:_
|
||||||
|
- [ ] have title "Send Tokens"
|
||||||
|
- [ ] have sub title "Only send [token symbol] to an Ethereum address."
|
||||||
|
- [ ] return user to main screen when top right X is clicked
|
||||||
|
- [ ] **From row** _It should:_
|
||||||
|
- [ ] Behave the same as 'Send ETH mode' (see above)
|
||||||
|
- [ ] **To row** _It should:_
|
||||||
|
- [ ] Behave the same as 'Send ETH mode' (see above)
|
||||||
|
- [ ] **Amount row** _It should:_
|
||||||
|
- [ ] allow user to enter any rational number >= 0
|
||||||
|
- [ ] allow user to copy and paste into the field
|
||||||
|
- [ ] show an 'insufficient tokens' error if an amount > token balance
|
||||||
|
- [ ] show an 'insufficient funds' error if an gas fee > eth balance
|
||||||
|
- [ ] display [token symbol] after the number amount. The position of [token symbol] should change as the length of the input amount text changes
|
||||||
|
- [ ] display the value of the amount of tokens in the current currency, formatted in that currency
|
||||||
|
- [ ] show a 'max' but if amount < token balance
|
||||||
|
- [ ] show no max button or error if amount === token balance
|
||||||
|
- [ ] set the amount to token balance if the 'max' button is clicked
|
||||||
|
- [ ] **Gas Fee Display row** _It should:_
|
||||||
|
- [ ] Behave the same as 'Send ETH mode' (see above)
|
||||||
|
- [ ] **Cancel button** _It should:_
|
||||||
|
- [ ] Take the user back to the main screen
|
||||||
|
- [ ] **submit button** _It should:_
|
||||||
|
- [ ] be disabled if no recipient address is provided or if any field is in error
|
||||||
|
- [ ] sign a token transaction with the info in the above form, and display the details of that transaction on the confirm screen
|
||||||
|
|
||||||
|
## Edit send Eth mode
|
||||||
|
- [ ] Say 'Editing transaction' in the header
|
||||||
|
- [ ] display a button to go back to the confirmation screen without applying update
|
||||||
|
- [ ] say 'update transaction' on the submit button
|
||||||
|
- [ ] update the existing transaction, instead of signing a new one, when clicking the submit button
|
||||||
|
- [ ] Otherwise, behave the same as 'Send ETH mode' (see above)
|
||||||
|
|
||||||
|
## Edit send token mode
|
||||||
|
- [ ] Behave the same as 'Edit send Eth mode' (see above)
|
||||||
|
|
||||||
|
## Specific cases to test
|
||||||
|
- [ ] Send eth to a hex address
|
||||||
|
- [ ] Send eth to an ENS address
|
||||||
|
- [ ] Donate to the faucet at https://faucet.metamask.io/ and edit the transaction before confirming
|
||||||
|
- [ ] Send a token that is available on the 'Add Token' screen search to a hex address
|
||||||
|
- [ ] Create a custom token at https://tokenfactory.surge.sh/ and send it to a hex address
|
||||||
|
- [ ] Send a token to an ENS address
|
||||||
|
- [ ] Create a token transaction using https://tokenfactory.surge.sh/#/, and edit the transaction before confirming
|
||||||
|
- [ ] Send each of MKR, EOS and ICON using myetherwallet, and edit the transaction before confirming
|
@ -35,7 +35,6 @@ const HDCreateVaultComplete = require('./keychains/hd/create-vault-complete')
|
|||||||
const HDRestoreVaultScreen = require('./keychains/hd/restore-vault')
|
const HDRestoreVaultScreen = require('./keychains/hd/restore-vault')
|
||||||
const RevealSeedConfirmation = require('./keychains/hd/recover-seed/confirmation')
|
const RevealSeedConfirmation = require('./keychains/hd/recover-seed/confirmation')
|
||||||
const AccountDropdowns = require('./components/account-dropdowns').AccountDropdowns
|
const AccountDropdowns = require('./components/account-dropdowns').AccountDropdowns
|
||||||
const { BETA_UI_NETWORK_TYPE } = require('../../app/scripts/controllers/network/enums')
|
|
||||||
|
|
||||||
module.exports = connect(mapStateToProps)(App)
|
module.exports = connect(mapStateToProps)(App)
|
||||||
|
|
||||||
@ -409,7 +408,6 @@ App.prototype.renderDropdown = function () {
|
|||||||
closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }),
|
closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }),
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
this.props.dispatch(actions.setFeatureFlag('betaUI', true, 'BETA_UI_NOTIFICATION_MODAL'))
|
this.props.dispatch(actions.setFeatureFlag('betaUI', true, 'BETA_UI_NOTIFICATION_MODAL'))
|
||||||
.then(() => this.props.dispatch(actions.setNetworkEndpoints(BETA_UI_NETWORK_TYPE)))
|
|
||||||
},
|
},
|
||||||
}, 'Try Beta!'),
|
}, 'Try Beta!'),
|
||||||
])
|
])
|
||||||
@ -472,7 +470,6 @@ App.prototype.renderPrimary = function () {
|
|||||||
onClick: () => {
|
onClick: () => {
|
||||||
global.platform.openExtensionInBrowser()
|
global.platform.openExtensionInBrowser()
|
||||||
props.dispatch(actions.setFeatureFlag('betaUI', true, 'BETA_UI_NOTIFICATION_MODAL'))
|
props.dispatch(actions.setFeatureFlag('betaUI', true, 'BETA_UI_NOTIFICATION_MODAL'))
|
||||||
.then(() => props.dispatch(actions.setNetworkEndpoints(BETA_UI_NETWORK_TYPE)))
|
|
||||||
},
|
},
|
||||||
style: {
|
style: {
|
||||||
fontSize: '0.8em',
|
fontSize: '0.8em',
|
||||||
|
@ -3,17 +3,15 @@ const nock = require('nock')
|
|||||||
const NetworkController = require('../../app/scripts/controllers/network')
|
const NetworkController = require('../../app/scripts/controllers/network')
|
||||||
const {
|
const {
|
||||||
getNetworkDisplayName,
|
getNetworkDisplayName,
|
||||||
getNetworkEndpoints,
|
|
||||||
} = require('../../app/scripts/controllers/network/util')
|
} = require('../../app/scripts/controllers/network/util')
|
||||||
|
|
||||||
const { createTestProviderTools } = require('../stub/provider')
|
const { createTestProviderTools } = require('../stub/provider')
|
||||||
const providerResultStub = {}
|
const providerResultStub = {}
|
||||||
const provider = createTestProviderTools({ scaffold: providerResultStub }).provider
|
|
||||||
|
|
||||||
describe('# Network Controller', function () {
|
describe('# Network Controller', function () {
|
||||||
let networkController
|
let networkController
|
||||||
const noop = () => {}
|
const noop = () => {}
|
||||||
const networkControllerProviderInit = {
|
const networkControllerProviderConfig = {
|
||||||
getAccounts: noop,
|
getAccounts: noop,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24,11 +22,9 @@ describe('# Network Controller', function () {
|
|||||||
.post('/metamask')
|
.post('/metamask')
|
||||||
.reply(200)
|
.reply(200)
|
||||||
|
|
||||||
networkController = new NetworkController({
|
networkController = new NetworkController()
|
||||||
provider,
|
|
||||||
})
|
|
||||||
|
|
||||||
networkController.initializeProvider(networkControllerProviderInit, provider)
|
networkController.initializeProvider(networkControllerProviderConfig)
|
||||||
})
|
})
|
||||||
|
|
||||||
afterEach(function () {
|
afterEach(function () {
|
||||||
@ -38,7 +34,7 @@ describe('# Network Controller', function () {
|
|||||||
describe('network', function () {
|
describe('network', function () {
|
||||||
describe('#provider', function () {
|
describe('#provider', function () {
|
||||||
it('provider should be updatable without reassignment', function () {
|
it('provider should be updatable without reassignment', function () {
|
||||||
networkController.initializeProvider(networkControllerProviderInit, provider)
|
networkController.initializeProvider(networkControllerProviderConfig)
|
||||||
const proxy = networkController._proxy
|
const proxy = networkController._proxy
|
||||||
proxy.setTarget({ test: true, on: () => {} })
|
proxy.setTarget({ test: true, on: () => {} })
|
||||||
assert.ok(proxy.test)
|
assert.ok(proxy.test)
|
||||||
@ -59,12 +55,6 @@ describe('# Network Controller', function () {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('#getRpcAddressForType', function () {
|
|
||||||
it('should return the right rpc address', function () {
|
|
||||||
const rpcTarget = networkController.getRpcAddressForType('mainnet')
|
|
||||||
assert.equal(rpcTarget, 'https://mainnet.infura.io/metamask', 'returns the right rpcAddress')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
describe('#setProviderType', function () {
|
describe('#setProviderType', function () {
|
||||||
it('should update provider.type', function () {
|
it('should update provider.type', function () {
|
||||||
networkController.setProviderType('mainnet')
|
networkController.setProviderType('mainnet')
|
||||||
@ -76,16 +66,11 @@ describe('# Network Controller', function () {
|
|||||||
const loading = networkController.isNetworkLoading()
|
const loading = networkController.isNetworkLoading()
|
||||||
assert.ok(loading, 'network is loading')
|
assert.ok(loading, 'network is loading')
|
||||||
})
|
})
|
||||||
it('should set the right rpcTarget', function () {
|
|
||||||
networkController.setProviderType('mainnet')
|
|
||||||
const rpcTarget = networkController.getProviderConfig().rpcTarget
|
|
||||||
assert.equal(rpcTarget, 'https://mainnet.infura.io/metamask', 'returns the right rpcAddress')
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('# Network utils', () => {
|
describe('Network utils', () => {
|
||||||
it('getNetworkDisplayName should return the correct network name', () => {
|
it('getNetworkDisplayName should return the correct network name', () => {
|
||||||
const tests = [
|
const tests = [
|
||||||
{
|
{
|
||||||
@ -114,9 +99,4 @@ describe('# Network utils', () => {
|
|||||||
|
|
||||||
tests.forEach(({ input, expected }) => assert.equal(getNetworkDisplayName(input), expected))
|
tests.forEach(({ input, expected }) => assert.equal(getNetworkDisplayName(input), expected))
|
||||||
})
|
})
|
||||||
|
|
||||||
it('getNetworkEndpoints should return the correct endpoints', () => {
|
|
||||||
assert.equal(getNetworkEndpoints('networkBeta').ropsten, 'https://ropsten.infura.io/metamask2')
|
|
||||||
assert.equal(getNetworkEndpoints('network').rinkeby, 'https://rinkeby.infura.io/metamask')
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
@ -1,26 +1,129 @@
|
|||||||
const assert = require('assert')
|
const assert = require('assert')
|
||||||
const clone = require('clone')
|
|
||||||
const txStateHistoryHelper = require('../../app/scripts/controllers/transactions/lib/tx-state-history-helper')
|
const txStateHistoryHelper = require('../../app/scripts/controllers/transactions/lib/tx-state-history-helper')
|
||||||
|
const testVault = require('../data/v17-long-history.json')
|
||||||
|
|
||||||
describe('deepCloneFromTxMeta', function () {
|
describe ('Transaction state history helper', function () {
|
||||||
it('should clone deep', function () {
|
|
||||||
const input = {
|
describe('#snapshotFromTxMeta', function () {
|
||||||
foo: {
|
it('should clone deep', function () {
|
||||||
bar: {
|
const input = {
|
||||||
bam: 'baz'
|
foo: {
|
||||||
|
bar: {
|
||||||
|
bam: 'baz'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
const output = txStateHistoryHelper.snapshotFromTxMeta(input)
|
||||||
const output = txStateHistoryHelper.snapshotFromTxMeta(input)
|
assert('foo' in output, 'has a foo key')
|
||||||
assert('foo' in output, 'has a foo key')
|
assert('bar' in output.foo, 'has a bar key')
|
||||||
assert('bar' in output.foo, 'has a bar key')
|
assert('bam' in output.foo.bar, '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')
|
||||||
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')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should remove the history key', function () {
|
describe('#migrateFromSnapshotsToDiffs', function () {
|
||||||
const input = { foo: 'bar', history: 'remembered' }
|
it('migrates history to diffs and can recover original values', function () {
|
||||||
const output = txStateHistoryHelper.snapshotFromTxMeta(input)
|
testVault.data.TransactionController.transactions.forEach((tx, index) => {
|
||||||
assert(typeof output.history, 'undefined', 'should remove history')
|
const newHistory = txStateHistoryHelper.migrateFromSnapshotsToDiffs(tx.history)
|
||||||
|
newHistory.forEach((newEntry, index) => {
|
||||||
|
if (index === 0) {
|
||||||
|
assert.equal(Array.isArray(newEntry), false, 'initial history item IS NOT a json patch obj')
|
||||||
|
} else {
|
||||||
|
assert.equal(Array.isArray(newEntry), true, 'non-initial history entry IS a json patch obj')
|
||||||
|
}
|
||||||
|
const oldEntry = tx.history[index]
|
||||||
|
const historySubset = newHistory.slice(0, index + 1)
|
||||||
|
const reconstructedValue = txStateHistoryHelper.replayHistory(historySubset)
|
||||||
|
assert.deepEqual(oldEntry, reconstructedValue, 'was able to reconstruct old entry from diffs')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
|
||||||
|
describe('#replayHistory', function () {
|
||||||
|
it('replaying history does not mutate the original obj', function () {
|
||||||
|
const initialState = { test: true, message: 'hello', value: 1 }
|
||||||
|
const diff1 = [{
|
||||||
|
"op": "replace",
|
||||||
|
"path": "/message",
|
||||||
|
"value": "haay",
|
||||||
|
}]
|
||||||
|
const diff2 = [{
|
||||||
|
"op": "replace",
|
||||||
|
"path": "/value",
|
||||||
|
"value": 2,
|
||||||
|
}]
|
||||||
|
const history = [initialState, diff1, diff2]
|
||||||
|
|
||||||
|
const beforeStateSnapshot = JSON.stringify(initialState)
|
||||||
|
const latestState = txStateHistoryHelper.replayHistory(history)
|
||||||
|
const afterStateSnapshot = JSON.stringify(initialState)
|
||||||
|
|
||||||
|
assert.notEqual(initialState, latestState, 'initial state is not the same obj as the latest state')
|
||||||
|
assert.equal(beforeStateSnapshot, afterStateSnapshot, 'initial state is not modified during run')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('#generateHistoryEntry', function () {
|
||||||
|
|
||||||
|
function generateHistoryEntryTest(note) {
|
||||||
|
|
||||||
|
const prevState = {
|
||||||
|
someValue: 'value 1',
|
||||||
|
foo: {
|
||||||
|
bar: {
|
||||||
|
bam: 'baz'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const nextState = {
|
||||||
|
newPropRoot: 'new property - root',
|
||||||
|
someValue: 'value 2',
|
||||||
|
foo: {
|
||||||
|
newPropFirstLevel: 'new property - first level',
|
||||||
|
bar: {
|
||||||
|
bam: 'baz'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const before = new Date().getTime()
|
||||||
|
const result = txStateHistoryHelper.generateHistoryEntry(prevState, nextState, note)
|
||||||
|
const after = new Date().getTime()
|
||||||
|
|
||||||
|
assert.ok(Array.isArray(result))
|
||||||
|
assert.equal(result.length, 3)
|
||||||
|
|
||||||
|
const expectedEntry1 = { op: 'add', path: '/foo/newPropFirstLevel', value: 'new property - first level' }
|
||||||
|
assert.equal(result[0].op, expectedEntry1.op)
|
||||||
|
assert.equal(result[0].path, expectedEntry1.path)
|
||||||
|
assert.equal(result[0].value, expectedEntry1.value)
|
||||||
|
assert.equal(result[0].value, expectedEntry1.value)
|
||||||
|
if (note)
|
||||||
|
assert.equal(result[0].note, note)
|
||||||
|
|
||||||
|
assert.ok(result[0].timestamp >= before && result[0].timestamp <= after)
|
||||||
|
|
||||||
|
const expectedEntry2 = { op: 'replace', path: '/someValue', value: 'value 2' }
|
||||||
|
assert.deepEqual(result[1], expectedEntry2)
|
||||||
|
|
||||||
|
const expectedEntry3 = { op: 'add', path: '/newPropRoot', value: 'new property - root' }
|
||||||
|
assert.deepEqual(result[2], expectedEntry3)
|
||||||
|
}
|
||||||
|
|
||||||
|
it('should generate history entries', function () {
|
||||||
|
generateHistoryEntryTest()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should add note to first entry', function () {
|
||||||
|
generateHistoryEntryTest('custom note')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
@ -1,46 +0,0 @@
|
|||||||
const assert = require('assert')
|
|
||||||
const txStateHistoryHelper = require('../../app/scripts/controllers/transactions/lib/tx-state-history-helper')
|
|
||||||
const testVault = require('../data/v17-long-history.json')
|
|
||||||
|
|
||||||
|
|
||||||
describe('tx-state-history-helper', function () {
|
|
||||||
it('migrates history to diffs and can recover original values', function () {
|
|
||||||
testVault.data.TransactionController.transactions.forEach((tx, index) => {
|
|
||||||
const newHistory = txStateHistoryHelper.migrateFromSnapshotsToDiffs(tx.history)
|
|
||||||
newHistory.forEach((newEntry, index) => {
|
|
||||||
if (index === 0) {
|
|
||||||
assert.equal(Array.isArray(newEntry), false, 'initial history item IS NOT a json patch obj')
|
|
||||||
} else {
|
|
||||||
assert.equal(Array.isArray(newEntry), true, 'non-initial history entry IS a json patch obj')
|
|
||||||
}
|
|
||||||
const oldEntry = tx.history[index]
|
|
||||||
const historySubset = newHistory.slice(0, index + 1)
|
|
||||||
const reconstructedValue = txStateHistoryHelper.replayHistory(historySubset)
|
|
||||||
assert.deepEqual(oldEntry, reconstructedValue, 'was able to reconstruct old entry from diffs')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it('replaying history does not mutate the original obj', function () {
|
|
||||||
const initialState = { test: true, message: 'hello', value: 1 }
|
|
||||||
const diff1 = [{
|
|
||||||
"op": "replace",
|
|
||||||
"path": "/message",
|
|
||||||
"value": "haay",
|
|
||||||
}]
|
|
||||||
const diff2 = [{
|
|
||||||
"op": "replace",
|
|
||||||
"path": "/value",
|
|
||||||
"value": 2,
|
|
||||||
}]
|
|
||||||
const history = [initialState, diff1, diff2]
|
|
||||||
|
|
||||||
const beforeStateSnapshot = JSON.stringify(initialState)
|
|
||||||
const latestState = txStateHistoryHelper.replayHistory(history)
|
|
||||||
const afterStateSnapshot = JSON.stringify(initialState)
|
|
||||||
|
|
||||||
assert.notEqual(initialState, latestState, 'initial state is not the same obj as the latest state')
|
|
||||||
assert.equal(beforeStateSnapshot, afterStateSnapshot, 'initial state is not modified during run')
|
|
||||||
})
|
|
||||||
|
|
||||||
})
|
|
@ -176,14 +176,21 @@ describe('TransactionStateManager', function () {
|
|||||||
assert.deepEqual(updatedTx.history[0], txStateHistoryHelper.snapshotFromTxMeta(updatedTx), 'first history item is initial state')
|
assert.deepEqual(updatedTx.history[0], txStateHistoryHelper.snapshotFromTxMeta(updatedTx), 'first history item is initial state')
|
||||||
// modify value and updateTx
|
// modify value and updateTx
|
||||||
updatedTx.txParams.gasPrice = desiredGasPrice
|
updatedTx.txParams.gasPrice = desiredGasPrice
|
||||||
|
const before = new Date().getTime()
|
||||||
txStateManager.updateTx(updatedTx)
|
txStateManager.updateTx(updatedTx)
|
||||||
|
const after = new Date().getTime()
|
||||||
// check updated value
|
// check updated value
|
||||||
const result = txStateManager.getTx('1')
|
const result = txStateManager.getTx('1')
|
||||||
assert.equal(result.txParams.gasPrice, desiredGasPrice, 'gas price updated')
|
assert.equal(result.txParams.gasPrice, desiredGasPrice, 'gas price updated')
|
||||||
// validate history was updated
|
// validate history was updated
|
||||||
assert.equal(result.history.length, 2, 'two history items (initial + diff)')
|
assert.equal(result.history.length, 2, 'two history items (initial + diff)')
|
||||||
|
assert.equal(result.history[1].length, 1, 'two history state items (initial + diff)')
|
||||||
|
|
||||||
const expectedEntry = { op: 'replace', path: '/txParams/gasPrice', value: desiredGasPrice }
|
const expectedEntry = { op: 'replace', path: '/txParams/gasPrice', value: desiredGasPrice }
|
||||||
assert.deepEqual(result.history[1], [expectedEntry], 'two history items (initial + diff)')
|
assert.deepEqual(result.history[1][0].op, expectedEntry.op, 'two history items (initial + diff) operation')
|
||||||
|
assert.deepEqual(result.history[1][0].path, expectedEntry.path, 'two history items (initial + diff) path')
|
||||||
|
assert.deepEqual(result.history[1][0].value, expectedEntry.value, 'two history items (initial + diff) value')
|
||||||
|
assert.ok(result.history[1][0].timestamp >= before && result.history[1][0].timestamp <= after)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -271,7 +271,6 @@ var actions = {
|
|||||||
SET_MOUSE_USER_STATE: 'SET_MOUSE_USER_STATE',
|
SET_MOUSE_USER_STATE: 'SET_MOUSE_USER_STATE',
|
||||||
|
|
||||||
// Network
|
// Network
|
||||||
setNetworkEndpoints,
|
|
||||||
updateNetworkEndpointType,
|
updateNetworkEndpointType,
|
||||||
UPDATE_NETWORK_ENDPOINT_TYPE: 'UPDATE_NETWORK_ENDPOINT_TYPE',
|
UPDATE_NETWORK_ENDPOINT_TYPE: 'UPDATE_NETWORK_ENDPOINT_TYPE',
|
||||||
|
|
||||||
@ -1924,23 +1923,6 @@ function setLocaleMessages (localeMessages) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setNetworkEndpoints (networkEndpointType) {
|
|
||||||
return dispatch => {
|
|
||||||
log.debug('background.setNetworkEndpoints')
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
background.setNetworkEndpoints(networkEndpointType, err => {
|
|
||||||
if (err) {
|
|
||||||
dispatch(actions.displayWarning(err.message))
|
|
||||||
return reject(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatch(actions.updateNetworkEndpointType(networkEndpointType))
|
|
||||||
resolve(networkEndpointType)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateNetworkEndpointType (networkEndpointType) {
|
function updateNetworkEndpointType (networkEndpointType) {
|
||||||
return {
|
return {
|
||||||
type: actions.UPDATE_NETWORK_ENDPOINT_TYPE,
|
type: actions.UPDATE_NETWORK_ENDPOINT_TYPE,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
const { Component } = require('react')
|
const { Component } = require('react')
|
||||||
const PropTypes = require('prop-types')
|
const PropTypes = require('prop-types')
|
||||||
const connect = require('react-redux').connect
|
const connect = require('react-redux').connect
|
||||||
const { Route, Switch, withRouter, matchPath } = require('react-router-dom')
|
const { Route, Switch, withRouter } = require('react-router-dom')
|
||||||
const { compose } = require('recompose')
|
const { compose } = require('recompose')
|
||||||
const h = require('react-hyperscript')
|
const h = require('react-hyperscript')
|
||||||
const actions = require('./actions')
|
const actions = require('./actions')
|
||||||
@ -83,15 +83,6 @@ class App extends Component {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
renderAppHeader () {
|
|
||||||
const { location } = this.props
|
|
||||||
const isInitializing = matchPath(location.pathname, {
|
|
||||||
path: INITIALIZE_ROUTE, exact: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
return isInitializing ? null : h(AppHeader)
|
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const {
|
const {
|
||||||
isLoading,
|
isLoading,
|
||||||
@ -128,7 +119,7 @@ class App extends Component {
|
|||||||
// global modal
|
// global modal
|
||||||
h(Modal, {}, []),
|
h(Modal, {}, []),
|
||||||
|
|
||||||
this.renderAppHeader(),
|
h(AppHeader),
|
||||||
|
|
||||||
// sidebar
|
// sidebar
|
||||||
this.renderSidebar(),
|
this.renderSidebar(),
|
||||||
|
@ -1,9 +1,13 @@
|
|||||||
import React, { Component } from 'react'
|
import React, { Component } from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import classnames from 'classnames'
|
import classnames from 'classnames'
|
||||||
|
import { matchPath } from 'react-router-dom'
|
||||||
|
|
||||||
const { ENVIRONMENT_TYPE_NOTIFICATION } = require('../../../../app/scripts/lib/enums')
|
const {
|
||||||
const { DEFAULT_ROUTE, CONFIRM_TRANSACTION_ROUTE } = require('../../routes')
|
ENVIRONMENT_TYPE_NOTIFICATION,
|
||||||
|
ENVIRONMENT_TYPE_POPUP,
|
||||||
|
} = require('../../../../app/scripts/lib/enums')
|
||||||
|
const { DEFAULT_ROUTE, INITIALIZE_ROUTE, CONFIRM_TRANSACTION_ROUTE } = require('../../routes')
|
||||||
const Identicon = require('../identicon')
|
const Identicon = require('../identicon')
|
||||||
const NetworkIndicator = require('../network')
|
const NetworkIndicator = require('../network')
|
||||||
|
|
||||||
@ -36,13 +40,23 @@ class AppHeader extends Component {
|
|||||||
: hideNetworkDropdown()
|
: hideNetworkDropdown()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isConfirming () {
|
||||||
|
const { location } = this.props
|
||||||
|
|
||||||
|
return Boolean(matchPath(location.pathname, {
|
||||||
|
path: CONFIRM_TRANSACTION_ROUTE, exact: false,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
renderAccountMenu () {
|
renderAccountMenu () {
|
||||||
const { isUnlocked, toggleAccountMenu, selectedAddress } = this.props
|
const { isUnlocked, toggleAccountMenu, selectedAddress } = this.props
|
||||||
|
|
||||||
return isUnlocked && (
|
return isUnlocked && (
|
||||||
<div
|
<div
|
||||||
className="account-menu__icon"
|
className={classnames('account-menu__icon', {
|
||||||
onClick={toggleAccountMenu}
|
'account-menu__icon--disabled': this.isConfirming(),
|
||||||
|
})}
|
||||||
|
onClick={() => this.isConfirming() || toggleAccountMenu()}
|
||||||
>
|
>
|
||||||
<Identicon
|
<Identicon
|
||||||
address={selectedAddress}
|
address={selectedAddress}
|
||||||
@ -52,6 +66,26 @@ class AppHeader extends Component {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hideAppHeader () {
|
||||||
|
const { location } = this.props
|
||||||
|
|
||||||
|
const isInitializing = Boolean(matchPath(location.pathname, {
|
||||||
|
path: INITIALIZE_ROUTE, exact: false,
|
||||||
|
}))
|
||||||
|
|
||||||
|
if (isInitializing) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (window.METAMASK_UI_TYPE === ENVIRONMENT_TYPE_NOTIFICATION) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (window.METAMASK_UI_TYPE === ENVIRONMENT_TYPE_POPUP && this.isConfirming()) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const {
|
const {
|
||||||
network,
|
network,
|
||||||
@ -61,7 +95,7 @@ class AppHeader extends Component {
|
|||||||
isUnlocked,
|
isUnlocked,
|
||||||
} = this.props
|
} = this.props
|
||||||
|
|
||||||
if (window.METAMASK_UI_TYPE === ENVIRONMENT_TYPE_NOTIFICATION) {
|
if (this.hideAppHeader()) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,7 +12,6 @@ const SimpleDropdown = require('../../dropdowns/simple-dropdown')
|
|||||||
const ToggleButton = require('react-toggle-button')
|
const ToggleButton = require('react-toggle-button')
|
||||||
const { REVEAL_SEED_ROUTE } = require('../../../routes')
|
const { REVEAL_SEED_ROUTE } = require('../../../routes')
|
||||||
const locales = require('../../../../../app/_locales/index.json')
|
const locales = require('../../../../../app/_locales/index.json')
|
||||||
const { OLD_UI_NETWORK_TYPE } = require('../../../../../app/scripts/controllers/network/enums')
|
|
||||||
|
|
||||||
const getInfuraCurrencyOptions = () => {
|
const getInfuraCurrencyOptions = () => {
|
||||||
const sortedCurrencies = infuraCurrencies.objects.sort((a, b) => {
|
const sortedCurrencies = infuraCurrencies.objects.sort((a, b) => {
|
||||||
@ -349,7 +348,6 @@ const mapDispatchToProps = dispatch => {
|
|||||||
updateCurrentLocale: key => dispatch(actions.updateCurrentLocale(key)),
|
updateCurrentLocale: key => dispatch(actions.updateCurrentLocale(key)),
|
||||||
setFeatureFlagToBeta: () => {
|
setFeatureFlagToBeta: () => {
|
||||||
return dispatch(actions.setFeatureFlag('betaUI', false, 'OLD_UI_NOTIFICATION_MODAL'))
|
return dispatch(actions.setFeatureFlag('betaUI', false, 'OLD_UI_NOTIFICATION_MODAL'))
|
||||||
.then(() => dispatch(actions.setNetworkEndpoints(OLD_UI_NETWORK_TYPE)))
|
|
||||||
},
|
},
|
||||||
showResetAccountConfirmationModal: () => {
|
showResetAccountConfirmationModal: () => {
|
||||||
return dispatch(actions.showModal({ name: 'CONFIRM_RESET_ACCOUNT' }))
|
return dispatch(actions.showModal({ name: 'CONFIRM_RESET_ACCOUNT' }))
|
||||||
|
@ -175,7 +175,6 @@ UnlockPage.propTypes = {
|
|||||||
isUnlocked: PropTypes.bool,
|
isUnlocked: PropTypes.bool,
|
||||||
t: PropTypes.func,
|
t: PropTypes.func,
|
||||||
useOldInterface: PropTypes.func,
|
useOldInterface: PropTypes.func,
|
||||||
setNetworkEndpoints: PropTypes.func,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default UnlockPage
|
export default UnlockPage
|
||||||
|
@ -6,7 +6,6 @@ const {
|
|||||||
tryUnlockMetamask,
|
tryUnlockMetamask,
|
||||||
forgotPassword,
|
forgotPassword,
|
||||||
markPasswordForgotten,
|
markPasswordForgotten,
|
||||||
setNetworkEndpoints,
|
|
||||||
} = require('../../../actions')
|
} = require('../../../actions')
|
||||||
|
|
||||||
import UnlockPage from './unlock-page.component'
|
import UnlockPage from './unlock-page.component'
|
||||||
@ -23,7 +22,6 @@ const mapDispatchToProps = dispatch => {
|
|||||||
forgotPassword: () => dispatch(forgotPassword()),
|
forgotPassword: () => dispatch(forgotPassword()),
|
||||||
tryUnlockMetamask: password => dispatch(tryUnlockMetamask(password)),
|
tryUnlockMetamask: password => dispatch(tryUnlockMetamask(password)),
|
||||||
markPasswordForgotten: () => dispatch(markPasswordForgotten()),
|
markPasswordForgotten: () => dispatch(markPasswordForgotten()),
|
||||||
setNetworkEndpoints: type => dispatch(setNetworkEndpoints(type)),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,6 +28,10 @@ const currencies = require('currency-formatter/currencies')
|
|||||||
|
|
||||||
const { MIN_GAS_PRICE_HEX } = require('../send/send-constants')
|
const { MIN_GAS_PRICE_HEX } = require('../send/send-constants')
|
||||||
const { SEND_ROUTE, DEFAULT_ROUTE } = require('../../routes')
|
const { SEND_ROUTE, DEFAULT_ROUTE } = require('../../routes')
|
||||||
|
const {
|
||||||
|
ENVIRONMENT_TYPE_POPUP,
|
||||||
|
ENVIRONMENT_TYPE_NOTIFICATION,
|
||||||
|
} = require('../../../../app/scripts/lib/enums')
|
||||||
|
|
||||||
ConfirmSendEther.contextTypes = {
|
ConfirmSendEther.contextTypes = {
|
||||||
t: PropTypes.func,
|
t: PropTypes.func,
|
||||||
@ -293,6 +297,14 @@ ConfirmSendEther.prototype.editTransaction = function (txMeta) {
|
|||||||
history.push(SEND_ROUTE)
|
history.push(SEND_ROUTE)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ConfirmSendEther.prototype.renderNetworkDisplay = function () {
|
||||||
|
const windowType = window.METAMASK_UI_TYPE
|
||||||
|
|
||||||
|
return (windowType === ENVIRONMENT_TYPE_NOTIFICATION || windowType === ENVIRONMENT_TYPE_POPUP)
|
||||||
|
? h(NetworkDisplay)
|
||||||
|
: null
|
||||||
|
}
|
||||||
|
|
||||||
ConfirmSendEther.prototype.render = function () {
|
ConfirmSendEther.prototype.render = function () {
|
||||||
const {
|
const {
|
||||||
currentCurrency,
|
currentCurrency,
|
||||||
@ -358,7 +370,7 @@ ConfirmSendEther.prototype.render = function () {
|
|||||||
visibility: !txMeta.lastGasPrice ? 'initial' : 'hidden',
|
visibility: !txMeta.lastGasPrice ? 'initial' : 'hidden',
|
||||||
},
|
},
|
||||||
}, 'Edit'),
|
}, 'Edit'),
|
||||||
window.METAMASK_UI_TYPE === 'notification' && h(NetworkDisplay),
|
this.renderNetworkDisplay(),
|
||||||
]),
|
]),
|
||||||
h('.page-container__title', title),
|
h('.page-container__title', title),
|
||||||
h('.page-container__subtitle', subtitle),
|
h('.page-container__subtitle', subtitle),
|
||||||
|
@ -23,6 +23,10 @@
|
|||||||
&__icon {
|
&__icon {
|
||||||
margin-left: 20px;
|
margin-left: 20px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
|
&--disabled {
|
||||||
|
cursor: initial;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__header {
|
&__header {
|
||||||
|
@ -82,10 +82,6 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-flow: row nowrap;
|
flex-flow: row nowrap;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
.identicon {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,7 +10,6 @@ const getCaretCoordinates = require('textarea-caret')
|
|||||||
const { RESTORE_VAULT_ROUTE, DEFAULT_ROUTE } = require('../routes')
|
const { RESTORE_VAULT_ROUTE, DEFAULT_ROUTE } = require('../routes')
|
||||||
const { getEnvironmentType } = require('../../../app/scripts/lib/util')
|
const { getEnvironmentType } = require('../../../app/scripts/lib/util')
|
||||||
const { ENVIRONMENT_TYPE_POPUP } = require('../../../app/scripts/lib/enums')
|
const { ENVIRONMENT_TYPE_POPUP } = require('../../../app/scripts/lib/enums')
|
||||||
const { OLD_UI_NETWORK_TYPE } = require('../../../app/scripts/controllers/network/enums')
|
|
||||||
|
|
||||||
class InitializeMenuScreen extends Component {
|
class InitializeMenuScreen extends Component {
|
||||||
constructor (props) {
|
constructor (props) {
|
||||||
@ -190,7 +189,6 @@ class InitializeMenuScreen extends Component {
|
|||||||
|
|
||||||
showOldUI () {
|
showOldUI () {
|
||||||
this.props.dispatch(actions.setFeatureFlag('betaUI', false, 'OLD_UI_NOTIFICATION_MODAL'))
|
this.props.dispatch(actions.setFeatureFlag('betaUI', false, 'OLD_UI_NOTIFICATION_MODAL'))
|
||||||
.then(() => this.props.dispatch(actions.setNetworkEndpoints(OLD_UI_NETWORK_TYPE)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,8 +6,7 @@ const { HashRouter } = require('react-router-dom')
|
|||||||
const App = require('./app')
|
const App = require('./app')
|
||||||
const OldApp = require('../../old-ui/app/app')
|
const OldApp = require('../../old-ui/app/app')
|
||||||
const { autoAddToBetaUI } = require('./selectors')
|
const { autoAddToBetaUI } = require('./selectors')
|
||||||
const { setFeatureFlag, setNetworkEndpoints } = require('./actions')
|
const { setFeatureFlag } = require('./actions')
|
||||||
const { BETA_UI_NETWORK_TYPE } = require('../../app/scripts/controllers/network/enums')
|
|
||||||
const I18nProvider = require('./i18n-provider')
|
const I18nProvider = require('./i18n-provider')
|
||||||
|
|
||||||
function mapStateToProps (state) {
|
function mapStateToProps (state) {
|
||||||
@ -24,11 +23,9 @@ function mapDispatchToProps (dispatch) {
|
|||||||
return {
|
return {
|
||||||
setFeatureFlagWithModal: () => {
|
setFeatureFlagWithModal: () => {
|
||||||
return dispatch(setFeatureFlag('betaUI', true, 'BETA_UI_NOTIFICATION_MODAL'))
|
return dispatch(setFeatureFlag('betaUI', true, 'BETA_UI_NOTIFICATION_MODAL'))
|
||||||
.then(() => dispatch(setNetworkEndpoints(BETA_UI_NETWORK_TYPE)))
|
|
||||||
},
|
},
|
||||||
setFeatureFlagWithoutModal: () => {
|
setFeatureFlagWithoutModal: () => {
|
||||||
return dispatch(setFeatureFlag('betaUI', true))
|
return dispatch(setFeatureFlag('betaUI', true))
|
||||||
.then(() => dispatch(setNetworkEndpoints(BETA_UI_NETWORK_TYPE)))
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,11 +5,6 @@ const actions = require('./app/actions')
|
|||||||
const configureStore = require('./app/store')
|
const configureStore = require('./app/store')
|
||||||
const txHelper = require('./lib/tx-helper')
|
const txHelper = require('./lib/tx-helper')
|
||||||
const { fetchLocale } = require('./i18n-helper')
|
const { fetchLocale } = require('./i18n-helper')
|
||||||
const {
|
|
||||||
OLD_UI_NETWORK_TYPE,
|
|
||||||
BETA_UI_NETWORK_TYPE,
|
|
||||||
} = require('../app/scripts/controllers/network/enums')
|
|
||||||
|
|
||||||
const log = require('loglevel')
|
const log = require('loglevel')
|
||||||
|
|
||||||
module.exports = launchMetamaskUi
|
module.exports = launchMetamaskUi
|
||||||
@ -55,10 +50,6 @@ async function startApp (metamaskState, accountManager, opts) {
|
|||||||
networkVersion: opts.networkVersion,
|
networkVersion: opts.networkVersion,
|
||||||
})
|
})
|
||||||
|
|
||||||
const useBetaUi = metamaskState.featureFlags.betaUI
|
|
||||||
const networkEndpointType = useBetaUi ? BETA_UI_NETWORK_TYPE : OLD_UI_NETWORK_TYPE
|
|
||||||
store.dispatch(actions.setNetworkEndpoints(networkEndpointType))
|
|
||||||
|
|
||||||
// if unconfirmed txs, start on txConf page
|
// if unconfirmed txs, start on txConf page
|
||||||
const unapprovedTxsAll = txHelper(metamaskState.unapprovedTxs, metamaskState.unapprovedMsgs, metamaskState.unapprovedPersonalMsgs, metamaskState.unapprovedTypedMessages, metamaskState.network)
|
const unapprovedTxsAll = txHelper(metamaskState.unapprovedTxs, metamaskState.unapprovedMsgs, metamaskState.unapprovedPersonalMsgs, metamaskState.unapprovedTypedMessages, metamaskState.network)
|
||||||
const numberOfUnapprivedTx = unapprovedTxsAll.length
|
const numberOfUnapprivedTx = unapprovedTxsAll.length
|
||||||
|
Loading…
Reference in New Issue
Block a user