diff --git a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/index.tsx b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/index.tsx index 51c351d2..7f7f7309 100644 --- a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/index.tsx +++ b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/index.tsx @@ -15,7 +15,6 @@ import Block from 'src/components/layout/Block' import Col from 'src/components/layout/Col' import Img from 'src/components/layout/Img' import Paragraph from 'src/components/layout/Paragraph/index' -import { TX_TYPE_CONFIRMATION } from 'src/logic/safe/transactions/send' import { userAccountSelector } from 'src/logic/wallets/store/selectors' import { makeTransaction } from 'src/routes/safe/store/models/transaction' import { safeOwnersSelector, safeThresholdSelector } from 'src/routes/safe/store/selectors' @@ -29,11 +28,8 @@ function getOwnersConfirmations(tx, userAddress) { currentUserAlreadyConfirmed = true } - if (conf.type === TX_TYPE_CONFIRMATION) { - ownersWhoConfirmed.push(conf.owner) - } + ownersWhoConfirmed.push(conf.owner) }) - return [ownersWhoConfirmed, currentUserAlreadyConfirmed] } diff --git a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/utils.ts b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/utils.ts index 05356aca..bcf7a81c 100644 --- a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/utils.ts +++ b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/utils.ts @@ -1,5 +1,4 @@ import { SAFE_METHODS_NAMES } from 'src/logic/contracts/methodIds' -import { getWeb3 } from 'src/logic/wallets/getWeb3' const getSafeVersion = (data) => { const contractAddress = data.substr(340, 40).toLowerCase() @@ -12,43 +11,50 @@ const getSafeVersion = (data) => { } export const getTxData = (tx) => { - const web3 = getWeb3() - const { fromWei, toBN } = web3.utils - const txData: any = {} - if (tx.isTokenTransfer && tx.decodedParams) { - txData.recipient = tx.decodedParams.recipient - txData.value = fromWei(toBN(tx.decodedParams.value), 'ether') + if (tx.decodedParams) { + if (tx.isTokenTransfer) { + const { to } = tx.decodedParams.transfer + txData.recipient = to + txData.isTokenTransfer = true + } + if (tx.isCollectibleTransfer) { + const { safeTransferFrom, transfer, transferFrom } = tx.decodedParams + const { to, value } = safeTransferFrom || transferFrom || transfer + txData.recipient = to + txData.tokenId = value + txData.isCollectibleTransfer = true + } } else if (tx.customTx) { txData.recipient = tx.recipient - txData.value = fromWei(toBN(tx.value), 'ether') txData.data = tx.data txData.customTx = true } else if (Number(tx.value) > 0) { txData.recipient = tx.recipient - txData.value = fromWei(toBN(tx.value), 'ether') } else if (tx.modifySettingsTx) { txData.recipient = tx.recipient txData.modifySettingsTx = true if (tx.decodedParams) { - txData.action = tx.decodedParams.methodName - - if (txData.action === SAFE_METHODS_NAMES.REMOVE_OWNER) { - txData.removedOwner = tx.decodedParams.args[1] - txData.newThreshold = tx.decodedParams.args[2] - } else if (txData.action === SAFE_METHODS_NAMES.CHANGE_THRESHOLD) { - txData.newThreshold = tx.decodedParams.args[0] - } else if (txData.action === SAFE_METHODS_NAMES.ADD_OWNER_WITH_THRESHOLD) { - txData.addedOwner = tx.decodedParams.args[0] - txData.newThreshold = tx.decodedParams.args[1] - } else if (txData.action === SAFE_METHODS_NAMES.SWAP_OWNER) { - txData.removedOwner = tx.decodedParams.args[1] - txData.addedOwner = tx.decodedParams.args[2] + if (tx.decodedParams[SAFE_METHODS_NAMES.REMOVE_OWNER]) { + const { _threshold, owner } = tx.decodedParams[SAFE_METHODS_NAMES.REMOVE_OWNER] + txData.removedOwner = owner + txData.newThreshold = _threshold + } else if (tx.decodedParams[SAFE_METHODS_NAMES.CHANGE_THRESHOLD]) { + const { _threshold } = tx.decodedParams[SAFE_METHODS_NAMES.CHANGE_THRESHOLD] + txData.newThreshold = _threshold + } else if (tx.decodedParams[SAFE_METHODS_NAMES.ADD_OWNER_WITH_THRESHOLD]) { + const { _threshold, owner } = tx.decodedParams[SAFE_METHODS_NAMES.ADD_OWNER_WITH_THRESHOLD] + txData.addedOwner = owner + txData.newThreshold = _threshold + } else if (tx.decodedParams[SAFE_METHODS_NAMES.SWAP_OWNER]) { + const { newOwner, oldOwner } = tx.decodedParams[SAFE_METHODS_NAMES.SWAP_OWNER] + txData.removedOwner = oldOwner + txData.addedOwner = newOwner } } - } else if (tx.cancellationTx) { + } else if (tx.isCancellationTx) { txData.cancellationTx = true } else if (tx.creationTx) { txData.creationTx = true @@ -57,7 +63,6 @@ export const getTxData = (tx) => { txData.data = `The contract of this Safe is upgraded to Version ${getSafeVersion(tx.data)}` } else { txData.recipient = tx.recipient - txData.value = 0 } return txData diff --git a/src/routes/safe/components/Transactions/TxsTable/TxType/index.tsx b/src/routes/safe/components/Transactions/TxsTable/TxType/index.tsx index bec45804..487f628a 100644 --- a/src/routes/safe/components/Transactions/TxsTable/TxType/index.tsx +++ b/src/routes/safe/components/Transactions/TxsTable/TxType/index.tsx @@ -11,6 +11,8 @@ import { getAppInfoFromOrigin, getAppInfoFromUrl } from 'src/routes/safe/compone const typeToIcon = { outgoing: OutgoingTxIcon, + token: OutgoingTxIcon, + collectible: OutgoingTxIcon, incoming: IncomingTxIcon, custom: CustomTxIcon, settings: SettingsTxIcon, @@ -21,6 +23,8 @@ const typeToIcon = { const typeToLabel = { outgoing: 'Outgoing transfer', + token: 'Outgoing transfer', + collectible: 'Outgoing transfer', incoming: 'Incoming transfer', custom: 'Contract Interaction', settings: 'Modify settings', diff --git a/src/routes/safe/components/Transactions/TxsTable/columns.tsx b/src/routes/safe/components/Transactions/TxsTable/columns.tsx index a856fdb7..b5cda0cb 100644 --- a/src/routes/safe/components/Transactions/TxsTable/columns.tsx +++ b/src/routes/safe/components/Transactions/TxsTable/columns.tsx @@ -2,7 +2,7 @@ import { BigNumber } from 'bignumber.js' import format from 'date-fns/format' import getTime from 'date-fns/getTime' import parseISO from 'date-fns/parseISO' -import { List, Map } from 'immutable' +import { List } from 'immutable' import React from 'react' import TxType from './TxType' @@ -43,7 +43,7 @@ export const getIncomingTxAmount = (tx, formatted = true) => { export const getTxAmount = (tx, formatted = true) => { const { decimals = 18, decodedParams, isTokenTransfer, symbol } = tx - const { value } = isTokenTransfer && decodedParams && decodedParams.value ? decodedParams : tx + const { value } = isTokenTransfer && !!decodedParams && !!decodedParams.transfer ? decodedParams.transfer : tx if (!isTokenTransfer && !(Number(value) > 0)) { return NOT_AVAILABLE @@ -65,22 +65,9 @@ const getIncomingTxTableData = (tx) => ({ const getTransactionTableData = (tx, cancelTx) => { const txDate = tx.submissionDate - let txType = 'outgoing' - if (tx.modifySettingsTx) { - txType = 'settings' - } else if (tx.cancellationTx) { - txType = 'cancellation' - } else if (tx.customTx) { - txType = 'custom' - } else if (tx.creationTx) { - txType = 'creation' - } else if (tx.upgradeTx) { - txType = 'upgrade' - } - return { [TX_TABLE_ID]: tx.blockNumber, - [TX_TABLE_TYPE_ID]: , + [TX_TABLE_TYPE_ID]: , [TX_TABLE_DATE_ID]: txDate ? formatDate(txDate) : '', [buildOrderFieldFrom(TX_TABLE_DATE_ID)]: txDate ? getTime(parseISO(txDate)) : null, [TX_TABLE_AMOUNT_ID]: getTxAmount(tx), @@ -91,17 +78,12 @@ const getTransactionTableData = (tx, cancelTx) => { } export const getTxTableData = (transactions, cancelTxs) => { - const cancelTxsByNonce = cancelTxs.reduce((acc, tx) => acc.set(tx.nonce, tx), Map()) - return transactions.map((tx) => { - if (INCOMING_TX_TYPES[tx.type]) { + if (INCOMING_TX_TYPES[tx.type] !== undefined) { return getIncomingTxTableData(tx) } - return getTransactionTableData( - tx, - Number.isInteger(Number.parseInt(tx.nonce, 10)) ? cancelTxsByNonce.get(tx.nonce) : undefined, - ) + return getTransactionTableData(tx, cancelTxs.get(`${tx.nonce}`)) }) } diff --git a/src/routes/safe/components/Transactions/TxsTable/index.tsx b/src/routes/safe/components/Transactions/TxsTable/index.tsx index e5dbd6bc..56ec6177 100644 --- a/src/routes/safe/components/Transactions/TxsTable/index.tsx +++ b/src/routes/safe/components/Transactions/TxsTable/index.tsx @@ -19,8 +19,8 @@ import Table from 'src/components/Table' import { cellWidth } from 'src/components/Table/TableHead' import Block from 'src/components/layout/Block' import Row from 'src/components/layout/Row' -import { extendedTransactionsSelector } from 'src/routes/safe/container/selector' import { safeCancellationTransactionsSelector } from 'src/routes/safe/store/selectors' +import { extendedTransactionsSelector } from '../../../store/selectors/transactions' export const TRANSACTION_ROW_TEST_ID = 'transaction-row' @@ -36,8 +36,8 @@ const TxsTable = ({ classes }) => { const cancellationTransactions = useSelector(safeCancellationTransactionsSelector) const transactions = useSelector(extendedTransactionsSelector) - const handleTxExpand = (safeTxHash) => { - setExpandedTx((prevTx) => (prevTx === safeTxHash ? null : safeTxHash)) + const handleTxExpand = (rowCombinedId) => { + setExpandedTx((prevId) => (prevId === rowCombinedId ? null : rowCombinedId)) } const columns = generateColumns() @@ -83,58 +83,65 @@ const TxsTable = ({ classes }) => { size={filteredData.size} > {(sortedData) => - sortedData.map((row, index) => ( - - handleTxExpand(row.tx.safeTxHash)} - tabIndex={-1} - > - {autoColumns.map((column: any) => ( - - {row[column.id]} + sortedData.map((row, index) => { + const rowCombinedId = `${row.tx.nonce + row.tx.data}` + + return ( + + handleTxExpand(rowCombinedId)} + tabIndex={-1} + > + {autoColumns.map((column: any) => ( + + {row[column.id]} + + ))} + + + + - ))} - - - - - - - {!row.tx.creationTx && ( - - {expandedTx === row.safeTxHash ? : } - - )} - - - {!row.tx.creationTx && ( - - - + + {!row.tx.creationTx && ( + + {expandedTx === rowCombinedId ? : } + + )} - )} - - )) + {!row.tx.creationTx && ( + + + + + + )} + + ) + }) } diff --git a/src/routes/safe/container/selector.ts b/src/routes/safe/container/selector.ts index 9c63b816..9c9fb0b4 100644 --- a/src/routes/safe/container/selector.ts +++ b/src/routes/safe/container/selector.ts @@ -1,4 +1,4 @@ -import { List, Map } from 'immutable' +import { Map } from 'immutable' import { createSelector } from 'reselect' import { tokensSelector } from 'src/logic/tokens/store/selectors' @@ -6,39 +6,7 @@ import { getEthAsToken } from 'src/logic/tokens/utils/tokenHelpers' import { isUserOwner } from 'src/logic/wallets/ethAddresses' import { userAccountSelector } from 'src/logic/wallets/store/selectors' -import { - safeActiveTokensSelector, - safeBalancesSelector, - safeCancellationTransactionsSelector, - safeIncomingTransactionsSelector, - safeSelector, - safeTransactionsSelector, -} from 'src/routes/safe/store/selectors' - -const getTxStatus = (tx, userAddress, safe) => { - let txStatus - if (tx.executionTxHash) { - txStatus = 'success' - } else if (tx.cancelled) { - txStatus = 'cancelled' - } else if (tx.confirmations.size === safe.threshold) { - txStatus = 'awaiting_execution' - } else if (tx.creationTx) { - txStatus = 'success' - } else if (!tx.confirmations.size) { - txStatus = 'pending' - } else { - const userConfirmed = tx.confirmations.filter((conf) => conf.owner === userAddress).size === 1 - const userIsSafeOwner = safe.owners.filter((owner) => owner.address === userAddress).size === 1 - txStatus = !userConfirmed && userIsSafeOwner ? 'awaiting_your_confirmation' : 'awaiting_confirmations' - } - - if (tx.isSuccessful === false) { - txStatus = 'failed' - } - - return txStatus -} +import { safeActiveTokensSelector, safeBalancesSelector, safeSelector } from 'src/routes/safe/store/selectors' export const grantedSelector = createSelector(userAccountSelector, safeSelector, (userAccount, safe) => isUserOwner(safe, userAccount), @@ -76,31 +44,3 @@ export const extendedSafeTokensSelector = createSelector( return extendedTokens.toList() }, ) - -export const extendedTransactionsSelector = createSelector( - safeSelector, - userAccountSelector, - safeTransactionsSelector, - safeCancellationTransactionsSelector, - safeIncomingTransactionsSelector, - (safe, userAddress, transactions, cancellationTransactions, incomingTransactions) => { - const cancellationTransactionsByNonce = cancellationTransactions.reduce((acc, tx) => acc.set(tx.nonce, tx), Map()) - const extendedTransactions = transactions.map((tx) => { - let extendedTx = tx - - if (!tx.isExecuted) { - if ( - (cancellationTransactionsByNonce.get(tx.nonce) && - cancellationTransactionsByNonce.get(tx.nonce).get('isExecuted')) || - transactions.find((safeTx) => tx.nonce === safeTx.nonce && safeTx.isExecuted) - ) { - extendedTx = tx.set('cancelled', true) - } - } - - return extendedTx.set('status', getTxStatus(extendedTx, userAddress, safe)) - }) - - return List([...extendedTransactions, ...incomingTransactions]) - }, -) diff --git a/src/routes/safe/store/selectors/transactions.ts b/src/routes/safe/store/selectors/transactions.ts index b5dbb408..da4b7782 100644 --- a/src/routes/safe/store/selectors/transactions.ts +++ b/src/routes/safe/store/selectors/transactions.ts @@ -1,65 +1,10 @@ -import { List, Map } from 'immutable' +import { List } from 'immutable' import { createSelector } from 'reselect' -import { userAccountSelector } from 'src/logic/wallets/store/selectors' -import { - safeCancellationTransactionsSelector, - safeIncomingTransactionsSelector, - safeSelector, - safeTransactionsSelector, -} from 'src/routes/safe/store/selectors' - -const getTxStatus = (tx: any, userAddress: string, safe): string => { - let txStatus - - if (tx.executionTxHash) { - txStatus = 'success' - } else if (tx.cancelled) { - txStatus = 'cancelled' - } else if (tx.confirmations.size === safe.threshold) { - txStatus = 'awaiting_execution' - } else if (tx.creationTx) { - txStatus = 'success' - } else if (!tx.confirmations.size) { - txStatus = 'pending' - } else { - const userConfirmed = tx.confirmations.filter((conf) => conf.owner === userAddress).size === 1 - const userIsSafeOwner = safe.owners.filter((owner) => owner.address === userAddress).size === 1 - txStatus = !userConfirmed && userIsSafeOwner ? 'awaiting_your_confirmation' : 'awaiting_confirmations' - } - - if (tx.isSuccessful === false) { - txStatus = 'failed' - } - - return txStatus -} +import { safeIncomingTransactionsSelector, safeTransactionsSelector } from 'src/routes/safe/store/selectors' export const extendedTransactionsSelector = createSelector( - safeSelector, - userAccountSelector, safeTransactionsSelector, - safeCancellationTransactionsSelector, safeIncomingTransactionsSelector, - (safe, userAddress, transactions, cancellationTransactions, incomingTransactions) => { - const cancellationTransactionsByNonce = cancellationTransactions.reduce((acc, tx) => acc.set(tx.nonce, tx), Map()) - const extendedTransactions = transactions.map((tx: any) => - tx.withMutations((transaction) => { - if (!transaction.isExecuted) { - if ( - (cancellationTransactionsByNonce.get(tx.nonce) && - cancellationTransactionsByNonce.get(tx.nonce).get('isExecuted')) || - transactions.find((safeTx) => tx.nonce === safeTx.nonce && safeTx.isExecuted) - ) { - transaction.set('cancelled', true) - } - } - transaction.set('status', getTxStatus(transaction, userAddress, safe)) - - return transaction - }), - ) - - return List([...extendedTransactions, ...incomingTransactions]) - }, + (transactions, incomingTransactions) => List([...transactions, ...incomingTransactions]), )