mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Merge pull request #2785 from danjm/merge-master-to-newUI-flat-dec20
[NewUI] Merge master to NewUI-flat
This commit is contained in:
commit
4ef71f0365
19
CHANGELOG.md
19
CHANGELOG.md
@ -2,6 +2,25 @@
|
|||||||
|
|
||||||
## Current Master
|
## Current Master
|
||||||
|
|
||||||
|
- Fix bug that prevented updating custom token details.
|
||||||
|
|
||||||
|
## 3.13.3 2017-12-14
|
||||||
|
|
||||||
|
- Show tokens that are held that have no balance.
|
||||||
|
- Reduce load on Infura by using a new block polling endpoint.
|
||||||
|
|
||||||
|
## 3.13.2 2017-12-9
|
||||||
|
|
||||||
|
- Reduce new block polling interval to 8000 ms, to ease server load.
|
||||||
|
|
||||||
|
## 3.13.1 2017-12-7
|
||||||
|
|
||||||
|
- Allow Dapps to specify a transaction nonce, allowing dapps to propose resubmit and force-cancel transactions.
|
||||||
|
|
||||||
|
## 3.13.0 2017-12-7
|
||||||
|
|
||||||
|
- Allow resubmitting transactions that are taking long to complete.
|
||||||
|
|
||||||
## 3.12.1 2017-11-29
|
## 3.12.1 2017-11-29
|
||||||
|
|
||||||
- Fix bug where a user could be shown two different seed phrases.
|
- Fix bug where a user could be shown two different seed phrases.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# MetaMask Plugin
|
# MetaMask Browser Extension
|
||||||
[](https://circleci.com/gh/MetaMask/metamask-extension) [](https://coveralls.io/github/MetaMask/metamask-extension?branch=master) [](https://greenkeeper.io/) [](http://waffle.io/MetaMask/metamask-extension)
|
[](https://circleci.com/gh/MetaMask/metamask-extension) [](https://coveralls.io/github/MetaMask/metamask-extension?branch=master) [](https://greenkeeper.io/) [](http://waffle.io/MetaMask/metamask-extension)
|
||||||
|
|
||||||
|
|
||||||
|
@ -96,7 +96,7 @@ function logStreamDisconnectWarning (remoteLabel, err) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function shouldInjectWeb3 () {
|
function shouldInjectWeb3 () {
|
||||||
return doctypeCheck() || suffixCheck()
|
return doctypeCheck() && suffixCheck() && documentElementCheck()
|
||||||
}
|
}
|
||||||
|
|
||||||
function doctypeCheck () {
|
function doctypeCheck () {
|
||||||
@ -104,7 +104,7 @@ function doctypeCheck () {
|
|||||||
if (doctype) {
|
if (doctype) {
|
||||||
return doctype.name === 'html'
|
return doctype.name === 'html'
|
||||||
} else {
|
} else {
|
||||||
return false
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,6 +121,14 @@ function suffixCheck () {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function documentElementCheck () {
|
||||||
|
var documentElement = document.documentElement.nodeName
|
||||||
|
if (documentElement) {
|
||||||
|
return documentElement.toLowerCase() === 'html'
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
function redirectToPhishingWarning () {
|
function redirectToPhishingWarning () {
|
||||||
console.log('MetaMask - redirecting to phishing warning')
|
console.log('MetaMask - redirecting to phishing warning')
|
||||||
window.location.href = 'https://metamask.io/phishing.html'
|
window.location.href = 'https://metamask.io/phishing.html'
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
const assert = require('assert')
|
const assert = require('assert')
|
||||||
const EventEmitter = require('events')
|
const EventEmitter = require('events')
|
||||||
const createMetamaskProvider = require('web3-provider-engine/zero.js')
|
const createMetamaskProvider = require('web3-provider-engine/zero.js')
|
||||||
|
const createInfuraProvider = require('eth-json-rpc-infura/src/createProvider')
|
||||||
const ObservableStore = require('obs-store')
|
const ObservableStore = require('obs-store')
|
||||||
const ComposedStore = require('obs-store/lib/composed')
|
const ComposedStore = require('obs-store/lib/composed')
|
||||||
const extend = require('xtend')
|
const extend = require('xtend')
|
||||||
@ -8,6 +9,7 @@ const EthQuery = require('eth-query')
|
|||||||
const createEventEmitterProxy = require('../lib/events-proxy.js')
|
const createEventEmitterProxy = require('../lib/events-proxy.js')
|
||||||
const RPC_ADDRESS_LIST = require('../config.js').network
|
const RPC_ADDRESS_LIST = require('../config.js').network
|
||||||
const DEFAULT_RPC = RPC_ADDRESS_LIST['rinkeby']
|
const DEFAULT_RPC = RPC_ADDRESS_LIST['rinkeby']
|
||||||
|
const INFURA_PROVIDER_TYPES = ['ropsten', 'rinkeby', 'kovan', 'mainnet']
|
||||||
|
|
||||||
module.exports = class NetworkController extends EventEmitter {
|
module.exports = class NetworkController extends EventEmitter {
|
||||||
|
|
||||||
@ -24,8 +26,13 @@ module.exports = class NetworkController extends EventEmitter {
|
|||||||
|
|
||||||
initializeProvider (_providerParams) {
|
initializeProvider (_providerParams) {
|
||||||
this._baseProviderParams = _providerParams
|
this._baseProviderParams = _providerParams
|
||||||
const rpcUrl = this.getCurrentRpcAddress()
|
const { type, rpcTarget } = this.providerStore.getState()
|
||||||
this._configureStandardProvider({ rpcUrl })
|
// map rpcTarget to rpcUrl
|
||||||
|
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)
|
||||||
@ -83,7 +90,7 @@ module.exports = class NetworkController extends EventEmitter {
|
|||||||
const rpcTarget = this.getRpcAddressForType(type)
|
const rpcTarget = this.getRpcAddressForType(type)
|
||||||
assert(rpcTarget, `NetworkController - unknown rpc address for type "${type}"`)
|
assert(rpcTarget, `NetworkController - unknown rpc address for type "${type}"`)
|
||||||
this.providerStore.updateState({ type, rpcTarget })
|
this.providerStore.updateState({ type, rpcTarget })
|
||||||
this._switchNetwork({ rpcUrl: rpcTarget })
|
this._switchNetwork({ type })
|
||||||
}
|
}
|
||||||
|
|
||||||
getProviderConfig () {
|
getProviderConfig () {
|
||||||
@ -99,14 +106,54 @@ module.exports = class NetworkController extends EventEmitter {
|
|||||||
// Private
|
// Private
|
||||||
//
|
//
|
||||||
|
|
||||||
_switchNetwork (providerParams) {
|
_switchNetwork (opts) {
|
||||||
this.setNetworkState('loading')
|
this.setNetworkState('loading')
|
||||||
this._configureStandardProvider(providerParams)
|
this._configureProvider(opts)
|
||||||
this.emit('networkDidChange')
|
this.emit('networkDidChange')
|
||||||
}
|
}
|
||||||
|
|
||||||
_configureStandardProvider (_providerParams) {
|
_configureProvider (opts) {
|
||||||
const providerParams = extend(this._baseProviderParams, _providerParams)
|
// type-based rpc endpoints
|
||||||
|
const { type } = opts
|
||||||
|
if (type) {
|
||||||
|
// type-based infura rpc endpoints
|
||||||
|
const isInfura = INFURA_PROVIDER_TYPES.includes(type)
|
||||||
|
opts.rpcUrl = this.getRpcAddressForType(type)
|
||||||
|
if (isInfura) {
|
||||||
|
this._configureInfuraProvider(opts)
|
||||||
|
// other type-based rpc endpoints
|
||||||
|
} else {
|
||||||
|
this._configureStandardProvider(opts)
|
||||||
|
}
|
||||||
|
// url-based rpc endpoints
|
||||||
|
} else {
|
||||||
|
this._configureStandardProvider(opts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_configureInfuraProvider (opts) {
|
||||||
|
log.info('_configureInfuraProvider', opts)
|
||||||
|
const blockTrackerProvider = createInfuraProvider({
|
||||||
|
network: opts.type,
|
||||||
|
})
|
||||||
|
const providerParams = extend(this._baseProviderParams, {
|
||||||
|
rpcUrl: opts.rpcUrl,
|
||||||
|
engineParams: {
|
||||||
|
pollingInterval: 8000,
|
||||||
|
blockTrackerProvider,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
const provider = createMetamaskProvider(providerParams)
|
||||||
|
this._setProvider(provider)
|
||||||
|
}
|
||||||
|
|
||||||
|
_configureStandardProvider ({ rpcUrl }) {
|
||||||
|
const providerParams = extend(this._baseProviderParams, {
|
||||||
|
rpcUrl,
|
||||||
|
engineParams: {
|
||||||
|
pollingInterval: 8000,
|
||||||
|
},
|
||||||
|
})
|
||||||
const provider = createMetamaskProvider(providerParams)
|
const provider = createMetamaskProvider(providerParams)
|
||||||
this._setProvider(provider)
|
this._setProvider(provider)
|
||||||
}
|
}
|
||||||
|
@ -36,22 +36,24 @@ class PreferencesController {
|
|||||||
return this.store.getState().selectedAddress
|
return this.store.getState().selectedAddress
|
||||||
}
|
}
|
||||||
|
|
||||||
addToken (rawAddress, symbol, decimals) {
|
async addToken (rawAddress, symbol, decimals) {
|
||||||
const address = normalizeAddress(rawAddress)
|
const address = normalizeAddress(rawAddress)
|
||||||
const newEntry = { address, symbol, decimals }
|
const newEntry = { address, symbol, decimals }
|
||||||
|
|
||||||
const tokens = this.store.getState().tokens
|
const tokens = this.store.getState().tokens
|
||||||
const previousIndex = tokens.find((token, index) => {
|
const previousEntry = tokens.find((token, index) => {
|
||||||
return token.address === address
|
return token.address === address
|
||||||
})
|
})
|
||||||
|
const previousIndex = tokens.indexOf(previousEntry)
|
||||||
|
|
||||||
if (previousIndex) {
|
if (previousEntry) {
|
||||||
tokens[previousIndex] = newEntry
|
tokens[previousIndex] = newEntry
|
||||||
} else {
|
} else {
|
||||||
tokens.push(newEntry)
|
tokens.push(newEntry)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.store.updateState({ tokens })
|
this.store.updateState({ tokens })
|
||||||
|
|
||||||
return Promise.resolve(tokens)
|
return Promise.resolve(tokens)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
44
app/scripts/controllers/recent-blocks.js
Normal file
44
app/scripts/controllers/recent-blocks.js
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
const ObservableStore = require('obs-store')
|
||||||
|
const extend = require('xtend')
|
||||||
|
|
||||||
|
class RecentBlocksController {
|
||||||
|
|
||||||
|
constructor (opts = {}) {
|
||||||
|
const { blockTracker } = opts
|
||||||
|
this.blockTracker = blockTracker
|
||||||
|
this.historyLength = opts.historyLength || 40
|
||||||
|
|
||||||
|
const initState = extend({
|
||||||
|
recentBlocks: [],
|
||||||
|
}, opts.initState)
|
||||||
|
this.store = new ObservableStore(initState)
|
||||||
|
|
||||||
|
this.blockTracker.on('block', this.processBlock.bind(this))
|
||||||
|
}
|
||||||
|
|
||||||
|
resetState () {
|
||||||
|
this.store.updateState({
|
||||||
|
recentBlocks: [],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
processBlock (newBlock) {
|
||||||
|
const block = extend(newBlock, {
|
||||||
|
gasPrices: newBlock.transactions.map((tx) => {
|
||||||
|
return tx.gasPrice
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
delete block.transactions
|
||||||
|
|
||||||
|
const state = this.store.getState()
|
||||||
|
state.recentBlocks.push(block)
|
||||||
|
|
||||||
|
while (state.recentBlocks.length > this.historyLength) {
|
||||||
|
state.recentBlocks.shift()
|
||||||
|
}
|
||||||
|
|
||||||
|
this.store.updateState(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = RecentBlocksController
|
@ -138,18 +138,20 @@ module.exports = class TransactionController extends EventEmitter {
|
|||||||
|
|
||||||
async newUnapprovedTransaction (txParams) {
|
async newUnapprovedTransaction (txParams) {
|
||||||
log.debug(`MetaMaskController newUnapprovedTransaction ${JSON.stringify(txParams)}`)
|
log.debug(`MetaMaskController newUnapprovedTransaction ${JSON.stringify(txParams)}`)
|
||||||
const txMeta = await this.addUnapprovedTransaction(txParams)
|
const initialTxMeta = await this.addUnapprovedTransaction(txParams)
|
||||||
this.emit('newUnapprovedTx', txMeta)
|
this.emit('newUnapprovedTx', initialTxMeta)
|
||||||
// listen for tx completion (success, fail)
|
// listen for tx completion (success, fail)
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this.txStateManager.once(`${txMeta.id}:finished`, (completedTx) => {
|
this.txStateManager.once(`${initialTxMeta.id}:finished`, (finishedTxMeta) => {
|
||||||
switch (completedTx.status) {
|
switch (finishedTxMeta.status) {
|
||||||
case 'submitted':
|
case 'submitted':
|
||||||
return resolve(completedTx.hash)
|
return resolve(finishedTxMeta.hash)
|
||||||
case 'rejected':
|
case 'rejected':
|
||||||
return reject(new Error('MetaMask Tx Signature: User denied transaction signature.'))
|
return reject(new Error('MetaMask Tx Signature: User denied transaction signature.'))
|
||||||
|
case 'failed':
|
||||||
|
return reject(new Error(finishedTxMeta.err.message))
|
||||||
default:
|
default:
|
||||||
return reject(new Error(`MetaMask Tx Signature: Unknown problem: ${JSON.stringify(completedTx.txParams)}`))
|
return reject(new Error(`MetaMask Tx Signature: Unknown problem: ${JSON.stringify(finishedTxMeta.txParams)}`))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -177,6 +179,7 @@ module.exports = class TransactionController extends EventEmitter {
|
|||||||
const txParams = txMeta.txParams
|
const txParams = txMeta.txParams
|
||||||
// ensure value
|
// ensure value
|
||||||
txMeta.gasPriceSpecified = Boolean(txParams.gasPrice)
|
txMeta.gasPriceSpecified = Boolean(txParams.gasPrice)
|
||||||
|
txMeta.nonceSpecified = Boolean(txParams.nonce)
|
||||||
const gasPrice = txParams.gasPrice || await this.query.gasPrice()
|
const gasPrice = txParams.gasPrice || await this.query.gasPrice()
|
||||||
txParams.gasPrice = ethUtil.addHexPrefix(gasPrice.toString(16))
|
txParams.gasPrice = ethUtil.addHexPrefix(gasPrice.toString(16))
|
||||||
txParams.value = txParams.value || '0x0'
|
txParams.value = txParams.value || '0x0'
|
||||||
@ -184,6 +187,13 @@ module.exports = class TransactionController extends EventEmitter {
|
|||||||
return await this.txGasUtil.analyzeGasUsage(txMeta)
|
return await this.txGasUtil.analyzeGasUsage(txMeta)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async retryTransaction (txId) {
|
||||||
|
this.txStateManager.setTxStatusUnapproved(txId)
|
||||||
|
const txMeta = this.txStateManager.getTx(txId)
|
||||||
|
txMeta.lastGasPrice = txMeta.txParams.gasPrice
|
||||||
|
this.txStateManager.updateTx(txMeta, 'retryTransaction: manual retry')
|
||||||
|
}
|
||||||
|
|
||||||
async updateTransaction (txMeta) {
|
async updateTransaction (txMeta) {
|
||||||
this.txStateManager.updateTx(txMeta, 'confTx: user updated transaction')
|
this.txStateManager.updateTx(txMeta, 'confTx: user updated transaction')
|
||||||
}
|
}
|
||||||
@ -204,7 +214,12 @@ module.exports = class TransactionController extends EventEmitter {
|
|||||||
// wait for a nonce
|
// wait for a nonce
|
||||||
nonceLock = await this.nonceTracker.getNonceLock(fromAddress)
|
nonceLock = await this.nonceTracker.getNonceLock(fromAddress)
|
||||||
// add nonce to txParams
|
// add nonce to txParams
|
||||||
txMeta.txParams.nonce = ethUtil.addHexPrefix(nonceLock.nextNonce.toString(16))
|
const nonce = txMeta.nonceSpecified ? txMeta.txParams.nonce : nonceLock.nextNonce
|
||||||
|
if (nonce > nonceLock.nextNonce) {
|
||||||
|
const message = `Specified nonce may not be larger than account's next valid nonce.`
|
||||||
|
throw new Error(message)
|
||||||
|
}
|
||||||
|
txMeta.txParams.nonce = ethUtil.addHexPrefix(nonce.toString(16))
|
||||||
// add nonce debugging information to txMeta
|
// add nonce debugging information to txMeta
|
||||||
txMeta.nonceDetails = nonceLock.nonceDetails
|
txMeta.nonceDetails = nonceLock.nonceDetails
|
||||||
this.txStateManager.updateTx(txMeta, 'transactions#approveTransaction')
|
this.txStateManager.updateTx(txMeta, 'transactions#approveTransaction')
|
||||||
|
@ -117,8 +117,6 @@ class AccountTracker extends EventEmitter {
|
|||||||
const query = this._query
|
const query = this._query
|
||||||
async.parallel({
|
async.parallel({
|
||||||
balance: query.getBalance.bind(query, address),
|
balance: query.getBalance.bind(query, address),
|
||||||
nonce: query.getTransactionCount.bind(query, address),
|
|
||||||
code: query.getCode.bind(query, address),
|
|
||||||
}, cb)
|
}, cb)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,7 +22,11 @@ module.exports = class txProvideUtil {
|
|||||||
try {
|
try {
|
||||||
estimatedGasHex = await this.estimateTxGas(txMeta, block.gasLimit)
|
estimatedGasHex = await this.estimateTxGas(txMeta, block.gasLimit)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err.message.includes('Transaction execution error.')) {
|
const simulationFailed = (
|
||||||
|
err.message.includes('Transaction execution error.') ||
|
||||||
|
err.message.includes('gas required exceeds allowance or always failing transaction')
|
||||||
|
)
|
||||||
|
if ( simulationFailed ) {
|
||||||
txMeta.simulationFails = true
|
txMeta.simulationFails = true
|
||||||
return txMeta
|
return txMeta
|
||||||
}
|
}
|
||||||
|
@ -187,6 +187,10 @@ module.exports = class TransactionStateManger extends EventEmitter {
|
|||||||
this._setTxStatus(txId, 'rejected')
|
this._setTxStatus(txId, 'rejected')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// should update the status of the tx to 'unapproved'.
|
||||||
|
setTxStatusUnapproved (txId) {
|
||||||
|
this._setTxStatus(txId, 'unapproved')
|
||||||
|
}
|
||||||
// should update the status of the tx to 'approved'.
|
// should update the status of the tx to 'approved'.
|
||||||
setTxStatusApproved (txId) {
|
setTxStatusApproved (txId) {
|
||||||
this._setTxStatus(txId, 'approved')
|
this._setTxStatus(txId, 'approved')
|
||||||
@ -236,7 +240,7 @@ module.exports = class TransactionStateManger extends EventEmitter {
|
|||||||
txMeta.status = status
|
txMeta.status = status
|
||||||
this.emit(`${txMeta.id}:${status}`, txId)
|
this.emit(`${txMeta.id}:${status}`, txId)
|
||||||
this.emit(`tx:status-update`, txId, status)
|
this.emit(`tx:status-update`, txId, status)
|
||||||
if (status === 'submitted' || status === 'rejected') {
|
if (['submitted', 'rejected', 'failed'].includes(status)) {
|
||||||
this.emit(`${txMeta.id}:finished`, txMeta)
|
this.emit(`${txMeta.id}:finished`, txMeta)
|
||||||
}
|
}
|
||||||
this.updateTx(txMeta, `txStateManager: setting status to ${status}`)
|
this.updateTx(txMeta, `txStateManager: setting status to ${status}`)
|
||||||
|
@ -23,6 +23,7 @@ const ShapeShiftController = require('./controllers/shapeshift')
|
|||||||
const AddressBookController = require('./controllers/address-book')
|
const AddressBookController = require('./controllers/address-book')
|
||||||
const InfuraController = require('./controllers/infura')
|
const InfuraController = require('./controllers/infura')
|
||||||
const BlacklistController = require('./controllers/blacklist')
|
const BlacklistController = require('./controllers/blacklist')
|
||||||
|
const RecentBlocksController = require('./controllers/recent-blocks')
|
||||||
const MessageManager = require('./lib/message-manager')
|
const MessageManager = require('./lib/message-manager')
|
||||||
const PersonalMessageManager = require('./lib/personal-message-manager')
|
const PersonalMessageManager = require('./lib/personal-message-manager')
|
||||||
const TypedMessageManager = require('./lib/typed-message-manager')
|
const TypedMessageManager = require('./lib/typed-message-manager')
|
||||||
@ -91,6 +92,10 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||||||
this.provider = this.initializeProvider()
|
this.provider = this.initializeProvider()
|
||||||
this.blockTracker = this.provider._blockTracker
|
this.blockTracker = this.provider._blockTracker
|
||||||
|
|
||||||
|
this.recentBlocksController = new RecentBlocksController({
|
||||||
|
blockTracker: this.blockTracker,
|
||||||
|
})
|
||||||
|
|
||||||
// eth data query tools
|
// eth data query tools
|
||||||
this.ethQuery = new EthQuery(this.provider)
|
this.ethQuery = new EthQuery(this.provider)
|
||||||
// account tracker watches balances, nonces, and any code at their address.
|
// account tracker watches balances, nonces, and any code at their address.
|
||||||
@ -196,25 +201,30 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||||||
this.blacklistController.store.subscribe((state) => {
|
this.blacklistController.store.subscribe((state) => {
|
||||||
this.store.updateState({ BlacklistController: state })
|
this.store.updateState({ BlacklistController: state })
|
||||||
})
|
})
|
||||||
|
this.recentBlocksController.store.subscribe((state) => {
|
||||||
|
this.store.updateState({ RecentBlocks: state })
|
||||||
|
})
|
||||||
this.infuraController.store.subscribe((state) => {
|
this.infuraController.store.subscribe((state) => {
|
||||||
this.store.updateState({ InfuraController: state })
|
this.store.updateState({ InfuraController: state })
|
||||||
})
|
})
|
||||||
|
|
||||||
// manual mem state subscriptions
|
// manual mem state subscriptions
|
||||||
this.networkController.store.subscribe(this.sendUpdate.bind(this))
|
const sendUpdate = this.sendUpdate.bind(this)
|
||||||
this.accountTracker.store.subscribe(this.sendUpdate.bind(this))
|
this.networkController.store.subscribe(sendUpdate)
|
||||||
this.txController.memStore.subscribe(this.sendUpdate.bind(this))
|
this.accountTracker.store.subscribe(sendUpdate)
|
||||||
this.balancesController.store.subscribe(this.sendUpdate.bind(this))
|
this.txController.memStore.subscribe(sendUpdate)
|
||||||
this.messageManager.memStore.subscribe(this.sendUpdate.bind(this))
|
this.balancesController.store.subscribe(sendUpdate)
|
||||||
this.personalMessageManager.memStore.subscribe(this.sendUpdate.bind(this))
|
this.messageManager.memStore.subscribe(sendUpdate)
|
||||||
this.typedMessageManager.memStore.subscribe(this.sendUpdate.bind(this))
|
this.personalMessageManager.memStore.subscribe(sendUpdate)
|
||||||
this.keyringController.memStore.subscribe(this.sendUpdate.bind(this))
|
this.typedMessageManager.memStore.subscribe(sendUpdate)
|
||||||
this.preferencesController.store.subscribe(this.sendUpdate.bind(this))
|
this.keyringController.memStore.subscribe(sendUpdate)
|
||||||
this.addressBookController.store.subscribe(this.sendUpdate.bind(this))
|
this.preferencesController.store.subscribe(sendUpdate)
|
||||||
this.currencyController.store.subscribe(this.sendUpdate.bind(this))
|
this.recentBlocksController.store.subscribe(sendUpdate)
|
||||||
this.noticeController.memStore.subscribe(this.sendUpdate.bind(this))
|
this.addressBookController.store.subscribe(sendUpdate)
|
||||||
this.shapeshiftController.store.subscribe(this.sendUpdate.bind(this))
|
this.currencyController.store.subscribe(sendUpdate)
|
||||||
this.infuraController.store.subscribe(this.sendUpdate.bind(this))
|
this.noticeController.memStore.subscribe(sendUpdate)
|
||||||
|
this.shapeshiftController.store.subscribe(sendUpdate)
|
||||||
|
this.infuraController.store.subscribe(sendUpdate)
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -298,6 +308,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||||||
this.currencyController.store.getState(),
|
this.currencyController.store.getState(),
|
||||||
this.noticeController.memStore.getState(),
|
this.noticeController.memStore.getState(),
|
||||||
this.infuraController.store.getState(),
|
this.infuraController.store.getState(),
|
||||||
|
this.recentBlocksController.store.getState(),
|
||||||
// config manager
|
// config manager
|
||||||
this.configManager.getConfig(),
|
this.configManager.getConfig(),
|
||||||
this.shapeshiftController.store.getState(),
|
this.shapeshiftController.store.getState(),
|
||||||
@ -367,6 +378,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||||||
cancelTransaction: nodeify(txController.cancelTransaction, txController),
|
cancelTransaction: nodeify(txController.cancelTransaction, txController),
|
||||||
updateTransaction: nodeify(txController.updateTransaction, txController),
|
updateTransaction: nodeify(txController.updateTransaction, txController),
|
||||||
updateAndApproveTransaction: nodeify(txController.updateAndApproveTransaction, txController),
|
updateAndApproveTransaction: nodeify(txController.updateAndApproveTransaction, txController),
|
||||||
|
retryTransaction: nodeify(this.retryTransaction, this),
|
||||||
|
|
||||||
// messageManager
|
// messageManager
|
||||||
signMessage: nodeify(this.signMessage, this),
|
signMessage: nodeify(this.signMessage, this),
|
||||||
@ -577,6 +589,14 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||||||
//
|
//
|
||||||
// Identity Management
|
// Identity Management
|
||||||
//
|
//
|
||||||
|
//
|
||||||
|
|
||||||
|
async retryTransaction (txId, cb) {
|
||||||
|
await this.txController.retryTransaction(txId)
|
||||||
|
const state = await this.getState()
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
newUnsignedMessage (msgParams, cb) {
|
newUnsignedMessage (msgParams, cb) {
|
||||||
const msgId = this.messageManager.addUnapprovedMessage(msgParams)
|
const msgId = this.messageManager.addUnapprovedMessage(msgParams)
|
||||||
|
739
development/states/pending-tx.json
Normal file
739
development/states/pending-tx.json
Normal file
@ -0,0 +1,739 @@
|
|||||||
|
{
|
||||||
|
"metamask": {
|
||||||
|
"isInitialized": true,
|
||||||
|
"isUnlocked": true,
|
||||||
|
"isMascara": false,
|
||||||
|
"rpcTarget": "https://rawtestrpc.metamask.io/",
|
||||||
|
"identities": {
|
||||||
|
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825": {
|
||||||
|
"address": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
|
"name": "Account 1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"unapprovedTxs": {},
|
||||||
|
"noActiveNotices": true,
|
||||||
|
"frequentRpcList": [
|
||||||
|
"http://192.168.1.34:7545/"
|
||||||
|
],
|
||||||
|
"addressBook": [],
|
||||||
|
"tokenExchangeRates": {},
|
||||||
|
"coinOptions": {},
|
||||||
|
"provider": {
|
||||||
|
"type": "mainnet",
|
||||||
|
"rpcTarget": "https://mainnet.infura.io/metamask"
|
||||||
|
},
|
||||||
|
"network": "1",
|
||||||
|
"accounts": {
|
||||||
|
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825": {
|
||||||
|
"code": "0x",
|
||||||
|
"balance": "0x1b3f641ed0c2f62",
|
||||||
|
"nonce": "0x35",
|
||||||
|
"address": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"currentBlockGasLimit": "0x66df83",
|
||||||
|
"selectedAddressTxList": [
|
||||||
|
{
|
||||||
|
"id": 3516145537630216,
|
||||||
|
"time": 1512615655535,
|
||||||
|
"status": "submitted",
|
||||||
|
"metamaskNetworkId": "1",
|
||||||
|
"txParams": {
|
||||||
|
"from": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
|
"to": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
|
"value": "0x16345785d8a0000",
|
||||||
|
"gasPrice": "0xc1b710800",
|
||||||
|
"gas": "0x7b0c",
|
||||||
|
"nonce": "0x35",
|
||||||
|
"chainId": "0x1"
|
||||||
|
},
|
||||||
|
"gasPriceSpecified": false,
|
||||||
|
"gasLimitSpecified": false,
|
||||||
|
"estimatedGas": "5208",
|
||||||
|
"history": [
|
||||||
|
{
|
||||||
|
"id": 3516145537630216,
|
||||||
|
"time": 1512615655535,
|
||||||
|
"status": "unapproved",
|
||||||
|
"metamaskNetworkId": "1",
|
||||||
|
"txParams": {
|
||||||
|
"from": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
|
"to": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
|
"value": "0x16345785d8a0000",
|
||||||
|
"gasPrice": "0xe6f7cec00",
|
||||||
|
"gas": "0x7b0c"
|
||||||
|
},
|
||||||
|
"gasPriceSpecified": false,
|
||||||
|
"gasLimitSpecified": false,
|
||||||
|
"estimatedGas": "5208"
|
||||||
|
},
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"op": "replace",
|
||||||
|
"path": "/txParams/gasPrice",
|
||||||
|
"value": "0xc1b710800",
|
||||||
|
"note": "confTx: user approved transaction"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"op": "replace",
|
||||||
|
"path": "/status",
|
||||||
|
"value": "approved",
|
||||||
|
"note": "txStateManager: setting status to approved"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"op": "add",
|
||||||
|
"path": "/txParams/nonce",
|
||||||
|
"value": "0x35",
|
||||||
|
"note": "transactions#approveTransaction"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"op": "add",
|
||||||
|
"path": "/nonceDetails",
|
||||||
|
"value": {
|
||||||
|
"params": {
|
||||||
|
"highestLocalNonce": 53,
|
||||||
|
"highestSuggested": 53,
|
||||||
|
"nextNetworkNonce": 53
|
||||||
|
},
|
||||||
|
"local": {
|
||||||
|
"name": "local",
|
||||||
|
"nonce": 53,
|
||||||
|
"details": {
|
||||||
|
"startPoint": 53,
|
||||||
|
"highest": 53
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"network": {
|
||||||
|
"name": "network",
|
||||||
|
"nonce": 53,
|
||||||
|
"details": {
|
||||||
|
"baseCount": 53
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"op": "add",
|
||||||
|
"path": "/txParams/chainId",
|
||||||
|
"value": "0x1",
|
||||||
|
"note": "txStateManager: setting status to signed"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"op": "replace",
|
||||||
|
"path": "/status",
|
||||||
|
"value": "signed"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"op": "add",
|
||||||
|
"path": "/rawTx",
|
||||||
|
"value": "0xf86c35850c1b710800827b0c94fdea65c8e26263f6d9a1b5de9555d2931a33b82588016345785d8a00008026a0f5142ba79a13ca7ec65548953017edafb217803244bbf9821d9ad077d89921e9a03afcb614169c90be9905d5b469d06984825c76675d3a535937cdb8f2ad1c0a95",
|
||||||
|
"note": "transactions#publishTransaction"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"op": "add",
|
||||||
|
"path": "/hash",
|
||||||
|
"value": "0x7ce19c0d128ca11293b44a4e6d3cc9063665c00ea8c8eb400f548e132c147353",
|
||||||
|
"note": "transactions#setTxHash"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"op": "replace",
|
||||||
|
"path": "/status",
|
||||||
|
"value": "submitted",
|
||||||
|
"note": "txStateManager: setting status to submitted"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"op": "add",
|
||||||
|
"path": "/firstRetryBlockNumber",
|
||||||
|
"value": "0x478ab3",
|
||||||
|
"note": "transactions/pending-tx-tracker#event: tx:block-update"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"nonceDetails": {
|
||||||
|
"params": {
|
||||||
|
"highestLocalNonce": 53,
|
||||||
|
"highestSuggested": 53,
|
||||||
|
"nextNetworkNonce": 53
|
||||||
|
},
|
||||||
|
"local": {
|
||||||
|
"name": "local",
|
||||||
|
"nonce": 53,
|
||||||
|
"details": {
|
||||||
|
"startPoint": 53,
|
||||||
|
"highest": 53
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"network": {
|
||||||
|
"name": "network",
|
||||||
|
"nonce": 53,
|
||||||
|
"details": {
|
||||||
|
"baseCount": 53
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rawTx": "0xf86c35850c1b710800827b0c94fdea65c8e26263f6d9a1b5de9555d2931a33b82588016345785d8a00008026a0f5142ba79a13ca7ec65548953017edafb217803244bbf9821d9ad077d89921e9a03afcb614169c90be9905d5b469d06984825c76675d3a535937cdb8f2ad1c0a95",
|
||||||
|
"hash": "0x7ce19c0d128ca11293b44a4e6d3cc9063665c00ea8c8eb400f548e132c147353",
|
||||||
|
"firstRetryBlockNumber": "0x478ab3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3516145537630211,
|
||||||
|
"time": 1512613432658,
|
||||||
|
"status": "confirmed",
|
||||||
|
"metamaskNetworkId": "1",
|
||||||
|
"txParams": {
|
||||||
|
"from": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
|
"to": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
|
"value": "0x16345785d8a0000",
|
||||||
|
"gasPrice": "0xba43b7400",
|
||||||
|
"gas": "0x7b0c",
|
||||||
|
"nonce": "0x34",
|
||||||
|
"chainId": "0x1"
|
||||||
|
},
|
||||||
|
"gasPriceSpecified": false,
|
||||||
|
"gasLimitSpecified": false,
|
||||||
|
"estimatedGas": "5208",
|
||||||
|
"history": [
|
||||||
|
{
|
||||||
|
"id": 3516145537630211,
|
||||||
|
"time": 1512613432658,
|
||||||
|
"status": "unapproved",
|
||||||
|
"metamaskNetworkId": "1",
|
||||||
|
"txParams": {
|
||||||
|
"from": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
|
"to": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
|
"value": "0x16345785d8a0000",
|
||||||
|
"gasPrice": "0xdf8475800",
|
||||||
|
"gas": "0x7b0c"
|
||||||
|
},
|
||||||
|
"gasPriceSpecified": false,
|
||||||
|
"gasLimitSpecified": false,
|
||||||
|
"estimatedGas": "5208"
|
||||||
|
},
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"op": "replace",
|
||||||
|
"path": "/txParams/gasPrice",
|
||||||
|
"value": "0xba43b7400",
|
||||||
|
"note": "confTx: user approved transaction"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"op": "replace",
|
||||||
|
"path": "/status",
|
||||||
|
"value": "approved",
|
||||||
|
"note": "txStateManager: setting status to approved"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"op": "add",
|
||||||
|
"path": "/txParams/nonce",
|
||||||
|
"value": "0x34",
|
||||||
|
"note": "transactions#approveTransaction"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"op": "add",
|
||||||
|
"path": "/nonceDetails",
|
||||||
|
"value": {
|
||||||
|
"params": {
|
||||||
|
"highestLocalNonce": 52,
|
||||||
|
"highestSuggested": 52,
|
||||||
|
"nextNetworkNonce": 52
|
||||||
|
},
|
||||||
|
"local": {
|
||||||
|
"name": "local",
|
||||||
|
"nonce": 52,
|
||||||
|
"details": {
|
||||||
|
"startPoint": 52,
|
||||||
|
"highest": 52
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"network": {
|
||||||
|
"name": "network",
|
||||||
|
"nonce": 52,
|
||||||
|
"details": {
|
||||||
|
"baseCount": 52
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"op": "add",
|
||||||
|
"path": "/txParams/chainId",
|
||||||
|
"value": "0x1",
|
||||||
|
"note": "txStateManager: setting status to signed"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"op": "replace",
|
||||||
|
"path": "/status",
|
||||||
|
"value": "signed"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"op": "add",
|
||||||
|
"path": "/rawTx",
|
||||||
|
"value": "0xf86c34850ba43b7400827b0c94fdea65c8e26263f6d9a1b5de9555d2931a33b82588016345785d8a00008026a073a4afdb8e8ad32b0cf9039af56c66baffd60d30e75cee5c1b783208824eafb8a0021ca6c1714a2c71281333ab77f776d3514348ab77967280fca8a5b4be44285e",
|
||||||
|
"note": "transactions#publishTransaction"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"op": "add",
|
||||||
|
"path": "/hash",
|
||||||
|
"value": "0x5c98409883fdfd3cd24058a83b91470da6c40ffae41a40eb90d7dee0b837d26d",
|
||||||
|
"note": "transactions#setTxHash"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"op": "replace",
|
||||||
|
"path": "/status",
|
||||||
|
"value": "submitted",
|
||||||
|
"note": "txStateManager: setting status to submitted"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"op": "add",
|
||||||
|
"path": "/firstRetryBlockNumber",
|
||||||
|
"value": "0x478a2c",
|
||||||
|
"note": "transactions/pending-tx-tracker#event: tx:block-update"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"op": "replace",
|
||||||
|
"path": "/status",
|
||||||
|
"value": "confirmed",
|
||||||
|
"note": "txStateManager: setting status to confirmed"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"nonceDetails": {
|
||||||
|
"params": {
|
||||||
|
"highestLocalNonce": 52,
|
||||||
|
"highestSuggested": 52,
|
||||||
|
"nextNetworkNonce": 52
|
||||||
|
},
|
||||||
|
"local": {
|
||||||
|
"name": "local",
|
||||||
|
"nonce": 52,
|
||||||
|
"details": {
|
||||||
|
"startPoint": 52,
|
||||||
|
"highest": 52
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"network": {
|
||||||
|
"name": "network",
|
||||||
|
"nonce": 52,
|
||||||
|
"details": {
|
||||||
|
"baseCount": 52
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rawTx": "0xf86c34850ba43b7400827b0c94fdea65c8e26263f6d9a1b5de9555d2931a33b82588016345785d8a00008026a073a4afdb8e8ad32b0cf9039af56c66baffd60d30e75cee5c1b783208824eafb8a0021ca6c1714a2c71281333ab77f776d3514348ab77967280fca8a5b4be44285e",
|
||||||
|
"hash": "0x5c98409883fdfd3cd24058a83b91470da6c40ffae41a40eb90d7dee0b837d26d",
|
||||||
|
"firstRetryBlockNumber": "0x478a2c"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3516145537630210,
|
||||||
|
"time": 1512612826136,
|
||||||
|
"status": "confirmed",
|
||||||
|
"metamaskNetworkId": "1",
|
||||||
|
"txParams": {
|
||||||
|
"from": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
|
"to": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
|
"value": "0x16345785d8a0000",
|
||||||
|
"gasPrice": "0xa7a358200",
|
||||||
|
"gas": "0x7b0c",
|
||||||
|
"nonce": "0x33",
|
||||||
|
"chainId": "0x1"
|
||||||
|
},
|
||||||
|
"gasPriceSpecified": false,
|
||||||
|
"gasLimitSpecified": false,
|
||||||
|
"estimatedGas": "5208",
|
||||||
|
"history": [
|
||||||
|
{
|
||||||
|
"id": 3516145537630210,
|
||||||
|
"time": 1512612826136,
|
||||||
|
"status": "unapproved",
|
||||||
|
"metamaskNetworkId": "1",
|
||||||
|
"txParams": {
|
||||||
|
"from": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
|
"to": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
|
"value": "0x16345785d8a0000",
|
||||||
|
"gasPrice": "0xba43b7400",
|
||||||
|
"gas": "0x7b0c"
|
||||||
|
},
|
||||||
|
"gasPriceSpecified": false,
|
||||||
|
"gasLimitSpecified": false,
|
||||||
|
"estimatedGas": "5208"
|
||||||
|
},
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"op": "replace",
|
||||||
|
"path": "/txParams/gasPrice",
|
||||||
|
"value": "0xa7a358200",
|
||||||
|
"note": "confTx: user approved transaction"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"op": "replace",
|
||||||
|
"path": "/status",
|
||||||
|
"value": "approved",
|
||||||
|
"note": "txStateManager: setting status to approved"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"op": "add",
|
||||||
|
"path": "/txParams/nonce",
|
||||||
|
"value": "0x33",
|
||||||
|
"note": "transactions#approveTransaction"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"op": "add",
|
||||||
|
"path": "/nonceDetails",
|
||||||
|
"value": {
|
||||||
|
"params": {
|
||||||
|
"highestLocalNonce": 0,
|
||||||
|
"highestSuggested": 51,
|
||||||
|
"nextNetworkNonce": 51
|
||||||
|
},
|
||||||
|
"local": {
|
||||||
|
"name": "local",
|
||||||
|
"nonce": 51,
|
||||||
|
"details": {
|
||||||
|
"startPoint": 51,
|
||||||
|
"highest": 51
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"network": {
|
||||||
|
"name": "network",
|
||||||
|
"nonce": 51,
|
||||||
|
"details": {
|
||||||
|
"baseCount": 51
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"op": "add",
|
||||||
|
"path": "/txParams/chainId",
|
||||||
|
"value": "0x1",
|
||||||
|
"note": "txStateManager: setting status to signed"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"op": "replace",
|
||||||
|
"path": "/status",
|
||||||
|
"value": "signed"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"op": "add",
|
||||||
|
"path": "/rawTx",
|
||||||
|
"value": "0xf86c33850a7a358200827b0c94fdea65c8e26263f6d9a1b5de9555d2931a33b82588016345785d8a00008026a0021a8cd6c10208cc593e22af53637e5d127cee5cc6f9443a3e758a02afff1d7ca025f7420e974d3f2c668c165040987c72543a8e709bfea3528a62836a6ced9ce8",
|
||||||
|
"note": "transactions#publishTransaction"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"op": "add",
|
||||||
|
"path": "/hash",
|
||||||
|
"value": "0x289772800898bc9cd414530d8581c0da257a9055e4aaaa6d10d92d700bfbd044",
|
||||||
|
"note": "transactions#setTxHash"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"op": "replace",
|
||||||
|
"path": "/status",
|
||||||
|
"value": "submitted",
|
||||||
|
"note": "txStateManager: setting status to submitted"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"op": "add",
|
||||||
|
"path": "/firstRetryBlockNumber",
|
||||||
|
"value": "0x478a04",
|
||||||
|
"note": "transactions/pending-tx-tracker#event: tx:block-update"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"op": "replace",
|
||||||
|
"path": "/status",
|
||||||
|
"value": "confirmed",
|
||||||
|
"note": "txStateManager: setting status to confirmed"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"nonceDetails": {
|
||||||
|
"params": {
|
||||||
|
"highestLocalNonce": 0,
|
||||||
|
"highestSuggested": 51,
|
||||||
|
"nextNetworkNonce": 51
|
||||||
|
},
|
||||||
|
"local": {
|
||||||
|
"name": "local",
|
||||||
|
"nonce": 51,
|
||||||
|
"details": {
|
||||||
|
"startPoint": 51,
|
||||||
|
"highest": 51
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"network": {
|
||||||
|
"name": "network",
|
||||||
|
"nonce": 51,
|
||||||
|
"details": {
|
||||||
|
"baseCount": 51
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rawTx": "0xf86c33850a7a358200827b0c94fdea65c8e26263f6d9a1b5de9555d2931a33b82588016345785d8a00008026a0021a8cd6c10208cc593e22af53637e5d127cee5cc6f9443a3e758a02afff1d7ca025f7420e974d3f2c668c165040987c72543a8e709bfea3528a62836a6ced9ce8",
|
||||||
|
"hash": "0x289772800898bc9cd414530d8581c0da257a9055e4aaaa6d10d92d700bfbd044",
|
||||||
|
"firstRetryBlockNumber": "0x478a04"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3516145537630209,
|
||||||
|
"time": 1512612809252,
|
||||||
|
"status": "failed",
|
||||||
|
"metamaskNetworkId": "1",
|
||||||
|
"txParams": {
|
||||||
|
"from": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
|
"to": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
|
"value": "0x16345785d8a0000",
|
||||||
|
"gasPrice": "0x77359400",
|
||||||
|
"gas": "0x7b0c",
|
||||||
|
"nonce": "0x33",
|
||||||
|
"chainId": "0x1"
|
||||||
|
},
|
||||||
|
"gasPriceSpecified": false,
|
||||||
|
"gasLimitSpecified": false,
|
||||||
|
"estimatedGas": "5208",
|
||||||
|
"history": [
|
||||||
|
{
|
||||||
|
"id": 3516145537630209,
|
||||||
|
"time": 1512612809252,
|
||||||
|
"status": "unapproved",
|
||||||
|
"metamaskNetworkId": "1",
|
||||||
|
"txParams": {
|
||||||
|
"from": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
|
"to": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
|
"value": "0x16345785d8a0000",
|
||||||
|
"gasPrice": "0xba43b7400",
|
||||||
|
"gas": "0x7b0c"
|
||||||
|
},
|
||||||
|
"gasPriceSpecified": false,
|
||||||
|
"gasLimitSpecified": false,
|
||||||
|
"estimatedGas": "5208"
|
||||||
|
},
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"op": "replace",
|
||||||
|
"path": "/txParams/gasPrice",
|
||||||
|
"value": "0x77359400",
|
||||||
|
"note": "confTx: user approved transaction"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"op": "replace",
|
||||||
|
"path": "/status",
|
||||||
|
"value": "approved",
|
||||||
|
"note": "txStateManager: setting status to approved"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"op": "add",
|
||||||
|
"path": "/txParams/nonce",
|
||||||
|
"value": "0x33",
|
||||||
|
"note": "transactions#approveTransaction"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"op": "add",
|
||||||
|
"path": "/nonceDetails",
|
||||||
|
"value": {
|
||||||
|
"params": {
|
||||||
|
"highestLocalNonce": 0,
|
||||||
|
"highestSuggested": 51,
|
||||||
|
"nextNetworkNonce": 51
|
||||||
|
},
|
||||||
|
"local": {
|
||||||
|
"name": "local",
|
||||||
|
"nonce": 51,
|
||||||
|
"details": {
|
||||||
|
"startPoint": 51,
|
||||||
|
"highest": 51
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"network": {
|
||||||
|
"name": "network",
|
||||||
|
"nonce": 51,
|
||||||
|
"details": {
|
||||||
|
"baseCount": 51
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"op": "add",
|
||||||
|
"path": "/txParams/chainId",
|
||||||
|
"value": "0x1",
|
||||||
|
"note": "txStateManager: setting status to signed"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"op": "replace",
|
||||||
|
"path": "/status",
|
||||||
|
"value": "signed"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"op": "add",
|
||||||
|
"path": "/rawTx",
|
||||||
|
"value": "0xf86b338477359400827b0c94fdea65c8e26263f6d9a1b5de9555d2931a33b82588016345785d8a00008025a098624a27ae79b2b1adc63b913850f266a920cb9d93e6588b8df9b8883eb1b323a00cc6fd855723a234f4f93b48caf7a7659366d09e5c5887f0a4c2e5fa68012cd7",
|
||||||
|
"note": "transactions#publishTransaction"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"op": "add",
|
||||||
|
"path": "/err",
|
||||||
|
"value": {
|
||||||
|
"message": "Error: [ethjs-rpc] rpc error with payload {\"id\":7801900228852,\"jsonrpc\":\"2.0\",\"params\":[\"0xf86b338477359400827b0c94fdea65c8e26263f6d9a1b5de9555d2931a33b82588016345785d8a00008025a098624a27ae79b2b1adc63b913850f266a920cb9d93e6588b8df9b8883eb1b323a00cc6fd855723a234f4f93b48caf7a7659366d09e5c5887f0a4c2e5fa68012cd7\"],\"method\":\"eth_sendRawTransaction\"} Error: transaction underpriced",
|
||||||
|
"stack": "Error: [ethjs-rpc] rpc error with payload {\"id\":7801900228852,\"jsonrpc\":\"2.0\",\"params\":[\"0xf86b338477359400827b0c94fdea65c8e26263f6d9a1b5de9555d2931a33b82588016345785d8a00008025a098624a27ae79b2b1adc63b913850f266a920cb9d93e6588b8df9b8883eb1b323a00cc6fd855723a234f4f93b48caf7a7659366d09e5c5887f0a4c2e5fa68012cd7\"],\"method\":\"eth_sendRawTransaction\"} Error: transaction underpriced\n at chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:60327:26\n at chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:88030:9\n at chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:16678:16\n at replenish (chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:16522:25)\n at iterateeCallback (chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:16512:17)\n at chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:16694:16\n at resultObj.id (chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:88012:9)\n at chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:16813:16\n at replenish (chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:16527:17)\n at iterateeCallback (chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:16512:17)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"op": "replace",
|
||||||
|
"path": "/status",
|
||||||
|
"value": "failed",
|
||||||
|
"note": "txStateManager: setting status to failed"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"nonceDetails": {
|
||||||
|
"params": {
|
||||||
|
"highestLocalNonce": 0,
|
||||||
|
"highestSuggested": 51,
|
||||||
|
"nextNetworkNonce": 51
|
||||||
|
},
|
||||||
|
"local": {
|
||||||
|
"name": "local",
|
||||||
|
"nonce": 51,
|
||||||
|
"details": {
|
||||||
|
"startPoint": 51,
|
||||||
|
"highest": 51
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"network": {
|
||||||
|
"name": "network",
|
||||||
|
"nonce": 51,
|
||||||
|
"details": {
|
||||||
|
"baseCount": 51
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rawTx": "0xf86b338477359400827b0c94fdea65c8e26263f6d9a1b5de9555d2931a33b82588016345785d8a00008025a098624a27ae79b2b1adc63b913850f266a920cb9d93e6588b8df9b8883eb1b323a00cc6fd855723a234f4f93b48caf7a7659366d09e5c5887f0a4c2e5fa68012cd7",
|
||||||
|
"err": {
|
||||||
|
"message": "Error: [ethjs-rpc] rpc error with payload {\"id\":7801900228852,\"jsonrpc\":\"2.0\",\"params\":[\"0xf86b338477359400827b0c94fdea65c8e26263f6d9a1b5de9555d2931a33b82588016345785d8a00008025a098624a27ae79b2b1adc63b913850f266a920cb9d93e6588b8df9b8883eb1b323a00cc6fd855723a234f4f93b48caf7a7659366d09e5c5887f0a4c2e5fa68012cd7\"],\"method\":\"eth_sendRawTransaction\"} Error: transaction underpriced",
|
||||||
|
"stack": "Error: [ethjs-rpc] rpc error with payload {\"id\":7801900228852,\"jsonrpc\":\"2.0\",\"params\":[\"0xf86b338477359400827b0c94fdea65c8e26263f6d9a1b5de9555d2931a33b82588016345785d8a00008025a098624a27ae79b2b1adc63b913850f266a920cb9d93e6588b8df9b8883eb1b323a00cc6fd855723a234f4f93b48caf7a7659366d09e5c5887f0a4c2e5fa68012cd7\"],\"method\":\"eth_sendRawTransaction\"} Error: transaction underpriced\n at chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:60327:26\n at chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:88030:9\n at chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:16678:16\n at replenish (chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:16522:25)\n at iterateeCallback (chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:16512:17)\n at chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:16694:16\n at resultObj.id (chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:88012:9)\n at chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:16813:16\n at replenish (chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:16527:17)\n at iterateeCallback (chrome-extension://ebjbdknjcgcbchkagneicjfpneaghdhb/scripts/background.js:16512:17)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"unapprovedMsgs": {},
|
||||||
|
"unapprovedMsgCount": 0,
|
||||||
|
"unapprovedPersonalMsgs": {},
|
||||||
|
"unapprovedPersonalMsgCount": 0,
|
||||||
|
"unapprovedTypedMessages": {},
|
||||||
|
"unapprovedTypedMessagesCount": 0,
|
||||||
|
"keyringTypes": [
|
||||||
|
"Simple Key Pair",
|
||||||
|
"HD Key Tree"
|
||||||
|
],
|
||||||
|
"keyrings": [
|
||||||
|
{
|
||||||
|
"type": "HD Key Tree",
|
||||||
|
"accounts": [
|
||||||
|
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"computedBalances": {},
|
||||||
|
"currentAccountTab": "history",
|
||||||
|
"tokens": [
|
||||||
|
{
|
||||||
|
"address": "0x0d8775f648430679a709e98d2b0cb6250d2887ef",
|
||||||
|
"symbol": "BAT",
|
||||||
|
"decimals": "18"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"selectedAddress": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
|
"currentCurrency": "usd",
|
||||||
|
"conversionRate": 418.62,
|
||||||
|
"conversionDate": 1512615622,
|
||||||
|
"infuraNetworkStatus": {
|
||||||
|
"mainnet": "ok",
|
||||||
|
"ropsten": "ok",
|
||||||
|
"kovan": "ok",
|
||||||
|
"rinkeby": "ok"
|
||||||
|
},
|
||||||
|
"shapeShiftTxList": [],
|
||||||
|
"lostAccounts": []
|
||||||
|
},
|
||||||
|
"appState": {
|
||||||
|
"shouldClose": true,
|
||||||
|
"menuOpen": false,
|
||||||
|
"currentView": {
|
||||||
|
"name": "accountDetail",
|
||||||
|
"context": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"
|
||||||
|
},
|
||||||
|
"accountDetail": {
|
||||||
|
"subview": "transactions",
|
||||||
|
"accountExport": "none",
|
||||||
|
"privateKey": ""
|
||||||
|
},
|
||||||
|
"transForward": false,
|
||||||
|
"isLoading": false,
|
||||||
|
"warning": null,
|
||||||
|
"forgottenPassword": false,
|
||||||
|
"scrollToBottom": false
|
||||||
|
},
|
||||||
|
"identities": {},
|
||||||
|
"version": "3.12.1",
|
||||||
|
"platform": {
|
||||||
|
"arch": "x86-64",
|
||||||
|
"nacl_arch": "x86-64",
|
||||||
|
"os": "mac"
|
||||||
|
},
|
||||||
|
"browser": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36"
|
||||||
|
}
|
@ -38,6 +38,16 @@ PendingTx.prototype.render = function () {
|
|||||||
const txMeta = this.gatherTxMeta()
|
const txMeta = this.gatherTxMeta()
|
||||||
const txParams = txMeta.txParams || {}
|
const txParams = txMeta.txParams || {}
|
||||||
|
|
||||||
|
// Allow retry txs
|
||||||
|
const { lastGasPrice } = txMeta
|
||||||
|
let forceGasMin
|
||||||
|
if (lastGasPrice) {
|
||||||
|
const stripped = ethUtil.stripHexPrefix(lastGasPrice)
|
||||||
|
const lastGas = new BN(stripped, 16)
|
||||||
|
const priceBump = lastGas.divn('10')
|
||||||
|
forceGasMin = lastGas.add(priceBump)
|
||||||
|
}
|
||||||
|
|
||||||
// Account Details
|
// Account Details
|
||||||
const address = txParams.from || props.selectedAddress
|
const address = txParams.from || props.selectedAddress
|
||||||
const identity = props.identities[address] || { address: address }
|
const identity = props.identities[address] || { address: address }
|
||||||
@ -199,7 +209,7 @@ PendingTx.prototype.render = function () {
|
|||||||
precision: 9,
|
precision: 9,
|
||||||
scale: 9,
|
scale: 9,
|
||||||
suffix: 'GWEI',
|
suffix: 'GWEI',
|
||||||
min: MIN_GAS_PRICE_BN,
|
min: forceGasMin || MIN_GAS_PRICE_BN,
|
||||||
style: {
|
style: {
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
top: '5px',
|
top: '5px',
|
||||||
|
@ -119,7 +119,7 @@ ConfigScreen.prototype.render = function () {
|
|||||||
if (err) {
|
if (err) {
|
||||||
state.dispatch(actions.displayWarning('Error in retrieving state logs.'))
|
state.dispatch(actions.displayWarning('Error in retrieving state logs.'))
|
||||||
} else {
|
} else {
|
||||||
exportAsFile('MetaMask State Logs', result)
|
exportAsFile('MetaMask State Logs.json', result)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
@ -107,6 +107,10 @@ button:not([disabled]):active, input[type="submit"]:not([disabled]):active {
|
|||||||
transform: scale(0.95);
|
transform: scale(0.95);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.grow-on-hover:hover {
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: inherit;
|
color: inherit;
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
"dist": "npm run dist:clear && npm install && gulp dist",
|
"dist": "npm run dist:clear && npm install && gulp dist",
|
||||||
"dist:clear": "rm -rf node_modules/eth-contract-metadata && rm -rf node_modules/eth-phishing-detect",
|
"dist:clear": "rm -rf node_modules/eth-contract-metadata && rm -rf node_modules/eth-phishing-detect",
|
||||||
"test": "npm run lint && npm run test:coverage && npm run test:integration",
|
"test": "npm run lint && npm run test:coverage && npm run test:integration",
|
||||||
"test:unit": "METAMASK_ENV=test mocha --compilers js:babel-core/register --require test/helper.js --recursive \"test/unit/**/*.js\"",
|
"test:unit": "METAMASK_ENV=test mocha --exit --compilers js:babel-core/register --require test/helper.js --recursive \"test/unit/**/*.js\"",
|
||||||
"test:single": "METAMASK_ENV=test mocha --require test/helper.js",
|
"test:single": "METAMASK_ENV=test mocha --require test/helper.js",
|
||||||
"test:integration": "gulp build:scss && npm run test:flat && npm run test:mascara",
|
"test:integration": "gulp build:scss && npm run test:flat && npm run test:mascara",
|
||||||
"test:coverage": "nyc npm run test:unit && npm run test:coveralls-upload",
|
"test:coverage": "nyc npm run test:unit && npm run test:coveralls-upload",
|
||||||
@ -80,6 +80,7 @@
|
|||||||
"eth-contract-metadata": "^1.1.5",
|
"eth-contract-metadata": "^1.1.5",
|
||||||
"eth-hd-keyring": "^1.2.1",
|
"eth-hd-keyring": "^1.2.1",
|
||||||
"eth-json-rpc-filters": "^1.2.4",
|
"eth-json-rpc-filters": "^1.2.4",
|
||||||
|
"eth-json-rpc-infura": "^1.0.2",
|
||||||
"eth-keyring-controller": "^2.1.2",
|
"eth-keyring-controller": "^2.1.2",
|
||||||
"eth-phishing-detect": "^1.1.4",
|
"eth-phishing-detect": "^1.1.4",
|
||||||
"eth-query": "^2.1.2",
|
"eth-query": "^2.1.2",
|
||||||
@ -168,7 +169,7 @@
|
|||||||
"valid-url": "^1.0.9",
|
"valid-url": "^1.0.9",
|
||||||
"vreme": "^3.0.2",
|
"vreme": "^3.0.2",
|
||||||
"web3": "^0.20.1",
|
"web3": "^0.20.1",
|
||||||
"web3-provider-engine": "^13.3.2",
|
"web3-provider-engine": "^13.4.0",
|
||||||
"web3-stream-provider": "^3.0.1",
|
"web3-stream-provider": "^3.0.1",
|
||||||
"xtend": "^4.0.1"
|
"xtend": "^4.0.1"
|
||||||
},
|
},
|
||||||
|
48
test/unit/preferences-controller-test.js
Normal file
48
test/unit/preferences-controller-test.js
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
const assert = require('assert')
|
||||||
|
const PreferencesController = require('../../app/scripts/controllers/preferences')
|
||||||
|
|
||||||
|
describe('preferences controller', function () {
|
||||||
|
let preferencesController
|
||||||
|
|
||||||
|
before(() => {
|
||||||
|
preferencesController = new PreferencesController()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('addToken', function () {
|
||||||
|
it('should add that token to its state', async function () {
|
||||||
|
const address = '0xabcdef1234567'
|
||||||
|
const symbol = 'ABBR'
|
||||||
|
const decimals = 5
|
||||||
|
|
||||||
|
await preferencesController.addToken(address, symbol, decimals)
|
||||||
|
|
||||||
|
const tokens = preferencesController.getTokens()
|
||||||
|
assert.equal(tokens.length, 1, 'one token added')
|
||||||
|
|
||||||
|
const added = tokens[0]
|
||||||
|
assert.equal(added.address, address, 'set address correctly')
|
||||||
|
assert.equal(added.symbol, symbol, 'set symbol correctly')
|
||||||
|
assert.equal(added.decimals, decimals, 'set decimals correctly')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should allow updating a token value', async function () {
|
||||||
|
const address = '0xabcdef1234567'
|
||||||
|
const symbol = 'ABBR'
|
||||||
|
const decimals = 5
|
||||||
|
|
||||||
|
await preferencesController.addToken(address, symbol, decimals)
|
||||||
|
|
||||||
|
const newDecimals = 6
|
||||||
|
await preferencesController.addToken(address, symbol, newDecimals)
|
||||||
|
|
||||||
|
const tokens = preferencesController.getTokens()
|
||||||
|
assert.equal(tokens.length, 1, 'one token added')
|
||||||
|
|
||||||
|
const added = tokens[0]
|
||||||
|
assert.equal(added.address, address, 'set address correctly')
|
||||||
|
assert.equal(added.symbol, symbol, 'set symbol correctly')
|
||||||
|
assert.equal(added.decimals, newDecimals, 'updated decimals correctly')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
@ -245,6 +245,8 @@ var actions = {
|
|||||||
setFeatureFlag,
|
setFeatureFlag,
|
||||||
updateFeatureFlags,
|
updateFeatureFlags,
|
||||||
UPDATE_FEATURE_FLAGS: 'UPDATE_FEATURE_FLAGS',
|
UPDATE_FEATURE_FLAGS: 'UPDATE_FEATURE_FLAGS',
|
||||||
|
|
||||||
|
retryTransaction,
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = actions
|
module.exports = actions
|
||||||
@ -1138,6 +1140,19 @@ function markAccountsFound () {
|
|||||||
return callBackgroundThenUpdate(background.markAccountsFound)
|
return callBackgroundThenUpdate(background.markAccountsFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function retryTransaction (txId) {
|
||||||
|
log.debug(`background.retryTransaction`)
|
||||||
|
return (dispatch) => {
|
||||||
|
background.retryTransaction(txId, (err, newState) => {
|
||||||
|
if (err) {
|
||||||
|
return dispatch(actions.displayWarning(err.message))
|
||||||
|
}
|
||||||
|
dispatch(actions.updateMetamaskState(newState))
|
||||||
|
dispatch(actions.viewPendingTx(txId))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// config
|
// config
|
||||||
//
|
//
|
||||||
|
@ -40,7 +40,7 @@ CoinbaseForm.prototype.render = function () {
|
|||||||
}, 'Continue to Coinbase'),
|
}, 'Continue to Coinbase'),
|
||||||
|
|
||||||
h('button.btn-red', {
|
h('button.btn-red', {
|
||||||
onClick: () => props.dispatch(actions.backTobuyView(props.accounts.address)),
|
onClick: () => props.dispatch(actions.goHome()),
|
||||||
}, 'Cancel'),
|
}, 'Cancel'),
|
||||||
]),
|
]),
|
||||||
])
|
])
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
const Component = require('react').Component
|
const Component = require('react').Component
|
||||||
const h = require('react-hyperscript')
|
const h = require('react-hyperscript')
|
||||||
const inherits = require('util').inherits
|
const inherits = require('util').inherits
|
||||||
|
const connect = require('react-redux').connect
|
||||||
|
|
||||||
const EthBalance = require('./eth-balance')
|
const EthBalance = require('./eth-balance')
|
||||||
const addressSummary = require('../util').addressSummary
|
const addressSummary = require('../util').addressSummary
|
||||||
@ -9,18 +10,33 @@ const CopyButton = require('./copyButton')
|
|||||||
const vreme = new (require('vreme'))()
|
const vreme = new (require('vreme'))()
|
||||||
const Tooltip = require('./tooltip')
|
const Tooltip = require('./tooltip')
|
||||||
const numberToBN = require('number-to-bn')
|
const numberToBN = require('number-to-bn')
|
||||||
|
const actions = require('../actions')
|
||||||
|
|
||||||
const TransactionIcon = require('./transaction-list-item-icon')
|
const TransactionIcon = require('./transaction-list-item-icon')
|
||||||
const ShiftListItem = require('./shift-list-item')
|
const ShiftListItem = require('./shift-list-item')
|
||||||
module.exports = TransactionListItem
|
|
||||||
|
const mapDispatchToProps = dispatch => {
|
||||||
|
return {
|
||||||
|
retryTransaction: transactionId => dispatch(actions.retryTransaction(transactionId)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = connect(null, mapDispatchToProps)(TransactionListItem)
|
||||||
|
|
||||||
inherits(TransactionListItem, Component)
|
inherits(TransactionListItem, Component)
|
||||||
function TransactionListItem () {
|
function TransactionListItem () {
|
||||||
Component.call(this)
|
Component.call(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TransactionListItem.prototype.showRetryButton = function () {
|
||||||
|
const { transaction = {} } = this.props
|
||||||
|
const { status, time } = transaction
|
||||||
|
return status === 'submitted' && Date.now() - time > 30000
|
||||||
|
}
|
||||||
|
|
||||||
TransactionListItem.prototype.render = function () {
|
TransactionListItem.prototype.render = function () {
|
||||||
const { transaction, network, conversionRate, currentCurrency } = this.props
|
const { transaction, network, conversionRate, currentCurrency } = this.props
|
||||||
|
const { status } = transaction
|
||||||
if (transaction.key === 'shapeshift') {
|
if (transaction.key === 'shapeshift') {
|
||||||
if (network === '1') return h(ShiftListItem, transaction)
|
if (network === '1') return h(ShiftListItem, transaction)
|
||||||
}
|
}
|
||||||
@ -32,7 +48,7 @@ TransactionListItem.prototype.render = function () {
|
|||||||
|
|
||||||
var isMsg = ('msgParams' in transaction)
|
var isMsg = ('msgParams' in transaction)
|
||||||
var isTx = ('txParams' in transaction)
|
var isTx = ('txParams' in transaction)
|
||||||
var isPending = transaction.status === 'unapproved'
|
var isPending = status === 'unapproved'
|
||||||
let txParams
|
let txParams
|
||||||
if (isTx) {
|
if (isTx) {
|
||||||
txParams = transaction.txParams
|
txParams = transaction.txParams
|
||||||
@ -44,7 +60,7 @@ TransactionListItem.prototype.render = function () {
|
|||||||
|
|
||||||
const isClickable = ('hash' in transaction && isLinkable) || isPending
|
const isClickable = ('hash' in transaction && isLinkable) || isPending
|
||||||
return (
|
return (
|
||||||
h(`.transaction-list-item.flex-row.flex-space-between${isClickable ? '.pointer' : ''}`, {
|
h('.transaction-list-item.flex-column', {
|
||||||
onClick: (event) => {
|
onClick: (event) => {
|
||||||
if (isPending) {
|
if (isPending) {
|
||||||
this.props.showTx(transaction.id)
|
this.props.showTx(transaction.id)
|
||||||
@ -56,51 +72,92 @@ TransactionListItem.prototype.render = function () {
|
|||||||
},
|
},
|
||||||
style: {
|
style: {
|
||||||
padding: '20px 0',
|
padding: '20px 0',
|
||||||
|
alignItems: 'center',
|
||||||
},
|
},
|
||||||
}, [
|
}, [
|
||||||
|
h(`.flex-row.flex-space-between${isClickable ? '.pointer' : ''}`, {
|
||||||
h('.identicon-wrapper.flex-column.flex-center.select-none', [
|
style: {
|
||||||
h(TransactionIcon, { txParams, transaction, isTx, isMsg }),
|
width: '100%',
|
||||||
]),
|
},
|
||||||
|
|
||||||
h(Tooltip, {
|
|
||||||
title: 'Transaction Number',
|
|
||||||
position: 'right',
|
|
||||||
}, [
|
}, [
|
||||||
h('span', {
|
h('.identicon-wrapper.flex-column.flex-center.select-none', [
|
||||||
|
h(TransactionIcon, { txParams, transaction, isTx, isMsg }),
|
||||||
|
]),
|
||||||
|
|
||||||
|
h(Tooltip, {
|
||||||
|
title: 'Transaction Number',
|
||||||
|
position: 'right',
|
||||||
|
}, [
|
||||||
|
h('span', {
|
||||||
|
style: {
|
||||||
|
display: 'flex',
|
||||||
|
cursor: 'normal',
|
||||||
|
flexDirection: 'column',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
padding: '10px',
|
||||||
|
},
|
||||||
|
}, nonce),
|
||||||
|
]),
|
||||||
|
|
||||||
|
h('.flex-column', {style: {width: '200px', overflow: 'hidden'}}, [
|
||||||
|
domainField(txParams),
|
||||||
|
h('div', date),
|
||||||
|
recipientField(txParams, transaction, isTx, isMsg),
|
||||||
|
]),
|
||||||
|
|
||||||
|
// Places a copy button if tx is successful, else places a placeholder empty div.
|
||||||
|
transaction.hash ? h(CopyButton, { value: transaction.hash }) : h('div', {style: { display: 'flex', alignItems: 'center', width: '26px' }}),
|
||||||
|
|
||||||
|
isTx ? h(EthBalance, {
|
||||||
|
value: txParams.value,
|
||||||
|
conversionRate,
|
||||||
|
currentCurrency,
|
||||||
|
width: '55px',
|
||||||
|
shorten: true,
|
||||||
|
showFiat: false,
|
||||||
|
style: {fontSize: '15px'},
|
||||||
|
}) : h('.flex-column'),
|
||||||
|
]),
|
||||||
|
|
||||||
|
this.showRetryButton() && h('.transition-list-item__retry.grow-on-hover', {
|
||||||
|
onClick: event => {
|
||||||
|
event.stopPropagation()
|
||||||
|
this.resubmit()
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
height: '22px',
|
||||||
|
borderRadius: '22px',
|
||||||
|
color: '#F9881B',
|
||||||
|
padding: '0 20px',
|
||||||
|
backgroundColor: '#FFE3C9',
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
fontSize: '8px',
|
||||||
|
cursor: 'pointer',
|
||||||
|
},
|
||||||
|
}, [
|
||||||
|
h('div', {
|
||||||
style: {
|
style: {
|
||||||
display: 'flex',
|
paddingRight: '2px',
|
||||||
cursor: 'normal',
|
|
||||||
flexDirection: 'column',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
padding: '10px',
|
|
||||||
},
|
},
|
||||||
}, nonce),
|
}, 'Taking too long?'),
|
||||||
|
h('div', {
|
||||||
|
style: {
|
||||||
|
textDecoration: 'underline',
|
||||||
|
},
|
||||||
|
}, 'Retry with a higher gas price here'),
|
||||||
]),
|
]),
|
||||||
|
|
||||||
h('.flex-column', {style: {width: '200px', overflow: 'hidden'}}, [
|
|
||||||
domainField(txParams),
|
|
||||||
h('div', date),
|
|
||||||
recipientField(txParams, transaction, isTx, isMsg),
|
|
||||||
]),
|
|
||||||
|
|
||||||
// Places a copy button if tx is successful, else places a placeholder empty div.
|
|
||||||
transaction.hash ? h(CopyButton, { value: transaction.hash }) : h('div', {style: { display: 'flex', alignItems: 'center', width: '26px' }}),
|
|
||||||
|
|
||||||
isTx ? h(EthBalance, {
|
|
||||||
value: txParams.value,
|
|
||||||
conversionRate,
|
|
||||||
currentCurrency,
|
|
||||||
width: '55px',
|
|
||||||
shorten: true,
|
|
||||||
showFiat: false,
|
|
||||||
style: {fontSize: '15px'},
|
|
||||||
}) : h('.flex-column'),
|
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TransactionListItem.prototype.resubmit = function () {
|
||||||
|
const { transaction } = this.props
|
||||||
|
this.props.retryTransaction(transaction.id)
|
||||||
|
}
|
||||||
|
|
||||||
function domainField (txParams) {
|
function domainField (txParams) {
|
||||||
return h('div', {
|
return h('div', {
|
||||||
style: {
|
style: {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user