(Fix) Pending transaction amount set to zero (#1316)

* fix: prevent runtime error when prev is `undefined`

* fix: prevent runtime error when prev is `undefined`

* fix: prevent runtime error when 'safes' is `undefined`

* fix: add `dataDecoded` to the mocked tx so information is properly displayed for known methods

* fix: set 'pending' status for tx being processed

- given that the confirmations key is no longer an empty array, tx status must be explicitly set this way

* fix: properly update mockedTx

* fix: hide buttons when tx is pending

* fix: type error

* Rollback patches trying to fix bug

Co-authored-by: Daniel Sanchez <daniel.sanchez@gnosis.pm>
Co-authored-by: Mikhail Mikheev <mmvsha73@gmail.com>
This commit is contained in:
Fernando 2020-09-14 08:46:34 -03:00 committed by GitHub
parent 5b99ceaa6d
commit ac92f49c72
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 37 additions and 19 deletions

View File

@ -5,6 +5,7 @@ import semverSatisfies from 'semver/functions/satisfies'
import { ThunkAction } from 'redux-thunk' import { ThunkAction } from 'redux-thunk'
import { onboardUser } from 'src/components/ConnectButton' import { onboardUser } from 'src/components/ConnectButton'
import { decodeMethods } from 'src/logic/contracts/methodIds'
import { getGnosisSafeInstanceAt } from 'src/logic/contracts/safeContracts' import { getGnosisSafeInstanceAt } from 'src/logic/contracts/safeContracts'
import { getNotificationsFromTxType } from 'src/logic/notifications' import { getNotificationsFromTxType } from 'src/logic/notifications'
import { import {
@ -205,6 +206,7 @@ const createTransaction = (
confirmations: [], // this is used to determine if a tx is pending or not. See `calculateTransactionStatus` helper confirmations: [], // this is used to determine if a tx is pending or not. See `calculateTransactionStatus` helper
value: txArgs.valueInWei, value: txArgs.valueInWei,
safeTxHash, safeTxHash,
dataDecoded: decodeMethods(txArgs.data),
submissionDate: new Date().toISOString(), submissionDate: new Date().toISOString(),
} }
const mockedTx = await mockTransaction(txToMock, safeAddress, state) const mockedTx = await mockTransaction(txToMock, safeAddress, state)

View File

@ -1,4 +1,3 @@
import { fromJS } from 'immutable'
import semverSatisfies from 'semver/functions/satisfies' import semverSatisfies from 'semver/functions/satisfies'
import { getGnosisSafeInstanceAt } from 'src/logic/contracts/safeContracts' import { getGnosisSafeInstanceAt } from 'src/logic/contracts/safeContracts'
@ -20,9 +19,9 @@ import {
import { getLastTx, getNewTxNonce, shouldExecuteTransaction } from 'src/logic/safe/store/actions/utils' import { getLastTx, getNewTxNonce, shouldExecuteTransaction } from 'src/logic/safe/store/actions/utils'
import { getErrorMessage } from 'src/test/utils/ethereumErrors' import { getErrorMessage } from 'src/test/utils/ethereumErrors'
import { makeConfirmation } from '../models/confirmation'
import { storeTx } from './createTransaction' 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 ( const processTransaction = ({ approveAndExecute, notifiedTransaction, safeAddress, tx, userAddress }) => async (
dispatch, dispatch,
@ -85,6 +84,8 @@ const processTransaction = ({ approveAndExecute, notifiedTransaction, safeAddres
dispatch(closeSnackbarAction(beforeExecutionKey)) dispatch(closeSnackbarAction(beforeExecutionKey))
await saveTxToHistory({ ...txArgs, signature }) 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(enqueueSnackbar(notificationsQueue.afterExecution.moreConfirmationsNeeded))
dispatch(fetchTransactions(safeAddress)) dispatch(fetchTransactions(safeAddress))
@ -105,9 +106,7 @@ const processTransaction = ({ approveAndExecute, notifiedTransaction, safeAddres
const txToMock: TxToMock = { const txToMock: TxToMock = {
...txArgs, ...txArgs,
confirmations: txArgs.confirmations, // this is used to determine if a tx is pending or not. See `calculateTransactionStatus` helper
value: txArgs.valueInWei, value: txArgs.valueInWei,
submissionDate: txArgs.submissionDate,
} }
const mockedTx = await mockTransaction(txToMock, safeAddress, state) const mockedTx = await mockTransaction(txToMock, safeAddress, state)
@ -123,10 +122,14 @@ const processTransaction = ({ approveAndExecute, notifiedTransaction, safeAddres
await Promise.all([ await Promise.all([
saveTxToHistory({ ...txArgs, txHash }), saveTxToHistory({ ...txArgs, txHash }),
storeTx( storeTx(
mockedTx.updateIn( mockedTx.withMutations((record) => {
['ownersWithPendingActions', mockedTx.isCancellationTx ? 'reject' : 'confirm'], record
(previous) => previous.push(from), .updateIn(
), ['ownersWithPendingActions', mockedTx.isCancellationTx ? 'reject' : 'confirm'],
(previous) => previous.push(from),
)
.set('status', TransactionStatus.PENDING)
}),
safeAddress, safeAddress,
dispatch, dispatch,
state, state,
@ -175,16 +178,20 @@ const processTransaction = ({ approveAndExecute, notifiedTransaction, safeAddres
: TransactionStatus.FAILED, : TransactionStatus.FAILED,
) )
.updateIn(['ownersWithPendingActions', 'reject'], (prev) => prev.clear()) .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( await storeTx(
toStoreTx.withMutations((record) => { toStoreTx.update('confirmations', (confirmations) => {
record const index = confirmations.findIndex(({ owner }) => owner === from)
.set('confirmations', fromJS([...tx.confirmations, makeConfirmation({ owner: from })])) return index === -1 ? confirmations.push(makeConfirmation({ owner: from })) : confirmations
.updateIn(['ownersWithPendingActions', toStoreTx.isCancellationTx ? 'reject' : 'confirm'], (previous) =>
previous.pop(from),
)
}), }),
safeAddress, safeAddress,
dispatch, dispatch,

View File

@ -34,7 +34,7 @@ import { TypedDataUtils } from 'eth-sig-util'
import { Token } from 'src/logic/tokens/store/model/token' import { Token } from 'src/logic/tokens/store/model/token'
import { ProviderRecord } from 'src/logic/wallets/store/model/provider' import { ProviderRecord } from 'src/logic/wallets/store/model/provider'
import { SafeRecord } from 'src/logic/safe/store/models/safe' 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 => { export const isEmptyData = (data?: string | null): boolean => {
return !data || data === EMPTY_DATA return !data || data === EMPTY_DATA
@ -316,6 +316,7 @@ export type TxToMock = TxArgs & {
safeTxHash: string safeTxHash: string
value: string value: string
submissionDate: string submissionDate: string
dataDecoded: DataDecoded | null
} }
export const mockTransaction = (tx: TxToMock, safeAddress: string, state: AppReduxState): Promise<Transaction> => { export const mockTransaction = (tx: TxToMock, safeAddress: string, state: AppReduxState): Promise<Transaction> => {

View File

@ -142,6 +142,7 @@ const OwnersColumn = ({
displayButtonRow = false displayButtonRow = false
} }
// TODO: simplify this whole logic around tx status, it's getting hard to maintain and follow
const showConfirmBtn = const showConfirmBtn =
!tx.isExecuted && !tx.isExecuted &&
tx.status !== 'pending' && tx.status !== 'pending' &&
@ -151,7 +152,8 @@ const OwnersColumn = ({
!currentUserAlreadyConfirmed && !currentUserAlreadyConfirmed &&
!thresholdReached !thresholdReached
const showExecuteBtn = canExecute && !tx.isExecuted && thresholdReached const showExecuteBtn =
canExecute && !tx.isExecuted && thresholdReached && tx.status !== 'pending' && cancelTx.status !== 'pending'
const showRejectBtn = const showRejectBtn =
!cancelTx.isExecuted && !cancelTx.isExecuted &&
@ -163,7 +165,13 @@ const OwnersColumn = ({
!cancelThresholdReached && !cancelThresholdReached &&
displayButtonRow 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 txThreshold = cancelTx.isExecuted ? tx.confirmations.size : threshold
const cancelThreshold = tx.isExecuted ? cancelTx.confirmations.size : threshold const cancelThreshold = tx.isExecuted ? cancelTx.confirmations.size : threshold