From 043efec8f9cb08232e6f2a332ce4df931e23b6a4 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 22 May 2025 17:41:17 +0000 Subject: [PATCH 01/17] Fix unsupported network detection and UI handling Co-Authored-By: nacho@reown.com --- .../__tests__/controllers/NetworkController.test.ts | 10 ++++++++++ packages/core/src/controllers/NetworkController.ts | 7 +++++++ .../scaffold/src/modal/w3m-network-button/index.tsx | 10 ++++++++-- .../src/views/w3m-unsupported-chain-view/index.tsx | 4 ++-- packages/wagmi/src/client.ts | 9 +++++++++ 5 files changed, 36 insertions(+), 4 deletions(-) diff --git a/packages/core/src/__tests__/controllers/NetworkController.test.ts b/packages/core/src/__tests__/controllers/NetworkController.test.ts index 9202383c..332b9e36 100644 --- a/packages/core/src/__tests__/controllers/NetworkController.test.ts +++ b/packages/core/src/__tests__/controllers/NetworkController.test.ts @@ -74,4 +74,14 @@ describe('NetworkController', () => { expect(NetworkController.state.approvedCaipNetworkIds).toEqual(undefined); expect(NetworkController.state.requestedCaipNetworks).toEqual(requestedCaipNetworks); }); + + it('should validate if active network is in requested networks', () => { + NetworkController.setRequestedCaipNetworks(requestedCaipNetworks); + + NetworkController.setCaipNetwork({ id: 'eip155:1', name: 'Ethereum' }); + expect(NetworkController.isActiveNetworkInRequestedNetworks()).toBe(true); + + NetworkController.setCaipNetwork({ id: 'eip155:99', name: 'Unknown Network' }); + expect(NetworkController.isActiveNetworkInRequestedNetworks()).toBe(false); + }); }); diff --git a/packages/core/src/controllers/NetworkController.ts b/packages/core/src/controllers/NetworkController.ts index f1023c95..62900a0d 100644 --- a/packages/core/src/controllers/NetworkController.ts +++ b/packages/core/src/controllers/NetworkController.ts @@ -116,5 +116,12 @@ export const NetworkController = { state.approvedCaipNetworkIds = undefined; state.supportsAllNetworks = true; state.smartAccountEnabledNetworks = []; + }, + + isActiveNetworkInRequestedNetworks() { + if (!state.caipNetwork || !state.requestedCaipNetworks?.length) { + return true; // No active network or no requested networks, so no validation needed + } + return state.requestedCaipNetworks.some(network => network.id === state.caipNetwork?.id); } }; diff --git a/packages/scaffold/src/modal/w3m-network-button/index.tsx b/packages/scaffold/src/modal/w3m-network-button/index.tsx index 353a1804..e9187950 100644 --- a/packages/scaffold/src/modal/w3m-network-button/index.tsx +++ b/packages/scaffold/src/modal/w3m-network-button/index.tsx @@ -21,9 +21,15 @@ export function NetworkButton({ disabled, style }: NetworkButtonProps) { const { caipNetwork } = useSnapshot(NetworkController.state); const { loading } = useSnapshot(ModalController.state); const { themeMode, themeVariables } = useSnapshot(ThemeController.state); + + const isNetworkSupported = NetworkController.isActiveNetworkInRequestedNetworks(); const onNetworkPress = () => { - ModalController.open({ view: 'Networks' }); + if (isConnected && !isNetworkSupported) { + ModalController.open({ view: 'UnsupportedChain' }); + } else { + ModalController.open({ view: 'Networks' }); + } EventsController.sendEvent({ type: 'track', event: 'CLICK_NETWORKS' @@ -41,7 +47,7 @@ export function NetworkButton({ disabled, style }: NetworkButtonProps) { loading={loading} testID="network-button" > - {caipNetwork?.name ?? (isConnected ? 'Unknown Network' : 'Select Network')} + {caipNetwork?.name ?? (isConnected ? (isNetworkSupported ? 'Unknown Network' : 'Switch Network') : 'Select Network')} ); diff --git a/packages/scaffold/src/views/w3m-unsupported-chain-view/index.tsx b/packages/scaffold/src/views/w3m-unsupported-chain-view/index.tsx index f2074e50..6bf9ef85 100644 --- a/packages/scaffold/src/views/w3m-unsupported-chain-view/index.tsx +++ b/packages/scaffold/src/views/w3m-unsupported-chain-view/index.tsx @@ -49,8 +49,8 @@ export function UnsupportedChainView() { ListHeaderComponentStyle={styles.header} ListHeaderComponent={ - The swap feature doesn't support your current network. Switch to an available option to - continue. + The current network is not supported by this application. + Please switch to an available option to continue. } contentContainerStyle={styles.contentContainer} diff --git a/packages/wagmi/src/client.ts b/packages/wagmi/src/client.ts index 5c4a4aac..adfffe58 100644 --- a/packages/wagmi/src/client.ts +++ b/packages/wagmi/src/client.ts @@ -470,6 +470,15 @@ export class AppKit extends AppKitScaffold { imageId: PresetsUtil.EIP155NetworkImageIds[id], imageUrl: this.options?.chainImages?.[id] }); + + const internalState = this.wagmiConfig.chains || []; + const isNetworkSupported = internalState.length === 0 || + internalState.some((chain: Chain) => + `${ConstantsUtil.EIP155}:${chain.id}` === caipChainId); + if (!isNetworkSupported) { + console.warn(`Network ${caipChainId} is not in the requested networks list`); + } + if (isConnected && address && chainId) { const caipAddress: CaipAddress = `${ConstantsUtil.EIP155}:${id}:${address}`; this.setCaipAddress(caipAddress); From d76da538c844c5f0d3247f247e5f8d39ffee1edc Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 22 May 2025 17:52:15 +0000 Subject: [PATCH 02/17] Fix lint errors and improve network validation Co-Authored-By: nacho@reown.com --- packages/core/src/controllers/NetworkController.ts | 1 + packages/scaffold/src/modal/w3m-network-button/index.tsx | 5 +++-- packages/wagmi/src/client.ts | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/core/src/controllers/NetworkController.ts b/packages/core/src/controllers/NetworkController.ts index 62900a0d..f59f4455 100644 --- a/packages/core/src/controllers/NetworkController.ts +++ b/packages/core/src/controllers/NetworkController.ts @@ -122,6 +122,7 @@ export const NetworkController = { if (!state.caipNetwork || !state.requestedCaipNetworks?.length) { return true; // No active network or no requested networks, so no validation needed } + return state.requestedCaipNetworks.some(network => network.id === state.caipNetwork?.id); } }; diff --git a/packages/scaffold/src/modal/w3m-network-button/index.tsx b/packages/scaffold/src/modal/w3m-network-button/index.tsx index e9187950..4f48d70b 100644 --- a/packages/scaffold/src/modal/w3m-network-button/index.tsx +++ b/packages/scaffold/src/modal/w3m-network-button/index.tsx @@ -18,11 +18,12 @@ export interface NetworkButtonProps { export function NetworkButton({ disabled, style }: NetworkButtonProps) { const { isConnected } = useSnapshot(AccountController.state); - const { caipNetwork } = useSnapshot(NetworkController.state); + const { caipNetwork, requestedCaipNetworks } = useSnapshot(NetworkController.state); const { loading } = useSnapshot(ModalController.state); const { themeMode, themeVariables } = useSnapshot(ThemeController.state); - const isNetworkSupported = NetworkController.isActiveNetworkInRequestedNetworks(); + const isNetworkSupported = !caipNetwork || !requestedCaipNetworks?.length || + requestedCaipNetworks.some(network => network.id === caipNetwork.id); const onNetworkPress = () => { if (isConnected && !isNetworkSupported) { diff --git a/packages/wagmi/src/client.ts b/packages/wagmi/src/client.ts index adfffe58..0624d72e 100644 --- a/packages/wagmi/src/client.ts +++ b/packages/wagmi/src/client.ts @@ -473,8 +473,8 @@ export class AppKit extends AppKitScaffold { const internalState = this.wagmiConfig.chains || []; const isNetworkSupported = internalState.length === 0 || - internalState.some((chain: Chain) => - `${ConstantsUtil.EIP155}:${chain.id}` === caipChainId); + internalState.some((chainItem: Chain) => + `${ConstantsUtil.EIP155}:${chainItem.id}` === caipChainId); if (!isNetworkSupported) { console.warn(`Network ${caipChainId} is not in the requested networks list`); } From 78499a8ccbaddaa7f780b82447a49863af20a69b Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 22 May 2025 18:32:41 +0000 Subject: [PATCH 03/17] Fix lint issues and improve network validation Co-Authored-By: nacho@reown.com --- .../controllers/NetworkController.test.ts | 4 ++-- .../core/src/controllers/NetworkController.ts | 2 +- .../src/modal/w3m-network-button/index.tsx | 23 ++++++++++++------- .../w3m-unsupported-chain-view/index.tsx | 4 ++-- packages/wagmi/src/client.ts | 12 ++++++---- 5 files changed, 27 insertions(+), 18 deletions(-) diff --git a/packages/core/src/__tests__/controllers/NetworkController.test.ts b/packages/core/src/__tests__/controllers/NetworkController.test.ts index 332b9e36..8ce093d4 100644 --- a/packages/core/src/__tests__/controllers/NetworkController.test.ts +++ b/packages/core/src/__tests__/controllers/NetworkController.test.ts @@ -77,10 +77,10 @@ describe('NetworkController', () => { it('should validate if active network is in requested networks', () => { NetworkController.setRequestedCaipNetworks(requestedCaipNetworks); - + NetworkController.setCaipNetwork({ id: 'eip155:1', name: 'Ethereum' }); expect(NetworkController.isActiveNetworkInRequestedNetworks()).toBe(true); - + NetworkController.setCaipNetwork({ id: 'eip155:99', name: 'Unknown Network' }); expect(NetworkController.isActiveNetworkInRequestedNetworks()).toBe(false); }); diff --git a/packages/core/src/controllers/NetworkController.ts b/packages/core/src/controllers/NetworkController.ts index f59f4455..7e578559 100644 --- a/packages/core/src/controllers/NetworkController.ts +++ b/packages/core/src/controllers/NetworkController.ts @@ -122,7 +122,7 @@ export const NetworkController = { if (!state.caipNetwork || !state.requestedCaipNetworks?.length) { return true; // No active network or no requested networks, so no validation needed } - + return state.requestedCaipNetworks.some(network => network.id === state.caipNetwork?.id); } }; diff --git a/packages/scaffold/src/modal/w3m-network-button/index.tsx b/packages/scaffold/src/modal/w3m-network-button/index.tsx index 4f48d70b..b54b0ce9 100644 --- a/packages/scaffold/src/modal/w3m-network-button/index.tsx +++ b/packages/scaffold/src/modal/w3m-network-button/index.tsx @@ -17,16 +17,18 @@ export interface NetworkButtonProps { } export function NetworkButton({ disabled, style }: NetworkButtonProps) { - const { isConnected } = useSnapshot(AccountController.state); - const { caipNetwork, requestedCaipNetworks } = useSnapshot(NetworkController.state); const { loading } = useSnapshot(ModalController.state); const { themeMode, themeVariables } = useSnapshot(ThemeController.state); - - const isNetworkSupported = !caipNetwork || !requestedCaipNetworks?.length || - requestedCaipNetworks.some(network => network.id === caipNetwork.id); + + const isNetworkSupported = + !NetworkController.state.caipNetwork || + !NetworkController.state.requestedCaipNetworks?.length || + NetworkController.state.requestedCaipNetworks.some( + network => network.id === NetworkController.state.caipNetwork?.id + ); const onNetworkPress = () => { - if (isConnected && !isNetworkSupported) { + if (AccountController.state.isConnected && !isNetworkSupported) { ModalController.open({ view: 'UnsupportedChain' }); } else { ModalController.open({ view: 'Networks' }); @@ -40,7 +42,7 @@ export function NetworkButton({ disabled, style }: NetworkButtonProps) { return ( - {caipNetwork?.name ?? (isConnected ? (isNetworkSupported ? 'Unknown Network' : 'Switch Network') : 'Select Network')} + {NetworkController.state.caipNetwork?.name ?? + (AccountController.state.isConnected + ? isNetworkSupported + ? 'Unknown Network' + : 'Switch Network' + : 'Select Network')} ); diff --git a/packages/scaffold/src/views/w3m-unsupported-chain-view/index.tsx b/packages/scaffold/src/views/w3m-unsupported-chain-view/index.tsx index 6bf9ef85..1dce52e1 100644 --- a/packages/scaffold/src/views/w3m-unsupported-chain-view/index.tsx +++ b/packages/scaffold/src/views/w3m-unsupported-chain-view/index.tsx @@ -49,8 +49,8 @@ export function UnsupportedChainView() { ListHeaderComponentStyle={styles.header} ListHeaderComponent={ - The current network is not supported by this application. - Please switch to an available option to continue. + The current network is not supported by this application. Please switch to an available + option to continue. } contentContainerStyle={styles.contentContainer} diff --git a/packages/wagmi/src/client.ts b/packages/wagmi/src/client.ts index 0624d72e..e1851f10 100644 --- a/packages/wagmi/src/client.ts +++ b/packages/wagmi/src/client.ts @@ -470,15 +470,17 @@ export class AppKit extends AppKitScaffold { imageId: PresetsUtil.EIP155NetworkImageIds[id], imageUrl: this.options?.chainImages?.[id] }); - + const internalState = this.wagmiConfig.chains || []; - const isNetworkSupported = internalState.length === 0 || - internalState.some((chainItem: Chain) => - `${ConstantsUtil.EIP155}:${chainItem.id}` === caipChainId); + const isNetworkSupported = + internalState.length === 0 || + internalState.some( + (chainItem: Chain) => `${ConstantsUtil.EIP155}:${chainItem.id}` === caipChainId + ); if (!isNetworkSupported) { console.warn(`Network ${caipChainId} is not in the requested networks list`); } - + if (isConnected && address && chainId) { const caipAddress: CaipAddress = `${ConstantsUtil.EIP155}:${id}:${address}`; this.setCaipAddress(caipAddress); From 4297e21dbfab62d50d21eddadaf1cb7b71029d7a Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 22 May 2025 19:00:44 +0000 Subject: [PATCH 04/17] Update AccountDefaultView and AccountView to handle unsupported networks Co-Authored-By: nacho@reown.com --- .../scaffold/src/views/w3m-account-default-view/index.tsx | 8 +++++++- packages/scaffold/src/views/w3m-account-view/index.tsx | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/packages/scaffold/src/views/w3m-account-default-view/index.tsx b/packages/scaffold/src/views/w3m-account-default-view/index.tsx index f3982ed9..f85cdfdd 100644 --- a/packages/scaffold/src/views/w3m-account-default-view/index.tsx +++ b/packages/scaffold/src/views/w3m-account-default-view/index.tsx @@ -146,7 +146,13 @@ export function AccountDefaultView() { }; const onNetworkPress = () => { - RouterController.push('Networks'); + const isNetworkSupported = NetworkController.isActiveNetworkInRequestedNetworks(); + + if (AccountController.state.isConnected && !isNetworkSupported) { + RouterController.push('UnsupportedChain'); + } else { + RouterController.push('Networks'); + } EventsController.sendEvent({ type: 'track', diff --git a/packages/scaffold/src/views/w3m-account-view/index.tsx b/packages/scaffold/src/views/w3m-account-view/index.tsx index 14e19d85..f757e889 100644 --- a/packages/scaffold/src/views/w3m-account-view/index.tsx +++ b/packages/scaffold/src/views/w3m-account-view/index.tsx @@ -39,7 +39,13 @@ export function AccountView() { }; const onNetworkPress = () => { - RouterController.push('Networks'); + const isNetworkSupported = NetworkController.isActiveNetworkInRequestedNetworks(); + + if (AccountController.state.isConnected && !isNetworkSupported) { + RouterController.push('UnsupportedChain'); + } else { + RouterController.push('Networks'); + } }; const onActivatePress = () => { From d119242598fed690fd34241575cbf6b106273560 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 22 May 2025 19:06:25 +0000 Subject: [PATCH 05/17] Fix formatting in AccountView component Co-Authored-By: nacho@reown.com --- packages/scaffold/src/views/w3m-account-view/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scaffold/src/views/w3m-account-view/index.tsx b/packages/scaffold/src/views/w3m-account-view/index.tsx index f757e889..f16a4c3e 100644 --- a/packages/scaffold/src/views/w3m-account-view/index.tsx +++ b/packages/scaffold/src/views/w3m-account-view/index.tsx @@ -40,7 +40,7 @@ export function AccountView() { const onNetworkPress = () => { const isNetworkSupported = NetworkController.isActiveNetworkInRequestedNetworks(); - + if (AccountController.state.isConnected && !isNetworkSupported) { RouterController.push('UnsupportedChain'); } else { From f893487bfd4d135f453f37e3e07ee028521f7a6c Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 22 May 2025 19:07:02 +0000 Subject: [PATCH 06/17] Update AccountDefaultView to show Switch Network when network is not supported Co-Authored-By: nacho@reown.com --- .../scaffold/src/views/w3m-account-default-view/index.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/scaffold/src/views/w3m-account-default-view/index.tsx b/packages/scaffold/src/views/w3m-account-default-view/index.tsx index f85cdfdd..b223a282 100644 --- a/packages/scaffold/src/views/w3m-account-default-view/index.tsx +++ b/packages/scaffold/src/views/w3m-account-default-view/index.tsx @@ -254,7 +254,9 @@ export function AccountDefaultView() { style={styles.actionButton} > - {caipNetwork?.name} + {!NetworkController.isActiveNetworkInRequestedNetworks() && AccountController.state.isConnected + ? 'Switch Network' + : caipNetwork?.name} From d3f4859e6f50caae9548c13c62c66e9e0998bad5 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 22 May 2025 19:07:58 +0000 Subject: [PATCH 07/17] Fix formatting in AccountDefaultView component Co-Authored-By: nacho@reown.com --- packages/scaffold/src/views/w3m-account-default-view/index.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/scaffold/src/views/w3m-account-default-view/index.tsx b/packages/scaffold/src/views/w3m-account-default-view/index.tsx index b223a282..9f6a073e 100644 --- a/packages/scaffold/src/views/w3m-account-default-view/index.tsx +++ b/packages/scaffold/src/views/w3m-account-default-view/index.tsx @@ -254,7 +254,8 @@ export function AccountDefaultView() { style={styles.actionButton} > - {!NetworkController.isActiveNetworkInRequestedNetworks() && AccountController.state.isConnected + {!NetworkController.isActiveNetworkInRequestedNetworks() && + AccountController.state.isConnected ? 'Switch Network' : caipNetwork?.name} From 75516c9f313b3a1262d12e7e3cf2816f6b441e87 Mon Sep 17 00:00:00 2001 From: nacho <25931366+ignaciosantise@users.noreply.github.com> Date: Thu, 22 May 2025 16:53:02 -0300 Subject: [PATCH 08/17] chore: improvements --- .../controllers/NetworkController.test.ts | 3 +- .../core/src/controllers/NetworkController.ts | 12 +++--- .../scaffold/src/modal/w3m-modal/index.tsx | 40 ++++++++++++++++++- .../src/modal/w3m-network-button/index.tsx | 19 ++++----- packages/scaffold/src/utils/UiUtil.ts | 32 ++++++++++++++- .../views/w3m-account-default-view/index.tsx | 12 +++--- 6 files changed, 87 insertions(+), 31 deletions(-) diff --git a/packages/core/src/__tests__/controllers/NetworkController.test.ts b/packages/core/src/__tests__/controllers/NetworkController.test.ts index 8ce093d4..8ed1d15c 100644 --- a/packages/core/src/__tests__/controllers/NetworkController.test.ts +++ b/packages/core/src/__tests__/controllers/NetworkController.test.ts @@ -19,7 +19,6 @@ const client: NetworkControllerClient = { const initialState = { _client: client, supportsAllNetworks: true, - isDefaultCaipNetwork: false, smartAccountEnabledNetworks: [] }; @@ -65,7 +64,7 @@ describe('NetworkController', () => { it('should update state correctly on setDefaultCaipNetwork()', () => { NetworkController.setDefaultCaipNetwork(caipNetwork); expect(NetworkController.state.caipNetwork).toEqual(caipNetwork); - expect(NetworkController.state.isDefaultCaipNetwork).toEqual(true); + expect(NetworkController.state.defaultCaipNetwork).toEqual(caipNetwork); }); it('should reset state correctly when default caip network is true', () => { diff --git a/packages/core/src/controllers/NetworkController.ts b/packages/core/src/controllers/NetworkController.ts index 7e578559..6ec69261 100644 --- a/packages/core/src/controllers/NetworkController.ts +++ b/packages/core/src/controllers/NetworkController.ts @@ -15,9 +15,9 @@ export interface NetworkControllerClient { export interface NetworkControllerState { supportsAllNetworks: boolean; - isDefaultCaipNetwork: boolean; _client?: NetworkControllerClient; caipNetwork?: CaipNetwork; + defaultCaipNetwork?: CaipNetwork; requestedCaipNetworks?: CaipNetwork[]; approvedCaipNetworkIds?: CaipNetworkId[]; smartAccountEnabledNetworks: number[]; @@ -26,7 +26,7 @@ export interface NetworkControllerState { // -- State --------------------------------------------- // const state = proxy({ supportsAllNetworks: true, - isDefaultCaipNetwork: false, + defaultCaipNetwork: undefined, smartAccountEnabledNetworks: [] }); @@ -53,7 +53,7 @@ export const NetworkController = { setDefaultCaipNetwork(caipNetwork: NetworkControllerState['caipNetwork']) { state.caipNetwork = caipNetwork; - state.isDefaultCaipNetwork = true; + state.defaultCaipNetwork = caipNetwork; PublicStateController.set({ selectedNetworkId: caipNetwork?.id }); }, @@ -110,9 +110,7 @@ export const NetworkController = { }, resetNetwork() { - if (!state.isDefaultCaipNetwork) { - state.caipNetwork = undefined; - } + state.caipNetwork = state.defaultCaipNetwork || undefined; state.approvedCaipNetworkIds = undefined; state.supportsAllNetworks = true; state.smartAccountEnabledNetworks = []; @@ -120,7 +118,7 @@ export const NetworkController = { isActiveNetworkInRequestedNetworks() { if (!state.caipNetwork || !state.requestedCaipNetworks?.length) { - return true; // No active network or no requested networks, so no validation needed + return false; } return state.requestedCaipNetworks.some(network => network.id === state.caipNetwork?.id); diff --git a/packages/scaffold/src/modal/w3m-modal/index.tsx b/packages/scaffold/src/modal/w3m-modal/index.tsx index 7a39c491..3d0eba33 100644 --- a/packages/scaffold/src/modal/w3m-modal/index.tsx +++ b/packages/scaffold/src/modal/w3m-modal/index.tsx @@ -1,5 +1,5 @@ import { useSnapshot } from 'valtio'; -import { useCallback, useEffect } from 'react'; +import { useCallback, useEffect, useState } from 'react'; import { useWindowDimensions, StatusBar } from 'react-native'; import Modal from 'react-native-modal'; import { Card, ThemeProvider } from '@reown/appkit-ui-react-native'; @@ -16,7 +16,8 @@ import { TransactionsController, type CaipAddress, type AppKitFrameProvider, - ThemeController + ThemeController, + NetworkController } from '@reown/appkit-core-react-native'; import { SIWEController } from '@reown/appkit-siwe-react-native'; @@ -30,7 +31,9 @@ export function AppKit() { const { open, loading } = useSnapshot(ModalController.state); const { connectors, connectedConnector } = useSnapshot(ConnectorController.state); const { caipAddress, isConnected } = useSnapshot(AccountController.state); + const { caipNetwork } = useSnapshot(NetworkController.state); const { themeMode, themeVariables } = useSnapshot(ThemeController.state); + const [isNetworkStateStable, setIsNetworkStateStable] = useState(false); const { height } = useWindowDimensions(); const { isLandscape } = useCustomDimensions(); const portraitHeight = height - 120; @@ -114,6 +117,39 @@ export function AppKit() { onNewAddress(caipAddress); }, [caipAddress, onNewAddress]); + useEffect(() => { + if (isConnected) { + const timer = setTimeout(() => { + setIsNetworkStateStable(true); + }, 750); // Stability period. Sometimes the network state updates at init + + return () => { + clearTimeout(timer); + }; + } else { + setIsNetworkStateStable(false); + + return () => {}; + } + }, [isConnected]); + + useEffect(() => { + if (isConnected && caipNetwork && isNetworkStateStable) { + const isNetworkSupported = NetworkController.isActiveNetworkInRequestedNetworks(); + if (!isNetworkSupported) { + const currentView = RouterController.state.view; + // Only push/open if not already on UnsupportedChain or actively choosing a network + if (currentView !== 'UnsupportedChain' && currentView !== 'Networks') { + if (ModalController.state.open) { + RouterController.push('UnsupportedChain'); + } else { + ModalController.open({ view: 'UnsupportedChain' }); + } + } + } + } + }, [caipNetwork, isConnected, isNetworkStateStable]); + return ( <> diff --git a/packages/scaffold/src/modal/w3m-network-button/index.tsx b/packages/scaffold/src/modal/w3m-network-button/index.tsx index b54b0ce9..066cee54 100644 --- a/packages/scaffold/src/modal/w3m-network-button/index.tsx +++ b/packages/scaffold/src/modal/w3m-network-button/index.tsx @@ -10,6 +10,7 @@ import { ThemeController } from '@reown/appkit-core-react-native'; import { NetworkButton as NetworkButtonUI, ThemeProvider } from '@reown/appkit-ui-react-native'; +import { UiUtil } from '../../utils/UiUtil'; export interface NetworkButtonProps { disabled?: boolean; @@ -18,14 +19,11 @@ export interface NetworkButtonProps { export function NetworkButton({ disabled, style }: NetworkButtonProps) { const { loading } = useSnapshot(ModalController.state); + const { caipNetwork } = useSnapshot(NetworkController.state); + const { isConnected } = useSnapshot(AccountController.state); const { themeMode, themeVariables } = useSnapshot(ThemeController.state); - const isNetworkSupported = - !NetworkController.state.caipNetwork || - !NetworkController.state.requestedCaipNetworks?.length || - NetworkController.state.requestedCaipNetworks.some( - network => network.id === NetworkController.state.caipNetwork?.id - ); + const isNetworkSupported = NetworkController.isActiveNetworkInRequestedNetworks(); const onNetworkPress = () => { if (AccountController.state.isConnected && !isNetworkSupported) { @@ -39,6 +37,8 @@ export function NetworkButton({ disabled, style }: NetworkButtonProps) { }); }; + const buttonText = UiUtil.getNetworkButtonText(isConnected, caipNetwork, isNetworkSupported); + return ( - {NetworkController.state.caipNetwork?.name ?? - (AccountController.state.isConnected - ? isNetworkSupported - ? 'Unknown Network' - : 'Switch Network' - : 'Select Network')} + {buttonText} ); diff --git a/packages/scaffold/src/utils/UiUtil.ts b/packages/scaffold/src/utils/UiUtil.ts index 65e1f9d9..3cd459aa 100644 --- a/packages/scaffold/src/utils/UiUtil.ts +++ b/packages/scaffold/src/utils/UiUtil.ts @@ -2,7 +2,8 @@ import { AssetUtil, ConnectionController, StorageUtil, - type WcWallet + type WcWallet, + type CaipNetwork } from '@reown/appkit-core-react-native'; import { LayoutAnimation } from 'react-native'; @@ -27,5 +28,34 @@ export const UiUtil = { const url = AssetUtil.getWalletImage(pressedWallet); ConnectionController.setConnectedWalletImageUrl(url); } + }, + + getNetworkButtonText( + isConnected: boolean, + caipNetwork: CaipNetwork | undefined, + isNetworkSupported: boolean + ): string { + let buttonText: string; + + if (!isConnected) { + if (caipNetwork) { + buttonText = caipNetwork.name ?? 'Unknown Network'; + } else { + buttonText = 'Select Network'; + } + } else { + // isConnected is true + if (caipNetwork) { + if (isNetworkSupported) { + buttonText = caipNetwork.name ?? 'Unknown Network'; + } else { + buttonText = 'Switch Network'; + } + } else { + buttonText = 'Select Network'; + } + } + + return buttonText; } }; diff --git a/packages/scaffold/src/views/w3m-account-default-view/index.tsx b/packages/scaffold/src/views/w3m-account-default-view/index.tsx index 9f6a073e..9ca4a75e 100644 --- a/packages/scaffold/src/views/w3m-account-default-view/index.tsx +++ b/packages/scaffold/src/views/w3m-account-default-view/index.tsx @@ -30,6 +30,7 @@ import { ListItem } from '@reown/appkit-ui-react-native'; import { useCustomDimensions } from '../../hooks/useCustomDimensions'; +import { UiUtil as ScaffoldUiUtil } from '../../utils/UiUtil'; import styles from './styles'; import { AuthButtons } from './components/auth-buttons'; @@ -42,7 +43,8 @@ export function AccountDefaultView() { balance, balanceSymbol, addressExplorerUrl, - preferredAccountType + preferredAccountType, + isConnected } = useSnapshot(AccountController.state); const { loading } = useSnapshot(ModalController.state); const [disconnecting, setDisconnecting] = useState(false); @@ -59,6 +61,7 @@ export function AccountDefaultView() { const showBack = history.length > 1; const showSwitchAccountType = isAuth && NetworkController.checkIfSmartAccountEnabled(); const { padding } = useCustomDimensions(); + const isNetworkSupported = NetworkController.isActiveNetworkInRequestedNetworks(); async function onDisconnect() { setDisconnecting(true); @@ -146,8 +149,6 @@ export function AccountDefaultView() { }; const onNetworkPress = () => { - const isNetworkSupported = NetworkController.isActiveNetworkInRequestedNetworks(); - if (AccountController.state.isConnected && !isNetworkSupported) { RouterController.push('UnsupportedChain'); } else { @@ -254,10 +255,7 @@ export function AccountDefaultView() { style={styles.actionButton} > - {!NetworkController.isActiveNetworkInRequestedNetworks() && - AccountController.state.isConnected - ? 'Switch Network' - : caipNetwork?.name} + {ScaffoldUiUtil.getNetworkButtonText(isConnected, caipNetwork, isNetworkSupported)} From 634bd4fa54e6fe492fd4f47f9d5244d9248f653c Mon Sep 17 00:00:00 2001 From: nacho <25931366+ignaciosantise@users.noreply.github.com> Date: Fri, 23 May 2025 15:48:53 -0300 Subject: [PATCH 09/17] fix: handle unsupported network gracefully, also in siwe --- .../controllers/NetworkController.test.ts | 15 +++---- .../core/src/controllers/NetworkController.ts | 17 ++++---- packages/core/src/utils/NetworkUtil.ts | 6 +-- packages/core/src/utils/RouterUtil.ts | 18 +++++--- packages/ethers/src/client.ts | 5 +++ packages/ethers5/src/client.ts | 5 +++ packages/scaffold/src/client.ts | 6 +++ .../scaffold/src/modal/w3m-modal/index.tsx | 42 ++++++++++++------- .../src/modal/w3m-network-button/index.tsx | 10 +++-- .../src/partials/w3m-header/index.tsx | 11 +++-- packages/scaffold/src/utils/UiUtil.ts | 8 ++-- .../views/w3m-account-default-view/index.tsx | 23 ++++++---- .../src/views/w3m-account-view/index.tsx | 4 +- .../w3m-unsupported-chain-view/index.tsx | 24 +++++++++-- .../views/w3m-connecting-siwe-view/index.tsx | 9 +--- packages/ui/src/utils/TypesUtil.ts | 4 ++ packages/wagmi/src/client.ts | 26 ++++++------ 17 files changed, 145 insertions(+), 88 deletions(-) diff --git a/packages/core/src/__tests__/controllers/NetworkController.test.ts b/packages/core/src/__tests__/controllers/NetworkController.test.ts index 8ed1d15c..c2090071 100644 --- a/packages/core/src/__tests__/controllers/NetworkController.test.ts +++ b/packages/core/src/__tests__/controllers/NetworkController.test.ts @@ -19,6 +19,7 @@ const client: NetworkControllerClient = { const initialState = { _client: client, supportsAllNetworks: true, + isUnsupportedNetwork: false, smartAccountEnabledNetworks: [] }; @@ -74,13 +75,13 @@ describe('NetworkController', () => { expect(NetworkController.state.requestedCaipNetworks).toEqual(requestedCaipNetworks); }); - it('should validate if active network is in requested networks', () => { - NetworkController.setRequestedCaipNetworks(requestedCaipNetworks); - - NetworkController.setCaipNetwork({ id: 'eip155:1', name: 'Ethereum' }); - expect(NetworkController.isActiveNetworkInRequestedNetworks()).toBe(true); + it('should set isUnsupportedNetwork to true when setUnsupportedNetwork is called', () => { + NetworkController.setUnsupportedNetwork(true); + expect(NetworkController.state.isUnsupportedNetwork).toEqual(true); + }); - NetworkController.setCaipNetwork({ id: 'eip155:99', name: 'Unknown Network' }); - expect(NetworkController.isActiveNetworkInRequestedNetworks()).toBe(false); + it('should set isUnsupportedNetwork to false when setUnsupportedNetwork is called', () => { + NetworkController.setUnsupportedNetwork(false); + expect(NetworkController.state.isUnsupportedNetwork).toEqual(false); }); }); diff --git a/packages/core/src/controllers/NetworkController.ts b/packages/core/src/controllers/NetworkController.ts index 6ec69261..3ca1c9c9 100644 --- a/packages/core/src/controllers/NetworkController.ts +++ b/packages/core/src/controllers/NetworkController.ts @@ -21,13 +21,15 @@ export interface NetworkControllerState { requestedCaipNetworks?: CaipNetwork[]; approvedCaipNetworkIds?: CaipNetworkId[]; smartAccountEnabledNetworks: number[]; + isUnsupportedNetwork?: boolean; } // -- State --------------------------------------------- // const state = proxy({ supportsAllNetworks: true, defaultCaipNetwork: undefined, - smartAccountEnabledNetworks: [] + smartAccountEnabledNetworks: [], + isUnsupportedNetwork: false }); // -- Controller ---------------------------------------- // @@ -57,6 +59,10 @@ export const NetworkController = { PublicStateController.set({ selectedNetworkId: caipNetwork?.id }); }, + setUnsupportedNetwork(isUnsupportedNetwork: NetworkControllerState['isUnsupportedNetwork']) { + state.isUnsupportedNetwork = isUnsupportedNetwork; + }, + setRequestedCaipNetworks(requestedNetworks: NetworkControllerState['requestedCaipNetworks']) { state.requestedCaipNetworks = requestedNetworks; }, @@ -111,16 +117,9 @@ export const NetworkController = { resetNetwork() { state.caipNetwork = state.defaultCaipNetwork || undefined; + state.isUnsupportedNetwork = undefined; state.approvedCaipNetworkIds = undefined; state.supportsAllNetworks = true; state.smartAccountEnabledNetworks = []; - }, - - isActiveNetworkInRequestedNetworks() { - if (!state.caipNetwork || !state.requestedCaipNetworks?.length) { - return false; - } - - return state.requestedCaipNetworks.some(network => network.id === state.caipNetwork?.id); } }; diff --git a/packages/core/src/utils/NetworkUtil.ts b/packages/core/src/utils/NetworkUtil.ts index 4ab2b5b4..ca5490b1 100644 --- a/packages/core/src/utils/NetworkUtil.ts +++ b/packages/core/src/utils/NetworkUtil.ts @@ -10,15 +10,15 @@ export const NetworkUtil = { async handleNetworkSwitch(network: CaipNetwork) { const { isConnected } = AccountController.state; const { caipNetwork, approvedCaipNetworkIds, supportsAllNetworks } = NetworkController.state; - const isAuthConnected = ConnectorController.state.connectedConnector === 'AUTH'; + const isAuthConnector = ConnectorController.state.connectedConnector === 'AUTH'; let eventData = null; if (isConnected && caipNetwork?.id !== network.id) { - if (approvedCaipNetworkIds?.includes(network.id) && !isAuthConnected) { + if (approvedCaipNetworkIds?.includes(network.id) && !isAuthConnector) { await NetworkController.switchActiveNetwork(network); RouterUtil.navigateAfterNetworkSwitch(['ConnectingSiwe']); eventData = { type: 'SWITCH_NETWORK', networkId: network.id }; - } else if (supportsAllNetworks || isAuthConnected) { + } else if (supportsAllNetworks || isAuthConnector) { RouterController.push('SwitchNetwork', { network }); } } else if (!isConnected) { diff --git a/packages/core/src/utils/RouterUtil.ts b/packages/core/src/utils/RouterUtil.ts index c0e61b62..88a31579 100644 --- a/packages/core/src/utils/RouterUtil.ts +++ b/packages/core/src/utils/RouterUtil.ts @@ -8,12 +8,20 @@ export const RouterUtil = { } const { history } = RouterController.state; - const networkSelectIndex = history.findIndex( - name => name === 'Networks' || name === 'UnsupportedChain' - ); - if (networkSelectIndex >= 1) { - RouterController.goBackToIndex(networkSelectIndex - 1); + // Find the last occurrence of 'Networks' or 'UnsupportedChain' + let lastNetworkViewIndex = -1; + for (let i = history.length - 1; i >= 0; i--) { + if (history[i] === 'Networks' || history[i] === 'UnsupportedChain') { + lastNetworkViewIndex = i; + break; + } + } + + // Case 1: Navigated from a network selection view deeper in the app + if (lastNetworkViewIndex > 0) { + // Go to the view right before the network selection + RouterController.goBackToIndex(lastNetworkViewIndex - 1); } else { ModalController.close(); } diff --git a/packages/ethers/src/client.ts b/packages/ethers/src/client.ts index c6204de2..0ad4b34a 100644 --- a/packages/ethers/src/client.ts +++ b/packages/ethers/src/client.ts @@ -766,6 +766,11 @@ export class AppKit extends AppKitScaffold { if (this.chains) { const chain = this.chains.find(c => c.chainId === chainId); + if (isConnected) { + // If the network is not supported, set the unsupported network state + this.setUnsupportedNetwork(!chain); + } + if (chain) { const caipChainId: CaipNetworkId = `${ConstantsUtil.EIP155}:${chain.chainId}`; diff --git a/packages/ethers5/src/client.ts b/packages/ethers5/src/client.ts index c36e0c81..c6daa98c 100644 --- a/packages/ethers5/src/client.ts +++ b/packages/ethers5/src/client.ts @@ -746,6 +746,11 @@ export class AppKit extends AppKitScaffold { if (this.chains) { const chain = this.chains.find(c => c.chainId === chainId); + if (isConnected) { + // If the network is not supported, set the unsupported network state + this.setUnsupportedNetwork(!chain); + } + if (chain) { const caipChainId: CaipNetworkId = `${ConstantsUtil.EIP155}:${chain.chainId}`; diff --git a/packages/scaffold/src/client.ts b/packages/scaffold/src/client.ts index 60daf796..1dc717de 100644 --- a/packages/scaffold/src/client.ts +++ b/packages/scaffold/src/client.ts @@ -274,6 +274,12 @@ export class AppKitScaffold { } } + protected setUnsupportedNetwork( + isUnsupportedNetwork: NetworkControllerState['isUnsupportedNetwork'] + ) { + NetworkController.setUnsupportedNetwork(isUnsupportedNetwork); + } + // -- Private ------------------------------------------------------------------ private async initControllers(options: ScaffoldOptions) { this.initAsyncValues(options); diff --git a/packages/scaffold/src/modal/w3m-modal/index.tsx b/packages/scaffold/src/modal/w3m-modal/index.tsx index 3d0eba33..4113224c 100644 --- a/packages/scaffold/src/modal/w3m-modal/index.tsx +++ b/packages/scaffold/src/modal/w3m-modal/index.tsx @@ -31,7 +31,7 @@ export function AppKit() { const { open, loading } = useSnapshot(ModalController.state); const { connectors, connectedConnector } = useSnapshot(ConnectorController.state); const { caipAddress, isConnected } = useSnapshot(AccountController.state); - const { caipNetwork } = useSnapshot(NetworkController.state); + const { isUnsupportedNetwork } = useSnapshot(NetworkController.state); const { themeMode, themeVariables } = useSnapshot(ThemeController.state); const [isNetworkStateStable, setIsNetworkStateStable] = useState(false); const { height } = useWindowDimensions(); @@ -42,8 +42,21 @@ export function AppKit() { const AuthView = authProvider?.AuthView; const SocialView = authProvider?.Webview; const showAuth = !connectedConnector || connectedConnector === 'AUTH'; + const disableClose = ['UnsupportedChain', 'ConnectingSiwe'].includes(RouterController.state.view); + + const onBackdropPress = () => { + if (disableClose) { + return; + } + + return ModalController.close(); + }; const onBackButtonPress = () => { + if (disableClose) { + return; + } + if (RouterController.state.history.length > 1) { return RouterController.goBack(); } @@ -74,6 +87,12 @@ export function AppKit() { TransactionsController.resetTransactions(); if (OptionsController.state.isSiweEnabled) { + if (NetworkController.state.isUnsupportedNetwork) { + // If the network is unsupported, don't do siwe stuff until user changes network + + return; + } + const newNetworkId = CoreHelperUtil.getNetworkId(address); const { signOutOnAccountChange, signOutOnNetworkChange } = @@ -134,21 +153,14 @@ export function AppKit() { }, [isConnected]); useEffect(() => { - if (isConnected && caipNetwork && isNetworkStateStable) { - const isNetworkSupported = NetworkController.isActiveNetworkInRequestedNetworks(); - if (!isNetworkSupported) { - const currentView = RouterController.state.view; - // Only push/open if not already on UnsupportedChain or actively choosing a network - if (currentView !== 'UnsupportedChain' && currentView !== 'Networks') { - if (ModalController.state.open) { - RouterController.push('UnsupportedChain'); - } else { - ModalController.open({ view: 'UnsupportedChain' }); - } - } + if (isUnsupportedNetwork && isNetworkStateStable) { + if (ModalController.state.open) { + RouterController.reset('UnsupportedChain'); + } else { + ModalController.open({ view: 'UnsupportedChain' }); } } - }, [caipNetwork, isConnected, isNetworkStateStable]); + }, [isUnsupportedNetwork, isNetworkStateStable]); return ( <> @@ -163,7 +175,7 @@ export function AppKit() { hideModalContentWhileAnimating propagateSwipe onModalHide={handleClose} - onBackdropPress={ModalController.close} + onBackdropPress={onBackdropPress} onBackButtonPress={onBackButtonPress} testID="w3m-modal" > diff --git a/packages/scaffold/src/modal/w3m-network-button/index.tsx b/packages/scaffold/src/modal/w3m-network-button/index.tsx index 066cee54..b23cdd48 100644 --- a/packages/scaffold/src/modal/w3m-network-button/index.tsx +++ b/packages/scaffold/src/modal/w3m-network-button/index.tsx @@ -23,10 +23,8 @@ export function NetworkButton({ disabled, style }: NetworkButtonProps) { const { isConnected } = useSnapshot(AccountController.state); const { themeMode, themeVariables } = useSnapshot(ThemeController.state); - const isNetworkSupported = NetworkController.isActiveNetworkInRequestedNetworks(); - const onNetworkPress = () => { - if (AccountController.state.isConnected && !isNetworkSupported) { + if (AccountController.state.isConnected && NetworkController.state.isUnsupportedNetwork) { ModalController.open({ view: 'UnsupportedChain' }); } else { ModalController.open({ view: 'Networks' }); @@ -37,7 +35,11 @@ export function NetworkButton({ disabled, style }: NetworkButtonProps) { }); }; - const buttonText = UiUtil.getNetworkButtonText(isConnected, caipNetwork, isNetworkSupported); + const buttonText = UiUtil.getNetworkButtonText( + isConnected, + caipNetwork, + NetworkController.state.isUnsupportedNetwork + ); return ( diff --git a/packages/scaffold/src/partials/w3m-header/index.tsx b/packages/scaffold/src/partials/w3m-header/index.tsx index 33d0b88f..d352df5b 100644 --- a/packages/scaffold/src/partials/w3m-header/index.tsx +++ b/packages/scaffold/src/partials/w3m-header/index.tsx @@ -19,6 +19,7 @@ export function Header() { RouterController.push('WhatIsAWallet'); EventsController.sendEvent({ type: 'track', event: 'CLICK_WALLET_HELP' }); }; + const showButtons = !['ConnectingSiwe', 'UnsupportedChain'].includes(view); const headings = (_data: RouterControllerState['data'], _view: RouterControllerState['view']) => { const connectorName = _data?.connector?.name; @@ -100,9 +101,7 @@ export function Header() { }; const dynamicButtonTemplate = () => { - const noButtonViews = ['ConnectingSiwe']; - - if (noButtonViews.includes(RouterController.state.view)) { + if (!showButtons) { return ; } @@ -130,7 +129,11 @@ export function Header() { {header} - + {showButtons ? ( + + ) : ( + + )} ); } diff --git a/packages/scaffold/src/utils/UiUtil.ts b/packages/scaffold/src/utils/UiUtil.ts index 3cd459aa..3614a322 100644 --- a/packages/scaffold/src/utils/UiUtil.ts +++ b/packages/scaffold/src/utils/UiUtil.ts @@ -33,7 +33,7 @@ export const UiUtil = { getNetworkButtonText( isConnected: boolean, caipNetwork: CaipNetwork | undefined, - isNetworkSupported: boolean + isUnsupportedNetwork?: boolean ): string { let buttonText: string; @@ -46,10 +46,10 @@ export const UiUtil = { } else { // isConnected is true if (caipNetwork) { - if (isNetworkSupported) { - buttonText = caipNetwork.name ?? 'Unknown Network'; - } else { + if (isUnsupportedNetwork) { buttonText = 'Switch Network'; + } else { + buttonText = caipNetwork.name ?? 'Unknown Network'; } } else { buttonText = 'Select Network'; diff --git a/packages/scaffold/src/views/w3m-account-default-view/index.tsx b/packages/scaffold/src/views/w3m-account-default-view/index.tsx index 9ca4a75e..ce887230 100644 --- a/packages/scaffold/src/views/w3m-account-default-view/index.tsx +++ b/packages/scaffold/src/views/w3m-account-default-view/index.tsx @@ -48,7 +48,7 @@ export function AccountDefaultView() { } = useSnapshot(AccountController.state); const { loading } = useSnapshot(ModalController.state); const [disconnecting, setDisconnecting] = useState(false); - const { caipNetwork } = useSnapshot(NetworkController.state); + const { caipNetwork, isUnsupportedNetwork } = useSnapshot(NetworkController.state); const { connectedConnector } = useSnapshot(ConnectorController.state); const { connectedSocialProvider } = useSnapshot(ConnectionController.state); const { features } = useSnapshot(OptionsController.state); @@ -61,7 +61,6 @@ export function AccountDefaultView() { const showBack = history.length > 1; const showSwitchAccountType = isAuth && NetworkController.checkIfSmartAccountEnabled(); const { padding } = useCustomDimensions(); - const isNetworkSupported = NetworkController.isActiveNetworkInRequestedNetworks(); async function onDisconnect() { setDisconnecting(true); @@ -149,7 +148,7 @@ export function AccountDefaultView() { }; const onNetworkPress = () => { - if (AccountController.state.isConnected && !isNetworkSupported) { + if (AccountController.state.isConnected && NetworkController.state.isUnsupportedNetwork) { RouterController.push('UnsupportedChain'); } else { RouterController.push('Networks'); @@ -245,17 +244,25 @@ export function AccountDefaultView() { )} - - {ScaffoldUiUtil.getNetworkButtonText(isConnected, caipNetwork, isNetworkSupported)} + + {ScaffoldUiUtil.getNetworkButtonText( + isConnected, + caipNetwork, + isUnsupportedNetwork + )} diff --git a/packages/scaffold/src/views/w3m-account-view/index.tsx b/packages/scaffold/src/views/w3m-account-view/index.tsx index f16a4c3e..7cbb0634 100644 --- a/packages/scaffold/src/views/w3m-account-view/index.tsx +++ b/packages/scaffold/src/views/w3m-account-view/index.tsx @@ -39,9 +39,7 @@ export function AccountView() { }; const onNetworkPress = () => { - const isNetworkSupported = NetworkController.isActiveNetworkInRequestedNetworks(); - - if (AccountController.state.isConnected && !isNetworkSupported) { + if (AccountController.state.isConnected && NetworkController.state.isUnsupportedNetwork) { RouterController.push('UnsupportedChain'); } else { RouterController.push('Networks'); diff --git a/packages/scaffold/src/views/w3m-unsupported-chain-view/index.tsx b/packages/scaffold/src/views/w3m-unsupported-chain-view/index.tsx index 1dce52e1..61ae9d67 100644 --- a/packages/scaffold/src/views/w3m-unsupported-chain-view/index.tsx +++ b/packages/scaffold/src/views/w3m-unsupported-chain-view/index.tsx @@ -1,5 +1,5 @@ import { useSnapshot } from 'valtio'; -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import { FlatList } from 'react-native'; import { Icon, ListItem, Separator, Text } from '@reown/appkit-ui-react-native'; import { @@ -11,19 +11,29 @@ import { NetworkController, NetworkUtil, type CaipNetwork, - type NetworkControllerState + type NetworkControllerState, + ModalController } from '@reown/appkit-core-react-native'; import styles from './styles'; export function UnsupportedChainView() { - const { caipNetwork, supportsAllNetworks, approvedCaipNetworkIds, requestedCaipNetworks } = - useSnapshot(NetworkController.state) as NetworkControllerState; + const { + caipNetwork, + supportsAllNetworks, + approvedCaipNetworkIds, + requestedCaipNetworks, + isUnsupportedNetwork + } = useSnapshot(NetworkController.state) as NetworkControllerState; const [disconnecting, setDisconnecting] = useState(false); const networks = CoreHelperUtil.sortNetworks(approvedCaipNetworkIds, requestedCaipNetworks); const imageHeaders = ApiController._getApiHeaders(); const onNetworkPress = async (network: CaipNetwork) => { + if (caipNetwork?.id === network.id) { + return ModalController.close(); + } + const result = await NetworkUtil.handleNetworkSwitch(network); if (result?.type === 'SWITCH_NETWORK') { EventsController.sendEvent({ @@ -42,6 +52,12 @@ export function UnsupportedChainView() { setDisconnecting(false); }; + useEffect(() => { + if (!isUnsupportedNetwork) { + ModalController.close(); + } + }, [isUnsupportedNetwork]); + return ( - Sign in diff --git a/packages/ui/src/utils/TypesUtil.ts b/packages/ui/src/utils/TypesUtil.ts index 151cc8e5..a4334534 100644 --- a/packages/ui/src/utils/TypesUtil.ts +++ b/packages/ui/src/utils/TypesUtil.ts @@ -113,6 +113,10 @@ export type ColorType = | 'gray-glass-010' | 'gray-glass-005' | 'gray-glass-002' + | 'error-glass-020' + | 'error-glass-015' + | 'error-glass-010' + | 'error-glass-005' | 'inverse-000' | 'inverse-100' | 'success-100' diff --git a/packages/wagmi/src/client.ts b/packages/wagmi/src/client.ts index e1851f10..1d08acac 100644 --- a/packages/wagmi/src/client.ts +++ b/packages/wagmi/src/client.ts @@ -246,10 +246,6 @@ export class AppKit extends AppKitScaffold { disconnect: async () => { await disconnect(this.wagmiConfig); this.setClientId(null); - - if (siweConfig?.options?.signOutOnDisconnect) { - await SIWEController.signOut(); - } }, sendTransaction: async (data: SendTransactionArgs) => { @@ -383,6 +379,7 @@ export class AppKit extends AppKitScaffold { this.syncAccount({ ...accountData }); if (accountData.status === 'disconnected' && prevAccountData.status === 'connected') { + this.onSiweDisconnect(); this.close(); } } @@ -460,6 +457,11 @@ export class AppKit extends AppKitScaffold { private async syncNetwork(address?: Hex, chainId?: number, isConnected?: boolean) { const chain = this.wagmiConfig.chains.find((c: Chain) => c.id === chainId); + if (isConnected) { + // If the network is not supported, set the unsupported network state + this.setUnsupportedNetwork(!chain); + } + if (chain || chainId) { const name = chain?.name ?? chainId?.toString(); const id = Number(chain?.id ?? chainId); @@ -471,16 +473,6 @@ export class AppKit extends AppKitScaffold { imageUrl: this.options?.chainImages?.[id] }); - const internalState = this.wagmiConfig.chains || []; - const isNetworkSupported = - internalState.length === 0 || - internalState.some( - (chainItem: Chain) => `${ConstantsUtil.EIP155}:${chainItem.id}` === caipChainId - ); - if (!isNetworkSupported) { - console.warn(`Network ${caipChainId} is not in the requested networks list`); - } - if (isConnected && address && chainId) { const caipAddress: CaipAddress = `${ConstantsUtil.EIP155}:${id}:${address}`; this.setCaipAddress(caipAddress); @@ -652,4 +644,10 @@ export class AppKit extends AppKitScaffold { this.setLoading(false); }); } + + private async onSiweDisconnect() { + if (this.options?.siweConfig?.options?.signOutOnDisconnect) { + await SIWEController.signOut(); + } + } } From 2ff7c813143e35488d5cbace43eaa4fa6cd1a527 Mon Sep 17 00:00:00 2001 From: nacho <25931366+ignaciosantise@users.noreply.github.com> Date: Mon, 26 May 2025 10:33:12 -0300 Subject: [PATCH 10/17] chore: set unsupported network on ethers --- packages/ethers/src/client.ts | 19 +++++++++++++------ packages/ethers5/src/client.ts | 19 +++++++++++++------ .../scaffold/src/modal/w3m-modal/index.tsx | 7 ++++--- 3 files changed, 30 insertions(+), 15 deletions(-) diff --git a/packages/ethers/src/client.ts b/packages/ethers/src/client.ts index 0ad4b34a..753d37e3 100644 --- a/packages/ethers/src/client.ts +++ b/packages/ethers/src/client.ts @@ -766,14 +766,10 @@ export class AppKit extends AppKitScaffold { if (this.chains) { const chain = this.chains.find(c => c.chainId === chainId); - if (isConnected) { - // If the network is not supported, set the unsupported network state - this.setUnsupportedNetwork(!chain); - } - + //Supported network if (chain) { const caipChainId: CaipNetworkId = `${ConstantsUtil.EIP155}:${chain.chainId}`; - + this.setUnsupportedNetwork(false); this.setCaipNetwork({ id: caipChainId, name: chain.name, @@ -794,6 +790,17 @@ export class AppKit extends AppKitScaffold { await this.syncBalance(address); } } + } else { + //Unsupported network + if (isConnected) { + this.setUnsupportedNetwork(true); + this.setCaipNetwork({ + id: `${ConstantsUtil.EIP155}:${chainId}`, + name: 'Unsupported Network' + }); + this.setAddressExplorerUrl(undefined); + this.setBalance(undefined, undefined); + } } } } diff --git a/packages/ethers5/src/client.ts b/packages/ethers5/src/client.ts index c6daa98c..f78b38da 100644 --- a/packages/ethers5/src/client.ts +++ b/packages/ethers5/src/client.ts @@ -746,14 +746,10 @@ export class AppKit extends AppKitScaffold { if (this.chains) { const chain = this.chains.find(c => c.chainId === chainId); - if (isConnected) { - // If the network is not supported, set the unsupported network state - this.setUnsupportedNetwork(!chain); - } - + //Supported network if (chain) { const caipChainId: CaipNetworkId = `${ConstantsUtil.EIP155}:${chain.chainId}`; - + this.setUnsupportedNetwork(false); this.setCaipNetwork({ id: caipChainId, name: chain.name, @@ -774,6 +770,17 @@ export class AppKit extends AppKitScaffold { await this.syncBalance(address); } } + } else { + //Unsupported network + if (isConnected) { + this.setUnsupportedNetwork(true); + this.setCaipNetwork({ + id: `${ConstantsUtil.EIP155}:${chainId}`, + name: 'Unsupported Network' + }); + this.setAddressExplorerUrl(undefined); + this.setBalance(undefined, undefined); + } } } } diff --git a/packages/scaffold/src/modal/w3m-modal/index.tsx b/packages/scaffold/src/modal/w3m-modal/index.tsx index 4113224c..cb8d38f1 100644 --- a/packages/scaffold/src/modal/w3m-modal/index.tsx +++ b/packages/scaffold/src/modal/w3m-modal/index.tsx @@ -27,6 +27,8 @@ import { Snackbar } from '../../partials/w3m-snackbar'; import { useCustomDimensions } from '../../hooks/useCustomDimensions'; import styles from './styles'; +const disableCloseViews = ['UnsupportedChain', 'ConnectingSiwe']; + export function AppKit() { const { open, loading } = useSnapshot(ModalController.state); const { connectors, connectedConnector } = useSnapshot(ConnectorController.state); @@ -42,10 +44,9 @@ export function AppKit() { const AuthView = authProvider?.AuthView; const SocialView = authProvider?.Webview; const showAuth = !connectedConnector || connectedConnector === 'AUTH'; - const disableClose = ['UnsupportedChain', 'ConnectingSiwe'].includes(RouterController.state.view); const onBackdropPress = () => { - if (disableClose) { + if (disableCloseViews.includes(RouterController.state.view)) { return; } @@ -53,7 +54,7 @@ export function AppKit() { }; const onBackButtonPress = () => { - if (disableClose) { + if (disableCloseViews.includes(RouterController.state.view)) { return; } From 89bec04a3daebdd4c84cbcd5a7515b45f9abca8b Mon Sep 17 00:00:00 2001 From: nacho <25931366+ignaciosantise@users.noreply.github.com> Date: Mon, 26 May 2025 13:24:43 -0300 Subject: [PATCH 11/17] chore: check namespaces approved networks to set unsupported flags --- packages/core/src/utils/NetworkUtil.ts | 14 +++- packages/core/src/utils/RouterUtil.ts | 25 +----- packages/ethers/src/client.ts | 76 ++++++++++--------- packages/ethers5/src/client.ts | 14 ++-- packages/scaffold/src/client.ts | 3 + .../scaffold/src/modal/w3m-modal/index.tsx | 2 +- .../views/w3m-network-switch-view/index.tsx | 2 +- .../w3m-unsupported-chain-view/index.tsx | 7 +- packages/siwe/src/client.ts | 4 +- packages/wagmi/src/client.ts | 18 +++-- 10 files changed, 85 insertions(+), 80 deletions(-) diff --git a/packages/core/src/utils/NetworkUtil.ts b/packages/core/src/utils/NetworkUtil.ts index ca5490b1..5fe087c0 100644 --- a/packages/core/src/utils/NetworkUtil.ts +++ b/packages/core/src/utils/NetworkUtil.ts @@ -7,7 +7,7 @@ import { SwapController } from '../controllers/SwapController'; import type { CaipNetwork } from '../utils/TypeUtil'; export const NetworkUtil = { - async handleNetworkSwitch(network: CaipNetwork) { + async handleNetworkSwitch(network: CaipNetwork, navigate = true) { const { isConnected } = AccountController.state; const { caipNetwork, approvedCaipNetworkIds, supportsAllNetworks } = NetworkController.state; const isAuthConnector = ConnectorController.state.connectedConnector === 'AUTH'; @@ -16,14 +16,20 @@ export const NetworkUtil = { if (isConnected && caipNetwork?.id !== network.id) { if (approvedCaipNetworkIds?.includes(network.id) && !isAuthConnector) { await NetworkController.switchActiveNetwork(network); - RouterUtil.navigateAfterNetworkSwitch(['ConnectingSiwe']); + if (navigate) { + RouterUtil.goBackOrCloseModal(); + } eventData = { type: 'SWITCH_NETWORK', networkId: network.id }; } else if (supportsAllNetworks || isAuthConnector) { - RouterController.push('SwitchNetwork', { network }); + if (navigate) { + RouterController.push('SwitchNetwork', { network }); + } } } else if (!isConnected) { NetworkController.setCaipNetwork(network); - RouterController.push('Connect'); + if (navigate) { + RouterController.push('Connect'); + } } SwapController.resetState(); diff --git a/packages/core/src/utils/RouterUtil.ts b/packages/core/src/utils/RouterUtil.ts index 88a31579..d52b844b 100644 --- a/packages/core/src/utils/RouterUtil.ts +++ b/packages/core/src/utils/RouterUtil.ts @@ -1,27 +1,10 @@ -import { RouterController, type RouterControllerState } from '../controllers/RouterController'; +import { RouterController } from '../controllers/RouterController'; import { ModalController } from '../controllers/ModalController'; export const RouterUtil = { - navigateAfterNetworkSwitch(excludeViews: RouterControllerState['view'][] = []) { - if (excludeViews.includes(RouterController.state.view)) { - return; - } - - const { history } = RouterController.state; - - // Find the last occurrence of 'Networks' or 'UnsupportedChain' - let lastNetworkViewIndex = -1; - for (let i = history.length - 1; i >= 0; i--) { - if (history[i] === 'Networks' || history[i] === 'UnsupportedChain') { - lastNetworkViewIndex = i; - break; - } - } - - // Case 1: Navigated from a network selection view deeper in the app - if (lastNetworkViewIndex > 0) { - // Go to the view right before the network selection - RouterController.goBackToIndex(lastNetworkViewIndex - 1); + goBackOrCloseModal() { + if (RouterController.state.history.length > 1) { + RouterController.goBack(); } else { ModalController.close(); } diff --git a/packages/ethers/src/client.ts b/packages/ethers/src/client.ts index 753d37e3..b7686133 100644 --- a/packages/ethers/src/client.ts +++ b/packages/ethers/src/client.ts @@ -745,11 +745,7 @@ export class AppKit extends AppKitScaffold { this.setCaipAddress(caipAddress); - await Promise.all([ - this.syncProfile(address), - this.syncBalance(address), - this.getApprovedCaipNetworksData() - ]); + await Promise.all([this.syncProfile(address), this.syncBalance(address)]); this.hasSyncedConnectedAccount = true; } else if (!isConnected && this.hasSyncedConnectedAccount) { this.close(); @@ -765,41 +761,49 @@ export class AppKit extends AppKitScaffold { const isConnected = EthersStoreUtil.state.isConnected; if (this.chains) { const chain = this.chains.find(c => c.chainId === chainId); + if (isConnected) { + await this.getApprovedCaipNetworksData(); + const approvedCaipNetworks = this.getApprovedCaipNetworks(); - //Supported network - if (chain) { - const caipChainId: CaipNetworkId = `${ConstantsUtil.EIP155}:${chain.chainId}`; - this.setUnsupportedNetwork(false); - this.setCaipNetwork({ - id: caipChainId, - name: chain.name, - imageId: PresetsUtil.EIP155NetworkImageIds[chain.chainId], - imageUrl: chainImages?.[chain.chainId] - }); - if (isConnected && address) { - const caipAddress: CaipAddress = `${ConstantsUtil.EIP155}:${chainId}:${address}`; - this.setCaipAddress(caipAddress); - if (chain.explorerUrl) { - const url = `${chain.explorerUrl}/address/${address}`; - this.setAddressExplorerUrl(url); - } else { - this.setAddressExplorerUrl(undefined); - } + const isApproved = approvedCaipNetworks.some( + network => network.id === `${ConstantsUtil.EIP155}:${chainId}` + ); - if (this.hasSyncedConnectedAccount) { - await this.syncBalance(address); - } - } - } else { - //Unsupported network - if (isConnected) { - this.setUnsupportedNetwork(true); + //Supported network + if (chain) { + const caipChainId: CaipNetworkId = `${ConstantsUtil.EIP155}:${chain.chainId}`; + this.setUnsupportedNetwork(!isApproved); this.setCaipNetwork({ - id: `${ConstantsUtil.EIP155}:${chainId}`, - name: 'Unsupported Network' + id: caipChainId, + name: chain.name, + imageId: PresetsUtil.EIP155NetworkImageIds[chain.chainId], + imageUrl: chainImages?.[chain.chainId] }); - this.setAddressExplorerUrl(undefined); - this.setBalance(undefined, undefined); + if (isConnected && address) { + const caipAddress: CaipAddress = `${ConstantsUtil.EIP155}:${chainId}:${address}`; + this.setCaipAddress(caipAddress); + if (chain.explorerUrl) { + const url = `${chain.explorerUrl}/address/${address}`; + this.setAddressExplorerUrl(url); + } else { + this.setAddressExplorerUrl(undefined); + } + + if (this.hasSyncedConnectedAccount) { + await this.syncBalance(address); + } + } + } else { + //Unsupported network + if (isConnected) { + this.setUnsupportedNetwork(true); + this.setCaipNetwork({ + id: `${ConstantsUtil.EIP155}:${chainId}`, + name: 'Unsupported Network' + }); + this.setAddressExplorerUrl(undefined); + this.setBalance(undefined, undefined); + } } } } diff --git a/packages/ethers5/src/client.ts b/packages/ethers5/src/client.ts index f78b38da..4b14023e 100644 --- a/packages/ethers5/src/client.ts +++ b/packages/ethers5/src/client.ts @@ -725,11 +725,7 @@ export class AppKit extends AppKitScaffold { this.setCaipAddress(caipAddress); - await Promise.all([ - this.syncProfile(address), - this.syncBalance(address), - this.getApprovedCaipNetworksData() - ]); + await Promise.all([this.syncProfile(address), this.syncBalance(address)]); this.hasSyncedConnectedAccount = true; } else if (!isConnected && this.hasSyncedConnectedAccount) { this.close(); @@ -745,11 +741,17 @@ export class AppKit extends AppKitScaffold { const isConnected = EthersStoreUtil.state.isConnected; if (this.chains) { const chain = this.chains.find(c => c.chainId === chainId); + await this.getApprovedCaipNetworksData(); + const approvedCaipNetworks = this.getApprovedCaipNetworks(); + + const isApproved = approvedCaipNetworks.some( + network => network.id === `${ConstantsUtil.EIP155}:${chainId}` + ); //Supported network if (chain) { const caipChainId: CaipNetworkId = `${ConstantsUtil.EIP155}:${chain.chainId}`; - this.setUnsupportedNetwork(false); + this.setUnsupportedNetwork(!isApproved); this.setCaipNetwork({ id: caipChainId, name: chain.name, diff --git a/packages/scaffold/src/client.ts b/packages/scaffold/src/client.ts index 1dc717de..d8880024 100644 --- a/packages/scaffold/src/client.ts +++ b/packages/scaffold/src/client.ts @@ -200,6 +200,9 @@ export class AppKitScaffold { protected getApprovedCaipNetworksData: (typeof NetworkController)['getApprovedCaipNetworksData'] = () => NetworkController.getApprovedCaipNetworksData(); + protected getApprovedCaipNetworks: (typeof NetworkController)['getApprovedCaipNetworks'] = () => + NetworkController.getApprovedCaipNetworks(); + protected resetNetwork: (typeof NetworkController)['resetNetwork'] = () => { NetworkController.resetNetwork(); }; diff --git a/packages/scaffold/src/modal/w3m-modal/index.tsx b/packages/scaffold/src/modal/w3m-modal/index.tsx index cb8d38f1..61cca3c8 100644 --- a/packages/scaffold/src/modal/w3m-modal/index.tsx +++ b/packages/scaffold/src/modal/w3m-modal/index.tsx @@ -156,7 +156,7 @@ export function AppKit() { useEffect(() => { if (isUnsupportedNetwork && isNetworkStateStable) { if (ModalController.state.open) { - RouterController.reset('UnsupportedChain'); + RouterController.push('UnsupportedChain'); } else { ModalController.open({ view: 'UnsupportedChain' }); } diff --git a/packages/scaffold/src/views/w3m-network-switch-view/index.tsx b/packages/scaffold/src/views/w3m-network-switch-view/index.tsx index 8adf0455..8a810da6 100644 --- a/packages/scaffold/src/views/w3m-network-switch-view/index.tsx +++ b/packages/scaffold/src/views/w3m-network-switch-view/index.tsx @@ -56,7 +56,7 @@ export function NetworkSwitchView() { useEffect(() => { // Go back if network is already switched if (caipNetwork?.id === network?.id) { - RouterUtil.navigateAfterNetworkSwitch(); + RouterUtil.goBackOrCloseModal(); } }, [caipNetwork?.id, network?.id]); diff --git a/packages/scaffold/src/views/w3m-unsupported-chain-view/index.tsx b/packages/scaffold/src/views/w3m-unsupported-chain-view/index.tsx index 61ae9d67..f46f291c 100644 --- a/packages/scaffold/src/views/w3m-unsupported-chain-view/index.tsx +++ b/packages/scaffold/src/views/w3m-unsupported-chain-view/index.tsx @@ -12,7 +12,8 @@ import { NetworkUtil, type CaipNetwork, type NetworkControllerState, - ModalController + ModalController, + RouterUtil } from '@reown/appkit-core-react-native'; import styles from './styles'; @@ -34,7 +35,7 @@ export function UnsupportedChainView() { return ModalController.close(); } - const result = await NetworkUtil.handleNetworkSwitch(network); + const result = await NetworkUtil.handleNetworkSwitch(network, false); if (result?.type === 'SWITCH_NETWORK') { EventsController.sendEvent({ type: 'track', @@ -54,7 +55,7 @@ export function UnsupportedChainView() { useEffect(() => { if (!isUnsupportedNetwork) { - ModalController.close(); + RouterUtil.goBackOrCloseModal(); } }, [isUnsupportedNetwork]); diff --git a/packages/siwe/src/client.ts b/packages/siwe/src/client.ts index c0c28441..b893e252 100644 --- a/packages/siwe/src/client.ts +++ b/packages/siwe/src/client.ts @@ -2,7 +2,7 @@ import { AccountController, NetworkController, ConnectionController, - RouterUtil + ModalController } from '@reown/appkit-core-react-native'; import { NetworkUtil } from '@reown/appkit-common-react-native'; @@ -120,7 +120,7 @@ export class AppKitSIWEClient { this.methods.onSignIn(session); } - RouterUtil.navigateAfterNetworkSwitch(); + ModalController.close(); return session; } diff --git a/packages/wagmi/src/client.ts b/packages/wagmi/src/client.ts index 1d08acac..446c2eb3 100644 --- a/packages/wagmi/src/client.ts +++ b/packages/wagmi/src/client.ts @@ -318,9 +318,7 @@ export class AppKit extends AppKitScaffold { getEnsAddress: async (value: string) => { try { if (!this.wagmiConfig) { - throw new Error( - 'networkControllerClient:getApprovedCaipNetworksData - wagmiConfig is undefined' - ); + throw new Error('WagmiAdapter:getEnsAddress - wagmiConfig is undefined'); } const chainId = Number(NetworkUtil.caipNetworkIdToNumber(this.getCaipNetwork()?.id)); let ensName: boolean | GetEnsAddressReturnType = false; @@ -443,8 +441,7 @@ export class AppKit extends AppKitScaffold { await Promise.all([ this.syncProfile(address, chainId), this.syncBalance(address, chainId), - this.syncConnectedWalletInfo(connector), - this.getApprovedCaipNetworksData() + this.syncConnectedWalletInfo(connector) ]); this.hasSyncedConnectedAccount = true; } else if (!isConnected && !isConnecting && !isReconnecting && this.hasSyncedConnectedAccount) { @@ -458,8 +455,17 @@ export class AppKit extends AppKitScaffold { const chain = this.wagmiConfig.chains.find((c: Chain) => c.id === chainId); if (isConnected) { + await this.getApprovedCaipNetworksData(); + const approvedCaipNetworks = this.getApprovedCaipNetworks(); + + const isApproved = approvedCaipNetworks.some( + network => network.id === `${ConstantsUtil.EIP155}:${chainId}` + ); + + const isSupported = !!chain && isApproved; + // If the network is not supported, set the unsupported network state - this.setUnsupportedNetwork(!chain); + this.setUnsupportedNetwork(!isSupported); } if (chain || chainId) { From 8d8b931ce4590bb9644f77249c76aeb497affe55 Mon Sep 17 00:00:00 2001 From: nacho <25931366+ignaciosantise@users.noreply.github.com> Date: Tue, 27 May 2025 11:24:43 -0300 Subject: [PATCH 12/17] chore: ethers improvements --- .../core/src/controllers/NetworkController.ts | 4 +- packages/core/src/utils/NetworkUtil.ts | 14 +- packages/ethers/src/client.ts | 171 ++++++++---------- packages/ethers5/src/client.ts | 98 ++++------ .../scaffold/src/modal/w3m-modal/index.tsx | 4 +- 5 files changed, 124 insertions(+), 167 deletions(-) diff --git a/packages/core/src/controllers/NetworkController.ts b/packages/core/src/controllers/NetworkController.ts index 3ca1c9c9..a3517f06 100644 --- a/packages/core/src/controllers/NetworkController.ts +++ b/packages/core/src/controllers/NetworkController.ts @@ -90,9 +90,11 @@ export const NetworkController = { }, getApprovedCaipNetworks() { - return state.approvedCaipNetworkIds + const networks = state.approvedCaipNetworkIds ?.map(id => state.requestedCaipNetworks?.find(network => network.id === id)) .filter(Boolean) as CaipNetwork[]; + + return networks ?? []; }, getSmartAccountEnabledNetworks() { diff --git a/packages/core/src/utils/NetworkUtil.ts b/packages/core/src/utils/NetworkUtil.ts index 5fe087c0..0aba7c93 100644 --- a/packages/core/src/utils/NetworkUtil.ts +++ b/packages/core/src/utils/NetworkUtil.ts @@ -7,7 +7,7 @@ import { SwapController } from '../controllers/SwapController'; import type { CaipNetwork } from '../utils/TypeUtil'; export const NetworkUtil = { - async handleNetworkSwitch(network: CaipNetwork, navigate = true) { + async handleNetworkSwitch(network: CaipNetwork) { const { isConnected } = AccountController.state; const { caipNetwork, approvedCaipNetworkIds, supportsAllNetworks } = NetworkController.state; const isAuthConnector = ConnectorController.state.connectedConnector === 'AUTH'; @@ -16,20 +16,14 @@ export const NetworkUtil = { if (isConnected && caipNetwork?.id !== network.id) { if (approvedCaipNetworkIds?.includes(network.id) && !isAuthConnector) { await NetworkController.switchActiveNetwork(network); - if (navigate) { - RouterUtil.goBackOrCloseModal(); - } + RouterUtil.goBackOrCloseModal(); eventData = { type: 'SWITCH_NETWORK', networkId: network.id }; } else if (supportsAllNetworks || isAuthConnector) { - if (navigate) { - RouterController.push('SwitchNetwork', { network }); - } + RouterController.push('SwitchNetwork', { network }); } } else if (!isConnected) { NetworkController.setCaipNetwork(network); - if (navigate) { - RouterController.push('Connect'); - } + RouterController.push('Connect'); } SwapController.resetState(); diff --git a/packages/ethers/src/client.ts b/packages/ethers/src/client.ts index b7686133..364a6e78 100644 --- a/packages/ethers/src/client.ts +++ b/packages/ethers/src/client.ts @@ -103,6 +103,12 @@ export class AppKit extends AppKitScaffold { private authProvider?: AppKitFrameProvider; + private providerHandlers: { + disconnect: () => void; + accountsChanged: (accounts: string[]) => void; + chainChanged: (chainId: string) => void; + } | null = null; + public constructor(options: AppKitClientOptions) { const { config, @@ -248,6 +254,7 @@ export class AppKit extends AppKitScaffold { await this.setCoinbaseProvider(coinbaseProvider as Provider); } catch (error) { EthersStoreUtil.setError(error); + throw error; } } else if (id === ConstantsUtil.AUTH_CONNECTOR_ID) { await this.setAuthProvider(); @@ -609,7 +616,6 @@ export class AppKit extends AppKitScaffold { if (walletId === ConstantsUtil.COINBASE_CONNECTOR_ID) { if (CoinbaseProvider.address) { await this.setCoinbaseProvider(provider); - await this.watchCoinbase(provider); } else { await StorageUtil.removeItem(EthersConstantsUtil.WALLET_ID); EthersStoreUtil.reset(); @@ -623,12 +629,12 @@ export class AppKit extends AppKitScaffold { const WalletConnectProvider = await this.getWalletConnectProvider(); if (WalletConnectProvider) { const providerType = PresetsUtil.ConnectorTypesMap[ConstantsUtil.WALLET_CONNECT_CONNECTOR_ID]; - EthersStoreUtil.setChainId(WalletConnectProvider.chainId); EthersStoreUtil.setProviderType(providerType); EthersStoreUtil.setProvider(WalletConnectProvider as unknown as Provider); EthersStoreUtil.setIsConnected(true); + EthersStoreUtil.setChainId(WalletConnectProvider.chainId); this.setAddress(WalletConnectProvider.accounts?.[0]); - await this.watchWalletConnect(); + this.listenProviderEvents(WalletConnectProvider as unknown as Provider); } } @@ -639,12 +645,12 @@ export class AppKit extends AppKitScaffold { const { address, chainId } = await EthersHelpersUtil.getUserInfo(provider); if (address && chainId) { const providerType = PresetsUtil.ConnectorTypesMap[ConstantsUtil.COINBASE_CONNECTOR_ID]; - EthersStoreUtil.setChainId(chainId); EthersStoreUtil.setProviderType(providerType); EthersStoreUtil.setProvider(provider); EthersStoreUtil.setIsConnected(true); this.setAddress(address); - await this.watchCoinbase(provider); + EthersStoreUtil.setChainId(chainId); + this.listenProviderEvents(provider); } } } @@ -656,81 +662,57 @@ export class AppKit extends AppKitScaffold { const { address, chainId } = await this.authProvider.connect(); super.setLoading(false); if (address && chainId) { - EthersStoreUtil.setChainId(chainId); EthersStoreUtil.setProviderType( PresetsUtil.ConnectorTypesMap[ConstantsUtil.AUTH_CONNECTOR_ID] ); EthersStoreUtil.setProvider(this.authProvider as CombinedProviderType); EthersStoreUtil.setIsConnected(true); EthersStoreUtil.setAddress(address as Address); + EthersStoreUtil.setChainId(chainId); } } } - private async watchWalletConnect() { - const WalletConnectProvider = await this.getWalletConnectProvider(); - - function disconnectHandler() { + private listenProviderEvents(provider: Provider) { + const disconnectHandler = () => { + this.removeProviderListeners(provider); StorageUtil.removeItem(EthersConstantsUtil.WALLET_ID); EthersStoreUtil.reset(); - - WalletConnectProvider?.removeListener('disconnect', disconnectHandler); - WalletConnectProvider?.removeListener('accountsChanged', accountsChangedHandler); - WalletConnectProvider?.removeListener('chainChanged', chainChangedHandler); - } - - function chainChangedHandler(chainId: string) { - if (chainId) { - const chain = EthersHelpersUtil.hexStringToNumber(chainId); - EthersStoreUtil.setChainId(chain); - } - } - - const accountsChangedHandler = async (accounts: string[]) => { - if (accounts.length > 0) { - await this.setWalletConnectProvider(); - } + this.setClientId(null); }; - if (WalletConnectProvider) { - WalletConnectProvider.on('disconnect', disconnectHandler); - WalletConnectProvider.on('accountsChanged', accountsChangedHandler); - WalletConnectProvider.on('chainChanged', chainChangedHandler); - } - } - - private async watchCoinbase(provider: Provider) { - const walletId = await StorageUtil.getItem(EthersConstantsUtil.WALLET_ID); - - function disconnectHandler() { - StorageUtil.removeItem(EthersConstantsUtil.WALLET_ID); - EthersStoreUtil.reset(); - - provider?.removeListener('disconnect', disconnectHandler); - provider?.removeListener('accountsChanged', accountsChangedHandler); - provider?.removeListener('chainChanged', chainChangedHandler); - } - - function accountsChangedHandler(accounts: string[]) { + const accountsChangedHandler = (accounts: string[]) => { if (accounts.length === 0) { - StorageUtil.removeItem(EthersConstantsUtil.WALLET_ID); - EthersStoreUtil.reset(); + disconnectHandler(); } else { EthersStoreUtil.setAddress(accounts[0] as Address); } - } + }; - function chainChangedHandler(chainId: string) { - if (chainId && walletId === ConstantsUtil.COINBASE_CONNECTOR_ID) { - const chain = Number(chainId); + const chainChangedHandler = (chainId: string) => { + if (chainId) { + const chain = EthersHelpersUtil.hexStringToNumber(chainId); EthersStoreUtil.setChainId(chain); } - } + }; - if (provider) { - provider.on('disconnect', disconnectHandler); - provider.on('accountsChanged', accountsChangedHandler); - provider.on('chainChanged', chainChangedHandler); + provider.on('disconnect', disconnectHandler); + provider.on('accountsChanged', accountsChangedHandler); + provider.on('chainChanged', chainChangedHandler); + + this.providerHandlers = { + disconnect: disconnectHandler, + accountsChanged: accountsChangedHandler, + chainChanged: chainChangedHandler + }; + } + + private removeProviderListeners(provider: Provider) { + if (this.providerHandlers) { + provider.removeListener('disconnect', this.providerHandlers.disconnect); + provider.removeListener('accountsChanged', this.providerHandlers.accountsChanged); + provider.removeListener('chainChanged', this.providerHandlers.chainChanged); + this.providerHandlers = null; } } @@ -761,50 +743,47 @@ export class AppKit extends AppKitScaffold { const isConnected = EthersStoreUtil.state.isConnected; if (this.chains) { const chain = this.chains.find(c => c.chainId === chainId); - if (isConnected) { - await this.getApprovedCaipNetworksData(); - const approvedCaipNetworks = this.getApprovedCaipNetworks(); - const isApproved = approvedCaipNetworks.some( - network => network.id === `${ConstantsUtil.EIP155}:${chainId}` - ); + // await this.getApprovedCaipNetworksData(); + const approvedCaipNetworks = this.getApprovedCaipNetworks(); - //Supported network - if (chain) { - const caipChainId: CaipNetworkId = `${ConstantsUtil.EIP155}:${chain.chainId}`; - this.setUnsupportedNetwork(!isApproved); - this.setCaipNetwork({ - id: caipChainId, - name: chain.name, - imageId: PresetsUtil.EIP155NetworkImageIds[chain.chainId], - imageUrl: chainImages?.[chain.chainId] - }); - if (isConnected && address) { - const caipAddress: CaipAddress = `${ConstantsUtil.EIP155}:${chainId}:${address}`; - this.setCaipAddress(caipAddress); - if (chain.explorerUrl) { - const url = `${chain.explorerUrl}/address/${address}`; - this.setAddressExplorerUrl(url); - } else { - this.setAddressExplorerUrl(undefined); - } + const isApproved = approvedCaipNetworks.some( + network => network.id === `${ConstantsUtil.EIP155}:${chainId}` + ); - if (this.hasSyncedConnectedAccount) { - await this.syncBalance(address); - } - } - } else { - //Unsupported network - if (isConnected) { - this.setUnsupportedNetwork(true); - this.setCaipNetwork({ - id: `${ConstantsUtil.EIP155}:${chainId}`, - name: 'Unsupported Network' - }); + //Supported network + if (chain) { + const caipChainId: CaipNetworkId = `${ConstantsUtil.EIP155}:${chain.chainId}`; + this.setCaipNetwork({ + id: caipChainId, + name: chain.name, + imageId: PresetsUtil.EIP155NetworkImageIds[chain.chainId], + imageUrl: chainImages?.[chain.chainId] + }); + this.setUnsupportedNetwork(isApproved); + if (isConnected && address) { + const caipAddress: CaipAddress = `${ConstantsUtil.EIP155}:${chainId}:${address}`; + this.setCaipAddress(caipAddress); + if (chain.explorerUrl) { + const url = `${chain.explorerUrl}/address/${address}`; + this.setAddressExplorerUrl(url); + } else { this.setAddressExplorerUrl(undefined); - this.setBalance(undefined, undefined); + } + + if (this.hasSyncedConnectedAccount) { + await this.syncBalance(address); } } + } else { + //Unsupported network + this.setUnsupportedNetwork(true); + this.setCaipNetwork({ + id: `${ConstantsUtil.EIP155}:${chainId}`, + name: 'Unsupported Network' + }); + this.setAddressExplorerUrl(undefined); + this.setBalance(undefined, undefined); } } } diff --git a/packages/ethers5/src/client.ts b/packages/ethers5/src/client.ts index 4b14023e..d53ae0f6 100644 --- a/packages/ethers5/src/client.ts +++ b/packages/ethers5/src/client.ts @@ -90,6 +90,12 @@ export class AppKit extends AppKitScaffold { private authProvider?: AppKitFrameProvider; + private providerHandlers: { + disconnect: () => void; + accountsChanged: (accounts: string[]) => void; + chainChanged: (chainId: string) => void; + } | null = null; + public constructor(options: AppKitClientOptions) { const { config, @@ -235,6 +241,7 @@ export class AppKit extends AppKitScaffold { await this.setCoinbaseProvider(coinbaseProvider as Provider); } catch (error) { EthersStoreUtil.setError(error); + throw error; } } else if (id === ConstantsUtil.AUTH_CONNECTOR_ID) { await this.setAuthProvider(); @@ -509,7 +516,7 @@ export class AppKit extends AppKitScaffold { EthersStoreUtil.reset(); this.setClientId(null); - await (provider as unknown as EthereumProvider).disconnect(); + await (provider as unknown as EthereumProvider)?.disconnect?.(); } // -- Private ----------------------------------------------------------------- @@ -589,7 +596,6 @@ export class AppKit extends AppKitScaffold { if (walletId === ConstantsUtil.COINBASE_CONNECTOR_ID) { if (CoinbaseProvider.address) { await this.setCoinbaseProvider(provider); - await this.watchCoinbase(provider); } else { await StorageUtil.removeItem(EthersConstantsUtil.WALLET_ID); EthersStoreUtil.reset(); @@ -603,12 +609,12 @@ export class AppKit extends AppKitScaffold { const WalletConnectProvider = await this.getWalletConnectProvider(); if (WalletConnectProvider) { const providerType = PresetsUtil.ConnectorTypesMap[ConstantsUtil.WALLET_CONNECT_CONNECTOR_ID]; - EthersStoreUtil.setChainId(WalletConnectProvider.chainId); EthersStoreUtil.setProviderType(providerType); EthersStoreUtil.setProvider(WalletConnectProvider as unknown as Provider); EthersStoreUtil.setIsConnected(true); this.setAddress(WalletConnectProvider.accounts?.[0]); - await this.watchWalletConnect(); + EthersStoreUtil.setChainId(WalletConnectProvider.chainId); + this.listenProviderEvents(WalletConnectProvider as unknown as Provider); } } @@ -619,12 +625,12 @@ export class AppKit extends AppKitScaffold { const { address, chainId } = await EthersHelpersUtil.getUserInfo(provider); if (address && chainId) { const providerType = PresetsUtil.ConnectorTypesMap[ConstantsUtil.COINBASE_CONNECTOR_ID]; - EthersStoreUtil.setChainId(chainId); EthersStoreUtil.setProviderType(providerType); EthersStoreUtil.setProvider(provider); EthersStoreUtil.setIsConnected(true); this.setAddress(address); - await this.watchCoinbase(provider); + EthersStoreUtil.setChainId(chainId); + this.listenProviderEvents(provider); } } } @@ -647,70 +653,46 @@ export class AppKit extends AppKitScaffold { } } - private async watchWalletConnect() { - const WalletConnectProvider = await this.getWalletConnectProvider(); - - function disconnectHandler() { + private listenProviderEvents(provider: Provider) { + const disconnectHandler = () => { + this.removeProviderListeners(provider); StorageUtil.removeItem(EthersConstantsUtil.WALLET_ID); EthersStoreUtil.reset(); - - WalletConnectProvider?.removeListener('disconnect', disconnectHandler); - WalletConnectProvider?.removeListener('accountsChanged', accountsChangedHandler); - WalletConnectProvider?.removeListener('chainChanged', chainChangedHandler); - } - - function chainChangedHandler(chainId: string) { - if (chainId) { - const chain = EthersHelpersUtil.hexStringToNumber(chainId); - EthersStoreUtil.setChainId(chain); - } - } - - const accountsChangedHandler = async (accounts: string[]) => { - if (accounts.length > 0) { - await this.setWalletConnectProvider(); - } + this.setClientId(null); }; - if (WalletConnectProvider) { - WalletConnectProvider.on('disconnect', disconnectHandler); - WalletConnectProvider.on('accountsChanged', accountsChangedHandler); - WalletConnectProvider.on('chainChanged', chainChangedHandler); - } - } - - private async watchCoinbase(provider: Provider) { - const walletId = await StorageUtil.getItem(EthersConstantsUtil.WALLET_ID); - - function disconnectHandler() { - StorageUtil.removeItem(EthersConstantsUtil.WALLET_ID); - EthersStoreUtil.reset(); - - provider?.removeListener('disconnect', disconnectHandler); - provider?.removeListener('accountsChanged', accountsChangedHandler); - provider?.removeListener('chainChanged', chainChangedHandler); - } - - function accountsChangedHandler(accounts: string[]) { + const accountsChangedHandler = (accounts: string[]) => { if (accounts.length === 0) { - StorageUtil.removeItem(EthersConstantsUtil.WALLET_ID); - EthersStoreUtil.reset(); + disconnectHandler(); } else { EthersStoreUtil.setAddress(accounts[0] as Address); } - } + }; - function chainChangedHandler(chainId: string) { - if (chainId && walletId === ConstantsUtil.COINBASE_CONNECTOR_ID) { - const chain = Number(chainId); + const chainChangedHandler = (chainId: string) => { + if (chainId) { + const chain = EthersHelpersUtil.hexStringToNumber(chainId); EthersStoreUtil.setChainId(chain); } - } + }; - if (provider) { - provider.on('disconnect', disconnectHandler); - provider.on('accountsChanged', accountsChangedHandler); - provider.on('chainChanged', chainChangedHandler); + provider.on('disconnect', disconnectHandler); + provider.on('accountsChanged', accountsChangedHandler); + provider.on('chainChanged', chainChangedHandler); + + this.providerHandlers = { + disconnect: disconnectHandler, + accountsChanged: accountsChangedHandler, + chainChanged: chainChangedHandler + }; + } + + private removeProviderListeners(provider: Provider) { + if (this.providerHandlers) { + provider.removeListener('disconnect', this.providerHandlers.disconnect); + provider.removeListener('accountsChanged', this.providerHandlers.accountsChanged); + provider.removeListener('chainChanged', this.providerHandlers.chainChanged); + this.providerHandlers = null; } } diff --git a/packages/scaffold/src/modal/w3m-modal/index.tsx b/packages/scaffold/src/modal/w3m-modal/index.tsx index 61cca3c8..9463e9da 100644 --- a/packages/scaffold/src/modal/w3m-modal/index.tsx +++ b/packages/scaffold/src/modal/w3m-modal/index.tsx @@ -154,14 +154,14 @@ export function AppKit() { }, [isConnected]); useEffect(() => { - if (isUnsupportedNetwork && isNetworkStateStable) { + if (isConnected && isUnsupportedNetwork && isNetworkStateStable) { if (ModalController.state.open) { RouterController.push('UnsupportedChain'); } else { ModalController.open({ view: 'UnsupportedChain' }); } } - }, [isUnsupportedNetwork, isNetworkStateStable]); + }, [isUnsupportedNetwork, isNetworkStateStable, isConnected]); return ( <> From 23c7822c863ca61ee01600cebb684bfc543aa6c2 Mon Sep 17 00:00:00 2001 From: nacho <25931366+ignaciosantise@users.noreply.github.com> Date: Tue, 27 May 2025 17:23:01 -0300 Subject: [PATCH 13/17] chore: moved siwe logic to clients --- packages/core/src/utils/NetworkUtil.ts | 6 +- packages/ethers/src/client.ts | 104 ++++++++++------- packages/ethers5/src/client.ts | 105 +++++++++++------- packages/scaffold/src/client.ts | 53 ++++++++- .../scaffold/src/modal/w3m-modal/index.tsx | 95 +--------------- .../src/modal/w3m-network-button/index.tsx | 12 +- packages/scaffold/src/utils/UiUtil.ts | 13 +-- .../views/w3m-account-default-view/index.tsx | 26 ++--- .../src/views/w3m-account-view/index.tsx | 6 +- .../w3m-unsupported-chain-view/index.tsx | 22 +--- packages/wagmi/src/client.ts | 78 +++++++++---- 11 files changed, 255 insertions(+), 265 deletions(-) diff --git a/packages/core/src/utils/NetworkUtil.ts b/packages/core/src/utils/NetworkUtil.ts index 0aba7c93..89082870 100644 --- a/packages/core/src/utils/NetworkUtil.ts +++ b/packages/core/src/utils/NetworkUtil.ts @@ -7,7 +7,7 @@ import { SwapController } from '../controllers/SwapController'; import type { CaipNetwork } from '../utils/TypeUtil'; export const NetworkUtil = { - async handleNetworkSwitch(network: CaipNetwork) { + async handleNetworkSwitch(network: CaipNetwork, navigate: boolean = true) { const { isConnected } = AccountController.state; const { caipNetwork, approvedCaipNetworkIds, supportsAllNetworks } = NetworkController.state; const isAuthConnector = ConnectorController.state.connectedConnector === 'AUTH'; @@ -16,7 +16,9 @@ export const NetworkUtil = { if (isConnected && caipNetwork?.id !== network.id) { if (approvedCaipNetworkIds?.includes(network.id) && !isAuthConnector) { await NetworkController.switchActiveNetwork(network); - RouterUtil.goBackOrCloseModal(); + if (navigate) { + RouterUtil.goBackOrCloseModal(); + } eventData = { type: 'SWITCH_NETWORK', networkId: network.id }; } else if (supportsAllNetworks || isAuthConnector) { RouterController.push('SwitchNetwork', { network }); diff --git a/packages/ethers/src/client.ts b/packages/ethers/src/client.ts index 364a6e78..72fd0efe 100644 --- a/packages/ethers/src/client.ts +++ b/packages/ethers/src/client.ts @@ -459,7 +459,7 @@ export class AppKit extends AppKitScaffold { }); EthersStoreUtil.subscribeKey('chainId', () => { - this.syncNetwork(chainImages); + this.syncNetwork(); }); EthersStoreUtil.subscribeKey('provider', provider => { @@ -719,6 +719,7 @@ export class AppKit extends AppKitScaffold { private async syncAccount({ address }: { address?: Address }) { const chainId = EthersStoreUtil.state.chainId; const isConnected = EthersStoreUtil.state.isConnected; + const isSiweEnabled = this.options?.siweConfig?.options?.enabled; if (isConnected && address && chainId) { const caipAddress: CaipAddress = `${ConstantsUtil.EIP155}:${chainId}:${address}`; @@ -726,9 +727,14 @@ export class AppKit extends AppKitScaffold { this.setIsConnected(isConnected); this.setCaipAddress(caipAddress); + this.resetTransactions(); await Promise.all([this.syncProfile(address), this.syncBalance(address)]); this.hasSyncedConnectedAccount = true; + + if (isSiweEnabled) { + this.handleSiweChange({ isNetworkChange: false, isAccountChange: true }); + } } else if (!isConnected && this.hasSyncedConnectedAccount) { this.close(); this.resetAccount(); @@ -737,53 +743,69 @@ export class AppKit extends AppKitScaffold { } } - private async syncNetwork(chainImages?: AppKitClientOptions['chainImages']) { + private async checkNetworkSupport(params: { chainId?: number; isConnected?: boolean }) { + const { isConnected = false, chainId } = params; + const chain = this.chains.find((c: Chain) => c.chainId === chainId); + await this.getApprovedCaipNetworksData(); + const isApproved = this.getApprovedCaipNetworks().some( + network => network.id === `${ConstantsUtil.EIP155}:${params.chainId}` + ); + + if (chain) { + const caipChainId: CaipNetworkId = `${ConstantsUtil.EIP155}:${chain.chainId}`; + this.setCaipNetwork({ + id: caipChainId, + name: chain.name, + imageId: PresetsUtil.EIP155NetworkImageIds[chain.chainId], + imageUrl: this.options?.chainImages?.[chain.chainId] + }); + } else if (params.chainId) { + this.setCaipNetwork({ + id: `${ConstantsUtil.EIP155}:${params.chainId}`, + name: 'Unsupported Network' + }); + this.setAddressExplorerUrl(undefined); + this.setBalance(undefined, undefined); + } + + if (isConnected) { + const isSupported = !!chain && isApproved; + + this.openUnsupportedNetworkView(isSupported); + + return isSupported; + } + + return false; + } + + private async syncNetwork() { const address = EthersStoreUtil.state.address; const chainId = EthersStoreUtil.state.chainId; const isConnected = EthersStoreUtil.state.isConnected; if (this.chains) { const chain = this.chains.find(c => c.chainId === chainId); + const isSupported = await this.checkNetworkSupport({ chainId, isConnected }); + const isSiweEnabled = this.options?.siweConfig?.options?.enabled; + + if (chain && isConnected && address) { + const caipAddress: CaipAddress = `${ConstantsUtil.EIP155}:${chainId}:${address}`; + this.setCaipAddress(caipAddress); + this.resetTransactions(); + if (chain.explorerUrl) { + const url = `${chain.explorerUrl}/address/${address}`; + this.setAddressExplorerUrl(url); + } else { + this.setAddressExplorerUrl(undefined); + } - // await this.getApprovedCaipNetworksData(); - const approvedCaipNetworks = this.getApprovedCaipNetworks(); - - const isApproved = approvedCaipNetworks.some( - network => network.id === `${ConstantsUtil.EIP155}:${chainId}` - ); - - //Supported network - if (chain) { - const caipChainId: CaipNetworkId = `${ConstantsUtil.EIP155}:${chain.chainId}`; - this.setCaipNetwork({ - id: caipChainId, - name: chain.name, - imageId: PresetsUtil.EIP155NetworkImageIds[chain.chainId], - imageUrl: chainImages?.[chain.chainId] - }); - this.setUnsupportedNetwork(isApproved); - if (isConnected && address) { - const caipAddress: CaipAddress = `${ConstantsUtil.EIP155}:${chainId}:${address}`; - this.setCaipAddress(caipAddress); - if (chain.explorerUrl) { - const url = `${chain.explorerUrl}/address/${address}`; - this.setAddressExplorerUrl(url); - } else { - this.setAddressExplorerUrl(undefined); - } - - if (this.hasSyncedConnectedAccount) { - await this.syncBalance(address); - } + if (this.hasSyncedConnectedAccount) { + await this.syncBalance(address); } - } else { - //Unsupported network - this.setUnsupportedNetwork(true); - this.setCaipNetwork({ - id: `${ConstantsUtil.EIP155}:${chainId}`, - name: 'Unsupported Network' - }); - this.setAddressExplorerUrl(undefined); - this.setBalance(undefined, undefined); + } + + if (isConnected && isSupported && isSiweEnabled) { + this.handleSiweChange({ isNetworkChange: true, isAccountChange: false }); } } } diff --git a/packages/ethers5/src/client.ts b/packages/ethers5/src/client.ts index d53ae0f6..8dcdc2ed 100644 --- a/packages/ethers5/src/client.ts +++ b/packages/ethers5/src/client.ts @@ -441,7 +441,7 @@ export class AppKit extends AppKitScaffold { }); EthersStoreUtil.subscribeKey('chainId', () => { - this.syncNetwork(chainImages); + this.syncNetwork(); }); EthersStoreUtil.subscribeKey('provider', provider => { @@ -699,6 +699,7 @@ export class AppKit extends AppKitScaffold { private async syncAccount({ address }: { address?: Address }) { const chainId = EthersStoreUtil.state.chainId; const isConnected = EthersStoreUtil.state.isConnected; + const isSiweEnabled = this.options?.siweConfig?.options?.enabled; if (isConnected && address && chainId) { const caipAddress: CaipAddress = `${ConstantsUtil.EIP155}:${chainId}:${address}`; @@ -706,9 +707,14 @@ export class AppKit extends AppKitScaffold { this.setIsConnected(isConnected); this.setCaipAddress(caipAddress); + this.resetTransactions(); await Promise.all([this.syncProfile(address), this.syncBalance(address)]); this.hasSyncedConnectedAccount = true; + + if (isSiweEnabled) { + this.handleSiweChange({ isNetworkChange: false, isAccountChange: true }); + } } else if (!isConnected && this.hasSyncedConnectedAccount) { this.close(); this.resetAccount(); @@ -717,54 +723,69 @@ export class AppKit extends AppKitScaffold { } } - private async syncNetwork(chainImages?: AppKitClientOptions['chainImages']) { + private async checkNetworkSupport(params: { chainId?: number; isConnected?: boolean }) { + const { isConnected = false, chainId } = params; + const chain = this.chains.find((c: Chain) => c.chainId === chainId); + await this.getApprovedCaipNetworksData(); + const isApproved = this.getApprovedCaipNetworks().some( + network => network.id === `${ConstantsUtil.EIP155}:${params.chainId}` + ); + + if (chain) { + const caipChainId: CaipNetworkId = `${ConstantsUtil.EIP155}:${chain.chainId}`; + this.setCaipNetwork({ + id: caipChainId, + name: chain.name, + imageId: PresetsUtil.EIP155NetworkImageIds[chain.chainId], + imageUrl: this.options?.chainImages?.[chain.chainId] + }); + } else if (params.chainId) { + this.setCaipNetwork({ + id: `${ConstantsUtil.EIP155}:${params.chainId}`, + name: 'Unsupported Network' + }); + this.setAddressExplorerUrl(undefined); + this.setBalance(undefined, undefined); + } + + if (isConnected) { + const isSupported = !!chain && isApproved; + + this.openUnsupportedNetworkView(isSupported); + + return isSupported; + } + + return false; + } + + private async syncNetwork() { const address = EthersStoreUtil.state.address; const chainId = EthersStoreUtil.state.chainId; const isConnected = EthersStoreUtil.state.isConnected; if (this.chains) { const chain = this.chains.find(c => c.chainId === chainId); - await this.getApprovedCaipNetworksData(); - const approvedCaipNetworks = this.getApprovedCaipNetworks(); - - const isApproved = approvedCaipNetworks.some( - network => network.id === `${ConstantsUtil.EIP155}:${chainId}` - ); - - //Supported network - if (chain) { - const caipChainId: CaipNetworkId = `${ConstantsUtil.EIP155}:${chain.chainId}`; - this.setUnsupportedNetwork(!isApproved); - this.setCaipNetwork({ - id: caipChainId, - name: chain.name, - imageId: PresetsUtil.EIP155NetworkImageIds[chain.chainId], - imageUrl: chainImages?.[chain.chainId] - }); - if (isConnected && address) { - const caipAddress: CaipAddress = `${ConstantsUtil.EIP155}:${chainId}:${address}`; - this.setCaipAddress(caipAddress); - if (chain.explorerUrl) { - const url = `${chain.explorerUrl}/address/${address}`; - this.setAddressExplorerUrl(url); - } else { - this.setAddressExplorerUrl(undefined); - } - - if (this.hasSyncedConnectedAccount) { - await this.syncBalance(address); - } - } - } else { - //Unsupported network - if (isConnected) { - this.setUnsupportedNetwork(true); - this.setCaipNetwork({ - id: `${ConstantsUtil.EIP155}:${chainId}`, - name: 'Unsupported Network' - }); + const isSupported = await this.checkNetworkSupport({ chainId, isConnected }); + const isSiweEnabled = this.options?.siweConfig?.options?.enabled; + + if (chain && isConnected && address) { + const caipAddress: CaipAddress = `${ConstantsUtil.EIP155}:${chainId}:${address}`; + this.setCaipAddress(caipAddress); + this.resetTransactions(); + if (chain.explorerUrl) { + const url = `${chain.explorerUrl}/address/${address}`; + this.setAddressExplorerUrl(url); + } else { this.setAddressExplorerUrl(undefined); - this.setBalance(undefined, undefined); } + + if (this.hasSyncedConnectedAccount) { + await this.syncBalance(address); + } + } + + if (isConnected && isSupported && isSiweEnabled) { + this.handleSiweChange({ isNetworkChange: true, isAccountChange: false }); } } } diff --git a/packages/scaffold/src/client.ts b/packages/scaffold/src/client.ts index d8880024..30d09c07 100644 --- a/packages/scaffold/src/client.ts +++ b/packages/scaffold/src/client.ts @@ -27,6 +27,8 @@ import { NetworkController, OptionsController, PublicStateController, + RouterController, + RouterUtil, SnackController, StorageUtil, ThemeController, @@ -277,10 +279,53 @@ export class AppKitScaffold { } } - protected setUnsupportedNetwork( - isUnsupportedNetwork: NetworkControllerState['isUnsupportedNetwork'] - ) { - NetworkController.setUnsupportedNetwork(isUnsupportedNetwork); + protected onSiweNavigation = () => { + if (ModalController.state.open) { + RouterController.push('ConnectingSiwe'); + } else { + ModalController.open({ view: 'ConnectingSiwe' }); + } + }; + + protected openUnsupportedNetworkView(isSupported: boolean) { + if (isSupported && RouterController.state.view === 'UnsupportedChain') { + return RouterUtil.goBackOrCloseModal(); + } else if (!isSupported) { + if (ModalController.state.open) { + RouterController.push('UnsupportedChain'); + } else { + ModalController.open({ view: 'UnsupportedChain' }); + } + } + } + + protected async handleSiweChange(params: { + isNetworkChange?: boolean; + isAccountChange?: boolean; + }) { + const { isNetworkChange, isAccountChange } = params; + const { enabled, signOutOnAccountChange, signOutOnNetworkChange } = + SIWEController.state._client?.options ?? {}; + + if (enabled) { + const session = await SIWEController.getSession(); + if (session && isAccountChange && signOutOnAccountChange) { + // If the address has changed and signOnAccountChange is enabled, sign out + await SIWEController.signOut(); + this.onSiweNavigation(); + } else if (isNetworkChange && signOutOnNetworkChange) { + // If the network has changed and signOnNetworkChange is enabled, sign out + await SIWEController.signOut(); + this.onSiweNavigation(); + } else if (!session) { + // If it's connected but there's no session, show sign view + this.onSiweNavigation(); + } + } + } + + protected resetTransactions() { + TransactionsController.resetTransactions(); } // -- Private ------------------------------------------------------------------ diff --git a/packages/scaffold/src/modal/w3m-modal/index.tsx b/packages/scaffold/src/modal/w3m-modal/index.tsx index 9463e9da..eaf80675 100644 --- a/packages/scaffold/src/modal/w3m-modal/index.tsx +++ b/packages/scaffold/src/modal/w3m-modal/index.tsx @@ -1,5 +1,5 @@ import { useSnapshot } from 'valtio'; -import { useCallback, useEffect, useState } from 'react'; +import { useEffect } from 'react'; import { useWindowDimensions, StatusBar } from 'react-native'; import Modal from 'react-native-modal'; import { Card, ThemeProvider } from '@reown/appkit-ui-react-native'; @@ -8,16 +8,12 @@ import { ApiController, ConnectionController, ConnectorController, - CoreHelperUtil, EventsController, ModalController, OptionsController, RouterController, - TransactionsController, - type CaipAddress, type AppKitFrameProvider, - ThemeController, - NetworkController + ThemeController } from '@reown/appkit-core-react-native'; import { SIWEController } from '@reown/appkit-siwe-react-native'; @@ -30,12 +26,10 @@ import styles from './styles'; const disableCloseViews = ['UnsupportedChain', 'ConnectingSiwe']; export function AppKit() { - const { open, loading } = useSnapshot(ModalController.state); + const { open } = useSnapshot(ModalController.state); const { connectors, connectedConnector } = useSnapshot(ConnectorController.state); - const { caipAddress, isConnected } = useSnapshot(AccountController.state); - const { isUnsupportedNetwork } = useSnapshot(NetworkController.state); + const { themeMode, themeVariables } = useSnapshot(ThemeController.state); - const [isNetworkStateStable, setIsNetworkStateStable] = useState(false); const { height } = useWindowDimensions(); const { isLandscape } = useCustomDimensions(); const portraitHeight = height - 120; @@ -78,91 +72,10 @@ export function AppKit() { } }; - const onNewAddress = useCallback( - async (address?: CaipAddress) => { - if (!isConnected || loading) { - return; - } - - const newAddress = CoreHelperUtil.getPlainAddress(address); - TransactionsController.resetTransactions(); - - if (OptionsController.state.isSiweEnabled) { - if (NetworkController.state.isUnsupportedNetwork) { - // If the network is unsupported, don't do siwe stuff until user changes network - - return; - } - - const newNetworkId = CoreHelperUtil.getNetworkId(address); - - const { signOutOnAccountChange, signOutOnNetworkChange } = - SIWEController.state._client?.options ?? {}; - const session = await SIWEController.getSession(); - - if (session && newAddress && signOutOnAccountChange) { - // If the address has changed and signOnAccountChange is enabled, sign out - await SIWEController.signOut(); - onSiweNavigation(); - } else if ( - newNetworkId && - session?.chainId.toString() !== newNetworkId && - signOutOnNetworkChange - ) { - // If the network has changed and signOnNetworkChange is enabled, sign out - await SIWEController.signOut(); - onSiweNavigation(); - } else if (!session) { - // If it's connected but there's no session, show sign view - onSiweNavigation(); - } - } - }, - [isConnected, loading] - ); - - const onSiweNavigation = () => { - if (ModalController.state.open) { - RouterController.push('ConnectingSiwe'); - } else { - ModalController.open({ view: 'ConnectingSiwe' }); - } - }; - useEffect(() => { prefetch(); }, []); - useEffect(() => { - onNewAddress(caipAddress); - }, [caipAddress, onNewAddress]); - - useEffect(() => { - if (isConnected) { - const timer = setTimeout(() => { - setIsNetworkStateStable(true); - }, 750); // Stability period. Sometimes the network state updates at init - - return () => { - clearTimeout(timer); - }; - } else { - setIsNetworkStateStable(false); - - return () => {}; - } - }, [isConnected]); - - useEffect(() => { - if (isConnected && isUnsupportedNetwork && isNetworkStateStable) { - if (ModalController.state.open) { - RouterController.push('UnsupportedChain'); - } else { - ModalController.open({ view: 'UnsupportedChain' }); - } - } - }, [isUnsupportedNetwork, isNetworkStateStable, isConnected]); - return ( <> diff --git a/packages/scaffold/src/modal/w3m-network-button/index.tsx b/packages/scaffold/src/modal/w3m-network-button/index.tsx index b23cdd48..40109342 100644 --- a/packages/scaffold/src/modal/w3m-network-button/index.tsx +++ b/packages/scaffold/src/modal/w3m-network-button/index.tsx @@ -24,22 +24,14 @@ export function NetworkButton({ disabled, style }: NetworkButtonProps) { const { themeMode, themeVariables } = useSnapshot(ThemeController.state); const onNetworkPress = () => { - if (AccountController.state.isConnected && NetworkController.state.isUnsupportedNetwork) { - ModalController.open({ view: 'UnsupportedChain' }); - } else { - ModalController.open({ view: 'Networks' }); - } + ModalController.open({ view: 'Networks' }); EventsController.sendEvent({ type: 'track', event: 'CLICK_NETWORKS' }); }; - const buttonText = UiUtil.getNetworkButtonText( - isConnected, - caipNetwork, - NetworkController.state.isUnsupportedNetwork - ); + const buttonText = UiUtil.getNetworkButtonText(isConnected, caipNetwork); return ( diff --git a/packages/scaffold/src/utils/UiUtil.ts b/packages/scaffold/src/utils/UiUtil.ts index 3614a322..21d7a493 100644 --- a/packages/scaffold/src/utils/UiUtil.ts +++ b/packages/scaffold/src/utils/UiUtil.ts @@ -30,11 +30,7 @@ export const UiUtil = { } }, - getNetworkButtonText( - isConnected: boolean, - caipNetwork: CaipNetwork | undefined, - isUnsupportedNetwork?: boolean - ): string { + getNetworkButtonText(isConnected: boolean, caipNetwork: CaipNetwork | undefined): string { let buttonText: string; if (!isConnected) { @@ -44,13 +40,8 @@ export const UiUtil = { buttonText = 'Select Network'; } } else { - // isConnected is true if (caipNetwork) { - if (isUnsupportedNetwork) { - buttonText = 'Switch Network'; - } else { - buttonText = caipNetwork.name ?? 'Unknown Network'; - } + buttonText = caipNetwork.name ?? 'Unknown Network'; } else { buttonText = 'Select Network'; } diff --git a/packages/scaffold/src/views/w3m-account-default-view/index.tsx b/packages/scaffold/src/views/w3m-account-default-view/index.tsx index ce887230..08893358 100644 --- a/packages/scaffold/src/views/w3m-account-default-view/index.tsx +++ b/packages/scaffold/src/views/w3m-account-default-view/index.tsx @@ -48,7 +48,7 @@ export function AccountDefaultView() { } = useSnapshot(AccountController.state); const { loading } = useSnapshot(ModalController.state); const [disconnecting, setDisconnecting] = useState(false); - const { caipNetwork, isUnsupportedNetwork } = useSnapshot(NetworkController.state); + const { caipNetwork } = useSnapshot(NetworkController.state); const { connectedConnector } = useSnapshot(ConnectorController.state); const { connectedSocialProvider } = useSnapshot(ConnectionController.state); const { features } = useSnapshot(OptionsController.state); @@ -148,11 +148,7 @@ export function AccountDefaultView() { }; const onNetworkPress = () => { - if (AccountController.state.isConnected && NetworkController.state.isUnsupportedNetwork) { - RouterController.push('UnsupportedChain'); - } else { - RouterController.push('Networks'); - } + RouterController.push('Networks'); EventsController.sendEvent({ type: 'track', @@ -244,25 +240,17 @@ export function AccountDefaultView() { )} - - {ScaffoldUiUtil.getNetworkButtonText( - isConnected, - caipNetwork, - isUnsupportedNetwork - )} + + {ScaffoldUiUtil.getNetworkButtonText(isConnected, caipNetwork)} diff --git a/packages/scaffold/src/views/w3m-account-view/index.tsx b/packages/scaffold/src/views/w3m-account-view/index.tsx index 7cbb0634..14e19d85 100644 --- a/packages/scaffold/src/views/w3m-account-view/index.tsx +++ b/packages/scaffold/src/views/w3m-account-view/index.tsx @@ -39,11 +39,7 @@ export function AccountView() { }; const onNetworkPress = () => { - if (AccountController.state.isConnected && NetworkController.state.isUnsupportedNetwork) { - RouterController.push('UnsupportedChain'); - } else { - RouterController.push('Networks'); - } + RouterController.push('Networks'); }; const onActivatePress = () => { diff --git a/packages/scaffold/src/views/w3m-unsupported-chain-view/index.tsx b/packages/scaffold/src/views/w3m-unsupported-chain-view/index.tsx index f46f291c..9c07f2f0 100644 --- a/packages/scaffold/src/views/w3m-unsupported-chain-view/index.tsx +++ b/packages/scaffold/src/views/w3m-unsupported-chain-view/index.tsx @@ -1,5 +1,5 @@ import { useSnapshot } from 'valtio'; -import { useEffect, useState } from 'react'; +import { useState } from 'react'; import { FlatList } from 'react-native'; import { Icon, ListItem, Separator, Text } from '@reown/appkit-ui-react-native'; import { @@ -12,26 +12,20 @@ import { NetworkUtil, type CaipNetwork, type NetworkControllerState, - ModalController, - RouterUtil + ModalController } from '@reown/appkit-core-react-native'; import styles from './styles'; export function UnsupportedChainView() { - const { - caipNetwork, - supportsAllNetworks, - approvedCaipNetworkIds, - requestedCaipNetworks, - isUnsupportedNetwork - } = useSnapshot(NetworkController.state) as NetworkControllerState; + const { caipNetwork, supportsAllNetworks, approvedCaipNetworkIds, requestedCaipNetworks } = + useSnapshot(NetworkController.state) as NetworkControllerState; const [disconnecting, setDisconnecting] = useState(false); const networks = CoreHelperUtil.sortNetworks(approvedCaipNetworkIds, requestedCaipNetworks); const imageHeaders = ApiController._getApiHeaders(); const onNetworkPress = async (network: CaipNetwork) => { - if (caipNetwork?.id === network.id) { + if (NetworkController.state.caipNetwork?.id === network.id) { return ModalController.close(); } @@ -53,12 +47,6 @@ export function UnsupportedChainView() { setDisconnecting(false); }; - useEffect(() => { - if (!isUnsupportedNetwork) { - RouterUtil.goBackOrCloseModal(); - } - }, [isUnsupportedNetwork]); - return ( { - this.syncAccount({ ...accountData }); + this.syncAccount({ + ...accountData, + isNetworkChange: accountData.chainId !== prevAccountData.chainId, + isAccountChange: accountData.address !== prevAccountData.address + }); if (accountData.status === 'disconnected' && prevAccountData.status === 'connected') { this.onSiweDisconnect(); @@ -426,18 +430,21 @@ export class AppKit extends AppKitScaffold { chainId, connector, isConnecting, - isReconnecting + isReconnecting, + isNetworkChange, + isAccountChange }: Pick< GetAccountReturnType, 'address' | 'isConnected' | 'chainId' | 'connector' | 'isConnecting' | 'isReconnecting' - >) { - this.syncNetwork(address, chainId, isConnected); + > & { isNetworkChange?: boolean; isAccountChange?: boolean }) { + this.syncNetwork(address, chainId, isConnected, isNetworkChange, isAccountChange); this.setLoading(!!connector && (isConnecting || isReconnecting)); if (isConnected && address && chainId) { const caipAddress: CaipAddress = `${ConstantsUtil.EIP155}:${chainId}:${address}`; this.setIsConnected(isConnected); this.setCaipAddress(caipAddress); + this.resetTransactions(); await Promise.all([ this.syncProfile(address, chainId), this.syncBalance(address, chainId), @@ -451,37 +458,58 @@ export class AppKit extends AppKitScaffold { } } - private async syncNetwork(address?: Hex, chainId?: number, isConnected?: boolean) { + private async checkNetworkSupport(params: { chainId?: number; isConnected?: boolean }) { + const { isConnected = false, chainId } = params; const chain = this.wagmiConfig.chains.find((c: Chain) => c.id === chainId); + await this.getApprovedCaipNetworksData(); + const isApproved = this.getApprovedCaipNetworks().some( + network => network.id === `${ConstantsUtil.EIP155}:${params.chainId}` + ); - if (isConnected) { - await this.getApprovedCaipNetworksData(); - const approvedCaipNetworks = this.getApprovedCaipNetworks(); - - const isApproved = approvedCaipNetworks.some( - network => network.id === `${ConstantsUtil.EIP155}:${chainId}` - ); - - const isSupported = !!chain && isApproved; - - // If the network is not supported, set the unsupported network state - this.setUnsupportedNetwork(!isSupported); - } - - if (chain || chainId) { - const name = chain?.name ?? chainId?.toString(); - const id = Number(chain?.id ?? chainId); + if (chain) { + const id = Number(chain.id); const caipChainId: CaipNetworkId = `${ConstantsUtil.EIP155}:${id}`; this.setCaipNetwork({ id: caipChainId, - name, + name: chain.name, imageId: PresetsUtil.EIP155NetworkImageIds[id], imageUrl: this.options?.chainImages?.[id] }); + } else if (params.chainId) { + this.setCaipNetwork({ + id: `${ConstantsUtil.EIP155}:${params.chainId}`, + name: 'Unsupported Network' + }); + } + if (isConnected) { + const isSupported = !!chain && isApproved; + + this.openUnsupportedNetworkView(isSupported); + + return isSupported; + } + + return false; + } + + private async syncNetwork( + address?: Hex, + chainId?: number, + isConnected?: boolean, + isNetworkChange?: boolean, + isAccountChange?: boolean + ) { + const chain = this.wagmiConfig.chains.find((c: Chain) => c.id === chainId); + const isSupported = await this.checkNetworkSupport({ chainId, isConnected }); + const isSiweEnabled = this.options?.siweConfig?.options?.enabled; + + if (chain || chainId) { + const id = Number(chain?.id ?? chainId); if (isConnected && address && chainId) { const caipAddress: CaipAddress = `${ConstantsUtil.EIP155}:${id}:${address}`; this.setCaipAddress(caipAddress); + this.resetTransactions(); if (chain?.blockExplorers?.default?.url) { const url = `${chain.blockExplorers.default.url}/address/${address}`; this.setAddressExplorerUrl(url); @@ -494,6 +522,10 @@ export class AppKit extends AppKitScaffold { } } } + + if (isConnected && isSupported && isSiweEnabled) { + this.handleSiweChange({ isNetworkChange, isAccountChange }); + } } private async syncProfile(address: Hex, chainId: number) { From 1013fc6c4742feaa26ad7ba684f99a00716876aa Mon Sep 17 00:00:00 2001 From: nacho <25931366+ignaciosantise@users.noreply.github.com> Date: Tue, 27 May 2025 17:32:24 -0300 Subject: [PATCH 14/17] chore: removed old tests --- .../__tests__/controllers/NetworkController.test.ts | 11 ----------- packages/core/src/controllers/NetworkController.ts | 9 +-------- 2 files changed, 1 insertion(+), 19 deletions(-) diff --git a/packages/core/src/__tests__/controllers/NetworkController.test.ts b/packages/core/src/__tests__/controllers/NetworkController.test.ts index c2090071..314b0357 100644 --- a/packages/core/src/__tests__/controllers/NetworkController.test.ts +++ b/packages/core/src/__tests__/controllers/NetworkController.test.ts @@ -19,7 +19,6 @@ const client: NetworkControllerClient = { const initialState = { _client: client, supportsAllNetworks: true, - isUnsupportedNetwork: false, smartAccountEnabledNetworks: [] }; @@ -74,14 +73,4 @@ describe('NetworkController', () => { expect(NetworkController.state.approvedCaipNetworkIds).toEqual(undefined); expect(NetworkController.state.requestedCaipNetworks).toEqual(requestedCaipNetworks); }); - - it('should set isUnsupportedNetwork to true when setUnsupportedNetwork is called', () => { - NetworkController.setUnsupportedNetwork(true); - expect(NetworkController.state.isUnsupportedNetwork).toEqual(true); - }); - - it('should set isUnsupportedNetwork to false when setUnsupportedNetwork is called', () => { - NetworkController.setUnsupportedNetwork(false); - expect(NetworkController.state.isUnsupportedNetwork).toEqual(false); - }); }); diff --git a/packages/core/src/controllers/NetworkController.ts b/packages/core/src/controllers/NetworkController.ts index a3517f06..60a8957f 100644 --- a/packages/core/src/controllers/NetworkController.ts +++ b/packages/core/src/controllers/NetworkController.ts @@ -21,15 +21,13 @@ export interface NetworkControllerState { requestedCaipNetworks?: CaipNetwork[]; approvedCaipNetworkIds?: CaipNetworkId[]; smartAccountEnabledNetworks: number[]; - isUnsupportedNetwork?: boolean; } // -- State --------------------------------------------- // const state = proxy({ supportsAllNetworks: true, defaultCaipNetwork: undefined, - smartAccountEnabledNetworks: [], - isUnsupportedNetwork: false + smartAccountEnabledNetworks: [] }); // -- Controller ---------------------------------------- // @@ -59,10 +57,6 @@ export const NetworkController = { PublicStateController.set({ selectedNetworkId: caipNetwork?.id }); }, - setUnsupportedNetwork(isUnsupportedNetwork: NetworkControllerState['isUnsupportedNetwork']) { - state.isUnsupportedNetwork = isUnsupportedNetwork; - }, - setRequestedCaipNetworks(requestedNetworks: NetworkControllerState['requestedCaipNetworks']) { state.requestedCaipNetworks = requestedNetworks; }, @@ -119,7 +113,6 @@ export const NetworkController = { resetNetwork() { state.caipNetwork = state.defaultCaipNetwork || undefined; - state.isUnsupportedNetwork = undefined; state.approvedCaipNetworkIds = undefined; state.supportsAllNetworks = true; state.smartAccountEnabledNetworks = []; From 41b2b257f34f1317c3aba032ff626692320237d5 Mon Sep 17 00:00:00 2001 From: nacho <25931366+ignaciosantise@users.noreply.github.com> Date: Wed, 28 May 2025 12:33:06 -0300 Subject: [PATCH 15/17] chore: fixed 1CA issue in wagmi connector + solved test issues --- apps/native/tests/shared/pages/ModalPage.ts | 4 ++-- apps/native/tests/shared/pages/WalletPage.ts | 4 ++-- apps/native/tests/shared/validators/WalletValidator.ts | 6 +++--- apps/native/tests/wallet.spec.ts | 2 +- packages/wagmi/src/client.ts | 5 +++-- 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/apps/native/tests/shared/pages/ModalPage.ts b/apps/native/tests/shared/pages/ModalPage.ts index b7e6f1e7..aeef6495 100644 --- a/apps/native/tests/shared/pages/ModalPage.ts +++ b/apps/native/tests/shared/pages/ModalPage.ts @@ -32,7 +32,7 @@ export class ModalPage { const qrLoadInitiatedTime = new Date(); const qrCode = this.page.getByTestId('qr-code'); - await expect(qrCode).toBeVisible(); + await expect(qrCode).toBeVisible({ timeout: 20000 }); const uri = await this.clickCopyLink(); const qrLoadedTime = new Date(); @@ -51,7 +51,7 @@ export class ModalPage { const qrLoadInitiatedTime = new Date(); const qrCode = this.page.getByTestId('qr-code'); - await expect(qrCode).toBeVisible(); + await expect(qrCode).toBeVisible({ timeout: 20000 }); const uri = await this.clickCopyLink(); const qrLoadedTime = new Date(); if (timingRecords) { diff --git a/apps/native/tests/shared/pages/WalletPage.ts b/apps/native/tests/shared/pages/WalletPage.ts index 44a48135..1eb43990 100644 --- a/apps/native/tests/shared/pages/WalletPage.ts +++ b/apps/native/tests/shared/pages/WalletPage.ts @@ -81,8 +81,8 @@ export class WalletPage { timeout: 30000 }); await expect(btn).toBeEnabled(); - await btn.focus(); - await this.page.keyboard.press('Space'); + await this.page.waitForTimeout(1000); + await btn.click(); } /** diff --git a/apps/native/tests/shared/validators/WalletValidator.ts b/apps/native/tests/shared/validators/WalletValidator.ts index c6e292e5..5fb20305 100644 --- a/apps/native/tests/shared/validators/WalletValidator.ts +++ b/apps/native/tests/shared/validators/WalletValidator.ts @@ -28,14 +28,14 @@ export class WalletValidator { async expectSessionCard({ visible = true }: { visible?: boolean }) { if (visible) { await expect( - this.page.getByTestId('session-card'), + this.page.getByTestId('session-card').first(), 'Session card should be visible' ).toBeVisible({ timeout: MAX_WAIT }); } else { await expect( - this.page.getByTestId('session-card'), + this.page.getByTestId('session-card').first(), 'Session card should not be visible' ).not.toBeVisible({ timeout: MAX_WAIT @@ -46,7 +46,7 @@ export class WalletValidator { async expectDisconnected() { await this.gotoSessions.click(); await expect( - this.page.getByTestId('session-card'), + this.page.getByTestId('session-card').first(), 'Session card should not be visible' ).not.toBeVisible({ timeout: MAX_WAIT diff --git a/apps/native/tests/wallet.spec.ts b/apps/native/tests/wallet.spec.ts index c1bc69de..446eb929 100644 --- a/apps/native/tests/wallet.spec.ts +++ b/apps/native/tests/wallet.spec.ts @@ -137,7 +137,7 @@ sampleWalletTest('it should disconnect using hook', async () => { await modalValidator.expectDisconnected(); }); -sampleWalletTest('it should disconnect and close modal when connecting from wallet', async () => { +sampleWalletTest('it should disconnect and close modal when disconnecting from wallet', async () => { await modalValidator.expectDisconnected(); await modalPage.qrCodeFlow(modalPage, walletPage); await modalValidator.expectConnected(); diff --git a/packages/wagmi/src/client.ts b/packages/wagmi/src/client.ts index be094b82..c75457a9 100644 --- a/packages/wagmi/src/client.ts +++ b/packages/wagmi/src/client.ts @@ -157,7 +157,8 @@ export class AppKit extends AppKitScaffold { this.setClientId(clientId); } - const chainId = NetworkUtil.caipNetworkIdToNumber(this.getCaipNetwork()?.id); + const caipNetwork = this.getCaipNetwork(); + const chainId = NetworkUtil.caipNetworkIdToNumber(caipNetwork?.id); // SIWE const siweParams = await siweConfig?.getMessageParams?.(); @@ -198,7 +199,7 @@ export class AppKit extends AppKitScaffold { cacao: signedCacao }); - if (address && chainId) { + if (address && cacaoChainId) { const session = { address, chainId: parseInt(cacaoChainId, 10) From e135b8eec2eae0467a231d665b175dff18b86f90 Mon Sep 17 00:00:00 2001 From: nacho <25931366+ignaciosantise@users.noreply.github.com> Date: Wed, 28 May 2025 12:53:55 -0300 Subject: [PATCH 16/17] fix: compare walletchoice with authtype in approved namespace getter --- packages/wagmi/src/client.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/wagmi/src/client.ts b/packages/wagmi/src/client.ts index c75457a9..044bdef1 100644 --- a/packages/wagmi/src/client.ts +++ b/packages/wagmi/src/client.ts @@ -124,7 +124,7 @@ export class AppKit extends AppKitScaffold { ); return getWalletConnectCaipNetworks(connector); - } else if (authType) { + } else if (walletChoice?.includes(authType)) { return getAuthCaipNetworks(); } From b76f26d9ccade0e04cef221d3f303ec5d8501611 Mon Sep 17 00:00:00 2001 From: nacho <25931366+ignaciosantise@users.noreply.github.com> Date: Wed, 28 May 2025 16:42:20 -0300 Subject: [PATCH 17/17] chore: check supported network only if needed, added timeout for the session to settle --- .../src/controllers/ConnectionController.ts | 2 +- .../src/controllers/ConnectorController.ts | 6 ++--- packages/ethers/src/client.ts | 22 +++++++++++-------- packages/ethers5/src/client.ts | 22 +++++++++++-------- packages/scaffold/src/client.ts | 2 +- .../src/views/w3m-connecting-view/index.tsx | 2 +- packages/wagmi/src/client.ts | 19 +++++++++++----- 7 files changed, 45 insertions(+), 30 deletions(-) diff --git a/packages/core/src/controllers/ConnectionController.ts b/packages/core/src/controllers/ConnectionController.ts index d36182b7..b3d2538d 100644 --- a/packages/core/src/controllers/ConnectionController.ts +++ b/packages/core/src/controllers/ConnectionController.ts @@ -93,7 +93,7 @@ export const ConnectionController = { async connectExternal(options: ConnectExternalOptions) { await this._getClient().connectExternal?.(options); - ConnectorController.setConnectedConnector(options.type); + await ConnectorController.setConnectedConnector(options.type); }, async signMessage(message: string) { diff --git a/packages/core/src/controllers/ConnectorController.ts b/packages/core/src/controllers/ConnectorController.ts index 7ff77664..798aadd5 100644 --- a/packages/core/src/controllers/ConnectorController.ts +++ b/packages/core/src/controllers/ConnectorController.ts @@ -42,7 +42,7 @@ export const ConnectorController = { return state.connectors.find(c => c.type === 'AUTH'); }, - setConnectedConnector( + async setConnectedConnector( connectorType: ConnectorControllerState['connectedConnector'], saveStorage = true ) { @@ -50,9 +50,9 @@ export const ConnectorController = { if (saveStorage) { if (connectorType) { - StorageUtil.setConnectedConnector(connectorType); + await StorageUtil.setConnectedConnector(connectorType); } else { - StorageUtil.removeConnectedConnector(); + await StorageUtil.removeConnectedConnector(); } } }, diff --git a/packages/ethers/src/client.ts b/packages/ethers/src/client.ts index 72fd0efe..729d4824 100644 --- a/packages/ethers/src/client.ts +++ b/packages/ethers/src/client.ts @@ -150,7 +150,7 @@ export class AppKit extends AppKitScaffold { const authType = PresetsUtil.ConnectorTypesMap[ConstantsUtil.AUTH_CONNECTOR_ID]!; if (walletChoice?.includes(walletConnectType)) { const provider = await this.getWalletConnectProvider(); - const result = getWalletConnectCaipNetworks(provider); + const result = await getWalletConnectCaipNetworks(provider); resolve(result); } else if (walletChoice?.includes(authType)) { @@ -718,13 +718,13 @@ export class AppKit extends AppKitScaffold { private async syncAccount({ address }: { address?: Address }) { const chainId = EthersStoreUtil.state.chainId; - const isConnected = EthersStoreUtil.state.isConnected; const isSiweEnabled = this.options?.siweConfig?.options?.enabled; - if (isConnected && address && chainId) { + if (address && chainId) { const caipAddress: CaipAddress = `${ConstantsUtil.EIP155}:${chainId}:${address}`; - this.setIsConnected(isConnected); + EthersStoreUtil.setIsConnected(true); + this.setIsConnected(true); this.setCaipAddress(caipAddress); this.resetTransactions(); @@ -735,7 +735,7 @@ export class AppKit extends AppKitScaffold { if (isSiweEnabled) { this.handleSiweChange({ isNetworkChange: false, isAccountChange: true }); } - } else if (!isConnected && this.hasSyncedConnectedAccount) { + } else if (!address && this.hasSyncedConnectedAccount) { this.close(); this.resetAccount(); this.resetWcConnection(); @@ -744,12 +744,11 @@ export class AppKit extends AppKitScaffold { } private async checkNetworkSupport(params: { chainId?: number; isConnected?: boolean }) { + // wait until the session is set + await new Promise(resolve => setTimeout(resolve, 500)); + const { isConnected = false, chainId } = params; const chain = this.chains.find((c: Chain) => c.chainId === chainId); - await this.getApprovedCaipNetworksData(); - const isApproved = this.getApprovedCaipNetworks().some( - network => network.id === `${ConstantsUtil.EIP155}:${params.chainId}` - ); if (chain) { const caipChainId: CaipNetworkId = `${ConstantsUtil.EIP155}:${chain.chainId}`; @@ -769,6 +768,11 @@ export class AppKit extends AppKitScaffold { } if (isConnected) { + await this.getApprovedCaipNetworksData(); + const isApproved = this.getApprovedCaipNetworks().some( + network => network.id === `${ConstantsUtil.EIP155}:${params.chainId}` + ); + const isSupported = !!chain && isApproved; this.openUnsupportedNetworkView(isSupported); diff --git a/packages/ethers5/src/client.ts b/packages/ethers5/src/client.ts index 8dcdc2ed..f17c5673 100644 --- a/packages/ethers5/src/client.ts +++ b/packages/ethers5/src/client.ts @@ -137,7 +137,7 @@ export class AppKit extends AppKitScaffold { const authType = PresetsUtil.ConnectorTypesMap[ConstantsUtil.AUTH_CONNECTOR_ID]!; if (walletChoice?.includes(walletConnectType)) { const provider = await this.getWalletConnectProvider(); - const result = getWalletConnectCaipNetworks(provider); + const result = await getWalletConnectCaipNetworks(provider); resolve(result); } else if (walletChoice?.includes(authType)) { @@ -698,13 +698,13 @@ export class AppKit extends AppKitScaffold { private async syncAccount({ address }: { address?: Address }) { const chainId = EthersStoreUtil.state.chainId; - const isConnected = EthersStoreUtil.state.isConnected; const isSiweEnabled = this.options?.siweConfig?.options?.enabled; - if (isConnected && address && chainId) { + if (address && chainId) { const caipAddress: CaipAddress = `${ConstantsUtil.EIP155}:${chainId}:${address}`; - this.setIsConnected(isConnected); + EthersStoreUtil.setIsConnected(true); + this.setIsConnected(true); this.setCaipAddress(caipAddress); this.resetTransactions(); @@ -715,7 +715,7 @@ export class AppKit extends AppKitScaffold { if (isSiweEnabled) { this.handleSiweChange({ isNetworkChange: false, isAccountChange: true }); } - } else if (!isConnected && this.hasSyncedConnectedAccount) { + } else if (!address && this.hasSyncedConnectedAccount) { this.close(); this.resetAccount(); this.resetWcConnection(); @@ -724,12 +724,11 @@ export class AppKit extends AppKitScaffold { } private async checkNetworkSupport(params: { chainId?: number; isConnected?: boolean }) { + // wait until the session is set + await new Promise(resolve => setTimeout(resolve, 500)); + const { isConnected = false, chainId } = params; const chain = this.chains.find((c: Chain) => c.chainId === chainId); - await this.getApprovedCaipNetworksData(); - const isApproved = this.getApprovedCaipNetworks().some( - network => network.id === `${ConstantsUtil.EIP155}:${params.chainId}` - ); if (chain) { const caipChainId: CaipNetworkId = `${ConstantsUtil.EIP155}:${chain.chainId}`; @@ -749,6 +748,11 @@ export class AppKit extends AppKitScaffold { } if (isConnected) { + await this.getApprovedCaipNetworksData(); + const isApproved = this.getApprovedCaipNetworks().some( + network => network.id === `${ConstantsUtil.EIP155}:${params.chainId}` + ); + const isSupported = !!chain && isApproved; this.openUnsupportedNetworkView(isSupported); diff --git a/packages/scaffold/src/client.ts b/packages/scaffold/src/client.ts index 30d09c07..b43fae22 100644 --- a/packages/scaffold/src/client.ts +++ b/packages/scaffold/src/client.ts @@ -410,7 +410,7 @@ export class AppKitScaffold { private async initConnectedConnector() { const connectedConnector = await StorageUtil.getConnectedConnector(); if (connectedConnector) { - ConnectorController.setConnectedConnector(connectedConnector, false); + await ConnectorController.setConnectedConnector(connectedConnector, false); } } diff --git a/packages/scaffold/src/views/w3m-connecting-view/index.tsx b/packages/scaffold/src/views/w3m-connecting-view/index.tsx index 44ee537c..d43de6e2 100644 --- a/packages/scaffold/src/views/w3m-connecting-view/index.tsx +++ b/packages/scaffold/src/views/w3m-connecting-view/index.tsx @@ -50,7 +50,7 @@ export function ConnectingView() { ConnectionController.setWcError(false); ConnectionController.connectWalletConnect(routeData?.wallet?.link_mode ?? undefined); await ConnectionController.state.wcPromise; - ConnectorController.setConnectedConnector('WALLET_CONNECT'); + await ConnectorController.setConnectedConnector('WALLET_CONNECT'); AccountController.setIsConnected(true); if (OptionsController.state.isSiweEnabled) { diff --git a/packages/wagmi/src/client.ts b/packages/wagmi/src/client.ts index 044bdef1..a35dcd8c 100644 --- a/packages/wagmi/src/client.ts +++ b/packages/wagmi/src/client.ts @@ -460,12 +460,11 @@ export class AppKit extends AppKitScaffold { } private async checkNetworkSupport(params: { chainId?: number; isConnected?: boolean }) { + // wait until the session is set + await new Promise(resolve => setTimeout(resolve, 500)); + const { isConnected = false, chainId } = params; const chain = this.wagmiConfig.chains.find((c: Chain) => c.id === chainId); - await this.getApprovedCaipNetworksData(); - const isApproved = this.getApprovedCaipNetworks().some( - network => network.id === `${ConstantsUtil.EIP155}:${params.chainId}` - ); if (chain) { const id = Number(chain.id); @@ -484,8 +483,12 @@ export class AppKit extends AppKitScaffold { } if (isConnected) { - const isSupported = !!chain && isApproved; + await this.getApprovedCaipNetworksData(); + const isApproved = this.getApprovedCaipNetworks().some( + network => network.id === `${ConstantsUtil.EIP155}:${params.chainId}` + ); + const isSupported = !!chain && isApproved; this.openUnsupportedNetworkView(isSupported); return isSupported; @@ -502,7 +505,6 @@ export class AppKit extends AppKitScaffold { isAccountChange?: boolean ) { const chain = this.wagmiConfig.chains.find((c: Chain) => c.id === chainId); - const isSupported = await this.checkNetworkSupport({ chainId, isConnected }); const isSiweEnabled = this.options?.siweConfig?.options?.enabled; if (chain || chainId) { @@ -524,6 +526,11 @@ export class AppKit extends AppKitScaffold { } } + let isSupported = true; + if (isNetworkChange) { + isSupported = await this.checkNetworkSupport({ chainId, isConnected }); + } + if (isConnected && isSupported && isSiweEnabled) { this.handleSiweChange({ isNetworkChange, isAccountChange }); }