Merge pull request #237 from poanetwork/multiple-hardware
(Feature) Multiple Ledger accounts for one session
This commit is contained in:
commit
c988d7cce8
|
@ -179,7 +179,7 @@ jobs:
|
||||||
key: dependency-cache-{{ .Revision }}
|
key: dependency-cache-{{ .Revision }}
|
||||||
- run:
|
- run:
|
||||||
name: Test
|
name: Test
|
||||||
command: sudo npm install -g npm@6 && npm audit
|
command: sudo npm install -g npm@6.4.1 && npm audit
|
||||||
|
|
||||||
test-e2e-chrome:
|
test-e2e-chrome:
|
||||||
docker:
|
docker:
|
||||||
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
const ObservableStore = require('obs-store')
|
||||||
|
const extend = require('xtend')
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} CachedBalancesOptions
|
||||||
|
* @property {Object} accountTracker An {@code AccountTracker} reference
|
||||||
|
* @property {Function} getNetwork A function to get the current network
|
||||||
|
* @property {Object} initState The initial controller state
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Background controller responsible for maintaining
|
||||||
|
* a cache of account balances in local storage
|
||||||
|
*/
|
||||||
|
class CachedBalancesController {
|
||||||
|
/**
|
||||||
|
* Creates a new controller instance
|
||||||
|
*
|
||||||
|
* @param {CachedBalancesOptions} [opts] Controller configuration parameters
|
||||||
|
*/
|
||||||
|
constructor (opts = {}) {
|
||||||
|
const { accountTracker, getNetwork } = opts
|
||||||
|
|
||||||
|
this.accountTracker = accountTracker
|
||||||
|
this.getNetwork = getNetwork
|
||||||
|
|
||||||
|
const initState = extend({
|
||||||
|
cachedBalances: {},
|
||||||
|
}, opts.initState)
|
||||||
|
this.store = new ObservableStore(initState)
|
||||||
|
|
||||||
|
this._registerUpdates()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the cachedBalances property for the current network. Cached balances will be updated to those in the passed accounts
|
||||||
|
* if balances in the passed accounts are truthy.
|
||||||
|
*
|
||||||
|
* @param {Object} obj The the recently updated accounts object for the current network
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
async updateCachedBalances ({ accounts }) {
|
||||||
|
const network = await this.getNetwork()
|
||||||
|
const balancesToCache = await this._generateBalancesToCache(accounts, network)
|
||||||
|
this.store.updateState({
|
||||||
|
cachedBalances: balancesToCache,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
_generateBalancesToCache (newAccounts, currentNetwork) {
|
||||||
|
const { cachedBalances } = this.store.getState()
|
||||||
|
const currentNetworkBalancesToCache = { ...cachedBalances[currentNetwork] }
|
||||||
|
|
||||||
|
Object.keys(newAccounts).forEach(accountID => {
|
||||||
|
const account = newAccounts[accountID]
|
||||||
|
|
||||||
|
if (account.balance) {
|
||||||
|
currentNetworkBalancesToCache[accountID] = account.balance
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const balancesToCache = {
|
||||||
|
...cachedBalances,
|
||||||
|
[currentNetwork]: currentNetworkBalancesToCache,
|
||||||
|
}
|
||||||
|
|
||||||
|
return balancesToCache
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets up listeners and subscriptions which should trigger an update of cached balances. These updates will
|
||||||
|
* happen when the current account changes. Which happens on block updates, as well as on network and account
|
||||||
|
* selections.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
_registerUpdates () {
|
||||||
|
const update = this.updateCachedBalances.bind(this)
|
||||||
|
this.accountTracker.store.subscribe(update)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = CachedBalancesController
|
|
@ -29,6 +29,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 CachedBalancesController = require('./controllers/cached-balances')
|
||||||
const RecentBlocksController = require('./controllers/recent-blocks')
|
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')
|
||||||
|
@ -52,6 +53,8 @@ const EthQuery = require('eth-query')
|
||||||
const ethUtil = require('ethereumjs-util')
|
const ethUtil = require('ethereumjs-util')
|
||||||
const sigUtil = require('eth-sig-util')
|
const sigUtil = require('eth-sig-util')
|
||||||
|
|
||||||
|
const accountsPerPage = 5
|
||||||
|
|
||||||
module.exports = class MetamaskController extends EventEmitter {
|
module.exports = class MetamaskController extends EventEmitter {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -138,6 +141,12 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
this.cachedBalancesController = new CachedBalancesController({
|
||||||
|
accountTracker: this.accountTracker,
|
||||||
|
getNetwork: this.networkController.getNetworkState.bind(this.networkController),
|
||||||
|
initState: initState.CachedBalancesController,
|
||||||
|
})
|
||||||
|
|
||||||
// ensure accountTracker updates balances after network change
|
// ensure accountTracker updates balances after network change
|
||||||
this.networkController.on('networkDidChange', () => {
|
this.networkController.on('networkDidChange', () => {
|
||||||
this.accountTracker._updateAccounts()
|
this.accountTracker._updateAccounts()
|
||||||
|
@ -227,6 +236,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||||
ShapeShiftController: this.shapeshiftController.store,
|
ShapeShiftController: this.shapeshiftController.store,
|
||||||
NetworkController: this.networkController.store,
|
NetworkController: this.networkController.store,
|
||||||
InfuraController: this.infuraController.store,
|
InfuraController: this.infuraController.store,
|
||||||
|
CachedBalancesController: this.cachedBalancesController.store,
|
||||||
})
|
})
|
||||||
|
|
||||||
this.memStore = new ComposableObservableStore(null, {
|
this.memStore = new ComposableObservableStore(null, {
|
||||||
|
@ -234,6 +244,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||||
AccountTracker: this.accountTracker.store,
|
AccountTracker: this.accountTracker.store,
|
||||||
TxController: this.txController.memStore,
|
TxController: this.txController.memStore,
|
||||||
BalancesController: this.balancesController.store,
|
BalancesController: this.balancesController.store,
|
||||||
|
CachedBalancesController: this.cachedBalancesController.store,
|
||||||
TokenRatesController: this.tokenRatesController.store,
|
TokenRatesController: this.tokenRatesController.store,
|
||||||
MessageManager: this.messageManager.memStore,
|
MessageManager: this.messageManager.memStore,
|
||||||
PersonalMessageManager: this.personalMessageManager.memStore,
|
PersonalMessageManager: this.personalMessageManager.memStore,
|
||||||
|
@ -374,6 +385,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||||
|
|
||||||
// hardware wallets
|
// hardware wallets
|
||||||
connectHardware: nodeify(this.connectHardware, this),
|
connectHardware: nodeify(this.connectHardware, this),
|
||||||
|
connectHardwareAndUnlockAddress: nodeify(this.connectHardwareAndUnlockAddress, this),
|
||||||
forgetDevice: nodeify(this.forgetDevice, this),
|
forgetDevice: nodeify(this.forgetDevice, this),
|
||||||
checkHardwareStatus: nodeify(this.checkHardwareStatus, this),
|
checkHardwareStatus: nodeify(this.checkHardwareStatus, this),
|
||||||
unlockHardwareWalletAccount: nodeify(this.unlockHardwareWalletAccount, this),
|
unlockHardwareWalletAccount: nodeify(this.unlockHardwareWalletAccount, this),
|
||||||
|
@ -645,6 +657,72 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||||
return accounts
|
return accounts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
connectHardwareAndUnlockAddress (deviceName, hdPath, addressToUnlock) {
|
||||||
|
return new Promise(async (resolve, reject) => {
|
||||||
|
try {
|
||||||
|
const keyring = await this.getKeyringForDevice(deviceName, hdPath)
|
||||||
|
|
||||||
|
const accountsFromFirstPage = await keyring.getFirstPage()
|
||||||
|
const initialPage = 0
|
||||||
|
let accounts = await this.findAccountInLedger({
|
||||||
|
accounts: accountsFromFirstPage,
|
||||||
|
keyring,
|
||||||
|
page: initialPage,
|
||||||
|
addressToUnlock,
|
||||||
|
hdPath,
|
||||||
|
})
|
||||||
|
accounts = accounts || accountsFromFirstPage
|
||||||
|
|
||||||
|
// Merge with existing accounts
|
||||||
|
// and make sure addresses are not repeated
|
||||||
|
const oldAccounts = await this.keyringController.getAccounts()
|
||||||
|
const accountsToTrack = [...new Set(oldAccounts.concat(accounts.map(a => a.address.toLowerCase())))]
|
||||||
|
this.accountTracker.syncWithAddresses(accountsToTrack)
|
||||||
|
|
||||||
|
resolve(accountsFromFirstPage)
|
||||||
|
} catch (e) {
|
||||||
|
reject(e)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async findAccountInLedger ({accounts, keyring, page, addressToUnlock, hdPath}) {
|
||||||
|
return new Promise(async (resolve, reject) => {
|
||||||
|
// to do: store pages depth in dropdown
|
||||||
|
const pagesDepth = 10
|
||||||
|
if (page >= pagesDepth) {
|
||||||
|
reject({
|
||||||
|
message: `Requested account ${addressToUnlock} is not found in ${pagesDepth} pages of ${hdPath} path of Ledger. Try to unlock this account from Ledger.`,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (accounts.length) {
|
||||||
|
const accountIsFound = accounts.some((account, ind) => {
|
||||||
|
const normalizedAddress = account.address.toLowerCase()
|
||||||
|
if (normalizedAddress === addressToUnlock) {
|
||||||
|
const indToUnlock = page * accountsPerPage + ind
|
||||||
|
keyring.setAccountToUnlock(indToUnlock)
|
||||||
|
}
|
||||||
|
return normalizedAddress === addressToUnlock
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!accountIsFound) {
|
||||||
|
accounts = await keyring.getNextPage()
|
||||||
|
page++
|
||||||
|
this.findAccountInLedger({accounts, keyring, page, addressToUnlock, hdPath})
|
||||||
|
.then(accounts => {
|
||||||
|
resolve(accounts)
|
||||||
|
})
|
||||||
|
.catch(e => {
|
||||||
|
reject(e)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
resolve(accounts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the device is unlocked
|
* Check if the device is unlocked
|
||||||
*
|
*
|
||||||
|
@ -674,21 +752,45 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||||
*/
|
*/
|
||||||
async unlockHardwareWalletAccount (index, deviceName, hdPath) {
|
async unlockHardwareWalletAccount (index, deviceName, hdPath) {
|
||||||
const keyring = await this.getKeyringForDevice(deviceName, hdPath)
|
const keyring = await this.getKeyringForDevice(deviceName, hdPath)
|
||||||
|
let hdAccounts
|
||||||
|
let indexInPage
|
||||||
|
if (deviceName.includes('ledger')) {
|
||||||
|
hdAccounts = await keyring.getFirstPage()
|
||||||
|
const accountPosition = Number(index) + 1
|
||||||
|
const pages = Math.ceil(accountPosition / accountsPerPage)
|
||||||
|
indexInPage = index % accountsPerPage
|
||||||
|
if (pages > 1) {
|
||||||
|
for (let iterator = 0; iterator < pages; iterator++) {
|
||||||
|
hdAccounts = await keyring.getNextPage()
|
||||||
|
iterator++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
keyring.setAccountToUnlock(index)
|
keyring.setAccountToUnlock(index)
|
||||||
const oldAccounts = await this.keyringController.getAccounts()
|
const oldAccounts = await this.keyringController.getAccounts()
|
||||||
const keyState = await this.keyringController.addNewAccount(keyring)
|
const keyState = await this.keyringController.addNewAccount(keyring)
|
||||||
const newAccounts = await this.keyringController.getAccounts()
|
const newAccounts = await this.keyringController.getAccounts()
|
||||||
this.preferencesController.setAddresses(newAccounts)
|
this.preferencesController.setAddresses(newAccounts)
|
||||||
|
|
||||||
|
let selectedAddressChanged = false
|
||||||
newAccounts.forEach(address => {
|
newAccounts.forEach(address => {
|
||||||
if (!oldAccounts.includes(address)) {
|
if (!oldAccounts.includes(address)) {
|
||||||
// Set the account label to Trezor 1 / Ledger 1, etc
|
// Set the account label to Trezor 1 / Ledger 1, etc
|
||||||
this.preferencesController.setAccountLabel(address, `${deviceName[0].toUpperCase()}${deviceName.slice(1)} ${parseInt(index, 10) + 1}`)
|
this.preferencesController.setAccountLabel(address, `${deviceName[0].toUpperCase()}${deviceName.slice(1)} ${parseInt(index, 10) + 1}`)
|
||||||
// Select the account
|
// Select the account
|
||||||
this.preferencesController.setSelectedAddress(address)
|
this.preferencesController.setSelectedAddress(address)
|
||||||
|
selectedAddressChanged = true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if (deviceName.includes('ledger')) {
|
||||||
|
if (!selectedAddressChanged) {
|
||||||
|
// Select the account
|
||||||
|
this.preferencesController.setSelectedAddress(hdAccounts[indexInPage].address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const { identities } = this.preferencesController.store.getState()
|
const { identities } = this.preferencesController.store.getState()
|
||||||
return { ...keyState, identities }
|
return { ...keyState, identities }
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,15 +15,20 @@ const TabBar = require('./components/tab-bar')
|
||||||
const TokenList = require('./components/token-list')
|
const TokenList = require('./components/token-list')
|
||||||
const AccountDropdowns = require('./components/account-dropdowns').AccountDropdowns
|
const AccountDropdowns = require('./components/account-dropdowns').AccountDropdowns
|
||||||
const CopyButton = require('./components/copyButton')
|
const CopyButton = require('./components/copyButton')
|
||||||
|
const ToastComponent = require('./components/toast')
|
||||||
|
import { getMetaMaskAccounts } from '../../ui/app/selectors'
|
||||||
|
|
||||||
module.exports = connect(mapStateToProps)(AccountDetailScreen)
|
module.exports = connect(mapStateToProps)(AccountDetailScreen)
|
||||||
|
|
||||||
function mapStateToProps (state) {
|
function mapStateToProps (state) {
|
||||||
|
const accounts = getMetaMaskAccounts(state)
|
||||||
return {
|
return {
|
||||||
metamask: state.metamask,
|
metamask: state.metamask,
|
||||||
identities: state.metamask.identities,
|
identities: state.metamask.identities,
|
||||||
keyrings: state.metamask.keyrings,
|
keyrings: state.metamask.keyrings,
|
||||||
accounts: state.metamask.accounts,
|
warning: state.appState.warning,
|
||||||
|
toastMsg: state.appState.toastMsg,
|
||||||
|
accounts,
|
||||||
address: state.metamask.selectedAddress,
|
address: state.metamask.selectedAddress,
|
||||||
accountDetail: state.appState.accountDetail,
|
accountDetail: state.appState.accountDetail,
|
||||||
network: state.metamask.network,
|
network: state.metamask.network,
|
||||||
|
@ -62,6 +67,11 @@ AccountDetailScreen.prototype.render = function () {
|
||||||
|
|
||||||
h('.account-detail-section.full-flex-height', [
|
h('.account-detail-section.full-flex-height', [
|
||||||
|
|
||||||
|
h(ToastComponent, {
|
||||||
|
msg: props.toastMsg,
|
||||||
|
isSuccess: false,
|
||||||
|
}),
|
||||||
|
|
||||||
// identicon, label, balance, etc
|
// identicon, label, balance, etc
|
||||||
h('.account-data-subsection', {
|
h('.account-data-subsection', {
|
||||||
style: {
|
style: {
|
||||||
|
|
|
@ -44,6 +44,7 @@ const DeleteRpc = require('./components/delete-rpc')
|
||||||
const DeleteImportedAccount = require('./components/delete-imported-account')
|
const DeleteImportedAccount = require('./components/delete-imported-account')
|
||||||
const ConfirmChangePassword = require('./components/confirm-change-password')
|
const ConfirmChangePassword = require('./components/confirm-change-password')
|
||||||
const ethNetProps = require('eth-net-props')
|
const ethNetProps = require('eth-net-props')
|
||||||
|
const { getMetaMaskAccounts } = require('../../ui/app/selectors')
|
||||||
|
|
||||||
module.exports = compose(
|
module.exports = compose(
|
||||||
withRouter,
|
withRouter,
|
||||||
|
@ -54,9 +55,11 @@ inherits(App, Component)
|
||||||
function App () { Component.call(this) }
|
function App () { Component.call(this) }
|
||||||
|
|
||||||
function mapStateToProps (state) {
|
function mapStateToProps (state) {
|
||||||
|
|
||||||
|
const accounts = getMetaMaskAccounts(state)
|
||||||
|
|
||||||
const {
|
const {
|
||||||
identities,
|
identities,
|
||||||
accounts,
|
|
||||||
address,
|
address,
|
||||||
keyrings,
|
keyrings,
|
||||||
isInitialized,
|
isInitialized,
|
||||||
|
|
|
@ -10,6 +10,7 @@ const ethUtil = require('ethereumjs-util')
|
||||||
const copyToClipboard = require('copy-to-clipboard')
|
const copyToClipboard = require('copy-to-clipboard')
|
||||||
const ethNetProps = require('eth-net-props')
|
const ethNetProps = require('eth-net-props')
|
||||||
const { getCurrentKeyring, ifLooseAcc, ifContractAcc } = require('../util')
|
const { getCurrentKeyring, ifLooseAcc, ifContractAcc } = require('../util')
|
||||||
|
const { getHdPaths } = require('./connect-hardware/util')
|
||||||
|
|
||||||
class AccountDropdowns extends Component {
|
class AccountDropdowns extends Component {
|
||||||
constructor (props) {
|
constructor (props) {
|
||||||
|
@ -62,6 +63,25 @@ class AccountDropdowns extends Component {
|
||||||
closeMenu: () => {},
|
closeMenu: () => {},
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
this.props.actions.showAccountDetail(identity.address)
|
this.props.actions.showAccountDetail(identity.address)
|
||||||
|
if (this.ifHardwareAcc(keyring)) {
|
||||||
|
const ledger = 'ledger'
|
||||||
|
if (keyring.type.toLowerCase().includes(ledger)) {
|
||||||
|
const hdPaths = getHdPaths()
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.props.actions.connectHardwareAndUnlockAddress(ledger, hdPaths[1].value, identity.address)
|
||||||
|
.then(_ => resolve())
|
||||||
|
.catch(e => {
|
||||||
|
this.props.actions.connectHardwareAndUnlockAddress(ledger, hdPaths[0].value, identity.address)
|
||||||
|
.then(_ => resolve())
|
||||||
|
.catch(e => reject(e))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch(e => {
|
||||||
|
this.props.actions.displayWarning((e && e.message) || e)
|
||||||
|
this.props.actions.displayToast(e)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
style: {
|
style: {
|
||||||
marginTop: index === 0 ? '5px' : '',
|
marginTop: index === 0 ? '5px' : '',
|
||||||
|
@ -404,7 +424,16 @@ const mapDispatchToProps = (dispatch) => {
|
||||||
showConnectHWWalletPage: () => dispatch(actions.showConnectHWWalletPage()),
|
showConnectHWWalletPage: () => dispatch(actions.showConnectHWWalletPage()),
|
||||||
showQrView: (selected, identity) => dispatch(actions.showQrView(selected, identity)),
|
showQrView: (selected, identity) => dispatch(actions.showQrView(selected, identity)),
|
||||||
showDeleteImportedAccount: (identity) => dispatch(actions.showDeleteImportedAccount(identity)),
|
showDeleteImportedAccount: (identity) => dispatch(actions.showDeleteImportedAccount(identity)),
|
||||||
|
displayWarning: (msg) => dispatch(actions.displayWarning(msg)),
|
||||||
getContract: (addr) => dispatch(actions.getContract(addr)),
|
getContract: (addr) => dispatch(actions.getContract(addr)),
|
||||||
|
setHardwareWalletDefaultHdPath: ({device, path}) => {
|
||||||
|
return dispatch(actions.setHardwareWalletDefaultHdPath({device, path}))
|
||||||
|
},
|
||||||
|
connectHardwareAndUnlockAddress: (deviceName, hdPath, address) => {
|
||||||
|
return dispatch(actions.connectHardwareAndUnlockAddress(deviceName, hdPath, address))
|
||||||
|
},
|
||||||
|
displayToast: (msg) => dispatch(actions.displayToast(msg)),
|
||||||
|
hideToast: () => dispatch(actions.hideToast()),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import { getNetworkDisplayName } from '../../../app/scripts/controllers/network/
|
||||||
import { getFaucets, getExchanges } from '../../../app/scripts/lib/buy-eth-url'
|
import { getFaucets, getExchanges } from '../../../app/scripts/lib/buy-eth-url'
|
||||||
import ethNetProps from 'eth-net-props'
|
import ethNetProps from 'eth-net-props'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
|
import { getMetaMaskAccounts } from '../../../ui/app/selectors'
|
||||||
|
|
||||||
class BuyButtonSubview extends Component {
|
class BuyButtonSubview extends Component {
|
||||||
render () {
|
render () {
|
||||||
|
@ -197,9 +198,10 @@ BuyButtonSubview.propTypes = {
|
||||||
}
|
}
|
||||||
|
|
||||||
function mapStateToProps (state) {
|
function mapStateToProps (state) {
|
||||||
|
const accounts = getMetaMaskAccounts(state)
|
||||||
return {
|
return {
|
||||||
identity: state.appState.identity,
|
identity: state.appState.identity,
|
||||||
account: state.metamask.accounts[state.appState.buyView.buyAddress],
|
account: accounts[state.appState.buyView.buyAddress],
|
||||||
warning: state.appState.warning,
|
warning: state.appState.warning,
|
||||||
buyView: state.appState.buyView,
|
buyView: state.appState.buyView,
|
||||||
network: state.metamask.network,
|
network: state.metamask.network,
|
||||||
|
|
|
@ -4,25 +4,13 @@ import ethNetProps from 'eth-net-props'
|
||||||
import { default as Select } from 'react-select'
|
import { default as Select } from 'react-select'
|
||||||
import Button from '../../../../ui/app/components/button'
|
import Button from '../../../../ui/app/components/button'
|
||||||
import { capitalizeFirstLetter } from '../../../../app/scripts/lib/util'
|
import { capitalizeFirstLetter } from '../../../../app/scripts/lib/util'
|
||||||
|
import { isLedger, getHdPaths } from './util'
|
||||||
|
|
||||||
class AccountList extends Component {
|
class AccountList extends Component {
|
||||||
constructor (props, context) {
|
constructor (props, context) {
|
||||||
super(props)
|
super(props)
|
||||||
}
|
}
|
||||||
|
|
||||||
getHdPaths = () => {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
label: `Ledger Live`,
|
|
||||||
value: `m/44'/60'/0'/0/0`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: `Legacy (MEW / MyCrypto)`,
|
|
||||||
value: `m/44'/60'/0'`,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
goToNextPage = () => {
|
goToNextPage = () => {
|
||||||
// If we have < 5 accounts, it's restricted by BIP-44
|
// If we have < 5 accounts, it's restricted by BIP-44
|
||||||
if (this.props.accounts.length === 5) {
|
if (this.props.accounts.length === 5) {
|
||||||
|
@ -39,7 +27,7 @@ class AccountList extends Component {
|
||||||
renderHdPathSelector = () => {
|
renderHdPathSelector = () => {
|
||||||
const { onPathChange, selectedPath } = this.props
|
const { onPathChange, selectedPath } = this.props
|
||||||
|
|
||||||
const options = this.getHdPaths()
|
const options = getHdPaths()
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h3 className="hw-connect__hdPath__title">Select HD Path</h3>
|
<h3 className="hw-connect__hdPath__title">Select HD Path</h3>
|
||||||
|
@ -67,26 +55,46 @@ class AccountList extends Component {
|
||||||
<h3 className="hw-connect">
|
<h3 className="hw-connect">
|
||||||
<h3 className="hw-connect__unlock-title">{`Unlock ${capitalizeFirstLetter(device)}`}</h3>
|
<h3 className="hw-connect__unlock-title">{`Unlock ${capitalizeFirstLetter(device)}`}</h3>
|
||||||
{device.toLowerCase() === 'ledger' ? this.renderHdPathSelector() : null}
|
{device.toLowerCase() === 'ledger' ? this.renderHdPathSelector() : null}
|
||||||
<p className="hw-connect__msg">Select the account to view in Nifty Wallet</p>
|
<p className="hw-connect__msg">Select the accounts to view in Nifty Wallet</p>
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderInput = (a, i) => {
|
||||||
|
const { device, selectedAccount, selectedAccounts } = this.props
|
||||||
|
if (isLedger(device)) {
|
||||||
|
return (
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
name={`selectedAccount-${i}`}
|
||||||
|
id={`address-${i}`}
|
||||||
|
value={a.index}
|
||||||
|
onChange={(e) => this.props.onAccountChange(e.target.value)}
|
||||||
|
checked={selectedAccounts.includes(a.index.toString())}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="selectedAccount"
|
||||||
|
id={`address-${i}`}
|
||||||
|
value={a.index}
|
||||||
|
onChange={(e) => this.props.onAccountChange(e.target.value)}
|
||||||
|
checked={selectedAccount === a.index.toString()}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
renderAccounts = () => {
|
renderAccounts = () => {
|
||||||
const rows = []
|
const rows = []
|
||||||
this.props.accounts.forEach((a, i) => {
|
this.props.accounts.forEach((a, i) => {
|
||||||
rows.push(
|
rows.push(
|
||||||
<div className="hw-account-list__item" key={a.address}>
|
<div className="hw-account-list__item" key={a.address}>
|
||||||
<div className="hw-account-list__item__radio">
|
<div className="hw-account-list__item__radio">
|
||||||
<input
|
{this.renderInput(a, i)}
|
||||||
type="radio"
|
|
||||||
name="selectedAccount"
|
|
||||||
id={`address-${i}`}
|
|
||||||
value={a.index}
|
|
||||||
onChange={(e) => this.props.onAccountChange(e.target.value)}
|
|
||||||
checked={this.props.selectedAccount === a.index.toString()}
|
|
||||||
/>
|
|
||||||
<label className="hw-account-list__item__label" htmlFor={`address-${i}`}>
|
<label className="hw-account-list__item__label" htmlFor={`address-${i}`}>
|
||||||
{`${a.address.slice(0, 4)}...${a.address.slice(-4)}`}
|
{`${a.address.slice(0, 4)}...${a.address.slice(-4)}`}
|
||||||
<span
|
<span
|
||||||
|
@ -125,7 +133,7 @@ class AccountList extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
renderButtons = () => {
|
renderButtons = () => {
|
||||||
const disabled = this.props.selectedAccount === null
|
const disabled = !this.props.selectedAccount && this.props.selectedAccounts.length === 0
|
||||||
const buttonProps = {}
|
const buttonProps = {}
|
||||||
if (disabled) {
|
if (disabled) {
|
||||||
buttonProps.disabled = true
|
buttonProps.disabled = true
|
||||||
|
@ -182,6 +190,7 @@ AccountList.propTypes = {
|
||||||
getPage: PropTypes.func.isRequired,
|
getPage: PropTypes.func.isRequired,
|
||||||
network: PropTypes.string,
|
network: PropTypes.string,
|
||||||
selectedAccount: PropTypes.string,
|
selectedAccount: PropTypes.string,
|
||||||
|
selectedAccounts: PropTypes.array,
|
||||||
history: PropTypes.object,
|
history: PropTypes.object,
|
||||||
onUnlockAccount: PropTypes.func,
|
onUnlockAccount: PropTypes.func,
|
||||||
onCancel: PropTypes.func,
|
onCancel: PropTypes.func,
|
||||||
|
|
|
@ -7,6 +7,8 @@ import AccountList from './account-list'
|
||||||
import { formatBalance } from '../../util'
|
import { formatBalance } from '../../util'
|
||||||
import { getPlatform } from '../../../../app/scripts/lib/util'
|
import { getPlatform } from '../../../../app/scripts/lib/util'
|
||||||
import { PLATFORM_FIREFOX } from '../../../../app/scripts/lib/enums'
|
import { PLATFORM_FIREFOX } from '../../../../app/scripts/lib/enums'
|
||||||
|
import { isLedger } from './util'
|
||||||
|
import { getMetaMaskAccounts } from '../../../../ui/app/selectors'
|
||||||
|
|
||||||
class ConnectHardwareForm extends Component {
|
class ConnectHardwareForm extends Component {
|
||||||
constructor (props, context) {
|
constructor (props, context) {
|
||||||
|
@ -14,6 +16,7 @@ class ConnectHardwareForm extends Component {
|
||||||
this.state = {
|
this.state = {
|
||||||
error: null,
|
error: null,
|
||||||
selectedAccount: null,
|
selectedAccount: null,
|
||||||
|
selectedAccounts: [],
|
||||||
accounts: [],
|
accounts: [],
|
||||||
browserSupported: true,
|
browserSupported: true,
|
||||||
unlocked: false,
|
unlocked: false,
|
||||||
|
@ -69,7 +72,25 @@ class ConnectHardwareForm extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
onAccountChange = (account) => {
|
onAccountChange = (account) => {
|
||||||
this.setState({selectedAccount: account.toString(), error: null})
|
let selectedAcc = account.toString()
|
||||||
|
if (isLedger(this.state.device)) {
|
||||||
|
const selectedAccounts = this.state.selectedAccounts
|
||||||
|
if (!selectedAccounts.includes(selectedAcc)) {
|
||||||
|
selectedAccounts.push(selectedAcc)
|
||||||
|
} else {
|
||||||
|
const indToRemove = selectedAccounts.indexOf(selectedAcc)
|
||||||
|
selectedAccounts.splice(indToRemove, 1)
|
||||||
|
selectedAcc = selectedAccounts[selectedAccounts.length - 1]
|
||||||
|
}
|
||||||
|
const newState = {
|
||||||
|
selectedAccounts,
|
||||||
|
selectedAccount: selectedAcc,
|
||||||
|
error: null,
|
||||||
|
}
|
||||||
|
this.setState(newState)
|
||||||
|
} else {
|
||||||
|
this.setState({selectedAccount: account.toString(), error: null})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onAccountRestriction = () => {
|
onAccountRestriction = () => {
|
||||||
|
@ -97,19 +118,20 @@ class ConnectHardwareForm extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
const newState = { unlocked: true, device, error: null }
|
const newState = { unlocked: true, device, error: null }
|
||||||
// Default to the first account
|
if (!isLedger(device)) {
|
||||||
if (this.state.selectedAccount === null) {
|
// Default to the first account
|
||||||
accounts.forEach((a, i) => {
|
if (this.state.selectedAccount === null) {
|
||||||
if (a.address.toLowerCase() === this.props.address) {
|
accounts.forEach((a, i) => {
|
||||||
newState.selectedAccount = a.index.toString()
|
if (a.address.toLowerCase() === this.props.address) {
|
||||||
}
|
newState.selectedAccount = a.index.toString()
|
||||||
})
|
}
|
||||||
// If the page doesn't contain the selected account, let's deselect it
|
})
|
||||||
} else if (!accounts.filter(a => a.index.toString() === this.state.selectedAccount).length) {
|
// If the page doesn't contain the selected account, let's deselect it
|
||||||
newState.selectedAccount = null
|
} else if (!accounts.filter(a => a.index.toString() === this.state.selectedAccount).length) {
|
||||||
|
newState.selectedAccount = null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Map accounts with balances
|
// Map accounts with balances
|
||||||
newState.accounts = accounts.map(account => {
|
newState.accounts = accounts.map(account => {
|
||||||
const normalizedAddress = account.address.toLowerCase()
|
const normalizedAddress = account.address.toLowerCase()
|
||||||
|
@ -136,6 +158,7 @@ class ConnectHardwareForm extends Component {
|
||||||
this.setState({
|
this.setState({
|
||||||
error: null,
|
error: null,
|
||||||
selectedAccount: null,
|
selectedAccount: null,
|
||||||
|
selectedAccounts: [],
|
||||||
accounts: [],
|
accounts: [],
|
||||||
unlocked: false,
|
unlocked: false,
|
||||||
})
|
})
|
||||||
|
@ -146,16 +169,35 @@ class ConnectHardwareForm extends Component {
|
||||||
|
|
||||||
onUnlockAccount = (device) => {
|
onUnlockAccount = (device) => {
|
||||||
|
|
||||||
if (this.state.selectedAccount === null) {
|
if (!this.state.selectedAccount && this.state.selectedAccounts.length === 0) {
|
||||||
this.setState({ error: 'You need to select an account!' })
|
this.setState({ error: 'You need to select an account!' })
|
||||||
}
|
}
|
||||||
|
|
||||||
this.props.unlockHardwareWalletAccount(this.state.selectedAccount, device)
|
if (this.state.selectedAccounts.length > 0) {
|
||||||
.then(_ => {
|
this.unlockHardwareWalletAccounts(this.state.selectedAccounts, device)
|
||||||
this.props.goHome()
|
.then(_ => {
|
||||||
}).catch(e => {
|
this.props.goHome()
|
||||||
this.setState({ error: (e.message || e.toString()) })
|
})
|
||||||
})
|
} else {
|
||||||
|
this.props.unlockHardwareWalletAccount(this.state.selectedAccount, device)
|
||||||
|
.then(_ => {
|
||||||
|
this.props.goHome()
|
||||||
|
}).catch(e => {
|
||||||
|
this.setState({ error: (e.message || e.toString()) })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unlockHardwareWalletAccounts = (accounts, device) => {
|
||||||
|
return accounts.reduce((promise, account) => {
|
||||||
|
return promise
|
||||||
|
.then((result) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
resolve(this.props.unlockHardwareWalletAccount(account, device))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch(e => this.setState({ error: (e.message || e.toString()) }))
|
||||||
|
}, Promise.resolve())
|
||||||
}
|
}
|
||||||
|
|
||||||
onCancel = () => {
|
onCancel = () => {
|
||||||
|
@ -185,6 +227,7 @@ class ConnectHardwareForm extends Component {
|
||||||
device={this.state.device}
|
device={this.state.device}
|
||||||
accounts={this.state.accounts}
|
accounts={this.state.accounts}
|
||||||
selectedAccount={this.state.selectedAccount}
|
selectedAccount={this.state.selectedAccount}
|
||||||
|
selectedAccounts={this.state.selectedAccounts}
|
||||||
onAccountChange={this.onAccountChange}
|
onAccountChange={this.onAccountChange}
|
||||||
network={this.props.network}
|
network={this.props.network}
|
||||||
getPage={this.getPage}
|
getPage={this.getPage}
|
||||||
|
@ -239,8 +282,9 @@ ConnectHardwareForm.propTypes = {
|
||||||
|
|
||||||
const mapStateToProps = state => {
|
const mapStateToProps = state => {
|
||||||
const {
|
const {
|
||||||
metamask: { network, selectedAddress, identities = {}, accounts = [] },
|
metamask: { network, selectedAddress, identities = {} },
|
||||||
} = state
|
} = state
|
||||||
|
const accounts = getMetaMaskAccounts(state)
|
||||||
const numberOfExistingAccounts = Object.keys(identities).length
|
const numberOfExistingAccounts = Object.keys(identities).length
|
||||||
const {
|
const {
|
||||||
appState: { defaultHdPaths },
|
appState: { defaultHdPaths },
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
|
||||||
|
function isLedger (device) {
|
||||||
|
return device && device.toLowerCase().includes('ledger')
|
||||||
|
}
|
||||||
|
|
||||||
|
function getHdPaths () {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: `Ledger Live`,
|
||||||
|
value: `m/44'/60'/0'/0/0`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: `Legacy (MEW / MyCrypto)`,
|
||||||
|
value: `m/44'/60'/0'`,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
isLedger,
|
||||||
|
getHdPaths,
|
||||||
|
}
|
|
@ -1,10 +1,12 @@
|
||||||
import React, {Component} from 'react'
|
import React, {Component} from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
|
import { connect } from 'react-redux'
|
||||||
|
import actions from '../../../ui/app/actions'
|
||||||
|
|
||||||
class SendError extends Component {
|
class ErrorComponent extends Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
error: PropTypes.string,
|
error: PropTypes.string,
|
||||||
onClose: PropTypes.func,
|
hideWarning: PropTypes.func,
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
|
@ -29,7 +31,7 @@ class SendError extends Component {
|
||||||
height: '16px',
|
height: '16px',
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
}}
|
}}
|
||||||
onClick={(e) => this.props.onClose(e)}
|
onClick={(e) => this.props.hideWarning()}
|
||||||
/>
|
/>
|
||||||
<div style={{
|
<div style={{
|
||||||
marginLeft: '30px',
|
marginLeft: '30px',
|
||||||
|
@ -47,4 +49,10 @@ class SendError extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = SendError
|
function mapDispatchToProps (dispatch) {
|
||||||
|
return {
|
||||||
|
hideWarning: () => dispatch(actions.hideWarning()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = connect(null, mapDispatchToProps)(ErrorComponent)
|
|
@ -24,6 +24,7 @@ const connect = require('react-redux').connect
|
||||||
const abiDecoder = require('abi-decoder')
|
const abiDecoder = require('abi-decoder')
|
||||||
const { tokenInfoGetter, calcTokenAmount } = require('../../../ui/app/token-util')
|
const { tokenInfoGetter, calcTokenAmount } = require('../../../ui/app/token-util')
|
||||||
const BigNumber = require('bignumber.js')
|
const BigNumber = require('bignumber.js')
|
||||||
|
import { getMetaMaskAccounts } from '../../../ui/app/selectors'
|
||||||
|
|
||||||
const MIN_GAS_PRICE_BN = new BN('0')
|
const MIN_GAS_PRICE_BN = new BN('0')
|
||||||
const MIN_GAS_LIMIT_BN = new BN('21000')
|
const MIN_GAS_LIMIT_BN = new BN('21000')
|
||||||
|
@ -44,9 +45,10 @@ function PendingTx () {
|
||||||
}
|
}
|
||||||
|
|
||||||
function mapStateToProps (state) {
|
function mapStateToProps (state) {
|
||||||
|
const accounts = getMetaMaskAccounts(state)
|
||||||
return {
|
return {
|
||||||
identities: state.metamask.identities,
|
identities: state.metamask.identities,
|
||||||
accounts: state.metamask.accounts,
|
accounts,
|
||||||
selectedAddress: state.metamask.selectedAddress,
|
selectedAddress: state.metamask.selectedAddress,
|
||||||
unapprovedTxs: state.metamask.unapprovedTxs,
|
unapprovedTxs: state.metamask.unapprovedTxs,
|
||||||
unapprovedMsgs: state.metamask.unapprovedMsgs,
|
unapprovedMsgs: state.metamask.unapprovedMsgs,
|
||||||
|
|
|
@ -4,9 +4,10 @@ import { connect } from 'react-redux'
|
||||||
import SendProfile from './send-profile'
|
import SendProfile from './send-profile'
|
||||||
import ExecutorCell from './executor-cell'
|
import ExecutorCell from './executor-cell'
|
||||||
import SendHeader from './send-header'
|
import SendHeader from './send-header'
|
||||||
import SendError from './send-error'
|
import ErrorComponent from '../error'
|
||||||
import actions from '../../../../ui/app/actions'
|
import actions from '../../../../ui/app/actions'
|
||||||
import { ifContractAcc } from '../../util'
|
import { ifContractAcc } from '../../util'
|
||||||
|
import { getMetaMaskAccounts } from '../../../../ui/app/selectors'
|
||||||
import Web3 from 'web3'
|
import Web3 from 'web3'
|
||||||
|
|
||||||
const ownerABI = [{
|
const ownerABI = [{
|
||||||
|
@ -73,12 +74,7 @@ class ChooseContractExecutor extends Component {
|
||||||
<div className="send-screen flex-column flex-grow">
|
<div className="send-screen flex-column flex-grow">
|
||||||
<SendProfile />
|
<SendProfile />
|
||||||
<SendHeader title="Choose contract executor" back={() => this.back()} />
|
<SendHeader title="Choose contract executor" back={() => this.back()} />
|
||||||
<SendError
|
<ErrorComponent error={error} />
|
||||||
error={error}
|
|
||||||
onClose={() => {
|
|
||||||
this.props.hideWarning()
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<div style={{ padding: '0 30px' }}>
|
<div style={{ padding: '0 30px' }}>
|
||||||
<span className="hw-connect__header__msg">Contract transaction will be executed from selected account</span>
|
<span className="hw-connect__header__msg">Contract transaction will be executed from selected account</span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -237,9 +233,10 @@ class ChooseContractExecutor extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
function mapStateToProps (state) {
|
function mapStateToProps (state) {
|
||||||
|
const accounts = getMetaMaskAccounts(state)
|
||||||
const result = {
|
const result = {
|
||||||
selected: state.metamask.selectedAddress,
|
selected: state.metamask.selectedAddress,
|
||||||
accounts: state.metamask.accounts,
|
accounts,
|
||||||
keyrings: state.metamask.keyrings,
|
keyrings: state.metamask.keyrings,
|
||||||
identities: state.metamask.identities,
|
identities: state.metamask.identities,
|
||||||
warning: state.appState.warning,
|
warning: state.appState.warning,
|
||||||
|
|
|
@ -4,7 +4,8 @@ import { connect } from 'react-redux'
|
||||||
import PersistentForm from '../../../lib/persistent-form'
|
import PersistentForm from '../../../lib/persistent-form'
|
||||||
import SendProfile from './send-profile'
|
import SendProfile from './send-profile'
|
||||||
import SendHeader from './send-header'
|
import SendHeader from './send-header'
|
||||||
import SendError from './send-error'
|
import ErrorComponent from '../error'
|
||||||
|
import ToastComponent from '../toast'
|
||||||
import Select from 'react-select'
|
import Select from 'react-select'
|
||||||
import actions from '../../../../ui/app/actions'
|
import actions from '../../../../ui/app/actions'
|
||||||
import abiEncoder from 'web3-eth-abi'
|
import abiEncoder from 'web3-eth-abi'
|
||||||
|
@ -109,15 +110,9 @@ class SendTransactionScreen extends PersistentForm {
|
||||||
copyDisabled: true,
|
copyDisabled: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
this.timerID = null
|
|
||||||
PersistentForm.call(this)
|
PersistentForm.call(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount () {
|
|
||||||
this.props.hideToast()
|
|
||||||
clearTimeout(this.timerID)
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillMount () {
|
componentWillMount () {
|
||||||
this.getContractMethods()
|
this.getContractMethods()
|
||||||
}
|
}
|
||||||
|
@ -132,11 +127,8 @@ class SendTransactionScreen extends PersistentForm {
|
||||||
<div className="send-screen flex-column flex-grow">
|
<div className="send-screen flex-column flex-grow">
|
||||||
<SendProfile />
|
<SendProfile />
|
||||||
<SendHeader title="Execute Method" />
|
<SendHeader title="Execute Method" />
|
||||||
<SendError
|
<ErrorComponent error={error} />
|
||||||
error={error}
|
<ToastComponent msg={this.props.toastMsg} isSuccess={true} />
|
||||||
onClose={() => { this.props.hideWarning() }}
|
|
||||||
/>
|
|
||||||
{this.props.toastMsg ? <div className="toast">{this.props.toastMsg}</div> : null}
|
|
||||||
<div style={{ padding: '0 30px' }}>
|
<div style={{ padding: '0 30px' }}>
|
||||||
<Select
|
<Select
|
||||||
clearable={false}
|
clearable={false}
|
||||||
|
@ -387,7 +379,6 @@ class SendTransactionScreen extends PersistentForm {
|
||||||
}
|
}
|
||||||
|
|
||||||
setOutputValue = (val, type) => {
|
setOutputValue = (val, type) => {
|
||||||
console.log(val)
|
|
||||||
if (!type) {
|
if (!type) {
|
||||||
return val || ''
|
return val || ''
|
||||||
}
|
}
|
||||||
|
@ -423,9 +414,6 @@ class SendTransactionScreen extends PersistentForm {
|
||||||
if (txData) {
|
if (txData) {
|
||||||
copyToClipboard(txData)
|
copyToClipboard(txData)
|
||||||
this.props.displayToast('Contract ABI encoded method call has been successfully copied to clipboard')
|
this.props.displayToast('Contract ABI encoded method call has been successfully copied to clipboard')
|
||||||
this.timerID = setTimeout(() => {
|
|
||||||
this.props.hideToast()
|
|
||||||
}, 4000)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import Identicon from '../identicon'
|
||||||
import { addressSummary } from '../../util'
|
import { addressSummary } from '../../util'
|
||||||
import EthBalance from '../eth-balance'
|
import EthBalance from '../eth-balance'
|
||||||
import TokenBalance from '../token-balance'
|
import TokenBalance from '../token-balance'
|
||||||
|
import { getMetaMaskAccounts } from '../../../../ui/app/selectors'
|
||||||
|
|
||||||
class SendProfile extends Component {
|
class SendProfile extends Component {
|
||||||
render () {
|
render () {
|
||||||
|
@ -77,9 +78,10 @@ class SendProfile extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
function mapStateToProps (state) {
|
function mapStateToProps (state) {
|
||||||
|
const accounts = getMetaMaskAccounts(state)
|
||||||
var result = {
|
var result = {
|
||||||
address: state.metamask.selectedAddress,
|
address: state.metamask.selectedAddress,
|
||||||
accounts: state.metamask.accounts,
|
accounts,
|
||||||
identities: state.metamask.identities,
|
identities: state.metamask.identities,
|
||||||
network: state.metamask.network,
|
network: state.metamask.network,
|
||||||
conversionRate: state.metamask.conversionRate,
|
conversionRate: state.metamask.conversionRate,
|
||||||
|
|
|
@ -18,14 +18,16 @@ BigNumber.config({ ERRORS: false })
|
||||||
const log = require('loglevel')
|
const log = require('loglevel')
|
||||||
import SendProfile from './send-profile'
|
import SendProfile from './send-profile'
|
||||||
import SendHeader from './send-header'
|
import SendHeader from './send-header'
|
||||||
import SendError from './send-error'
|
import ErrorComponent from '../error'
|
||||||
|
import { getMetaMaskAccounts } from '../../../../ui/app/selectors'
|
||||||
|
|
||||||
module.exports = connect(mapStateToProps)(SendTransactionScreen)
|
module.exports = connect(mapStateToProps)(SendTransactionScreen)
|
||||||
|
|
||||||
function mapStateToProps (state) {
|
function mapStateToProps (state) {
|
||||||
|
const accounts = getMetaMaskAccounts(state)
|
||||||
var result = {
|
var result = {
|
||||||
address: state.metamask.selectedAddress,
|
address: state.metamask.selectedAddress,
|
||||||
accounts: state.metamask.accounts,
|
accounts,
|
||||||
identities: state.metamask.identities,
|
identities: state.metamask.identities,
|
||||||
warning: state.appState.warning,
|
warning: state.appState.warning,
|
||||||
network: state.metamask.network,
|
network: state.metamask.network,
|
||||||
|
@ -95,7 +97,7 @@ SendTransactionScreen.prototype.render = function () {
|
||||||
}),
|
}),
|
||||||
|
|
||||||
// error message
|
// error message
|
||||||
h(SendError, {
|
h(ErrorComponent, {
|
||||||
error,
|
error,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
|
|
@ -14,13 +14,15 @@ const EnsInput = require('../ens-input')
|
||||||
const ethUtil = require('ethereumjs-util')
|
const ethUtil = require('ethereumjs-util')
|
||||||
import SendProfile from './send-profile'
|
import SendProfile from './send-profile'
|
||||||
import SendHeader from './send-header'
|
import SendHeader from './send-header'
|
||||||
import SendError from './send-error'
|
import ErrorComponent from '../error'
|
||||||
|
import { getMetaMaskAccounts } from '../../../../ui/app/selectors'
|
||||||
module.exports = connect(mapStateToProps)(SendTransactionScreen)
|
module.exports = connect(mapStateToProps)(SendTransactionScreen)
|
||||||
|
|
||||||
function mapStateToProps (state) {
|
function mapStateToProps (state) {
|
||||||
|
const accounts = getMetaMaskAccounts(state)
|
||||||
var result = {
|
var result = {
|
||||||
address: state.metamask.selectedAddress,
|
address: state.metamask.selectedAddress,
|
||||||
accounts: state.metamask.accounts,
|
accounts,
|
||||||
identities: state.metamask.identities,
|
identities: state.metamask.identities,
|
||||||
warning: state.appState.warning,
|
warning: state.appState.warning,
|
||||||
network: state.metamask.network,
|
network: state.metamask.network,
|
||||||
|
@ -69,11 +71,8 @@ SendTransactionScreen.prototype.render = function () {
|
||||||
}),
|
}),
|
||||||
|
|
||||||
// error message
|
// error message
|
||||||
h(SendError, {
|
h(ErrorComponent, {
|
||||||
error,
|
error,
|
||||||
onClose: () => {
|
|
||||||
this.props.dispatch(actions.hideWarning())
|
|
||||||
},
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
// 'to' field
|
// 'to' field
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
import React, {Component} from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import { connect } from 'react-redux'
|
||||||
|
import classnames from 'classnames'
|
||||||
|
import actions from '../../../ui/app/actions'
|
||||||
|
|
||||||
|
class ToastComponent extends Component {
|
||||||
|
static propTypes = {
|
||||||
|
msg: PropTypes.string,
|
||||||
|
isSuccess: PropTypes.bool,
|
||||||
|
hideToast: PropTypes.func,
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor (props) {
|
||||||
|
super(props)
|
||||||
|
this.timerID = null
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate (prevProps) {
|
||||||
|
if (!prevProps.msg && this.props.msg) {
|
||||||
|
this.timerID = setTimeout(() => {
|
||||||
|
this.props.hideToast()
|
||||||
|
clearTimeout(this.timerID)
|
||||||
|
}, 4000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount () {
|
||||||
|
this.props.hideToast()
|
||||||
|
clearTimeout(this.timerID)
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { msg } = this.props
|
||||||
|
return msg ? (
|
||||||
|
<div
|
||||||
|
className={classnames('toast', {
|
||||||
|
'green': this.props.isSuccess,
|
||||||
|
'red': !this.props.isSuccess,
|
||||||
|
})}
|
||||||
|
onClick={(e) => this.props.hideToast()}
|
||||||
|
>{(msg && msg.message) || msg}</div>
|
||||||
|
) : null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapStateToProps (state) {
|
||||||
|
return {
|
||||||
|
toastMsg: state.appState.toastMsg,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapDispatchToProps (dispatch) {
|
||||||
|
return {
|
||||||
|
hideToast: () => dispatch(actions.hideToast()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = connect(mapStateToProps, mapDispatchToProps)(ToastComponent)
|
|
@ -13,6 +13,7 @@ import PendingMsg from './components/pending-msg'
|
||||||
import PendingPersonalMsg from './components/pending-personal-msg'
|
import PendingPersonalMsg from './components/pending-personal-msg'
|
||||||
import PendingTypedMsg from './components/pending-typed-msg'
|
import PendingTypedMsg from './components/pending-typed-msg'
|
||||||
const Loading = require('./components/loading')
|
const Loading = require('./components/loading')
|
||||||
|
const { getMetaMaskAccounts } = require('../../ui/app/selectors')
|
||||||
|
|
||||||
module.exports = connect(mapStateToProps)(ConfirmTxScreen)
|
module.exports = connect(mapStateToProps)(ConfirmTxScreen)
|
||||||
|
|
||||||
|
@ -21,8 +22,8 @@ function mapStateToProps (state) {
|
||||||
const { screenParams, pendingTxIndex } = appState.currentView
|
const { screenParams, pendingTxIndex } = appState.currentView
|
||||||
return {
|
return {
|
||||||
identities: metamask.identities,
|
identities: metamask.identities,
|
||||||
|
accounts: getMetaMaskAccounts(state),
|
||||||
keyrings: metamask.keyrings,
|
keyrings: metamask.keyrings,
|
||||||
accounts: metamask.accounts,
|
|
||||||
selectedAddress: metamask.selectedAddress,
|
selectedAddress: metamask.selectedAddress,
|
||||||
unapprovedTxs: metamask.unapprovedTxs,
|
unapprovedTxs: metamask.unapprovedTxs,
|
||||||
unapprovedMsgs: metamask.unapprovedMsgs,
|
unapprovedMsgs: metamask.unapprovedMsgs,
|
||||||
|
|
|
@ -280,10 +280,13 @@ app sections
|
||||||
|
|
||||||
/* unlock */
|
/* unlock */
|
||||||
.toast {
|
.toast {
|
||||||
border: 1px solid #60db97 !important;
|
background-image: url('../images/remove.svg');
|
||||||
|
background-size: 12px 12px;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: 5px 5px;
|
||||||
|
border: 1px solid !important;
|
||||||
color: #ffffff !important;
|
color: #ffffff !important;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
background: #60db97;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
width: 357px;
|
width: 357px;
|
||||||
|
@ -294,6 +297,17 @@ app sections
|
||||||
right: 0px;
|
right: 0px;
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
animation: 500ms ease-out 0s move;
|
animation: 500ms ease-out 0s move;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast.green {
|
||||||
|
background-color: #60db97;
|
||||||
|
border-color: #60db97 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast.red {
|
||||||
|
background-color: #ff1345;
|
||||||
|
border-color: #ff1345 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes move {
|
@keyframes move {
|
||||||
|
|
|
@ -10124,8 +10124,8 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"eth-hd-keyring": {
|
"eth-hd-keyring": {
|
||||||
"version": "https://registry.npmjs.org/eth-hd-keyring/-/eth-hd-keyring-2.0.0.tgz",
|
"version": "github:vbaranov/eth-hd-keyring#64d0fa741af88d5f232f9518fd150190c421b3e7",
|
||||||
"from": "eth-hd-keyring@2.0.0",
|
"from": "github:vbaranov/eth-hd-keyring#2.0.1",
|
||||||
"requires": {
|
"requires": {
|
||||||
"bip39": "^2.2.0",
|
"bip39": "^2.2.0",
|
||||||
"eth-sig-util": "^2.0.1",
|
"eth-sig-util": "^2.0.1",
|
||||||
|
@ -10450,7 +10450,7 @@
|
||||||
"bip39": "^2.4.0",
|
"bip39": "^2.4.0",
|
||||||
"bluebird": "^3.5.0",
|
"bluebird": "^3.5.0",
|
||||||
"browser-passworder": "^2.0.3",
|
"browser-passworder": "^2.0.3",
|
||||||
"eth-hd-keyring": "https://registry.npmjs.org/eth-hd-keyring/-/eth-hd-keyring-2.0.0.tgz",
|
"eth-hd-keyring": "github:vbaranov/eth-hd-keyring#64d0fa741af88d5f232f9518fd150190c421b3e7",
|
||||||
"eth-sig-util": "^1.4.0",
|
"eth-sig-util": "^1.4.0",
|
||||||
"eth-simple-keyring": "^2.0.0",
|
"eth-simple-keyring": "^2.0.0",
|
||||||
"ethereumjs-util": "^5.1.2",
|
"ethereumjs-util": "^5.1.2",
|
||||||
|
@ -10524,9 +10524,8 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"eth-ledger-bridge-keyring": {
|
"eth-ledger-bridge-keyring": {
|
||||||
"version": "0.1.1",
|
"version": "github:vbaranov/eth-ledger-bridge-keyring#ab97ae49167ddbe8442c06c857c3f8ca5f7cbcf6",
|
||||||
"resolved": "https://registry.npmjs.org/eth-ledger-bridge-keyring/-/eth-ledger-bridge-keyring-0.1.1.tgz",
|
"from": "github:vbaranov/eth-ledger-bridge-keyring#0.1.0-multiple-accounts",
|
||||||
"integrity": "sha512-EhClGSy5ixcd55yHGXoA3C7I8iFFi6kgSqvKOSj+5URtg5PYpHP8kv+KemFPOT1Px6se/IFHI9OIelUS8kN3lw==",
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"eth-sig-util": "^1.4.2",
|
"eth-sig-util": "^1.4.2",
|
||||||
"ethereumjs-tx": "^1.3.4",
|
"ethereumjs-tx": "^1.3.4",
|
||||||
|
@ -10540,7 +10539,18 @@
|
||||||
"resolved": "https://registry.npmjs.org/eth-sig-util/-/eth-sig-util-1.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/eth-sig-util/-/eth-sig-util-1.4.2.tgz",
|
||||||
"integrity": "sha1-jZWCAsftuq6Dlwf7pvCf8ydgYhA=",
|
"integrity": "sha1-jZWCAsftuq6Dlwf7pvCf8ydgYhA=",
|
||||||
"requires": {
|
"requires": {
|
||||||
|
"ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git#2863c40e0982acfc0b7163f0285d4c56427c7799",
|
||||||
"ethereumjs-util": "^5.1.1"
|
"ethereumjs-util": "^5.1.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"ethereumjs-abi": {
|
||||||
|
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#2863c40e0982acfc0b7163f0285d4c56427c7799",
|
||||||
|
"from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
|
||||||
|
"requires": {
|
||||||
|
"bn.js": "^4.10.0",
|
||||||
|
"ethereumjs-util": "^5.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ethereum-common": {
|
"ethereum-common": {
|
||||||
|
@ -10963,6 +10973,16 @@
|
||||||
"requires": {
|
"requires": {
|
||||||
"ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git#2863c40e0982acfc0b7163f0285d4c56427c7799",
|
"ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git#2863c40e0982acfc0b7163f0285d4c56427c7799",
|
||||||
"ethereumjs-util": "^5.1.1"
|
"ethereumjs-util": "^5.1.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"ethereumjs-abi": {
|
||||||
|
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#2863c40e0982acfc0b7163f0285d4c56427c7799",
|
||||||
|
"from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
|
||||||
|
"requires": {
|
||||||
|
"bn.js": "^4.10.0",
|
||||||
|
"ethereumjs-util": "^5.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ethereum-common": {
|
"ethereum-common": {
|
||||||
|
@ -14994,7 +15014,8 @@
|
||||||
"bindings": {
|
"bindings": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.3.0.tgz",
|
||||||
"integrity": "sha512-DpLh5EzMR2kzvX1KIlVC0VkC3iZtHKTgdtZ0a3pglBZdaQFjt5S9g9xd1lE+YvXyfd6mtCeRnrUfOLYiTMlNSw=="
|
"integrity": "sha512-DpLh5EzMR2kzvX1KIlVC0VkC3iZtHKTgdtZ0a3pglBZdaQFjt5S9g9xd1lE+YvXyfd6mtCeRnrUfOLYiTMlNSw==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"bip39": {
|
"bip39": {
|
||||||
"version": "2.5.0",
|
"version": "2.5.0",
|
||||||
|
@ -15013,6 +15034,7 @@
|
||||||
"version": "1.1.5",
|
"version": "1.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/bip66/-/bip66-1.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/bip66/-/bip66-1.1.5.tgz",
|
||||||
"integrity": "sha1-AfqHSHhcpwlV1QESF9GzE5lpyiI=",
|
"integrity": "sha1-AfqHSHhcpwlV1QESF9GzE5lpyiI=",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"safe-buffer": "^5.0.1"
|
"safe-buffer": "^5.0.1"
|
||||||
}
|
}
|
||||||
|
@ -15047,7 +15069,8 @@
|
||||||
"bn.js": {
|
"bn.js": {
|
||||||
"version": "4.11.8",
|
"version": "4.11.8",
|
||||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz",
|
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz",
|
||||||
"integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA=="
|
"integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"body-parser": {
|
"body-parser": {
|
||||||
"version": "1.18.3",
|
"version": "1.18.3",
|
||||||
|
@ -15090,12 +15113,14 @@
|
||||||
"brorand": {
|
"brorand": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
|
||||||
"integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8="
|
"integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"browserify-aes": {
|
"browserify-aes": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
|
"resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
|
||||||
"integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==",
|
"integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"buffer-xor": "^1.0.3",
|
"buffer-xor": "^1.0.3",
|
||||||
"cipher-base": "^1.0.0",
|
"cipher-base": "^1.0.0",
|
||||||
|
@ -15252,7 +15277,8 @@
|
||||||
"buffer-xor": {
|
"buffer-xor": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz",
|
||||||
"integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk="
|
"integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"builtin-modules": {
|
"builtin-modules": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
|
@ -15350,6 +15376,7 @@
|
||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
|
||||||
"integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==",
|
"integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"inherits": "^2.0.1",
|
"inherits": "^2.0.1",
|
||||||
"safe-buffer": "^5.0.1"
|
"safe-buffer": "^5.0.1"
|
||||||
|
@ -15509,6 +15536,7 @@
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "http://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
|
"resolved": "http://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
|
||||||
"integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
|
"integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"cipher-base": "^1.0.1",
|
"cipher-base": "^1.0.1",
|
||||||
"inherits": "^2.0.1",
|
"inherits": "^2.0.1",
|
||||||
|
@ -15521,6 +15549,7 @@
|
||||||
"version": "1.1.7",
|
"version": "1.1.7",
|
||||||
"resolved": "http://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
|
"resolved": "http://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
|
||||||
"integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
|
"integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"cipher-base": "^1.0.3",
|
"cipher-base": "^1.0.3",
|
||||||
"create-hash": "^1.1.0",
|
"create-hash": "^1.1.0",
|
||||||
|
@ -15820,6 +15849,7 @@
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/drbg.js/-/drbg.js-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/drbg.js/-/drbg.js-1.0.1.tgz",
|
||||||
"integrity": "sha1-Pja2xCs3BDgjzbwzLVjzHiRFSAs=",
|
"integrity": "sha1-Pja2xCs3BDgjzbwzLVjzHiRFSAs=",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"browserify-aes": "^1.0.6",
|
"browserify-aes": "^1.0.6",
|
||||||
"create-hash": "^1.1.2",
|
"create-hash": "^1.1.2",
|
||||||
|
@ -15858,6 +15888,7 @@
|
||||||
"version": "6.4.1",
|
"version": "6.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz",
|
||||||
"integrity": "sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==",
|
"integrity": "sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"bn.js": "^4.4.0",
|
"bn.js": "^4.4.0",
|
||||||
"brorand": "^1.0.1",
|
"brorand": "^1.0.1",
|
||||||
|
@ -16150,17 +16181,6 @@
|
||||||
"requires": {
|
"requires": {
|
||||||
"ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git#2863c40e0982acfc0b7163f0285d4c56427c7799",
|
"ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git#2863c40e0982acfc0b7163f0285d4c56427c7799",
|
||||||
"ethereumjs-util": "^5.1.1"
|
"ethereumjs-util": "^5.1.1"
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"ethereumjs-abi": {
|
|
||||||
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#2863c40e0982acfc0b7163f0285d4c56427c7799",
|
|
||||||
"from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"bn.js": "^4.10.0",
|
|
||||||
"ethereumjs-util": "^5.0.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ethereum-common": {
|
"ethereum-common": {
|
||||||
|
@ -16171,7 +16191,8 @@
|
||||||
},
|
},
|
||||||
"ethereumjs-abi": {
|
"ethereumjs-abi": {
|
||||||
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#2863c40e0982acfc0b7163f0285d4c56427c7799",
|
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#2863c40e0982acfc0b7163f0285d4c56427c7799",
|
||||||
"from": "git+https://github.com/ethereumjs/ethereumjs-abi.git#2863c40e0982acfc0b7163f0285d4c56427c7799",
|
"from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"bn.js": "^4.10.0",
|
"bn.js": "^4.10.0",
|
||||||
"ethereumjs-util": "^5.0.0"
|
"ethereumjs-util": "^5.0.0"
|
||||||
|
@ -16366,6 +16387,7 @@
|
||||||
"version": "5.2.0",
|
"version": "5.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.0.tgz",
|
||||||
"integrity": "sha512-CJAKdI0wgMbQFLlLRtZKGcy/L6pzVRgelIZqRqNbuVFM3K9VEnyfbcvz0ncWMRNCe4kaHWjwRYQcYMucmwsnWA==",
|
"integrity": "sha512-CJAKdI0wgMbQFLlLRtZKGcy/L6pzVRgelIZqRqNbuVFM3K9VEnyfbcvz0ncWMRNCe4kaHWjwRYQcYMucmwsnWA==",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"bn.js": "^4.11.0",
|
"bn.js": "^4.11.0",
|
||||||
"create-hash": "^1.1.2",
|
"create-hash": "^1.1.2",
|
||||||
|
@ -16455,6 +16477,7 @@
|
||||||
"version": "0.1.6",
|
"version": "0.1.6",
|
||||||
"resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz",
|
"resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz",
|
||||||
"integrity": "sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w==",
|
"integrity": "sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w==",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"is-hex-prefixed": "1.0.0",
|
"is-hex-prefixed": "1.0.0",
|
||||||
"strip-hex-prefix": "1.0.0"
|
"strip-hex-prefix": "1.0.0"
|
||||||
|
@ -16476,6 +16499,7 @@
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz",
|
||||||
"integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==",
|
"integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"md5.js": "^1.3.4",
|
"md5.js": "^1.3.4",
|
||||||
"safe-buffer": "^5.1.1"
|
"safe-buffer": "^5.1.1"
|
||||||
|
@ -16886,6 +16910,7 @@
|
||||||
"version": "3.0.4",
|
"version": "3.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz",
|
||||||
"integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=",
|
"integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"inherits": "^2.0.1",
|
"inherits": "^2.0.1",
|
||||||
"safe-buffer": "^5.0.1"
|
"safe-buffer": "^5.0.1"
|
||||||
|
@ -16895,6 +16920,7 @@
|
||||||
"version": "1.1.5",
|
"version": "1.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.5.tgz",
|
||||||
"integrity": "sha512-eWI5HG9Np+eHV1KQhisXWwM+4EPPYe5dFX1UZZH7k/E3JzDEazVH+VGlZi6R94ZqImq+A3D1mCEtrFIfg/E7sA==",
|
"integrity": "sha512-eWI5HG9Np+eHV1KQhisXWwM+4EPPYe5dFX1UZZH7k/E3JzDEazVH+VGlZi6R94ZqImq+A3D1mCEtrFIfg/E7sA==",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"inherits": "^2.0.3",
|
"inherits": "^2.0.3",
|
||||||
"minimalistic-assert": "^1.0.1"
|
"minimalistic-assert": "^1.0.1"
|
||||||
|
@ -16922,6 +16948,7 @@
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
|
||||||
"integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=",
|
"integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"hash.js": "^1.0.3",
|
"hash.js": "^1.0.3",
|
||||||
"minimalistic-assert": "^1.0.0",
|
"minimalistic-assert": "^1.0.0",
|
||||||
|
@ -17089,7 +17116,8 @@
|
||||||
"is-hex-prefixed": {
|
"is-hex-prefixed": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz",
|
||||||
"integrity": "sha1-fY035q135dEnFIkTxXPggtd39VQ="
|
"integrity": "sha1-fY035q135dEnFIkTxXPggtd39VQ=",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"is-natural-number": {
|
"is-natural-number": {
|
||||||
"version": "4.0.1",
|
"version": "4.0.1",
|
||||||
|
@ -17290,6 +17318,7 @@
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/keccak/-/keccak-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/keccak/-/keccak-1.4.0.tgz",
|
||||||
"integrity": "sha512-eZVaCpblK5formjPjeTBik7TAg+pqnDrMHIffSvi9Lh7PQgM1+hSzakUeZFCk9DVVG0dacZJuaz2ntwlzZUIBw==",
|
"integrity": "sha512-eZVaCpblK5formjPjeTBik7TAg+pqnDrMHIffSvi9Lh7PQgM1+hSzakUeZFCk9DVVG0dacZJuaz2ntwlzZUIBw==",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"bindings": "^1.2.1",
|
"bindings": "^1.2.1",
|
||||||
"inherits": "^2.0.3",
|
"inherits": "^2.0.3",
|
||||||
|
@ -17589,6 +17618,7 @@
|
||||||
"version": "1.3.5",
|
"version": "1.3.5",
|
||||||
"resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
|
"resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
|
||||||
"integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==",
|
"integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"hash-base": "^3.0.0",
|
"hash-base": "^3.0.0",
|
||||||
"inherits": "^2.0.1",
|
"inherits": "^2.0.1",
|
||||||
|
@ -17748,12 +17778,14 @@
|
||||||
"minimalistic-assert": {
|
"minimalistic-assert": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
|
||||||
"integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="
|
"integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"minimalistic-crypto-utils": {
|
"minimalistic-crypto-utils": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
|
||||||
"integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo="
|
"integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"minimatch": {
|
"minimatch": {
|
||||||
"version": "3.0.4",
|
"version": "3.0.4",
|
||||||
|
@ -17823,7 +17855,8 @@
|
||||||
"nan": {
|
"nan": {
|
||||||
"version": "2.10.0",
|
"version": "2.10.0",
|
||||||
"resolved": "http://registry.npmjs.org/nan/-/nan-2.10.0.tgz",
|
"resolved": "http://registry.npmjs.org/nan/-/nan-2.10.0.tgz",
|
||||||
"integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA=="
|
"integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"nano-json-stream-parser": {
|
"nano-json-stream-parser": {
|
||||||
"version": "0.1.2",
|
"version": "0.1.2",
|
||||||
|
@ -18490,6 +18523,7 @@
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz",
|
||||||
"integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==",
|
"integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"hash-base": "^3.0.0",
|
"hash-base": "^3.0.0",
|
||||||
"inherits": "^2.0.1"
|
"inherits": "^2.0.1"
|
||||||
|
@ -18499,6 +18533,7 @@
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/rlp/-/rlp-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/rlp/-/rlp-2.1.0.tgz",
|
||||||
"integrity": "sha512-93U7IKH5j7nmXFVg19MeNBGzQW5uXW1pmCuKY8veeKIhYTE32C2d0mOegfiIAfXcHOKJjjPlJisn8iHDF5AezA==",
|
"integrity": "sha512-93U7IKH5j7nmXFVg19MeNBGzQW5uXW1pmCuKY8veeKIhYTE32C2d0mOegfiIAfXcHOKJjjPlJisn8iHDF5AezA==",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"safe-buffer": "^5.1.1"
|
"safe-buffer": "^5.1.1"
|
||||||
}
|
}
|
||||||
|
@ -18512,7 +18547,8 @@
|
||||||
"safe-buffer": {
|
"safe-buffer": {
|
||||||
"version": "5.1.2",
|
"version": "5.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"safe-event-emitter": {
|
"safe-event-emitter": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
|
@ -18564,6 +18600,7 @@
|
||||||
"version": "3.5.2",
|
"version": "3.5.2",
|
||||||
"resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-3.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-3.5.2.tgz",
|
||||||
"integrity": "sha512-iin3kojdybY6NArd+UFsoTuapOF7bnJNf2UbcWXaY3z+E1sJDipl60vtzB5hbO/uquBu7z0fd4VC4Irp+xoFVQ==",
|
"integrity": "sha512-iin3kojdybY6NArd+UFsoTuapOF7bnJNf2UbcWXaY3z+E1sJDipl60vtzB5hbO/uquBu7z0fd4VC4Irp+xoFVQ==",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"bindings": "^1.2.1",
|
"bindings": "^1.2.1",
|
||||||
"bip66": "^1.1.3",
|
"bip66": "^1.1.3",
|
||||||
|
@ -18695,6 +18732,7 @@
|
||||||
"version": "2.4.11",
|
"version": "2.4.11",
|
||||||
"resolved": "http://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
|
"resolved": "http://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
|
||||||
"integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
|
"integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"inherits": "^2.0.1",
|
"inherits": "^2.0.1",
|
||||||
"safe-buffer": "^5.0.1"
|
"safe-buffer": "^5.0.1"
|
||||||
|
@ -18905,6 +18943,7 @@
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz",
|
||||||
"integrity": "sha1-DF8VX+8RUTczd96du1iNoFUA428=",
|
"integrity": "sha1-DF8VX+8RUTczd96du1iNoFUA428=",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"is-hex-prefixed": "1.0.0"
|
"is-hex-prefixed": "1.0.0"
|
||||||
}
|
}
|
||||||
|
|
|
@ -116,7 +116,7 @@
|
||||||
"eth-json-rpc-filters": "github:poanetwork/eth-json-rpc-filters#3.0.2",
|
"eth-json-rpc-filters": "github:poanetwork/eth-json-rpc-filters#3.0.2",
|
||||||
"eth-json-rpc-infura": "^3.0.0",
|
"eth-json-rpc-infura": "^3.0.0",
|
||||||
"eth-keychain-controller": "github:vbaranov/KeyringController#simple-address",
|
"eth-keychain-controller": "github:vbaranov/KeyringController#simple-address",
|
||||||
"eth-ledger-bridge-keyring": "^0.1.0",
|
"eth-ledger-bridge-keyring": "github:vbaranov/eth-ledger-bridge-keyring#0.1.0-multiple-accounts",
|
||||||
"eth-method-registry": "^1.0.0",
|
"eth-method-registry": "^1.0.0",
|
||||||
"eth-net-props": "^1.0.10",
|
"eth-net-props": "^1.0.10",
|
||||||
"eth-phishing-detect": "^1.1.4",
|
"eth-phishing-detect": "^1.1.4",
|
||||||
|
|
|
@ -30,6 +30,7 @@ module.exports = {
|
||||||
info: By.css('li.dropdown-menu-item:nth-child(4)'),
|
info: By.css('li.dropdown-menu-item:nth-child(4)'),
|
||||||
},
|
},
|
||||||
account: {
|
account: {
|
||||||
|
item: By.className('dropdown-menu-item'),
|
||||||
account1: By.css('#app-content > div > div.full-width > div.full-width > div > div:nth-child(2) > span > div > div > span > div > li:nth-child(2) > span'),
|
account1: By.css('#app-content > div > div.full-width > div.full-width > div > div:nth-child(2) > span > div > div > span > div > li:nth-child(2) > span'),
|
||||||
account2: By.css('#app-content > div > div.full-width > div.full-width > div > div:nth-child(2) > span > div > div > span > div > li:nth-child(3) > span'),
|
account2: By.css('#app-content > div > div.full-width > div.full-width > div > div:nth-child(2) > span > div > div > span > div > li:nth-child(3) > span'),
|
||||||
account3: By.css('#app-content > div > div.full-width > div.full-width > div > div:nth-child(2) > span > div > div > span > div > li:nth-child(4) > span'),
|
account3: By.css('#app-content > div > div.full-width > div.full-width > div > div:nth-child(2) > span > div > div > span > div > li:nth-child(4) > span'),
|
||||||
|
@ -54,6 +55,17 @@ module.exports = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
screens: {
|
screens: {
|
||||||
|
hdWallet: {
|
||||||
|
buttonArrow: By.className('fa fa-arrow-left fa-lg cursor-pointer'),
|
||||||
|
error: By.className('error'),
|
||||||
|
title: By.className('section-title flex-row flex-center'),
|
||||||
|
buttonConnect: {
|
||||||
|
enabled: By.className('hw-connect__connect-btn'),
|
||||||
|
disabled: By.className('hw-connect__connect-btn disabled'),
|
||||||
|
},
|
||||||
|
image: By.className('hw-connect__btn__img'),
|
||||||
|
imageSelected: By.className('hw-connect__btn selected'),
|
||||||
|
},
|
||||||
chooseContractExecutor: {
|
chooseContractExecutor: {
|
||||||
title: By.className('flex-center send-header'),
|
title: By.className('flex-center send-header'),
|
||||||
titleText: 'Choose contract executor',
|
titleText: 'Choose contract executor',
|
||||||
|
|
|
@ -269,6 +269,97 @@ describe('Metamask popup page', async function () {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('Connect Hardware Wallet', async function () {
|
||||||
|
|
||||||
|
it("Account menu contais item 'Connect HD wallet'", async function () {
|
||||||
|
const menu = await f.waitUntilShowUp(menus.account.menu)
|
||||||
|
await menu.click()
|
||||||
|
await f.waitUntilShowUp(menus.account.item)
|
||||||
|
const items = await driver.findElements(menus.account.item)
|
||||||
|
await f.delay(500)
|
||||||
|
assert.equal(await items[4].getText(), 'Connect hardware wallet', "item's text incorrect")
|
||||||
|
await items[4].click()
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
it("Opens screen 'Connect HD wallet',title is correct", async function () {
|
||||||
|
const title = await f.waitUntilShowUp(screens.hdWallet.title)
|
||||||
|
assert.equal(await title.getText(), 'Connect to hardware wallet', "item's text incorrect")
|
||||||
|
})
|
||||||
|
|
||||||
|
if (process.env.SELENIUM_BROWSER === 'chrome') {
|
||||||
|
it("Button 'Connect' disabled by default", async function () {
|
||||||
|
const button = await f.waitUntilShowUp(screens.hdWallet.buttonConnect.disabled)
|
||||||
|
assert.notEqual(button, false, "button isn't displayed")
|
||||||
|
assert.equal(await button.getText(), 'CONNECT', 'button has incorrect text')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Ledger image is displayed', async function () {
|
||||||
|
const image = await f.waitUntilShowUp(screens.hdWallet.image)
|
||||||
|
assert.notEqual(image, false, "ledger's image isn't displayed")
|
||||||
|
const src = await image.getAttribute('src')
|
||||||
|
assert.equal(src.includes('images/ledger-logo.svg'), true, 'Ledger has incorrect image')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Trezor image is displayed', async function () {
|
||||||
|
const images = await driver.findElements(screens.hdWallet.image)
|
||||||
|
assert.notEqual(images[1], false, "trezor's image isn't displayed")
|
||||||
|
const src = await images[1].getAttribute('src')
|
||||||
|
assert.equal(src.includes('images/trezor-logo.svg'), true, 'Trezor has incorrect image')
|
||||||
|
})
|
||||||
|
|
||||||
|
it("Button 'Connect' enabled if Trezor selected", async function () {
|
||||||
|
const images = await driver.findElements(screens.hdWallet.image)
|
||||||
|
await images[1].click()
|
||||||
|
const button = await f.waitUntilShowUp(screens.hdWallet.buttonConnect.enabled)
|
||||||
|
assert.equal(await button.isEnabled(), true, 'button is disabled')
|
||||||
|
})
|
||||||
|
|
||||||
|
it("Button 'Connect' enabled if Ledger selected", async function () {
|
||||||
|
const images = await driver.findElements(screens.hdWallet.image)
|
||||||
|
await images[0].click()
|
||||||
|
const button = await f.waitUntilShowUp(screens.hdWallet.buttonConnect.enabled)
|
||||||
|
assert.equal(await button.isEnabled(), true, 'button is disabled')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Only one device can be selected', async function () {
|
||||||
|
const selected = await driver.findElements(screens.hdWallet.imageSelected)
|
||||||
|
assert.equal(await selected.length, 1, 'more than one device is selected')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Error message if connect Ledger', async function () {
|
||||||
|
const button = await f.waitUntilShowUp(screens.hdWallet.buttonConnect.enabled)
|
||||||
|
await button.click()
|
||||||
|
const error = await f.waitUntilShowUp(screens.hdWallet.error)
|
||||||
|
const shouldBe = "TransportError: U2F browser support is needed for Ledger. Please use Chrome, Opera or Firefox with a U2F extension. Also make sure you're on an HTTPS connection"
|
||||||
|
assert.equal(await error.getText(), shouldBe, 'error has incorrect text')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Popup opens if connect Trezor', async function () {
|
||||||
|
const images = await driver.findElements(screens.hdWallet.image)
|
||||||
|
await images[1].click()
|
||||||
|
const button = await f.waitUntilShowUp(screens.hdWallet.buttonConnect.enabled)
|
||||||
|
await button.click()
|
||||||
|
const allHandles = await driver.getAllWindowHandles()
|
||||||
|
assert.equal(allHandles.length, 2, "popup isn't opened")
|
||||||
|
await f.switchToFirstPage()
|
||||||
|
await driver.navigate().refresh()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
it('Button arrow leads to main screen', async function () {
|
||||||
|
const menu = await f.waitUntilShowUp(menus.account.menu)
|
||||||
|
await menu.click()
|
||||||
|
await f.waitUntilShowUp(menus.account.item)
|
||||||
|
const items = await driver.findElements(menus.account.item)
|
||||||
|
await f.delay(500)
|
||||||
|
await items[4].click()
|
||||||
|
const arrow = await f.waitUntilShowUp(screens.hdWallet.buttonArrow)
|
||||||
|
await arrow.click()
|
||||||
|
const ident = await f.waitUntilShowUp(screens.main.identicon, 20)
|
||||||
|
assert.notEqual(ident, false, "main screen isn't opened")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('Import Account', async function () {
|
describe('Import Account', async function () {
|
||||||
|
|
||||||
it('Open import account menu', async function () {
|
it('Open import account menu', async function () {
|
||||||
|
|
|
@ -0,0 +1,137 @@
|
||||||
|
const assert = require('assert')
|
||||||
|
const sinon = require('sinon')
|
||||||
|
const CachedBalancesController = require('../../../../app/scripts/controllers/cached-balances')
|
||||||
|
|
||||||
|
describe('CachedBalancesController', () => {
|
||||||
|
describe('updateCachedBalances', () => {
|
||||||
|
it('should update the cached balances', async () => {
|
||||||
|
const controller = new CachedBalancesController({
|
||||||
|
getNetwork: () => Promise.resolve(17),
|
||||||
|
accountTracker: {
|
||||||
|
store: {
|
||||||
|
subscribe: () => {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
initState: {
|
||||||
|
cachedBalances: 'mockCachedBalances',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
controller._generateBalancesToCache = sinon.stub().callsFake(() => Promise.resolve('mockNewCachedBalances'))
|
||||||
|
|
||||||
|
await controller.updateCachedBalances({ accounts: 'mockAccounts' })
|
||||||
|
|
||||||
|
assert.equal(controller._generateBalancesToCache.callCount, 1)
|
||||||
|
assert.deepEqual(controller._generateBalancesToCache.args[0], ['mockAccounts', 17])
|
||||||
|
assert.equal(controller.store.getState().cachedBalances, 'mockNewCachedBalances')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('_generateBalancesToCache', () => {
|
||||||
|
it('should generate updated account balances where the current network was updated', () => {
|
||||||
|
const controller = new CachedBalancesController({
|
||||||
|
accountTracker: {
|
||||||
|
store: {
|
||||||
|
subscribe: () => {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
initState: {
|
||||||
|
cachedBalances: {
|
||||||
|
17: {
|
||||||
|
a: '0x1',
|
||||||
|
b: '0x2',
|
||||||
|
c: '0x3',
|
||||||
|
},
|
||||||
|
16: {
|
||||||
|
a: '0xa',
|
||||||
|
b: '0xb',
|
||||||
|
c: '0xc',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const result = controller._generateBalancesToCache({
|
||||||
|
a: { balance: '0x4' },
|
||||||
|
b: { balance: null },
|
||||||
|
c: { balance: '0x5' },
|
||||||
|
}, 17)
|
||||||
|
|
||||||
|
assert.deepEqual(result, {
|
||||||
|
17: {
|
||||||
|
a: '0x4',
|
||||||
|
b: '0x2',
|
||||||
|
c: '0x5',
|
||||||
|
},
|
||||||
|
16: {
|
||||||
|
a: '0xa',
|
||||||
|
b: '0xb',
|
||||||
|
c: '0xc',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should generate updated account balances where the a new network was selected', () => {
|
||||||
|
const controller = new CachedBalancesController({
|
||||||
|
accountTracker: {
|
||||||
|
store: {
|
||||||
|
subscribe: () => {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
initState: {
|
||||||
|
cachedBalances: {
|
||||||
|
17: {
|
||||||
|
a: '0x1',
|
||||||
|
b: '0x2',
|
||||||
|
c: '0x3',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const result = controller._generateBalancesToCache({
|
||||||
|
a: { balance: '0x4' },
|
||||||
|
b: { balance: null },
|
||||||
|
c: { balance: '0x5' },
|
||||||
|
}, 16)
|
||||||
|
|
||||||
|
assert.deepEqual(result, {
|
||||||
|
17: {
|
||||||
|
a: '0x1',
|
||||||
|
b: '0x2',
|
||||||
|
c: '0x3',
|
||||||
|
},
|
||||||
|
16: {
|
||||||
|
a: '0x4',
|
||||||
|
c: '0x5',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('_registerUpdates', () => {
|
||||||
|
it('should subscribe to the account tracker with the updateCachedBalances method', async () => {
|
||||||
|
const subscribeSpy = sinon.spy()
|
||||||
|
const controller = new CachedBalancesController({
|
||||||
|
getNetwork: () => Promise.resolve(17),
|
||||||
|
accountTracker: {
|
||||||
|
store: {
|
||||||
|
subscribe: subscribeSpy,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
subscribeSpy.resetHistory()
|
||||||
|
|
||||||
|
const updateCachedBalancesSpy = sinon.spy()
|
||||||
|
controller.updateCachedBalances = updateCachedBalancesSpy
|
||||||
|
controller._registerUpdates({ accounts: 'mockAccounts' })
|
||||||
|
|
||||||
|
assert.equal(subscribeSpy.callCount, 1)
|
||||||
|
|
||||||
|
subscribeSpy.args[0][0]()
|
||||||
|
|
||||||
|
assert.equal(updateCachedBalancesSpy.callCount, 1)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
|
@ -358,7 +358,8 @@ describe('MetaMaskController', function () {
|
||||||
let addNewAccountStub
|
let addNewAccountStub
|
||||||
let getAccountsStub
|
let getAccountsStub
|
||||||
beforeEach(async function () {
|
beforeEach(async function () {
|
||||||
accountToUnlock = 10
|
this.timeout(10000)
|
||||||
|
accountToUnlock = 4
|
||||||
windowOpenStub = sinon.stub(window, 'open')
|
windowOpenStub = sinon.stub(window, 'open')
|
||||||
windowOpenStub.returns(noop)
|
windowOpenStub.returns(noop)
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import assert from 'assert'
|
import assert from 'assert'
|
||||||
import SendContractError from '../../../../../../old-ui/app/components/send/send-error'
|
import ErrorComponent from '../../../../../old-ui/app/components/error'
|
||||||
import { mount } from 'enzyme'
|
import { mount } from 'enzyme'
|
||||||
import thunk from 'redux-thunk'
|
import thunk from 'redux-thunk'
|
||||||
import configureMockStore from 'redux-mock-store'
|
import configureMockStore from 'redux-mock-store'
|
||||||
|
@ -16,12 +16,12 @@ const mockStore = configureMockStore(middlewares)
|
||||||
const store = mockStore(state)
|
const store = mockStore(state)
|
||||||
let wrapper
|
let wrapper
|
||||||
|
|
||||||
describe('SendContractError component', () => {
|
describe('ErrorComponent', () => {
|
||||||
describe('renders SendContractError component', () => {
|
describe('renders ErrorComponent', () => {
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
wrapper = mount(
|
wrapper = mount(
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<SendContractError error="Error!"/>
|
<ErrorComponent error="Error!"/>
|
||||||
</Provider>
|
</Provider>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -30,11 +30,11 @@ describe('SendContractError component', () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('doesn\'t render SendContractError component', () => {
|
describe('doesn\'t render ErrorComponent component', () => {
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
wrapper = mount(
|
wrapper = mount(
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<SendContractError/>
|
<ErrorComponent/>
|
||||||
</Provider>
|
</Provider>
|
||||||
)
|
)
|
||||||
})
|
})
|
|
@ -12,6 +12,7 @@ describe('ChooseContractExecutor component', () => {
|
||||||
metamask: {
|
metamask: {
|
||||||
selectedAddress: '0x99a22ce737b6a48f44cad6331432ce98693cad07',
|
selectedAddress: '0x99a22ce737b6a48f44cad6331432ce98693cad07',
|
||||||
accounts: ['0x99a22ce737b6a48f44cad6331432ce98693cad07'],
|
accounts: ['0x99a22ce737b6a48f44cad6331432ce98693cad07'],
|
||||||
|
cachedBalances: {'0x99a22ce737b6a48f44cad6331432ce98693cad07': 1},
|
||||||
keyrings: [
|
keyrings: [
|
||||||
{
|
{
|
||||||
'type': 'HD Key Tree',
|
'type': 'HD Key Tree',
|
||||||
|
|
|
@ -11,6 +11,7 @@ const state = {
|
||||||
metamask: {
|
metamask: {
|
||||||
selectedAddress: '0x99a22ce737b6a48f44cad6331432ce98693cad07',
|
selectedAddress: '0x99a22ce737b6a48f44cad6331432ce98693cad07',
|
||||||
accounts: ['0x99a22ce737b6a48f44cad6331432ce98693cad07'],
|
accounts: ['0x99a22ce737b6a48f44cad6331432ce98693cad07'],
|
||||||
|
cachedBalances: {'0x99a22ce737b6a48f44cad6331432ce98693cad07': 1},
|
||||||
identities: {
|
identities: {
|
||||||
'0x99a22ce737b6a48f44cad6331432ce98693cad07': {
|
'0x99a22ce737b6a48f44cad6331432ce98693cad07': {
|
||||||
name: 'Account 1',
|
name: 'Account 1',
|
||||||
|
|
|
@ -91,6 +91,7 @@ var actions = {
|
||||||
importNewAccount,
|
importNewAccount,
|
||||||
addNewAccount,
|
addNewAccount,
|
||||||
connectHardware,
|
connectHardware,
|
||||||
|
connectHardwareAndUnlockAddress,
|
||||||
checkHardwareStatus,
|
checkHardwareStatus,
|
||||||
forgetDevice,
|
forgetDevice,
|
||||||
unlockHardwareWalletAccount,
|
unlockHardwareWalletAccount,
|
||||||
|
@ -788,6 +789,23 @@ function connectHardware (deviceName, page, hdPath) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function connectHardwareAndUnlockAddress (deviceName, hdPath, addressToUnlock) {
|
||||||
|
log.debug(`background.connectHardwareAndUnlockAddress`, deviceName, hdPath, addressToUnlock)
|
||||||
|
return (dispatch, getState) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
background.connectHardwareAndUnlockAddress(deviceName, hdPath, addressToUnlock, (err, accounts) => {
|
||||||
|
if (err) {
|
||||||
|
log.error(err)
|
||||||
|
return reject(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
forceUpdateMetamaskState(dispatch)
|
||||||
|
return resolve(accounts)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function unlockHardwareWalletAccount (index, deviceName, hdPath) {
|
function unlockHardwareWalletAccount (index, deviceName, hdPath) {
|
||||||
log.debug(`background.unlockHardwareWalletAccount`, index, deviceName, hdPath)
|
log.debug(`background.unlockHardwareWalletAccount`, index, deviceName, hdPath)
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
|
|
|
@ -7,6 +7,7 @@ const h = require('react-hyperscript')
|
||||||
const actions = require('./actions')
|
const actions = require('./actions')
|
||||||
const classnames = require('classnames')
|
const classnames = require('classnames')
|
||||||
const log = require('loglevel')
|
const log = require('loglevel')
|
||||||
|
const { getMetaMaskAccounts } = require('./selectors')
|
||||||
|
|
||||||
// init
|
// init
|
||||||
const InitializeScreen = require('../../mascara/src/app/first-time').default
|
const InitializeScreen = require('../../mascara/src/app/first-time').default
|
||||||
|
@ -279,9 +280,10 @@ function mapStateToProps (state) {
|
||||||
loadingMessage,
|
loadingMessage,
|
||||||
} = appState
|
} = appState
|
||||||
|
|
||||||
|
const accounts = getMetaMaskAccounts(state)
|
||||||
|
|
||||||
const {
|
const {
|
||||||
identities,
|
identities,
|
||||||
accounts,
|
|
||||||
address,
|
address,
|
||||||
keyrings,
|
keyrings,
|
||||||
isInitialized,
|
isInitialized,
|
||||||
|
|
|
@ -13,6 +13,7 @@ const { getEnvironmentType } = require('../../../../app/scripts/lib/util')
|
||||||
const Tooltip = require('../tooltip')
|
const Tooltip = require('../tooltip')
|
||||||
import UserPreferencedCurrencyDisplay from '../user-preferenced-currency-display'
|
import UserPreferencedCurrencyDisplay from '../user-preferenced-currency-display'
|
||||||
import { PRIMARY } from '../../constants/common'
|
import { PRIMARY } from '../../constants/common'
|
||||||
|
import { getMetaMaskAccounts } from '../../selectors'
|
||||||
|
|
||||||
const {
|
const {
|
||||||
SETTINGS_ROUTE,
|
SETTINGS_ROUTE,
|
||||||
|
@ -41,7 +42,7 @@ function mapStateToProps (state) {
|
||||||
isAccountMenuOpen: state.metamask.isAccountMenuOpen,
|
isAccountMenuOpen: state.metamask.isAccountMenuOpen,
|
||||||
keyrings: state.metamask.keyrings,
|
keyrings: state.metamask.keyrings,
|
||||||
identities: state.metamask.identities,
|
identities: state.metamask.identities,
|
||||||
accounts: state.metamask.accounts,
|
accounts: getMetaMaskAccounts(state),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,14 +6,14 @@ const TokenBalance = require('./token-balance')
|
||||||
const Identicon = require('./identicon')
|
const Identicon = require('./identicon')
|
||||||
import UserPreferencedCurrencyDisplay from './user-preferenced-currency-display'
|
import UserPreferencedCurrencyDisplay from './user-preferenced-currency-display'
|
||||||
import { PRIMARY, SECONDARY } from '../constants/common'
|
import { PRIMARY, SECONDARY } from '../constants/common'
|
||||||
const { getAssetImages, conversionRateSelector, getCurrentCurrency} = require('../selectors')
|
const { getAssetImages, conversionRateSelector, getCurrentCurrency, getMetaMaskAccounts } = require('../selectors')
|
||||||
|
|
||||||
const { formatBalance } = require('../util')
|
const { formatBalance } = require('../util')
|
||||||
|
|
||||||
module.exports = connect(mapStateToProps)(BalanceComponent)
|
module.exports = connect(mapStateToProps)(BalanceComponent)
|
||||||
|
|
||||||
function mapStateToProps (state) {
|
function mapStateToProps (state) {
|
||||||
const accounts = state.metamask.accounts
|
const accounts = getMetaMaskAccounts(state)
|
||||||
const network = state.metamask.network
|
const network = state.metamask.network
|
||||||
const selectedAddress = state.metamask.selectedAddress || Object.keys(accounts)[0]
|
const selectedAddress = state.metamask.selectedAddress || Object.keys(accounts)[0]
|
||||||
const account = accounts[selectedAddress]
|
const account = accounts[selectedAddress]
|
||||||
|
|
|
@ -18,6 +18,7 @@ import { isBalanceSufficient } from '../../send/send.utils'
|
||||||
import { conversionGreaterThan } from '../../../conversion-util'
|
import { conversionGreaterThan } from '../../../conversion-util'
|
||||||
import { MIN_GAS_LIMIT_DEC } from '../../send/send.constants'
|
import { MIN_GAS_LIMIT_DEC } from '../../send/send.constants'
|
||||||
import { addressSlicer, valuesFor } from '../../../util'
|
import { addressSlicer, valuesFor } from '../../../util'
|
||||||
|
import { getMetaMaskAccounts } from '../../../selectors'
|
||||||
|
|
||||||
const casedContractMap = Object.keys(contractMap).reduce((acc, base) => {
|
const casedContractMap = Object.keys(contractMap).reduce((acc, base) => {
|
||||||
return {
|
return {
|
||||||
|
@ -47,11 +48,11 @@ const mapStateToProps = (state, props) => {
|
||||||
} = confirmTransaction
|
} = confirmTransaction
|
||||||
const { txParams = {}, lastGasPrice, id: transactionId } = txData
|
const { txParams = {}, lastGasPrice, id: transactionId } = txData
|
||||||
const { from: fromAddress, to: txParamsToAddress } = txParams
|
const { from: fromAddress, to: txParamsToAddress } = txParams
|
||||||
|
const accounts = getMetaMaskAccounts(state)
|
||||||
const {
|
const {
|
||||||
conversionRate,
|
conversionRate,
|
||||||
identities,
|
identities,
|
||||||
currentCurrency,
|
currentCurrency,
|
||||||
accounts,
|
|
||||||
selectedAddress,
|
selectedAddress,
|
||||||
selectedAddressTxList,
|
selectedAddressTxList,
|
||||||
assetImages,
|
assetImages,
|
||||||
|
|
|
@ -3,6 +3,7 @@ const PropTypes = require('prop-types')
|
||||||
const h = require('react-hyperscript')
|
const h = require('react-hyperscript')
|
||||||
const connect = require('react-redux').connect
|
const connect = require('react-redux').connect
|
||||||
const actions = require('../../../../actions')
|
const actions = require('../../../../actions')
|
||||||
|
const { getMetaMaskAccounts } = require('../../../../selectors')
|
||||||
const ConnectScreen = require('./connect-screen')
|
const ConnectScreen = require('./connect-screen')
|
||||||
const AccountList = require('./account-list')
|
const AccountList = require('./account-list')
|
||||||
const { DEFAULT_ROUTE } = require('../../../../routes')
|
const { DEFAULT_ROUTE } = require('../../../../routes')
|
||||||
|
@ -225,8 +226,9 @@ ConnectHardwareForm.propTypes = {
|
||||||
|
|
||||||
const mapStateToProps = state => {
|
const mapStateToProps = state => {
|
||||||
const {
|
const {
|
||||||
metamask: { network, selectedAddress, identities = {}, accounts = [] },
|
metamask: { network, selectedAddress, identities = {} },
|
||||||
} = state
|
} = state
|
||||||
|
const accounts = getMetaMaskAccounts(state)
|
||||||
const numberOfExistingAccounts = Object.keys(identities).length
|
const numberOfExistingAccounts = Object.keys(identities).length
|
||||||
const {
|
const {
|
||||||
appState: { defaultHdPaths },
|
appState: { defaultHdPaths },
|
||||||
|
|
|
@ -7,6 +7,7 @@ const connect = require('react-redux').connect
|
||||||
const actions = require('../../../../actions')
|
const actions = require('../../../../actions')
|
||||||
const FileInput = require('react-simple-file-input').default
|
const FileInput = require('react-simple-file-input').default
|
||||||
const { DEFAULT_ROUTE } = require('../../../../routes')
|
const { DEFAULT_ROUTE } = require('../../../../routes')
|
||||||
|
const { getMetaMaskAccounts } = require('../../../../selectors')
|
||||||
const HELP_LINK = 'https://support.metamask.io/kb/article/7-importing-accounts'
|
const HELP_LINK = 'https://support.metamask.io/kb/article/7-importing-accounts'
|
||||||
import Button from '../../../button'
|
import Button from '../../../button'
|
||||||
|
|
||||||
|
@ -136,7 +137,7 @@ JsonImportSubview.propTypes = {
|
||||||
const mapStateToProps = state => {
|
const mapStateToProps = state => {
|
||||||
return {
|
return {
|
||||||
error: state.appState.warning,
|
error: state.appState.warning,
|
||||||
firstAddress: Object.keys(state.metamask.accounts)[0],
|
firstAddress: Object.keys(getMetaMaskAccounts(state))[0],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ const PropTypes = require('prop-types')
|
||||||
const connect = require('react-redux').connect
|
const connect = require('react-redux').connect
|
||||||
const actions = require('../../../../actions')
|
const actions = require('../../../../actions')
|
||||||
const { DEFAULT_ROUTE } = require('../../../../routes')
|
const { DEFAULT_ROUTE } = require('../../../../routes')
|
||||||
|
const { getMetaMaskAccounts } = require('../../../../selectors')
|
||||||
import Button from '../../../button'
|
import Button from '../../../button'
|
||||||
|
|
||||||
PrivateKeyImportView.contextTypes = {
|
PrivateKeyImportView.contextTypes = {
|
||||||
|
@ -22,7 +23,7 @@ module.exports = compose(
|
||||||
function mapStateToProps (state) {
|
function mapStateToProps (state) {
|
||||||
return {
|
return {
|
||||||
error: state.appState.warning,
|
error: state.appState.warning,
|
||||||
firstAddress: Object.keys(state.metamask.accounts)[0],
|
firstAddress: Object.keys(getMetaMaskAccounts(state))[0],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,9 @@ const abi = require('human-standard-token-abi')
|
||||||
const {
|
const {
|
||||||
multiplyCurrencies,
|
multiplyCurrencies,
|
||||||
} = require('../../conversion-util')
|
} = require('../../conversion-util')
|
||||||
|
const {
|
||||||
|
getMetaMaskAccounts,
|
||||||
|
} = require('../../selectors')
|
||||||
const {
|
const {
|
||||||
estimateGasPriceFromRecentBlocks,
|
estimateGasPriceFromRecentBlocks,
|
||||||
} = require('./send.utils')
|
} = require('./send.utils')
|
||||||
|
@ -53,10 +56,8 @@ const selectors = {
|
||||||
module.exports = selectors
|
module.exports = selectors
|
||||||
|
|
||||||
function accountsWithSendEtherInfoSelector (state) {
|
function accountsWithSendEtherInfoSelector (state) {
|
||||||
const {
|
const accounts = getMetaMaskAccounts(state)
|
||||||
accounts,
|
const { identities } = state.metamask
|
||||||
identities,
|
|
||||||
} = state.metamask
|
|
||||||
|
|
||||||
const accountsWithSendEtherInfo = Object.entries(accounts).map(([key, account]) => {
|
const accountsWithSendEtherInfo = Object.entries(accounts).map(([key, account]) => {
|
||||||
return Object.assign({}, account, identities[key])
|
return Object.assign({}, account, identities[key])
|
||||||
|
@ -71,7 +72,7 @@ function accountsWithSendEtherInfoSelector (state) {
|
||||||
// const autoAddTokensThreshold = 1
|
// const autoAddTokensThreshold = 1
|
||||||
|
|
||||||
// const numberOfTransactions = state.metamask.selectedAddressTxList.length
|
// const numberOfTransactions = state.metamask.selectedAddressTxList.length
|
||||||
// const numberOfAccounts = Object.keys(state.metamask.accounts).length
|
// const numberOfAccounts = Object.keys(getMetaMaskAccounts(state)).length
|
||||||
// const numberOfTokensAdded = state.metamask.tokens.length
|
// const numberOfTokensAdded = state.metamask.tokens.length
|
||||||
|
|
||||||
// const userPassesThreshold = (numberOfTransactions > autoAddTransactionThreshold) &&
|
// const userPassesThreshold = (numberOfTransactions > autoAddTransactionThreshold) &&
|
||||||
|
@ -150,14 +151,14 @@ function getRecentBlocks (state) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSelectedAccount (state) {
|
function getSelectedAccount (state) {
|
||||||
const accounts = state.metamask.accounts
|
const accounts = getMetaMaskAccounts(state)
|
||||||
const selectedAddress = getSelectedAddress(state)
|
const selectedAddress = getSelectedAddress(state)
|
||||||
|
|
||||||
return accounts[selectedAddress]
|
return accounts[selectedAddress]
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSelectedAddress (state) {
|
function getSelectedAddress (state) {
|
||||||
const selectedAddress = state.metamask.selectedAddress || Object.keys(state.metamask.accounts)[0]
|
const selectedAddress = state.metamask.selectedAddress || Object.keys(getMetaMaskAccounts(state))[0]
|
||||||
|
|
||||||
return selectedAddress
|
return selectedAddress
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,12 +2,18 @@ import { connect } from 'react-redux'
|
||||||
import { withRouter } from 'react-router-dom'
|
import { withRouter } from 'react-router-dom'
|
||||||
import { compose } from 'recompose'
|
import { compose } from 'recompose'
|
||||||
import TransactionViewBalance from './transaction-view-balance.component'
|
import TransactionViewBalance from './transaction-view-balance.component'
|
||||||
import { getSelectedToken, getSelectedAddress, getSelectedTokenAssetImage } from '../../selectors'
|
import {
|
||||||
|
getSelectedToken,
|
||||||
|
getSelectedAddress,
|
||||||
|
getSelectedTokenAssetImage,
|
||||||
|
getMetaMaskAccounts,
|
||||||
|
} from '../../selectors'
|
||||||
import { showModal } from '../../actions'
|
import { showModal } from '../../actions'
|
||||||
|
|
||||||
const mapStateToProps = state => {
|
const mapStateToProps = state => {
|
||||||
const selectedAddress = getSelectedAddress(state)
|
const selectedAddress = getSelectedAddress(state)
|
||||||
const { metamask: { network, accounts } } = state
|
const { metamask: { network } } = state
|
||||||
|
const accounts = getMetaMaskAccounts(state)
|
||||||
const account = accounts[selectedAddress]
|
const account = accounts[selectedAddress]
|
||||||
const { balance } = account
|
const { balance } = account
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ function mapStateToProps (state) {
|
||||||
network: state.metamask.network,
|
network: state.metamask.network,
|
||||||
sidebarOpen: state.appState.sidebar.isOpen,
|
sidebarOpen: state.appState.sidebar.isOpen,
|
||||||
identities: state.metamask.identities,
|
identities: state.metamask.identities,
|
||||||
accounts: state.metamask.accounts,
|
accounts: selectors.getMetaMaskAccounts(state),
|
||||||
tokens: state.metamask.tokens,
|
tokens: state.metamask.tokens,
|
||||||
keyrings: state.metamask.keyrings,
|
keyrings: state.metamask.keyrings,
|
||||||
selectedAddress: selectors.getSelectedAddress(state),
|
selectedAddress: selectors.getSelectedAddress(state),
|
||||||
|
|
|
@ -12,6 +12,7 @@ const R = require('ramda')
|
||||||
const SignatureRequest = require('./components/signature-request')
|
const SignatureRequest = require('./components/signature-request')
|
||||||
const Loading = require('./components/loading-screen')
|
const Loading = require('./components/loading-screen')
|
||||||
const { DEFAULT_ROUTE } = require('./routes')
|
const { DEFAULT_ROUTE } = require('./routes')
|
||||||
|
const { getMetaMaskAccounts } = require('./selectors')
|
||||||
|
|
||||||
module.exports = compose(
|
module.exports = compose(
|
||||||
withRouter,
|
withRouter,
|
||||||
|
@ -28,7 +29,7 @@ function mapStateToProps (state) {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
identities: state.metamask.identities,
|
identities: state.metamask.identities,
|
||||||
accounts: state.metamask.accounts,
|
accounts: getMetaMaskAccounts(state),
|
||||||
selectedAddress: state.metamask.selectedAddress,
|
selectedAddress: state.metamask.selectedAddress,
|
||||||
unapprovedTxs: state.metamask.unapprovedTxs,
|
unapprovedTxs: state.metamask.unapprovedTxs,
|
||||||
unapprovedMsgs: state.metamask.unapprovedMsgs,
|
unapprovedMsgs: state.metamask.unapprovedMsgs,
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
const abi = require('human-standard-token-abi')
|
const abi = require('human-standard-token-abi')
|
||||||
|
|
||||||
import {
|
import {
|
||||||
transactionsSelector,
|
transactionsSelector,
|
||||||
} from './selectors/transactions'
|
} from './selectors/transactions'
|
||||||
|
|
||||||
const {
|
const {
|
||||||
multiplyCurrencies,
|
multiplyCurrencies,
|
||||||
} = require('./conversion-util')
|
} = require('./conversion-util')
|
||||||
|
@ -34,12 +32,13 @@ const selectors = {
|
||||||
getCurrentViewContext,
|
getCurrentViewContext,
|
||||||
getTotalUnapprovedCount,
|
getTotalUnapprovedCount,
|
||||||
preferencesSelector,
|
preferencesSelector,
|
||||||
|
getMetaMaskAccounts,
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = selectors
|
module.exports = selectors
|
||||||
|
|
||||||
function getSelectedAddress (state) {
|
function getSelectedAddress (state) {
|
||||||
const selectedAddress = state.metamask.selectedAddress || Object.keys(state.metamask.accounts)[0]
|
const selectedAddress = state.metamask.selectedAddress || Object.keys(getMetaMaskAccounts(state))[0]
|
||||||
|
|
||||||
return selectedAddress
|
return selectedAddress
|
||||||
}
|
}
|
||||||
|
@ -51,8 +50,27 @@ function getSelectedIdentity (state) {
|
||||||
return identities[selectedAddress]
|
return identities[selectedAddress]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getMetaMaskAccounts (state) {
|
||||||
|
const currentAccounts = state.metamask.accounts
|
||||||
|
const cachedBalances = state.metamask.cachedBalances
|
||||||
|
const selectedAccounts = {}
|
||||||
|
|
||||||
|
Object.keys(currentAccounts).forEach(accountID => {
|
||||||
|
const account = currentAccounts[accountID]
|
||||||
|
if (account && account.balance === null || account.balance === undefined) {
|
||||||
|
selectedAccounts[accountID] = {
|
||||||
|
...account,
|
||||||
|
balance: cachedBalances[accountID],
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
selectedAccounts[accountID] = account
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return selectedAccounts
|
||||||
|
}
|
||||||
|
|
||||||
function getSelectedAccount (state) {
|
function getSelectedAccount (state) {
|
||||||
const accounts = state.metamask.accounts
|
const accounts = getMetaMaskAccounts(state)
|
||||||
const selectedAddress = getSelectedAddress(state)
|
const selectedAddress = getSelectedAddress(state)
|
||||||
|
|
||||||
return accounts[selectedAddress]
|
return accounts[selectedAddress]
|
||||||
|
@ -100,10 +118,8 @@ function getAddressBook (state) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function accountsWithSendEtherInfoSelector (state) {
|
function accountsWithSendEtherInfoSelector (state) {
|
||||||
const {
|
const accounts = getMetaMaskAccounts(state)
|
||||||
accounts,
|
const { identities } = state.metamask
|
||||||
identities,
|
|
||||||
} = state.metamask
|
|
||||||
|
|
||||||
const accountsWithSendEtherInfo = Object.entries(accounts).map(([key, account]) => {
|
const accountsWithSendEtherInfo = Object.entries(accounts).map(([key, account]) => {
|
||||||
return Object.assign({}, account, identities[key])
|
return Object.assign({}, account, identities[key])
|
||||||
|
@ -169,7 +185,7 @@ function autoAddToBetaUI (state) {
|
||||||
const autoAddTokensThreshold = 1
|
const autoAddTokensThreshold = 1
|
||||||
|
|
||||||
const numberOfTransactions = state.metamask.selectedAddressTxList.length
|
const numberOfTransactions = state.metamask.selectedAddressTxList.length
|
||||||
const numberOfAccounts = Object.keys(state.metamask.accounts).length
|
const numberOfAccounts = Object.keys(getMetaMaskAccounts(state)).length
|
||||||
const numberOfTokensAdded = state.metamask.tokens.length
|
const numberOfTokensAdded = state.metamask.tokens.length
|
||||||
|
|
||||||
const userPassesThreshold = (numberOfTransactions > autoAddTransactionThreshold) &&
|
const userPassesThreshold = (numberOfTransactions > autoAddTransactionThreshold) &&
|
||||||
|
|
Loading…
Reference in New Issue