Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Show error notifications in safe creation #1135

Merged
merged 2 commits into from
Nov 14, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/components/new-safe/steps/Step3/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,8 @@ const CreateSafeStep3 = ({ data, onSubmit, onBack, setStep }: StepRenderProps<Ne
</Box>
}
/>
<Grid xs={3} />
<Grid xs={9} pt={1} pl={3}>
<Grid item xs={3} />
<Grid item xs={9} pt={1} pl={3}>
<Typography variant="body2" color="text.secondary">
You will have to confirm a transaction with your currently connected wallet.
</Typography>
Expand Down
27 changes: 26 additions & 1 deletion src/components/new-safe/steps/Step4/logic/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ import type { UrlObject } from 'url'
import chains from '@/config/chains'
import { AppRoutes } from '@/config/routes'
import { SAFE_APPS_EVENTS, trackEvent } from '@/services/analytics'
import type { AppDispatch, AppThunk } from '@/store'
import { showNotification } from '@/store/notificationsSlice'
import { formatError } from '@/hooks/useTxNotifications'

/**
* Prepare data for creating a Safe for the Core SDK
Expand Down Expand Up @@ -180,10 +183,24 @@ export const handleSafeCreationError = (error: EthersError) => {
return SafeCreationStatus.TIMEOUT
}

export const showSafeCreationError = (error: EthersError): AppThunk => {
return (dispatch) => {
dispatch(
showNotification({
message: `Your transaction was unsuccessful. Reason: ${formatError(error)}`,
detailedMessage: error.message,
groupKey: 'create-safe-error',
variant: 'error',
}),
)
}
}

export const checkSafeCreationTx = async (
provider: JsonRpcProvider,
pendingTx: PendingSafeTx,
txHash: string,
dispatch: AppDispatch,
): Promise<SafeCreationStatus> => {
const TIMEOUT_TIME = 6.5 * 60 * 1000 // 6.5 minutes

Expand All @@ -196,7 +213,15 @@ export const checkSafeCreationTx = async (

return SafeCreationStatus.SUCCESS
} catch (err) {
return handleSafeCreationError(err as EthersError)
const _err = err as EthersError

const status = handleSafeCreationError(_err)

if (status !== SafeCreationStatus.SUCCESS) {
dispatch(showSafeCreationError(_err))
}

return status
}
}

Expand Down
20 changes: 16 additions & 4 deletions src/components/new-safe/steps/Step4/useSafeCreation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ import {
getSafeCreationTxInfo,
getSafeDeployProps,
handleSafeCreationError,
showSafeCreationError,
} from '@/components/new-safe/steps/Step4/logic'
import { useAppDispatch } from '@/store'
import { closeAllNotifications } from '@/store/notificationsSlice'

export enum SafeCreationStatus {
AWAITING,
Expand All @@ -34,6 +37,7 @@ export const useSafeCreation = (
) => {
const [isCreating, setIsCreating] = useState(false)
const [isWatching, setIsWatching] = useState(false)
const dispatch = useAppDispatch()

const wallet = useWallet()
const provider = useWeb3()
Expand All @@ -50,6 +54,7 @@ export const useSafeCreation = (
if (!pendingSafe || !provider || !chain || !wallet || isCreating) return

setIsCreating(true)
dispatch(closeAllNotifications())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These notifications share the same groupKey and will close each other if they "overflow". I don't think we should programmatically close them like this.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, we only show notifications for errors. This adds a bit of confusion imo when retrying the tx and the error notification is still being displayed. In the past we looked into duplicating the TxEvents flows for safe creation i.e. showing notifications for signing etc. but decided against it as we wanted to use inline messages. Might be worth reconsidering but I don't want to blow up this PR.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense. I would adjust closeAllNotifications to closeByGroupKey to prevent the closing of unrelated notifcations though.


try {
const tx = await getSafeCreationTxInfo(provider, pendingSafe, chain, pendingSafe.saltNonce, wallet)
Expand All @@ -66,22 +71,29 @@ export const useSafeCreation = (

await createNewSafe(provider, safeParams)
} catch (err) {
setStatus(handleSafeCreationError(err as EthersError))
const _err = err as EthersError
const status = handleSafeCreationError(_err)

setStatus(status)

if (status !== SafeCreationStatus.SUCCESS) {
dispatch(showSafeCreationError(_err))
}
}

setIsCreating(false)
}, [chain, createSafeCallback, isCreating, pendingSafe, provider, setStatus, wallet])
}, [chain, createSafeCallback, dispatch, isCreating, pendingSafe, provider, setStatus, wallet])

const watchSafeTx = useCallback(async () => {
if (!pendingSafe?.tx || !pendingSafe?.txHash || !provider || isWatching) return

setStatus(SafeCreationStatus.PROCESSING)
setIsWatching(true)

const txStatus = await checkSafeCreationTx(provider, pendingSafe.tx, pendingSafe.txHash)
const txStatus = await checkSafeCreationTx(provider, pendingSafe.tx, pendingSafe.txHash, dispatch)
setStatus(txStatus)
setIsWatching(false)
}, [isWatching, pendingSafe, provider, setStatus])
}, [isWatching, pendingSafe, provider, setStatus, dispatch])

useEffect(() => {
if (status !== SafeCreationStatus.AWAITING) return
Expand Down
3 changes: 2 additions & 1 deletion src/components/welcome/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ const NewSafe = () => {
for creating your new Safe.
</Typography>
<Track {...CREATE_SAFE_EVENTS.CREATE_BUTTON}>
<Button variant="contained" onClick={() => router.push('/open')}>
{/* TODO: Revert this to /open before merging into dev */}
<Button variant="contained" onClick={() => router.push('/new-safe/create')}>
+ Create new Safe
</Button>
</Track>
Expand Down
2 changes: 1 addition & 1 deletion src/hooks/useTxNotifications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ enum Variant {
}

// Format the error message
const formatError = (error: Error & { reason?: string }): string => {
export const formatError = (error: Error & { reason?: string }): string => {
let { reason } = error
if (!reason) return ''
if (!reason.endsWith('.')) reason += '.'
Expand Down
14 changes: 12 additions & 2 deletions src/store/notificationsSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ export const notificationsSlice = createSlice({
return notification.id === payload.id ? { ...notification, isDismissed: true } : notification
})
},
closeAllNotifications: (state): NotificationState => {
return state.map((notification) => {
return { ...notification, isDismissed: true }
})
},
deleteNotification: (state, { payload }: PayloadAction<Notification>) => {
return state.filter((notification) => notification.id !== payload.id)
},
Expand All @@ -45,8 +50,13 @@ export const notificationsSlice = createSlice({
},
})

export const { closeNotification, deleteNotification, deleteAllNotifications, readNotification } =
notificationsSlice.actions
export const {
closeNotification,
closeAllNotifications,
deleteNotification,
deleteAllNotifications,
readNotification,
} = notificationsSlice.actions

export const showNotification = (payload: Omit<Notification, 'id' | 'timestamp'>): AppThunk<string> => {
return (dispatch) => {
Expand Down