diff --git a/src/logic/safe/store/actions/createTransaction.ts b/src/logic/safe/store/actions/createTransaction.ts index 6f0424a730..f21a5d349d 100644 --- a/src/logic/safe/store/actions/createTransaction.ts +++ b/src/logic/safe/store/actions/createTransaction.ts @@ -5,6 +5,7 @@ import semverSatisfies from 'semver/functions/satisfies' import { ThunkAction } from 'redux-thunk' import { onboardUser } from 'src/components/ConnectButton' +import { decodeMethods } from 'src/logic/contracts/methodIds' import { getGnosisSafeInstanceAt } from 'src/logic/contracts/safeContracts' import { getNotificationsFromTxType } from 'src/logic/notifications' import { @@ -205,6 +206,7 @@ const createTransaction = ( confirmations: [], // this is used to determine if a tx is pending or not. See `calculateTransactionStatus` helper value: txArgs.valueInWei, safeTxHash, + dataDecoded: decodeMethods(txArgs.data), submissionDate: new Date().toISOString(), } const mockedTx = await mockTransaction(txToMock, safeAddress, state) diff --git a/src/logic/safe/store/actions/processTransaction.ts b/src/logic/safe/store/actions/processTransaction.ts index 0791584c75..9c016876c6 100644 --- a/src/logic/safe/store/actions/processTransaction.ts +++ b/src/logic/safe/store/actions/processTransaction.ts @@ -1,4 +1,3 @@ -import { fromJS } from 'immutable' import semverSatisfies from 'semver/functions/satisfies' import { getGnosisSafeInstanceAt } from 'src/logic/contracts/safeContracts' @@ -20,9 +19,9 @@ import { import { getLastTx, getNewTxNonce, shouldExecuteTransaction } from 'src/logic/safe/store/actions/utils' import { getErrorMessage } from 'src/test/utils/ethereumErrors' -import { makeConfirmation } from '../models/confirmation' import { storeTx } from './createTransaction' -import { TransactionStatus } from '../models/types/transaction' +import { TransactionStatus } from 'src/logic/safe/store/models/types/transaction' +import { makeConfirmation } from 'src/logic/safe/store/models/confirmation' const processTransaction = ({ approveAndExecute, notifiedTransaction, safeAddress, tx, userAddress }) => async ( dispatch, @@ -85,6 +84,8 @@ const processTransaction = ({ approveAndExecute, notifiedTransaction, safeAddres dispatch(closeSnackbarAction(beforeExecutionKey)) await saveTxToHistory({ ...txArgs, signature }) + // TODO: while we wait for the tx to be stored in the service and later update the tx info + // we should update the tx status in the store to disable owners' action buttons dispatch(enqueueSnackbar(notificationsQueue.afterExecution.moreConfirmationsNeeded)) dispatch(fetchTransactions(safeAddress)) @@ -105,9 +106,7 @@ const processTransaction = ({ approveAndExecute, notifiedTransaction, safeAddres const txToMock: TxToMock = { ...txArgs, - confirmations: txArgs.confirmations, // this is used to determine if a tx is pending or not. See `calculateTransactionStatus` helper value: txArgs.valueInWei, - submissionDate: txArgs.submissionDate, } const mockedTx = await mockTransaction(txToMock, safeAddress, state) @@ -123,10 +122,14 @@ const processTransaction = ({ approveAndExecute, notifiedTransaction, safeAddres await Promise.all([ saveTxToHistory({ ...txArgs, txHash }), storeTx( - mockedTx.updateIn( - ['ownersWithPendingActions', mockedTx.isCancellationTx ? 'reject' : 'confirm'], - (previous) => previous.push(from), - ), + mockedTx.withMutations((record) => { + record + .updateIn( + ['ownersWithPendingActions', mockedTx.isCancellationTx ? 'reject' : 'confirm'], + (previous) => previous.push(from), + ) + .set('status', TransactionStatus.PENDING) + }), safeAddress, dispatch, state, @@ -175,16 +178,20 @@ const processTransaction = ({ approveAndExecute, notifiedTransaction, safeAddres : TransactionStatus.FAILED, ) .updateIn(['ownersWithPendingActions', 'reject'], (prev) => prev.clear()) + .updateIn(['ownersWithPendingActions', 'confirm'], (prev) => prev.clear()) + }) + : mockedTx.withMutations((record) => { + record + .updateIn(['ownersWithPendingActions', toStoreTx.isCancellationTx ? 'reject' : 'confirm'], (previous) => + previous.pop(), + ) + .set('status', TransactionStatus.AWAITING_CONFIRMATIONS) }) - : mockedTx.set('status', TransactionStatus.AWAITING_CONFIRMATIONS) await storeTx( - toStoreTx.withMutations((record) => { - record - .set('confirmations', fromJS([...tx.confirmations, makeConfirmation({ owner: from })])) - .updateIn(['ownersWithPendingActions', toStoreTx.isCancellationTx ? 'reject' : 'confirm'], (previous) => - previous.pop(from), - ) + toStoreTx.update('confirmations', (confirmations) => { + const index = confirmations.findIndex(({ owner }) => owner === from) + return index === -1 ? confirmations.push(makeConfirmation({ owner: from })) : confirmations }), safeAddress, dispatch, diff --git a/src/logic/safe/store/actions/transactions/utils/transactionHelpers.ts b/src/logic/safe/store/actions/transactions/utils/transactionHelpers.ts index 05b89bb8fc..71512cc085 100644 --- a/src/logic/safe/store/actions/transactions/utils/transactionHelpers.ts +++ b/src/logic/safe/store/actions/transactions/utils/transactionHelpers.ts @@ -34,7 +34,7 @@ import { TypedDataUtils } from 'eth-sig-util' import { Token } from 'src/logic/tokens/store/model/token' import { ProviderRecord } from 'src/logic/wallets/store/model/provider' import { SafeRecord } from 'src/logic/safe/store/models/safe' -import { DecodedParams } from 'src/routes/safe/store/models/types/transactions.d' +import { DataDecoded, DecodedParams } from 'src/routes/safe/store/models/types/transactions.d' export const isEmptyData = (data?: string | null): boolean => { return !data || data === EMPTY_DATA @@ -316,6 +316,7 @@ export type TxToMock = TxArgs & { safeTxHash: string value: string submissionDate: string + dataDecoded: DataDecoded | null } export const mockTransaction = (tx: TxToMock, safeAddress: string, state: AppReduxState): Promise => { 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 0413ee4860..bf23228989 100644 --- a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/index.tsx +++ b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/index.tsx @@ -142,6 +142,7 @@ const OwnersColumn = ({ displayButtonRow = false } + // TODO: simplify this whole logic around tx status, it's getting hard to maintain and follow const showConfirmBtn = !tx.isExecuted && tx.status !== 'pending' && @@ -151,7 +152,8 @@ const OwnersColumn = ({ !currentUserAlreadyConfirmed && !thresholdReached - const showExecuteBtn = canExecute && !tx.isExecuted && thresholdReached + const showExecuteBtn = + canExecute && !tx.isExecuted && thresholdReached && tx.status !== 'pending' && cancelTx.status !== 'pending' const showRejectBtn = !cancelTx.isExecuted && @@ -163,7 +165,13 @@ const OwnersColumn = ({ !cancelThresholdReached && displayButtonRow - const showExecuteRejectBtn = !cancelTx.isExecuted && !tx.isExecuted && canExecuteCancel && cancelThresholdReached + const showExecuteRejectBtn = + !cancelTx.isExecuted && + !tx.isExecuted && + canExecuteCancel && + cancelThresholdReached && + tx.status !== 'pending' && + cancelTx.status !== 'pending' const txThreshold = cancelTx.isExecuted ? tx.confirmations.size : threshold const cancelThreshold = tx.isExecuted ? cancelTx.confirmations.size : threshold