Skip to content

Commit bb6c71e

Browse files
fix: fallback to eip1193 provider chain when switching chains is not supported (#6004)
1 parent 89beb3c commit bb6c71e

File tree

7 files changed

+2904
-631
lines changed

7 files changed

+2904
-631
lines changed

.changeset/eleven-clocks-push.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"thirdweb": patch
3+
---
4+
5+
Fallback to eip1193 provider chain when switching chain is not supported

apps/playground-web/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
"fix": "eslint ./src --fix"
1414
},
1515
"dependencies": {
16+
"@abstract-foundation/agw-client": "^1.0.1",
17+
"@abstract-foundation/agw-react": "^1.0.1",
1618
"@radix-ui/react-accordion": "^1.2.2",
1719
"@radix-ui/react-checkbox": "^1.1.3",
1820
"@radix-ui/react-dialog": "1.1.4",

apps/playground-web/src/app/connect/sign-in/button/RightSection.tsx

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
1+
import { abstractWallet } from "@abstract-foundation/agw-react/thirdweb";
12
import { XIcon } from "lucide-react";
23
import { useRouter } from "next/navigation";
34
import { useEffect, useMemo } from "react";
4-
import { ethereum } from "thirdweb/chains";
5+
import {
6+
arbitrumSepolia,
7+
baseSepolia,
8+
optimismSepolia,
9+
sepolia,
10+
} from "thirdweb/chains";
511
import {
612
ConnectButton,
713
type ConnectButtonProps,
@@ -108,12 +114,13 @@ export function RightSection(props: {
108114
showThirdwebBranding: connectOptions.ShowThirdwebBranding,
109115
requireApproval: connectOptions.requireApproval,
110116
}}
117+
chains={[sepolia, baseSepolia, optimismSepolia, arbitrumSepolia]}
111118
wallets={wallets}
112119
auth={connectOptions.enableAuth ? playgroundAuth : undefined}
113120
accountAbstraction={
114121
connectOptions.enableAccountAbstraction
115122
? {
116-
chain: ethereum,
123+
chain: sepolia,
117124
sponsorGas: true,
118125
}
119126
: undefined
@@ -171,10 +178,16 @@ export function RightSection(props: {
171178
}}
172179
locale={connectOptions.localeId}
173180
auth={connectOptions.enableAuth ? playgroundAuth : undefined}
181+
chains={[
182+
sepolia,
183+
baseSepolia,
184+
optimismSepolia,
185+
arbitrumSepolia,
186+
]}
174187
accountAbstraction={
175188
connectOptions.enableAccountAbstraction
176189
? {
177-
chain: ethereum,
190+
chain: sepolia,
178191
sponsorGas: true,
179192
}
180193
: undefined
@@ -206,7 +219,14 @@ export function RightSection(props: {
206219
* @internal
207220
*/
208221
export function getWallets(connectOptions: ConnectPlaygroundOptions) {
209-
const wallets = [...connectOptions.walletIds.map((id) => createWallet(id))];
222+
const wallets = [
223+
...connectOptions.walletIds.map((id) => {
224+
if (id === "xyz.abs") {
225+
return abstractWallet();
226+
}
227+
return createWallet(id);
228+
}),
229+
];
210230

211231
if (
212232
connectOptions.inAppWallet.enabled &&

packages/thirdweb/src/react/web/ui/ConnectWallet/Modal/SmartWalletConnectUI.tsx

Lines changed: 3 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,19 @@
11
"use client";
2-
import { ExclamationTriangleIcon } from "@radix-ui/react-icons";
32
import { useQuery } from "@tanstack/react-query";
43
import { useCallback, useEffect, useRef, useState } from "react";
54
import type { Chain } from "../../../../../chains/types.js";
65
import type { ThirdwebClient } from "../../../../../client/client.js";
76
import type { Wallet } from "../../../../../wallets/interfaces/wallet.js";
87
import type { SmartWalletOptions } from "../../../../../wallets/smart/types.js";
98
import type { WalletInfo } from "../../../../../wallets/wallet-info.js";
10-
import {
11-
fontSize,
12-
iconSize,
13-
spacing,
14-
} from "../../../../core/design-system/index.js";
159
import { useConnectionManager } from "../../../../core/providers/connection-manager.js";
1610
import { useWalletInfo } from "../../../../core/utils/wallet.js";
1711
import { LoadingScreen } from "../../../wallets/shared/LoadingScreen.js";
1812
import { getSmartWalletLocale } from "../../../wallets/smartWallet/locale/getSmartWalletLocale.js";
1913
import type { SmartWalletLocale } from "../../../wallets/smartWallet/locale/types.js";
2014
import { Spacer } from "../../components/Spacer.js";
2115
import { Spinner } from "../../components/Spinner.js";
22-
import { Container, ModalHeader } from "../../components/basic.js";
23-
import { Button } from "../../components/buttons.js";
16+
import { Container } from "../../components/basic.js";
2417
import { Text } from "../../components/text.js";
2518
import type { LocaleId } from "../../types.js";
2619
import type { ConnectLocale } from "../locale/types.js";
@@ -109,32 +102,11 @@ function SmartWalletConnecting(props: {
109102
queryKey: ["getSmartWalletLocale", props.localeId],
110103
queryFn: () => getSmartWalletLocale(props.localeId),
111104
});
112-
const { chain: smartWalletChain } = props.accountAbstraction;
113-
114105
const { personalWallet } = props;
115106
const { done } = props;
116107

117-
const [personalWalletChainId, setPersonalWalletChainId] = useState<
118-
number | undefined
119-
>(personalWallet.getChain()?.id);
120-
121-
useEffect(() => {
122-
const unsubChainChanged = personalWallet.subscribe(
123-
"chainChanged",
124-
(chain) => setPersonalWalletChainId(chain.id),
125-
);
126-
127-
return () => {
128-
unsubChainChanged();
129-
};
130-
}, [personalWallet]);
131-
132-
const wrongNetwork = personalWalletChainId !== smartWalletChain.id;
133-
134108
const [smartWalletConnectionStatus, setSmartWalletConnectionStatus] =
135109
useState<"connecting" | "connect-error" | "idle">("idle");
136-
const [personalWalletChainSwitchStatus, setPersonalWalletChainSwitchStatus] =
137-
useState<"switching" | "switch-error" | "idle">("idle");
138110
const connectionManager = useConnectionManager();
139111

140112
const handleConnect = useCallback(async () => {
@@ -168,117 +140,16 @@ function SmartWalletConnecting(props: {
168140

169141
const connectStarted = useRef(false);
170142
useEffect(() => {
171-
if (!wrongNetwork && !connectStarted.current) {
143+
if (!connectStarted.current) {
172144
handleConnect();
173145
connectStarted.current = true;
174146
}
175-
}, [handleConnect, wrongNetwork]);
147+
}, [handleConnect]);
176148

177149
if (!localeQuery.data) {
178150
return <LoadingScreen />;
179151
}
180152

181-
if (wrongNetwork) {
182-
return (
183-
<Container fullHeight animate="fadein" flex="column">
184-
<Container p="lg">
185-
<ModalHeader
186-
title={props.personalWalletInfo.name}
187-
onBack={props.onBack}
188-
/>
189-
</Container>
190-
191-
{props.size === "compact" && <Spacer y="lg" />}
192-
193-
<Container expand flex="column" center="both" p="lg">
194-
<Container p={props.size === "wide" ? "lg" : undefined}>
195-
<Container flex="row" center="x" color="danger">
196-
<ExclamationTriangleIcon
197-
width={iconSize.lg}
198-
height={iconSize.lg}
199-
/>
200-
</Container>
201-
202-
<Spacer y="md" />
203-
204-
<Text size="lg" color="primaryText" center weight={500}>
205-
{localeQuery.data.wrongNetworkScreen.title}
206-
</Text>
207-
208-
<Spacer y="lg" />
209-
210-
<Text multiline center>
211-
{localeQuery.data.wrongNetworkScreen.subtitle}
212-
</Text>
213-
214-
<Spacer y="xl" />
215-
216-
<Container flex="column" gap="md">
217-
<Button
218-
type="button"
219-
fullWidth
220-
variant="accent"
221-
style={{
222-
display: "flex",
223-
alignItems: "center",
224-
gap: spacing.sm,
225-
}}
226-
onClick={async () => {
227-
if (!personalWallet.switchChain) {
228-
setPersonalWalletChainSwitchStatus("switch-error");
229-
throw new Error("No switchChain method");
230-
}
231-
232-
try {
233-
setPersonalWalletChainSwitchStatus("switching");
234-
await personalWallet.switchChain(smartWalletChain);
235-
const newChain = personalWallet.getChain();
236-
if (newChain) {
237-
setPersonalWalletChainId(newChain.id);
238-
}
239-
setPersonalWalletChainSwitchStatus("idle");
240-
} catch (e) {
241-
console.error(e);
242-
setPersonalWalletChainSwitchStatus("switch-error");
243-
}
244-
}}
245-
>
246-
{personalWalletChainSwitchStatus === "switching"
247-
? "Switching"
248-
: "Switch Network"}
249-
{personalWalletChainSwitchStatus === "switching" && (
250-
<Spinner size="sm" color="accentButtonText" />
251-
)}
252-
</Button>
253-
254-
<Container
255-
flex="row"
256-
gap="sm"
257-
center="both"
258-
color="danger"
259-
style={{
260-
textAlign: "center",
261-
fontSize: fontSize.sm,
262-
opacity:
263-
personalWalletChainSwitchStatus === "switch-error" ? 1 : 0,
264-
transition: "opacity 200ms ease",
265-
}}
266-
>
267-
<ExclamationTriangleIcon
268-
width={iconSize.sm}
269-
height={iconSize.sm}
270-
/>
271-
<span>
272-
{localeQuery.data.wrongNetworkScreen.failedToSwitch}
273-
</span>
274-
</Container>
275-
</Container>
276-
</Container>
277-
</Container>
278-
</Container>
279-
);
280-
}
281-
282153
if (smartWalletConnectionStatus === "connect-error") {
283154
return (
284155
<Container

packages/thirdweb/src/wallets/injected/index.ts

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -79,16 +79,25 @@ export async function connectEip1193Wallet({
7979
// get the chainId the provider is on
8080
const chainId = await provider
8181
.request({ method: "eth_chainId" })
82-
.then(normalizeChainId);
82+
.then(normalizeChainId)
83+
.catch((e) => {
84+
throw new Error("Error reading chainId from provider", e);
85+
});
8386

8487
let connectedChain =
8588
chain && chain.id === chainId ? chain : getCachedChain(chainId);
8689

87-
// if we want a specific chainId and it is not the same as the provider chainId, trigger switchChain
88-
// we check for undefined chain ID since some chain-specific wallets like Abstract will not send a chain ID on connection
89-
if (chain && typeof chain.id !== "undefined" && chain.id !== chainId) {
90-
await switchChain(provider, chain);
91-
connectedChain = chain;
90+
try {
91+
// if we want a specific chainId and it is not the same as the provider chainId, trigger switchChain
92+
// we check for undefined chain ID since some chain-specific wallets like Abstract will not send a chain ID on connection
93+
if (chain && typeof chain.id !== "undefined" && chain.id !== chainId) {
94+
await switchChain(provider, chain);
95+
connectedChain = chain;
96+
}
97+
} catch {
98+
console.warn(
99+
`Error switching to chain ${chain?.id} - defaulting to wallet chain (${chainId})`,
100+
);
92101
}
93102

94103
return onConnect({

packages/thirdweb/test/vitest.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export default defineConfig({
3737
setupFiles: [join(__dirname, "./reactSetup.ts")],
3838
globalSetup: [join(__dirname, "./globalSetup.ts")],
3939
testTimeout: 90_000,
40-
retry: process.env.CI ? 3 : 0,
40+
retry: 3,
4141
maxConcurrency: 3,
4242
bail: 1,
4343
// clear any mocks between any tests

0 commit comments

Comments
 (0)