From c3cb2a2e62f3b7852b8e64070f2d2f7474a331b8 Mon Sep 17 00:00:00 2001 From: Usame Algan Date: Mon, 16 Sep 2024 16:05:49 +0200 Subject: [PATCH 1/3] fix: Show zksync network but disabled --- .../common/NetworkSelector/index.tsx | 39 +++++++++---------- .../common/NetworkSelector/styles.module.css | 8 ++++ .../multichain/hooks/useReplayableNetworks.ts | 21 ++++++---- 3 files changed, 40 insertions(+), 28 deletions(-) diff --git a/src/components/common/NetworkSelector/index.tsx b/src/components/common/NetworkSelector/index.tsx index 65b69aa80f..3a8e206302 100644 --- a/src/components/common/NetworkSelector/index.tsx +++ b/src/components/common/NetworkSelector/index.tsx @@ -66,25 +66,32 @@ export const getNetworkLink = (router: NextRouter, networkShortName: string, isW } const UndeployedNetworkMenuItem = ({ - chainId, - chainConfigs, + chain, isSelected = false, onSelect, }: { - chainId: string - chainConfigs: ChainInfo[] + chain: ChainInfo & { available: boolean } isSelected?: boolean onSelect: (chain: ChainInfo) => void }) => { - const chain = useMemo(() => chainConfigs.find((chain) => chain.chainId === chainId), [chainConfigs, chainId]) - - if (!chain) return null + const isDisabled = !chain.available return ( - onSelect(chain)}> + onSelect(chain)} + disabled={isDisabled} + > - + {isDisabled ? ( + + Not available + + ) : ( + + )} ) @@ -174,21 +181,11 @@ const UndeployedNetworks = ({ ) : ( <> {prodNets.map((chain) => ( - + ))} {testNets.length > 0 && } {testNets.map((chain) => ( - + ))} )} diff --git a/src/components/common/NetworkSelector/styles.module.css b/src/components/common/NetworkSelector/styles.module.css index e4429bdf3d..13529a2efd 100644 --- a/src/components/common/NetworkSelector/styles.module.css +++ b/src/components/common/NetworkSelector/styles.module.css @@ -73,6 +73,7 @@ .item { display: flex; align-items: center; + justify-content: space-between; gap: var(--space-1); width: 100%; } @@ -81,3 +82,10 @@ padding: var(--space-2) 0; margin: 2px; } + +.comingSoon { + background-color: var(--color-border-light); + border-radius: 4px; + color: var(--color-text-primary); + padding: 4px 8px; +} diff --git a/src/features/multichain/hooks/useReplayableNetworks.ts b/src/features/multichain/hooks/useReplayableNetworks.ts index d184f8a788..86668e5312 100644 --- a/src/features/multichain/hooks/useReplayableNetworks.ts +++ b/src/features/multichain/hooks/useReplayableNetworks.ts @@ -8,6 +8,7 @@ import { getSafeL2SingletonDeployments, getSafeSingletonDeployments, } from '@safe-global/safe-deployments' +import type { ChainInfo } from '@safe-global/safe-gateway-typescript-sdk' const SUPPORTED_VERSIONS: SafeVersion[] = ['1.4.1', '1.3.0', '1.1.1'] @@ -26,7 +27,10 @@ const hasDeployment = (chainId: string, contractAddress: string, deployments: Si * Therefore the creation's masterCopy and factory need to be deployed to that network. * @param creation */ -export const useReplayableNetworks = (creation: ReplayedSafeProps | undefined, deployedChainIds: string[]) => { +export const useReplayableNetworks = ( + creation: ReplayedSafeProps | undefined, + deployedChainIds: string[], +): (ChainInfo & { available: boolean })[] => { const { configs } = useChains() if (!creation) { @@ -53,10 +57,13 @@ export const useReplayableNetworks = (creation: ReplayedSafeProps | undefined, d return configs .filter((config) => !deployedChainIds.includes(config.chainId)) - .filter( - (config) => - (hasDeployment(config.chainId, masterCopy, allL1SingletonDeployments) || - hasDeployment(config.chainId, masterCopy, allL2SingletonDeployments)) && - hasDeployment(config.chainId, factoryAddress, allProxyFactoryDeployments), - ) + .map((config) => { + return { + ...config, + available: + (hasDeployment(config.chainId, masterCopy, allL1SingletonDeployments) || + hasDeployment(config.chainId, masterCopy, allL2SingletonDeployments)) && + hasDeployment(config.chainId, factoryAddress, allProxyFactoryDeployments), + } + }) } From c523943e65c8a4bbb772cdd134d2419761ce8bc2 Mon Sep 17 00:00:00 2001 From: Usame Algan Date: Mon, 16 Sep 2024 16:19:13 +0200 Subject: [PATCH 2/3] fix: Show message if zksync safe is open --- src/components/common/NetworkSelector/index.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/common/NetworkSelector/index.tsx b/src/components/common/NetworkSelector/index.tsx index 3a8e206302..579529b206 100644 --- a/src/components/common/NetworkSelector/index.tsx +++ b/src/components/common/NetworkSelector/index.tsx @@ -143,13 +143,15 @@ const UndeployedNetworks = ({ [availableNetworks], ) + const noAvailableNetworks = useMemo(() => availableNetworks.every((config) => !config.available), [availableNetworks]) + const onSelect = (chain: ChainInfo) => { setReplayOnChain(chain) } - if (safeCreationDataError) { + if (safeCreationDataError || (safeCreationData && noAvailableNetworks)) { return ( - + Adding another network is not possible for this Safe From 1fd32fffd684dfc63f7532592330004b2ed24fce Mon Sep 17 00:00:00 2001 From: Usame Algan Date: Mon, 16 Sep 2024 17:30:45 +0200 Subject: [PATCH 3/3] fix: Adjust tests for useReplayableNetworks --- .../__tests__/useReplayableNetworks.test.ts | 86 +++++++------------ 1 file changed, 31 insertions(+), 55 deletions(-) diff --git a/src/features/multichain/hooks/__tests__/useReplayableNetworks.test.ts b/src/features/multichain/hooks/__tests__/useReplayableNetworks.test.ts index f75acb3f4a..73fab0ce50 100644 --- a/src/features/multichain/hooks/__tests__/useReplayableNetworks.test.ts +++ b/src/features/multichain/hooks/__tests__/useReplayableNetworks.test.ts @@ -76,7 +76,7 @@ describe('useReplayableNetworks', () => { expect(result.current).toHaveLength(0) }) - it('should return empty list for unknown masterCopies', () => { + it('should set available to false for unknown masterCopies', () => { const callData = { owners: [faker.finance.ethereumAddress()], threshold: 1, @@ -106,43 +106,10 @@ describe('useReplayableNetworks', () => { setupData, } const { result } = renderHook(() => useReplayableNetworks(creationData, [])) - expect(result.current).toHaveLength(0) - }) - - it('should return empty list for unknown masterCopies', () => { - const callData = { - owners: [faker.finance.ethereumAddress()], - threshold: 1, - to: ZERO_ADDRESS, - data: EMPTY_DATA, - fallbackHandler: faker.finance.ethereumAddress(), - paymentToken: ZERO_ADDRESS, - payment: 0, - paymentReceiver: ECOSYSTEM_ID_ADDRESS, - } - - const setupData = safeInterface.encodeFunctionData('setup', [ - callData.owners, - callData.threshold, - callData.to, - callData.data, - callData.fallbackHandler, - callData.paymentToken, - callData.payment, - callData.paymentReceiver, - ]) - - const creationData: ReplayedSafeProps = { - factoryAddress: faker.finance.ethereumAddress(), - masterCopy: faker.finance.ethereumAddress(), - saltNonce: '0', - setupData, - } - const { result } = renderHook(() => useReplayableNetworks(creationData, [])) - expect(result.current).toHaveLength(0) + expect(result.current.every((config) => config.available)).toEqual(false) }) - it('should return everything but zkSync for 1.4.1 Safes', () => { + it('should set everything to available except zkSync for 1.4.1 Safes', () => { const callData = { owners: [faker.finance.ethereumAddress()], threshold: 1, @@ -173,8 +140,9 @@ describe('useReplayableNetworks', () => { setupData, } const { result } = renderHook(() => useReplayableNetworks(creationData, [])) - expect(result.current).toHaveLength(4) - expect(result.current.map((chain) => chain.chainId)).toEqual(['1', '10', '100', '480']) + expect(result.current).toHaveLength(5) + expect(result.current.map((chain) => chain.chainId)).toEqual(['1', '10', '100', '324', '480']) + expect(result.current.map((chain) => chain.available)).toEqual([true, true, true, false, true]) } { @@ -185,12 +153,13 @@ describe('useReplayableNetworks', () => { setupData, } const { result } = renderHook(() => useReplayableNetworks(creationData, [])) - expect(result.current).toHaveLength(4) - expect(result.current.map((chain) => chain.chainId)).toEqual(['1', '10', '100', '480']) + expect(result.current).toHaveLength(5) + expect(result.current.map((chain) => chain.chainId)).toEqual(['1', '10', '100', '324', '480']) + expect(result.current.map((chain) => chain.available)).toEqual([true, true, true, false, true]) } }) - it('should remove already deployed chains from result', () => { + it('should mark already deployed chains as not available', () => { const callData = { owners: [faker.finance.ethereumAddress()], threshold: 1, @@ -221,8 +190,9 @@ describe('useReplayableNetworks', () => { setupData, } const { result } = renderHook(() => useReplayableNetworks(creationData, ['10', '100'])) - expect(result.current).toHaveLength(2) - expect(result.current.map((chain) => chain.chainId)).toEqual(['1', '480']) + expect(result.current).toHaveLength(3) + expect(result.current.map((chain) => chain.chainId)).toEqual(['1', '324', '480']) + expect(result.current.map((chain) => chain.available)).toEqual([true, false, true]) } { @@ -233,8 +203,9 @@ describe('useReplayableNetworks', () => { setupData, } const { result } = renderHook(() => useReplayableNetworks(creationData, [])) - expect(result.current).toHaveLength(4) - expect(result.current.map((chain) => chain.chainId)).toEqual(['1', '10', '100', '480']) + expect(result.current).toHaveLength(5) + expect(result.current.map((chain) => chain.chainId)).toEqual(['1', '10', '100', '324', '480']) + expect(result.current.map((chain) => chain.available)).toEqual([true, true, true, false, true]) } }) @@ -270,8 +241,9 @@ describe('useReplayableNetworks', () => { setupData, } const { result } = renderHook(() => useReplayableNetworks(creationData, [])) - expect(result.current).toHaveLength(4) - expect(result.current.map((chain) => chain.chainId)).toEqual(['1', '10', '100', '480']) + expect(result.current).toHaveLength(5) + expect(result.current.map((chain) => chain.chainId)).toEqual(['1', '10', '100', '324', '480']) + expect(result.current.map((chain) => chain.available)).toEqual([true, true, true, false, true]) } // 1.3.0, L2 and canonical @@ -283,8 +255,9 @@ describe('useReplayableNetworks', () => { setupData, } const { result } = renderHook(() => useReplayableNetworks(creationData, [])) - expect(result.current).toHaveLength(4) - expect(result.current.map((chain) => chain.chainId)).toEqual(['1', '10', '100', '480']) + expect(result.current).toHaveLength(5) + expect(result.current.map((chain) => chain.chainId)).toEqual(['1', '10', '100', '324', '480']) + expect(result.current.map((chain) => chain.available)).toEqual([true, true, true, false, true]) } // 1.3.0, L1 and EIP155 is not available on Worldchain @@ -296,8 +269,9 @@ describe('useReplayableNetworks', () => { setupData, } const { result } = renderHook(() => useReplayableNetworks(creationData, [])) - expect(result.current).toHaveLength(3) - expect(result.current.map((chain) => chain.chainId)).toEqual(['1', '10', '100']) + expect(result.current).toHaveLength(5) + expect(result.current.map((chain) => chain.chainId)).toEqual(['1', '10', '100', '324', '480']) + expect(result.current.map((chain) => chain.available)).toEqual([true, true, true, false, false]) } // 1.3.0, L2 and EIP155 @@ -309,8 +283,9 @@ describe('useReplayableNetworks', () => { setupData, } const { result } = renderHook(() => useReplayableNetworks(creationData, [])) - expect(result.current).toHaveLength(3) - expect(result.current.map((chain) => chain.chainId)).toEqual(['1', '10', '100']) + expect(result.current).toHaveLength(5) + expect(result.current.map((chain) => chain.chainId)).toEqual(['1', '10', '100', '324', '480']) + expect(result.current.map((chain) => chain.available)).toEqual([true, true, true, false, false]) } }) @@ -344,7 +319,8 @@ describe('useReplayableNetworks', () => { setupData, } const { result } = renderHook(() => useReplayableNetworks(creationData, [])) - expect(result.current).toHaveLength(2) - expect(result.current.map((chain) => chain.chainId)).toEqual(['1', '100']) + expect(result.current).toHaveLength(5) + expect(result.current.map((chain) => chain.chainId)).toEqual(['1', '10', '100', '324', '480']) + expect(result.current.map((chain) => chain.available)).toEqual([true, false, true, false, false]) }) })