diff --git a/.changeset/every-sides-invent.md b/.changeset/every-sides-invent.md new file mode 100644 index 00000000000..c33f2cd11c9 --- /dev/null +++ b/.changeset/every-sides-invent.md @@ -0,0 +1,5 @@ +--- +"thirdweb": patch +--- + +Automatically trigger SIWE sign in when a wallet is connected diff --git a/apps/playground-web/src/components/auth/auth-button.tsx b/apps/playground-web/src/components/auth/auth-button.tsx index 5e39d170511..08de975f3c2 100644 --- a/apps/playground-web/src/components/auth/auth-button.tsx +++ b/apps/playground-web/src/components/auth/auth-button.tsx @@ -8,11 +8,25 @@ import { } from "@/app/connect/auth/server/actions/auth"; import { THIRDWEB_CLIENT } from "@/lib/client"; import { ConnectButton } from "thirdweb/react"; +import { createWallet, inAppWallet } from "thirdweb/wallets"; export function AuthButton() { return ( isLoggedIn(address), doLogin: (params) => login(params), diff --git a/packages/thirdweb/src/react/native/ui/connect/ConnectButton.tsx b/packages/thirdweb/src/react/native/ui/connect/ConnectButton.tsx index 683f75a9c51..ae053189125 100644 --- a/packages/thirdweb/src/react/native/ui/connect/ConnectButton.tsx +++ b/packages/thirdweb/src/react/native/ui/connect/ConnectButton.tsx @@ -47,7 +47,10 @@ export function ConnectButton(props: ConnectButtonProps) { const status = useActiveWalletConnectionStatus(); const connectionManager = useConnectionManager(); const siweAuth = useSiweAuth(wallet, account, props.auth); - useAutoConnect(props); + useAutoConnect({ + ...props, + siweAuth: siweAuth, + }); const fadeAnim = useRef(new Animated.Value(0)); // For background opacity const slideAnim = useRef(new Animated.Value(screenHeight)); // For bottom sheet position diff --git a/packages/thirdweb/src/react/web/ui/ConnectWallet/ConnectButton.tsx b/packages/thirdweb/src/react/web/ui/ConnectWallet/ConnectButton.tsx index 0a54aeb85f6..442f67abecc 100644 --- a/packages/thirdweb/src/react/web/ui/ConnectWallet/ConnectButton.tsx +++ b/packages/thirdweb/src/react/web/ui/ConnectWallet/ConnectButton.tsx @@ -297,6 +297,9 @@ export function ConnectButton(props: ConnectButtonProps) { ); const localeQuery = useConnectLocale(props.locale || "en_US"); const connectionManager = useConnectionManager(); + const activeAccount = useActiveAccount(); + const activeWallet = useActiveWallet(); + const siweAuth = useSiweAuth(activeWallet, activeAccount, props.auth); usePreloadWalletProviders({ wallets, @@ -337,6 +340,7 @@ export function ConnectButton(props: ConnectButtonProps) { } accountAbstraction={props.accountAbstraction} onConnect={props.onConnect} + siweAuth={siweAuth} /> ); @@ -362,7 +366,11 @@ export function ConnectButton(props: ConnectButtonProps) { return ( - + ; }, ) { - const activeWallet = useActiveWallet(); + const siweAuth = props.siweAuth; const activeAccount = useActiveAccount(); - const siweAuth = useSiweAuth(activeWallet, activeAccount, props.auth); const [showSignatureModal, setShowSignatureModal] = useState(false); // if wallet gets disconnected suddently, close the signature modal if it's open diff --git a/packages/thirdweb/src/react/web/ui/ConnectWallet/Modal/ConnectEmbed.tsx b/packages/thirdweb/src/react/web/ui/ConnectWallet/Modal/ConnectEmbed.tsx index 62570702010..d876f3ac138 100644 --- a/packages/thirdweb/src/react/web/ui/ConnectWallet/Modal/ConnectEmbed.tsx +++ b/packages/thirdweb/src/react/web/ui/ConnectWallet/Modal/ConnectEmbed.tsx @@ -247,6 +247,7 @@ export function ConnectEmbed(props: ConnectEmbedProps) { chain={preferredChain} appMetadata={props.appMetadata} client={props.client} + siweAuth={siweAuth} wallets={wallets} accountAbstraction={props.accountAbstraction} timeout={ diff --git a/packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/fiat/OnRampScreen.tsx b/packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/fiat/OnRampScreen.tsx index cc49c2459eb..971651d215b 100644 --- a/packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/fiat/OnRampScreen.tsx +++ b/packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/fiat/OnRampScreen.tsx @@ -23,10 +23,7 @@ import { sendTransaction } from "../../../../../../../transaction/actions/send-t import type { WaitForReceiptOptions } from "../../../../../../../transaction/actions/wait-for-tx-receipt.js"; import { waitForReceipt } from "../../../../../../../transaction/actions/wait-for-tx-receipt.js"; import { formatNumber } from "../../../../../../../utils/formatNumber.js"; -import { isEcosystemWallet } from "../../../../../../../wallets/ecosystem/is-ecosystem-wallet.js"; -import { isInAppWallet } from "../../../../../../../wallets/in-app/core/wallet/index.js"; -import type { Wallet } from "../../../../../../../wallets/interfaces/wallet.js"; -import { isSmartWallet } from "../../../../../../../wallets/smart/is-smart-wallet.js"; +import { isInAppSigner } from "../../../../../../../wallets/in-app/core/wallet/is-in-app-signer.js"; import { spacing } from "../../../../../../core/design-system/index.js"; import { useChainName } from "../../../../../../core/hooks/others/useChainQuery.js"; import { useBuyWithCryptoStatus } from "../../../../../../core/hooks/pay/useBuyWithCryptoStatus.js"; @@ -779,20 +776,3 @@ function useSwapMutation(props: { }, }); } - -function isInAppSigner(options: { - wallet: Wallet; - connectedWallets: Wallet[]; -}) { - const isInAppOrEcosystem = (w: Wallet) => - isInAppWallet(w) || isEcosystemWallet(w); - const isSmartWalletWithAdmin = - isSmartWallet(options.wallet) && - options.connectedWallets.some( - (w) => - isInAppOrEcosystem(w) && - w.getAccount()?.address?.toLowerCase() === - options.wallet.getAdminAccount?.()?.address?.toLowerCase(), - ); - return isInAppOrEcosystem(options.wallet) || isSmartWalletWithAdmin; -} diff --git a/packages/thirdweb/src/react/web/ui/PayEmbed.tsx b/packages/thirdweb/src/react/web/ui/PayEmbed.tsx index d7efb970c35..b123511b02c 100644 --- a/packages/thirdweb/src/react/web/ui/PayEmbed.tsx +++ b/packages/thirdweb/src/react/web/ui/PayEmbed.tsx @@ -9,13 +9,19 @@ import type { AppMetadata } from "../../../wallets/types.js"; import type { WalletId } from "../../../wallets/wallet-types.js"; import { CustomThemeProvider } from "../../core/design-system/CustomThemeProvider.js"; import type { Theme } from "../../core/design-system/index.js"; -import type { SiweAuthOptions } from "../../core/hooks/auth/useSiweAuth.js"; +import { + type SiweAuthOptions, + useSiweAuth, +} from "../../core/hooks/auth/useSiweAuth.js"; import type { ConnectButton_connectModalOptions, PayUIOptions, } from "../../core/hooks/connection/ConnectButtonProps.js"; +import { useActiveAccount } from "../../core/hooks/wallets/useActiveAccount.js"; +import { useActiveWallet } from "../../core/hooks/wallets/useActiveWallet.js"; import { useConnectionManager } from "../../core/providers/connection-manager.js"; import type { SupportedTokens } from "../../core/utils/defaultTokens.js"; +import { AutoConnect } from "../../web/ui/AutoConnect/AutoConnect.js"; import { EmbedContainer } from "./ConnectWallet/Modal/ConnectEmbed.js"; import { useConnectLocale } from "./ConnectWallet/locale/getConnectLocale.js"; import BuyScreen from "./ConnectWallet/screens/Buy/BuyScreen.js"; @@ -300,6 +306,13 @@ export function PayEmbed(props: PayEmbedProps) { const [screen, setScreen] = useState<"buy" | "execute-tx">("buy"); const theme = props.theme || "dark"; const connectionManager = useConnectionManager(); + const activeAccount = useActiveAccount(); + const activeWallet = useActiveWallet(); + const siweAuth = useSiweAuth( + activeWallet, + activeAccount, + props.connectOptions?.auth, + ); // Add props.chain and props.chains to defined chains store useEffect(() => { @@ -342,6 +355,7 @@ export function PayEmbed(props: PayEmbedProps) { } else { content = ( <> + {screen === "buy" && ( w.id === result?.walletId); - if (result?.authCookie && wallet) { + const wallet = wallets.find((w) => w.id === urlToken?.walletId); + if (urlToken?.authCookie && wallet) { const clientStorage = new ClientScopedStorage({ storage, clientId: props.client.clientId, @@ -97,17 +97,17 @@ const _autoConnectCore = async ({ } : undefined, }); - await clientStorage.saveAuthCookie(result.authCookie); + await clientStorage.saveAuthCookie(urlToken.authCookie); } - if (result?.walletId) { - lastActiveWalletId = result.walletId; - lastConnectedWalletIds = lastConnectedWalletIds?.includes(result.walletId) + if (urlToken?.walletId) { + lastActiveWalletId = urlToken.walletId; + lastConnectedWalletIds = lastConnectedWalletIds?.includes(urlToken.walletId) ? lastConnectedWalletIds - : [result.walletId, ...(lastConnectedWalletIds || [])]; + : [urlToken.walletId, ...(lastConnectedWalletIds || [])]; } - if (result?.authProvider) { - await setLastAuthProvider?.(result.authProvider, storage); + if (urlToken?.authProvider) { + await setLastAuthProvider?.(urlToken.authProvider, storage); } // if no wallets were last connected or we didn't receive an auth token @@ -132,7 +132,7 @@ const _autoConnectCore = async ({ wallet: activeWallet, client: props.client, lastConnectedChain, - authResult: result?.authResult, + authResult: urlToken?.authResult, }), { ms: timeout, @@ -183,13 +183,18 @@ const _autoConnectCore = async ({ wallet, client: props.client, lastConnectedChain, - authResult: result?.authResult, + authResult: urlToken?.authResult, }); manager.addConnectedWallet(wallet); } catch { // no-op } } + + // Auto-login with SIWE + if (urlToken && activeWallet && props.siweAuth?.requiresAuth) { + await props.siweAuth?.doLogin(); + } manager.isAutoConnecting.setValue(false); return autoConnected; // useQuery needs a return value }; diff --git a/packages/thirdweb/src/wallets/connection/types.ts b/packages/thirdweb/src/wallets/connection/types.ts index a08ef8bbd6c..2af5015ad68 100644 --- a/packages/thirdweb/src/wallets/connection/types.ts +++ b/packages/thirdweb/src/wallets/connection/types.ts @@ -118,4 +118,12 @@ export type AutoConnectProps = { * Callback to be called when the connection is timeout-ed */ onTimeout?: () => void; + + /** + * @hidden + */ + siweAuth?: { + requiresAuth: boolean; + doLogin: () => Promise; + }; }; diff --git a/packages/thirdweb/src/wallets/in-app/core/wallet/is-in-app-signer.ts b/packages/thirdweb/src/wallets/in-app/core/wallet/is-in-app-signer.ts new file mode 100644 index 00000000000..48a57680a8c --- /dev/null +++ b/packages/thirdweb/src/wallets/in-app/core/wallet/is-in-app-signer.ts @@ -0,0 +1,21 @@ +import { isEcosystemWallet } from "../../../../wallets/ecosystem/is-ecosystem-wallet.js"; +import type { Wallet } from "../../../interfaces/wallet.js"; +import { isSmartWallet } from "../../../smart/index.js"; +import { isInAppWallet } from "./index.js"; + +export function isInAppSigner(options: { + wallet: Wallet; + connectedWallets: Wallet[]; +}) { + const isInAppOrEcosystem = (w: Wallet) => + isInAppWallet(w) || isEcosystemWallet(w); + const isSmartWalletWithAdmin = + isSmartWallet(options.wallet) && + options.connectedWallets.some( + (w) => + isInAppOrEcosystem(w) && + w.getAccount()?.address?.toLowerCase() === + options.wallet.getAdminAccount?.()?.address?.toLowerCase(), + ); + return isInAppOrEcosystem(options.wallet) || isSmartWalletWithAdmin; +}