1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-11-22 18:00:18 +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 {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 {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 {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 {TransactionMeta[]} currentNetworkTxList - An array of transactions associated with the currently selected network.
* @property {Object} unapprovedMsgs - An object of messages associated with the currently selected account, mapping a unique ID to the options. * @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 {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 {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 {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 {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 {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 {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. * @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._onBootCleanUp()
this._updateMemstore() this._updateMemstore()
}) })
this.preferencesStore.subscribe(() => this._updateMemstore())
// request state update to finalize initialization // request state update to finalize initialization
this._updatePendingTxsAfterFirstBlock() this._updatePendingTxsAfterFirstBlock()
@ -748,11 +747,10 @@ class TransactionController extends EventEmitter {
_updateMemstore () { _updateMemstore () {
this.pendingTxTracker.updatePendingTxs() this.pendingTxTracker.updatePendingTxs()
const unapprovedTxs = this.txStateManager.getUnapprovedTxList() const unapprovedTxs = this.txStateManager.getUnapprovedTxList()
const selectedAddressTxList = this.txStateManager.getFilteredTxList({ const currentNetworkTxList = this.txStateManager.getFilteredTxList({
from: this.getSelectedAddress(),
metamaskNetworkId: this.getNetwork(), 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. * @returns {number} The id of the newly created EncryptionPublicKey.
* *
*/ */
addUnapprovedMessage (address, _req) { addUnapprovedMessage (address, req) {
log.debug(`EncryptionPublicKeyManager addUnapprovedMessage: address`) log.debug(`EncryptionPublicKeyManager addUnapprovedMessage: address`)
// create txData obj with parameters and meta data // create txData obj with parameters and meta data
const time = (new Date()).getTime() const time = (new Date()).getTime()
@ -117,8 +117,8 @@ export default class EncryptionPublicKeyManager extends EventEmitter {
type: 'eth_getEncryptionPublicKey', type: 'eth_getEncryptionPublicKey',
} }
if (_req) { if (req) {
msgData.origin = _req.origin msgData.origin = req.origin
} }
this.addMsg(msgData) this.addMsg(msgData)

View File

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

View File

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

View File

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

File diff suppressed because one or more lines are too long

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -157,7 +157,7 @@
"forceGasMin": null, "forceGasMin": null,
"toNickname": "" "toNickname": ""
}, },
"selectedAddressTxList": [ "currentNetworkTxList": [
{ {
"id": 3387511061307736, "id": 3387511061307736,
"time": 1528133130531, "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 () { it('should return a state object with the right keys and datat types', function () {
const exposedState = txController.getState() const exposedState = txController.getState()
assert('unapprovedTxs' in exposedState, 'state should have the key unapprovedTxs') 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(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 () { it('updates value of tx by id', function () {
const oldState = { const oldState = {
selectedAddressTxList: [ currentNetworkTxList: [
{ {
id: 1, id: 1,
txParams: 'foo', txParams: 'foo',
@ -288,7 +288,7 @@ describe('MetaMask Reducers', function () {
value: 'bar', value: 'bar',
}) })
assert.equal(state.selectedAddressTxList[0].txParams, 'bar') assert.equal(state.currentNetworkTxList[0].txParams, 'bar')
}) })
it('sets blockies', function () { 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' import { calcMaxAmount } from '../../../../pages/send/send-content/send-amount-row/amount-max-button/amount-max-button.utils'
const mapStateToProps = (state, ownProps) => { const mapStateToProps = (state, ownProps) => {
const { selectedAddressTxList } = state.metamask const { currentNetworkTxList } = state.metamask
const { modalState: { props: modalProps } = {} } = state.appState.modal || {} const { modalState: { props: modalProps } = {} } = state.appState.modal || {}
const { txData = {} } = modalProps || {} const { txData = {} } = modalProps || {}
const { transaction = {} } = ownProps 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 buttonDataLoading = getBasicGasEstimateLoadingStatus(state)
const gasEstimatesLoading = getGasEstimatesLoadingStatus(state) const gasEstimatesLoading = getGasEstimatesLoadingStatus(state)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -6,6 +6,7 @@ import SignatureRequest from '../signature-request.component'
describe('Signature Request Component', function () { describe('Signature Request Component', function () {
describe('render', function () { describe('render', function () {
const fromAddress = '0x123456789abcdef'
it('should render a div with one child', function () { it('should render a div with one child', function () {
const wrapper = shallow(( const wrapper = shallow((
<SignatureRequest <SignatureRequest
@ -15,9 +16,10 @@ describe('Signature Request Component', function () {
txData={{ txData={{
msgParams: { msgParams: {
data: '{"message": {"from": {"name": "hello"}}}', 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 store = configureMockStore()(mockStore)
const props = { const props = {
selectedAccount: '0xd8f6a2ffb0fc5952d16c9768b71cfd35b6399aa5', fromAccount: {
address: '0xd8f6a2ffb0fc5952d16c9768b71cfd35b6399aa5',
},
history: { history: {
push: sinon.spy(), push: sinon.spy(),
}, },

View File

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

View File

@ -287,3 +287,11 @@ export function getOriginFromUrl (url) {
const origin = url.hostname const origin = url.hostname
return origin 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 { id: paramsTransactionId } = params
const { const {
confirmTransaction, confirmTransaction,
metamask: { currentCurrency, conversionRate, selectedAddressTxList, domainMetadata = {}, selectedAddress }, metamask: {
currentCurrency,
conversionRate,
currentNetworkTxList,
domainMetadata = {},
selectedAddress,
},
} = state } = state
const { const {
txData: { id: transactionId, txParams: { to: tokenAddress, data } = {} } = {}, txData: { id: transactionId, txParams: { to: tokenAddress, data } = {} } = {},
} = confirmTransaction } = confirmTransaction
const transaction = selectedAddressTxList.find(({ id }) => id === (Number(paramsTransactionId) || transactionId)) || {} const transaction = (
currentNetworkTxList.find(({ id }) => id === (Number(paramsTransactionId) ||
transactionId)) || {}
)
const { const {
ethTransactionTotal, ethTransactionTotal,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,22 +1,18 @@
import { valuesFor } from '../../helpers/utils/util'
import abi from 'human-standard-token-abi' import abi from 'human-standard-token-abi'
import { multiplyCurrencies } from '../../helpers/utils/conversion-util' 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 { estimateGasPriceFromRecentBlocks, calcGasTotal } from './send.utils'
import { import {
getAveragePriceEstimateInHexWEI, getAveragePriceEstimateInHexWEI,
} from '../../selectors/custom-gas' } 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) { export function getAmountConversionRate (state) {
return getSelectedToken(state) return getSelectedToken(state)
? getSelectedTokenToFiatRate(state) ? getSelectedTokenToFiatRate(state)
@ -31,13 +27,6 @@ export function getConversionRate (state) {
return state.metamask.conversionRate 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) { export function getCurrentCurrency (state) {
return state.metamask.currentCurrency return state.metamask.currentCurrency
} }
@ -162,7 +151,10 @@ export function getSendFromBalance (state) {
return from.balance return from.balance
} }
export function getSendFromObject (state) { export function getSendFromObject (state, address) {
if (address) {
return getTargetAccountWithSendEtherInfo(state, address)
}
return getSendFrom(state) || getCurrentAccountWithSendEtherInfo(state) return getSendFrom(state) || getCurrentAccountWithSendEtherInfo(state)
} }
@ -207,21 +199,6 @@ export function getUnapprovedTxs (state) {
return state.metamask.unapprovedTxs 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) { export function getQrCodeData (state) {
return state.appState.qrCodeData return state.appState.qrCodeData
} }

View File

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

View File

@ -2,11 +2,12 @@ import assert from 'assert'
import sinon from 'sinon' import sinon from 'sinon'
import { import {
accountsWithSendEtherInfoSelector, accountsWithSendEtherInfoSelector,
// autoAddToBetaUI, getCurrentAccountWithSendEtherInfo,
} from '../../../selectors/selectors'
import {
getBlockGasLimit, getBlockGasLimit,
getAmountConversionRate, getAmountConversionRate,
getConversionRate, getConversionRate,
getCurrentAccountWithSendEtherInfo,
getCurrentCurrency, getCurrentCurrency,
getCurrentNetwork, getCurrentNetwork,
getNativeCurrency, getNativeCurrency,
@ -35,7 +36,6 @@ import {
getTokenBalance, getTokenBalance,
getTokenExchangeRate, getTokenExchangeRate,
getUnapprovedTxs, getUnapprovedTxs,
transactionsSelector,
} from '../send.selectors.js' } from '../send.selectors.js'
import mockState from './send-selectors-test-data' 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, checksumAddress,
formatDate, formatDate,
getOriginFromUrl, getOriginFromUrl,
getAccountByAddress,
} from '../helpers/utils/util' } from '../helpers/utils/util'
import { getPermittedAccountsMap } from './permissions' import { getPermittedAccountsMap } from './permissions'
@ -250,7 +251,12 @@ export function getCurrentAccountWithSendEtherInfo (state) {
const currentAddress = getSelectedAddress(state) const currentAddress = getSelectedAddress(state)
const accounts = accountsWithSendEtherInfoSelector(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) { export function getCurrentEthBalance (state) {

View File

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

View File

@ -1,4 +1,4 @@
import assert from 'assert' import { strict as assert } from 'assert'
import { import {
unapprovedMessagesSelector, unapprovedMessagesSelector,
transactionsSelector, transactionsSelector,
@ -11,6 +11,7 @@ import {
describe('Transaction Selectors', function () { describe('Transaction Selectors', function () {
describe('unapprovedMessagesSelector', function () { describe('unapprovedMessagesSelector', function () {
it('returns eth sign msg from unapprovedMsgs', function () { it('returns eth sign msg from unapprovedMsgs', function () {
const msg = { const msg = {
@ -94,13 +95,12 @@ describe('Transaction Selectors', function () {
assert(Array.isArray(msgSelector)) assert(Array.isArray(msgSelector))
assert.deepEqual(msgSelector, [msg]) assert.deepEqual(msgSelector, [msg])
}) })
}) })
describe('transactionsSelector', function () { describe('transactionsSelector', function () {
it('selectedAddressTxList', function () { it('selects the currentNetworkTxList', function () {
const state = { const state = {
metamask: { metamask: {
@ -110,7 +110,8 @@ describe('Transaction Selectors', function () {
featureFlags: { featureFlags: {
showIncomingTransactions: false, showIncomingTransactions: false,
}, },
selectedAddressTxList: [ selectedAddress: '0xAddress',
currentNetworkTxList: [
{ {
id: 0, id: 0,
time: 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(Array.isArray(selectedTx))
assert.deepEqual(txSelector, orderedTxlist) 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 = { const state = {
metamask: { metamask: {
@ -149,8 +150,9 @@ describe('Transaction Selectors', function () {
featureFlags: { featureFlags: {
showIncomingTransactions: false, showIncomingTransactions: false,
}, },
selectedAddress: '0xAddress',
selectedTokenAddress: '0xToken', selectedTokenAddress: '0xToken',
selectedAddressTxList: [ currentNetworkTxList: [
{ {
id: 0, id: 0,
time: 0, time: 0,
@ -162,6 +164,14 @@ describe('Transaction Selectors', function () {
{ {
id: 1, id: 1,
time: 1, time: 1,
txParams: {
from: '0xAddress',
to: '0xRecipient',
},
},
{
id: 2,
time: 2,
txParams: { txParams: {
from: '0xAddress', from: '0xAddress',
to: '0xToken', 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) const selectedTx = transactionsSelector(state)
assert(Array.isArray(txSelector))
assert.deepEqual(txSelector, orderedTxlist)
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 () { describe('nonceSortedTransactionsSelector', function () {
@ -212,10 +314,11 @@ describe('Transaction Selectors', function () {
provider: { provider: {
nickname: 'mainnet', nickname: 'mainnet',
}, },
selectedAddress: '0xAddress',
featureFlags: { featureFlags: {
showIncomingTransactions: false, showIncomingTransactions: false,
}, },
selectedAddressTxList: [ currentNetworkTxList: [
tx1, tx1,
tx2, tx2,
], ],
@ -296,10 +399,11 @@ describe('Transaction Selectors', function () {
provider: { provider: {
nickname: 'mainnet', nickname: 'mainnet',
}, },
selectedAddress: '0xAddress',
featureFlags: { featureFlags: {
showIncomingTransactions: false, showIncomingTransactions: false,
}, },
selectedAddressTxList: [ currentNetworkTxList: [
submittedTx, submittedTx,
unapprovedTx, unapprovedTx,
approvedTx, approvedTx,
@ -360,9 +464,6 @@ describe('Transaction Selectors', function () {
const expectedResult = [ submittedTx ] const expectedResult = [ submittedTx ]
assert.deepEqual(submittedPendingTransactionsSelector(state), expectedResult) assert.deepEqual(submittedPendingTransactionsSelector(state), expectedResult)
}) })
}) })
}) })

View File

@ -12,7 +12,11 @@ import {
import { hexToDecimal } from '../helpers/utils/conversions.util' import { hexToDecimal } from '../helpers/utils/conversions.util'
import { selectedTokenAddressSelector } from './tokens' import { selectedTokenAddressSelector } from './tokens'
import { getFastPriceEstimateInHexWEI } from './custom-gas' import { getFastPriceEstimateInHexWEI } from './custom-gas'
import { getSelectedToken, getIsMainnet } from './selectors' import {
getIsMainnet,
getSelectedToken,
getSelectedAddress,
} from './selectors'
import txHelper from '../../lib/tx-helper' import txHelper from '../../lib/tx-helper'
export const shapeShiftTxListSelector = (state) => { export const shapeShiftTxListSelector = (state) => {
@ -29,20 +33,28 @@ export const incomingTxListSelector = (state) => {
} }
const network = state.metamask.network const network = state.metamask.network
const selectedAddress = state.metamask.selectedAddress const selectedAddress = getSelectedAddress(state)
return Object.values(state.metamask.incomingTransactions) return Object.values(state.metamask.incomingTransactions)
.filter(({ metamaskNetworkId, txParams }) => ( .filter(({ metamaskNetworkId, txParams }) => (
txParams.to === selectedAddress && metamaskNetworkId === network txParams.to === selectedAddress && metamaskNetworkId === network
)) ))
} }
export const unapprovedMsgsSelector = (state) => state.metamask.unapprovedMsgs 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 unapprovedPersonalMsgsSelector = (state) => state.metamask.unapprovedPersonalMsgs
export const unapprovedDecryptMsgsSelector = (state) => state.metamask.unapprovedDecryptMsgs export const unapprovedDecryptMsgsSelector = (state) => state.metamask.unapprovedDecryptMsgs
export const unapprovedEncryptionPublicKeyMsgsSelector = (state) => state.metamask.unapprovedEncryptionPublicKeyMsgs export const unapprovedEncryptionPublicKeyMsgsSelector = (state) => state.metamask.unapprovedEncryptionPublicKeyMsgs
export const unapprovedTypedMessagesSelector = (state) => state.metamask.unapprovedTypedMessages export const unapprovedTypedMessagesSelector = (state) => state.metamask.unapprovedTypedMessages
export const networkSelector = (state) => state.metamask.network 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( export const unapprovedMessagesSelector = createSelector(
unapprovedMsgsSelector, unapprovedMsgsSelector,
unapprovedPersonalMsgsSelector, unapprovedPersonalMsgsSelector,
@ -79,21 +91,43 @@ const priorityStatusHash = {
[CONFIRMED_STATUS]: true, [CONFIRMED_STATUS]: true,
} }
export const transactionsSelector = createSelector( export const transactionSubSelector = createSelector(
selectedTokenAddressSelector,
unapprovedMessagesSelector, unapprovedMessagesSelector,
shapeShiftTxListSelector, shapeShiftTxListSelector,
incomingTxListSelector, incomingTxListSelector,
selectedAddressTxListSelector, (unapprovedMessages = [], shapeShiftTxList = [], incomingTxList = []) => {
(selectedTokenAddress, unapprovedMessages = [], shapeShiftTxList = [], incomingTxList = [], transactions = []) => { return unapprovedMessages.concat(shapeShiftTxList, incomingTxList)
const txsToRender = transactions.concat(unapprovedMessages, shapeShiftTxList, incomingTxList) }
)
const transactionSelectorReturnHelper = (selectedTokenAddress, transactions) => {
return selectedTokenAddress return selectedTokenAddress
? txsToRender ? transactions
.filter(({ txParams }) => txParams && txParams.to === selectedTokenAddress) .filter(({ txParams }) => txParams && txParams.to === selectedTokenAddress)
.sort((a, b) => b.time - a.time) .sort((a, b) => b.time - a.time)
: txsToRender : transactions
.sort((a, b) => b.time - a.time) .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) return reject(err)
} }
const { selectedAddressTxList } = newState const { currentNetworkTxList } = newState
const { id } = selectedAddressTxList[selectedAddressTxList.length - 1] const { id } = currentNetworkTxList[currentNetworkTxList.length - 1]
newTxId = id newTxId = id
resolve(newState) resolve(newState)
}) })
@ -1496,8 +1496,8 @@ export function createCancelTransaction (txId, customGasPrice) {
return reject(err) return reject(err)
} }
const { selectedAddressTxList } = newState const { currentNetworkTxList } = newState
const { id } = selectedAddressTxList[selectedAddressTxList.length - 1] const { id } = currentNetworkTxList[currentNetworkTxList.length - 1]
newTxId = id newTxId = id
resolve(newState) resolve(newState)
}) })
@ -1519,8 +1519,8 @@ export function createSpeedUpTransaction (txId, customGasPrice) {
return reject(err) return reject(err)
} }
const { selectedAddressTxList } = newState const { currentNetworkTxList } = newState
newTx = selectedAddressTxList[selectedAddressTxList.length - 1] newTx = currentNetworkTxList[currentNetworkTxList.length - 1]
resolve(newState) resolve(newState)
}) })
}) })
@ -1541,8 +1541,8 @@ export function createRetryTransaction (txId, customGasPrice) {
return reject(err) return reject(err)
} }
const { selectedAddressTxList } = newState const { currentNetworkTxList } = newState
newTx = selectedAddressTxList[selectedAddressTxList.length - 1] newTx = currentNetworkTxList[currentNetworkTxList.length - 1]
resolve(newState) resolve(newState)
}) })
}) })

View File

@ -59,7 +59,15 @@ async function startApp (metamaskState, backgroundConnection, opts) {
}) })
// if unconfirmed txs, start on txConf page // 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 const numberOfUnapprivedTx = unapprovedTxsAll.length
if (numberOfUnapprivedTx > 0) { if (numberOfUnapprivedTx > 0) {
store.dispatch(actions.showConfTxPage({ store.dispatch(actions.showConfTxPage({