Skip to content

Commit

Permalink
[Multichain] Feat: disable adding new chains to safes created from ol…
Browse files Browse the repository at this point in the history
…d mastercopy versions [SW-167] (#4161)



Co-authored-by: schmanu <[email protected]>
  • Loading branch information
jmealy and schmanu committed Sep 17, 2024
1 parent b0d17ea commit 4f5e9bf
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 112 deletions.
33 changes: 27 additions & 6 deletions src/components/common/NetworkSelector/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type { SelectChangeEvent } from '@mui/material'
import {
Box,
ButtonBase,
CircularProgress,
Collapse,
Divider,
ListSubheader,
Expand All @@ -32,7 +33,7 @@ import useSafeAddress from '@/hooks/useSafeAddress'
import { sameAddress } from '@/utils/addresses'
import uniq from 'lodash/uniq'
import useSafeOverviews from '@/components/welcome/MyAccounts/useSafeOverviews'
import { useReplayableNetworks } from '@/features/multichain/hooks/useReplayableNetworks'
import { useCompatibleNetworks } from '@/features/multichain/hooks/useCompatibleNetworks'
import { useSafeCreationData } from '@/features/multichain/hooks/useSafeCreationData'
import { type ChainInfo } from '@safe-global/safe-gateway-typescript-sdk'
import PlusIcon from '@/public/images/common/plus.svg'
Expand Down Expand Up @@ -127,9 +128,15 @@ const UndeployedNetworks = ({
[chains, deployedChains],
)
const safeCreationResult = useSafeCreationData(safeAddress, deployedChainInfos)
const [safeCreationData, safeCreationDataError] = safeCreationResult
const [safeCreationData, safeCreationDataError, safeCreationLoading] = safeCreationResult

const availableNetworks = useReplayableNetworks(safeCreationData, deployedChains)
const allCompatibleChains = useCompatibleNetworks(safeCreationData)
const isUnsupportedSafeCreationVersion = Boolean(!allCompatibleChains?.length)

const availableNetworks = useMemo(
() => allCompatibleChains?.filter((config) => !deployedChains.includes(config.chainId)) || [],
[allCompatibleChains, deployedChains],
)

const [testNets, prodNets] = useMemo(
() => partition(availableNetworks, (config) => config.isTestnet),
Expand All @@ -140,11 +147,25 @@ const UndeployedNetworks = ({
setReplayOnChain(chain)
}

if (safeCreationDataError) {
if (safeCreationLoading) {
return (
<Box display="flex" alignItems="center" justifyContent="center" my={1}>
<CircularProgress size={18} />
</Box>
)
}

const errorMessage = safeCreationDataError
? 'Adding another network is not possible for this Safe.'
: isUnsupportedSafeCreationVersion
? 'This account was created from an outdated mastercopy. Adding another network is not possible.'
: ''

if (errorMessage) {
return (
<Box p="0px 16px">
<Typography color="text.secondary" fontSize="14px">
Adding another network is not possible for this Safe
<Typography color="text.secondary" fontSize="14px" maxWidth={300}>
{errorMessage}
</Typography>
</Box>
)
Expand Down
87 changes: 58 additions & 29 deletions src/features/multichain/components/CreateSafeOnNewChain/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,19 @@ import NameInput from '@/components/common/NameInput'
import NetworkInput from '@/components/common/NetworkInput'
import ErrorMessage from '@/components/tx/ErrorMessage'
import {
Box,
Button,
CircularProgress,
Dialog,
DialogActions,
DialogContent,
DialogTitle,
Divider,
Stack,
Typography,
} from '@mui/material'
import { FormProvider, useForm } from 'react-hook-form'
import { useSafeCreationData } from '../../hooks/useSafeCreationData'
import { useReplayableNetworks } from '../../hooks/useReplayableNetworks'
import { replayCounterfactualSafeDeployment } from '@/features/counterfactual/utils'

import useChains from '@/hooks/useChains'
Expand All @@ -22,10 +23,12 @@ import { selectRpc } from '@/store/settingsSlice'
import { createWeb3ReadOnly } from '@/hooks/wallets/web3'
import { predictAddressBasedOnReplayData } from '@/components/welcome/MyAccounts/utils/multiChainSafe'
import { sameAddress } from '@/utils/addresses'
import ExternalLink from '@/components/common/ExternalLink'
import { useRouter } from 'next/router'
import ChainIndicator from '@/components/common/ChainIndicator'
import { type ChainInfo } from '@safe-global/safe-gateway-typescript-sdk'
import { useMemo, useState } from 'react'
import { useCompatibleNetworks } from '../../hooks/useCompatibleNetworks'

type CreateSafeOnNewChainForm = {
name: string
Expand All @@ -35,11 +38,12 @@ type CreateSafeOnNewChainForm = {
type ReplaySafeDialogProps = {
safeAddress: string
safeCreationResult: ReturnType<typeof useSafeCreationData>
replayableChains?: ReturnType<typeof useReplayableNetworks>
replayableChains?: ReturnType<typeof useCompatibleNetworks>
chain?: ChainInfo
currentName: string | undefined
open: boolean
onClose: () => void
isUnsupportedSafeCreationVersion?: boolean
}

const ReplaySafeDialog = ({
Expand All @@ -50,6 +54,7 @@ const ReplaySafeDialog = ({
onClose,
safeCreationResult,
replayableChains,
isUnsupportedSafeCreationVersion,
}: ReplaySafeDialogProps) => {
const formMethods = useForm<CreateSafeOnNewChainForm>({
mode: 'all',
Expand All @@ -58,8 +63,7 @@ const ReplaySafeDialog = ({
chainId: chain?.chainId,
},
})

const { handleSubmit } = formMethods
const { handleSubmit, formState } = formMethods
const router = useRouter()

const customRpc = useAppSelector(selectRpc)
Expand Down Expand Up @@ -102,17 +106,28 @@ const ReplaySafeDialog = ({
onClose()
})

const submitDisabled = !!safeCreationDataError || safeCreationDataLoading || !formMethods.formState.isValid
const submitDisabled =
isUnsupportedSafeCreationVersion || !!safeCreationDataError || safeCreationDataLoading || !formState.isValid

return (
<Dialog open={open} onClose={onClose}>
<form onSubmit={onFormSubmit} id="recreate-safe">
<DialogTitle fontWeight={700}>Add another network</DialogTitle>
<Divider />
<DialogContent>
{safeCreationDataError ? (
<ErrorMessage error={safeCreationDataError} level="error">
Could not determine the Safe creation parameters.
</ErrorMessage>
) : safeCreationDataLoading ? (
<Stack direction="column" alignItems="center" gap={1}>
<CircularProgress />
<Typography variant="body2">Loading Safe data</Typography>
</Stack>
) : isUnsupportedSafeCreationVersion ? (
<ErrorMessage>
This account was created from an outdated mastercopy. Adding another network is not possible.
</ErrorMessage>
) : (
<FormProvider {...formMethods}>
<Stack spacing={2}>
Expand All @@ -122,21 +137,12 @@ const ReplaySafeDialog = ({
the Safe&apos;s version will not be reflected in the copy.
</ErrorMessage>

{safeCreationDataLoading ? (
<Stack direction="column" alignItems="center" gap={1}>
<CircularProgress />
<Typography variant="body2">Loading Safe data</Typography>
</Stack>
<NameInput name="name" label="Name" />

{chain ? (
<ChainIndicator chainId={chain.chainId} />
) : (
<>
<NameInput name="name" label="Name" />

{chain ? (
<ChainIndicator chainId={chain.chainId} />
) : (
<NetworkInput required name="chainId" chainConfigs={replayableChains ?? []} />
)}
</>
<NetworkInput required name="chainId" chainConfigs={replayableChains ?? []} />
)}

{creationError && (
Expand All @@ -148,13 +154,27 @@ const ReplaySafeDialog = ({
</FormProvider>
)}
</DialogContent>
<DialogActions>
<Button variant="outlined" onClick={onClose}>
Cancel
</Button>
<Button type="submit" variant="contained" disabled={submitDisabled}>
Submit
</Button>
<Divider />
<DialogActions sx={{ m: 2 }}>
{isUnsupportedSafeCreationVersion ? (
<Box display="flex" width="100%" alignItems="center" justifyContent="space-between">
<ExternalLink sx={{ flexGrow: 1 }} href="https://safe.global">
Read more
</ExternalLink>
<Button variant="contained" onClick={onClose}>
Got it
</Button>
</Box>
) : (
<>
<Button variant="outlined" onClick={onClose}>
Cancel
</Button>
<Button type="submit" variant="contained" disabled={submitDisabled}>
Add network
</Button>
</>
)}
</DialogActions>
</form>
</Dialog>
Expand All @@ -165,7 +185,10 @@ export const CreateSafeOnNewChain = ({
safeAddress,
deployedChainIds,
...props
}: Omit<ReplaySafeDialogProps, 'safeCreationResult' | 'replayableChains' | 'chain'> & {
}: Omit<
ReplaySafeDialogProps,
'safeCreationResult' | 'replayableChains' | 'chain' | 'isUnsupportedSafeCreationVersion'
> & {
deployedChainIds: string[]
}) => {
const { configs } = useChains()
Expand All @@ -175,18 +198,24 @@ export const CreateSafeOnNewChain = ({
)

const safeCreationResult = useSafeCreationData(safeAddress, deployedChains)
const replayableChains = useReplayableNetworks(safeCreationResult[0], deployedChainIds)
const allCompatibleChains = useCompatibleNetworks(safeCreationResult[0])
const isUnsupportedSafeCreationVersion = Boolean(!allCompatibleChains?.length)
const replayableChains = useMemo(
() => allCompatibleChains?.filter((config) => !deployedChainIds.includes(config.chainId)) || [],
[allCompatibleChains, deployedChainIds],
)

return (
<ReplaySafeDialog
safeCreationResult={safeCreationResult}
replayableChains={replayableChains}
safeAddress={safeAddress}
isUnsupportedSafeCreationVersion={isUnsupportedSafeCreationVersion}
{...props}
/>
)
}

export const CreateSafeOnSpecificChain = ({ ...props }: Omit<ReplaySafeDialogProps, 'replayableChains'>) => {
return <ReplaySafeDialog {...props} />
return <ReplaySafeDialog {...props} isUnsupportedSafeCreationVersion={false} />
}
Loading

0 comments on commit 4f5e9bf

Please sign in to comment.