Skip to content

Commit 634bd4f

Browse files
fix: handle unsupported network gracefully, also in siwe
1 parent 75516c9 commit 634bd4f

File tree

17 files changed

+145
-88
lines changed

17 files changed

+145
-88
lines changed

packages/core/src/__tests__/controllers/NetworkController.test.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ const client: NetworkControllerClient = {
1919
const initialState = {
2020
_client: client,
2121
supportsAllNetworks: true,
22+
isUnsupportedNetwork: false,
2223
smartAccountEnabledNetworks: []
2324
};
2425

@@ -74,13 +75,13 @@ describe('NetworkController', () => {
7475
expect(NetworkController.state.requestedCaipNetworks).toEqual(requestedCaipNetworks);
7576
});
7677

77-
it('should validate if active network is in requested networks', () => {
78-
NetworkController.setRequestedCaipNetworks(requestedCaipNetworks);
79-
80-
NetworkController.setCaipNetwork({ id: 'eip155:1', name: 'Ethereum' });
81-
expect(NetworkController.isActiveNetworkInRequestedNetworks()).toBe(true);
78+
it('should set isUnsupportedNetwork to true when setUnsupportedNetwork is called', () => {
79+
NetworkController.setUnsupportedNetwork(true);
80+
expect(NetworkController.state.isUnsupportedNetwork).toEqual(true);
81+
});
8282

83-
NetworkController.setCaipNetwork({ id: 'eip155:99', name: 'Unknown Network' });
84-
expect(NetworkController.isActiveNetworkInRequestedNetworks()).toBe(false);
83+
it('should set isUnsupportedNetwork to false when setUnsupportedNetwork is called', () => {
84+
NetworkController.setUnsupportedNetwork(false);
85+
expect(NetworkController.state.isUnsupportedNetwork).toEqual(false);
8586
});
8687
});

packages/core/src/controllers/NetworkController.ts

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,15 @@ export interface NetworkControllerState {
2121
requestedCaipNetworks?: CaipNetwork[];
2222
approvedCaipNetworkIds?: CaipNetworkId[];
2323
smartAccountEnabledNetworks: number[];
24+
isUnsupportedNetwork?: boolean;
2425
}
2526

2627
// -- State --------------------------------------------- //
2728
const state = proxy<NetworkControllerState>({
2829
supportsAllNetworks: true,
2930
defaultCaipNetwork: undefined,
30-
smartAccountEnabledNetworks: []
31+
smartAccountEnabledNetworks: [],
32+
isUnsupportedNetwork: false
3133
});
3234

3335
// -- Controller ---------------------------------------- //
@@ -57,6 +59,10 @@ export const NetworkController = {
5759
PublicStateController.set({ selectedNetworkId: caipNetwork?.id });
5860
},
5961

62+
setUnsupportedNetwork(isUnsupportedNetwork: NetworkControllerState['isUnsupportedNetwork']) {
63+
state.isUnsupportedNetwork = isUnsupportedNetwork;
64+
},
65+
6066
setRequestedCaipNetworks(requestedNetworks: NetworkControllerState['requestedCaipNetworks']) {
6167
state.requestedCaipNetworks = requestedNetworks;
6268
},
@@ -111,16 +117,9 @@ export const NetworkController = {
111117

112118
resetNetwork() {
113119
state.caipNetwork = state.defaultCaipNetwork || undefined;
120+
state.isUnsupportedNetwork = undefined;
114121
state.approvedCaipNetworkIds = undefined;
115122
state.supportsAllNetworks = true;
116123
state.smartAccountEnabledNetworks = [];
117-
},
118-
119-
isActiveNetworkInRequestedNetworks() {
120-
if (!state.caipNetwork || !state.requestedCaipNetworks?.length) {
121-
return false;
122-
}
123-
124-
return state.requestedCaipNetworks.some(network => network.id === state.caipNetwork?.id);
125124
}
126125
};

packages/core/src/utils/NetworkUtil.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@ export const NetworkUtil = {
1010
async handleNetworkSwitch(network: CaipNetwork) {
1111
const { isConnected } = AccountController.state;
1212
const { caipNetwork, approvedCaipNetworkIds, supportsAllNetworks } = NetworkController.state;
13-
const isAuthConnected = ConnectorController.state.connectedConnector === 'AUTH';
13+
const isAuthConnector = ConnectorController.state.connectedConnector === 'AUTH';
1414
let eventData = null;
1515

1616
if (isConnected && caipNetwork?.id !== network.id) {
17-
if (approvedCaipNetworkIds?.includes(network.id) && !isAuthConnected) {
17+
if (approvedCaipNetworkIds?.includes(network.id) && !isAuthConnector) {
1818
await NetworkController.switchActiveNetwork(network);
1919
RouterUtil.navigateAfterNetworkSwitch(['ConnectingSiwe']);
2020
eventData = { type: 'SWITCH_NETWORK', networkId: network.id };
21-
} else if (supportsAllNetworks || isAuthConnected) {
21+
} else if (supportsAllNetworks || isAuthConnector) {
2222
RouterController.push('SwitchNetwork', { network });
2323
}
2424
} else if (!isConnected) {

packages/core/src/utils/RouterUtil.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,20 @@ export const RouterUtil = {
88
}
99

1010
const { history } = RouterController.state;
11-
const networkSelectIndex = history.findIndex(
12-
name => name === 'Networks' || name === 'UnsupportedChain'
13-
);
1411

15-
if (networkSelectIndex >= 1) {
16-
RouterController.goBackToIndex(networkSelectIndex - 1);
12+
// Find the last occurrence of 'Networks' or 'UnsupportedChain'
13+
let lastNetworkViewIndex = -1;
14+
for (let i = history.length - 1; i >= 0; i--) {
15+
if (history[i] === 'Networks' || history[i] === 'UnsupportedChain') {
16+
lastNetworkViewIndex = i;
17+
break;
18+
}
19+
}
20+
21+
// Case 1: Navigated from a network selection view deeper in the app
22+
if (lastNetworkViewIndex > 0) {
23+
// Go to the view right before the network selection
24+
RouterController.goBackToIndex(lastNetworkViewIndex - 1);
1725
} else {
1826
ModalController.close();
1927
}

packages/ethers/src/client.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -766,6 +766,11 @@ export class AppKit extends AppKitScaffold {
766766
if (this.chains) {
767767
const chain = this.chains.find(c => c.chainId === chainId);
768768

769+
if (isConnected) {
770+
// If the network is not supported, set the unsupported network state
771+
this.setUnsupportedNetwork(!chain);
772+
}
773+
769774
if (chain) {
770775
const caipChainId: CaipNetworkId = `${ConstantsUtil.EIP155}:${chain.chainId}`;
771776

packages/ethers5/src/client.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -746,6 +746,11 @@ export class AppKit extends AppKitScaffold {
746746
if (this.chains) {
747747
const chain = this.chains.find(c => c.chainId === chainId);
748748

749+
if (isConnected) {
750+
// If the network is not supported, set the unsupported network state
751+
this.setUnsupportedNetwork(!chain);
752+
}
753+
749754
if (chain) {
750755
const caipChainId: CaipNetworkId = `${ConstantsUtil.EIP155}:${chain.chainId}`;
751756

packages/scaffold/src/client.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,12 @@ export class AppKitScaffold {
274274
}
275275
}
276276

277+
protected setUnsupportedNetwork(
278+
isUnsupportedNetwork: NetworkControllerState['isUnsupportedNetwork']
279+
) {
280+
NetworkController.setUnsupportedNetwork(isUnsupportedNetwork);
281+
}
282+
277283
// -- Private ------------------------------------------------------------------
278284
private async initControllers(options: ScaffoldOptions) {
279285
this.initAsyncValues(options);

packages/scaffold/src/modal/w3m-modal/index.tsx

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export function AppKit() {
3131
const { open, loading } = useSnapshot(ModalController.state);
3232
const { connectors, connectedConnector } = useSnapshot(ConnectorController.state);
3333
const { caipAddress, isConnected } = useSnapshot(AccountController.state);
34-
const { caipNetwork } = useSnapshot(NetworkController.state);
34+
const { isUnsupportedNetwork } = useSnapshot(NetworkController.state);
3535
const { themeMode, themeVariables } = useSnapshot(ThemeController.state);
3636
const [isNetworkStateStable, setIsNetworkStateStable] = useState(false);
3737
const { height } = useWindowDimensions();
@@ -42,8 +42,21 @@ export function AppKit() {
4242
const AuthView = authProvider?.AuthView;
4343
const SocialView = authProvider?.Webview;
4444
const showAuth = !connectedConnector || connectedConnector === 'AUTH';
45+
const disableClose = ['UnsupportedChain', 'ConnectingSiwe'].includes(RouterController.state.view);
46+
47+
const onBackdropPress = () => {
48+
if (disableClose) {
49+
return;
50+
}
51+
52+
return ModalController.close();
53+
};
4554

4655
const onBackButtonPress = () => {
56+
if (disableClose) {
57+
return;
58+
}
59+
4760
if (RouterController.state.history.length > 1) {
4861
return RouterController.goBack();
4962
}
@@ -74,6 +87,12 @@ export function AppKit() {
7487
TransactionsController.resetTransactions();
7588

7689
if (OptionsController.state.isSiweEnabled) {
90+
if (NetworkController.state.isUnsupportedNetwork) {
91+
// If the network is unsupported, don't do siwe stuff until user changes network
92+
93+
return;
94+
}
95+
7796
const newNetworkId = CoreHelperUtil.getNetworkId(address);
7897

7998
const { signOutOnAccountChange, signOutOnNetworkChange } =
@@ -134,21 +153,14 @@ export function AppKit() {
134153
}, [isConnected]);
135154

136155
useEffect(() => {
137-
if (isConnected && caipNetwork && isNetworkStateStable) {
138-
const isNetworkSupported = NetworkController.isActiveNetworkInRequestedNetworks();
139-
if (!isNetworkSupported) {
140-
const currentView = RouterController.state.view;
141-
// Only push/open if not already on UnsupportedChain or actively choosing a network
142-
if (currentView !== 'UnsupportedChain' && currentView !== 'Networks') {
143-
if (ModalController.state.open) {
144-
RouterController.push('UnsupportedChain');
145-
} else {
146-
ModalController.open({ view: 'UnsupportedChain' });
147-
}
148-
}
156+
if (isUnsupportedNetwork && isNetworkStateStable) {
157+
if (ModalController.state.open) {
158+
RouterController.reset('UnsupportedChain');
159+
} else {
160+
ModalController.open({ view: 'UnsupportedChain' });
149161
}
150162
}
151-
}, [caipNetwork, isConnected, isNetworkStateStable]);
163+
}, [isUnsupportedNetwork, isNetworkStateStable]);
152164

153165
return (
154166
<>
@@ -163,7 +175,7 @@ export function AppKit() {
163175
hideModalContentWhileAnimating
164176
propagateSwipe
165177
onModalHide={handleClose}
166-
onBackdropPress={ModalController.close}
178+
onBackdropPress={onBackdropPress}
167179
onBackButtonPress={onBackButtonPress}
168180
testID="w3m-modal"
169181
>

packages/scaffold/src/modal/w3m-network-button/index.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,8 @@ export function NetworkButton({ disabled, style }: NetworkButtonProps) {
2323
const { isConnected } = useSnapshot(AccountController.state);
2424
const { themeMode, themeVariables } = useSnapshot(ThemeController.state);
2525

26-
const isNetworkSupported = NetworkController.isActiveNetworkInRequestedNetworks();
27-
2826
const onNetworkPress = () => {
29-
if (AccountController.state.isConnected && !isNetworkSupported) {
27+
if (AccountController.state.isConnected && NetworkController.state.isUnsupportedNetwork) {
3028
ModalController.open({ view: 'UnsupportedChain' });
3129
} else {
3230
ModalController.open({ view: 'Networks' });
@@ -37,7 +35,11 @@ export function NetworkButton({ disabled, style }: NetworkButtonProps) {
3735
});
3836
};
3937

40-
const buttonText = UiUtil.getNetworkButtonText(isConnected, caipNetwork, isNetworkSupported);
38+
const buttonText = UiUtil.getNetworkButtonText(
39+
isConnected,
40+
caipNetwork,
41+
NetworkController.state.isUnsupportedNetwork
42+
);
4143

4244
return (
4345
<ThemeProvider themeMode={themeMode} themeVariables={themeVariables}>

packages/scaffold/src/partials/w3m-header/index.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export function Header() {
1919
RouterController.push('WhatIsAWallet');
2020
EventsController.sendEvent({ type: 'track', event: 'CLICK_WALLET_HELP' });
2121
};
22+
const showButtons = !['ConnectingSiwe', 'UnsupportedChain'].includes(view);
2223

2324
const headings = (_data: RouterControllerState['data'], _view: RouterControllerState['view']) => {
2425
const connectorName = _data?.connector?.name;
@@ -100,9 +101,7 @@ export function Header() {
100101
};
101102

102103
const dynamicButtonTemplate = () => {
103-
const noButtonViews = ['ConnectingSiwe'];
104-
105-
if (noButtonViews.includes(RouterController.state.view)) {
104+
if (!showButtons) {
106105
return <FlexView style={styles.iconPlaceholder} />;
107106
}
108107

@@ -130,7 +129,11 @@ export function Header() {
130129
<Text variant="paragraph-600" numberOfLines={1} testID="header-text">
131130
{header}
132131
</Text>
133-
<IconLink icon="close" size="md" onPress={handleClose} testID="header-close" />
132+
{showButtons ? (
133+
<IconLink icon="close" size="md" onPress={handleClose} testID="header-close" />
134+
) : (
135+
<FlexView style={styles.iconPlaceholder} />
136+
)}
134137
</FlexView>
135138
);
136139
}

packages/scaffold/src/utils/UiUtil.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export const UiUtil = {
3333
getNetworkButtonText(
3434
isConnected: boolean,
3535
caipNetwork: CaipNetwork | undefined,
36-
isNetworkSupported: boolean
36+
isUnsupportedNetwork?: boolean
3737
): string {
3838
let buttonText: string;
3939

@@ -46,10 +46,10 @@ export const UiUtil = {
4646
} else {
4747
// isConnected is true
4848
if (caipNetwork) {
49-
if (isNetworkSupported) {
50-
buttonText = caipNetwork.name ?? 'Unknown Network';
51-
} else {
49+
if (isUnsupportedNetwork) {
5250
buttonText = 'Switch Network';
51+
} else {
52+
buttonText = caipNetwork.name ?? 'Unknown Network';
5353
}
5454
} else {
5555
buttonText = 'Select Network';

packages/scaffold/src/views/w3m-account-default-view/index.tsx

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ export function AccountDefaultView() {
4848
} = useSnapshot(AccountController.state);
4949
const { loading } = useSnapshot(ModalController.state);
5050
const [disconnecting, setDisconnecting] = useState(false);
51-
const { caipNetwork } = useSnapshot(NetworkController.state);
51+
const { caipNetwork, isUnsupportedNetwork } = useSnapshot(NetworkController.state);
5252
const { connectedConnector } = useSnapshot(ConnectorController.state);
5353
const { connectedSocialProvider } = useSnapshot(ConnectionController.state);
5454
const { features } = useSnapshot(OptionsController.state);
@@ -61,7 +61,6 @@ export function AccountDefaultView() {
6161
const showBack = history.length > 1;
6262
const showSwitchAccountType = isAuth && NetworkController.checkIfSmartAccountEnabled();
6363
const { padding } = useCustomDimensions();
64-
const isNetworkSupported = NetworkController.isActiveNetworkInRequestedNetworks();
6564

6665
async function onDisconnect() {
6766
setDisconnecting(true);
@@ -149,7 +148,7 @@ export function AccountDefaultView() {
149148
};
150149

151150
const onNetworkPress = () => {
152-
if (AccountController.state.isConnected && !isNetworkSupported) {
151+
if (AccountController.state.isConnected && NetworkController.state.isUnsupportedNetwork) {
153152
RouterController.push('UnsupportedChain');
154153
} else {
155154
RouterController.push('Networks');
@@ -245,17 +244,25 @@ export function AccountDefaultView() {
245244
)}
246245
<ListItem
247246
chevron
248-
icon="networkPlaceholder"
249-
iconColor="accent-100"
250-
iconBackgroundColor="accent-glass-015"
247+
icon={isUnsupportedNetwork ? 'infoCircle' : 'networkPlaceholder'}
248+
iconColor={isUnsupportedNetwork ? 'error-100' : 'accent-100'}
249+
iconBackgroundColor={isUnsupportedNetwork ? 'error-glass-015' : 'accent-glass-015'}
251250
imageSrc={networkImage}
252251
imageHeaders={ApiController._getApiHeaders()}
253252
onPress={onNetworkPress}
254253
testID="button-network"
255254
style={styles.actionButton}
256255
>
257-
<Text numberOfLines={1} color="fg-100" testID="account-select-network-text">
258-
{ScaffoldUiUtil.getNetworkButtonText(isConnected, caipNetwork, isNetworkSupported)}
256+
<Text
257+
numberOfLines={1}
258+
color={isUnsupportedNetwork ? 'error-100' : 'fg-100'}
259+
testID="account-select-network-text"
260+
>
261+
{ScaffoldUiUtil.getNetworkButtonText(
262+
isConnected,
263+
caipNetwork,
264+
isUnsupportedNetwork
265+
)}
259266
</Text>
260267
</ListItem>
261268

packages/scaffold/src/views/w3m-account-view/index.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,7 @@ export function AccountView() {
3939
};
4040

4141
const onNetworkPress = () => {
42-
const isNetworkSupported = NetworkController.isActiveNetworkInRequestedNetworks();
43-
44-
if (AccountController.state.isConnected && !isNetworkSupported) {
42+
if (AccountController.state.isConnected && NetworkController.state.isUnsupportedNetwork) {
4543
RouterController.push('UnsupportedChain');
4644
} else {
4745
RouterController.push('Networks');

0 commit comments

Comments
 (0)