(Fix) Executed transactions status (#1552)
Co-authored-by: Daniel Sanchez <daniel.sanchez@gnosis.pm>
This commit is contained in:
parent
c9fb7fcc10
commit
7a881537e5
|
@ -35,7 +35,7 @@ export const decodeParamsFromSafeMethod = (data: string): DataDecoded | null =>
|
|||
return {
|
||||
method: METHOD_TO_ID[methodId],
|
||||
parameters: [
|
||||
{ name: 'oldOwner', type: 'address', value: decodedParameters[1] },
|
||||
{ name: 'owner', type: 'address', value: decodedParameters[1] },
|
||||
{ name: '_threshold', type: 'uint', value: decodedParameters[2] },
|
||||
],
|
||||
}
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
import { push } from 'connected-react-router'
|
||||
import { List, Map } from 'immutable'
|
||||
import { batch } from 'react-redux'
|
||||
import semverSatisfies from 'semver/functions/satisfies'
|
||||
import { ThunkAction } from 'redux-thunk'
|
||||
|
||||
|
@ -24,10 +22,11 @@ import { providerSelector } from 'src/logic/wallets/store/selectors'
|
|||
import { SAFELIST_ADDRESS } from 'src/routes/routes'
|
||||
import enqueueSnackbar from 'src/logic/notifications/store/actions/enqueueSnackbar'
|
||||
import closeSnackbarAction from 'src/logic/notifications/store/actions/closeSnackbar'
|
||||
import { addOrUpdateCancellationTransactions } from 'src/logic/safe/store/actions/transactions/addOrUpdateCancellationTransactions'
|
||||
import { addOrUpdateTransactions } from 'src/logic/safe/store/actions/transactions/addOrUpdateTransactions'
|
||||
import { removeCancellationTransaction } from 'src/logic/safe/store/actions/transactions/removeCancellationTransaction'
|
||||
import { removeTransaction } from 'src/logic/safe/store/actions/transactions/removeTransaction'
|
||||
import {
|
||||
removeTxFromStore,
|
||||
storeSignedTx,
|
||||
storeExecutedTx,
|
||||
} from 'src/logic/safe/store/actions/transactions/pendingTransactions'
|
||||
import {
|
||||
generateSafeTxHash,
|
||||
mockTransaction,
|
||||
|
@ -35,68 +34,13 @@ import {
|
|||
} from 'src/logic/safe/store/actions/transactions/utils/transactionHelpers'
|
||||
import { getLastTx, getNewTxNonce, shouldExecuteTransaction } from 'src/logic/safe/store/actions/utils'
|
||||
import { getErrorMessage } from 'src/test/utils/ethereumErrors'
|
||||
import { makeConfirmation } from '../models/confirmation'
|
||||
import fetchTransactions from './transactions/fetchTransactions'
|
||||
import { safeTransactionsSelector } from 'src/logic/safe/store/selectors'
|
||||
import { Transaction, TransactionStatus, TxArgs } from 'src/logic/safe/store/models/types/transaction'
|
||||
import { TxArgs } from 'src/logic/safe/store/models/types/transaction'
|
||||
import { AnyAction } from 'redux'
|
||||
import { PayableTx } from 'src/types/contracts/types.d'
|
||||
import { AppReduxState } from 'src/store'
|
||||
import { Dispatch, DispatchReturn } from './types'
|
||||
|
||||
export const removeTxFromStore = (
|
||||
tx: Transaction,
|
||||
safeAddress: string,
|
||||
dispatch: Dispatch,
|
||||
state: AppReduxState,
|
||||
): void => {
|
||||
if (tx.isCancellationTx) {
|
||||
const newTxStatus = TransactionStatus.AWAITING_YOUR_CONFIRMATION
|
||||
const transactions = safeTransactionsSelector(state)
|
||||
const txsToUpdate = transactions
|
||||
.filter((transaction) => Number(transaction.nonce) === Number(tx.nonce))
|
||||
.withMutations((list) => list.map((tx) => tx.set('status', newTxStatus)))
|
||||
|
||||
batch(() => {
|
||||
dispatch(addOrUpdateTransactions({ safeAddress, transactions: txsToUpdate }))
|
||||
dispatch(removeCancellationTransaction({ safeAddress, transaction: tx }))
|
||||
})
|
||||
} else {
|
||||
dispatch(removeTransaction({ safeAddress, transaction: tx }))
|
||||
}
|
||||
}
|
||||
|
||||
export const storeTx = async (
|
||||
tx: Transaction,
|
||||
safeAddress: string,
|
||||
dispatch: Dispatch,
|
||||
state: AppReduxState,
|
||||
): Promise<void> => {
|
||||
if (tx.isCancellationTx) {
|
||||
let newTxStatus: TransactionStatus = TransactionStatus.AWAITING_YOUR_CONFIRMATION
|
||||
|
||||
if (tx.isExecuted) {
|
||||
newTxStatus = TransactionStatus.CANCELLED
|
||||
} else if (tx.status === TransactionStatus.PENDING) {
|
||||
newTxStatus = tx.status
|
||||
}
|
||||
|
||||
const transactions = safeTransactionsSelector(state)
|
||||
const txsToUpdate = transactions
|
||||
.filter((transaction) => Number(transaction.nonce) === Number(tx.nonce))
|
||||
.withMutations((list) =>
|
||||
list.map((tx) => tx.set('status', newTxStatus).set('cancelled', newTxStatus === TransactionStatus.CANCELLED)),
|
||||
)
|
||||
|
||||
batch(() => {
|
||||
dispatch(addOrUpdateCancellationTransactions({ safeAddress, transactions: Map({ [`${tx.nonce}`]: tx }) }))
|
||||
dispatch(addOrUpdateTransactions({ safeAddress, transactions: txsToUpdate }))
|
||||
})
|
||||
} else {
|
||||
dispatch(addOrUpdateTransactions({ safeAddress, transactions: List([tx]) }))
|
||||
}
|
||||
}
|
||||
|
||||
interface CreateTransactionArgs {
|
||||
navigateToTransactionsTab?: boolean
|
||||
notifiedTransaction: string
|
||||
|
@ -228,15 +172,7 @@ const createTransaction = (
|
|||
|
||||
await Promise.all([
|
||||
saveTxToHistory({ ...txArgs, txHash, origin }),
|
||||
storeTx(
|
||||
mockedTx.updateIn(
|
||||
['ownersWithPendingActions', mockedTx.isCancellationTx ? 'reject' : 'confirm'],
|
||||
(previous) => previous.push(from),
|
||||
),
|
||||
safeAddress,
|
||||
dispatch,
|
||||
state,
|
||||
),
|
||||
storeSignedTx({ transaction: mockedTx, from, isExecution, safeAddress, dispatch, state }),
|
||||
])
|
||||
dispatch(fetchTransactions(safeAddress))
|
||||
} catch (e) {
|
||||
|
@ -263,29 +199,8 @@ const createTransaction = (
|
|||
),
|
||||
)
|
||||
|
||||
const toStoreTx = isExecution
|
||||
? mockedTx.withMutations((record) => {
|
||||
record
|
||||
.set('executionTxHash', receipt.transactionHash)
|
||||
.set('executor', from)
|
||||
.set('isExecuted', true)
|
||||
.set('isSuccessful', receipt.status)
|
||||
.set('status', receipt.status ? TransactionStatus.SUCCESS : TransactionStatus.FAILED)
|
||||
})
|
||||
: mockedTx.set('status', TransactionStatus.AWAITING_CONFIRMATIONS)
|
||||
await storeExecutedTx({ transaction: mockedTx, from, safeAddress, isExecution, receipt, dispatch, state })
|
||||
|
||||
await storeTx(
|
||||
toStoreTx.withMutations((record) => {
|
||||
record
|
||||
.set('confirmations', List([makeConfirmation({ owner: from })]))
|
||||
.updateIn(['ownersWithPendingActions', toStoreTx.isCancellationTx ? 'reject' : 'confirm'], (previous) =>
|
||||
previous.pop(from),
|
||||
)
|
||||
}),
|
||||
safeAddress,
|
||||
dispatch,
|
||||
state,
|
||||
)
|
||||
dispatch(fetchTransactions(safeAddress))
|
||||
|
||||
return receipt.transactionHash
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { AnyAction } from 'redux'
|
||||
import { ThunkAction } from 'redux-thunk'
|
||||
import semverSatisfies from 'semver/functions/satisfies'
|
||||
|
||||
import { getGnosisSafeInstanceAt } from 'src/logic/contracts/safeContracts'
|
||||
|
@ -6,27 +8,41 @@ import { generateSignaturesFromTxConfirmations } from 'src/logic/safe/safeTxSign
|
|||
import { getApprovalTransaction, getExecutionTransaction, saveTxToHistory } from 'src/logic/safe/transactions'
|
||||
import { SAFE_VERSION_FOR_OFFCHAIN_SIGNATURES, tryOffchainSigning } from 'src/logic/safe/transactions/offchainSigner'
|
||||
import { getCurrentSafeVersion } from 'src/logic/safe/utils/safeVersion'
|
||||
import { EMPTY_DATA } from 'src/logic/wallets/ethTransactions'
|
||||
import { providerSelector } from 'src/logic/wallets/store/selectors'
|
||||
import enqueueSnackbar from 'src/logic/notifications/store/actions/enqueueSnackbar'
|
||||
import closeSnackbarAction from 'src/logic/notifications/store/actions/closeSnackbar'
|
||||
import fetchSafe from 'src/logic/safe/store/actions/fetchSafe'
|
||||
import fetchTransactions from 'src/logic/safe/store/actions/transactions/fetchTransactions'
|
||||
import {
|
||||
isCancelTransaction,
|
||||
mockTransaction,
|
||||
TxToMock,
|
||||
} from 'src/logic/safe/store/actions/transactions/utils/transactionHelpers'
|
||||
import { mockTransaction, TxToMock } from 'src/logic/safe/store/actions/transactions/utils/transactionHelpers'
|
||||
import { getLastTx, getNewTxNonce, shouldExecuteTransaction } from 'src/logic/safe/store/actions/utils'
|
||||
|
||||
import { AppReduxState } from 'src/store'
|
||||
import { getErrorMessage } from 'src/test/utils/ethereumErrors'
|
||||
import { storeTx } from './createTransaction'
|
||||
import { TransactionStatus } from 'src/logic/safe/store/models/types/transaction'
|
||||
import { makeConfirmation } from 'src/logic/safe/store/models/confirmation'
|
||||
import { storeExecutedTx, storeSignedTx, storeTx } from 'src/logic/safe/store/actions/transactions/pendingTransactions'
|
||||
import { Transaction } from 'src/logic/safe/store/models/types/transaction'
|
||||
|
||||
const processTransaction = ({ approveAndExecute, notifiedTransaction, safeAddress, tx, userAddress }) => async (
|
||||
dispatch,
|
||||
getState,
|
||||
) => {
|
||||
import { Dispatch, DispatchReturn } from './types'
|
||||
|
||||
interface ProcessTransactionArgs {
|
||||
approveAndExecute: boolean
|
||||
notifiedTransaction: string
|
||||
safeAddress: string
|
||||
tx: Transaction
|
||||
userAddress: string
|
||||
}
|
||||
|
||||
type ProcessTransactionAction = ThunkAction<Promise<void | string>, AppReduxState, DispatchReturn, AnyAction>
|
||||
|
||||
const processTransaction = ({
|
||||
approveAndExecute,
|
||||
notifiedTransaction,
|
||||
safeAddress,
|
||||
tx,
|
||||
userAddress,
|
||||
}: ProcessTransactionArgs): ProcessTransactionAction => async (
|
||||
dispatch: Dispatch,
|
||||
getState: () => AppReduxState,
|
||||
): Promise<DispatchReturn> => {
|
||||
const state = getState()
|
||||
|
||||
const { account: from, hardwareWallet, smartContractWallet } = providerSelector(state)
|
||||
|
@ -57,7 +73,7 @@ const processTransaction = ({ approveAndExecute, notifiedTransaction, safeAddres
|
|||
safeInstance,
|
||||
to: tx.recipient,
|
||||
valueInWei: tx.value,
|
||||
data: tx.data,
|
||||
data: tx.data ?? EMPTY_DATA,
|
||||
operation: tx.operation,
|
||||
nonce: tx.nonce,
|
||||
safeTxGas: tx.safeTxGas,
|
||||
|
@ -121,30 +137,18 @@ const processTransaction = ({ approveAndExecute, notifiedTransaction, safeAddres
|
|||
try {
|
||||
await Promise.all([
|
||||
saveTxToHistory({ ...txArgs, txHash }),
|
||||
storeTx(
|
||||
mockedTx.withMutations((record) => {
|
||||
record
|
||||
.updateIn(
|
||||
['ownersWithPendingActions', mockedTx.isCancellationTx ? 'reject' : 'confirm'],
|
||||
(previous) => previous.push(from),
|
||||
)
|
||||
.set('status', TransactionStatus.PENDING)
|
||||
}),
|
||||
safeAddress,
|
||||
dispatch,
|
||||
state,
|
||||
),
|
||||
storeSignedTx({ transaction: mockedTx, from, isExecution, safeAddress, dispatch, state }),
|
||||
])
|
||||
dispatch(fetchTransactions(safeAddress))
|
||||
} catch (e) {
|
||||
dispatch(closeSnackbarAction(pendingExecutionKey))
|
||||
await storeTx(tx, safeAddress, dispatch, state)
|
||||
await storeTx({ transaction: tx, safeAddress, dispatch, state })
|
||||
console.error(e)
|
||||
}
|
||||
})
|
||||
.on('error', (error) => {
|
||||
dispatch(closeSnackbarAction(pendingExecutionKey))
|
||||
storeTx(tx, safeAddress, dispatch, state)
|
||||
storeTx({ transaction: tx, safeAddress, dispatch, state })
|
||||
console.error('Processing transaction error: ', error)
|
||||
})
|
||||
.then(async (receipt) => {
|
||||
|
@ -160,43 +164,7 @@ const processTransaction = ({ approveAndExecute, notifiedTransaction, safeAddres
|
|||
),
|
||||
)
|
||||
|
||||
const toStoreTx = isExecution
|
||||
? mockedTx.withMutations((record) => {
|
||||
record
|
||||
.set('executionTxHash', receipt.transactionHash)
|
||||
.set('blockNumber', receipt.blockNumber)
|
||||
.set('executionDate', record.submissionDate)
|
||||
.set('executor', from)
|
||||
.set('isExecuted', true)
|
||||
.set('isSuccessful', receipt.status)
|
||||
.set(
|
||||
'status',
|
||||
receipt.status
|
||||
? isCancelTransaction(record, safeAddress)
|
||||
? TransactionStatus.CANCELLED
|
||||
: TransactionStatus.SUCCESS
|
||||
: 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)
|
||||
})
|
||||
|
||||
await storeTx(
|
||||
toStoreTx.update('confirmations', (confirmations) => {
|
||||
const index = confirmations.findIndex(({ owner }) => owner === from)
|
||||
return index === -1 ? confirmations.push(makeConfirmation({ owner: from })) : confirmations
|
||||
}),
|
||||
safeAddress,
|
||||
dispatch,
|
||||
state,
|
||||
)
|
||||
await storeExecutedTx({ transaction: mockedTx, from, safeAddress, isExecution, receipt, dispatch, state })
|
||||
|
||||
dispatch(fetchTransactions(safeAddress))
|
||||
|
||||
|
|
|
@ -0,0 +1,169 @@
|
|||
import { List, Map } from 'immutable'
|
||||
import { batch } from 'react-redux'
|
||||
import { TransactionReceipt } from 'web3-core'
|
||||
|
||||
import { addOrUpdateCancellationTransactions } from 'src/logic/safe/store/actions/transactions/addOrUpdateCancellationTransactions'
|
||||
import { addOrUpdateTransactions } from 'src/logic/safe/store/actions/transactions/addOrUpdateTransactions'
|
||||
import { removeCancellationTransaction } from 'src/logic/safe/store/actions/transactions/removeCancellationTransaction'
|
||||
import { removeTransaction } from 'src/logic/safe/store/actions/transactions/removeTransaction'
|
||||
import { Dispatch } from 'src/logic/safe/store/actions/types.d'
|
||||
import { makeConfirmation } from 'src/logic/safe/store/models/confirmation'
|
||||
import { Transaction, TransactionStatus } from 'src/logic/safe/store/models/types/transaction'
|
||||
import { safeTransactionsSelector } from 'src/logic/safe/store/selectors'
|
||||
import { sameAddress } from 'src/logic/wallets/ethAddresses'
|
||||
import { web3ReadOnly } from 'src/logic/wallets/getWeb3'
|
||||
import { AppReduxState } from 'src/store'
|
||||
|
||||
type SetPendingTransactionParams = {
|
||||
transaction: Transaction
|
||||
from: string
|
||||
}
|
||||
|
||||
const setTxStatusAsPending = ({ transaction, from }: SetPendingTransactionParams): Transaction =>
|
||||
transaction.withMutations((transaction) => {
|
||||
transaction
|
||||
// setting user as the one who has triggered the tx
|
||||
// this allows to display the owner's "pending" status
|
||||
.updateIn(['ownersWithPendingActions', transaction.isCancellationTx ? 'reject' : 'confirm'], (previous) =>
|
||||
previous.push(from),
|
||||
)
|
||||
// global transaction status
|
||||
.set('status', TransactionStatus.PENDING)
|
||||
})
|
||||
|
||||
type SetOptimisticTransactionParams = {
|
||||
transaction: Transaction
|
||||
from: string
|
||||
isExecution: boolean
|
||||
receipt: TransactionReceipt
|
||||
}
|
||||
|
||||
const updateTxBasedOnReceipt = ({
|
||||
transaction,
|
||||
from,
|
||||
isExecution,
|
||||
receipt,
|
||||
}: SetOptimisticTransactionParams): Transaction => {
|
||||
const txToStore = isExecution
|
||||
? transaction.withMutations((tx) => {
|
||||
tx.set('executionTxHash', receipt.transactionHash)
|
||||
.set('blockNumber', receipt.blockNumber)
|
||||
.set('executionDate', tx.submissionDate)
|
||||
.set('fee', web3ReadOnly.utils.toWei(`${receipt.gasUsed}`, 'gwei'))
|
||||
.set('executor', from)
|
||||
.set('isExecuted', true)
|
||||
.set('isSuccessful', receipt.status)
|
||||
.set('status', receipt.status ? TransactionStatus.SUCCESS : TransactionStatus.FAILED)
|
||||
})
|
||||
: transaction.set('status', TransactionStatus.AWAITING_CONFIRMATIONS)
|
||||
|
||||
return txToStore.withMutations((tx) => {
|
||||
const senderHasAlreadyConfirmed = tx.confirmations.findIndex(({ owner }) => sameAddress(owner, from)) !== -1
|
||||
|
||||
if (!senderHasAlreadyConfirmed) {
|
||||
// updates confirmations status
|
||||
tx.update('confirmations', (confirmations) => confirmations.push(makeConfirmation({ owner: from })))
|
||||
}
|
||||
|
||||
tx.updateIn(['ownersWithPendingActions', 'reject'], (prev) => prev.clear()).updateIn(
|
||||
['ownersWithPendingActions', 'confirm'],
|
||||
(prev) => prev.clear(),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
type StoreTxParams = {
|
||||
transaction: Transaction
|
||||
safeAddress: string
|
||||
dispatch: Dispatch
|
||||
state: AppReduxState
|
||||
}
|
||||
|
||||
export const storeTx = async ({ transaction, safeAddress, dispatch, state }: StoreTxParams): Promise<void> => {
|
||||
if (transaction.isCancellationTx) {
|
||||
// `transaction` is the Cancellation tx
|
||||
// So we need to decide the `status` for the main transaction this `transaction` is cancelling
|
||||
let status: TransactionStatus = TransactionStatus.AWAITING_YOUR_CONFIRMATION
|
||||
// `cancelled`, will become true if its corresponding Cancellation tx was successfully executed
|
||||
let cancelled = false
|
||||
|
||||
switch (transaction.status) {
|
||||
case TransactionStatus.SUCCESS:
|
||||
status = TransactionStatus.CANCELLED
|
||||
cancelled = true
|
||||
break
|
||||
case TransactionStatus.PENDING:
|
||||
status = TransactionStatus.PENDING
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
const safeTransactions = safeTransactionsSelector(state)
|
||||
|
||||
const transactions = safeTransactions.withMutations((txs) => {
|
||||
const txIndex = txs.findIndex(({ nonce }) => Number(nonce) === Number(transaction.nonce))
|
||||
txs.update(txIndex, (tx) => tx.set('status', status).set('cancelled', cancelled))
|
||||
})
|
||||
|
||||
batch(() => {
|
||||
dispatch(
|
||||
addOrUpdateCancellationTransactions({
|
||||
safeAddress,
|
||||
transactions: Map({ [`${transaction.nonce}`]: transaction }),
|
||||
}),
|
||||
)
|
||||
dispatch(addOrUpdateTransactions({ safeAddress, transactions }))
|
||||
})
|
||||
} else {
|
||||
dispatch(addOrUpdateTransactions({ safeAddress, transactions: List([transaction]) }))
|
||||
}
|
||||
}
|
||||
|
||||
type StoreSignedTxParams = StoreTxParams & {
|
||||
from: string
|
||||
isExecution: boolean
|
||||
}
|
||||
|
||||
export const storeSignedTx = ({ transaction, from, isExecution, ...rest }: StoreSignedTxParams): Promise<void> =>
|
||||
storeTx({
|
||||
transaction: isExecution ? setTxStatusAsPending({ transaction, from }) : transaction,
|
||||
...rest,
|
||||
})
|
||||
|
||||
type StoreExecParams = StoreTxParams & {
|
||||
from: string
|
||||
isExecution: boolean
|
||||
safeAddress: string
|
||||
receipt: TransactionReceipt
|
||||
}
|
||||
|
||||
export const storeExecutedTx = ({ safeAddress, dispatch, state, ...rest }: StoreExecParams): Promise<void> =>
|
||||
storeTx({
|
||||
transaction: updateTxBasedOnReceipt({ ...rest }),
|
||||
safeAddress,
|
||||
dispatch,
|
||||
state,
|
||||
})
|
||||
|
||||
export const removeTxFromStore = (
|
||||
transaction: Transaction,
|
||||
safeAddress: string,
|
||||
dispatch: Dispatch,
|
||||
state: AppReduxState,
|
||||
): void => {
|
||||
if (transaction.isCancellationTx) {
|
||||
const safeTransactions = safeTransactionsSelector(state)
|
||||
const transactions = safeTransactions.withMutations((txs) => {
|
||||
const txIndex = txs.findIndex(({ nonce }) => Number(nonce) === Number(transaction.nonce))
|
||||
txs[txIndex].set('status', TransactionStatus.AWAITING_YOUR_CONFIRMATION)
|
||||
})
|
||||
|
||||
batch(() => {
|
||||
dispatch(addOrUpdateTransactions({ safeAddress, transactions }))
|
||||
dispatch(removeCancellationTransaction({ safeAddress, transaction }))
|
||||
})
|
||||
} else {
|
||||
dispatch(removeTransaction({ safeAddress, transaction }))
|
||||
}
|
||||
}
|
|
@ -21,11 +21,12 @@ import {
|
|||
TxArgs,
|
||||
RefundParams,
|
||||
} from 'src/logic/safe/store/models/types/transaction'
|
||||
import { CANCELLATION_TRANSACTIONS_REDUCER_ID } from 'src/logic/safe/store/reducer/cancellationTransactions'
|
||||
import { SAFE_REDUCER_ID } from 'src/logic/safe/store/reducer/safe'
|
||||
import { TRANSACTIONS_REDUCER_ID } from 'src/logic/safe/store/reducer/transactions'
|
||||
import { AppReduxState, store } from 'src/store'
|
||||
import { safeSelector, safeTransactionsSelector } from 'src/logic/safe/store/selectors'
|
||||
import {
|
||||
safeSelector,
|
||||
safeTransactionsSelector,
|
||||
safeCancellationTransactionsSelector,
|
||||
} from 'src/logic/safe/store/selectors'
|
||||
import { addOrUpdateTransactions } from 'src/logic/safe/store/actions/transactions/addOrUpdateTransactions'
|
||||
import {
|
||||
BatchProcessTxsProps,
|
||||
|
@ -323,9 +324,13 @@ export type TxToMock = TxArgs & {
|
|||
|
||||
export const mockTransaction = (tx: TxToMock, safeAddress: string, state: AppReduxState): Promise<Transaction> => {
|
||||
const knownTokens: Map<string, Token> = state[TOKEN_REDUCER_ID]
|
||||
const safe: SafeRecord = state[SAFE_REDUCER_ID].getIn(['safes', safeAddress])
|
||||
const cancellationTxs = state[CANCELLATION_TRANSACTIONS_REDUCER_ID].get(safeAddress) || Map()
|
||||
const outgoingTxs = state[TRANSACTIONS_REDUCER_ID].get(safeAddress) || List()
|
||||
const safe = safeSelector(state)
|
||||
const cancellationTxs = safeCancellationTransactionsSelector(state)
|
||||
const outgoingTxs = safeTransactionsSelector(state)
|
||||
|
||||
if (!safe) {
|
||||
throw new Error('Failed to recover Safe from the store')
|
||||
}
|
||||
|
||||
return buildTx({
|
||||
cancellationTxs,
|
||||
|
|
|
@ -59,7 +59,7 @@ export type TransactionProps = {
|
|||
isCollectibleTransfer: boolean
|
||||
isExecuted: boolean
|
||||
isPending?: boolean
|
||||
isSuccessful: boolean
|
||||
isSuccessful?: boolean
|
||||
isTokenTransfer: boolean
|
||||
masterCopy: string
|
||||
modifySettingsTx: boolean
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
import CircularProgress from '@material-ui/core/CircularProgress'
|
||||
import { withStyles } from '@material-ui/core/styles'
|
||||
import * as React from 'react'
|
||||
import React, { ReactElement } from 'react'
|
||||
|
||||
import AwaitingIcon from './assets/awaiting.svg'
|
||||
import ErrorIcon from './assets/error.svg'
|
||||
import OkIcon from './assets/ok.svg'
|
||||
import { styles } from './style'
|
||||
import { useStyles } from './style'
|
||||
|
||||
import Block from 'src/components/layout/Block'
|
||||
import Img from 'src/components/layout/Img'
|
||||
|
@ -19,7 +18,7 @@ const statusToIcon = {
|
|||
awaiting_confirmations: AwaitingIcon,
|
||||
awaiting_execution: AwaitingIcon,
|
||||
pending: <CircularProgress size={14} />,
|
||||
}
|
||||
} as const
|
||||
|
||||
const statusToLabel = {
|
||||
success: 'Success',
|
||||
|
@ -29,15 +28,16 @@ const statusToLabel = {
|
|||
awaiting_confirmations: 'Awaiting confirmations',
|
||||
awaiting_execution: 'Awaiting execution',
|
||||
pending: 'Pending',
|
||||
}
|
||||
} as const
|
||||
|
||||
const statusIconStyle = {
|
||||
height: '14px',
|
||||
width: '14px',
|
||||
}
|
||||
|
||||
const Status = ({ classes, status }) => {
|
||||
const Icon = statusToIcon[status]
|
||||
const Status = ({ status }: { status: keyof typeof statusToLabel }): ReactElement => {
|
||||
const classes = useStyles()
|
||||
const Icon: typeof statusToIcon[keyof typeof statusToIcon] = statusToIcon[status]
|
||||
|
||||
return (
|
||||
<Block className={`${classes.container} ${classes[status]}`}>
|
||||
|
@ -49,4 +49,4 @@ const Status = ({ classes, status }) => {
|
|||
)
|
||||
}
|
||||
|
||||
export default withStyles(styles as any)(Status)
|
||||
export default Status
|
||||
|
|
|
@ -1,49 +1,52 @@
|
|||
import { createStyles, makeStyles } from '@material-ui/core/styles'
|
||||
import { boldFont, disabled, error, extraSmallFontSize, lg, secondary, sm } from 'src/theme/variables'
|
||||
|
||||
export const styles = () => ({
|
||||
container: {
|
||||
display: 'flex',
|
||||
fontSize: extraSmallFontSize,
|
||||
fontWeight: boldFont,
|
||||
padding: sm,
|
||||
alignItems: 'center',
|
||||
boxSizing: 'border-box',
|
||||
height: lg,
|
||||
marginTop: sm,
|
||||
marginBottom: sm,
|
||||
borderRadius: '3px',
|
||||
},
|
||||
success: {
|
||||
backgroundColor: '#A1D2CA',
|
||||
color: secondary,
|
||||
},
|
||||
cancelled: {
|
||||
backgroundColor: 'transparent',
|
||||
color: error,
|
||||
border: `1px solid ${error}`,
|
||||
},
|
||||
failed: {
|
||||
backgroundColor: 'transparent',
|
||||
color: error,
|
||||
border: `1px solid ${error}`,
|
||||
},
|
||||
awaiting_your_confirmation: {
|
||||
backgroundColor: '#d4d5d3',
|
||||
color: disabled,
|
||||
},
|
||||
awaiting_confirmations: {
|
||||
backgroundColor: '#d4d5d3',
|
||||
color: disabled,
|
||||
},
|
||||
awaiting_execution: {
|
||||
backgroundColor: '#d4d5d3',
|
||||
color: disabled,
|
||||
},
|
||||
pending: {
|
||||
backgroundColor: '#fff3e2',
|
||||
color: '#e8673c',
|
||||
},
|
||||
statusText: {
|
||||
padding: '0 7px',
|
||||
},
|
||||
})
|
||||
export const useStyles = makeStyles(
|
||||
createStyles({
|
||||
container: {
|
||||
display: 'flex',
|
||||
fontSize: extraSmallFontSize,
|
||||
fontWeight: boldFont,
|
||||
padding: sm,
|
||||
alignItems: 'center',
|
||||
boxSizing: 'border-box',
|
||||
height: lg,
|
||||
marginTop: sm,
|
||||
marginBottom: sm,
|
||||
borderRadius: '3px',
|
||||
},
|
||||
success: {
|
||||
backgroundColor: '#A1D2CA',
|
||||
color: secondary,
|
||||
},
|
||||
cancelled: {
|
||||
backgroundColor: 'transparent',
|
||||
color: error,
|
||||
border: `1px solid ${error}`,
|
||||
},
|
||||
failed: {
|
||||
backgroundColor: 'transparent',
|
||||
color: error,
|
||||
border: `1px solid ${error}`,
|
||||
},
|
||||
awaiting_your_confirmation: {
|
||||
backgroundColor: '#d4d5d3',
|
||||
color: disabled,
|
||||
},
|
||||
awaiting_confirmations: {
|
||||
backgroundColor: '#d4d5d3',
|
||||
color: disabled,
|
||||
},
|
||||
awaiting_execution: {
|
||||
backgroundColor: '#d4d5d3',
|
||||
color: disabled,
|
||||
},
|
||||
pending: {
|
||||
backgroundColor: '#fff3e2',
|
||||
color: '#e8673c',
|
||||
},
|
||||
statusText: {
|
||||
padding: '0 7px',
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue