1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-11-22 09:57:02 +01:00

LoginPerSite: Support multiple accounts without automatic switching (#8079)

* transaction editing: use txParams 'from' account

* signature-request: use txParams 'from' account

* signature-request-original: use txParams 'from' account

* encryption/decryption: use txParams 'from' account

* update tests

* set 'send' state 'from' address in confirm containers
This commit is contained in:
Erik Marks 2020-03-06 13:34:56 -08:00 committed by GitHub
parent 0775c61f09
commit 2df8b85c5f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
58 changed files with 456 additions and 362 deletions

View File

@ -122,16 +122,16 @@ initialize().catch(log.error)
* @property {string} network - A stringified number of the current network ID.
* @property {Object} accounts - An object mapping lower-case hex addresses to objects with "balance" and "address" keys, both storing hex string values.
* @property {hex} currentBlockGasLimit - The most recently seen block gas limit, in a lower case hex prefixed string.
* @property {TransactionMeta[]} selectedAddressTxList - An array of transactions associated with the currently selected account.
* @property {Object} unapprovedMsgs - An object of messages associated with the currently selected account, mapping a unique ID to the options.
* @property {TransactionMeta[]} currentNetworkTxList - An array of transactions associated with the currently selected network.
* @property {Object} unapprovedMsgs - An object of messages pending approval, mapping a unique ID to the options.
* @property {number} unapprovedMsgCount - The number of messages in unapprovedMsgs.
* @property {Object} unapprovedPersonalMsgs - An object of messages associated with the currently selected account, mapping a unique ID to the options.
* @property {Object} unapprovedPersonalMsgs - An object of messages pending approval, mapping a unique ID to the options.
* @property {number} unapprovedPersonalMsgCount - The number of messages in unapprovedPersonalMsgs.
* @property {Object} EncryptionPublicKeyMsgs - An object of messages associated with the currently selected account, mapping a unique ID to the options.
* @property {Object} unapprovedEncryptionPublicKeyMsgs - An object of messages pending approval, mapping a unique ID to the options.
* @property {number} unapprovedEncryptionPublicKeyMsgCount - The number of messages in EncryptionPublicKeyMsgs.
* @property {Object} unapprovedDecryptMsgs - An object of messages associated with the currently selected account, mapping a unique ID to the options.
* @property {Object} unapprovedDecryptMsgs - An object of messages pending approval, mapping a unique ID to the options.
* @property {number} unapprovedDecryptMsgCount - The number of messages in unapprovedDecryptMsgs.
* @property {Object} unapprovedTypedMsgs - An object of messages associated with the currently selected account, mapping a unique ID to the options.
* @property {Object} unapprovedTypedMsgs - An object of messages pending approval, mapping a unique ID to the options.
* @property {number} unapprovedTypedMsgCount - The number of messages in unapprovedTypedMsgs.
* @property {string[]} keyringTypes - An array of unique keyring identifying strings, representing available strategies for creating accounts.
* @property {Keyring[]} keyrings - An array of keyring descriptions, summarizing the accounts that are available for use, and what keyrings they belong to.

View File

@ -119,7 +119,6 @@ class TransactionController extends EventEmitter {
this._onBootCleanUp()
this._updateMemstore()
})
this.preferencesStore.subscribe(() => this._updateMemstore())
// request state update to finalize initialization
this._updatePendingTxsAfterFirstBlock()
@ -748,11 +747,10 @@ class TransactionController extends EventEmitter {
_updateMemstore () {
this.pendingTxTracker.updatePendingTxs()
const unapprovedTxs = this.txStateManager.getUnapprovedTxList()
const selectedAddressTxList = this.txStateManager.getFilteredTxList({
from: this.getSelectedAddress(),
const currentNetworkTxList = this.txStateManager.getFilteredTxList({
metamaskNetworkId: this.getNetwork(),
})
this.memStore.updateState({ unapprovedTxs, selectedAddressTxList })
this.memStore.updateState({ unapprovedTxs, currentNetworkTxList })
}
}

View File

@ -104,7 +104,7 @@ export default class EncryptionPublicKeyManager extends EventEmitter {
* @returns {number} The id of the newly created EncryptionPublicKey.
*
*/
addUnapprovedMessage (address, _req) {
addUnapprovedMessage (address, req) {
log.debug(`EncryptionPublicKeyManager addUnapprovedMessage: address`)
// create txData obj with parameters and meta data
const time = (new Date()).getTime()
@ -117,8 +117,8 @@ export default class EncryptionPublicKeyManager extends EventEmitter {
type: 'eth_getEncryptionPublicKey',
}
if (_req) {
msgData.origin = _req.origin
if (req) {
msgData.origin = req.origin
}
this.addMsg(msgData)

View File

@ -62,7 +62,7 @@
],
"tokens": [],
"transactions": {},
"selectedAddressTxList": [],
"currentNetworkTxList": [],
"unapprovedMsgs": {},
"unapprovedMsgCount": 0,
"unapprovedPersonalMsgs": {},

View File

@ -92,7 +92,7 @@
}
},
"transactions": {},
"selectedAddressTxList": [
"currentNetworkTxList": [
{
"id": 3870222542191014,
"time": 1487271497135,

View File

@ -61,7 +61,7 @@
],
"tokens": [],
"transactions": {},
"selectedAddressTxList": [],
"currentNetworkTxList": [],
"unapprovedTxs": {
"4768706228115573": {
"id": 4768706228115573,

File diff suppressed because one or more lines are too long

View File

@ -68,7 +68,7 @@
"tokens": [],
"transactions": {},
"incomingTransactions": {},
"selectedAddressTxList": [],
"currentNetworkTxList": [],
"unapprovedMsgs": {},
"unapprovedMsgCount": 0,
"unapprovedPersonalMsgs": {},

View File

@ -20,7 +20,7 @@
"network": "3",
"accounts": {},
"transactions": {},
"selectedAddressTxList": [],
"currentNetworkTxList": [],
"unapprovedMsgs": {},
"unapprovedMsgCount": 0,
"keyringTypes": [

View File

@ -169,7 +169,7 @@
}
},
"currentBlockGasLimit": "0x731e25",
"selectedAddressTxList": [],
"currentNetworkTxList": [],
"unapprovedMsgs": {},
"unapprovedMsgCount": 0,
"unapprovedPersonalMsgs": {},

View File

@ -44,7 +44,7 @@
}
},
"transactions": {},
"selectedAddressTxList": [
"currentNetworkTxList": [
{
"id": 1188547363326880,
"time": 1487662234127,

View File

@ -31,7 +31,7 @@
}
},
"currentBlockGasLimit": "0x66df83",
"selectedAddressTxList": [
"currentNetworkTxList": [
{
"id": 3516145537630216,
"time": 1512615655535,

View File

@ -43,7 +43,7 @@
}
},
"transactions": {},
"selectedAddressTxList": [],
"currentNetworkTxList": [],
"unapprovedMsgs": {},
"unapprovedMsgCount": 0,
"unapprovedPersonalMsgs": {

View File

@ -23,7 +23,7 @@
}
},
"transactions": {},
"selectedAddressTxList": [],
"currentNetworkTxList": [],
"unapprovedMsgs": {},
"unapprovedMsgCount": 0,
"unapprovedPersonalMsgs": {},

View File

@ -23,7 +23,7 @@
}
},
"transactions": {},
"selectedAddressTxList": [],
"currentNetworkTxList": [],
"unapprovedMsgs": {},
"unapprovedMsgCount": 0,
"unapprovedPersonalMsgs": {},

View File

@ -64,7 +64,7 @@
],
"tokens": [],
"transactions": {},
"selectedAddressTxList": [{
"currentNetworkTxList": [{
"id": 4768706228115573,
"time": 1487363153561,
"status": "unapproved",

View File

@ -66,7 +66,7 @@
"assetImages": {},
"tokens": [],
"transactions": {},
"selectedAddressTxList": [],
"currentNetworkTxList": [],
"unapprovedMsgs": {},
"unapprovedMsgCount": 0,
"unapprovedPersonalMsgs": {},

View File

@ -55,7 +55,7 @@
}
},
"transactions": {},
"selectedAddressTxList": [],
"currentNetworkTxList": [],
"unapprovedMsgs": {},
"unapprovedMsgCount": 0,
"unapprovedPersonalMsgs": {},

View File

@ -3,7 +3,9 @@
"completedOnboarding": true,
"isInitialized": true,
"isUnlocked": true,
"featureFlags": {"betaUI": true},
"featureFlags": {
"betaUI": true
},
"rpcTarget": "https://rawtestrpc.metamask.io/",
"identities": {
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825": {
@ -68,7 +70,7 @@
"tokens": [],
"transactions": {},
"incomingTransactions": {},
"selectedAddressTxList": [
"currentNetworkTxList": [
{
"err": {
"message": "Error: intrinsic gas too low",
@ -226,7 +228,7 @@
"time": 1522378334455,
"txParams": {
"chainId": "0x3",
"from": "0x5b1cbd5636d484bf1cb6927a9425db9e7dc73ce4",
"from": "0xd85a4b6a394794842887b8284293d69163007bbb",
"gas": "0x0",
"gasPrice": "0x77359400",
"nonce": "0x3",
@ -241,7 +243,7 @@
"metamaskNetworkId": "1",
"loadingDefaults": false,
"txParams": {
"from": "0x5b1cbd5636d484bf1cb6927a9425db9e7dc73ce4",
"from": "0xd85a4b6a394794842887b8284293d69163007bbb",
"to": "0xf45d68f31b3c9ac84ff0d07b86c59b753a60b1e3",
"value": "0x0",
"gas": "0x0",
@ -506,7 +508,7 @@
"txParams": {
"chainId": "0x3",
"data": "0xa9059cbb000000000000000000000000e7884118ee52ec3f4eef715cb022279d7d4181a9000000000000000000000000000000000000000000000000000000000000000b",
"from": "0x5b1cbd5636d484bf1cb6927a9425db9e7dc73ce4",
"from": "0xd85a4b6a394794842887b8284293d69163007bbb",
"gas": "0xd3e1",
"gasPrice": "0x5f5e100",
"nonce": "0x6",
@ -714,7 +716,7 @@
"txParams": {
"chainId": "0x3",
"data": "0xa9059cbb000000000000000000000000e7884118ee52ec3f4eef715cb022279d7d4181a9000000000000000000000000000000000000000000000000000000000000000b",
"from": "0x5b1cbd5636d484bf1cb6927a9425db9e7dc73ce4",
"from": "0xd85a4b6a394794842887b8284293d69163007bbb",
"gas": "0xd3e1",
"gasPrice": "0x5f5e100",
"nonce": "0x2",
@ -729,7 +731,7 @@
"metamaskNetworkId": "1",
"loadingDefaults": false,
"txParams": {
"from": "0x5b1cbd5636d484bf1cb6927a9425db9e7dc73ce4",
"from": "0xd85a4b6a394794842887b8284293d69163007bbb",
"to": "0xf45d68f31b3c9ac84ff0d07b86c59b753a60b1e3",
"value": "0x0",
"gas": "0xcf08",
@ -908,7 +910,7 @@
"metamaskNetworkId": "1",
"loadingDefaults": false,
"txParams": {
"from": "0x5b1cbd5636d484bf1cb6927a9425db9e7dc73ce4",
"from": "0xd85a4b6a394794842887b8284293d69163007bbb",
"to": "0xf45d68f31b3c9ac84ff0d07b86c59b753a60b1e3",
"value": "0x0",
"gas": "0xcf08",

View File

@ -13,7 +13,7 @@
},
"currentBlockGasLimit": "",
"unapprovedTxs": {},
"selectedAddressTxList": [],
"currentNetworkTxList": [],
"unapprovedMsgs": {},
"unapprovedMsgCount": 0,
"unapprovedPersonalMsgs": {},

View File

@ -157,7 +157,7 @@
"forceGasMin": null,
"toNickname": ""
},
"selectedAddressTxList": [
"currentNetworkTxList": [
{
"id": 3387511061307736,
"time": 1528133130531,

View File

@ -56,9 +56,9 @@ describe('Transaction Controller', function () {
it('should return a state object with the right keys and datat types', function () {
const exposedState = txController.getState()
assert('unapprovedTxs' in exposedState, 'state should have the key unapprovedTxs')
assert('selectedAddressTxList' in exposedState, 'state should have the key selectedAddressTxList')
assert('currentNetworkTxList' in exposedState, 'state should have the key currentNetworkTxList')
assert(exposedState && typeof exposedState.unapprovedTxs === 'object', 'should be an object')
assert(Array.isArray(exposedState.selectedAddressTxList), 'should be an array')
assert(Array.isArray(exposedState.currentNetworkTxList), 'should be an array')
})
})

View File

@ -274,7 +274,7 @@ describe('MetaMask Reducers', function () {
it('updates value of tx by id', function () {
const oldState = {
selectedAddressTxList: [
currentNetworkTxList: [
{
id: 1,
txParams: 'foo',
@ -288,7 +288,7 @@ describe('MetaMask Reducers', function () {
value: 'bar',
})
assert.equal(state.selectedAddressTxList[0].txParams, 'bar')
assert.equal(state.currentNetworkTxList[0].txParams, 'bar')
})
it('sets blockies', function () {

View File

@ -71,11 +71,11 @@ import { getMaxModeOn } from '../../../../pages/send/send-content/send-amount-ro
import { calcMaxAmount } from '../../../../pages/send/send-content/send-amount-row/amount-max-button/amount-max-button.utils'
const mapStateToProps = (state, ownProps) => {
const { selectedAddressTxList } = state.metamask
const { currentNetworkTxList } = state.metamask
const { modalState: { props: modalProps } = {} } = state.appState.modal || {}
const { txData = {} } = modalProps || {}
const { transaction = {} } = ownProps
const selectedTransaction = selectedAddressTxList.find(({ id }) => id === (transaction.id || txData.id))
const selectedTransaction = currentNetworkTxList.find(({ id }) => id === (transaction.id || txData.id))
const buttonDataLoading = getBasicGasEstimateLoadingStatus(state)
const gasEstimatesLoading = getGasEstimatesLoadingStatus(state)

View File

@ -85,7 +85,7 @@ describe('gas-modal-page-container container', function () {
provider: {
type: 'mainnet',
},
selectedAddressTxList: [{
currentNetworkTxList: [{
id: 34,
txParams: {
gas: '0x1600000',

View File

@ -10,8 +10,8 @@ import { getHexGasTotal } from '../../../../helpers/utils/confirm-tx.util'
const mapStateToProps = (state, ownProps) => {
const { metamask } = state
const { transactionId, originalGasPrice } = ownProps
const { selectedAddressTxList } = metamask
const transaction = selectedAddressTxList.find(({ id }) => id === transactionId)
const { currentNetworkTxList } = metamask
const transaction = currentNetworkTxList.find(({ id }) => id === transactionId)
const transactionStatus = transaction ? transaction.status : ''
const defaultNewGasPrice = ethUtil.addHexPrefix(

View File

@ -19,19 +19,22 @@ export default class SignatureRequestOriginal extends Component {
}
static propTypes = {
fromAccount: PropTypes.shape({
address: PropTypes.string.isRequired,
balance: PropTypes.string,
name: PropTypes.string,
}).isRequired,
cancel: PropTypes.func.isRequired,
clearConfirmTransaction: PropTypes.func.isRequired,
conversionRate: PropTypes.number,
history: PropTypes.object.isRequired,
requesterAddress: PropTypes.string,
selectedAccount: PropTypes.string,
sign: PropTypes.func.isRequired,
txData: PropTypes.object.isRequired,
}
state = {
selectedAccount: this.props.selectedAccount,
fromAccount: this.props.fromAccount,
}
componentDidMount = () => {
@ -81,7 +84,7 @@ export default class SignatureRequestOriginal extends Component {
}
renderAccount = () => {
const { selectedAccount } = this.state
const { fromAccount } = this.state
return (
<div className="request-signature__account">
@ -91,7 +94,7 @@ export default class SignatureRequestOriginal extends Component {
<div className="request-signature__account-item">
<AccountListItem
account={selectedAccount}
account={fromAccount}
displayBalance={false}
/>
</div>
@ -100,7 +103,8 @@ export default class SignatureRequestOriginal extends Component {
}
renderBalance = () => {
const { balance, conversionRate } = this.props
const { conversionRate } = this.props
const { fromAccount: { balance } } = this.state
const balanceInEther = conversionUtil(balance, {
fromNumericBase: 'hex',

View File

@ -2,30 +2,28 @@ import { connect } from 'react-redux'
import { compose } from 'redux'
import { withRouter } from 'react-router-dom'
import * as actions from '../../../store/actions'
import { goHome } from '../../../store/actions'
import {
getSelectedAccount,
getCurrentAccountWithSendEtherInfo,
getSelectedAddress,
accountsWithSendEtherInfoSelector,
conversionRateSelector,
} from '../../../selectors/selectors.js'
import { getAccountByAddress } from '../../../helpers/utils/util'
import { clearConfirmTransaction } from '../../../ducks/confirm-transaction/confirm-transaction.duck'
import SignatureRequestOriginal from './signature-request-original.component'
function mapStateToProps (state) {
return {
balance: getSelectedAccount(state).balance,
selectedAccount: getCurrentAccountWithSendEtherInfo(state),
selectedAddress: getSelectedAddress(state),
requester: null,
requesterAddress: null,
conversionRate: conversionRateSelector(state),
// not passed to component
allAccounts: accountsWithSendEtherInfoSelector(state),
}
}
function mapDispatchToProps (dispatch) {
return {
goHome: () => dispatch(actions.goHome()),
goHome: () => dispatch(goHome()),
clearConfirmTransaction: () => dispatch(clearConfirmTransaction()),
}
}
@ -41,7 +39,12 @@ function mergeProps (stateProps, dispatchProps, ownProps) {
txData,
} = ownProps
const { type } = txData
const { allAccounts } = stateProps
delete stateProps.allAccounts
const { type, msgParams: { from } } = txData
const fromAccount = getAccountByAddress(allAccounts, from)
let cancel
let sign
@ -60,6 +63,7 @@ function mergeProps (stateProps, dispatchProps, ownProps) {
...ownProps,
...stateProps,
...dispatchProps,
fromAccount,
txData,
cancel,
sign,

View File

@ -10,11 +10,11 @@ import Identicon from '../../ui/identicon'
export default class SignatureRequest extends PureComponent {
static propTypes = {
txData: PropTypes.object.isRequired,
selectedAccount: PropTypes.shape({
address: PropTypes.string,
fromAccount: PropTypes.shape({
address: PropTypes.string.isRequired,
balance: PropTypes.string,
name: PropTypes.string,
}),
}).isRequired,
clearConfirmTransaction: PropTypes.func.isRequired,
cancel: PropTypes.func.isRequired,
@ -50,29 +50,30 @@ export default class SignatureRequest extends PureComponent {
render () {
const {
selectedAccount,
txData: { msgParams: { data, origin, from: senderWallet } },
fromAccount,
txData: { msgParams: { data, origin } },
cancel,
sign,
} = this.props
const { address: fromAddress } = fromAccount
const { message, domain = {} } = JSON.parse(data)
return (
<div className="signature-request page-container">
<Header selectedAccount={selectedAccount} />
<Header fromAccount={fromAccount} />
<div className="signature-request-content">
<div className="signature-request-content__title">{this.context.t('sigRequest')}</div>
<div className="signature-request-content__identicon-container">
<div className="signature-request-content__identicon-initial" >{ domain.name && domain.name[0] }</div>
<div className="signature-request-content__identicon-border" />
<Identicon
address={senderWallet}
address={fromAddress}
diameter={70}
/>
</div>
<div className="signature-request-content__info--bolded">{domain.name}</div>
<div className="signature-request-content__info">{origin}</div>
<div className="signature-request-content__info">{this.formatWallet(senderWallet)}</div>
<div className="signature-request-content__info">{this.formatWallet(fromAddress)}</div>
</div>
<Message data={message} />
<Footer cancelAction={cancel} signAction={sign} />

View File

@ -1,33 +1,26 @@
import { connect } from 'react-redux'
import SignatureRequest from './signature-request.component'
import { goHome } from '../../../store/actions'
import { clearConfirmTransaction } from '../../../ducks/confirm-transaction/confirm-transaction.duck'
import {
getSelectedAccount,
getCurrentAccountWithSendEtherInfo,
getSelectedAddress,
accountsWithSendEtherInfoSelector,
conversionRateSelector,
} from '../../../selectors/selectors.js'
import { getAccountByAddress } from '../../../helpers/utils/util'
function mapStateToProps (state) {
return {
balance: getSelectedAccount(state).balance,
selectedAccount: getCurrentAccountWithSendEtherInfo(state),
selectedAddress: getSelectedAddress(state),
accounts: accountsWithSendEtherInfoSelector(state),
conversionRate: conversionRateSelector(state),
// not forwarded to component
allAccounts: accountsWithSendEtherInfoSelector(state),
}
}
function mapDispatchToProps (dispatch) {
return {
goHome: () => dispatch(goHome()),
clearConfirmTransaction: () => dispatch(clearConfirmTransaction()),
}
}
function mergeProps (stateProps, dispatchProps, ownProps) {
const { allAccounts } = stateProps
const {
signPersonalMessage,
signTypedMessage,
@ -38,7 +31,9 @@ function mergeProps (stateProps, dispatchProps, ownProps) {
txData,
} = ownProps
const { type } = txData
const { type, msgParams: { from } } = txData
const fromAccount = getAccountByAddress(allAccounts, from)
let cancel
let sign
@ -55,9 +50,9 @@ function mergeProps (stateProps, dispatchProps, ownProps) {
}
return {
...stateProps,
...dispatchProps,
...ownProps,
...dispatchProps,
fromAccount,
txData,
cancel,
sign,

View File

@ -6,6 +6,7 @@ import SignatureRequest from '../signature-request.component'
describe('Signature Request Component', function () {
describe('render', function () {
const fromAddress = '0x123456789abcdef'
it('should render a div with one child', function () {
const wrapper = shallow((
<SignatureRequest
@ -15,9 +16,10 @@ describe('Signature Request Component', function () {
txData={{
msgParams: {
data: '{"message": {"from": {"name": "hello"}}}',
from: '0x123456789abcdef',
from: fromAddress,
},
}}
fromAccount={{ address: fromAddress }}
/>
))

View File

@ -19,7 +19,9 @@ describe('Signature Request', function () {
const store = configureMockStore()(mockStore)
const props = {
selectedAccount: '0xd8f6a2ffb0fc5952d16c9768b71cfd35b6399aa5',
fromAccount: {
address: '0xd8f6a2ffb0fc5952d16c9768b71cfd35b6399aa5',
},
history: {
push: sinon.spy(),
},

View File

@ -118,6 +118,7 @@ export default function reduceMetamask (state = {}, action) {
}
newSend.tokenBalance = null
newSend.balance = '0'
newSend.from = unapprovedTx.from || ''
}
newState.send = newSend
@ -279,8 +280,8 @@ export default function reduceMetamask (state = {}, action) {
case actions.UPDATE_TRANSACTION_PARAMS:
const { id: txId, value } = action
let { selectedAddressTxList } = metamaskState
selectedAddressTxList = selectedAddressTxList.map((tx) => {
let { currentNetworkTxList } = metamaskState
currentNetworkTxList = currentNetworkTxList.map((tx) => {
if (tx.id === txId) {
const newTx = Object.assign({}, tx)
newTx.txParams = value
@ -291,7 +292,7 @@ export default function reduceMetamask (state = {}, action) {
return {
...metamaskState,
selectedAddressTxList,
currentNetworkTxList,
}
case actions.SET_PARTICIPATE_IN_METAMETRICS:

View File

@ -287,3 +287,11 @@ export function getOriginFromUrl (url) {
const origin = url.hostname
return origin
}
export function getTxById (transactions = [], targetId) {
return transactions.find(({ id }) => String(id) === targetId)
}
export function getAccountByAddress (accounts = [], targetAddress) {
return accounts.find(({ address }) => address === targetAddress)
}

View File

@ -23,14 +23,23 @@ const mapStateToProps = (state, ownProps) => {
const { id: paramsTransactionId } = params
const {
confirmTransaction,
metamask: { currentCurrency, conversionRate, selectedAddressTxList, domainMetadata = {}, selectedAddress },
metamask: {
currentCurrency,
conversionRate,
currentNetworkTxList,
domainMetadata = {},
selectedAddress,
},
} = state
const {
txData: { id: transactionId, txParams: { to: tokenAddress, data } = {} } = {},
} = confirmTransaction
const transaction = selectedAddressTxList.find(({ id }) => id === (Number(paramsTransactionId) || transactionId)) || {}
const transaction = (
currentNetworkTxList.find(({ id }) => id === (Number(paramsTransactionId) ||
transactionId)) || {}
)
const {
ethTransactionTotal,

View File

@ -1,15 +1,16 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import Tooltip from '../../components/ui/tooltip-v2'
import copyToClipboard from 'copy-to-clipboard'
import classnames from 'classnames'
import AccountListItem from '../send/account-list-item/account-list-item.component'
import Button from '../../components/ui/button'
import Identicon from '../../components/ui/identicon'
import Tooltip from '../../components/ui/tooltip-v2'
import { ENVIRONMENT_TYPE_NOTIFICATION } from '../../../../app/scripts/lib/enums'
import { getEnvironmentType } from '../../../../app/scripts/lib/util'
import Identicon from '../../components/ui/identicon'
import AccountListItem from '../send/account-list-item/account-list-item.component'
import { conversionUtil } from '../../helpers/utils/conversion-util'
import Button from '../../components/ui/button'
import { DEFAULT_ROUTE } from '../../helpers/constants/routes'
export default class ConfirmDecryptMessage extends Component {
@ -19,7 +20,11 @@ export default class ConfirmDecryptMessage extends Component {
}
static propTypes = {
fromAccount: PropTypes.shape({
address: PropTypes.string.isRequired,
balance: PropTypes.string,
name: PropTypes.string,
}).isRequired,
clearConfirmTransaction: PropTypes.func.isRequired,
cancelDecryptMessage: PropTypes.func.isRequired,
decryptMessage: PropTypes.func.isRequired,
@ -27,15 +32,14 @@ export default class ConfirmDecryptMessage extends Component {
conversionRate: PropTypes.number,
history: PropTypes.object.isRequired,
requesterAddress: PropTypes.string,
selectedAccount: PropTypes.object,
txData: PropTypes.object,
domainMetadata: PropTypes.object,
}
state = {
selectedAccount: this.props.selectedAccount,
hasCopied: false,
fromAccount: this.props.fromAccount,
copyToClipboardPressed: false,
hasCopied: false,
}
componentDidMount = () => {
@ -98,7 +102,7 @@ export default class ConfirmDecryptMessage extends Component {
}
renderAccount = () => {
const { selectedAccount } = this.state
const { fromAccount } = this.state
return (
<div className="request-decrypt-message__account">
@ -108,7 +112,7 @@ export default class ConfirmDecryptMessage extends Component {
<div className="request-decrypt-message__account-item">
<AccountListItem
account={selectedAccount}
account={fromAccount}
displayBalance={false}
/>
</div>
@ -117,7 +121,8 @@ export default class ConfirmDecryptMessage extends Component {
}
renderBalance = () => {
const { balance, conversionRate } = this.props
const { conversionRate } = this.props
const { fromAccount: { balance } } = this.state
const balanceInEther = conversionUtil(balance, {
fromNumericBase: 'hex',

View File

@ -1,14 +1,17 @@
import { connect } from 'react-redux'
import { compose } from 'redux'
import { withRouter } from 'react-router-dom'
import { goHome, decryptMsg, cancelDecryptMsg, decryptMsgInline } from '../../store/actions'
import {
getSelectedAccount,
getCurrentAccountWithSendEtherInfo,
getSelectedAddress,
goHome,
decryptMsg,
cancelDecryptMsg,
decryptMsgInline,
} from '../../store/actions'
import {
getTargetAccountWithSendEtherInfo,
conversionRateSelector,
} from '../../selectors/selectors.js'
} from '../../selectors/selectors'
import { clearConfirmTransaction } from '../../ducks/confirm-transaction/confirm-transaction.duck'
import ConfirmDecryptMessage from './confirm-decrypt-message.component'
@ -21,12 +24,14 @@ function mapStateToProps (state) {
txData = {},
} = confirmTransaction
const { msgParams: { from } } = txData
const fromAccount = getTargetAccountWithSendEtherInfo(state, from)
return {
txData: txData,
domainMetadata: domainMetadata,
balance: getSelectedAccount(state).balance,
selectedAccount: getCurrentAccountWithSendEtherInfo(state),
selectedAddress: getSelectedAddress(state),
domainMetadata,
fromAccount,
requester: null,
requesterAddress: null,
conversionRate: conversionRateSelector(state),

View File

@ -1,12 +1,13 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import AccountListItem from '../send/account-list-item/account-list-item.component'
import Button from '../../components/ui/button'
import Identicon from '../../components/ui/identicon'
import { ENVIRONMENT_TYPE_NOTIFICATION } from '../../../../app/scripts/lib/enums'
import { getEnvironmentType } from '../../../../app/scripts/lib/util'
import Identicon from '../../components/ui/identicon'
import AccountListItem from '../send/account-list-item/account-list-item.component'
import { conversionUtil } from '../../helpers/utils/conversion-util'
import Button from '../../components/ui/button'
import { DEFAULT_ROUTE } from '../../helpers/constants/routes'
export default class ConfirmEncryptionPublicKey extends Component {
@ -16,20 +17,23 @@ export default class ConfirmEncryptionPublicKey extends Component {
}
static propTypes = {
fromAccount: PropTypes.shape({
address: PropTypes.string.isRequired,
balance: PropTypes.string,
name: PropTypes.string,
}).isRequired,
clearConfirmTransaction: PropTypes.func.isRequired,
cancelEncryptionPublicKey: PropTypes.func.isRequired,
encryptionPublicKey: PropTypes.func.isRequired,
conversionRate: PropTypes.number,
history: PropTypes.object.isRequired,
requesterAddress: PropTypes.string,
selectedAccount: PropTypes.object,
txData: PropTypes.object,
domainMetadata: PropTypes.object,
}
state = {
selectedAccount: this.props.selectedAccount,
fromAccount: this.props.fromAccount,
}
componentDidMount = () => {
@ -79,7 +83,7 @@ export default class ConfirmEncryptionPublicKey extends Component {
}
renderAccount = () => {
const { selectedAccount } = this.state
const { fromAccount } = this.state
return (
<div className="request-encryption-public-key__account">
@ -89,7 +93,7 @@ export default class ConfirmEncryptionPublicKey extends Component {
<div className="request-encryption-public-key__account-item">
<AccountListItem
account={selectedAccount}
account={fromAccount}
displayBalance={false}
/>
</div>
@ -98,7 +102,8 @@ export default class ConfirmEncryptionPublicKey extends Component {
}
renderBalance = () => {
const { balance, conversionRate } = this.props
const { conversionRate } = this.props
const { fromAccount: { balance } } = this.state
const balanceInEther = conversionUtil(balance, {
fromNumericBase: 'hex',

View File

@ -1,14 +1,18 @@
import { connect } from 'react-redux'
import { compose } from 'redux'
import { withRouter } from 'react-router-dom'
import { goHome, encryptionPublicKeyMsg, cancelEncryptionPublicKeyMsg } from '../../store/actions'
import {
getSelectedAccount,
getCurrentAccountWithSendEtherInfo,
getSelectedAddress,
goHome,
encryptionPublicKeyMsg,
cancelEncryptionPublicKeyMsg,
} from '../../store/actions'
import {
conversionRateSelector,
} from '../../selectors/selectors.js'
getTargetAccountWithSendEtherInfo,
} from '../../selectors/selectors'
import { clearConfirmTransaction } from '../../ducks/confirm-transaction/confirm-transaction.duck'
import ConfirmEncryptionPublicKey from './confirm-encryption-public-key.component'
@ -21,12 +25,14 @@ function mapStateToProps (state) {
txData = {},
} = confirmTransaction
const { msgParams: from } = txData
const fromAccount = getTargetAccountWithSendEtherInfo(state, from)
return {
txData: txData,
domainMetadata: domainMetadata,
balance: getSelectedAccount(state).balance,
selectedAccount: getCurrentAccountWithSendEtherInfo(state),
selectedAddress: getSelectedAddress(state),
fromAccount,
requester: null,
requesterAddress: null,
conversionRate: conversionRateSelector(state),

View File

@ -18,6 +18,7 @@ const mapDispatchToProps = (dispatch) => {
editTransaction: (txData) => {
const { id, txParams } = txData
const {
from,
gas: gasLimit,
gasPrice,
to,
@ -25,6 +26,7 @@ const mapDispatchToProps = (dispatch) => {
} = txParams
dispatch(updateSend({
from,
gasLimit,
gasPrice,
gasTotal: null,

View File

@ -18,16 +18,29 @@ const mapStateToProps = (state) => {
const mapDispatchToProps = (dispatch) => {
return {
editTransaction: ({ txData, tokenData, tokenProps }) => {
const { txParams: { to: tokenAddress, gas: gasLimit, gasPrice } = {}, id } = txData
const {
id,
txParams: {
from,
to: tokenAddress,
gas: gasLimit,
gasPrice,
} = {},
} = txData
const { params = [] } = tokenData
const { value: to } = params[0] || {}
const { value: tokenAmountInDec } = params[1] || {}
const tokenAmountInHex = conversionUtil(tokenAmountInDec, {
fromNumericBase: 'dec',
toNumericBase: 'hex',
})
dispatch(setSelectedToken(tokenAddress))
dispatch(updateSend({
from,
gasLimit,
gasPrice,
gasTotal: null,

View File

@ -20,13 +20,19 @@ import {
const mapStateToProps = (state, ownProps) => {
const { match: { params = {} } } = ownProps
const { id: paramsTransactionId } = params
const { confirmTransaction, metamask: { currentCurrency, conversionRate, selectedAddressTxList } } = state
const {
confirmTransaction,
metamask: { currentCurrency, conversionRate, currentNetworkTxList },
} = state
const {
txData: { id: transactionId, txParams: { to: tokenAddress, data } = {} } = {},
} = confirmTransaction
const transaction = selectedAddressTxList.find(({ id }) => id === (Number(paramsTransactionId) || transactionId)) || {}
const transaction = (
currentNetworkTxList.find(({ id }) => id === (Number(paramsTransactionId) ||
transactionId)) || {}
)
const {
ethTransactionTotal,

View File

@ -39,7 +39,7 @@ function mapStateToProps (state) {
unapprovedPersonalMsgCount,
unapprovedTypedMessagesCount,
send: state.metamask.send,
selectedAddressTxList: state.metamask.selectedAddressTxList,
currentNetworkTxList: state.metamask.currentNetworkTxList,
}
}
@ -56,11 +56,11 @@ class ConfirmTxScreen extends Component {
unapprovedTypedMessages: PropTypes.object,
match: PropTypes.shape({
params: PropTypes.shape({
id: PropTypes.number,
id: PropTypes.string,
}),
}),
selectedAddressTxList: PropTypes.array,
currentNetworkTxList: PropTypes.array,
currentCurrency: PropTypes.string,
blockGasLimit: PropTypes.string,
history: PropTypes.object,
@ -181,7 +181,7 @@ class ConfirmTxScreen extends Component {
const {
unapprovedTxs = {},
network,
selectedAddressTxList,
currentNetworkTxList,
send,
history,
match: { params: { id: transactionId } = {} },
@ -190,12 +190,12 @@ class ConfirmTxScreen extends Component {
let prevTx
if (transactionId) {
prevTx = R.find(({ id }) => id + '' === transactionId)(selectedAddressTxList)
prevTx = R.find(({ id }) => id + '' === transactionId)(currentNetworkTxList)
} else {
const { index: prevIndex, unapprovedTxs: prevUnapprovedTxs } = prevProps
const prevUnconfTxList = txHelper(prevUnapprovedTxs, {}, {}, {}, network)
const prevTxData = prevUnconfTxList[prevIndex] || {}
prevTx = selectedAddressTxList.find(({ id }) => id === prevTxData.id) || {}
prevTx = currentNetworkTxList.find(({ id }) => id === prevTxData.id) || {}
}
const unconfTxList = txHelper(unapprovedTxs, {}, {}, {}, network)

View File

@ -389,7 +389,6 @@ function mapStateToProps (state) {
submittedPendingTransactions: submittedPendingTransactionsSelector(state),
network: state.metamask.network,
provider: state.metamask.provider,
selectedAddress: state.metamask.selectedAddress,
frequentRpcListDetail: state.metamask.frequentRpcListDetail || [],
currentCurrency: state.metamask.currentCurrency,
isMouseUser: state.appState.isMouseUser,
@ -406,7 +405,6 @@ function mapDispatchToProps (dispatch) {
setCurrentCurrencyToUSD: () => dispatch(actions.setCurrentCurrency('usd')),
setMouseUserState: (isMouseUser) => dispatch(actions.setMouseUserState(isMouseUser)),
setLastActiveTime: () => dispatch(actions.setLastActiveTime()),
showAccountDetail: (address) => dispatch(actions.showAccountDetail(address)),
}
}

View File

@ -1,10 +1,10 @@
import { connect } from 'react-redux'
import {
accountsWithSendEtherInfoSelector,
getSendEnsResolution,
getSendEnsResolutionError,
} from '../../send.selectors.js'
import {
accountsWithSendEtherInfoSelector,
getAddressBook,
getAddressBookEntry,
} from '../../../../selectors/selectors'

View File

@ -20,11 +20,11 @@ proxyquire('../add-recipient.container.js', {
'../../send.selectors.js': {
getSendEnsResolution: (s) => `mockSendEnsResolution:${s}`,
getSendEnsResolutionError: (s) => `mockSendEnsResolutionError:${s}`,
accountsWithSendEtherInfoSelector: (s) => `mockAccountsWithSendEtherInfoSelector:${s}`,
},
'../../../../selectors/selectors': {
getAddressBook: (s) => [{ name: `mockAddressBook:${s}` }],
getAddressBookEntry: (s) => `mockAddressBookEntry:${s}`,
accountsWithSendEtherInfoSelector: (s) => `mockAccountsWithSendEtherInfoSelector:${s}`,
},
'../../../../store/actions': actionSpies,
})

View File

@ -1,10 +1,10 @@
import { connect } from 'react-redux'
import SendContent from './send-content.component'
import {
accountsWithSendEtherInfoSelector,
getSendTo,
} from '../send.selectors'
import {
accountsWithSendEtherInfoSelector,
getAddressBookEntry,
} from '../../../selectors/selectors'
import * as actions from '../../../store/actions'

View File

@ -24,6 +24,10 @@ import {
getSendErrors,
} from '../send.selectors'
import { getGasIsLoading } from '../../../selectors/selectors'
import { networkTransactionsSelector } from '../../../selectors/transactions'
import {
getTxById,
} from '../../../helpers/utils/util'
import {
isSendFormInError,
} from './send-footer.selectors'
@ -40,6 +44,7 @@ import {
export default connect(mapStateToProps, mapDispatchToProps)(SendFooter)
function mapStateToProps (state) {
const gasButtonInfo = getRenderableEstimateDataForSmallButtonsFromGWEI(state)
const gasPrice = getGasPrice(state)
const activeButtonIndex = getDefaultActiveButtonIndex(gasButtonInfo, gasPrice)
@ -47,11 +52,19 @@ function mapStateToProps (state) {
? gasButtonInfo[activeButtonIndex].gasEstimateType
: 'custom'
let fromAddress
const editingTransactionId = getSendEditingTransactionId(state)
if (editingTransactionId) {
const transactions = networkTransactionsSelector(state)
const tx = getTxById(transactions, editingTransactionId)
fromAddress = tx && tx.txParams && tx.txParams.from
}
return {
amount: getSendAmount(state),
data: getSendHexData(state),
editingTransactionId: getSendEditingTransactionId(state),
from: getSendFromObject(state),
editingTransactionId,
from: getSendFromObject(state, fromAddress),
gasLimit: getGasLimit(state),
gasPrice: getGasPrice(state),
gasTotal: getGasTotal(state),

View File

@ -28,6 +28,9 @@ import {
getSelectedAddress,
getAddressBook,
} from '../../selectors/selectors'
import {
networkTransactionsSelector,
} from '../../selectors/transactions'
import { getTokens } from './send-content/add-recipient/add-recipient.selectors'
import {
updateSendTo,
@ -51,17 +54,27 @@ import {
} from './send.utils.js'
import {
isValidDomainName,
getTxById,
} from '../../helpers/utils/util'
function mapStateToProps (state) {
let fromAddress
const editingTransactionId = getSendEditingTransactionId(state)
if (editingTransactionId) {
const transactions = networkTransactionsSelector(state)
const tx = getTxById(transactions, editingTransactionId)
fromAddress = tx && tx.txParams && tx.txParams.from
}
return {
addressBook: getAddressBook(state),
amount: getSendAmount(state),
amountConversionRate: getAmountConversionRate(state),
blockGasLimit: getBlockGasLimit(state),
conversionRate: getConversionRate(state),
editingTransactionId: getSendEditingTransactionId(state),
from: getSendFromObject(state),
editingTransactionId,
from: getSendFromObject(state, fromAddress),
gasLimit: getGasLimit(state),
gasPrice: getGasPrice(state),
gasTotal: getGasTotal(state),

View File

@ -1,22 +1,18 @@
import { valuesFor } from '../../helpers/utils/util'
import abi from 'human-standard-token-abi'
import { multiplyCurrencies } from '../../helpers/utils/conversion-util'
import { getMetaMaskAccounts, getSelectedAddress, getAddressBook } from '../../selectors/selectors'
import {
accountsWithSendEtherInfoSelector,
getAddressBook,
getCurrentAccountWithSendEtherInfo,
getTargetAccountWithSendEtherInfo,
getMetaMaskAccounts,
getSelectedAddress,
} from '../../selectors/selectors'
import { estimateGasPriceFromRecentBlocks, calcGasTotal } from './send.utils'
import {
getAveragePriceEstimateInHexWEI,
} from '../../selectors/custom-gas'
export function accountsWithSendEtherInfoSelector (state) {
const accounts = getMetaMaskAccounts(state)
const { identities } = state.metamask
const accountsWithSendEtherInfo = Object.entries(accounts).map(([key, account]) => {
return Object.assign({}, account, identities[key])
})
return accountsWithSendEtherInfo
}
export function getAmountConversionRate (state) {
return getSelectedToken(state)
? getSelectedTokenToFiatRate(state)
@ -31,13 +27,6 @@ export function getConversionRate (state) {
return state.metamask.conversionRate
}
export function getCurrentAccountWithSendEtherInfo (state) {
const currentAddress = getSelectedAddress(state)
const accounts = accountsWithSendEtherInfoSelector(state)
return accounts.find(({ address }) => address === currentAddress)
}
export function getCurrentCurrency (state) {
return state.metamask.currentCurrency
}
@ -162,7 +151,10 @@ export function getSendFromBalance (state) {
return from.balance
}
export function getSendFromObject (state) {
export function getSendFromObject (state, address) {
if (address) {
return getTargetAccountWithSendEtherInfo(state, address)
}
return getSendFrom(state) || getCurrentAccountWithSendEtherInfo(state)
}
@ -207,21 +199,6 @@ export function getUnapprovedTxs (state) {
return state.metamask.unapprovedTxs
}
export function transactionsSelector (state) {
const { network, selectedTokenAddress } = state.metamask
const unapprovedMsgs = valuesFor(state.metamask.unapprovedMsgs)
const shapeShiftTxList = (network === '1') ? state.metamask.shapeShiftTxList : undefined
const transactions = state.metamask.selectedAddressTxList || []
const txsToRender = !shapeShiftTxList ? transactions.concat(unapprovedMsgs) : transactions.concat(unapprovedMsgs, shapeShiftTxList)
return selectedTokenAddress
? txsToRender
.filter(({ txParams }) => txParams && txParams.to === selectedTokenAddress)
.sort((a, b) => b.time - a.time)
: txsToRender
.sort((a, b) => b.time - a.time)
}
export function getQrCodeData (state) {
return state.appState.qrCodeData
}

View File

@ -91,11 +91,12 @@ export default {
},
},
'transactions': {},
'selectedAddressTxList': [
'currentNetworkTxList': [
{
'id': 'mockTokenTx1',
'txParams': {
'to': '0x8d6b81208414189a58339873ab429b6c47ab92d3',
'from': '0xd85a4b6a394794842887b8284293d69163007bbb',
},
'time': 1700000000000,
},
@ -103,6 +104,7 @@ export default {
'id': 'mockTokenTx2',
'txParams': {
'to': '0xafaketokenaddress',
'from': '0xd85a4b6a394794842887b8284293d69163007bbb',
},
'time': 1600000000000,
},
@ -110,6 +112,7 @@ export default {
'id': 'mockTokenTx3',
'txParams': {
'to': '0x8d6b81208414189a58339873ab429b6c47ab92d3',
'from': '0xd85a4b6a394794842887b8284293d69163007bbb',
},
'time': 1500000000000,
},
@ -117,6 +120,7 @@ export default {
'id': 'mockEthTx1',
'txParams': {
'to': '0xd85a4b6a394794842887b8284293d69163007bbb',
'from': '0xd85a4b6a394794842887b8284293d69163007bbb',
},
'time': 1400000000000,
},

View File

@ -2,11 +2,12 @@ import assert from 'assert'
import sinon from 'sinon'
import {
accountsWithSendEtherInfoSelector,
// autoAddToBetaUI,
getCurrentAccountWithSendEtherInfo,
} from '../../../selectors/selectors'
import {
getBlockGasLimit,
getAmountConversionRate,
getConversionRate,
getCurrentAccountWithSendEtherInfo,
getCurrentCurrency,
getCurrentNetwork,
getNativeCurrency,
@ -35,7 +36,6 @@ import {
getTokenBalance,
getTokenExchangeRate,
getUnapprovedTxs,
transactionsSelector,
} from '../send.selectors.js'
import mockState from './send-selectors-test-data'
@ -540,132 +540,4 @@ describe('send selectors', function () {
)
})
})
describe('transactionsSelector()', function () {
it('should return the selected addresses selected token transactions', function () {
assert.deepEqual(
transactionsSelector(mockState),
[
{
id: 'mockTokenTx1',
txParams: {
to: '0x8d6b81208414189a58339873ab429b6c47ab92d3',
},
time: 1700000000000,
},
{
id: 'mockTokenTx3',
txParams: {
to: '0x8d6b81208414189a58339873ab429b6c47ab92d3',
},
time: 1500000000000,
},
]
)
})
it('should return all transactions if no token is selected', function () {
const modifiedMetamaskState = Object.assign({}, mockState.metamask, { selectedTokenAddress: false })
const modifiedState = Object.assign({}, mockState, { metamask: modifiedMetamaskState })
assert.deepEqual(
transactionsSelector(modifiedState),
[
{
id: 'mockTokenTx1',
time: 1700000000000,
txParams: {
to: '0x8d6b81208414189a58339873ab429b6c47ab92d3',
},
},
{
id: 'unapprovedMessage1',
time: 1650000000000,
},
{
id: 'mockTokenTx2',
time: 1600000000000,
txParams: {
to: '0xafaketokenaddress',
},
},
{
id: 'unapprovedMessage2',
time: 1550000000000,
},
{
id: 'mockTokenTx3',
time: 1500000000000,
txParams: {
to: '0x8d6b81208414189a58339873ab429b6c47ab92d3',
},
},
{
id: 'unapprovedMessage3',
time: 1450000000000,
},
{
id: 'mockEthTx1',
time: 1400000000000,
txParams: {
to: '0xd85a4b6a394794842887b8284293d69163007bbb',
},
},
]
)
})
it('should return shapeshift transactions if current network is 1', function () {
const modifiedMetamaskState = Object.assign({}, mockState.metamask, { selectedTokenAddress: false, network: '1' })
const modifiedState = Object.assign({}, mockState, { metamask: modifiedMetamaskState })
assert.deepEqual(
transactionsSelector(modifiedState),
[
{
id: 'mockTokenTx1',
time: 1700000000000,
txParams: {
to: '0x8d6b81208414189a58339873ab429b6c47ab92d3',
},
},
{ id: 'shapeShiftTx1', 'time': 1675000000000 },
{
id: 'unapprovedMessage1',
time: 1650000000000,
},
{
id: 'mockTokenTx2',
time: 1600000000000,
txParams: {
to: '0xafaketokenaddress',
},
},
{ id: 'shapeShiftTx2', 'time': 1575000000000 },
{
id: 'unapprovedMessage2',
time: 1550000000000,
},
{
id: 'mockTokenTx3',
time: 1500000000000,
txParams: {
to: '0x8d6b81208414189a58339873ab429b6c47ab92d3',
},
},
{ id: 'shapeShiftTx3', 'time': 1475000000000 },
{
id: 'unapprovedMessage3',
time: 1450000000000,
},
{
id: 'mockEthTx1',
time: 1400000000000,
txParams: {
to: '0xd85a4b6a394794842887b8284293d69163007bbb',
},
},
]
)
})
})
})

View File

@ -9,6 +9,7 @@ import {
checksumAddress,
formatDate,
getOriginFromUrl,
getAccountByAddress,
} from '../helpers/utils/util'
import { getPermittedAccountsMap } from './permissions'
@ -250,7 +251,12 @@ export function getCurrentAccountWithSendEtherInfo (state) {
const currentAddress = getSelectedAddress(state)
const accounts = accountsWithSendEtherInfoSelector(state)
return accounts.find(({ address }) => address === currentAddress)
return getAccountByAddress(accounts, currentAddress)
}
export function getTargetAccountWithSendEtherInfo (state, targetAddress) {
const accounts = accountsWithSendEtherInfoSelector(state)
return getAccountByAddress(accounts, targetAddress)
}
export function getCurrentEthBalance (state) {

View File

@ -93,7 +93,7 @@ export default {
},
},
'transactions': {},
'selectedAddressTxList': [
'currentNetworkTxList': [
{
'id': 'mockTokenTx1',
'txParams': {

View File

@ -1,4 +1,4 @@
import assert from 'assert'
import { strict as assert } from 'assert'
import {
unapprovedMessagesSelector,
transactionsSelector,
@ -11,6 +11,7 @@ import {
describe('Transaction Selectors', function () {
describe('unapprovedMessagesSelector', function () {
it('returns eth sign msg from unapprovedMsgs', function () {
const msg = {
@ -94,13 +95,12 @@ describe('Transaction Selectors', function () {
assert(Array.isArray(msgSelector))
assert.deepEqual(msgSelector, [msg])
})
})
describe('transactionsSelector', function () {
it('selectedAddressTxList', function () {
it('selects the currentNetworkTxList', function () {
const state = {
metamask: {
@ -110,7 +110,8 @@ describe('Transaction Selectors', function () {
featureFlags: {
showIncomingTransactions: false,
},
selectedAddressTxList: [
selectedAddress: '0xAddress',
currentNetworkTxList: [
{
id: 0,
time: 0,
@ -131,15 +132,15 @@ describe('Transaction Selectors', function () {
},
}
const orderedTxlist = state.metamask.selectedAddressTxList.sort((a, b) => b.time - a.time)
const orderedTxList = state.metamask.currentNetworkTxList.sort((a, b) => b.time - a.time)
const txSelector = transactionsSelector(state)
const selectedTx = transactionsSelector(state)
assert(Array.isArray(txSelector))
assert.deepEqual(txSelector, orderedTxlist)
assert(Array.isArray(selectedTx))
assert.deepEqual(selectedTx, orderedTxList)
})
it('returns token tx from selectedAddressTxList when selectedTokenAddress is valid', function () {
it('returns token transactions from currentNetworkTxList when selectedTokenAddress is valid', function () {
const state = {
metamask: {
@ -149,8 +150,9 @@ describe('Transaction Selectors', function () {
featureFlags: {
showIncomingTransactions: false,
},
selectedAddress: '0xAddress',
selectedTokenAddress: '0xToken',
selectedAddressTxList: [
currentNetworkTxList: [
{
id: 0,
time: 0,
@ -162,6 +164,14 @@ describe('Transaction Selectors', function () {
{
id: 1,
time: 1,
txParams: {
from: '0xAddress',
to: '0xRecipient',
},
},
{
id: 2,
time: 2,
txParams: {
from: '0xAddress',
to: '0xToken',
@ -169,18 +179,110 @@ describe('Transaction Selectors', function () {
},
],
},
}
const orderedTxlist = state.metamask.selectedAddressTxList.sort((a, b) => b.time - a.time)
const orderedTxList = state.metamask.currentNetworkTxList
.filter((tx) => tx.txParams.to === '0xToken')
.sort((a, b) => b.time - a.time)
const txSelector = transactionsSelector(state)
assert(Array.isArray(txSelector))
assert.deepEqual(txSelector, orderedTxlist)
const selectedTx = transactionsSelector(state)
assert(Array.isArray(selectedTx))
assert.deepEqual(selectedTx, orderedTxList)
})
it('should return shapeshift transactions if current network is mainnet', function () {
const state = {
metamask: {
provider: {
nickname: 'mainnet',
},
featureFlags: {
showIncomingTransactions: false,
},
selectedAddress: '0xAddress',
currentNetworkTxList: [
{
id: 0,
time: 0,
txParams: {
from: '0xAddress',
to: '0xRecipient',
},
},
{
id: 1,
time: 2,
txParams: {
from: '0xAddress',
to: '0xRecipient',
},
},
],
shapeShiftTxList: [
{ id: 'shapeShiftTx1', 'time': 1 },
{ id: 'shapeShiftTx2', 'time': 3 },
{ id: 'shapeShiftTx3', 'time': 4 },
],
},
}
const orderedTxList = state.metamask.currentNetworkTxList
.concat(state.metamask.shapeShiftTxList)
.sort((a, b) => b.time - a.time)
const selectedTx = transactionsSelector(state)
assert(Array.isArray(selectedTx))
assert.deepEqual(selectedTx, orderedTxList)
})
it('should not return shapeshift transactions if current network is not mainnet', function () {
const state = {
metamask: {
provider: {
nickname: 'rinkeby',
},
featureFlags: {
showIncomingTransactions: false,
},
selectedAddress: '0xAddress',
currentNetworkTxList: [
{
id: 0,
time: 0,
txParams: {
from: '0xAddress',
to: '0xRecipient',
},
},
{
id: 1,
time: 2,
txParams: {
from: '0xAddress',
to: '0xRecipient',
},
},
],
shapeShiftTxList: [
{ id: 'shapeShiftTx1', 'time': 1 },
{ id: 'shapeShiftTx2', 'time': 3 },
{ id: 'shapeShiftTx3', 'time': 4 },
],
},
}
const orderedTxList = state.metamask.currentNetworkTxList
.sort((a, b) => b.time - a.time)
const selectedTx = transactionsSelector(state)
assert(Array.isArray(selectedTx))
assert.deepEqual(selectedTx, orderedTxList)
})
})
describe('nonceSortedTransactionsSelector', function () {
@ -212,10 +314,11 @@ describe('Transaction Selectors', function () {
provider: {
nickname: 'mainnet',
},
selectedAddress: '0xAddress',
featureFlags: {
showIncomingTransactions: false,
},
selectedAddressTxList: [
currentNetworkTxList: [
tx1,
tx2,
],
@ -296,10 +399,11 @@ describe('Transaction Selectors', function () {
provider: {
nickname: 'mainnet',
},
selectedAddress: '0xAddress',
featureFlags: {
showIncomingTransactions: false,
},
selectedAddressTxList: [
currentNetworkTxList: [
submittedTx,
unapprovedTx,
approvedTx,
@ -360,9 +464,6 @@ describe('Transaction Selectors', function () {
const expectedResult = [ submittedTx ]
assert.deepEqual(submittedPendingTransactionsSelector(state), expectedResult)
})
})
})

View File

@ -12,7 +12,11 @@ import {
import { hexToDecimal } from '../helpers/utils/conversions.util'
import { selectedTokenAddressSelector } from './tokens'
import { getFastPriceEstimateInHexWEI } from './custom-gas'
import { getSelectedToken, getIsMainnet } from './selectors'
import {
getIsMainnet,
getSelectedToken,
getSelectedAddress,
} from './selectors'
import txHelper from '../../lib/tx-helper'
export const shapeShiftTxListSelector = (state) => {
@ -29,20 +33,28 @@ export const incomingTxListSelector = (state) => {
}
const network = state.metamask.network
const selectedAddress = state.metamask.selectedAddress
const selectedAddress = getSelectedAddress(state)
return Object.values(state.metamask.incomingTransactions)
.filter(({ metamaskNetworkId, txParams }) => (
txParams.to === selectedAddress && metamaskNetworkId === network
))
}
export const unapprovedMsgsSelector = (state) => state.metamask.unapprovedMsgs
export const selectedAddressTxListSelector = (state) => state.metamask.selectedAddressTxList
export const currentNetworkTxListSelector = (state) => state.metamask.currentNetworkTxList
export const unapprovedPersonalMsgsSelector = (state) => state.metamask.unapprovedPersonalMsgs
export const unapprovedDecryptMsgsSelector = (state) => state.metamask.unapprovedDecryptMsgs
export const unapprovedEncryptionPublicKeyMsgsSelector = (state) => state.metamask.unapprovedEncryptionPublicKeyMsgs
export const unapprovedTypedMessagesSelector = (state) => state.metamask.unapprovedTypedMessages
export const networkSelector = (state) => state.metamask.network
export const selectedAddressTxListSelector = createSelector(
getSelectedAddress,
currentNetworkTxListSelector,
(selectedAddress, transactions = []) => {
return transactions.filter(({ txParams }) => txParams.from === selectedAddress)
}
)
export const unapprovedMessagesSelector = createSelector(
unapprovedMsgsSelector,
unapprovedPersonalMsgsSelector,
@ -79,21 +91,43 @@ const priorityStatusHash = {
[CONFIRMED_STATUS]: true,
}
export const transactionsSelector = createSelector(
selectedTokenAddressSelector,
export const transactionSubSelector = createSelector(
unapprovedMessagesSelector,
shapeShiftTxListSelector,
incomingTxListSelector,
selectedAddressTxListSelector,
(selectedTokenAddress, unapprovedMessages = [], shapeShiftTxList = [], incomingTxList = [], transactions = []) => {
const txsToRender = transactions.concat(unapprovedMessages, shapeShiftTxList, incomingTxList)
(unapprovedMessages = [], shapeShiftTxList = [], incomingTxList = []) => {
return unapprovedMessages.concat(shapeShiftTxList, incomingTxList)
}
)
const transactionSelectorReturnHelper = (selectedTokenAddress, transactions) => {
return selectedTokenAddress
? txsToRender
? transactions
.filter(({ txParams }) => txParams && txParams.to === selectedTokenAddress)
.sort((a, b) => b.time - a.time)
: txsToRender
: transactions
.sort((a, b) => b.time - a.time)
}
export const networkTransactionsSelector = createSelector(
selectedTokenAddressSelector,
transactionSubSelector,
currentNetworkTxListSelector,
(selectedTokenAddress, subSelectorTxList = [], networkTxList = []) => {
const txsToRender = networkTxList.concat(subSelectorTxList)
return transactionSelectorReturnHelper(selectedTokenAddress, txsToRender)
}
)
export const transactionsSelector = createSelector(
selectedTokenAddressSelector,
transactionSubSelector,
selectedAddressTxListSelector,
(selectedTokenAddress, subSelectorTxList = [], selectedAddressTxList = []) => {
const txsToRender = selectedAddressTxList.concat(subSelectorTxList)
return transactionSelectorReturnHelper(selectedTokenAddress, txsToRender)
}
)

View File

@ -1473,8 +1473,8 @@ export function retryTransaction (txId, gasPrice) {
return reject(err)
}
const { selectedAddressTxList } = newState
const { id } = selectedAddressTxList[selectedAddressTxList.length - 1]
const { currentNetworkTxList } = newState
const { id } = currentNetworkTxList[currentNetworkTxList.length - 1]
newTxId = id
resolve(newState)
})
@ -1496,8 +1496,8 @@ export function createCancelTransaction (txId, customGasPrice) {
return reject(err)
}
const { selectedAddressTxList } = newState
const { id } = selectedAddressTxList[selectedAddressTxList.length - 1]
const { currentNetworkTxList } = newState
const { id } = currentNetworkTxList[currentNetworkTxList.length - 1]
newTxId = id
resolve(newState)
})
@ -1519,8 +1519,8 @@ export function createSpeedUpTransaction (txId, customGasPrice) {
return reject(err)
}
const { selectedAddressTxList } = newState
newTx = selectedAddressTxList[selectedAddressTxList.length - 1]
const { currentNetworkTxList } = newState
newTx = currentNetworkTxList[currentNetworkTxList.length - 1]
resolve(newState)
})
})
@ -1541,8 +1541,8 @@ export function createRetryTransaction (txId, customGasPrice) {
return reject(err)
}
const { selectedAddressTxList } = newState
newTx = selectedAddressTxList[selectedAddressTxList.length - 1]
const { currentNetworkTxList } = newState
newTx = currentNetworkTxList[currentNetworkTxList.length - 1]
resolve(newState)
})
})

View File

@ -59,7 +59,15 @@ async function startApp (metamaskState, backgroundConnection, opts) {
})
// if unconfirmed txs, start on txConf page
const unapprovedTxsAll = txHelper(metamaskState.unapprovedTxs, metamaskState.unapprovedMsgs, metamaskState.unapprovedPersonalMsgs, metamaskState.unapprovedDecryptMsgs, metamaskState.unapprovedEncryptionPublicKeyMsgs, metamaskState.unapprovedTypedMessages, metamaskState.network)
const unapprovedTxsAll = txHelper(
metamaskState.unapprovedTxs,
metamaskState.unapprovedMsgs,
metamaskState.unapprovedPersonalMsgs,
metamaskState.unapprovedDecryptMsgs,
metamaskState.unapprovedEncryptionPublicKeyMsgs,
metamaskState.unapprovedTypedMessages,
metamaskState.network
)
const numberOfUnapprivedTx = unapprovedTxsAll.length
if (numberOfUnapprivedTx > 0) {
store.dispatch(actions.showConfTxPage({