Skip to content
This repository was archived by the owner on Nov 10, 2023. It is now read-only.
79 changes: 43 additions & 36 deletions src/components/ReviewInfoText/ReviewInfoText.test.tsx
Original file line number Diff line number Diff line change
@@ -1,57 +1,64 @@
import { useSelector } from 'react-redux'
import { EstimationStatus } from 'src/logic/hooks/useEstimateTransactionGas'
import { render, screen } from 'src/utils/test-utils'
import { ReviewInfoText } from './index'
import { history } from 'src/routes/routes'

const safeAddress = '0xC245cb45B044d66fbE8Fb33C26c0b28B4fc367B2'
const url = `/rin:${safeAddress}/settings/advanced`
history.location.pathname = url
jest.mock('src/logic/hooks/useRecommendedNonce', () => ({
__esModule: true,
default: () => 9,
}))

describe('<ReviewInfoText>', () => {
const initialData = {
gasCostFormatted: '0',
isExecution: true,
isCreation: false,
isCreation: true,
isOffChainSignature: false,
txEstimationExecutionStatus: EstimationStatus.SUCCESS,
}
const customState = {
safes: {
safes: {
[safeAddress]: {
address: safeAddress,
nonce: 8,
modules: null,
guard: '',
currentVersion: '1.3.0',
},
},
},
}
const testId = 'reviewInfoText-component'
const warningCommonCopy =
'will need to be created and executed before this transaction, are you sure you want to do this?'

it('Renders ReviewInfoText with safeNonce being one lastTxNonce + 1', () => {
const lastTxNonce = 10
const safeNonce = `${lastTxNonce + 1}`
it('renders ReviewInfoText with safeNonce in order', () => {
render(<ReviewInfoText {...initialData} safeNonce="9" />)

expect(screen.getByText(/You're about to create a transaction/)).toBeInTheDocument()
})

it('renders ReviewInfoText with safeNonce in order and not a creation', () => {
render(<ReviewInfoText {...initialData} safeNonce="9" isCreation={false} />)

expect(screen.getByText(/You're about to execute a transaction/)).toBeInTheDocument()
})

render(<ReviewInfoText {...initialData} safeNonce={safeNonce} testId={testId} />, customState)
it('renders ReviewInfoText that is not a creation and nonce in future', () => {
render(<ReviewInfoText {...initialData} safeNonce="100" isCreation={false} />)

expect(screen.getByTestId(testId)).toBeInTheDocument()
expect(screen.queryByText(warningCommonCopy)).not.toBeInTheDocument()
expect(screen.getByText(/You're about to execute a transaction/)).toBeInTheDocument()
expect(screen.queryByText(/will need to be created and executed before this transaction/)).not.toBeInTheDocument()
})

it('Renders ReviewInfoText with safeNonce more than one transaction ahead of lastTxNonce', () => {
const lastTxNonce = 10
const safeNonce = `${lastTxNonce + 4}`
const expectedCopy = 'transactions ' + warningCommonCopy
it('renders ReviewInfoText with a safeNonce 1 tx in the future', () => {
render(<ReviewInfoText {...initialData} safeNonce="10" />)

expect(screen.getByText(/1/)).toBeInTheDocument()
expect(
screen.getByText(/transaction will need to be created and executed before this transaction/),
).toBeInTheDocument()
})

it('renders ReviewInfoText with a safeNonce 2 txs the future', () => {
render(<ReviewInfoText {...initialData} safeNonce="11" />)

expect(screen.getByText(/2/)).toBeInTheDocument()
expect(
screen.getByText(/transactions will need to be created and executed before this transaction/),
).toBeInTheDocument()
})

render(<ReviewInfoText {...initialData} safeNonce={safeNonce} testId={testId} />, customState)
it('renders ReviewInfoText with already used safeNonce', () => {
render(<ReviewInfoText {...initialData} safeNonce="6" />)

expect(screen.getByTestId(testId)).toBeInTheDocument()
expect(screen.getByText('6')).toBeInTheDocument()
expect(screen.queryByText(expectedCopy)).toBeInTheDocument()
expect(screen.getByText(/6/)).toBeInTheDocument()
expect(screen.getByText(/9/)).toBeInTheDocument()
expect(screen.getByText(/is below the latest transaction's nonce./)).toBeInTheDocument()
expect(screen.getByText(/Your transaction might fail./)).toBeInTheDocument()
})
})
84 changes: 34 additions & 50 deletions src/components/ReviewInfoText/index.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
import styled from 'styled-components'
import { Text } from '@gnosis.pm/safe-react-components'
import { ReactElement } from 'react'
import { useSelector } from 'react-redux'
import styled from 'styled-components'

import Paragraph from 'src/components/layout/Paragraph'
import { currentSafe } from 'src/logic/safe/store/selectors'
import { getLastTxNonce } from 'src/logic/safe/store/selectors/gatewayTransactions'
import { lg } from 'src/theme/variables'
import { getRecommendedNonce } from 'src/logic/safe/api/fetchSafeTxGasEstimation'
import { extractSafeAddress } from 'src/routes/routes'
import { useEffect, useState } from 'react'
import { TransactionFailText } from '../TransactionFailText'
import { EstimationStatus } from 'src/logic/hooks/useEstimateTransactionGas'
import useRecommendedNonce from 'src/logic/hooks/useRecommendedNonce'
import { currentSafeWithNames } from 'src/logic/safe/store/selectors'

const ReviewInfoTextWrapper = styled.div`
padding: 0 ${lg};
Expand All @@ -27,54 +24,41 @@ type ReviewInfoTextProps = {
export const ReviewInfoText = ({
isCreation,
isExecution,
safeNonce: txParamsSafeNonce = '',
safeNonce = '',
testId,
txEstimationExecutionStatus,
}: ReviewInfoTextProps): React.ReactElement => {
const { nonce } = useSelector(currentSafe)
const safeNonceNumber = parseInt(txParamsSafeNonce, 10)
const lastTxNonce = useSelector(getLastTxNonce)
const storeNextNonce = `${lastTxNonce && lastTxNonce + 1}`
const safeAddress = extractSafeAddress()
const [recommendedNonce, setRecommendedNonce] = useState<string>(storeNextNonce)
const transactionAction = isCreation ? 'create' : isExecution ? 'execute' : 'approve'
}: ReviewInfoTextProps): ReactElement => {
const safeTxNonce = parseInt(safeNonce, 10)
const { address: safeAddress } = useSelector(currentSafeWithNames)
const recommendedNonce = useRecommendedNonce(safeAddress)

useEffect(() => {
const fetchRecommendedNonce = async () => {
try {
const recommendedNonce = (await getRecommendedNonce(safeAddress)).toString()
setRecommendedNonce(recommendedNonce)
} catch (e) {
return
}
}
fetchRecommendedNonce()
}, [safeAddress])
const isTxNonceOutOfOrder = () => {
// safeNonce can be undefined while waiting for the request.
if (isNaN(safeTxNonce)) return false
if (safeTxNonce === recommendedNonce) return false
return true
}

const warningMessage = () => {
const isTxNonceOutOfOrder = () => {
// safeNonce can be undefined while waiting for the request.
if (isNaN(safeNonceNumber) || safeNonceNumber === nonce) return false
if (lastTxNonce !== undefined && safeNonceNumber === lastTxNonce + 1) return false
return true
}
const shouldShowWarning = isTxNonceOutOfOrder()
if (!shouldShowWarning) return null
const getWarning = (): ReactElement | null => {
if (!isCreation) return null
if (!isTxNonceOutOfOrder()) return null

const transactionsToGo = safeTxNonce - recommendedNonce

const transactionsToGo = safeNonceNumber - nonce
return (
<Paragraph size="md" align="center" color="disabled" noMargin>
{transactionsToGo < 0 ? (
`Nonce ${txParamsSafeNonce} has already been used. Your transaction will fail. Please use nonce ${recommendedNonce}.`
{transactionsToGo > 0 ? (
/* tx in the future */ <>
<b>{transactionsToGo}</b>
&nbsp;{`transaction${transactionsToGo > 1 ? 's' : ''}`}&nbsp;will need to be created and executed before
this transaction, are you sure you want to do this?
</>
) : (
<>
<Text size="lg" as="span" color="text" strong>
{transactionsToGo}
</Text>
{` transaction${
transactionsToGo > 1 ? 's' : ''
} will need to be created and executed before this transaction,
are you sure you want to do this?`}
/* tx in the past */ <>
Nonce&nbsp;
<b>{safeTxNonce}</b>
&nbsp;is below the latest transaction&apos;s nonce. Your transaction might fail. Please use nonce&nbsp;
<b>{recommendedNonce}</b>.
</>
)}
</Paragraph>
Expand All @@ -83,11 +67,11 @@ export const ReviewInfoText = ({

return (
<ReviewInfoTextWrapper data-testid={testId}>
{warningMessage() || (
{getWarning() || (
<>
<Paragraph size="md" align="center" color="disabled" noMargin>
You&apos;re about to {transactionAction} a transaction and will have to confirm it with your currently
connected wallet.
You&apos;re about to {isCreation ? 'create' : isExecution ? 'execute' : 'approve'} a transaction and will
have to confirm it with your currently connected wallet.
</Paragraph>
</>
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
import { SafeTransactionEstimation } from '@gnosis.pm/safe-react-gateway-sdk'
import { useEffect, useState } from 'react'
import { useSelector } from 'react-redux'
import { getRecommendedNonce } from '../safe/api/fetchSafeTxGasEstimation'
import { getLastTxNonce } from '../safe/store/selectors/gatewayTransactions'
import { getRecommendedNonce } from 'src/logic/safe/api/fetchSafeTxGasEstimation'
import { getLastTxNonce } from 'src/logic/safe/store/selectors/gatewayTransactions'

type UseGetRecommendedNonce = (safeAddress: string) => number | undefined

const useGetRecommendedNonce: UseGetRecommendedNonce = (safeAddress) => {
const useRecommendedNonce = (safeAddress: string): number => {
const lastTxNonce = useSelector(getLastTxNonce)
const storeNextNonce = lastTxNonce ? lastTxNonce + 1 : undefined

const [recommendedNonce, setRecommendedNonce] = useState<number | undefined>(storeNextNonce)
const [recommendedNonce, setRecommendedNonce] = useState<number>(lastTxNonce ? lastTxNonce + 1 : 0)

useEffect(() => {
let isCurrent = true
Expand All @@ -37,4 +33,4 @@ const useGetRecommendedNonce: UseGetRecommendedNonce = (safeAddress) => {
return recommendedNonce
}

export default useGetRecommendedNonce
export default useRecommendedNonce
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import {
import useSafeTxGas from 'src/routes/safe/components/Transactions/helpers/useSafeTxGas'
import { isMaxFeeParam } from 'src/logic/safe/transactions/gas'
import { extractSafeAddress } from 'src/routes/routes'
import useGetRecommendedNonce from 'src/logic/hooks/useGetRecommendedNonce'
import useRecommendedNonce from 'src/logic/hooks/useRecommendedNonce'
import Paragraph from 'src/components/layout/Paragraph'

const StyledDivider = styled(Divider)`
Expand Down Expand Up @@ -103,7 +103,7 @@ export const EditTxParametersForm = ({
const { safeNonce, safeTxGas, ethNonce, ethGasLimit, ethGasPrice, ethMaxPrioFee } = txParameters
const showSafeTxGas = useSafeTxGas()
const safeAddress = extractSafeAddress()
const recommendedNonce = useGetRecommendedNonce(safeAddress)
const recommendedNonce = useRecommendedNonce(safeAddress)

const onSubmit = (values: TxParameters) => {
onClose(values)
Expand Down