Skip to content

Commit

Permalink
Fix: don't treat user rejections as errors (#3090)
Browse files Browse the repository at this point in the history
  • Loading branch information
jmealy authored Jan 16, 2024
1 parent 7dcfb50 commit efc21e5
Show file tree
Hide file tree
Showing 10 changed files with 86 additions and 11 deletions.
15 changes: 13 additions & 2 deletions src/components/tx-flow/flows/ExecuteBatch/ReviewBatch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,13 @@ import { hasFeature } from '@/utils/chains'
import type { PayableOverrides } from 'ethers'
import { trackEvent } from '@/services/analytics'
import { TX_EVENTS, TX_TYPES } from '@/services/analytics/events/transactions'
import { isWalletRejection } from '@/utils/wallets'
import WalletRejectionError from '@/components/tx/SignOrExecuteForm/WalletRejectionError'

export const ReviewBatch = ({ params }: { params: ExecuteBatchFlowProps }) => {
const [isSubmittable, setIsSubmittable] = useState<boolean>(true)
const [submitError, setSubmitError] = useState<Error | undefined>()
const [isRejectedByUser, setIsRejectedByUser] = useState<Boolean>(false)
const [executionMethod, setExecutionMethod] = useState(ExecutionMethod.RELAY)
const chain = useCurrentChain()
const { safe } = useSafeInfo()
Expand Down Expand Up @@ -108,15 +111,21 @@ export const ReviewBatch = ({ params }: { params: ExecuteBatchFlowProps }) => {
e.preventDefault()
setIsSubmittable(false)
setSubmitError(undefined)
setIsRejectedByUser(false)

try {
await (willRelay ? onRelay() : onExecute())
setTxFlow(undefined)
} catch (_err) {
const err = asError(_err)
logError(Errors._804, err)
if (isWalletRejection(err)) {
setIsRejectedByUser(true)
} else {
logError(Errors._804, err)
setSubmitError(err)
}

setIsSubmittable(true)
setSubmitError(err)
return
}

Expand Down Expand Up @@ -190,6 +199,8 @@ export const ReviewBatch = ({ params }: { params: ExecuteBatchFlowProps }) => {
<ErrorMessage error={submitError}>Error submitting the transaction. Please try again.</ErrorMessage>
)}

{isRejectedByUser && <WalletRejectionError />}

<div>
<Divider className={commonCss.nestedDivider} sx={{ pt: 2 }} />

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,16 @@ import { getPeriod } from '@/utils/date'
import useRecovery from '@/features/recovery/hooks/useRecovery'
import { useIsValidRecoveryExecTransactionFromModule } from '@/features/recovery/hooks/useIsValidRecoveryExecution'
import type { RecoverAccountFlowProps } from '.'
import { isWalletRejection } from '@/utils/wallets'
import WalletRejectionError from '@/components/tx/SignOrExecuteForm/WalletRejectionError'

import commonCss from '@/components/tx-flow/common/styles.module.css'

export function RecoverAccountFlowReview({ params }: { params: RecoverAccountFlowProps }): ReactElement | null {
// Form state
const [isSubmittable, setIsSubmittable] = useState<boolean>(true)
const [submitError, setSubmitError] = useState<Error | undefined>()
const [isRejectedByUser, setIsRejectedByUser] = useState<Boolean>(false)

// Hooks
const { setTxFlow } = useContext(TxModalContext)
Expand Down Expand Up @@ -73,6 +76,7 @@ export function RecoverAccountFlowReview({ params }: { params: RecoverAccountFlo

setIsSubmittable(false)
setSubmitError(undefined)
setIsRejectedByUser(false)

try {
await dispatchRecoveryProposal({
Expand All @@ -84,9 +88,13 @@ export function RecoverAccountFlowReview({ params }: { params: RecoverAccountFlo
trackEvent({ ...RECOVERY_EVENTS.SUBMIT_RECOVERY_ATTEMPT })
} catch (_err) {
const err = asError(_err)
trackError(Errors._810, err)
if (isWalletRejection(err)) {
setIsRejectedByUser(true)
} else {
trackError(Errors._804, err)
setSubmitError(err)
}
setIsSubmittable(true)
setSubmitError(err)
return
}

Expand Down Expand Up @@ -161,6 +169,8 @@ export function RecoverAccountFlowReview({ params }: { params: RecoverAccountFlo
</ErrorMessage>
)}

{isRejectedByUser && <WalletRejectionError />}

<Divider className={commonCss.nestedDivider} />

<CardActions sx={{ mt: 'var(--space-1) !important' }}>
Expand Down
3 changes: 3 additions & 0 deletions src/components/tx-flow/flows/SignMessage/SignMessage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import { SafeTxContext } from '../../SafeTxProvider'
import RiskConfirmationError from '@/components/tx/SignOrExecuteForm/RiskConfirmationError'
import { Redefine } from '@/components/tx/security/redefine'
import { TxSecurityContext } from '@/components/tx/security/shared/TxSecurityContext'
import { isWalletRejection } from '@/utils/wallets'

const createSkeletonMessage = (confirmationsRequired: number): SafeMessage => {
return {
Expand Down Expand Up @@ -100,6 +101,8 @@ const MessageDialogError = ({ isOwner, submitError }: { isOwner: boolean; submit
? 'No wallet is connected.'
: !isOwner
? "You are currently not an owner of this Safe Account and won't be able to confirm this message."
: submitError && isWalletRejection(submitError)
? 'User rejected signing.'
: submitError
? 'Error confirming the message. Please try again.'
: null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { EMPTY_DATA, ZERO_ADDRESS } from '@safe-global/safe-core-sdk/dist/src/ut
import useSafeInfo from '@/hooks/useSafeInfo'
import { Errors, logError } from '@/services/exceptions'
import ErrorMessage from '@/components/tx/ErrorMessage'
import WalletRejectionError from '@/components/tx/SignOrExecuteForm/WalletRejectionError'
import { useCurrentChain } from '@/hooks/useChains'
import { dispatchSpendingLimitTxExecution } from '@/services/tx/tx-sender'
import { getTxOptions } from '@/utils/transactions'
Expand All @@ -25,6 +26,7 @@ import TxCard from '@/components/tx-flow/common/TxCard'
import { TxModalContext } from '@/components/tx-flow'
import { type SubmitCallback } from '@/components/tx/SignOrExecuteForm'
import { TX_EVENTS, TX_TYPES } from '@/services/analytics/events/transactions'
import { isWalletRejection } from '@/utils/wallets'

export type SpendingLimitTxParams = {
safeAddress: string
Expand All @@ -46,6 +48,7 @@ const ReviewSpendingLimitTx = ({
}): ReactElement => {
const [isSubmittable, setIsSubmittable] = useState<boolean>(true)
const [submitError, setSubmitError] = useState<Error | undefined>()
const [isRejectedByUser, setIsRejectedByUser] = useState<Boolean>(false)
const { setTxFlow } = useContext(TxModalContext)
const currentChain = useCurrentChain()
const onboard = useOnboard()
Expand Down Expand Up @@ -87,6 +90,7 @@ const ReviewSpendingLimitTx = ({

setIsSubmittable(false)
setSubmitError(undefined)
setIsRejectedByUser(false)

const txOptions = getTxOptions(advancedParams, currentChain)

Expand All @@ -96,9 +100,13 @@ const ReviewSpendingLimitTx = ({
setTxFlow(undefined)
} catch (_err) {
const err = asError(_err)
logError(Errors._801, err)
if (isWalletRejection(err)) {
setIsRejectedByUser(true)
} else {
logError(Errors._801, err)
setSubmitError(err)
}
setIsSubmittable(true)
setSubmitError(err)
}

trackEvent({ ...TX_EVENTS.CREATE, label: TX_TYPES.transfer_token })
Expand Down Expand Up @@ -128,6 +136,8 @@ const ReviewSpendingLimitTx = ({
<ErrorMessage error={submitError}>Error submitting the transaction. Please try again.</ErrorMessage>
)}

{isRejectedByUser && <WalletRejectionError />}

<Typography variant="body2" color="primary.light" textAlign="center">
You&apos;re about to create a transaction and will need to confirm it with your currently connected wallet.
</Typography>
Expand Down
18 changes: 16 additions & 2 deletions src/components/tx/SignOrExecuteForm/ExecuteForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@ import { SuccessScreenFlow } from '@/components/tx-flow/flows'
import useGasLimit from '@/hooks/useGasLimit'
import AdvancedParams, { useAdvancedParams } from '../AdvancedParams'
import { asError } from '@/services/exceptions/utils'
import { isWalletRejection } from '@/utils/wallets'

import css from './styles.module.css'
import commonCss from '@/components/tx-flow/common/styles.module.css'
import { TxSecurityContext } from '../security/shared/TxSecurityContext'
import useIsSafeOwner from '@/hooks/useIsSafeOwner'
import NonOwnerError from '@/components/tx/SignOrExecuteForm/NonOwnerError'
import WalletRejectionError from '@/components/tx/SignOrExecuteForm/WalletRejectionError'

export const ExecuteForm = ({
safeTx,
Expand All @@ -53,6 +55,7 @@ export const ExecuteForm = ({
// Form state
const [isSubmittable, setIsSubmittable] = useState<boolean>(true)
const [submitError, setSubmitError] = useState<Error | undefined>()
const [isRejectedByUser, setIsRejectedByUser] = useState<Boolean>(false)

// Hooks
const currentChain = useCurrentChain()
Expand Down Expand Up @@ -88,6 +91,7 @@ export const ExecuteForm = ({

setIsSubmittable(false)
setSubmitError(undefined)
setIsRejectedByUser(false)

const txOptions = getTxOptions(advancedParams, currentChain)

Expand All @@ -96,9 +100,13 @@ export const ExecuteForm = ({
executedTxId = await executeTx(txOptions, safeTx, txId, origin, willRelay)
} catch (_err) {
const err = asError(_err)
trackError(Errors._804, err)
if (isWalletRejection(err)) {
setIsRejectedByUser(true)
} else {
trackError(Errors._804, err)
setSubmitError(err)
}
setIsSubmittable(true)
setSubmitError(err)
return
}

Expand Down Expand Up @@ -170,6 +178,12 @@ export const ExecuteForm = ({
</Box>
)}

{isRejectedByUser && (
<Box mt={1}>
<WalletRejectionError />
</Box>
)}

<Divider className={commonCss.nestedDivider} sx={{ pt: 3 }} />

<CardActions>
Expand Down
20 changes: 17 additions & 3 deletions src/components/tx/SignOrExecuteForm/SignForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ import { useAlreadySigned, useTxActions } from './hooks'
import type { SignOrExecuteProps } from '.'
import type { SafeTransaction } from '@safe-global/safe-core-sdk-types'
import { TxModalContext } from '@/components/tx-flow'
import { asError } from '@/services/exceptions/utils'
import commonCss from '@/components/tx-flow/common/styles.module.css'
import { TxSecurityContext } from '../security/shared/TxSecurityContext'
import NonOwnerError from '@/components/tx/SignOrExecuteForm/NonOwnerError'
import WalletRejectionError from '@/components/tx/SignOrExecuteForm/WalletRejectionError'
import BatchButton from './BatchButton'
import { asError } from '@/services/exceptions/utils'
import { isWalletRejection } from '@/utils/wallets'

export const SignForm = ({
safeTx,
Expand All @@ -37,6 +39,7 @@ export const SignForm = ({
// Form state
const [isSubmittable, setIsSubmittable] = useState<boolean>(true)
const [submitError, setSubmitError] = useState<Error | undefined>()
const [isRejectedByUser, setIsRejectedByUser] = useState<Boolean>(false)

// Hooks
const { signTx, addToBatch } = txActions
Expand All @@ -57,15 +60,20 @@ export const SignForm = ({

setIsSubmittable(false)
setSubmitError(undefined)
setIsRejectedByUser(false)

let resultTxId: string
try {
resultTxId = await (isAddingToBatch ? addToBatch(safeTx, origin) : signTx(safeTx, txId, origin))
} catch (_err) {
const err = asError(_err)
trackError(Errors._805, err)
if (isWalletRejection(err)) {
setIsRejectedByUser(true)
} else {
trackError(Errors._804, err)
setSubmitError(err)
}
setIsSubmittable(true)
setSubmitError(err)
return
}

Expand Down Expand Up @@ -97,6 +105,12 @@ export const SignForm = ({
)
)}

{isRejectedByUser && (
<Box mt={1}>
<WalletRejectionError />
</Box>
)}

<Divider className={commonCss.nestedDivider} sx={{ pt: 3 }} />

<CardActions>
Expand Down
7 changes: 7 additions & 0 deletions src/components/tx/SignOrExecuteForm/WalletRejectionError.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import ErrorMessage from '@/components/tx/ErrorMessage'

const WalletRejectionError = () => {
return <ErrorMessage>User rejected signing.</ErrorMessage>
}

export default WalletRejectionError
2 changes: 2 additions & 0 deletions src/features/recovery/hooks/useRecoveryTxNotification.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import useSafeAddress from '../../../hooks/useSafeAddress'
import { RecoveryEvent, RecoveryTxType, recoverySubscribe } from '@/features/recovery/services/recoveryEvents'
import { getExplorerLink } from '@/utils/gateway'
import { useCurrentChain } from '../../../hooks/useChains'
import { isWalletRejection } from '@/utils/wallets'

const SUCCESS_EVENTS = [
RecoveryEvent.PROCESSING_BY_SMART_CONTRACT_WALLET,
Expand Down Expand Up @@ -49,6 +50,7 @@ export function useRecoveryTxNotifications(): void {
recoverySubscribe(event, async (detail) => {
const isSuccess = SUCCESS_EVENTS.includes(event)
const isError = 'error' in detail
if (isError && isWalletRejection(detail.error)) return

const txHash = 'txHash' in detail ? detail.txHash : undefined
const recoveryTxHash = 'recoveryTxHash' in detail ? detail.recoveryTxHash : undefined
Expand Down
2 changes: 2 additions & 0 deletions src/hooks/messages/useSafeMessageNotifications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import useWallet from '@/hooks/wallets/useWallet'
import { useCurrentChain } from '@/hooks/useChains'
import useSafeAddress from '@/hooks/useSafeAddress'
import type { PendingSafeMessagesState } from '@/store/pendingSafeMessagesSlice'
import { isWalletRejection } from '@/utils/wallets'

const SafeMessageNotifications: Partial<Record<SafeMsgEvent, string>> = {
[SafeMsgEvent.PROPOSE]: 'You successfully signed the message.',
Expand Down Expand Up @@ -50,6 +51,7 @@ const useSafeMessageNotifications = () => {
const unsubFns = entries.map(([event, baseMessage]) =>
safeMsgSubscribe(event, (detail) => {
const isError = 'error' in detail
if (isError && isWalletRejection(detail.error)) return
const isSuccess = event === SafeMsgEvent.PROPOSE || event === SafeMsgEvent.SIGNATURE_PREPARED
const message = isError ? `${baseMessage}${formatError(detail.error)}` : baseMessage

Expand Down
2 changes: 2 additions & 0 deletions src/hooks/useTxNotifications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import useWallet from './wallets/useWallet'
import useSafeAddress from './useSafeAddress'
import { getExplorerLink } from '@/utils/gateway'
import { getTxDetails } from '@/services/tx/txDetails'
import { isWalletRejection } from '@/utils/wallets'

const TxNotifications = {
[TxEvent.SIGN_FAILED]: 'Failed to sign. Please try again.',
Expand Down Expand Up @@ -72,6 +73,7 @@ const useTxNotifications = (): void => {
const unsubFns = entries.map(([event, baseMessage]) =>
txSubscribe(event, async (detail) => {
const isError = 'error' in detail
if (isError && isWalletRejection(detail.error)) return
const isSuccess = successEvents.includes(event)
const message = isError ? `${baseMessage} ${formatError(detail.error)}` : baseMessage
const txId = 'txId' in detail ? detail.txId : undefined
Expand Down

0 comments on commit efc21e5

Please sign in to comment.