mirror of
https://github.com/status-im/safe-react.git
synced 2025-02-19 21:18:09 +00:00
(Feature) Proper rejections (#2096)
* remove `isCancelTransaction` utility function in favor of `txInfo.isCancellation` flag provided by client-gateway * replace "cancel" concept in favor of "reject" * add circle-cross-red icon to "On-chain rejection" transaction info Adjust owner's list text color * identify queued on-chain rejection * apply styles to on-chain rejection type identifier * update awaiting messages wording * fix styles on styles to on-chain rejection * replace local svg with SRC `Icon` component wherever is possible
This commit is contained in:
parent
e48891ba2b
commit
c96e3192ff
@ -161,7 +161,7 @@
|
|||||||
"@gnosis.pm/safe-apps-sdk": "1.0.3",
|
"@gnosis.pm/safe-apps-sdk": "1.0.3",
|
||||||
"@gnosis.pm/safe-apps-sdk-v1": "npm:@gnosis.pm/safe-apps-sdk@0.4.2",
|
"@gnosis.pm/safe-apps-sdk-v1": "npm:@gnosis.pm/safe-apps-sdk@0.4.2",
|
||||||
"@gnosis.pm/safe-contracts": "1.1.1-dev.2",
|
"@gnosis.pm/safe-contracts": "1.1.1-dev.2",
|
||||||
"@gnosis.pm/safe-react-components": "https://github.com/gnosis/safe-react-components.git#a68a67e",
|
"@gnosis.pm/safe-react-components": "https://github.com/gnosis/safe-react-components.git#7ebc414",
|
||||||
"@gnosis.pm/util-contracts": "2.0.6",
|
"@gnosis.pm/util-contracts": "2.0.6",
|
||||||
"@ledgerhq/hw-transport-node-hid-singleton": "5.45.0",
|
"@ledgerhq/hw-transport-node-hid-singleton": "5.45.0",
|
||||||
"@material-ui/core": "^4.11.0",
|
"@material-ui/core": "^4.11.0",
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Text } from '@gnosis.pm/safe-react-components'
|
import { Text } from '@gnosis.pm/safe-react-components'
|
||||||
import React from 'react'
|
import React, { ReactElement } from 'react'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
|
|
||||||
const Wrapper = styled.div`
|
const Wrapper = styled.div`
|
||||||
@ -12,11 +12,9 @@ const Icon = styled.img`
|
|||||||
margin-right: 9px;
|
margin-right: 9px;
|
||||||
`
|
`
|
||||||
|
|
||||||
const CustomIconText = ({ iconUrl, text }: { iconUrl: string; text?: string }) => (
|
export const CustomIconText = ({ iconUrl, text }: { iconUrl: string; text?: string }): ReactElement => (
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
<Icon alt={text} src={iconUrl} />
|
<Icon alt={text} src={iconUrl} />
|
||||||
{text && <Text size="xl">{text}</Text>}
|
{text && <Text size="xl">{text}</Text>}
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
)
|
)
|
||||||
|
|
||||||
export default CustomIconText
|
|
||||||
|
@ -41,7 +41,7 @@ export const TransactionFailText = ({
|
|||||||
if (isExecution) {
|
if (isExecution) {
|
||||||
errorMessage =
|
errorMessage =
|
||||||
threshold && threshold > 1
|
threshold && threshold > 1
|
||||||
? `To save gas costs, cancel this transaction`
|
? `To save gas costs, reject this transaction`
|
||||||
: `To save gas costs, avoid executing the transaction.`
|
: `To save gas costs, avoid executing the transaction.`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -236,7 +236,7 @@ type MultiSigExecutionDetails = {
|
|||||||
type DetailedExecutionInfo = ModuleExecutionDetails | MultiSigExecutionDetails
|
type DetailedExecutionInfo = ModuleExecutionDetails | MultiSigExecutionDetails
|
||||||
|
|
||||||
type ExpandedTxDetails = {
|
type ExpandedTxDetails = {
|
||||||
executedAt: number
|
executedAt: number | null
|
||||||
txStatus: TransactionStatus
|
txStatus: TransactionStatus
|
||||||
txInfo: TransactionInfo
|
txInfo: TransactionInfo
|
||||||
txData: TransactionData | null
|
txData: TransactionData | null
|
||||||
|
@ -4,7 +4,7 @@ import CircularProgress from '@material-ui/core/CircularProgress'
|
|||||||
import React, { ReactElement, useContext, useRef } from 'react'
|
import React, { ReactElement, useContext, useRef } from 'react'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
|
|
||||||
import CustomIconText from 'src/components/CustomIconText'
|
import { CustomIconText } from 'src/components/CustomIconText'
|
||||||
import {
|
import {
|
||||||
isCustomTxInfo,
|
isCustomTxInfo,
|
||||||
isMultiSendTxInfo,
|
isMultiSendTxInfo,
|
||||||
@ -24,6 +24,7 @@ import { TokenTransferAmount } from './TokenTransferAmount'
|
|||||||
import { TxsInfiniteScrollContext } from './TxsInfiniteScroll'
|
import { TxsInfiniteScrollContext } from './TxsInfiniteScroll'
|
||||||
import { TxLocationContext } from './TxLocationProvider'
|
import { TxLocationContext } from './TxLocationProvider'
|
||||||
import { CalculatedVotes } from './TxQueueCollapsed'
|
import { CalculatedVotes } from './TxQueueCollapsed'
|
||||||
|
import { isCancelTxDetails } from './utils'
|
||||||
|
|
||||||
const TxInfo = ({ info }: { info: AssetInfo }) => {
|
const TxInfo = ({ info }: { info: AssetInfo }) => {
|
||||||
if (isTokenTransferAsset(info)) {
|
if (isTokenTransferAsset(info)) {
|
||||||
@ -116,6 +117,8 @@ export const TxCollapsed = ({
|
|||||||
const { ref, lastItemId } = useContext(TxsInfiniteScrollContext)
|
const { ref, lastItemId } = useContext(TxsInfiniteScrollContext)
|
||||||
|
|
||||||
const willBeReplaced = transaction?.txStatus === 'WILL_BE_REPLACED' ? ' will-be-replaced' : ''
|
const willBeReplaced = transaction?.txStatus === 'WILL_BE_REPLACED' ? ' will-be-replaced' : ''
|
||||||
|
const onChainRejection =
|
||||||
|
isCancelTxDetails(transaction.txInfo) && txLocation !== 'history' ? ' on-chain-rejection' : ''
|
||||||
|
|
||||||
const txCollapsedNonce = (
|
const txCollapsedNonce = (
|
||||||
<div className={'tx-nonce' + willBeReplaced}>
|
<div className={'tx-nonce' + willBeReplaced}>
|
||||||
@ -124,7 +127,7 @@ export const TxCollapsed = ({
|
|||||||
)
|
)
|
||||||
|
|
||||||
const txCollapsedType = (
|
const txCollapsedType = (
|
||||||
<div className={'tx-type' + willBeReplaced}>
|
<div className={'tx-type' + willBeReplaced + onChainRejection}>
|
||||||
<CustomIconText iconUrl={type.icon} text={type.text} />
|
<CustomIconText iconUrl={type.icon} text={type.text} />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -46,7 +46,7 @@ export const TxCollapsedActions = ({ transaction }: TxCollapsedActionsProps): Re
|
|||||||
</span>
|
</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
{canCancel && (
|
{canCancel && (
|
||||||
<Tooltip title="Cancel" placement="top">
|
<Tooltip title="Reject" placement="top">
|
||||||
<span>
|
<span>
|
||||||
<IconButton size="small" type="button" onClick={handleCancelButtonClick} disabled={isPending}>
|
<IconButton size="small" type="button" onClick={handleCancelButtonClick} disabled={isPending}>
|
||||||
<Icon type="circleCross" color="error" size="sm" />
|
<Icon type="circleCross" color="error" size="sm" />
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { Icon, Link, Loader, Text } from '@gnosis.pm/safe-react-components'
|
import { Icon, Link, Loader, Text } from '@gnosis.pm/safe-react-components'
|
||||||
import cn from 'classnames'
|
import cn from 'classnames'
|
||||||
import React, { ReactElement, useContext } from 'react'
|
import React, { ReactElement, useContext } from 'react'
|
||||||
import { useSelector } from 'react-redux'
|
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -12,7 +11,6 @@ import {
|
|||||||
MultiSigExecutionDetails,
|
MultiSigExecutionDetails,
|
||||||
Transaction,
|
Transaction,
|
||||||
} from 'src/logic/safe/store/models/types/gateway.d'
|
} from 'src/logic/safe/store/models/types/gateway.d'
|
||||||
import { safeParamAddressFromStateSelector } from 'src/logic/safe/store/selectors'
|
|
||||||
import { TransactionActions } from './hooks/useTransactionActions'
|
import { TransactionActions } from './hooks/useTransactionActions'
|
||||||
import { useTransactionDetails } from './hooks/useTransactionDetails'
|
import { useTransactionDetails } from './hooks/useTransactionDetails'
|
||||||
import { TxDetailsContainer, Centered, AlignItemsWithMargin } from './styled'
|
import { TxDetailsContainer, Centered, AlignItemsWithMargin } from './styled'
|
||||||
@ -30,35 +28,45 @@ const NormalBreakingText = styled(Text)`
|
|||||||
`
|
`
|
||||||
|
|
||||||
const TxDataGroup = ({ txDetails }: { txDetails: ExpandedTxDetails }): ReactElement | null => {
|
const TxDataGroup = ({ txDetails }: { txDetails: ExpandedTxDetails }): ReactElement | null => {
|
||||||
const safeAddress = useSelector(safeParamAddressFromStateSelector)
|
|
||||||
|
|
||||||
if (isTransferTxInfo(txDetails.txInfo) || isSettingsChangeTxInfo(txDetails.txInfo)) {
|
if (isTransferTxInfo(txDetails.txInfo) || isSettingsChangeTxInfo(txDetails.txInfo)) {
|
||||||
return <TxInfo txInfo={txDetails.txInfo} />
|
return <TxInfo txInfo={txDetails.txInfo} />
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isCancelTxDetails({ executedAt: txDetails.executedAt, txInfo: txDetails.txInfo, safeAddress })) {
|
if (isCancelTxDetails(txDetails.txInfo)) {
|
||||||
|
const txNonce = `${(txDetails.detailedExecutionInfo as MultiSigExecutionDetails).nonce ?? NOT_AVAILABLE}`
|
||||||
|
const isTxExecuted = txDetails.executedAt
|
||||||
|
|
||||||
|
// executed rejection transaction
|
||||||
|
let message = `This is an on-chain rejection that didn't send any funds.
|
||||||
|
This on-chain rejection replaced all transactions with nonce ${txNonce}.`
|
||||||
|
|
||||||
|
if (!isTxExecuted) {
|
||||||
|
// queued rejection transaction
|
||||||
|
message = `This is an on-chain rejection that doesn't send any funds.
|
||||||
|
Executing this on-chain rejection will replace all currently awaiting transactions with nonce ${txNonce}.`
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<NormalBreakingText size="xl">
|
<NormalBreakingText size="xl">{message}</NormalBreakingText>
|
||||||
{`This is an empty cancelling transaction that doesn't send any funds.
|
{!isTxExecuted && (
|
||||||
Executing this transaction will replace all currently awaiting transactions with nonce ${
|
<>
|
||||||
(txDetails.detailedExecutionInfo as MultiSigExecutionDetails).nonce ?? NOT_AVAILABLE
|
<br />
|
||||||
}.`}
|
|
||||||
</NormalBreakingText>
|
|
||||||
<Link
|
<Link
|
||||||
href="https://help.gnosis-safe.io/en/articles/4738501-why-do-i-need-to-pay-for-cancelling-a-transaction"
|
href="https://help.gnosis-safe.io/en/articles/4738501-why-do-i-need-to-pay-for-cancelling-a-transaction"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
title="Why do I need to pay for cancelling a transaction?"
|
title="Why do I need to pay for rejecting a transaction?"
|
||||||
>
|
>
|
||||||
<AlignItemsWithMargin>
|
<AlignItemsWithMargin>
|
||||||
<Text size="xl" as="span" color="primary">
|
<Text size="xl" as="span" color="primary">
|
||||||
Why do I need to pay for cancelling a transaction?
|
Why do I need to pay for rejecting a transaction?
|
||||||
</Text>
|
</Text>
|
||||||
<Icon size="sm" type="externalLink" color="primary" />
|
<Icon size="sm" type="externalLink" color="primary" />
|
||||||
</AlignItemsWithMargin>
|
</AlignItemsWithMargin>
|
||||||
</Link>
|
</Link>
|
||||||
</>
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,7 +124,7 @@ export const TxDetails = ({ transaction, actions }: TxDetailsProps): ReactElemen
|
|||||||
'will-be-replaced': transaction.txStatus === 'WILL_BE_REPLACED',
|
'will-be-replaced': transaction.txStatus === 'WILL_BE_REPLACED',
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<TxOwners detailedExecutionInfo={data.detailedExecutionInfo} />
|
<TxOwners txDetails={data} />
|
||||||
</div>
|
</div>
|
||||||
{!data.executedAt && txLocation !== 'history' && actions?.isUserAnOwner && (
|
{!data.executedAt && txLocation !== 'history' && actions?.isUserAnOwner && (
|
||||||
<div className={cn('tx-details-actions', { 'will-be-replaced': transaction.txStatus === 'WILL_BE_REPLACED' })}>
|
<div className={cn('tx-details-actions', { 'will-be-replaced': transaction.txStatus === 'WILL_BE_REPLACED' })}>
|
||||||
|
@ -41,7 +41,7 @@ export const TxExpandedActions = ({ transaction }: TxExpandedActionsProps): Reac
|
|||||||
</Button>
|
</Button>
|
||||||
{canCancel && (
|
{canCancel && (
|
||||||
<Button size="md" color="error" onClick={handleCancelButtonClick} className="error" disabled={isPending}>
|
<Button size="md" color="error" onClick={handleCancelButtonClick} className="error" disabled={isPending}>
|
||||||
Cancel
|
Reject
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Text } from '@gnosis.pm/safe-react-components'
|
import { Text, Icon } from '@gnosis.pm/safe-react-components'
|
||||||
import React, { ReactElement } from 'react'
|
import React, { ReactElement } from 'react'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
|
|
||||||
@ -6,32 +6,39 @@ import Img from 'src/components/layout/Img'
|
|||||||
import { ExpandedTxDetails, isModuleExecutionDetails } from 'src/logic/safe/store/models/types/gateway.d'
|
import { ExpandedTxDetails, isModuleExecutionDetails } from 'src/logic/safe/store/models/types/gateway.d'
|
||||||
import TransactionListActive from './assets/transactions-list-active.svg'
|
import TransactionListActive from './assets/transactions-list-active.svg'
|
||||||
import TransactionListInactive from './assets/transactions-list-inactive.svg'
|
import TransactionListInactive from './assets/transactions-list-inactive.svg'
|
||||||
import CheckCircleGreen from './assets/check-circle-green.svg'
|
|
||||||
import PlusCircleGreen from './assets/plus-circle-green.svg'
|
|
||||||
import { OwnerRow } from './OwnerRow'
|
import { OwnerRow } from './OwnerRow'
|
||||||
import { OwnerList, OwnerListItem } from './styled'
|
import { OwnerList, OwnerListItem } from './styled'
|
||||||
|
import { isCancelTxDetails } from './utils'
|
||||||
type TxOwnersProps = {
|
|
||||||
detailedExecutionInfo: ExpandedTxDetails['detailedExecutionInfo']
|
|
||||||
}
|
|
||||||
|
|
||||||
const StyledImg = styled(Img)`
|
const StyledImg = styled(Img)`
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
`
|
`
|
||||||
|
|
||||||
export const TxOwners = ({ detailedExecutionInfo }: TxOwnersProps): ReactElement | null => {
|
export const TxOwners = ({ txDetails }: { txDetails: ExpandedTxDetails }): ReactElement | null => {
|
||||||
|
const { txInfo, detailedExecutionInfo } = txDetails
|
||||||
|
|
||||||
if (!detailedExecutionInfo || isModuleExecutionDetails(detailedExecutionInfo)) {
|
if (!detailedExecutionInfo || isModuleExecutionDetails(detailedExecutionInfo)) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
const confirmationsNeeded = detailedExecutionInfo.confirmationsRequired - detailedExecutionInfo.confirmations.length
|
const confirmationsNeeded = detailedExecutionInfo.confirmationsRequired - detailedExecutionInfo.confirmations.length
|
||||||
|
|
||||||
return (
|
const CreationNode = isCancelTxDetails(txInfo) ? (
|
||||||
<OwnerList>
|
|
||||||
<OwnerListItem>
|
<OwnerListItem>
|
||||||
<span className="icon">
|
<span className="icon">
|
||||||
<StyledImg alt="" src={PlusCircleGreen} />
|
<Icon size="sm" type="circleCross" color="error" />
|
||||||
|
</span>
|
||||||
|
<div className="legend">
|
||||||
|
<Text color="error" size="xl" strong>
|
||||||
|
On-chain rejection created
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
</OwnerListItem>
|
||||||
|
) : (
|
||||||
|
<OwnerListItem>
|
||||||
|
<span className="icon">
|
||||||
|
<Icon size="sm" type="add" color="primary" />
|
||||||
</span>
|
</span>
|
||||||
<div className="legend">
|
<div className="legend">
|
||||||
<Text color="primary" size="xl" strong>
|
<Text color="primary" size="xl" strong>
|
||||||
@ -39,10 +46,15 @@ export const TxOwners = ({ detailedExecutionInfo }: TxOwnersProps): ReactElement
|
|||||||
</Text>
|
</Text>
|
||||||
</div>
|
</div>
|
||||||
</OwnerListItem>
|
</OwnerListItem>
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<OwnerList>
|
||||||
|
{CreationNode}
|
||||||
{detailedExecutionInfo.confirmations.map(({ signer }) => (
|
{detailedExecutionInfo.confirmations.map(({ signer }) => (
|
||||||
<OwnerListItem key={signer}>
|
<OwnerListItem key={signer}>
|
||||||
<span className="icon">
|
<span className="icon">
|
||||||
<StyledImg alt="" src={CheckCircleGreen} />
|
<Icon size="sm" type="circleCheck" color="primary" />
|
||||||
</span>
|
</span>
|
||||||
<div className="legend">
|
<div className="legend">
|
||||||
<Text color="primary" size="xl" strong>
|
<Text color="primary" size="xl" strong>
|
||||||
@ -55,7 +67,11 @@ export const TxOwners = ({ detailedExecutionInfo }: TxOwnersProps): ReactElement
|
|||||||
{confirmationsNeeded <= 0 ? (
|
{confirmationsNeeded <= 0 ? (
|
||||||
<OwnerListItem>
|
<OwnerListItem>
|
||||||
<span className="icon">
|
<span className="icon">
|
||||||
<StyledImg alt="" src={detailedExecutionInfo.executor ? CheckCircleGreen : TransactionListActive} />
|
{detailedExecutionInfo.executor ? (
|
||||||
|
<Icon type="circleCheck" size="sm" color="primary" />
|
||||||
|
) : (
|
||||||
|
<StyledImg alt="" src={TransactionListActive} />
|
||||||
|
)}
|
||||||
</span>
|
</span>
|
||||||
<div className="legend">
|
<div className="legend">
|
||||||
<Text color="primary" size="xl" strong>
|
<Text color="primary" size="xl" strong>
|
||||||
|
@ -27,7 +27,7 @@ export const TxSummary = ({ txDetails }: { txDetails: ExpandedTxDetails }): Reac
|
|||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{nonce && (
|
{nonce !== undefined && (
|
||||||
<div className="tx-nonce">
|
<div className="tx-nonce">
|
||||||
<Text size="xl" strong as="span">
|
<Text size="xl" strong as="span">
|
||||||
Nonce:{' '}
|
Nonce:{' '}
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||||
|
<g fill="none" fill-rule="evenodd">
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path d="M0 0H16V16H0z" transform="translate(-273 -201) translate(273 201)"/>
|
||||||
|
<path fill="#F02525" fill-rule="nonzero" d="M8 15c3.866 0 7-3.134 7-7s-3.134-7-7-7-7 3.134-7 7 3.134 7 7 7zm0-2c-2.761 0-5-2.239-5-5s2.239-5 5-5 5 2.239 5 5-2.239 5-5 5z" transform="translate(-273 -201) translate(273 201)"/>
|
||||||
|
<path fill="#F02525" d="M9.414 8l1.414 1.414c.391.39.391 1.024 0 1.414-.39.391-1.023.391-1.414 0L8 9.414l-1.414 1.414c-.39.391-1.024.391-1.414 0-.391-.39-.391-1.023 0-1.414L6.586 8 5.172 6.586c-.391-.39-.391-1.024 0-1.414.39-.391 1.023-.391 1.414 0L8 6.586l1.414-1.414c.39-.391 1.024-.391 1.414 0 .391.39.391 1.023 0 1.414L9.414 8z" transform="translate(-273 -201) translate(273 201)"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 936 B |
@ -7,7 +7,6 @@ import { getQueuedTransactionsByNonce } from 'src/logic/safe/store/selectors/gat
|
|||||||
import { sameAddress } from 'src/logic/wallets/ethAddresses'
|
import { sameAddress } from 'src/logic/wallets/ethAddresses'
|
||||||
import { userAccountSelector } from 'src/logic/wallets/store/selectors'
|
import { userAccountSelector } from 'src/logic/wallets/store/selectors'
|
||||||
import { TxLocationContext } from 'src/routes/safe/components/Transactions/TxList/TxLocationProvider'
|
import { TxLocationContext } from 'src/routes/safe/components/Transactions/TxList/TxLocationProvider'
|
||||||
import { isCancelTransaction } from 'src/routes/safe/components/Transactions/TxList/utils'
|
|
||||||
import { grantedSelector } from 'src/routes/safe/container/selector'
|
import { grantedSelector } from 'src/routes/safe/container/selector'
|
||||||
import { AppReduxState } from 'src/store'
|
import { AppReduxState } from 'src/store'
|
||||||
|
|
||||||
@ -60,14 +59,7 @@ export const useTransactionActions = (transaction: Transaction): TransactionActi
|
|||||||
canConfirm,
|
canConfirm,
|
||||||
canConfirmThenExecute: txLocation === 'queued.next' && canConfirm && oneToGo,
|
canConfirmThenExecute: txLocation === 'queued.next' && canConfirm && oneToGo,
|
||||||
canExecute: txLocation === 'queued.next' && thresholdReached,
|
canExecute: txLocation === 'queued.next' && thresholdReached,
|
||||||
canCancel: !transactionsByNonce.some(
|
canCancel: !transactionsByNonce.some(({ txInfo }) => isCustomTxInfo(txInfo) && txInfo.isCancellation),
|
||||||
({ txInfo }) =>
|
|
||||||
isCustomTxInfo(txInfo) &&
|
|
||||||
isCancelTransaction({
|
|
||||||
txInfo,
|
|
||||||
safeAddress,
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
isUserAnOwner,
|
isUserAnOwner,
|
||||||
oneToGo,
|
oneToGo,
|
||||||
})
|
})
|
||||||
|
@ -37,10 +37,10 @@ export const useTransactionStatus = (transaction: Transaction): TransactionStatu
|
|||||||
|
|
||||||
switch (transaction.txStatus) {
|
switch (transaction.txStatus) {
|
||||||
case 'AWAITING_CONFIRMATIONS':
|
case 'AWAITING_CONFIRMATIONS':
|
||||||
text = signaturePending(currentUser) ? 'Awaiting your confirmation' : 'Awaiting confirmations'
|
text = signaturePending(currentUser) ? 'Needs your confirmation' : 'Needs confirmations'
|
||||||
break
|
break
|
||||||
case 'AWAITING_EXECUTION':
|
case 'AWAITING_EXECUTION':
|
||||||
text = 'Awaiting execution'
|
text = 'Needs execution'
|
||||||
break
|
break
|
||||||
case 'PENDING':
|
case 'PENDING':
|
||||||
case 'PENDING_FAILED':
|
case 'PENDING_FAILED':
|
||||||
|
@ -4,10 +4,10 @@ import { useSelector } from 'react-redux'
|
|||||||
import { Transaction } from 'src/logic/safe/store/models/types/gateway.d'
|
import { Transaction } from 'src/logic/safe/store/models/types/gateway.d'
|
||||||
import { safeParamAddressFromStateSelector } from 'src/logic/safe/store/selectors'
|
import { safeParamAddressFromStateSelector } from 'src/logic/safe/store/selectors'
|
||||||
import CustomTxIcon from 'src/routes/safe/components/Transactions/TxList/assets/custom.svg'
|
import CustomTxIcon from 'src/routes/safe/components/Transactions/TxList/assets/custom.svg'
|
||||||
|
import CircleCrossRed from 'src/routes/safe/components/Transactions/TxList/assets/circle-cross-red.svg'
|
||||||
import IncomingTxIcon from 'src/routes/safe/components/Transactions/TxList/assets/incoming.svg'
|
import IncomingTxIcon from 'src/routes/safe/components/Transactions/TxList/assets/incoming.svg'
|
||||||
import OutgoingTxIcon from 'src/routes/safe/components/Transactions/TxList/assets/outgoing.svg'
|
import OutgoingTxIcon from 'src/routes/safe/components/Transactions/TxList/assets/outgoing.svg'
|
||||||
import SettingsTxIcon from 'src/routes/safe/components/Transactions/TxList/assets/settings.svg'
|
import SettingsTxIcon from 'src/routes/safe/components/Transactions/TxList/assets/settings.svg'
|
||||||
import { isCancelTransaction } from 'src/routes/safe/components/Transactions/TxList/utils'
|
|
||||||
|
|
||||||
export type TxTypeProps = {
|
export type TxTypeProps = {
|
||||||
icon: string
|
icon: string
|
||||||
@ -40,20 +40,8 @@ export const useTransactionType = (tx: Transaction): TxTypeProps => {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: isCancel
|
if (tx.txInfo.isCancellation) {
|
||||||
// there are two 'cancelling' tx identification
|
setType({ icon: CircleCrossRed, text: 'On-chain rejection' })
|
||||||
// this one is the candidate to remain when the client gateway implements
|
|
||||||
// https://github.com/gnosis/safe-client-gateway/issues/255
|
|
||||||
if (typeof tx.txInfo.isCancellation === 'boolean' && tx.txInfo.isCancellation) {
|
|
||||||
setType({ icon: CustomTxIcon, text: 'Cancelling transaction' })
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: isCancel
|
|
||||||
// remove the following condition when issue#255 is implemented
|
|
||||||
// also remove `isCancelTransaction` function
|
|
||||||
if (isCancelTransaction({ txInfo: tx.txInfo, safeAddress })) {
|
|
||||||
setType({ icon: CustomTxIcon, text: 'Cancelling transaction' })
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,6 +163,32 @@ const failedTransaction = css`
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const onChainRejection = css`
|
||||||
|
&.on-chain-rejection {
|
||||||
|
background-color: ${({ theme }) => theme.colors.errorTooltip};
|
||||||
|
border-left: 4px solid ${({ theme }) => theme.colors.error};
|
||||||
|
border-radius: 4px;
|
||||||
|
padding-left: 7px;
|
||||||
|
height: 22px;
|
||||||
|
max-width: 165px;
|
||||||
|
|
||||||
|
> div {
|
||||||
|
height: 17px;
|
||||||
|
align-items: center;
|
||||||
|
padding-top: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-size: 11px;
|
||||||
|
line-height: 16px;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
font-weight: bold;
|
||||||
|
text-transform: uppercase;
|
||||||
|
margin-left: -2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
export const StyledTransaction = styled.div`
|
export const StyledTransaction = styled.div`
|
||||||
${willBeReplaced};
|
${willBeReplaced};
|
||||||
${failedTransaction};
|
${failedTransaction};
|
||||||
@ -175,6 +201,10 @@ export const StyledTransaction = styled.div`
|
|||||||
align-self: center;
|
align-self: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tx-type {
|
||||||
|
${onChainRejection};
|
||||||
|
}
|
||||||
|
|
||||||
.tx-votes {
|
.tx-votes {
|
||||||
justify-self: center;
|
justify-self: center;
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ import { BigNumber } from 'bignumber.js'
|
|||||||
|
|
||||||
import { getNetworkInfo } from 'src/config'
|
import { getNetworkInfo } from 'src/config'
|
||||||
import {
|
import {
|
||||||
Custom,
|
|
||||||
isCustomTxInfo,
|
isCustomTxInfo,
|
||||||
isTransferTxInfo,
|
isTransferTxInfo,
|
||||||
Transaction,
|
Transaction,
|
||||||
@ -12,7 +11,6 @@ import {
|
|||||||
|
|
||||||
import { formatAmount } from 'src/logic/tokens/utils/formatAmount'
|
import { formatAmount } from 'src/logic/tokens/utils/formatAmount'
|
||||||
import { sameAddress } from 'src/logic/wallets/ethAddresses'
|
import { sameAddress } from 'src/logic/wallets/ethAddresses'
|
||||||
import { sameString } from 'src/utils/strings'
|
|
||||||
|
|
||||||
export const NOT_AVAILABLE = 'n/a'
|
export const NOT_AVAILABLE = 'n/a'
|
||||||
|
|
||||||
@ -90,27 +88,11 @@ export const getTxTokenData = (txInfo: Transfer): txTokenData => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: isCancel
|
export const isCancelTxDetails = (txInfo: Transaction['txInfo']): boolean =>
|
||||||
// how can we be sure that it's a cancel tx without asking for tx-details?
|
|
||||||
// can the client-gateway service provide info about the tx, Like: `isCancelTransaction: boolean`?
|
|
||||||
// it will be solved as part of: https://github.com/gnosis/safe-client-gateway/issues/255
|
|
||||||
export const isCancelTransaction = ({ txInfo, safeAddress }: { txInfo: Custom; safeAddress: string }): boolean =>
|
|
||||||
sameAddress(txInfo.to, safeAddress) &&
|
|
||||||
sameString(txInfo.dataSize, '0') &&
|
|
||||||
sameString(txInfo.value, '0') &&
|
|
||||||
txInfo.methodName === null
|
|
||||||
|
|
||||||
type IsCancelTxDetailsProps = {
|
|
||||||
executedAt: number | null
|
|
||||||
txInfo: Transaction['txInfo']
|
|
||||||
safeAddress: string
|
|
||||||
}
|
|
||||||
export const isCancelTxDetails = ({ executedAt, txInfo, safeAddress }: IsCancelTxDetailsProps): boolean =>
|
|
||||||
!executedAt &&
|
|
||||||
// custom transaction
|
// custom transaction
|
||||||
isCustomTxInfo(txInfo) &&
|
isCustomTxInfo(txInfo) &&
|
||||||
// verify that it's a cancel tx based on it's info
|
// flag-based identification
|
||||||
isCancelTransaction({ safeAddress, txInfo })
|
txInfo.isCancellation
|
||||||
|
|
||||||
export const addressInList = (list: string[] = []) => (address: string): boolean =>
|
export const addressInList = (list: string[] = []) => (address: string): boolean =>
|
||||||
list.some((ownerAddress) => sameAddress(ownerAddress, address))
|
list.some((ownerAddress) => sameAddress(ownerAddress, address))
|
||||||
|
@ -1596,9 +1596,9 @@
|
|||||||
solc "0.5.14"
|
solc "0.5.14"
|
||||||
truffle "^5.1.21"
|
truffle "^5.1.21"
|
||||||
|
|
||||||
"@gnosis.pm/safe-react-components@https://github.com/gnosis/safe-react-components.git#a68a67e":
|
"@gnosis.pm/safe-react-components@https://github.com/gnosis/safe-react-components.git#7ebc414":
|
||||||
version "0.5.0"
|
version "0.5.0"
|
||||||
resolved "https://github.com/gnosis/safe-react-components.git#a68a67e634d0be091856ebba9f6874eebb767cd7"
|
resolved "https://github.com/gnosis/safe-react-components.git#7ebc414ae975d60846704c5a8db5e61c30348d12"
|
||||||
dependencies:
|
dependencies:
|
||||||
classnames "^2.2.6"
|
classnames "^2.2.6"
|
||||||
react-media "^1.10.0"
|
react-media "^1.10.0"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user