@@ -124,7 +127,7 @@ export const TxCollapsed = ({
)
const txCollapsedType = (
-
+
)
diff --git a/src/routes/safe/components/Transactions/TxList/TxCollapsedActions.tsx b/src/routes/safe/components/Transactions/TxList/TxCollapsedActions.tsx
index 1e753ecd..4751cc37 100644
--- a/src/routes/safe/components/Transactions/TxList/TxCollapsedActions.tsx
+++ b/src/routes/safe/components/Transactions/TxList/TxCollapsedActions.tsx
@@ -46,7 +46,7 @@ export const TxCollapsedActions = ({ transaction }: TxCollapsedActionsProps): Re
{canCancel && (
-
+
diff --git a/src/routes/safe/components/Transactions/TxList/TxData.tsx b/src/routes/safe/components/Transactions/TxList/TxData.tsx
index d6c8dfa5..f559d721 100644
--- a/src/routes/safe/components/Transactions/TxList/TxData.tsx
+++ b/src/routes/safe/components/Transactions/TxList/TxData.tsx
@@ -1,7 +1,12 @@
import React, { ReactElement, ReactNode } from 'react'
import { getNetworkInfo } from 'src/config'
-import { ExpandedTxDetails, TransactionData } from 'src/logic/safe/store/models/types/gateway.d'
+import {
+ ExpandedTxDetails,
+ isCustomTxInfo,
+ TransactionData,
+ TransactionInfo,
+} from 'src/logic/safe/store/models/types/gateway.d'
import { fromTokenUnit } from 'src/logic/tokens/utils/humanReadableValue'
import {
DeleteSpendingLimitDetails,
@@ -20,23 +25,39 @@ const { nativeCoin } = getNetworkInfo()
type DetailsWithTxInfoProps = {
children: ReactNode
txData: TransactionData
+ txInfo: TransactionInfo
}
-const DetailsWithTxInfo = ({ children, txData }: DetailsWithTxInfoProps): ReactElement => (
- <>
-
- {children}
- >
-)
+const DetailsWithTxInfo = ({ children, txData, txInfo }: DetailsWithTxInfoProps): ReactElement => {
+ const amount = txData.value ? fromTokenUnit(txData.value, nativeCoin.decimals) : 'n/a'
+ let name
+ let avatarUrl
+
+ if (isCustomTxInfo(txInfo)) {
+ name = txInfo.toInfo.name
+ avatarUrl = txInfo.toInfo.logoUri
+ }
+
+ return (
+ <>
+
+
+ {children}
+ >
+ )
+}
type TxDataProps = {
txData: ExpandedTxDetails['txData']
+ txInfo: TransactionInfo
}
-export const TxData = ({ txData }: TxDataProps): ReactElement | null => {
+export const TxData = ({ txData, txInfo }: TxDataProps): ReactElement | null => {
// nothing to render
if (!txData) {
return null
@@ -51,7 +72,7 @@ export const TxData = ({ txData }: TxDataProps): ReactElement | null => {
// we render the hex encoded data
return (
-
+
)
@@ -74,7 +95,7 @@ export const TxData = ({ txData }: TxDataProps): ReactElement | null => {
// we render the decoded data
return (
-
+
)
diff --git a/src/routes/safe/components/Transactions/TxList/TxDetails.tsx b/src/routes/safe/components/Transactions/TxList/TxDetails.tsx
index 789afd82..fdc043ca 100644
--- a/src/routes/safe/components/Transactions/TxList/TxDetails.tsx
+++ b/src/routes/safe/components/Transactions/TxList/TxDetails.tsx
@@ -1,7 +1,6 @@
import { Icon, Link, Loader, Text } from '@gnosis.pm/safe-react-components'
import cn from 'classnames'
import React, { ReactElement, useContext } from 'react'
-import { useSelector } from 'react-redux'
import styled from 'styled-components'
import {
@@ -12,7 +11,6 @@ import {
MultiSigExecutionDetails,
Transaction,
} from 'src/logic/safe/store/models/types/gateway.d'
-import { safeParamAddressFromStateSelector } from 'src/logic/safe/store/selectors'
import { TransactionActions } from './hooks/useTransactionActions'
import { useTransactionDetails } from './hooks/useTransactionDetails'
import { TxDetailsContainer, Centered, AlignItemsWithMargin } from './styled'
@@ -30,34 +28,44 @@ const NormalBreakingText = styled(Text)`
`
const TxDataGroup = ({ txDetails }: { txDetails: ExpandedTxDetails }): ReactElement | null => {
- const safeAddress = useSelector(safeParamAddressFromStateSelector)
-
if (isTransferTxInfo(txDetails.txInfo) || isSettingsChangeTxInfo(txDetails.txInfo)) {
return
}
- 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 (
<>
-
- {`This is an empty cancelling transaction that doesn't send any funds.
- Executing this transaction will replace all currently awaiting transactions with nonce ${
- (txDetails.detailedExecutionInfo as MultiSigExecutionDetails).nonce ?? NOT_AVAILABLE
- }.`}
-
-
-
-
- Why do I need to pay for cancelling a transaction?
-
-
-
-
+ {message}
+ {!isTxExecuted && (
+ <>
+
+
+
+
+ Why do I need to pay for rejecting a transaction?
+
+
+
+
+ >
+ )}
>
)
}
@@ -66,7 +74,7 @@ const TxDataGroup = ({ txDetails }: { txDetails: ExpandedTxDetails }): ReactElem
return null
}
- return
+ return
}
type TxDetailsProps = {
@@ -116,7 +124,7 @@ export const TxDetails = ({ transaction, actions }: TxDetailsProps): ReactElemen
'will-be-replaced': transaction.txStatus === 'WILL_BE_REPLACED',
})}
>
-
+
{!data.executedAt && txLocation !== 'history' && actions?.isUserAnOwner && (
diff --git a/src/routes/safe/components/Transactions/TxList/TxExpandedActions.tsx b/src/routes/safe/components/Transactions/TxList/TxExpandedActions.tsx
index 71e0c28c..81f7d804 100644
--- a/src/routes/safe/components/Transactions/TxList/TxExpandedActions.tsx
+++ b/src/routes/safe/components/Transactions/TxList/TxExpandedActions.tsx
@@ -41,7 +41,7 @@ export const TxExpandedActions = ({ transaction }: TxExpandedActionsProps): Reac
{canCancel && (
)}
>
diff --git a/src/routes/safe/components/Transactions/TxList/TxInfo.tsx b/src/routes/safe/components/Transactions/TxList/TxInfo.tsx
index 9a485d9a..725d6780 100644
--- a/src/routes/safe/components/Transactions/TxList/TxInfo.tsx
+++ b/src/routes/safe/components/Transactions/TxList/TxInfo.tsx
@@ -1,18 +1,10 @@
import React, { ReactElement } from 'react'
-import {
- ExpandedTxDetails,
- isSettingsChangeTxInfo,
- isTransferTxInfo,
-} from 'src/logic/safe/store/models/types/gateway.d'
+import { TransactionInfo, isSettingsChangeTxInfo, isTransferTxInfo } from 'src/logic/safe/store/models/types/gateway.d'
import { TxInfoSettings } from './TxInfoSettings'
import { TxInfoTransfer } from './TxInfoTransfer'
-type TxInfoProps = {
- txInfo: ExpandedTxDetails['txInfo']
-}
-
-export const TxInfo = ({ txInfo }: TxInfoProps): ReactElement | null => {
+export const TxInfo = ({ txInfo }: { txInfo: TransactionInfo }): ReactElement | null => {
if (isSettingsChangeTxInfo(txInfo)) {
return
}
diff --git a/src/routes/safe/components/Transactions/TxList/TxInfoDetails.tsx b/src/routes/safe/components/Transactions/TxList/TxInfoDetails.tsx
index 2ef7d24a..8c0a84a0 100644
--- a/src/routes/safe/components/Transactions/TxList/TxInfoDetails.tsx
+++ b/src/routes/safe/components/Transactions/TxList/TxInfoDetails.tsx
@@ -21,11 +21,20 @@ const SingleRow = styled.div`
type TxInfoDetailsProps = {
title: string
address: string
+ name?: string | undefined
+ avatarUrl?: string | undefined
isTransferType?: boolean
txInfo?: Transfer
}
-export const TxInfoDetails = ({ title, address, isTransferType, txInfo }: TxInfoDetailsProps): ReactElement => {
+export const TxInfoDetails = ({
+ title,
+ address,
+ isTransferType,
+ txInfo,
+ name,
+ avatarUrl,
+}: TxInfoDetailsProps): ReactElement => {
const recipientName = useSelector((state) => getNameFromAddressBookSelector(state, address))
const knownAddress = recipientName !== 'UNKNOWN'
@@ -59,6 +68,7 @@ export const TxInfoDetails = ({ title, address, isTransferType, txInfo }: TxInfo
selectedToken: ZERO_ADDRESS,
tokenAmount: '0',
})
+
useEffect(() => {
if (txInfo) {
const isCollectible = txInfo.transferInfo.type === 'ERC721'
@@ -76,7 +86,7 @@ export const TxInfoDetails = ({ title, address, isTransferType, txInfo }: TxInfo
return (
-
+
{
+export const TxOwners = ({ txDetails }: { txDetails: ExpandedTxDetails }): ReactElement | null => {
+ const { txInfo, detailedExecutionInfo } = txDetails
+
if (!detailedExecutionInfo || isModuleExecutionDetails(detailedExecutionInfo)) {
return null
}
const confirmationsNeeded = detailedExecutionInfo.confirmationsRequired - detailedExecutionInfo.confirmations.length
+ const CreationNode = isCancelTxDetails(txInfo) ? (
+
+
+
+
+
+
+ On-chain rejection created
+
+
+
+ ) : (
+
+
+
+
+
+
+ Created
+
+
+
+ )
+
return (
-
-
-
-
-
-
- Created
-
-
-
+ {CreationNode}
{detailedExecutionInfo.confirmations.map(({ signer }) => (
-
+
@@ -55,7 +67,11 @@ export const TxOwners = ({ detailedExecutionInfo }: TxOwnersProps): ReactElement
{confirmationsNeeded <= 0 ? (
-
+ {detailedExecutionInfo.executor ? (
+
+ ) : (
+
+ )}
diff --git a/src/routes/safe/components/Transactions/TxList/TxSummary.tsx b/src/routes/safe/components/Transactions/TxList/TxSummary.tsx
index 84fb6cc7..4bc50b77 100644
--- a/src/routes/safe/components/Transactions/TxList/TxSummary.tsx
+++ b/src/routes/safe/components/Transactions/TxList/TxSummary.tsx
@@ -27,7 +27,7 @@ export const TxSummary = ({ txDetails }: { txDetails: ExpandedTxDetails }): Reac
)}
- {nonce && (
+ {nonce !== undefined && (
Nonce:{' '}
diff --git a/src/routes/safe/components/Transactions/TxList/assets/circle-cross-red.svg b/src/routes/safe/components/Transactions/TxList/assets/circle-cross-red.svg
new file mode 100644
index 00000000..ff8f6306
--- /dev/null
+++ b/src/routes/safe/components/Transactions/TxList/assets/circle-cross-red.svg
@@ -0,0 +1,11 @@
+
diff --git a/src/routes/safe/components/Transactions/TxList/hooks/useTransactionActions.ts b/src/routes/safe/components/Transactions/TxList/hooks/useTransactionActions.ts
index 5e3b7566..3609d6bc 100644
--- a/src/routes/safe/components/Transactions/TxList/hooks/useTransactionActions.ts
+++ b/src/routes/safe/components/Transactions/TxList/hooks/useTransactionActions.ts
@@ -7,7 +7,6 @@ import { getQueuedTransactionsByNonce } from 'src/logic/safe/store/selectors/gat
import { sameAddress } from 'src/logic/wallets/ethAddresses'
import { userAccountSelector } from 'src/logic/wallets/store/selectors'
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 { AppReduxState } from 'src/store'
@@ -60,14 +59,7 @@ export const useTransactionActions = (transaction: Transaction): TransactionActi
canConfirm,
canConfirmThenExecute: txLocation === 'queued.next' && canConfirm && oneToGo,
canExecute: txLocation === 'queued.next' && thresholdReached,
- canCancel: !transactionsByNonce.some(
- ({ txInfo }) =>
- isCustomTxInfo(txInfo) &&
- isCancelTransaction({
- txInfo,
- safeAddress,
- }),
- ),
+ canCancel: !transactionsByNonce.some(({ txInfo }) => isCustomTxInfo(txInfo) && txInfo.isCancellation),
isUserAnOwner,
oneToGo,
})
diff --git a/src/routes/safe/components/Transactions/TxList/hooks/useTransactionStatus.ts b/src/routes/safe/components/Transactions/TxList/hooks/useTransactionStatus.ts
index e64637b7..4df71af3 100644
--- a/src/routes/safe/components/Transactions/TxList/hooks/useTransactionStatus.ts
+++ b/src/routes/safe/components/Transactions/TxList/hooks/useTransactionStatus.ts
@@ -37,10 +37,10 @@ export const useTransactionStatus = (transaction: Transaction): TransactionStatu
switch (transaction.txStatus) {
case 'AWAITING_CONFIRMATIONS':
- text = signaturePending(currentUser) ? 'Awaiting your confirmation' : 'Awaiting confirmations'
+ text = signaturePending(currentUser) ? 'Needs your confirmation' : 'Needs confirmations'
break
case 'AWAITING_EXECUTION':
- text = 'Awaiting execution'
+ text = 'Needs execution'
break
case 'PENDING':
case 'PENDING_FAILED':
diff --git a/src/routes/safe/components/Transactions/TxList/hooks/useTransactionType.ts b/src/routes/safe/components/Transactions/TxList/hooks/useTransactionType.ts
index ca04efbc..8766fa38 100644
--- a/src/routes/safe/components/Transactions/TxList/hooks/useTransactionType.ts
+++ b/src/routes/safe/components/Transactions/TxList/hooks/useTransactionType.ts
@@ -4,13 +4,13 @@ import { useSelector } from 'react-redux'
import { Transaction } from 'src/logic/safe/store/models/types/gateway.d'
import { safeParamAddressFromStateSelector } from 'src/logic/safe/store/selectors'
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 OutgoingTxIcon from 'src/routes/safe/components/Transactions/TxList/assets/outgoing.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 = {
- icon: string
+ icon: string | null
text: string
}
@@ -40,20 +40,8 @@ export const useTransactionType = (tx: Transaction): TxTypeProps => {
break
}
- // TODO: isCancel
- // there are two 'cancelling' tx identification
- // 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' })
+ if (tx.txInfo.isCancellation) {
+ setType({ icon: CircleCrossRed, text: 'On-chain rejection' })
break
}
@@ -62,6 +50,12 @@ export const useTransactionType = (tx: Transaction): TxTypeProps => {
break
}
+ const toInfo = tx.txInfo.toInfo
+ if (toInfo) {
+ setType({ icon: toInfo.logoUri, text: toInfo.name })
+ break
+ }
+
setType({ icon: CustomTxIcon, text: 'Contract interaction' })
break
}
diff --git a/src/routes/safe/components/Transactions/TxList/modals/ApproveTxModal.tsx b/src/routes/safe/components/Transactions/TxList/modals/ApproveTxModal.tsx
index c8b24649..9816a7cd 100644
--- a/src/routes/safe/components/Transactions/TxList/modals/ApproveTxModal.tsx
+++ b/src/routes/safe/components/Transactions/TxList/modals/ApproveTxModal.tsx
@@ -229,6 +229,7 @@ export const ApproveTxModal = ({
const oneConfirmationLeft = !thresholdReached && _countingCurrentConfirmation === _threshold
const isTheTxReadyToBeExecuted = oneConfirmationLeft ? true : thresholdReached
const [manualGasPrice, setManualGasPrice] = useState()
+ const [manualGasLimit, setManualGasLimit] = useState()
const {
confirmations,
data,
@@ -262,7 +263,8 @@ export const ApproveTxModal = ({
preApprovingOwner: approveAndExecute ? userAddress : undefined,
safeTxGas,
operation,
- manualGasPrice: manualGasPrice,
+ manualGasPrice,
+ manualGasLimit,
})
const handleExecuteCheckbox = () => setApproveAndExecute((prevApproveAndExecute) => !prevApproveAndExecute)
@@ -312,6 +314,10 @@ export const ApproveTxModal = ({
if (newGasPrice && oldGasPrice !== newGasPrice) {
setManualGasPrice(newGasPrice.toString())
}
+
+ if (txParameters.ethGasLimit && gasLimit !== txParameters.ethGasLimit) {
+ setManualGasLimit(txParameters.ethGasLimit.toString())
+ }
}
return (
diff --git a/src/routes/safe/components/Transactions/TxList/styled.tsx b/src/routes/safe/components/Transactions/TxList/styled.tsx
index de8d7f19..9058bc80 100644
--- a/src/routes/safe/components/Transactions/TxList/styled.tsx
+++ b/src/routes/safe/components/Transactions/TxList/styled.tsx
@@ -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`
${willBeReplaced};
${failedTransaction};
@@ -175,6 +201,10 @@ export const StyledTransaction = styled.div`
align-self: center;
}
+ .tx-type {
+ ${onChainRejection};
+ }
+
.tx-votes {
justify-self: center;
}
diff --git a/src/routes/safe/components/Transactions/TxList/utils.ts b/src/routes/safe/components/Transactions/TxList/utils.ts
index d461e8cc..13b15057 100644
--- a/src/routes/safe/components/Transactions/TxList/utils.ts
+++ b/src/routes/safe/components/Transactions/TxList/utils.ts
@@ -2,7 +2,6 @@ import { BigNumber } from 'bignumber.js'
import { getNetworkInfo } from 'src/config'
import {
- Custom,
isCustomTxInfo,
isTransferTxInfo,
Transaction,
@@ -12,7 +11,6 @@ import {
import { formatAmount } from 'src/logic/tokens/utils/formatAmount'
import { sameAddress } from 'src/logic/wallets/ethAddresses'
-import { sameString } from 'src/utils/strings'
export const NOT_AVAILABLE = 'n/a'
@@ -90,27 +88,11 @@ export const getTxTokenData = (txInfo: Transfer): txTokenData => {
}
}
-// TODO: isCancel
-// 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 &&
+export const isCancelTxDetails = (txInfo: Transaction['txInfo']): boolean =>
// custom transaction
isCustomTxInfo(txInfo) &&
- // verify that it's a cancel tx based on it's info
- isCancelTransaction({ safeAddress, txInfo })
+ // flag-based identification
+ txInfo.isCancellation
export const addressInList = (list: string[] = []) => (address: string): boolean =>
list.some((ownerAddress) => sameAddress(ownerAddress, address))
diff --git a/src/routes/safe/container/hooks/useTransactionParameters.ts b/src/routes/safe/container/hooks/useTransactionParameters.ts
index 1185c765..fb7215d9 100644
--- a/src/routes/safe/container/hooks/useTransactionParameters.ts
+++ b/src/routes/safe/container/hooks/useTransactionParameters.ts
@@ -81,7 +81,7 @@ export const useTransactionParameters = (props?: Props): TxParameters => {
useEffect(() => {
const getSafeNonce = async () => {
if (safeAddress) {
- const safeInstance = await getGnosisSafeInstanceAt(safeAddress)
+ const safeInstance = getGnosisSafeInstanceAt(safeAddress)
const lastTx = await getLastTx(safeAddress)
const nonce = await getNewTxNonce(lastTx, safeInstance)
setSafeNonce(nonce)
diff --git a/src/test/safe.dom.create.tsx b/src/test/safe.dom.create.tsx
index d686009f..7c9d8475 100644
--- a/src/test/safe.dom.create.tsx
+++ b/src/test/safe.dom.create.tsx
@@ -130,7 +130,7 @@ describe('DOM > Feature > CREATE a Safe', () => {
expect(address).not.toBe(null)
expect(address).not.toBe(undefined)
- const gnosisSafe = await getGnosisSafeInstanceAt(address)
+ const gnosisSafe = getGnosisSafeInstanceAt(address)
const storedOwners = await gnosisSafe.methods.getOwners().call()
expect(storedOwners.length).toEqual(4)
const safeThreshold = await gnosisSafe.methods.getThreshold().call()
diff --git a/yarn.lock b/yarn.lock
index 56873e07..79aa2e77 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1596,9 +1596,9 @@
solc "0.5.14"
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#2e427ee":
version "0.5.0"
- resolved "https://github.com/gnosis/safe-react-components.git#a68a67e634d0be091856ebba9f6874eebb767cd7"
+ resolved "https://github.com/gnosis/safe-react-components.git#2e427ee36694c7964301fc155b0c080101a34bed"
dependencies:
classnames "^2.2.6"
react-media "^1.10.0"