diff --git a/src/routes/safe/components/TransactionsNew/TxsTable/Status/index.jsx b/src/routes/safe/components/TransactionsNew/TxsTable/Status/index.jsx index b183a7d3..ba175309 100644 --- a/src/routes/safe/components/TransactionsNew/TxsTable/Status/index.jsx +++ b/src/routes/safe/components/TransactionsNew/TxsTable/Status/index.jsx @@ -11,12 +11,12 @@ import { styles } from './style' type Props = { classes: Object, - status: 'pending' | 'awaiting' | 'success' | 'failed', + status: 'awaiting' | 'success' | 'cancelled', } const statusToIcon = { success: OkIcon, - failed: ErrorIcon, + cancelled: ErrorIcon, awaiting: AwaitingIcon, } diff --git a/src/routes/safe/components/TransactionsNew/TxsTable/Status/style.js b/src/routes/safe/components/TransactionsNew/TxsTable/Status/style.js index cd0e65d5..ad4a321f 100644 --- a/src/routes/safe/components/TransactionsNew/TxsTable/Status/style.js +++ b/src/routes/safe/components/TransactionsNew/TxsTable/Status/style.js @@ -15,7 +15,7 @@ export const styles = () => ({ backgroundColor: '#d7f3f3', color: '#346d6d', }, - failed: { + cancelled: { backgroundColor: 'transparent', color: '#fd7890', }, diff --git a/src/routes/safe/components/TransactionsNew/TxsTable/columns.js b/src/routes/safe/components/TransactionsNew/TxsTable/columns.js index ed417165..7abcc774 100644 --- a/src/routes/safe/components/TransactionsNew/TxsTable/columns.js +++ b/src/routes/safe/components/TransactionsNew/TxsTable/columns.js @@ -30,6 +30,18 @@ export const formatDate = (date: Date): string => format(date, 'MMM D, YYYY - h: export type TransactionRow = SortRow +const getTxStatus = (tx: Transaction): string => { + let txStatus = 'awaiting' + + if (tx.isExecuted) { + txStatus = 'success' + } else if (tx.cancelled) { + txStatus = 'cancelled' + } + + return txStatus +} + export const getTxTableData = (transactions: List): List => { const rows = transactions.map((tx: Transaction) => { const txDate = tx.isExecuted ? tx.executionDate : tx.submissionDate @@ -40,7 +52,7 @@ export const getTxTableData = (transactions: List): List 0 ? `${fromWei(toBN(tx.value), 'ether')} ${tx.symbol}` : 'n/a', - [TX_TABLE_STATUS_ID]: tx.isExecuted ? 'success' : 'awaiting', + [TX_TABLE_STATUS_ID]: getTxStatus(tx), [TX_TABLE_RAW_TX_ID]: tx, } }) diff --git a/src/routes/safe/components/TransactionsNew/TxsTable/index.jsx b/src/routes/safe/components/TransactionsNew/TxsTable/index.jsx index b801c186..f9a43564 100644 --- a/src/routes/safe/components/TransactionsNew/TxsTable/index.jsx +++ b/src/routes/safe/components/TransactionsNew/TxsTable/index.jsx @@ -50,8 +50,8 @@ const TxsTable = ({ }: Props) => { const [expandedTx, setExpandedTx] = useState(null) - const handleTxExpand = (nonce) => { - setExpandedTx(prevTx => (prevTx === nonce ? null : nonce)) + const handleTxExpand = (creationTxHash) => { + setExpandedTx(prevTx => (prevTx === creationTxHash ? null : creationTxHash)) } const columns = generateColumns() @@ -73,8 +73,8 @@ const TxsTable = ({ handleTxExpand(row.tx.id)} + className={cn(classes.row, expandedTx === row.tx.creationTxHash && classes.expandedRow)} + onClick={() => handleTxExpand(row.tx.creationTxHash)} > {autoColumns.map((column: Column) => ( }, ) +const extendedTransactionsSelector: Selector> = createSelector( + safeTransactionsSelector, + (transactions) => { + const extendedTransactions = transactions.map((tx: Transaction) => { + if (tx.isExecuted) { + return tx + } + + // If transactions is not executed, but there's a transaction with the same nonce submitted later + // it means that the transaction was cancelled (Replaced) and shouldn't get executed + const replacementTransaction = transactions.findLast( + transaction => transaction.nonce === tx.nonce && isAfter(transaction.submissionDate, tx.submissionDate), + ) + + if (!replacementTransaction) { + return tx + } + + return tx.set('cancelled', true) + }) + + return extendedTransactions + }, +) + export default createStructuredSelector({ safe: safeSelector, provider: providerNameSelector, @@ -98,5 +124,5 @@ export default createStructuredSelector({ userAddress: userAccountSelector, network: networkSelector, safeUrl: safeParamAddressSelector, - transactions: safeTransactionsSelector, + transactions: extendedTransactionsSelector, }) diff --git a/src/routes/safe/store/actions/fetchTransactions.js b/src/routes/safe/store/actions/fetchTransactions.js index 4b239bba..8673042f 100644 --- a/src/routes/safe/store/actions/fetchTransactions.js +++ b/src/routes/safe/store/actions/fetchTransactions.js @@ -13,7 +13,7 @@ import { EMPTY_DATA } from '~/logic/wallets/ethTransactions' import { addTransactions } from './addTransactions' import { getHumanFriendlyToken } from '~/logic/tokens/store/actions/fetchTokens' import { isAddressAToken } from '~/logic/tokens/utils/tokenHelpers' -import { TX_TYPE_EXECUTION } from '~/logic/safe/transactions/send' +import { TX_TYPE_EXECUTION, TX_TYPE_CONFIRMATION } from '~/logic/safe/transactions/send' type ConfirmationServiceModel = { owner: string, @@ -49,6 +49,7 @@ const buildTransactionFrom = async (safeAddress: string, tx: TxServiceModel, saf }), ) const isToken = await isAddressAToken(tx.to) + const creationTxHash = confirmations.find(conf => conf.type === TX_TYPE_CONFIRMATION).hash let executionTxHash const executionTx = confirmations.find(conf => conf.type === TX_TYPE_EXECUTION) @@ -76,6 +77,7 @@ const buildTransactionFrom = async (safeAddress: string, tx: TxServiceModel, saf submissionDate: tx.submissionDate, executionDate: tx.executionDate, executionTxHash, + creationTxHash, }) } diff --git a/src/routes/safe/store/models/transaction.js b/src/routes/safe/store/models/transaction.js index d2e225b4..bc162b50 100644 --- a/src/routes/safe/store/models/transaction.js +++ b/src/routes/safe/store/models/transaction.js @@ -14,7 +14,9 @@ export type TransactionProps = { submissionDate: Date, executionDate: Date, symbol: string, + creationTxHash: string, executionTxHash?: string, + cancelled?: boolean, } export const makeTransaction: RecordFactory = Record({ @@ -29,6 +31,8 @@ export const makeTransaction: RecordFactory = Record({ executionDate: '', symbol: '', executionTxHash: undefined, + creationTxHash: '', + cancelled: false, }) export type Transaction = RecordOf