From 5974edc6d0915d86b116cff9386bf2e6dbc1d34c Mon Sep 17 00:00:00 2001 From: Manan Tank Date: Thu, 7 Dec 2023 04:56:34 +0530 Subject: [PATCH 01/21] Fix double connection, add connect-embed --- .../thirdweb-wallet-provider copy.tsx | 682 ++++++++++++++++++ .../providers/thirdweb-wallet-provider.tsx | 422 +++++++---- packages/react-core/src/core/types/wallet.ts | 160 +++- packages/react-core/src/evm/index.ts | 3 + .../ChooseWallet/ChooseWallet.tsx | 26 +- .../ChooseWallet/ChooseWalletContent.tsx | 25 +- .../ConnectWalletFlow/ConnectWalletFlow.tsx | 33 +- .../SmartWallet/SmartWalletFlow.tsx | 20 + .../src/evm/wallets/wallets/smart-wallet.tsx | 34 +- packages/react/src/evm/index.ts | 11 +- packages/react/src/evm/locales/en.ts | 2 + packages/react/src/evm/locales/es.ts | 2 + packages/react/src/evm/locales/ja.ts | 2 + packages/react/src/evm/locales/types.ts | 2 + .../providers/wallet-ui-states-provider.tsx | 9 + .../wallet/ConnectWallet/ConnectWallet.tsx | 6 - .../src/wallet/ConnectWallet/Details.tsx | 2 + .../ConnectWallet/Modal/ConnectEmbed.tsx | 259 +++++++ .../ConnectWallet/Modal/ConnectModal.tsx | 127 ++-- .../Modal/ConnectModalInline.tsx | 128 ++-- .../src/wallet/ConnectWallet/Modal/screen.tsx | 10 +- .../wallet/ConnectWallet/SignatureScreen.tsx | 33 +- .../wallet/ConnectWallet/WalletSelector.tsx | 102 ++- .../ConnectWallet/screens/ScanScreen.tsx | 5 +- .../ConnectWallet/screens/WCOpenUri.tsx | 57 +- .../ExtensionORWCConnectionUI.tsx} | 112 +-- .../wallets/_common/WCConnectableWallet.ts | 11 + .../WalletConnectScanUI.tsx} | 52 +- .../src/wallet/wallets/coin98/Coin98Scan.tsx | 65 -- .../wallet/wallets/coin98/coin98Wallet.tsx | 44 +- .../wallet/wallets/coin98/coin98WalletUris.ts | 5 - .../wallets/coinbase/CoinbaseConnectUI.tsx | 14 +- .../wallet/wallets/coinbase/CoinbaseScan.tsx | 16 +- .../wallets/coinbase/coinbaseWallet.tsx | 6 +- .../coreWallet/CoreWalletConnectUI.tsx | 148 ---- .../wallet/wallets/coreWallet/coreWallet.tsx | 47 +- .../wallets/coreWallet/coreWalletUris.ts | 5 - .../defiWallet/CryptoDefiWalletConnectUI.tsx | 150 ---- .../defiWallet/CryptoDefiWalletScan.tsx | 65 -- .../wallets/defiWallet/cryptoDefiWallet.tsx | 49 +- .../defiWallet/cryptoDefiWalletUris.ts | 5 - .../embeddedWallet/EmbeddedWalletFormUI.tsx | 27 +- .../EmbeddedWalletOTPLoginUI.tsx | 10 +- .../EmbeddedWalletSocialLogin.tsx | 23 +- .../wallets/embeddedWallet/embeddedWallet.tsx | 7 + .../wallet/wallets/frame/FrameConnectUI.tsx | 8 +- .../src/wallet/wallets/headlessConnectUI.tsx | 105 ++- .../wallets/localWallet/CreateLocalWallet.tsx | 13 +- .../wallets/localWallet/ExportLocalWallet.tsx | 10 +- .../wallets/localWallet/ImportLocalWallet.tsx | 6 +- .../localWallet/LocalWalletConnectUI.tsx | 8 + .../localWallet/ReconnectLocalWallet.tsx | 15 +- .../localWallet/overrideConfirmation.tsx | 5 +- .../src/wallet/wallets/magic/magicLink.tsx | 18 +- .../wallets/metamask/MetamaskConnectUI.tsx | 13 +- .../wallet/wallets/metamask/MetamaskScan.tsx | 34 +- .../src/wallet/wallets/okx/OKXConnectUI.tsx | 12 +- .../react/src/wallet/wallets/okx/OKXScan.tsx | 30 +- .../wallet/wallets/oneKey/OneKeyConnectUI.tsx | 148 ---- .../src/wallet/wallets/oneKey/OneKeyScan.tsx | 65 -- .../wallet/wallets/oneKey/oneKeyWallet.tsx | 44 +- .../wallet/wallets/oneKey/oneKeyWalletUris.ts | 5 - .../src/wallet/wallets/paper/PaperFormUI.tsx | 24 +- .../wallet/wallets/paper/PaperGoogleLogin.tsx | 24 +- .../wallet/wallets/paper/PaperOTPLoginUI.tsx | 8 +- .../src/wallet/wallets/paper/paperWallet.tsx | 6 + .../wallets/phantom/PhantomConnectUI.tsx | 8 +- .../wallet/wallets/rabby/RabbyConnectUI.tsx | 10 +- .../src/wallet/wallets/rabby/RabbyScan.tsx | 19 +- .../wallets/rainbow/RainbowConnectUI.tsx | 143 ---- .../wallet/wallets/rainbow/RainbowScan.tsx | 65 -- .../wallet/wallets/rainbow/RainbowWallet.tsx | 45 +- .../wallets/rainbow/rainbowWalletUris.ts | 5 - .../src/wallet/wallets/safe/SelectAccount.tsx | 34 +- .../wallets/safe/SelectPersonalWallet.tsx | 13 +- .../src/wallet/wallets/safe/safeWallet.tsx | 41 +- .../smartWallet/SmartWalletConnecting.tsx | 45 +- .../wallets/smartWallet/smartWallet.tsx | 133 +++- .../wallets/trustWallet/TrustConnectUI.tsx | 142 ---- .../wallet/wallets/trustWallet/TrustScan.tsx | 65 -- .../wallets/trustWallet/TrustWallet.tsx | 51 +- .../wallets/trustWallet/trustWalletUris.ts | 5 - .../walletConnect/WalletConnectScan.tsx | 35 +- .../wallets/walletConnect/walletConnect.tsx | 5 +- .../wallet/wallets/zerion/ZerionConnectUI.tsx | 138 ---- .../src/wallet/wallets/zerion/ZerionScan.tsx | 65 -- .../wallet/wallets/zerion/zerionWallet.tsx | 51 +- .../wallet/wallets/zerion/zerionWalletUris.ts | 5 - .../iFrameCommunication/IframeCommunicator.ts | 2 +- .../rainbow/getInjectedRainbowProvider.ts | 1 + 90 files changed, 2740 insertions(+), 2010 deletions(-) create mode 100644 packages/react-core/src/core/providers/thirdweb-wallet-provider copy.tsx create mode 100644 packages/react/src/wallet/ConnectWallet/Modal/ConnectEmbed.tsx rename packages/react/src/wallet/wallets/{coin98/Coin98ConnectUI.tsx => _common/ExtensionORWCConnectionUI.tsx} (53%) create mode 100644 packages/react/src/wallet/wallets/_common/WCConnectableWallet.ts rename packages/react/src/wallet/wallets/{coreWallet/CoreWalletScan.tsx => _common/WalletConnectScanUI.tsx} (51%) delete mode 100644 packages/react/src/wallet/wallets/coin98/Coin98Scan.tsx delete mode 100644 packages/react/src/wallet/wallets/coin98/coin98WalletUris.ts delete mode 100644 packages/react/src/wallet/wallets/coreWallet/CoreWalletConnectUI.tsx delete mode 100644 packages/react/src/wallet/wallets/coreWallet/coreWalletUris.ts delete mode 100644 packages/react/src/wallet/wallets/defiWallet/CryptoDefiWalletConnectUI.tsx delete mode 100644 packages/react/src/wallet/wallets/defiWallet/CryptoDefiWalletScan.tsx delete mode 100644 packages/react/src/wallet/wallets/defiWallet/cryptoDefiWalletUris.ts delete mode 100644 packages/react/src/wallet/wallets/oneKey/OneKeyConnectUI.tsx delete mode 100644 packages/react/src/wallet/wallets/oneKey/OneKeyScan.tsx delete mode 100644 packages/react/src/wallet/wallets/oneKey/oneKeyWalletUris.ts delete mode 100644 packages/react/src/wallet/wallets/rainbow/RainbowConnectUI.tsx delete mode 100644 packages/react/src/wallet/wallets/rainbow/RainbowScan.tsx delete mode 100644 packages/react/src/wallet/wallets/rainbow/rainbowWalletUris.ts delete mode 100644 packages/react/src/wallet/wallets/trustWallet/TrustConnectUI.tsx delete mode 100644 packages/react/src/wallet/wallets/trustWallet/TrustScan.tsx delete mode 100644 packages/react/src/wallet/wallets/trustWallet/trustWalletUris.ts delete mode 100644 packages/react/src/wallet/wallets/zerion/ZerionConnectUI.tsx delete mode 100644 packages/react/src/wallet/wallets/zerion/ZerionScan.tsx delete mode 100644 packages/react/src/wallet/wallets/zerion/zerionWalletUris.ts diff --git a/packages/react-core/src/core/providers/thirdweb-wallet-provider copy.tsx b/packages/react-core/src/core/providers/thirdweb-wallet-provider copy.tsx new file mode 100644 index 00000000000..7a1423fbe02 --- /dev/null +++ b/packages/react-core/src/core/providers/thirdweb-wallet-provider copy.tsx @@ -0,0 +1,682 @@ +import { DAppMetaData } from "../types/dAppMeta"; +import type { + ConnectionStatus, + WalletConfig, + WalletConnectParams, + WalletInstance, + WalletOptions, +} from "../types/wallet"; +import { Chain } from "@thirdweb-dev/chains"; +import { + AbstractClientWallet, + AsyncStorage, + ConnectParams, + CreateAsyncStorage, + SignerWallet, + walletIds, +} from "@thirdweb-dev/wallets"; +import { Signer } from "ethers"; +import { + createContext, + PropsWithChildren, + useCallback, + useContext, + useEffect, + useMemo, + useRef, + useState, +} from "react"; + +const LAST_CONNECTED_WALLET_STORAGE_KEY = "lastConnectedWallet"; + +let lastConnectedWalletStorage: AsyncStorage; + +type LastConnectedWalletInfo = { + walletId: string; + connectParams?: ConnectParams & { + personalWallet?: { + walletId: string; + connectParams?: ConnectParams; + }; + }; +}; + +type ConnectFnArgs = + // if second argument is optional + undefined extends WalletConnectParams + ? [ + wallet: WalletConfig, + connectParams?: NonNullable>, + ] + : // if second argument is required + [ + wallet: WalletConfig, + connectParams: NonNullable>, + ]; + +// maps wallet instance to it's wallet config +const walletInstanceToConfig: Map< + WalletInstance, + WalletConfig +> = new Map(); + +type WalletSetupData = { + chains: Chain[]; + dAppMeta?: DAppMetaData; + activeChain?: Chain; + clientId?: string; + chainToConnect?: Chain; +}; + +type ThirdwebWalletContextData = { + wallets: WalletConfig[]; + signer?: Signer; + activeWallet?: WalletInstance; + activeWalletConfig?: WalletConfig; + connect: (...args: ConnectFnArgs) => Promise; + disconnect: () => Promise; + connectionStatus: ConnectionStatus; + setConnectionStatus: (status: ConnectionStatus) => void; + createWalletInstance: ( + Wallet: WalletConfig, + ) => I; + createdWalletInstance?: WalletInstance; + createWalletStorage: CreateAsyncStorage; + switchChain: (chain: number) => Promise; + chainToConnect?: Chain; + activeChain: Chain; + setConnectedWallet: ( + wallet: WalletInstance, + params?: ConnectParams>, + ) => Promise; + /** + * Get wallet config object from wallet instance + */ + getWalletConfig: (walletInstance: WalletInstance) => WalletConfig | undefined; + activeChainSetExplicitly: boolean; + clientId?: string; + walletSetupData: WalletSetupData; +}; + +const ThirdwebWalletContext = /* @__PURE__ */ createContext< + ThirdwebWalletContextData | undefined +>(undefined); + +export function ThirdwebWalletProvider( + props: PropsWithChildren<{ + activeChain: Chain; + supportedWallets: WalletConfig[]; + shouldAutoConnect?: boolean; + createWalletStorage: CreateAsyncStorage; + dAppMeta?: DAppMetaData; + chains: Chain[]; + autoSwitch?: boolean; + autoConnectTimeout?: number; + clientId?: string; + activeChainSetExplicitly: boolean; + signerWallet?: WalletConfig; + }>, +) { + const autoConnectTimeout = props.autoConnectTimeout || 15000; + + // if autoSwitch is enabled - enforce connection to activeChain + const chainToConnect = props.autoSwitch ? props.activeChain : undefined; + + const walletSetupData: WalletSetupData = { + chains: props.chains, + dAppMeta: props.dAppMeta, + activeChain: props.activeChain, + clientId: props.clientId, + chainToConnect, + }; + + const { + signer, + connectionStatus, + setConnectionStatus, + activeWallet, + createdWalletInstance, + activeWalletConfig, + createWalletInstance, + setConnectedWallet, + switchChain, + connectWallet, + disconnectWallet, + } = useWalletConnectionSetup(walletSetupData); + + if (!lastConnectedWalletStorage) { + lastConnectedWalletStorage = + props.createWalletStorage("coordinatorStorage"); + } + + const autoConnectTriggered = useRef(false); + + // Auto Connect + useEffect(() => { + if (autoConnectTriggered.current) { + return; + } + + autoConnectTriggered.current = true; + + // do not auto connect if signerWallet is given + if (props.signerWallet) { + return; + } + + // if explicitly set to false, don't auto connect + // by default, auto connect + if (props.shouldAutoConnect === false) { + setConnectionStatus("disconnected"); + return; + } + + if (activeWallet) { + // there's already an active wallet, don't auto connect + return; + } + + if (connectionStatus !== "unknown") { + // only try to auto connect if we're in the unknown state + return; + } + + autoConnectTriggered.current = true; + + async function autoConnect() { + const walletInfo = await getLastConnectedWalletInfo(); + + if (!walletInfo) { + setConnectionStatus("disconnected"); + return; + } + + const walletObj = props.supportedWallets.find( + (W) => W.id === walletInfo.walletId, + ); + + if (!walletObj) { + // last connected wallet is no longer present in the supported wallets + setConnectionStatus("disconnected"); + return; + } + + let _personalWalletInfo = walletInfo.connectParams?.personalWallet; + + // when connecting to magicLink with social login, it redirects to other page + // before redirecting, we save the walletInfo to local storage so that we can auto connect after redirect back to current page + // when using smartWallet + magicLink combination - the walletInfo will only contain info about magicLink and not smartWallet because it was never connected + // so if smartWallet + magicLink combination is used, we need to connect magicLink first and then connect smartWallet + if ( + walletInfo.walletId === walletIds.magicLink && + walletInfo.connectParams && + "oauthProvider" in walletInfo.connectParams + ) { + // if the wallet requires a personal wallet (like smartWallet), but the saved data does not have it + if (walletObj.personalWallets && !_personalWalletInfo) { + // fix the connectParams by adding the personal wallet info + _personalWalletInfo = { + walletId: walletInfo.walletId, + connectParams: walletInfo.connectParams, + }; + } + } + + const personalWalletInfo = _personalWalletInfo; + + if (personalWalletInfo) { + const personalWallets = walletObj.personalWallets || []; + + const personalWalletObj = personalWallets.find( + (W) => W.id === personalWalletInfo.walletId, + ); + if (personalWalletObj) { + // create a personal wallet instance and auto connect it + const personalWalletInstance = + createWalletInstance(personalWalletObj); + + try { + await timeoutPromise( + personalWalletInstance.autoConnect( + personalWalletInfo.connectParams, + ), + { + ms: autoConnectTimeout, + message: autoConnectTimeoutErrorMessage, + }, + ); + } catch (e) { + console.error("Failed to auto connect personal wallet"); + console.error(e); + setConnectionStatus("disconnected"); + return; + } + + // set the personal wallet instance to the connectParams + walletInfo.connectParams = { + ...walletInfo.connectParams, + personalWallet: personalWalletInstance, + }; + } else { + // last used personal wallet is no longer present in the supported wallets + setConnectionStatus("disconnected"); + return; + } + } + + // create a wallet instance and auto connect it + const wallet = createWalletInstance(walletObj); + + try { + setConnectionStatus("connecting"); + await timeoutPromise(wallet.autoConnect(walletInfo.connectParams), { + ms: autoConnectTimeout, + message: autoConnectTimeoutErrorMessage, + }); + setConnectedWallet(wallet, walletInfo.connectParams, true); + } catch (e) { + console.error("Failed to auto connect wallet"); + console.error(e); + if ( + e instanceof Error && + e.message === autoConnectTimeoutErrorMessage + ) { + lastConnectedWalletStorage.removeItem( + LAST_CONNECTED_WALLET_STORAGE_KEY, + ); + } + setConnectionStatus("disconnected"); + } + } + + autoConnect(); + }, [ + createWalletInstance, + props.supportedWallets, + setConnectedWallet, + props.shouldAutoConnect, + activeWallet, + connectionStatus, + autoConnectTimeout, + props.signerWallet, + setConnectionStatus, + ]); + + // connect signerWallet immediately if it's passed + // and disconnect it if it's not passed + const signerConnected = useRef(); + useEffect(() => { + if (!props.signerWallet) { + if (signerConnected.current) { + disconnectWallet(); + signerConnected.current = undefined; + } + return; + } + + if (signerConnected.current === props.signerWallet) { + return; + } + + const wallet = createWalletInstance(props.signerWallet); + setConnectedWallet(wallet); + signerConnected.current = props.signerWallet; + }, [ + createWalletInstance, + props.supportedWallets, + setConnectedWallet, + props.signerWallet, + disconnectWallet, + ]); + + return ( + { + return walletInstanceToConfig.get(walletInstance); + }, + activeChainSetExplicitly: props.activeChainSetExplicitly, + clientId: props.clientId, + walletSetupData: walletSetupData, + }} + > + {props.children} + + ); +} + +export function useWalletConnectionSetup(data: WalletSetupData) { + const { chains, chainToConnect } = data; + const [signer, setSigner] = useState(undefined); + const [connectionStatus, setConnectionStatus] = + useState("unknown"); + const [connectedChainId, setConnectedChainId] = useState( + undefined, + ); + + const [activeWallet, setActiveWallet] = useState< + WalletInstance | undefined + >(); + + const [createdWalletInstance, setCreatedWalletInstance] = useState< + WalletInstance | undefined + >(); + + const [activeWalletConfig, setActiveWalletConfig] = useState< + WalletConfig | undefined + >(); + + const walletParams: WalletOptions = useMemo(() => { + return { + chains: data.chains, + dappMetadata: data.dAppMeta, + chain: data.activeChain || data.chains[0], + clientId: data.clientId, + }; + }, [data.chains, data.dAppMeta, data.activeChain, data.clientId]); + + const createWalletInstance = useCallback( + (walletConfig: WalletConfig): I => { + const walletInstance = walletConfig.create(walletParams); + if (walletInstance.walletId === walletIds.magicLink) { + // NOTE: removing this if statement causes the component to re-render + // Patch for magic link wallet in react native + // needed because we need to add a component to the view tree + // from the instance, right before calling connect. + // Check it out in RN's DappContextProvider. + setCreatedWalletInstance(walletInstance); + } + walletInstanceToConfig.set(walletInstance, walletConfig); + return walletInstance; + }, + [walletParams], + ); + + const setConnectedWallet = useCallback( + async ( + wallet: WalletInstance, + connectParams?: ConnectParams>, + isAutoConnect = false, + ) => { + setActiveWallet(wallet); + const walletConfig = walletInstanceToConfig.get(wallet); + if (!walletConfig) { + throw new Error( + "Wallet config not found for given wallet instance. Do not create a wallet instance manually - use the useCreateWalletInstance() hook instead", + ); + } + setActiveWalletConfig(walletConfig); + setConnectionStatus("connected"); + const _signer = await wallet.getSigner(); + setSigner(_signer); + + // it auto-connected, then the details is already saved in storage, no need to store again + if (isAutoConnect) { + return; + } + + // save to storage + + const walletInfo: LastConnectedWalletInfo = { + walletId: walletConfig.id, + connectParams: connectParams || wallet.getConnectParams(), + }; + + // if personal wallet exists, we need to replace the connectParams.personalWallet to a stringifiable version + const personalWallet = wallet.getPersonalWallet() as AbstractClientWallet; + const personalWalletConfig = walletInstanceToConfig.get(personalWallet); + + if (personalWallet && personalWalletConfig) { + walletInfo.connectParams = { + ...walletInfo.connectParams, + personalWallet: { + walletId: personalWalletConfig.id, + connectParams: personalWallet.getConnectParams(), + }, + }; + + saveLastConnectedWalletInfo(walletInfo); + } else { + saveLastConnectedWalletInfo(walletInfo); + } + }, + [], + ); + + const storeLastActiveChainId = useCallback(async (chainId: number) => { + const lastConnectedWallet = await lastConnectedWalletStorage.getItem( + LAST_CONNECTED_WALLET_STORAGE_KEY, + ); + + if (!lastConnectedWallet) { + return; + } + + try { + const parsedWallet = JSON.parse(lastConnectedWallet as string); + if (parsedWallet.connectParams) { + parsedWallet.connectParams.chainId = chainId; + } else { + parsedWallet.connectParams = { chainId }; + } + await lastConnectedWalletStorage.setItem( + LAST_CONNECTED_WALLET_STORAGE_KEY, + JSON.stringify(parsedWallet), + ); + } catch (error) { + console.error(`Error saving the last active chain: ${error}`); + } + }, []); + + const switchChain = useCallback( + async (chainId: number) => { + if (!activeWallet) { + throw new Error("No active wallet"); + } + + await activeWallet.switchChain(chainId); + const _signer = await activeWallet.getSigner(); + await storeLastActiveChainId(chainId); + + setSigner(_signer); + }, + [activeWallet, storeLastActiveChainId], + ); + + const connectWallet = useCallback( + async (...args: ConnectFnArgs): Promise => { + const [WalletObj, connectParams] = args; + + const _connectedParams = { + chainId: chainToConnect?.chainId, + ...(connectParams || {}), + }; + + const wallet = createWalletInstance(WalletObj); + setConnectionStatus("connecting"); + try { + // if magic is using social login - it will redirect the page - so need to save walletInfo before connecting + // TODO: find a better way to handle this + if (WalletObj.id === walletIds.magicLink) { + saveLastConnectedWalletInfo({ + walletId: WalletObj.id, + connectParams: _connectedParams, + }); + } + await wallet.connect(_connectedParams); + setConnectedWallet(wallet, _connectedParams); + } catch (e: any) { + console.error(`Error connecting to wallet: ${e}`); + setConnectionStatus("disconnected"); + throw e; + } + + return wallet; + }, + [createWalletInstance, setConnectedWallet, chainToConnect], + ); + + const onWalletDisconnect = useCallback(async () => { + await lastConnectedWalletStorage.removeItem( + LAST_CONNECTED_WALLET_STORAGE_KEY, + ); + setConnectionStatus("disconnected"); + setSigner(undefined); + setActiveWallet(undefined); + setActiveWalletConfig(undefined); + }, []); + + const disconnectWallet = useCallback(async () => { + // if disconnect is called before the wallet is connected + if (!activeWallet) { + onWalletDisconnect(); + return; + } + + const personalWallet = activeWallet.getPersonalWallet(); + await activeWallet.disconnect(); + + if (personalWallet) { + await (personalWallet as AbstractClientWallet)?.disconnect(); + } + + onWalletDisconnect(); + }, [activeWallet, onWalletDisconnect]); + + // if chains is updated, update the active wallet's chains + useEffect(() => { + if (activeWallet) { + activeWallet.updateChains(chains); + } + }, [activeWallet, chains]); + + // when wallet's network or account is changed using the extension, update UI + useEffect(() => { + if (!activeWallet) { + return; + } + + const updateChainId = () => { + activeWallet?.getChainId().then((_chainId) => { + setConnectedChainId(_chainId); + }); + }; + + const update = async () => { + updateChainId(); + const _signer = await activeWallet.getSigner(); + setSigner(_signer); + }; + + updateChainId(); + activeWallet.addListener("change", update); + activeWallet.addListener("disconnect", onWalletDisconnect); + + return () => { + activeWallet.removeListener("change", update); + activeWallet.removeListener("disconnect", onWalletDisconnect); + }; + }, [activeWallet, onWalletDisconnect, setSigner]); + + return { + signer, + connectionStatus, + setConnectionStatus, + activeWallet, + createdWalletInstance, + activeWalletConfig, + createWalletInstance, + setConnectedWallet, + switchChain, + connectWallet, + disconnectWallet, + connectedChainId, + }; +} + +export function useWalletContext() { + const ctx = useContext(ThirdwebWalletContext); + if (!ctx) { + throw new Error( + `useWalletContext() can only be used inside `, + ); + } + return ctx; +} + +async function getLastConnectedWalletInfo() { + const str = await lastConnectedWalletStorage.getItem( + LAST_CONNECTED_WALLET_STORAGE_KEY, + ); + if (!str) { + return null; + } + + try { + return JSON.parse(str) as LastConnectedWalletInfo; + } catch { + await lastConnectedWalletStorage.removeItem( + LAST_CONNECTED_WALLET_STORAGE_KEY, + ); + return null; + } +} + +async function saveLastConnectedWalletInfo( + walletInfo: LastConnectedWalletInfo, +) { + try { + await lastConnectedWalletStorage.setItem( + LAST_CONNECTED_WALLET_STORAGE_KEY, + JSON.stringify(walletInfo), + ); + } catch (e) { + console.error("Error saving the last connected wallet info", e); + } +} + +/** + * Timeout a promise with a given Error message if the promise does not resolve in given time + * + * @param promise - Promise to track for timeout + * @param option - timeout options + * @returns + */ +function timeoutPromise( + promise: Promise, + option: { ms: number; message: string }, +) { + return new Promise((resolve, reject) => { + const timeoutId = setTimeout(() => { + reject(new Error(option.message)); + }, option.ms); + + promise.then( + (res) => { + clearTimeout(timeoutId); + resolve(res); + }, + (err) => { + clearTimeout(timeoutId); + reject(err); + }, + ); + }); +} + +const autoConnectTimeoutErrorMessage = `Failed to Auto connect. Auto connect timed out. You can increase the timeout duration using the autoConnectTimeout prop on `; diff --git a/packages/react-core/src/core/providers/thirdweb-wallet-provider.tsx b/packages/react-core/src/core/providers/thirdweb-wallet-provider.tsx index 68ebb7fe0ba..20c537ca221 100644 --- a/packages/react-core/src/core/providers/thirdweb-wallet-provider.tsx +++ b/packages/react-core/src/core/providers/thirdweb-wallet-provider.tsx @@ -1,6 +1,9 @@ +// update here import { DAppMetaData } from "../types/dAppMeta"; import type { + ConnectionStatus, WalletConfig, + WalletConnectParams, WalletInstance, WalletOptions, } from "../types/wallet"; @@ -43,13 +46,6 @@ type LastConnectedWalletInfo = { }; }; -type NonNullable = T extends null | undefined ? never : T; -type WalletConnectParams = Parameters< - I["connect"] ->[0]; - -type ConnectionStatus = "unknown" | "connected" | "disconnected" | "connecting"; - type ConnectFnArgs = // if second argument is optional undefined extends WalletConnectParams @@ -69,6 +65,14 @@ const walletInstanceToConfig: Map< WalletConfig > = new Map(); +type WalletSetupData = { + chains: Chain[]; + dAppMeta?: DAppMetaData; + activeChain?: Chain; + clientId?: string; + chainToConnect?: Chain; +}; + type ThirdwebWalletContextData = { wallets: WalletConfig[]; signer?: Signer; @@ -97,32 +101,50 @@ type ThirdwebWalletContextData = { activeChainSetExplicitly: boolean; clientId?: string; walletConnectHandler: WalletConnectHandler | undefined; + personalWalletConnection: WalletConnectionSetup; + isAutoConnecting: boolean; }; const ThirdwebWalletContext = /* @__PURE__ */ createContext< ThirdwebWalletContextData | undefined >(undefined); -export function ThirdwebWalletProvider( - props: PropsWithChildren<{ - activeChain: Chain; - supportedWallets: WalletConfig[]; - shouldAutoConnect?: boolean; - createWalletStorage: CreateAsyncStorage; - dAppMeta?: DAppMetaData; - chains: Chain[]; - autoSwitch?: boolean; - autoConnectTimeout?: number; - clientId?: string; - activeChainSetExplicitly: boolean; - signerWallet?: WalletConfig; - }>, -) { - const [signer, setSigner] = useState(undefined); - const [connectionStatus, setConnectionStatus] = - useState("unknown"); +export type WalletConnectionSetup = { + signer: Signer | undefined; + connectionStatus: ConnectionStatus; + setConnectionStatus: (status: ConnectionStatus) => void; + activeWallet: WalletInstance | undefined; + createdWalletInstance: WalletInstance | undefined; + activeWalletConfig: WalletConfig | undefined; + createWalletInstance: ( + walletConfig: WalletConfig, + ) => I; + setConnectedWallet: ( + WalletInstance: WalletInstance, + connectParams?: ConnectParams>, + isAutoConnect?: boolean, + ) => Promise; + switchChain: (chainId: number) => Promise; + connectWallet: ( + ...args: ConnectFnArgs + ) => Promise; + disconnectWallet: () => Promise; + connectedChainId: number | undefined; + connectedAddress: string | undefined; +}; - const autoConnectTimeout = props.autoConnectTimeout || 15000; +export function useWalletConnectionSetup( + data: WalletSetupData, + initialValue: { + connectionStatus: ConnectionStatus; + }, +): WalletConnectionSetup { + const { chains, chainToConnect, dAppMeta, clientId, activeChain } = data; + + const [signer, setSigner] = useState(undefined); + const [connectionStatus, setConnectionStatus] = useState( + initialValue.connectionStatus, + ); const [activeWallet, setActiveWallet] = useState< WalletInstance | undefined @@ -136,25 +158,30 @@ export function ThirdwebWalletProvider( WalletConfig | undefined >(); - const [walletConnectHandler, setWalletConnectHandler] = - useState(); + const [connectedChainId, setConnectedChainId] = useState( + undefined, + ); - if (!lastConnectedWalletStorage) { - lastConnectedWalletStorage = - props.createWalletStorage("coordinatorStorage"); - } + const [connectedAddress, setConnectedAddress] = useState( + undefined, + ); - // if autoSwitch is enabled - enforce connection to activeChain - const chainToConnect = props.autoSwitch ? props.activeChain : undefined; + useEffect(() => { + if (!activeWallet) { + setConnectedAddress(undefined); + } else { + activeWallet.getAddress().then(setConnectedAddress); + } + }, [activeWallet]); const walletParams: WalletOptions = useMemo(() => { return { - chains: props.chains, - dappMetadata: props.dAppMeta, - chain: props.activeChain || props.chains[0], - clientId: props.clientId, + chains: chains, + dappMetadata: dAppMeta, + chain: activeChain || chains[0], + clientId: clientId, }; - }, [props.chains, props.dAppMeta, props.activeChain, props.clientId]); + }, [chains, dAppMeta, activeChain, clientId]); const createWalletInstance = useCallback( (walletConfig: WalletConfig): I => { @@ -176,37 +203,9 @@ export function ThirdwebWalletProvider( // if props.chains is updated, update the active wallet's chains useEffect(() => { if (activeWallet) { - activeWallet.updateChains(props.chains); - } - }, [activeWallet, props.chains]); - - useEffect(() => { - if (!activeWallet) { - return; + activeWallet.updateChains(chains); } - - const initWCHandler = async () => { - const wcReceiverOptions = - activeWallet?.getOptions() as WalletConnectReceiverConfig; - - const handler = new WalletConnectV2Handler( - { - walletConnectReceiver: { - ...(wcReceiverOptions?.walletConnectReceiver === true - ? {} - : wcReceiverOptions?.walletConnectReceiver), - }, - }, - activeWallet, - ); - await handler.init(); - setWalletConnectHandler(handler); - }; - - if (isWalletConnectReceiverEnabled(activeWallet)) { - initWCHandler(); - } - }, [activeWallet]); + }, [activeWallet, chains]); const setConnectedWallet = useCallback( async ( @@ -299,6 +298,198 @@ export function ThirdwebWalletProvider( [activeWallet, storeLastActiveChainId], ); + const connectWallet = useCallback( + async (...args: ConnectFnArgs): Promise => { + const [WalletObj, connectParams] = args; + + const _connectedParams = { + chainId: chainToConnect?.chainId, + ...(connectParams || {}), + }; + + const wallet = createWalletInstance(WalletObj); + setConnectionStatus("connecting"); + try { + // if magic is using social login - it will redirect the page - so need to save walletInfo before connecting + // TODO: find a better way to handle this + if (WalletObj.id === walletIds.magicLink) { + saveLastConnectedWalletInfo({ + walletId: WalletObj.id, + connectParams: _connectedParams, + }); + } + await wallet.connect(_connectedParams); + setConnectedWallet(wallet, _connectedParams); + } catch (e: any) { + console.error(`Error connecting to wallet: ${e}`); + setConnectionStatus("disconnected"); + throw e; + } + + return wallet; + }, + [createWalletInstance, setConnectedWallet, chainToConnect], + ); + + const onWalletDisconnect = useCallback(async () => { + await lastConnectedWalletStorage.removeItem( + LAST_CONNECTED_WALLET_STORAGE_KEY, + ); + setConnectionStatus("disconnected"); + setSigner(undefined); + setActiveWallet(undefined); + setActiveWalletConfig(undefined); + }, []); + + const disconnectWallet = useCallback(async () => { + // if disconnect is called before the wallet is connected + if (!activeWallet) { + onWalletDisconnect(); + return; + } + + const personalWallet = activeWallet.getPersonalWallet(); + await activeWallet.disconnect(); + + if (personalWallet) { + await (personalWallet as AbstractClientWallet)?.disconnect(); + } + + onWalletDisconnect(); + }, [activeWallet, onWalletDisconnect]); + + // when wallet's network or account is changed using the extension, update UI + useEffect(() => { + if (!activeWallet) { + return; + } + + const updateChainId = () => { + activeWallet?.getChainId().then((_chainId) => { + setConnectedChainId(_chainId); + }); + }; + + const update = async () => { + updateChainId(); + const _signer = await activeWallet.getSigner(); + setSigner(_signer); + }; + + updateChainId(); + activeWallet.addListener("change", update); + + activeWallet.addListener("disconnect", onWalletDisconnect); + + return () => { + activeWallet.removeListener("change", update); + activeWallet.removeListener("disconnect", onWalletDisconnect); + }; + }, [activeWallet, onWalletDisconnect]); + + return { + signer, + connectionStatus, + setConnectionStatus, + activeWallet, + createdWalletInstance, + activeWalletConfig, + createWalletInstance, + setConnectedWallet, + switchChain, + connectWallet, + disconnectWallet, + connectedChainId, + connectedAddress, + }; +} + +export function ThirdwebWalletProvider( + props: PropsWithChildren<{ + activeChain: Chain; + supportedWallets: WalletConfig[]; + shouldAutoConnect?: boolean; + createWalletStorage: CreateAsyncStorage; + dAppMeta?: DAppMetaData; + chains: Chain[]; + autoSwitch?: boolean; + autoConnectTimeout?: number; + clientId?: string; + activeChainSetExplicitly: boolean; + signerWallet?: WalletConfig; + }>, +) { + // if autoSwitch is enabled - enforce connection to activeChain + const chainToConnect = props.autoSwitch ? props.activeChain : undefined; + + const autoConnectTimeout = props.autoConnectTimeout || 15000; + + const walletSetupData: WalletSetupData = { + chains: props.chains, + dAppMeta: props.dAppMeta, + activeChain: props.activeChain, + clientId: props.clientId, + chainToConnect, + }; + + const { + signer, + connectionStatus, + setConnectionStatus, + activeWallet, + createdWalletInstance, + activeWalletConfig, + createWalletInstance, + setConnectedWallet, + switchChain, + connectWallet, + disconnectWallet, + } = useWalletConnectionSetup(walletSetupData, { + connectionStatus: "unknown", + }); + + const personalWalletConnection = useWalletConnectionSetup(walletSetupData, { + connectionStatus: "disconnected", + }); + + const [isAutoConnecting, setIsAutoConnecting] = useState(false); + + const [walletConnectHandler, setWalletConnectHandler] = + useState(); + + if (!lastConnectedWalletStorage) { + lastConnectedWalletStorage = + props.createWalletStorage("coordinatorStorage"); + } + + useEffect(() => { + if (!activeWallet) { + return; + } + + const initWCHandler = async () => { + const wcReceiverOptions = + activeWallet?.getOptions() as WalletConnectReceiverConfig; + + const handler = new WalletConnectV2Handler( + { + walletConnectReceiver: { + ...(wcReceiverOptions?.walletConnectReceiver === true + ? {} + : wcReceiverOptions?.walletConnectReceiver), + }, + }, + activeWallet, + ); + await handler.init(); + setWalletConnectHandler(handler); + }; + + if (isWalletConnectReceiverEnabled(activeWallet)) { + initWCHandler(); + } + }, [activeWallet]); + const autoConnectTriggered = useRef(false); // Auto Connect @@ -408,6 +599,7 @@ export function ThirdwebWalletProvider( const wallet = createWalletInstance(walletObj); try { + setIsAutoConnecting(true); setConnectionStatus("connecting"); await timeoutPromise(wallet.autoConnect(walletInfo.connectParams), { ms: autoConnectTimeout, @@ -427,6 +619,8 @@ export function ThirdwebWalletProvider( } setConnectionStatus("disconnected"); } + + setIsAutoConnecting(false); } autoconnect(); @@ -437,95 +631,11 @@ export function ThirdwebWalletProvider( props.shouldAutoConnect, activeWallet, connectionStatus, - autoConnectTimeout, props.signerWallet, + setConnectionStatus, + autoConnectTimeout, ]); - const connectWallet = useCallback( - async (...args: ConnectFnArgs): Promise => { - const [WalletObj, connectParams] = args; - - const _connectedParams = { - chainId: chainToConnect?.chainId, - ...(connectParams || {}), - }; - - const wallet = createWalletInstance(WalletObj); - setConnectionStatus("connecting"); - try { - // if magic is using social login - it will redirect the page - so need to save walletInfo before connecting - // TODO: find a better way to handle this - if (WalletObj.id === walletIds.magicLink) { - saveLastConnectedWalletInfo({ - walletId: WalletObj.id, - connectParams: _connectedParams, - }); - } - await wallet.connect(_connectedParams); - setConnectedWallet(wallet, _connectedParams); - } catch (e: any) { - console.error(`Error connecting to wallet: ${e}`); - setConnectionStatus("disconnected"); - throw e; - } - - return wallet; - }, - [createWalletInstance, setConnectedWallet, chainToConnect], - ); - - const onWalletDisconnect = useCallback(async () => { - await lastConnectedWalletStorage.removeItem( - LAST_CONNECTED_WALLET_STORAGE_KEY, - ); - setConnectionStatus("disconnected"); - setSigner(undefined); - setActiveWallet(undefined); - setActiveWalletConfig(undefined); - }, []); - - const disconnectWallet = useCallback(async () => { - // if disconnect is called before the wallet is connected - if (!activeWallet) { - onWalletDisconnect(); - return; - } - - const personalWallet = activeWallet.getPersonalWallet(); - await activeWallet.disconnect(); - - if (personalWallet) { - await (personalWallet as AbstractClientWallet)?.disconnect(); - } - - onWalletDisconnect(); - }, [activeWallet, onWalletDisconnect]); - - // when wallet's network or account is changed using the extension, update UI - useEffect(() => { - if (!activeWallet) { - return; - } - - const update = async () => { - const _signer = await activeWallet.getSigner(); - setSigner(_signer); - }; - - activeWallet.addListener("change", () => { - update(); - }); - - activeWallet.addListener("disconnect", () => { - onWalletDisconnect(); - }); - - return () => { - activeWallet.removeListener("change"); - activeWallet.removeListener("disconnect"); - }; - }, [activeWallet, onWalletDisconnect]); - // connect signerWallet immediately if it's passed // and disconnect it if it's not passed const signerConnected = useRef(); @@ -577,6 +687,8 @@ export function ThirdwebWalletProvider( activeChainSetExplicitly: props.activeChainSetExplicitly, clientId: props.clientId, walletConnectHandler: walletConnectHandler, + personalWalletConnection, + isAutoConnecting, }} > {props.children} diff --git a/packages/react-core/src/core/types/wallet.ts b/packages/react-core/src/core/types/wallet.ts index 4d41bd6ff17..aa56a4a3b84 100644 --- a/packages/react-core/src/core/types/wallet.ts +++ b/packages/react-core/src/core/types/wallet.ts @@ -1,6 +1,12 @@ import type { AbstractClientWallet, Chain } from "@thirdweb-dev/wallets"; import { WalletOptions as WalletOptions_ } from "@thirdweb-dev/wallets"; +export type ConnectionStatus = + | "unknown" + | "connected" + | "disconnected" + | "connecting"; + // these are extra options provided by the react-core package export type ExtraCoreWalletOptions = { chain: Chain; @@ -10,6 +16,11 @@ export type WalletOptions = WalletOptions_; export type WalletInstance = AbstractClientWallet; +export type NonNullable = T extends null | undefined ? never : T; +export type WalletConnectParams = Parameters< + I["connect"] +>[0]; + export type WalletClass = { id: string; new (options: WalletOptions): I; @@ -56,6 +67,12 @@ export type WalletConfig = { isHeadless?: boolean; }; +type ConnectArgs = + // if second argument is optional + undefined extends WalletConnectParams + ? [connectParams?: NonNullable>] + : [connectParams: NonNullable>]; + export type ConnectUIProps = { /** * temporarily hide the ConnectModal @@ -156,9 +173,75 @@ export type ConnectUIProps = { /** * Called when the wallet is connected but it's * part of another wallet's connection flow. - * @param walleInstance - the instance of the connected wallet + * @param walletInstance - the instance of the connected wallet + */ + onLocallyConnected?: (walletInstance: WalletInstance) => void; + + /** + * connect wallet + * + * If you need more control over the connection process, you can manually create wallet instance and call the `connect` method of the wallet instance instead and use `setConnectedWallet` and `setConnectionStatus` to set the connected wallet and connection status. + */ + connect: (...args: ConnectArgs) => Promise; + + /** + * Set the connection status of the wallet. + * This is only relevant if you are manually creating wallet instance and calling the `wallet.connect` method. + * + * @example + * ```ts + * const createWalletInstance = useCreateWalletInstance(); + * const wallet = createWalletInstance(walletConfig); + * setConnectionStatus('connecting'); + * try { + * await wallet.connect(options); + * setConnectedWallet(wallet); + * } catch { + * setConnectionStatus('disconnected'); + * } + * ``` + */ + setConnectionStatus: (status: ConnectionStatus) => void; + + /** + * Connection status of the wallet */ - onLocallyConnected?: (walleInstance: WalletInstance) => void; + connectionStatus: ConnectionStatus; + + /** + * Set the wallet instance as connected in thirdweb context + * This is only relevant if you are manually creating wallet instance and calling the `wallet.connect` method. + * + * @example + * ```ts + * const createWalletInstance = useCreateWalletInstance(); + * const wallet = createWalletInstance(walletConfig); + * setConnectionStatus('connecting'); + * try { + * await wallet.connect(options); + * setConnectedWallet(wallet); + * } catch { + * setConnectionStatus('disconnected'); + * } + * ``` + */ + setConnectedWallet: (walletInstance: I) => void; + + /** + * Create an instance of the wallet + */ + createWalletInstance: () => I; + + /** + * Connected wallet instance + * This is set by if `connect` succeeds or it set manually by the `setConnectedWallet` function + */ + connectedWallet?: I; + + /** + * Address of the connected wallet instance + */ + connectedWalletAddress?: string; }; export type SelectUIProps = { @@ -195,4 +278,77 @@ export type SelectUIProps = { * This is always `compact` on React Native */ modalSize: "compact" | "wide"; + + /** + * Called when the wallet is connected but it's + * part of another wallet's connection flow. + * @param walletInstance - the instance of the connected wallet + */ + onLocallyConnected?: (walletInstance: WalletInstance) => void; + + /** + * connect wallet + * + * If you need more control over the connection process, you can manually create wallet instance and call the `connect` method of the wallet instance instead and use `setConnectedWallet` and `setConnectionStatus` to set the connected wallet and connection status. + */ + connect: (...args: ConnectArgs) => Promise; + + /** + * Set the connection status of the wallet. + * This is only relevant if you are manually creating wallet instance and calling the `wallet.connect` method. + * + * @example + * ```ts + * const createWalletInstance = useCreateWalletInstance(); + * const wallet = createWalletInstance(walletConfig); + * setConnectionStatus('connecting'); + * try { + * await wallet.connect(options); + * setConnectedWallet(wallet); + * } catch { + * setConnectionStatus('disconnected'); + * } + * ``` + */ + setConnectionStatus: (status: ConnectionStatus) => void; + + /** + * Connection status of the wallet + */ + connectionStatus: ConnectionStatus; + + /** + * Set the wallet instance as connected in thirdweb context + * This is only relevant if you are manually creating wallet instance and calling the `wallet.connect` method. + * + * @example + * ```ts + * const createWalletInstance = useCreateWalletInstance(); + * const wallet = createWalletInstance(walletConfig); + * setConnectionStatus('connecting'); + * try { + * await wallet.connect(options); + * setConnectedWallet(wallet); + * } catch { + * setConnectionStatus('disconnected'); + * } + * ``` + */ + setConnectedWallet: (walletInstance: I) => void; + + /** + * Create an instance of the wallet + */ + createWalletInstance: () => I; + + /** + * Connected wallet instance + * This is set by if `connect` succeeds or it set manually by the `setConnectedWallet` function + */ + connectedWallet?: I; + + /** + * Address of the connected wallet instance + */ + connectedWalletAddress?: string; }; diff --git a/packages/react-core/src/evm/index.ts b/packages/react-core/src/evm/index.ts index 1c3ef5a67e6..aa26b8468bd 100644 --- a/packages/react-core/src/evm/index.ts +++ b/packages/react-core/src/evm/index.ts @@ -7,6 +7,8 @@ export type { ThirdwebProviderCoreProps } from "../core/providers/thirdweb-provi // constants export { __DEV__ } from "../core/constants/runtime"; +export { useWalletConnectionSetup } from "../core/providers/thirdweb-wallet-provider"; + // wallet hooks export { useWallet, @@ -42,6 +44,7 @@ export type { RequiredParam } from "../core/query-utils/required-param"; export type { WalletConfig, ConnectUIProps, + ConnectionStatus, SelectUIProps, WalletClass, WalletInstance, diff --git a/packages/react-native/src/evm/components/ConnectWalletFlow/ChooseWallet/ChooseWallet.tsx b/packages/react-native/src/evm/components/ConnectWalletFlow/ChooseWallet/ChooseWallet.tsx index aeea64cd9e8..32d0288e57a 100644 --- a/packages/react-native/src/evm/components/ConnectWalletFlow/ChooseWallet/ChooseWallet.tsx +++ b/packages/react-native/src/evm/components/ConnectWalletFlow/ChooseWallet/ChooseWallet.tsx @@ -1,6 +1,11 @@ import { walletIds } from "@thirdweb-dev/wallets"; import { ModalHeaderTextClose } from "../../base/modal/ModalHeaderTextClose"; -import { WalletConfig } from "@thirdweb-dev/react-core"; +import { + WalletConfig, + useAddress, + useConnect, + useWalletContext, +} from "@thirdweb-dev/react-core"; import { ReactNode, useMemo, useState } from "react"; import Box from "../../base/Box"; import Text from "../../base/Text"; @@ -107,6 +112,17 @@ export function ChooseWallet({ } }; + // TEMP BUILD FIX + const connect = useConnect(); + const address = useAddress(); + const { + setConnectedWallet, + setConnectionStatus, + connectionStatus, + createWalletInstance, + activeWallet, + } = useWalletContext(); + return ( connect(emailWallet, options)} + connectedWallet={activeWallet} + connectedWalletAddress={address} + connectionStatus={connectionStatus} + createWalletInstance={() => createWalletInstance(emailWallet)} + setConnectedWallet={setConnectedWallet} + setConnectionStatus={setConnectionStatus} /> ) : null} {emailWallet && diff --git a/packages/react-native/src/evm/components/ConnectWalletFlow/ChooseWallet/ChooseWalletContent.tsx b/packages/react-native/src/evm/components/ConnectWalletFlow/ChooseWallet/ChooseWalletContent.tsx index a74174c1269..c31bba40bd0 100644 --- a/packages/react-native/src/evm/components/ConnectWalletFlow/ChooseWallet/ChooseWalletContent.tsx +++ b/packages/react-native/src/evm/components/ConnectWalletFlow/ChooseWallet/ChooseWalletContent.tsx @@ -1,4 +1,9 @@ -import type { WalletConfig } from "@thirdweb-dev/react-core"; +import { + useAddress, + useConnect, + useWalletContext, + type WalletConfig, +} from "@thirdweb-dev/react-core"; import { Dimensions, ScrollView, View } from "react-native"; import { WalletButton } from "../../base/WalletButton"; import Box from "../../base/Box"; @@ -16,6 +21,16 @@ export const ChooseWalletContent = ({ onChooseWallet, }: ChooseWalletContentProps) => { const theme = useTheme(); + // TEMP BUILD FIX + const connect = useConnect(); + const address = useAddress(); + const { + setConnectedWallet, + setConnectionStatus, + connectionStatus, + createWalletInstance, + activeWallet, + } = useWalletContext(); return ( @@ -46,6 +61,14 @@ export const ChooseWalletContent = ({ onChooseWallet(item, data); }} walletConfig={item} + // TEMPORARY BUILD FIX + connect={(options: any) => connect(item, options)} + connectedWallet={activeWallet} + connectedWalletAddress={address} + connectionStatus={connectionStatus} + createWalletInstance={() => createWalletInstance(item)} + setConnectedWallet={setConnectedWallet} + setConnectionStatus={setConnectionStatus} /> ) : ( diff --git a/packages/react-native/src/evm/components/ConnectWalletFlow/ConnectWalletFlow.tsx b/packages/react-native/src/evm/components/ConnectWalletFlow/ConnectWalletFlow.tsx index c5092992423..a2e9f628581 100644 --- a/packages/react-native/src/evm/components/ConnectWalletFlow/ConnectWalletFlow.tsx +++ b/packages/react-native/src/evm/components/ConnectWalletFlow/ConnectWalletFlow.tsx @@ -1,7 +1,13 @@ import Text from "../base/Text"; import { ChooseWallet } from "./ChooseWallet/ChooseWallet"; import { ConnectingWallet } from "./ConnectingWallet/ConnectingWallet"; -import { WalletConfig, useConnect, useWallets } from "@thirdweb-dev/react-core"; +import { + WalletConfig, + useAddress, + useConnect, + useWalletContext, + useWallets, +} from "@thirdweb-dev/react-core"; import { useCallback, useEffect, useState } from "react"; import { walletIds } from "@thirdweb-dev/wallets"; import { useColorScheme } from "react-native"; @@ -35,6 +41,15 @@ export const ConnectWalletFlow = () => { const theme = useColorScheme(); const appTheme = useGlobalTheme(); const connect = useConnect(); + // TEMP BUILD FIX + const address = useAddress(); + const { + activeWallet: connectedWallet, + setConnectedWallet, + setConnectionStatus, + connectionStatus, + createWalletInstance, + } = useWalletContext(); const onClose = useCallback( (reset?: boolean) => { @@ -125,15 +140,31 @@ export const ConnectWalletFlow = () => { supportedWallets={supportedWallets} selectionData={selectionData} setSelectionData={() => {}} // TODO + connect={(options) => { + return connect(activeWallet, options); + }} // TEMP BUILD FIX + connectedWallet={connectedWallet} + connectedWalletAddress={address} + connectionStatus={connectionStatus} + createWalletInstance={() => createWalletInstance(activeWallet)} + setConnectedWallet={setConnectedWallet} // TEMP BUILD FIX + setConnectionStatus={setConnectionStatus} // TEMP BUILD FIX /> ); } }, [ activeWallet, + address, + connect, + connectedWallet, + connectionStatus, + createWalletInstance, handleClose, modalVisible, onBackPress, selectionData, + setConnectedWallet, + setConnectionStatus, supportedWallets, theme, ]); diff --git a/packages/react-native/src/evm/components/ConnectWalletFlow/SmartWallet/SmartWalletFlow.tsx b/packages/react-native/src/evm/components/ConnectWalletFlow/SmartWallet/SmartWalletFlow.tsx index d099c960ecc..264ecb7e5e6 100644 --- a/packages/react-native/src/evm/components/ConnectWalletFlow/SmartWallet/SmartWalletFlow.tsx +++ b/packages/react-native/src/evm/components/ConnectWalletFlow/SmartWallet/SmartWalletFlow.tsx @@ -3,6 +3,7 @@ import { ConnectUIProps, WalletConfig, WalletInstance, + useAddress, useConnect, useCreateWalletInstance, useWalletContext, @@ -39,6 +40,15 @@ export const SmartWalletFlow = ({ const connect = useConnect(); const targetChain = useWalletContext().activeChain; + // TEMP BUILD FIX + const address = useAddress(); + const { + setConnectedWallet, + setConnectionStatus, + connectionStatus, + activeWallet, + } = useWalletContext(); + const mismatch = personalWalletChainId ? personalWalletChainId !== targetChain.chainId : false; @@ -127,6 +137,16 @@ export const SmartWalletFlow = ({ goBack={goBack} walletConfig={personalWalletConfig} onLocallyConnected={onPersonalWalletConnected} + // TEMPORARY BUILD FIX + connect={(options: any) => connect(personalWalletConfig, options)} + connectedWallet={activeWallet} + connectedWalletAddress={address} + connectionStatus={connectionStatus} + createWalletInstance={() => + createWalletInstance(personalWalletConfig) + } + setConnectedWallet={setConnectedWallet} + setConnectionStatus={setConnectionStatus} /> ) : ( diff --git a/packages/react-native/src/evm/wallets/wallets/smart-wallet.tsx b/packages/react-native/src/evm/wallets/wallets/smart-wallet.tsx index 0af75da26e0..fc64177f8be 100644 --- a/packages/react-native/src/evm/wallets/wallets/smart-wallet.tsx +++ b/packages/react-native/src/evm/wallets/wallets/smart-wallet.tsx @@ -1,4 +1,10 @@ -import type { WalletConfig, WalletOptions } from "@thirdweb-dev/react-core"; +import { + useAddress, + useConnect, + useWalletContext, + type WalletConfig, + type WalletOptions, +} from "@thirdweb-dev/react-core"; import { SmartWallet, createAsyncLocalStorage } from "@thirdweb-dev/wallets"; import { SmartWalletConfig } from "../types/smart-wallet"; import { SmartWalletFlow } from "../../components/ConnectWalletFlow/SmartWallet/SmartWalletFlow"; @@ -22,7 +28,31 @@ export const smartWallet = ( }, selectUI: WalletSelectUI ? (props) => { - return ; + // TEMP BUILD FIX + const connect = useConnect(); + const address = useAddress(); + const { + setConnectedWallet, + setConnectionStatus, + connectionStatus, + createWalletInstance, + activeWallet, + } = useWalletContext(); + + return ( + connect(wallet, options)} + connectedWallet={activeWallet} + connectedWalletAddress={address} + connectionStatus={connectionStatus} + createWalletInstance={() => createWalletInstance(wallet)} + setConnectedWallet={setConnectedWallet} + setConnectionStatus={setConnectionStatus} + /> + ); } : undefined, personalWallets: [wallet], diff --git a/packages/react/src/evm/index.ts b/packages/react/src/evm/index.ts index 8b62373f9fa..7fe0e28612d 100644 --- a/packages/react/src/evm/index.ts +++ b/packages/react/src/evm/index.ts @@ -11,7 +11,16 @@ export { type ConnectWalletProps, } from "../wallet/ConnectWallet/ConnectWallet"; export type { WelcomeScreen } from "../wallet/ConnectWallet/screens/types"; -export { ConnectModalInline } from "../wallet/ConnectWallet/Modal/ConnectModalInline"; +export { + ConnectModalInline, + type ConnectModalInlineProps, +} from "../wallet/ConnectWallet/Modal/ConnectModalInline"; + +export { + useShowConnectEmbed, + ConnectEmbed, + type ConnectEmbedProps, +} from "../wallet/ConnectWallet/Modal/ConnectEmbed"; export { NetworkSelector, diff --git a/packages/react/src/evm/locales/en.ts b/packages/react/src/evm/locales/en.ts index 121892b25d3..4c2673be3d1 100644 --- a/packages/react/src/evm/locales/en.ts +++ b/packages/react/src/evm/locales/en.ts @@ -53,6 +53,7 @@ export function enDefault() { personalWallet: "Personal Wallet", smartWallet: "Smart Wallet", or: "OR", + goBackButton: "Back", download: { chrome: "Download Chrome Extension", android: "Download on Google Play", @@ -109,6 +110,7 @@ export function enDefault() { instruction: "Please sign the message request in your wallet to continue", signInButton: "Sign in", + disconnectWallet: "Disconnect Wallet", }, signingScreen: { title: "Signing In", diff --git a/packages/react/src/evm/locales/es.ts b/packages/react/src/evm/locales/es.ts index 68c1a74fc58..336e25a3e71 100644 --- a/packages/react/src/evm/locales/es.ts +++ b/packages/react/src/evm/locales/es.ts @@ -54,6 +54,7 @@ export function esDefault(): ThirdwebLocale { personalWallet: "Cartera personal", smartWallet: "Cartera inteligente", or: "O", + goBackButton: "Atras", download: { chrome: "Descargar extensión para Chrome", android: "Descargar en Google Play", @@ -111,6 +112,7 @@ export function esDefault(): ThirdwebLocale { instruction: "Por favor, firma la solicitud de mensaje en tu cartera para continuar", signInButton: "Iniciar sesión", + disconnectWallet: "Desconectar cartera", }, signingScreen: { title: "Iniciando sesión", diff --git a/packages/react/src/evm/locales/ja.ts b/packages/react/src/evm/locales/ja.ts index 507ce37762e..90530438a55 100644 --- a/packages/react/src/evm/locales/ja.ts +++ b/packages/react/src/evm/locales/ja.ts @@ -53,6 +53,7 @@ export function jaDefault(): ThirdwebLocale { personalWallet: "パーソナルウォレット", smartWallet: "スマートウォレット", or: "または", + goBackButton: "戻る", // TODO - check translation download: { chrome: "Chrome拡張をダウンロード", android: "Google Playでダウンロード", @@ -110,6 +111,7 @@ export function jaDefault(): ThirdwebLocale { instruction: "続行するためにウォレットでメッセージリクエストにサインしてください", signInButton: "サインイン", + disconnectWallet: "ウォレットの切断", }, signingScreen: { title: "サインイン中", diff --git a/packages/react/src/evm/locales/types.ts b/packages/react/src/evm/locales/types.ts index bc2a81afe53..43b9f60ac47 100644 --- a/packages/react/src/evm/locales/types.ts +++ b/packages/react/src/evm/locales/types.ts @@ -18,6 +18,7 @@ export type ThirdwebLocale = { privacyPolicy: string; termsOfService: string; }; + goBackButton: string; backupWallet: string; confirmInWallet: string; connectAWallet: string; @@ -79,6 +80,7 @@ export type ThirdwebLocale = { instruction: string; signInButton: string; title: string; + disconnectWallet: string; }; signingScreen: { approveTransactionInSafe: string; diff --git a/packages/react/src/evm/providers/wallet-ui-states-provider.tsx b/packages/react/src/evm/providers/wallet-ui-states-provider.tsx index dfe0156bef7..9bc9c8452b3 100644 --- a/packages/react/src/evm/providers/wallet-ui-states-provider.tsx +++ b/packages/react/src/evm/providers/wallet-ui-states-provider.tsx @@ -20,6 +20,7 @@ export type ModalConfig = { onLogin?: (token: string) => void; onLogout?: () => void; }; + isEmbed?: boolean; }; const WalletModalOpen = /* @__PURE__ */ createContext(false); @@ -47,6 +48,12 @@ export const WalletUIStatesProvider = ( termsOfServiceUrl?: string; privacyPolicyUrl?: string; welcomeScreen?: WelcomeScreen; + isEmbed?: boolean; + auth?: { + loginOptional?: boolean; + onLogin?: (token: string) => void; + onLogout?: () => void; + }; }>, ) => { const [isWalletModalOpen, setIsWalletModalOpen] = useState(false); @@ -62,6 +69,8 @@ export const WalletUIStatesProvider = ( privacyPolicyUrl: props.privacyPolicyUrl, welcomeScreen: props.welcomeScreen, titleIconUrl: props.titleIconUrl, + isEmbed: props.isEmbed, + auth: props.auth, }); return ( diff --git a/packages/react/src/wallet/ConnectWallet/ConnectWallet.tsx b/packages/react/src/wallet/ConnectWallet/ConnectWallet.tsx index 7ed6c76d58b..875e16dd241 100644 --- a/packages/react/src/wallet/ConnectWallet/ConnectWallet.tsx +++ b/packages/react/src/wallet/ConnectWallet/ConnectWallet.tsx @@ -3,7 +3,6 @@ import { ConnectedWalletDetails, type DropDownPosition } from "./Details"; import { useAddress, useConnectionStatus, - useDisconnect, useLogout, useNetworkMismatch, useSwitchChain, @@ -22,7 +21,6 @@ import { Button } from "../../components/buttons"; import { Spinner } from "../../components/Spinner"; import styled from "@emotion/styled"; import type { NetworkSelectorProps } from "./NetworkSelector"; -import { onModalUnmount } from "./constants"; import { isMobile } from "../../evm/utils/isMobile"; import { CustomThemeProvider, @@ -344,7 +342,6 @@ export function ConnectWallet(props: ConnectWalletProps) { const [showSignatureModal, setShowSignatureModal] = useState(false); const address = useAddress(); const { user } = useUser(); - const disconnect = useDisconnect(); const connectedButNotSignedIn = !!authConfig?.authUrl && @@ -387,9 +384,6 @@ export function ConnectWallet(props: ConnectWalletProps) { setOpen={(value) => { if (!value) { setShowSignatureModal(false); - onModalUnmount(() => { - disconnect(); - }); } }} > diff --git a/packages/react/src/wallet/ConnectWallet/Details.tsx b/packages/react/src/wallet/ConnectWallet/Details.tsx index 07f1827ad73..d86e965ac65 100644 --- a/packages/react/src/wallet/ConnectWallet/Details.tsx +++ b/packages/react/src/wallet/ConnectWallet/Details.tsx @@ -606,6 +606,8 @@ export const ConnectedWalletDetails: React.FC<{ onExport={() => { setShowExportModal(false); }} + walletAddress={address} + walletInstance={activeWallet} /> diff --git a/packages/react/src/wallet/ConnectWallet/Modal/ConnectEmbed.tsx b/packages/react/src/wallet/ConnectWallet/Modal/ConnectEmbed.tsx new file mode 100644 index 00000000000..6406efa6c8c --- /dev/null +++ b/packages/react/src/wallet/ConnectWallet/Modal/ConnectEmbed.tsx @@ -0,0 +1,259 @@ +import { + SetModalConfigCtx, + WalletUIStatesProvider, +} from "../../../evm/providers/wallet-ui-states-provider"; +import { + modalMaxWidthCompact, + defaultTheme, + reservedScreens, +} from "../constants"; +import { ConnectModalContent } from "./ConnectModal"; +import { useScreen } from "./screen"; +import { isMobile } from "../../../evm/utils/isMobile"; +import { DynamicHeight } from "../../../components/DynamicHeight"; +import { + CustomThemeProvider, + useCustomTheme, +} from "../../../design-system/CustomThemeProvider"; +import { ComponentProps, useContext, useEffect } from "react"; +import { useTWLocale } from "../../../evm/providers/locale-provider"; +import { StyledDiv } from "../../../design-system/elements"; +import { Theme, radius } from "../../../design-system"; +import { + WalletConfig, + useConnectionStatus, + useThirdwebAuthContext, + useUser, + useWalletContext, +} from "@thirdweb-dev/react-core"; +import { Container } from "../../../components/basic"; +import { Spinner } from "../../../components/Spinner"; + +export type ConnectEmbedProps = { + className?: string; + theme?: "dark" | "light" | Theme; + + style?: React.CSSProperties; + + /** + * If provided, Embed will show a Terms of Service message at the bottom with below link + */ + termsOfServiceUrl?: string; + + /** + * If provided, Embed will show a Privacy Policy message at the bottom with below link + */ + privacyPolicyUrl?: string; + + /** + * Enforce that users must sign in with their wallet using [auth](https://portal.thirdweb.com/auth) after connecting their wallet. + * + * This requires the `authConfig` prop to be set on the `ThirdWebProvider` component. + */ + auth?: { + /** + * specify whether signing in is optional or not. + * + * By default it is `true` if `authConfig` is set on `ThirdWebProvider` + */ + loginOptional?: boolean; + /** + * Callback to be called after user signs in with their wallet + */ + onLogin?: (token: string) => void; + /** + * Callback to be called after user signs out + */ + onLogout?: () => void; + }; +}; + +/** + * @internal + */ +function useSignInRequired(loginOptional?: boolean) { + const connectionStatus = useConnectionStatus(); + const { user } = useUser(); + const authConfig = useThirdwebAuthContext(); + + if (loginOptional === false) { + return false; + } + + return ( + !!authConfig?.authUrl && !user?.address && connectionStatus === "connected" + ); +} + +/** + * Returns true if the `` should be rendered. + * It returns true if either one of the following conditions are met: + * - the wallet is not connected + * - the wallet is connected but the user is not signed in and `auth` is required ( loginOptional is not set to false ) + * + * @param loginOptional - + * Specify whether the `` you want to render has auth enabled or not. + * If not specified, it is assumed to be `false` ( login is required ) + * + */ +export function useShowConnectEmbed(loginOptional?: boolean) { + const connectionStatus = useConnectionStatus(); + const signInRequired = useSignInRequired(loginOptional); + + return connectionStatus !== "connected" || signInRequired; +} + +export const ConnectEmbed = (props: ConnectEmbedProps) => { + const loginOptional = props.auth?.loginOptional; + const requiresSignIn = useSignInRequired(loginOptional); + const show = useShowConnectEmbed(loginOptional); + + const { screen, setScreen, initialScreen } = useScreen(); + + // if showing main screen but signIn is required, switch to signIn screen + useEffect(() => { + if (requiresSignIn && screen === reservedScreens.main) { + setScreen(reservedScreens.signIn); + } + }, [requiresSignIn, screen, setScreen]); + + if (show) { + return ( + { + setScreen(initialScreen); + }} + screen={screen} + setScreen={setScreen} + initialScreen={initialScreen} + /> + ); + } + + return null; +}; + +const ConnectEmbedContent = ( + props: Omit & { + onClose: () => void; + screen: string | WalletConfig; + setScreen: (screen: string | WalletConfig) => void; + initialScreen: string | WalletConfig; + }, +) => { + const modalSize = "compact" as const; + const connectionStatus = useConnectionStatus(); + const { isAutoConnecting } = useWalletContext(); + + let content = ( + { + // no op + }} + onShow={() => { + // no op + }} + /> + ); + + if (isAutoConnecting || connectionStatus === "unknown") { + content = ( + + + + ); + } + + const walletUIStatesProps = { + theme: props.theme || defaultTheme, + modalSize: modalSize, + title: undefined, + termsOfServiceUrl: props.termsOfServiceUrl, + privacyPolicyUrl: props.privacyPolicyUrl, + isEmbed: true, + auth: props.auth, + }; + + return ( + + + + {content} + + + + + ); +}; + +export function SyncedWalletUIStates( + props: ComponentProps, +) { + const setModalConfig = useContext(SetModalConfigCtx); + const locale = useTWLocale(); + + // update modalConfig on props change + useEffect(() => { + setModalConfig((c) => ({ + ...c, + title: props.title || locale.connectWallet.defaultModalTitle, + theme: props.theme || "dark", + modalSize: (isMobile() ? "compact" : props.modalSize) || "wide", + termsOfServiceUrl: props.termsOfServiceUrl, + privacyPolicyUrl: props.privacyPolicyUrl, + welcomeScreen: props.welcomeScreen, + titleIconUrl: props.titleIconUrl, + })); + }, [ + props.title, + props.theme, + props.modalSize, + props.termsOfServiceUrl, + props.privacyPolicyUrl, + props.welcomeScreen, + props.titleIconUrl, + setModalConfig, + locale.connectWallet.defaultModalTitle, + ]); + + return ; +} + +const EmbedContainer = /* @__PURE__ */ StyledDiv(() => { + const theme = useCustomTheme(); + return { + color: theme.colors.primaryText, + background: theme.colors.modalBg, + width: "100%", + boxSizing: "border-box", + position: "relative", + lineHeight: "normal", + borderRadius: radius.xl, + border: `1px solid ${theme.colors.borderColor}`, + overflow: "hidden", + fontFamily: theme.fontFamily, + "& *::selection": { + backgroundColor: theme.colors.primaryText, + color: theme.colors.modalBg, + }, + }; +}); diff --git a/packages/react/src/wallet/ConnectWallet/Modal/ConnectModal.tsx b/packages/react/src/wallet/ConnectWallet/Modal/ConnectModal.tsx index 82a9b16a632..0f62a941512 100644 --- a/packages/react/src/wallet/ConnectWallet/Modal/ConnectModal.tsx +++ b/packages/react/src/wallet/ConnectWallet/Modal/ConnectModal.tsx @@ -2,11 +2,14 @@ import { Modal } from "../../../components/Modal"; import { WalletSelector } from "../WalletSelector"; import { WalletConfig, + useAddress, + useConnect, useConnectionStatus, useDisconnect, useThirdwebAuthContext, useUser, useWallet, + useWalletContext, useWallets, } from "@thirdweb-dev/react-core"; import { @@ -36,17 +39,17 @@ export const ConnectModalContent = (props: { screen: string | WalletConfig; initialScreen: string | WalletConfig; setScreen: (screen: string | WalletConfig) => void; - setHideModal: (hide: boolean) => void; + onHide: () => void; + onShow: () => void; + isOpen: boolean; + onClose: () => void; }) => { - const { screen, setScreen, initialScreen } = props; + const { screen, setScreen, initialScreen, onHide, onShow, onClose } = props; const walletConfigs = useWallets(); const connectionStatus = useConnectionStatus(); const disconnect = useDisconnect(); - const isWalletModalOpen = useIsWalletModalOpen(); - const setIsWalletModalOpen = useSetIsWalletModalOpen(); - const modalConfig = useContext(ModalConfigCtx); const setModalConfig = useContext(SetModalConfigCtx); @@ -58,21 +61,12 @@ export const ConnectModalContent = (props: { const { user } = useUser(); const authConfig = useThirdwebAuthContext(); - const closeModal = () => { - setIsWalletModalOpen(false); - onModalUnmount(() => { - setScreen(initialScreen); - }); - }; - - const { setHideModal } = props; const handleConnected = useCallback(() => { const requiresSignIn = modalConfig.auth?.loginOptional ? false : !!authConfig?.authUrl && !user?.address; - setHideModal(false); - + onShow(); // show sign in screen if required if (requiresSignIn) { setScreen(reservedScreens.signIn); @@ -80,19 +74,15 @@ export const ConnectModalContent = (props: { // close modal and reset screen else { - setIsWalletModalOpen(false); - onModalUnmount(() => { - setScreen(initialScreen); - }); + onClose(); } }, [ modalConfig.auth?.loginOptional, authConfig?.authUrl, user?.address, - setIsWalletModalOpen, setScreen, - initialScreen, - setHideModal, + onShow, + onClose, ]); const handleBack = useCallback(() => { @@ -102,6 +92,16 @@ export const ConnectModalContent = (props: { } }, [setScreen, initialScreen, connectionStatus, disconnect]); + const connect = useConnect(); + const address = useAddress(); + + const { + setConnectionStatus, + setConnectedWallet, + createWalletInstance, + activeWallet, + } = useWalletContext(); + const walletList = ( ); @@ -124,16 +131,19 @@ export const ConnectModalContent = (props: { theme={typeof theme === "string" ? theme : theme.type} goBack={handleBack} connected={handleConnected} - isOpen={isWalletModalOpen} - show={() => { - props.setHideModal(false); - }} - hide={() => { - props.setHideModal(true); - }} + isOpen={props.isOpen} + show={onShow} + hide={onHide} walletConfig={walletConfig} modalSize={modalConfig.modalSize} selectionData={modalConfig.data} + connect={(options: any) => connect(walletConfig, options)} + setConnectionStatus={setConnectionStatus} + setConnectedWallet={setConnectedWallet} + createWalletInstance={() => createWalletInstance(walletConfig)} + connectionStatus={connectionStatus} + connectedWallet={activeWallet} + connectedWalletAddress={address} setSelectionData={(data) => { setModalConfig((config) => ({ ...config, @@ -144,6 +154,15 @@ export const ConnectModalContent = (props: { ); }; + const signatureScreen = ( + + ); + return ( {isWideModal ? ( @@ -156,14 +175,7 @@ export const ConnectModalContent = (props: { > {walletList} - {screen === reservedScreens.signIn && ( - - )} + {screen === reservedScreens.signIn && signatureScreen} {screen === reservedScreens.main && <>{getStarted}} {screen === reservedScreens.getStarted && getStarted} {typeof screen !== "string" && getWalletUI(screen)} @@ -178,14 +190,7 @@ export const ConnectModalContent = (props: { maxHeight: compactModalMaxHeight, }} > - {screen === reservedScreens.signIn && ( - - )} + {screen === reservedScreens.signIn && signatureScreen} {screen === reservedScreens.main && walletList} {screen === reservedScreens.getStarted && getStarted} {typeof screen !== "string" && getWalletUI(screen)} @@ -196,9 +201,7 @@ export const ConnectModalContent = (props: { }; export const ConnectModal = () => { - const { theme, modalSize, auth } = useContext(ModalConfigCtx); - const authConfig = useThirdwebAuthContext(); - const { user } = useUser(); + const { theme, modalSize } = useContext(ModalConfigCtx); const { screen, setScreen, initialScreen } = useScreen(); const isWalletModalOpen = useIsWalletModalOpen(); @@ -206,14 +209,20 @@ export const ConnectModal = () => { const [hideModal, setHideModal] = useState(false); const connectionStatus = useConnectionStatus(); + const closeModal = useCallback(() => { + setIsWalletModalOpen(false); + onModalUnmount(() => { + setScreen(initialScreen); + }); + }, [initialScreen, setIsWalletModalOpen, setScreen]); + const [prevConnectionStatus, setPrevConnectionStatus] = useState(connectionStatus); + useEffect(() => { setPrevConnectionStatus(connectionStatus); }, [connectionStatus]); - const disconnect = useDisconnect(); - const wallet = useWallet(); const isWrapperConnected = !!wallet?.getPersonalWallet(); @@ -269,26 +278,16 @@ export const ConnectModal = () => { open={isWalletModalOpen} setOpen={(value) => { setIsWalletModalOpen(value); - if (!value) { - const requiresSignIn = auth?.loginOptional - ? false - : !!authConfig?.authUrl && !user?.address; - - onModalUnmount(() => { - if (connectionStatus === "connecting" || requiresSignIn) { - disconnect(); - } - - setScreen(initialScreen); - }); - } }} > setHideModal(true)} + onShow={() => setHideModal(false)} + isOpen={isWalletModalOpen} + onClose={closeModal} /> diff --git a/packages/react/src/wallet/ConnectWallet/Modal/ConnectModalInline.tsx b/packages/react/src/wallet/ConnectWallet/Modal/ConnectModalInline.tsx index e6591ea8b50..b31b1b0e9e7 100644 --- a/packages/react/src/wallet/ConnectWallet/Modal/ConnectModalInline.tsx +++ b/packages/react/src/wallet/ConnectWallet/Modal/ConnectModalInline.tsx @@ -1,11 +1,8 @@ import { Cross2Icon } from "@radix-ui/react-icons"; import { CrossContainer } from "../../../components/Modal"; import { IconButton } from "../../../components/buttons"; -import { iconSize, radius, shadow } from "../../../design-system"; -import { - SetModalConfigCtx, - WalletUIStatesProvider, -} from "../../../evm/providers/wallet-ui-states-provider"; +import { Theme, iconSize, radius, shadow } from "../../../design-system"; +import { WalletUIStatesProvider } from "../../../evm/providers/wallet-ui-states-provider"; import { wideModalMaxHeight, modalMaxWidthCompact, @@ -20,29 +17,65 @@ import { CustomThemeProvider, useCustomTheme, } from "../../../design-system/CustomThemeProvider"; -import { ComponentProps, useContext, useEffect } from "react"; -import { ConnectWalletProps } from "../ConnectWallet"; -import { useTWLocale } from "../../../evm/providers/locale-provider"; import { StyledDiv } from "../../../design-system/elements"; +import { SyncedWalletUIStates } from "./ConnectEmbed"; +import { WelcomeScreen } from "../screens/types"; + +/** + * @internal + */ +export type ConnectModalInlineProps = { + className?: string; + theme?: "dark" | "light" | Theme; + + /** + * Set a custom title for the modal + * @defaultValue "Connect" + */ + modalTitle?: string; + + /** + * Replace the thirdweb icon next to modalTitle and set your own iconUrl + * + * Set to empty string to hide the icon + */ + modalTitleIconUrl?: string; + + style?: React.CSSProperties; + + /** + * Set the size of the modal - `compact` or `wide` on desktop + * + * Modal size is always `compact` on mobile + * + * @defaultValue "wide" + */ + modalSize?: "compact" | "wide"; + + /** + * If provided, Modal will show a Terms of Service message at the bottom with below link + */ + termsOfServiceUrl?: string; + + /** + * If provided, Modal will show a Privacy Policy message at the bottom with below link + */ + privacyPolicyUrl?: string; + + /** + * Customize the welcome screen + * + * Either provide a component to replace the default screen entirely + * + * or an object with title, subtitle and imgSrc to change the content of the default screen + */ + welcomeScreen?: WelcomeScreen; +}; /** * @internal */ -export const ConnectModalInline = ( - props: Omit< - ConnectWalletProps, - | "detailsBtn" - | "dropdownPosition" - | "auth" - | "networkSelector" - | "hideTestnetFaucet" - | "switchToActiveChain" - | "supportedTokens" - | "hideSwitchToPersonalWallet" - > & { - onModalHide?: () => void; - }, -) => { +export const ConnectModalInline = (props: ConnectModalInlineProps) => { const { screen, setScreen, initialScreen } = useScreen(); const walletConfigs = useWallets(); const modalSize = @@ -55,10 +88,15 @@ export const ConnectModalInline = ( initialScreen={initialScreen} screen={screen} setScreen={setScreen} - setHideModal={() => { - if (props.onModalHide) { - props.onModalHide(); - } + onHide={() => { + // no op + }} + isOpen={true} + onClose={() => { + // no op + }} + onShow={() => { + // no op }} /> @@ -98,9 +136,10 @@ export const ConnectModalInline = ( modalSize === "compact" ? modalMaxWidthCompact : modalMaxWidthWide, + ...props.style, }} > - {props.modalSize === "compact" ? ( + {modalSize === "compact" ? ( {content} ) : ( content @@ -112,39 +151,6 @@ export const ConnectModalInline = ( ); }; -function SyncedWalletUIStates( - props: ComponentProps, -) { - const setModalConfig = useContext(SetModalConfigCtx); - const locale = useTWLocale(); - - // update modalConfig on props change - useEffect(() => { - setModalConfig((c) => ({ - ...c, - title: props.title || locale.connectWallet.defaultModalTitle, - theme: props.theme || "dark", - modalSize: (isMobile() ? "compact" : props.modalSize) || "wide", - termsOfServiceUrl: props.termsOfServiceUrl, - privacyPolicyUrl: props.privacyPolicyUrl, - welcomeScreen: props.welcomeScreen, - titleIconUrl: props.titleIconUrl, - })); - }, [ - props.title, - props.theme, - props.modalSize, - props.termsOfServiceUrl, - props.privacyPolicyUrl, - props.welcomeScreen, - props.titleIconUrl, - setModalConfig, - locale.connectWallet.defaultModalTitle, - ]); - - return ; -} - const ConnectModalInlineContainer = /* @__PURE__ */ StyledDiv(() => { const theme = useCustomTheme(); return { diff --git a/packages/react/src/wallet/ConnectWallet/Modal/screen.tsx b/packages/react/src/wallet/ConnectWallet/Modal/screen.tsx index 58b11c84ba7..a71a6c62db1 100644 --- a/packages/react/src/wallet/ConnectWallet/Modal/screen.tsx +++ b/packages/react/src/wallet/ConnectWallet/Modal/screen.tsx @@ -1,4 +1,4 @@ -import { WalletConfig, useWallets } from "@thirdweb-dev/react-core"; +import { WalletConfig, useWallet, useWallets } from "@thirdweb-dev/react-core"; import { createContext, useState, useContext, useEffect, useRef } from "react"; import { reservedScreens } from "../constants"; @@ -17,6 +17,7 @@ export function useScreen() { const [screen, setScreen] = useState(initialScreen); const prevInitialScreen = useRef(initialScreen); + const wallet = useWallet(); // when the initial screen changes, reset the screen to the initial screen ( if the modal is closed ) useEffect(() => { @@ -26,6 +27,13 @@ export function useScreen() { } }, [initialScreen]); + // if on signature screen and suddenly the wallet is disconnected, go back to the main screen + useEffect(() => { + if (!wallet && screen === reservedScreens.signIn) { + setScreen(reservedScreens.main); + } + }, [wallet, screen]); + return { screen, setScreen, diff --git a/packages/react/src/wallet/ConnectWallet/SignatureScreen.tsx b/packages/react/src/wallet/ConnectWallet/SignatureScreen.tsx index 524509f5f2c..d1a7d5b8072 100644 --- a/packages/react/src/wallet/ConnectWallet/SignatureScreen.tsx +++ b/packages/react/src/wallet/ConnectWallet/SignatureScreen.tsx @@ -2,6 +2,7 @@ import { WalletInstance, useAddress, useChainId, + useDisconnect, useLogin, useWallet, useWalletConfig, @@ -46,6 +47,7 @@ export const SignatureScreen: React.FC<{ const [status, setStatus] = useState("idle"); const { login } = useLogin(); const [tryId, setTryId] = useState(0); + const disconnect = useDisconnect(); const isSafeWallet = wallet?.walletId === walletIds.safe; @@ -132,19 +134,32 @@ export const SignatureScreen: React.FC<{ {locale.instructionScreen.instruction} - + + + ) : ( <> @@ -218,6 +233,20 @@ export const SignatureScreen: React.FC<{ {locale.signingScreen.tryAgain} + + )} diff --git a/packages/react/src/wallet/ConnectWallet/WalletSelector.tsx b/packages/react/src/wallet/ConnectWallet/WalletSelector.tsx index 003f629b1d9..210f2e64c2b 100644 --- a/packages/react/src/wallet/ConnectWallet/WalletSelector.tsx +++ b/packages/react/src/wallet/ConnectWallet/WalletSelector.tsx @@ -7,11 +7,13 @@ import { ScreenBottomContainer, Line, } from "../../components/basic"; -import { Button } from "../../components/buttons"; +import { Button, IconButton } from "../../components/buttons"; import { ModalTitle } from "../../components/modalElements"; import { iconSize, radius, spacing } from "../../design-system"; import { + SelectUIProps, WalletConfig, + WalletInstance, useConnectionStatus, useDisconnect, } from "@thirdweb-dev/react-core"; @@ -26,14 +28,29 @@ import { Spacer } from "../../components/Spacer"; import { TextDivider } from "../../components/TextDivider"; import { TOS } from "./Modal/TOS"; import { useTWLocale } from "../../evm/providers/locale-provider"; +import { ChevronLeftIcon } from "@radix-ui/react-icons"; import { StyledButton, StyledUl } from "../../design-system/elements"; import { useCustomTheme } from "../../design-system/CustomThemeProvider"; +type WalletSelectUIProps = { + connect: ( + walletConfig: WalletConfig, + options?: any, + ) => Promise; + connectionStatus: SelectUIProps["connectionStatus"]; + createWalletInstance: (walletConfig: WalletConfig) => WalletInstance; + setConnectedWallet: SelectUIProps["setConnectedWallet"]; + setConnectionStatus: SelectUIProps["setConnectionStatus"]; + connectedWallet?: WalletInstance; + connectedWalletAddress?: string; +}; + export const WalletSelector: React.FC<{ walletConfigs: WalletConfig[]; selectWallet: (wallet: WalletConfig) => void; onGetStarted: () => void; title: string; + selectUIProps: WalletSelectUIProps; }> = (props) => { const modalConfig = useContext(ModalConfigCtx); const isCompact = modalConfig.modalSize === "compact"; @@ -193,6 +210,7 @@ export const WalletSelector: React.FC<{ ); @@ -211,6 +229,7 @@ export const WalletSelector: React.FC<{ ); @@ -246,6 +265,7 @@ export const WalletSelector: React.FC<{ {eoaWallets.length > 0 && ( <> @@ -301,6 +321,7 @@ export const WalletSelector: React.FC<{ @@ -330,6 +351,7 @@ export const WalletSelector: React.FC<{ ); @@ -343,28 +365,62 @@ export const WalletSelector: React.FC<{ return ( {/* Header */} - - {isWalletGroupExpanded ? ( - { - setIsWalletGroupExpanded(false); - }} - /> - ) : ( - twTitle - )} - + {!modalConfig.isEmbed && ( + + {isWalletGroupExpanded ? ( + { + setIsWalletGroupExpanded(false); + }} + /> + ) : ( + twTitle + )} + + )} {/* Body */} + {modalConfig.isEmbed && isWalletGroupExpanded && ( + + { + setIsWalletGroupExpanded(false); + }} + style={{ + gap: spacing.xxs, + transform: `translateX(-${spacing.xs})`, + paddingBlock: spacing.xxs, + paddingRight: spacing.xs, + }} + > + + {locale.goBackButton} + + + )} + {topSection} @@ -377,6 +433,7 @@ export const WalletSelection: React.FC<{ walletConfigs: WalletConfig[]; selectWallet: (wallet: WalletConfig) => void; maxHeight?: string; + selectUIProps: WalletSelectUIProps; }> = (props) => { const modalConfig = useContext(ModalConfigCtx); const setModalConfig = useContext(SetModalConfigCtx); @@ -401,6 +458,19 @@ export const WalletSelection: React.FC<{ setModalConfig((config) => ({ ...config, data })); }} walletConfig={walletConfig} + connect={(options: any) => + props.selectUIProps.connect(walletConfig, options) + } + connectionStatus={props.selectUIProps.connectionStatus} + createWalletInstance={() => + props.selectUIProps.createWalletInstance(walletConfig) + } + setConnectedWallet={props.selectUIProps.setConnectedWallet} + setConnectionStatus={props.selectUIProps.setConnectionStatus} + connectedWallet={props.selectUIProps.connectedWallet} + connectedWalletAddress={ + props.selectUIProps.connectedWalletAddress + } /> ) : ( - + diff --git a/packages/react/src/wallet/ConnectWallet/screens/WCOpenUri.tsx b/packages/react/src/wallet/ConnectWallet/screens/WCOpenUri.tsx index 50c8b0dd955..8afa3d77b20 100644 --- a/packages/react/src/wallet/ConnectWallet/screens/WCOpenUri.tsx +++ b/packages/react/src/wallet/ConnectWallet/screens/WCOpenUri.tsx @@ -1,10 +1,11 @@ -import { - useCreateWalletInstance, - useWalletContext, -} from "@thirdweb-dev/react-core"; +import { useWalletContext } from "@thirdweb-dev/react-core"; import { useEffect, useRef } from "react"; import type { WalletConnect } from "@thirdweb-dev/wallets"; -import type { WalletConfig } from "@thirdweb-dev/react-core"; +import type { + ConnectUIProps, + WalletConfig, + WalletInstance, +} from "@thirdweb-dev/react-core"; import { isAndroid, isIOS } from "../../../evm/utils/isMobile"; import { ConnectingScreen } from "./ConnectingScreen"; import { openWindow } from "../../utils/openWindow"; @@ -12,13 +13,14 @@ import { openWindow } from "../../utils/openWindow"; export const WCOpenURI: React.FC<{ onBack: () => void; onConnected: () => void; - walletConfig: WalletConfig; + createWalletInstance: () => WalletInstance; + setConnectedWallet: ConnectUIProps["setConnectedWallet"]; + setConnectionStatus: ConnectUIProps["setConnectionStatus"]; appUriPrefix: { ios: string; android: string; other: string; }; - // supportLink: string; errorConnecting: boolean; onRetry: () => void; hideBackButton: boolean; @@ -30,20 +32,24 @@ export const WCOpenURI: React.FC<{ failed: string; inProgress: string; }; -}> = ({ - onBack, - onConnected, - walletConfig, - appUriPrefix, - onRetry, - errorConnecting, - hideBackButton, - onGetStarted, - locale, -}) => { - const createInstance = useCreateWalletInstance(); - const { setConnectedWallet, chainToConnect, setConnectionStatus } = - useWalletContext(); + meta: WalletConfig["meta"]; +}> = (props) => { + const { + createWalletInstance, + setConnectionStatus, + setConnectedWallet, + onBack, + onConnected, + appUriPrefix, + onRetry, + errorConnecting, + hideBackButton, + onGetStarted, + locale, + meta, + } = props; + + const { chainToConnect } = useWalletContext(); const connectStarted = useRef(false); useEffect(() => { @@ -52,7 +58,7 @@ export const WCOpenURI: React.FC<{ } connectStarted.current = true; - const wallet = createInstance(walletConfig) as WalletConnect; + const wallet = createWalletInstance() as WalletConnect; setConnectionStatus("connecting"); wallet.connectWithQrCode({ @@ -74,13 +80,12 @@ export const WCOpenURI: React.FC<{ }, }); }, [ - createInstance, setConnectedWallet, chainToConnect, onConnected, - walletConfig, setConnectionStatus, appUriPrefix, + createWalletInstance, ]); return ( @@ -88,8 +93,8 @@ export const WCOpenURI: React.FC<{ locale={locale} hideBackButton={hideBackButton} onBack={onBack} - walletName={walletConfig.meta.name} - walletIconURL={walletConfig.meta.iconURL} + walletName={meta.name} + walletIconURL={meta.iconURL} errorConnecting={errorConnecting} onRetry={onRetry} onGetStarted={onGetStarted} diff --git a/packages/react/src/wallet/wallets/coin98/Coin98ConnectUI.tsx b/packages/react/src/wallet/wallets/_common/ExtensionORWCConnectionUI.tsx similarity index 53% rename from packages/react/src/wallet/wallets/coin98/Coin98ConnectUI.tsx rename to packages/react/src/wallet/wallets/_common/ExtensionORWCConnectionUI.tsx index 0c80b65a0f7..2a73ba94a2a 100644 --- a/packages/react/src/wallet/wallets/coin98/Coin98ConnectUI.tsx +++ b/packages/react/src/wallet/wallets/_common/ExtensionORWCConnectionUI.tsx @@ -1,20 +1,49 @@ -import { ConnectUIProps, useConnect } from "@thirdweb-dev/react-core"; +import { ConnectUIProps, WalletConfig } from "@thirdweb-dev/react-core"; import { ConnectingScreen } from "../../ConnectWallet/screens/ConnectingScreen"; import { isMobile } from "../../../evm/utils/isMobile"; import { useCallback, useEffect, useRef, useState } from "react"; import { GetStartedScreen } from "../../ConnectWallet/screens/GetStartedScreen"; -import type { Coin98Wallet } from "@thirdweb-dev/wallets"; import { wait } from "../../../utils/wait"; -import { useTWLocale } from "../../../evm/providers/locale-provider"; -import { Coin98Scan } from "./Coin98Scan"; import { WCOpenURI } from "../../ConnectWallet/screens/WCOpenUri"; -import { coin98WalletUris } from "./coin98WalletUris"; +import { WalletConnectScanUI } from "./WalletConnectScanUI"; +import { WCConnectableWallet } from "./WCConnectableWallet"; +import { ExtensionAndQRScreensLocale } from "../../../evm/locales/types"; -export const Coin98ConnectUI = (props: ConnectUIProps) => { +export const ExtensionOrWCConnectionUI = (props: { + walletConnectUris: { + ios: string; + android: string; + other: string; + }; + walletLocale: ExtensionAndQRScreensLocale; + meta: WalletConfig["meta"]; + isInstalled?: () => boolean; + supportedWallets: WalletConfig[]; + connect: ConnectUIProps["connect"]; + connected: ConnectUIProps["connected"]; + goBack: ConnectUIProps["goBack"]; + setConnectedWallet: ConnectUIProps["setConnectedWallet"]; + setConnectionStatus: ConnectUIProps["setConnectionStatus"]; + createWalletInstance: () => WCConnectableWallet; +}) => { const [screen, setScreen] = useState< "connecting" | "scanning" | "get-started" | "open-wc-uri" >("connecting"); - const locale = useTWLocale().wallets.coin98Wallet; + + const { + connected, + connect, + supportedWallets, + isInstalled, + meta, + createWalletInstance, + setConnectedWallet, + setConnectionStatus, + goBack, + walletConnectUris, + walletLocale: locale, + } = props; + const connectingLocale = { getStartedLink: locale.getStartedLink, instruction: locale.connectionScreen.instruction, @@ -22,11 +51,9 @@ export const Coin98ConnectUI = (props: ConnectUIProps) => { inProgress: locale.connectionScreen.inProgress, failed: locale.connectionScreen.failed, }; - const { walletConfig, connected } = props; - const connect = useConnect(); - const [errorConnecting, setErrorConnecting] = useState(false); - const hideBackButton = props.supportedWallets.length === 1; + const [errorConnecting, setErrorConnecting] = useState(false); + const hideBackButton = supportedWallets.length === 1; const connectToExtension = useCallback(async () => { try { @@ -34,13 +61,13 @@ export const Coin98ConnectUI = (props: ConnectUIProps) => { setErrorConnecting(false); setScreen("connecting"); await wait(1000); - await connect(walletConfig); + await connect(); connected(); } catch (e) { setErrorConnecting(true); console.error(e); } - }, [connected, connect, walletConfig]); + }, [connected, connect]); const connectPrompted = useRef(false); useEffect(() => { @@ -48,13 +75,11 @@ export const Coin98ConnectUI = (props: ConnectUIProps) => { return; } - const isInstalled = walletConfig.isInstalled - ? walletConfig.isInstalled() - : false; + const isInstalledVal = isInstalled ? isInstalled() : false; // if loading (async () => { - if (isInstalled) { + if (isInstalledVal) { connectToExtension(); } @@ -69,27 +94,21 @@ export const Coin98ConnectUI = (props: ConnectUIProps) => { } } })(); - }, [connectToExtension, walletConfig]); + }, [connectToExtension, isInstalled]); if (screen === "connecting") { return ( { setScreen("get-started"); }} onRetry={connectToExtension} hideBackButton={hideBackButton} - onBack={props.goBack} - walletName={walletConfig.meta.name} - walletIconURL={walletConfig.meta.iconURL} + onBack={goBack} + walletName={meta.name} + walletIconURL={meta.iconURL} /> ); } @@ -106,10 +125,15 @@ export const Coin98ConnectUI = (props: ConnectUIProps) => { setScreen("get-started"); }} hideBackButton={hideBackButton} - onBack={props.goBack} + onBack={goBack} onConnected={connected} - walletConfig={walletConfig} - appUriPrefix={coin98WalletUris} + appUriPrefix={walletConnectUris} + createWalletInstance={createWalletInstance} + meta={meta} + setConnectedWallet={(w) => { + setConnectedWallet(w as WCConnectableWallet); + }} + setConnectionStatus={setConnectionStatus} /> ); } @@ -120,26 +144,32 @@ export const Coin98ConnectUI = (props: ConnectUIProps) => { locale={{ scanToDownload: locale.getStartedScreen.instruction, }} - walletIconURL={walletConfig.meta.iconURL} - walletName={walletConfig.meta.name} - chromeExtensionLink={walletConfig.meta.urls?.chrome} - googlePlayStoreLink={walletConfig.meta.urls?.android} - appleStoreLink={walletConfig.meta.urls?.ios} - onBack={props.goBack} + walletIconURL={meta.iconURL} + walletName={meta.name} + chromeExtensionLink={meta.urls?.chrome} + googlePlayStoreLink={meta.urls?.android} + appleStoreLink={meta.urls?.ios} + onBack={goBack} /> ); } if (screen === "scanning") { return ( - { setScreen("get-started"); }} hideBackButton={hideBackButton} - walletConfig={walletConfig} + createWalletInstance={createWalletInstance} + meta={meta} + setConnectedWallet={(w) => { + setConnectedWallet(w as WCConnectableWallet); + }} + setConnectionStatus={setConnectionStatus} + walletLocale={locale} /> ); } diff --git a/packages/react/src/wallet/wallets/_common/WCConnectableWallet.ts b/packages/react/src/wallet/wallets/_common/WCConnectableWallet.ts new file mode 100644 index 00000000000..beb08728f51 --- /dev/null +++ b/packages/react/src/wallet/wallets/_common/WCConnectableWallet.ts @@ -0,0 +1,11 @@ +import { AbstractClientWallet } from "@thirdweb-dev/wallets"; + +type ConnectWithQrCodeArgs = { + chainId?: number; + onQrCodeUri: (uri: string) => void; + onConnected: (accountAddress: string) => void; +}; + +export interface WCConnectableWallet extends AbstractClientWallet { + connectWithQrCode: (options: ConnectWithQrCodeArgs) => Promise; +} diff --git a/packages/react/src/wallet/wallets/coreWallet/CoreWalletScan.tsx b/packages/react/src/wallet/wallets/_common/WalletConnectScanUI.tsx similarity index 51% rename from packages/react/src/wallet/wallets/coreWallet/CoreWalletScan.tsx rename to packages/react/src/wallet/wallets/_common/WalletConnectScanUI.tsx index 8556efd9ca0..9ce9d5ed690 100644 --- a/packages/react/src/wallet/wallets/coreWallet/CoreWalletScan.tsx +++ b/packages/react/src/wallet/wallets/_common/WalletConnectScanUI.tsx @@ -1,25 +1,38 @@ import { ScanScreen } from "../../ConnectWallet/screens/ScanScreen"; -import { - useCreateWalletInstance, - useWalletContext, -} from "@thirdweb-dev/react-core"; +import { useWalletContext } from "@thirdweb-dev/react-core"; import { useEffect, useRef, useState } from "react"; -import type { CoreWallet } from "@thirdweb-dev/wallets"; -import type { WalletConfig } from "@thirdweb-dev/react-core"; -import { useTWLocale } from "../../../evm/providers/locale-provider"; +import type { + ConnectionStatus, + WalletConfig, + WalletInstance, +} from "@thirdweb-dev/react-core"; +import { WCConnectableWallet } from "./WCConnectableWallet"; +import { ExtensionAndQRScreensLocale } from "../../../evm/locales/types"; -export const CoreWalletScan: React.FC<{ +export const WalletConnectScanUI: React.FC<{ onBack: () => void; onGetStarted: () => void; onConnected: () => void; - walletConfig: WalletConfig; hideBackButton: boolean; -}> = ({ onBack, onConnected, onGetStarted, walletConfig, hideBackButton }) => { - const locale = useTWLocale().wallets.coreWallet; - const createInstance = useCreateWalletInstance(); + setConnectionStatus: (status: ConnectionStatus) => void; + setConnectedWallet: (wallet: WalletInstance) => void; + walletLocale: ExtensionAndQRScreensLocale; + createWalletInstance: () => WCConnectableWallet; + meta: WalletConfig["meta"]; +}> = (props) => { + const { + onBack, + onConnected, + onGetStarted, + hideBackButton, + setConnectionStatus, + setConnectedWallet, + createWalletInstance, + } = props; + + const locale = props.walletLocale; const [qrCodeUri, setQrCodeUri] = useState(); - const { setConnectedWallet, chainToConnect, setConnectionStatus } = - useWalletContext(); + const { chainToConnect } = useWalletContext(); const scanStarted = useRef(false); useEffect(() => { @@ -28,25 +41,24 @@ export const CoreWalletScan: React.FC<{ } scanStarted.current = true; - const wallet = createInstance(walletConfig); + const wallet = createWalletInstance(); - setConnectionStatus("connecting"); wallet.connectWithQrCode({ chainId: chainToConnect?.chainId, onQrCodeUri(uri) { setQrCodeUri(uri); }, onConnected() { + setConnectionStatus("connecting"); setConnectedWallet(wallet); onConnected(); }, }); }, [ - createInstance, + createWalletInstance, setConnectedWallet, chainToConnect, onConnected, - walletConfig, setConnectionStatus, ]); @@ -56,8 +68,8 @@ export const CoreWalletScan: React.FC<{ onBack={onBack} onGetStarted={onGetStarted} qrCodeUri={qrCodeUri} - walletName={walletConfig.meta.name} - walletIconURL={walletConfig.meta.iconURL} + walletName={props.meta.name} + walletIconURL={props.meta.iconURL} hideBackButton={hideBackButton} getStartedLink={locale.getStartedLink} /> diff --git a/packages/react/src/wallet/wallets/coin98/Coin98Scan.tsx b/packages/react/src/wallet/wallets/coin98/Coin98Scan.tsx deleted file mode 100644 index 86babdd8b7e..00000000000 --- a/packages/react/src/wallet/wallets/coin98/Coin98Scan.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import { ScanScreen } from "../../ConnectWallet/screens/ScanScreen"; -import { - useCreateWalletInstance, - useWalletContext, -} from "@thirdweb-dev/react-core"; -import { useEffect, useRef, useState } from "react"; -import type { Coin98Wallet } from "@thirdweb-dev/wallets"; -import type { WalletConfig } from "@thirdweb-dev/react-core"; -import { useTWLocale } from "../../../evm/providers/locale-provider"; - -export const Coin98Scan: React.FC<{ - onBack: () => void; - onGetStarted: () => void; - onConnected: () => void; - walletConfig: WalletConfig; - hideBackButton: boolean; -}> = ({ onBack, onConnected, onGetStarted, walletConfig, hideBackButton }) => { - const locale = useTWLocale().wallets.coin98Wallet; - const createInstance = useCreateWalletInstance(); - const [qrCodeUri, setQrCodeUri] = useState(); - const { setConnectedWallet, chainToConnect, setConnectionStatus } = - useWalletContext(); - - const scanStarted = useRef(false); - useEffect(() => { - if (scanStarted.current) { - return; - } - scanStarted.current = true; - - const wallet = createInstance(walletConfig); - - setConnectionStatus("connecting"); - wallet.connectWithQrCode({ - chainId: chainToConnect?.chainId, - onQrCodeUri(uri) { - setQrCodeUri(uri); - }, - onConnected() { - setConnectedWallet(wallet); - onConnected(); - }, - }); - }, [ - createInstance, - setConnectedWallet, - chainToConnect, - onConnected, - walletConfig, - setConnectionStatus, - ]); - - return ( - - ); -}; diff --git a/packages/react/src/wallet/wallets/coin98/coin98Wallet.tsx b/packages/react/src/wallet/wallets/coin98/coin98Wallet.tsx index 5eca54608bb..c435af5db33 100644 --- a/packages/react/src/wallet/wallets/coin98/coin98Wallet.tsx +++ b/packages/react/src/wallet/wallets/coin98/coin98Wallet.tsx @@ -1,6 +1,17 @@ -import type { WalletOptions, WalletConfig } from "@thirdweb-dev/react-core"; +import type { + WalletOptions, + WalletConfig, + ConnectUIProps, +} from "@thirdweb-dev/react-core"; import { Coin98Wallet, getInjectedCoin98Provider } from "@thirdweb-dev/wallets"; -import { Coin98ConnectUI } from "./Coin98ConnectUI"; +import { ExtensionOrWCConnectionUI } from "../_common/ExtensionORWCConnectionUI"; +import { useTWLocale } from "../../../evm/providers/locale-provider"; + +const coin98WalletUris = { + ios: "coin98://", + android: "coin98://", + other: "coin98://", +}; export type Coin98WalletConfigOptions = { /** @@ -82,9 +93,30 @@ export const coin98Wallet = ( return wallet; }, - connectUI: Coin98ConnectUI, - isInstalled() { - return !!getInjectedCoin98Provider(); - }, + connectUI: ConnectUI, + isInstalled: isCoin98Installed, }; }; + +function isCoin98Installed() { + return !!getInjectedCoin98Provider(); +} + +function ConnectUI(props: ConnectUIProps) { + const locale = useTWLocale(); + return ( + props.setConnectedWallet(w as Coin98Wallet)} + setConnectionStatus={props.setConnectionStatus} + supportedWallets={props.supportedWallets} + walletConnectUris={coin98WalletUris} + walletLocale={locale.wallets.coin98Wallet} + isInstalled={isCoin98Installed} + /> + ); +} diff --git a/packages/react/src/wallet/wallets/coin98/coin98WalletUris.ts b/packages/react/src/wallet/wallets/coin98/coin98WalletUris.ts deleted file mode 100644 index 119ff08e435..00000000000 --- a/packages/react/src/wallet/wallets/coin98/coin98WalletUris.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const coin98WalletUris = { - ios: "coin98://", - android: "coin98://", - other: "coin98://", -}; diff --git a/packages/react/src/wallet/wallets/coinbase/CoinbaseConnectUI.tsx b/packages/react/src/wallet/wallets/coinbase/CoinbaseConnectUI.tsx index c7e4a3a21ba..421ea1b3e85 100644 --- a/packages/react/src/wallet/wallets/coinbase/CoinbaseConnectUI.tsx +++ b/packages/react/src/wallet/wallets/coinbase/CoinbaseConnectUI.tsx @@ -1,4 +1,4 @@ -import { ConnectUIProps, useConnect } from "@thirdweb-dev/react-core"; +import type { ConnectUIProps } from "@thirdweb-dev/react-core"; import { ConnectingScreen } from "../../ConnectWallet/screens/ConnectingScreen"; import { isMobile } from "../../../evm/utils/isMobile"; import { useCallback, useEffect, useRef, useState } from "react"; @@ -13,8 +13,10 @@ export const CoinbaseConnectUI = ({ connected, goBack, supportedWallets, + setConnectedWallet, + setConnectionStatus, + connect, }: ConnectUIProps) => { - const connect = useConnect(); const { meta } = walletConfig; const [screen, setScreen] = useState< "connecting" | "loading" | "scanning" | "get-started" @@ -30,13 +32,13 @@ export const CoinbaseConnectUI = ({ connectPrompted.current = true; await wait(1000); setScreen("connecting"); - await connect(walletConfig); + await connect(); connected(); } catch (e) { setErrorConnecting(true); console.error(e); } - }, [connected, connect, walletConfig]); + }, [connected, connect]); const connectPrompted = useRef(false); useEffect(() => { @@ -62,7 +64,7 @@ export const CoinbaseConnectUI = ({ else { if (isMobile()) { // coinbase will redirect to download page for coinbase wallet apps - connect(walletConfig); + connect(); } else { setScreen("scanning"); } @@ -117,6 +119,8 @@ export const CoinbaseConnectUI = ({ onGetStarted={() => setScreen("get-started")} walletConfig={walletConfig} hideBackButton={hideBackButton} + setConnectedWallet={setConnectedWallet} + setConnectionStatus={setConnectionStatus} /> ); } diff --git a/packages/react/src/wallet/wallets/coinbase/CoinbaseScan.tsx b/packages/react/src/wallet/wallets/coinbase/CoinbaseScan.tsx index 8f90d3e91e6..767efa8a391 100644 --- a/packages/react/src/wallet/wallets/coinbase/CoinbaseScan.tsx +++ b/packages/react/src/wallet/wallets/coinbase/CoinbaseScan.tsx @@ -1,5 +1,6 @@ import type { CoinbaseWallet } from "@thirdweb-dev/wallets"; import { + ConnectUIProps, WalletConfig, useCreateWalletInstance, useWalletContext, @@ -14,12 +15,21 @@ export const CoinbaseScan: React.FC<{ onConnected: () => void; walletConfig: WalletConfig; hideBackButton: boolean; -}> = ({ walletConfig, onConnected, onGetStarted, onBack, hideBackButton }) => { + setConnectedWallet: ConnectUIProps["setConnectedWallet"]; + setConnectionStatus: ConnectUIProps["setConnectionStatus"]; +}> = ({ + walletConfig, + onConnected, + onGetStarted, + onBack, + hideBackButton, + setConnectionStatus, + setConnectedWallet, +}) => { const locale = useTWLocale().wallets.coinbaseWallet; const createInstance = useCreateWalletInstance(); const [qrCodeUri, setQrCodeUri] = useState(undefined); - const { setConnectedWallet, chainToConnect, setConnectionStatus } = - useWalletContext(); + const { chainToConnect } = useWalletContext(); const scanStarted = useRef(false); diff --git a/packages/react/src/wallet/wallets/coinbase/coinbaseWallet.tsx b/packages/react/src/wallet/wallets/coinbase/coinbaseWallet.tsx index d7740354a57..5eb49fc9844 100644 --- a/packages/react/src/wallet/wallets/coinbase/coinbaseWallet.tsx +++ b/packages/react/src/wallet/wallets/coinbase/coinbaseWallet.tsx @@ -7,8 +7,6 @@ import { CoinbaseConnectUI } from "./CoinbaseConnectUI"; import { ConnectUIProps, useCreateWalletInstance, - useSetConnectedWallet, - useSetConnectionStatus, } from "@thirdweb-dev/react-core"; import { useEffect, useRef } from "react"; @@ -105,10 +103,10 @@ export const CoinbaseNativeModalConnectUI = ({ supportedWallets, theme, goBack, + setConnectionStatus, + setConnectedWallet, }: ConnectUIProps) => { const createWalletInstance = useCreateWalletInstance(); - const setConnectionStatus = useSetConnectionStatus(); - const setConnectedWallet = useSetConnectedWallet(); const prompted = useRef(false); const singleWallet = supportedWallets.length === 1; diff --git a/packages/react/src/wallet/wallets/coreWallet/CoreWalletConnectUI.tsx b/packages/react/src/wallet/wallets/coreWallet/CoreWalletConnectUI.tsx deleted file mode 100644 index 616d12ef7ca..00000000000 --- a/packages/react/src/wallet/wallets/coreWallet/CoreWalletConnectUI.tsx +++ /dev/null @@ -1,148 +0,0 @@ -import { ConnectUIProps, useConnect } from "@thirdweb-dev/react-core"; -import { ConnectingScreen } from "../../ConnectWallet/screens/ConnectingScreen"; -import { isMobile } from "../../../evm/utils/isMobile"; -import { useCallback, useEffect, useRef, useState } from "react"; -import { CoreWalletScan } from "./CoreWalletScan"; -import { GetStartedScreen } from "../../ConnectWallet/screens/GetStartedScreen"; -import type { CoreWallet } from "@thirdweb-dev/wallets"; -import { wait } from "../../../utils/wait"; -import { useTWLocale } from "../../../evm/providers/locale-provider"; -import { WCOpenURI } from "../../ConnectWallet/screens/WCOpenUri"; -import { coreWalletUris } from "./coreWalletUris"; - -export const CoreWalletConnectUI = (props: ConnectUIProps) => { - const [screen, setScreen] = useState< - "connecting" | "scanning" | "get-started" | "open-wc-uri" - >("connecting"); - const locale = useTWLocale().wallets.coreWallet; - const connectingLocale = { - getStartedLink: locale.getStartedLink, - instruction: locale.connectionScreen.instruction, - tryAgain: locale.connectionScreen.retry, - inProgress: locale.connectionScreen.inProgress, - failed: locale.connectionScreen.failed, - }; - const { walletConfig, connected } = props; - const connect = useConnect(); - const [errorConnecting, setErrorConnecting] = useState(false); - - const hideBackButton = props.supportedWallets.length === 1; - - const connectToExtension = useCallback(async () => { - try { - connectPrompted.current = true; - setErrorConnecting(false); - setScreen("connecting"); - await wait(1000); - await connect(walletConfig); - connected(); - } catch (e) { - setErrorConnecting(true); - console.error(e); - } - }, [connected, connect, walletConfig]); - - const connectPrompted = useRef(false); - useEffect(() => { - if (connectPrompted.current) { - return; - } - - const isInstalled = walletConfig.isInstalled - ? walletConfig.isInstalled() - : false; - - // if loading - (async () => { - if (isInstalled) { - connectToExtension(); - } - - // if wallet is not injected - else { - // on mobile, open the Core Mobile via wallet connect - if (isMobile()) { - setScreen("open-wc-uri"); - } else { - // on desktop, show the Core app scan qr code - setScreen("scanning"); - } - } - })(); - }, [connectToExtension, walletConfig]); - - if (screen === "connecting") { - return ( - { - setScreen("get-started"); - }} - onRetry={connectToExtension} - hideBackButton={hideBackButton} - onBack={props.goBack} - walletName={walletConfig.meta.name} - walletIconURL={walletConfig.meta.iconURL} - /> - ); - } - - if (screen === "open-wc-uri") { - return ( - { - // NOOP - TODO make onRetry optional - }} - errorConnecting={errorConnecting} - onGetStarted={() => { - setScreen("get-started"); - }} - hideBackButton={hideBackButton} - onBack={props.goBack} - onConnected={connected} - walletConfig={walletConfig} - appUriPrefix={coreWalletUris} - /> - ); - } - - if (screen === "get-started") { - return ( - - ); - } - - if (screen === "scanning") { - return ( - { - setScreen("get-started"); - }} - hideBackButton={hideBackButton} - walletConfig={walletConfig} - /> - ); - } - - return null; -}; diff --git a/packages/react/src/wallet/wallets/coreWallet/coreWallet.tsx b/packages/react/src/wallet/wallets/coreWallet/coreWallet.tsx index 514eeb914aa..0bf88b92a3b 100644 --- a/packages/react/src/wallet/wallets/coreWallet/coreWallet.tsx +++ b/packages/react/src/wallet/wallets/coreWallet/coreWallet.tsx @@ -1,9 +1,21 @@ -import type { WalletOptions, WalletConfig } from "@thirdweb-dev/react-core"; +import type { + WalletOptions, + WalletConfig, + ConnectUIProps, +} from "@thirdweb-dev/react-core"; import { CoreWallet, getInjectedCoreWalletProvider, } from "@thirdweb-dev/wallets"; -import { CoreWalletConnectUI } from "./CoreWalletConnectUI"; +import { handelWCSessionRequest } from "../handleWCSessionRequest"; +import { useTWLocale } from "../../../evm/providers/locale-provider"; +import { ExtensionOrWCConnectionUI } from "../_common/ExtensionORWCConnectionUI"; + +const coreWalletUris = { + ios: "core://", + android: "core://", + other: "core://", +}; export type CoreWalletConfigOptions = { /** @@ -82,11 +94,34 @@ export const coreWallet = ( qrcode: false, }); + handelWCSessionRequest(wallet, coreWalletUris); + return wallet; }, - connectUI: CoreWalletConnectUI, - isInstalled() { - return !!getInjectedCoreWalletProvider(); - }, + connectUI: ConnectUI, + isInstalled: isCoreWalletInstalled, }; }; + +function isCoreWalletInstalled() { + return !!getInjectedCoreWalletProvider(); +} + +function ConnectUI(props: ConnectUIProps) { + const locale = useTWLocale(); + return ( + props.setConnectedWallet(w as CoreWallet)} + setConnectionStatus={props.setConnectionStatus} + supportedWallets={props.supportedWallets} + walletConnectUris={coreWalletUris} + walletLocale={locale.wallets.coreWallet} + isInstalled={isCoreWalletInstalled} + /> + ); +} diff --git a/packages/react/src/wallet/wallets/coreWallet/coreWalletUris.ts b/packages/react/src/wallet/wallets/coreWallet/coreWalletUris.ts deleted file mode 100644 index 6f77fd4eb1d..00000000000 --- a/packages/react/src/wallet/wallets/coreWallet/coreWalletUris.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const coreWalletUris = { - ios: "core://", - android: "core://", - other: "core://", -}; diff --git a/packages/react/src/wallet/wallets/defiWallet/CryptoDefiWalletConnectUI.tsx b/packages/react/src/wallet/wallets/defiWallet/CryptoDefiWalletConnectUI.tsx deleted file mode 100644 index b193d6a4754..00000000000 --- a/packages/react/src/wallet/wallets/defiWallet/CryptoDefiWalletConnectUI.tsx +++ /dev/null @@ -1,150 +0,0 @@ -import { ConnectUIProps, useConnect } from "@thirdweb-dev/react-core"; -import { ConnectingScreen } from "../../ConnectWallet/screens/ConnectingScreen"; -import { isMobile } from "../../../evm/utils/isMobile"; -import { useCallback, useEffect, useRef, useState } from "react"; -import { CryptoDefiWalletScan } from "./CryptoDefiWalletScan"; -import { GetStartedScreen } from "../../ConnectWallet/screens/GetStartedScreen"; -import type { CryptoDefiWallet } from "@thirdweb-dev/wallets"; -import { wait } from "../../../utils/wait"; -import { useTWLocale } from "../../../evm/providers/locale-provider"; -import { WCOpenURI } from "../../ConnectWallet/screens/WCOpenUri"; -import { cryptoDefiWalletUris } from "./cryptoDefiWalletUris"; - -export const CryptoDefiWalletConnectUI = ( - props: ConnectUIProps, -) => { - const [screen, setScreen] = useState< - "connecting" | "scanning" | "get-started" | "open-wc-uri" - >("connecting"); - const locale = useTWLocale().wallets.cryptoDefiWallet; - const connectingLocale = { - getStartedLink: locale.getStartedLink, - instruction: locale.connectionScreen.instruction, - tryAgain: locale.connectionScreen.retry, - inProgress: locale.connectionScreen.inProgress, - failed: locale.connectionScreen.failed, - }; - const { walletConfig, connected } = props; - const connect = useConnect(); - const [errorConnecting, setErrorConnecting] = useState(false); - - const hideBackButton = props.supportedWallets.length === 1; - - const connectToExtension = useCallback(async () => { - try { - connectPrompted.current = true; - setErrorConnecting(false); - setScreen("connecting"); - await wait(1000); - await connect(walletConfig); - connected(); - } catch (e) { - setErrorConnecting(true); - console.error(e); - } - }, [connected, connect, walletConfig]); - - const connectPrompted = useRef(false); - useEffect(() => { - if (connectPrompted.current) { - return; - } - - const isInstalled = walletConfig.isInstalled - ? walletConfig.isInstalled() - : false; - - // if loading - (async () => { - if (isInstalled) { - connectToExtension(); - } - - // if wallet is not injected - else { - // on mobile, open the Defi Mobile via wallet connect - if (isMobile()) { - setScreen("open-wc-uri"); - } else { - // on desktop, show the Defi app scan qr code - setScreen("scanning"); - } - } - })(); - }, [connectToExtension, walletConfig]); - - if (screen === "connecting") { - return ( - { - setScreen("get-started"); - }} - onRetry={connectToExtension} - hideBackButton={hideBackButton} - onBack={props.goBack} - walletName={walletConfig.meta.name} - walletIconURL={walletConfig.meta.iconURL} - /> - ); - } - - if (screen === "open-wc-uri") { - return ( - { - // NOOP - TODO make onRetry optional - }} - errorConnecting={errorConnecting} - onGetStarted={() => { - setScreen("get-started"); - }} - hideBackButton={hideBackButton} - onBack={props.goBack} - onConnected={connected} - walletConfig={walletConfig} - appUriPrefix={cryptoDefiWalletUris} - /> - ); - } - - if (screen === "get-started") { - return ( - - ); - } - - if (screen === "scanning") { - return ( - { - setScreen("get-started"); - }} - hideBackButton={hideBackButton} - walletConfig={walletConfig} - /> - ); - } - - return null; -}; diff --git a/packages/react/src/wallet/wallets/defiWallet/CryptoDefiWalletScan.tsx b/packages/react/src/wallet/wallets/defiWallet/CryptoDefiWalletScan.tsx deleted file mode 100644 index f5398b45957..00000000000 --- a/packages/react/src/wallet/wallets/defiWallet/CryptoDefiWalletScan.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import { ScanScreen } from "../../ConnectWallet/screens/ScanScreen"; -import { - useCreateWalletInstance, - useWalletContext, -} from "@thirdweb-dev/react-core"; -import { useEffect, useRef, useState } from "react"; -import type { CryptoDefiWallet } from "@thirdweb-dev/wallets"; -import type { WalletConfig } from "@thirdweb-dev/react-core"; -import { useTWLocale } from "../../../evm/providers/locale-provider"; - -export const CryptoDefiWalletScan: React.FC<{ - onBack: () => void; - onGetStarted: () => void; - onConnected: () => void; - walletConfig: WalletConfig; - hideBackButton: boolean; -}> = ({ onBack, onConnected, onGetStarted, walletConfig, hideBackButton }) => { - const locale = useTWLocale().wallets.cryptoDefiWallet; - const createInstance = useCreateWalletInstance(); - const [qrCodeUri, setQrCodeUri] = useState(); - const { setConnectedWallet, chainToConnect, setConnectionStatus } = - useWalletContext(); - - const scanStarted = useRef(false); - useEffect(() => { - if (scanStarted.current) { - return; - } - scanStarted.current = true; - - const wallet = createInstance(walletConfig); - - setConnectionStatus("connecting"); - wallet.connectWithQrCode({ - chainId: chainToConnect?.chainId, - onQrCodeUri(uri) { - setQrCodeUri(uri); - }, - onConnected() { - setConnectedWallet(wallet); - onConnected(); - }, - }); - }, [ - createInstance, - setConnectedWallet, - chainToConnect, - onConnected, - walletConfig, - setConnectionStatus, - ]); - - return ( - - ); -}; diff --git a/packages/react/src/wallet/wallets/defiWallet/cryptoDefiWallet.tsx b/packages/react/src/wallet/wallets/defiWallet/cryptoDefiWallet.tsx index 7da2b5e7a55..0b7e6eade63 100644 --- a/packages/react/src/wallet/wallets/defiWallet/cryptoDefiWallet.tsx +++ b/packages/react/src/wallet/wallets/defiWallet/cryptoDefiWallet.tsx @@ -1,9 +1,21 @@ -import type { WalletOptions, WalletConfig } from "@thirdweb-dev/react-core"; +import type { + WalletOptions, + WalletConfig, + ConnectUIProps, +} from "@thirdweb-dev/react-core"; import { CryptoDefiWallet, getInjectedCryptoDefiWalletProvider, } from "@thirdweb-dev/wallets"; -import { CryptoDefiWalletConnectUI } from "./CryptoDefiWalletConnectUI"; +import { useTWLocale } from "../../../evm/providers/locale-provider"; +import { ExtensionOrWCConnectionUI } from "../_common/ExtensionORWCConnectionUI"; +import { handelWCSessionRequest } from "../handleWCSessionRequest"; + +const cryptoDefiWalletUris = { + ios: "dfw://", + android: "dfw://", + other: "dfw://", +}; export type CryptoDefiWalletConfigOptions = { /** @@ -83,11 +95,36 @@ export const cryptoDefiWallet = ( qrcode: false, }); + handelWCSessionRequest(wallet, cryptoDefiWalletUris); + return wallet; }, - connectUI: CryptoDefiWalletConnectUI, - isInstalled() { - return !!getInjectedCryptoDefiWalletProvider(); - }, + connectUI: ConnectUI, + isInstalled: isWalletInstalled, }; }; + +function isWalletInstalled() { + return !!getInjectedCryptoDefiWalletProvider(); +} + +function ConnectUI(props: ConnectUIProps) { + const locale = useTWLocale(); + return ( + + props.setConnectedWallet(w as CryptoDefiWallet) + } + setConnectionStatus={props.setConnectionStatus} + supportedWallets={props.supportedWallets} + walletConnectUris={cryptoDefiWalletUris} + walletLocale={locale.wallets.cryptoDefiWallet} + isInstalled={isWalletInstalled} + /> + ); +} diff --git a/packages/react/src/wallet/wallets/defiWallet/cryptoDefiWalletUris.ts b/packages/react/src/wallet/wallets/defiWallet/cryptoDefiWalletUris.ts deleted file mode 100644 index ab7c97609a4..00000000000 --- a/packages/react/src/wallet/wallets/defiWallet/cryptoDefiWalletUris.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const cryptoDefiWalletUris = { - ios: "dfw://", - android: "dfw://", - other: "dfw://", -}; diff --git a/packages/react/src/wallet/wallets/embeddedWallet/EmbeddedWalletFormUI.tsx b/packages/react/src/wallet/wallets/embeddedWallet/EmbeddedWalletFormUI.tsx index 791c3520b8f..3de268edb0e 100644 --- a/packages/react/src/wallet/wallets/embeddedWallet/EmbeddedWalletFormUI.tsx +++ b/packages/react/src/wallet/wallets/embeddedWallet/EmbeddedWalletFormUI.tsx @@ -1,10 +1,5 @@ import styled from "@emotion/styled"; -import { - WalletConfig, - useCreateWalletInstance, - useSetConnectedWallet, - useSetConnectionStatus, -} from "@thirdweb-dev/react-core"; +import { ConnectUIProps, WalletConfig } from "@thirdweb-dev/react-core"; import { EmbeddedWallet, EmbeddedWalletOauthStrategy, @@ -26,13 +21,17 @@ export const EmbeddedWalletFormUI = (props: { onSelect: (loginType: EmbeddedWalletLoginType) => void; walletConfig: WalletConfig; authOptions: AuthOption[]; - modalSize?: "compact" | "wide"; + modalSize: "compact" | "wide"; + createWalletInstance: ConnectUIProps["createWalletInstance"]; + setConnectionStatus: ConnectUIProps["setConnectionStatus"]; + setConnectedWallet: ConnectUIProps["setConnectedWallet"]; }) => { const twLocale = useTWLocale(); const locale = twLocale.wallets.embeddedWallet; - const createWalletInstance = useCreateWalletInstance(); - const setConnectionStatus = useSetConnectionStatus(); - const setConnectedWallet = useSetConnectedWallet(); + + const { createWalletInstance, setConnectionStatus, setConnectedWallet } = + props; + const themeObj = useCustomTheme(); const loginMethodsLabel: Record = { @@ -52,7 +51,7 @@ export const EmbeddedWalletFormUI = (props: { // Need to trigger login on button click to avoid popup from being blocked const socialLogin = async (strategy: EmbeddedWalletOauthStrategy) => { try { - const embeddedWallet = createWalletInstance(props.walletConfig); + const embeddedWallet = createWalletInstance(); setConnectionStatus("connecting"); const socialLoginWindow = openOauthSignInWindow(strategy, themeObj); @@ -150,6 +149,9 @@ export const EmbeddedWalletFormUIScreen: React.FC<{ modalSize: "compact" | "wide"; walletConfig: WalletConfig; authOptions: AuthOption[]; + createWalletInstance: ConnectUIProps["createWalletInstance"]; + setConnectionStatus: ConnectUIProps["setConnectionStatus"]; + setConnectedWallet: ConnectUIProps["setConnectedWallet"]; }> = (props) => { const locale = useTWLocale().wallets.embeddedWallet.emailLoginScreen; const isCompact = props.modalSize === "compact"; @@ -177,6 +179,9 @@ export const EmbeddedWalletFormUIScreen: React.FC<{ authOptions={props.authOptions} walletConfig={props.walletConfig} onSelect={props.onSelect} + createWalletInstance={props.createWalletInstance} + setConnectionStatus={props.setConnectionStatus} + setConnectedWallet={props.setConnectedWallet} /> diff --git a/packages/react/src/wallet/wallets/embeddedWallet/EmbeddedWalletOTPLoginUI.tsx b/packages/react/src/wallet/wallets/embeddedWallet/EmbeddedWalletOTPLoginUI.tsx index 02ab8719aa7..f3b29f53135 100644 --- a/packages/react/src/wallet/wallets/embeddedWallet/EmbeddedWalletOTPLoginUI.tsx +++ b/packages/react/src/wallet/wallets/embeddedWallet/EmbeddedWalletOTPLoginUI.tsx @@ -1,4 +1,4 @@ -import { ConnectUIProps, useWalletContext } from "@thirdweb-dev/react-core"; +import { ConnectUIProps } from "@thirdweb-dev/react-core"; import { EmbeddedWallet, SendEmailOtpReturnType } from "@thirdweb-dev/wallets"; import { useCallback, useEffect, useRef, useState } from "react"; import { FadeIn } from "../../../components/FadeIn"; @@ -33,13 +33,13 @@ export const EmbeddedWalletOTPLoginUI: React.FC< const [otpInput, setOtpInput] = useState(""); const { createWalletInstance, setConnectedWallet, setConnectionStatus } = - useWalletContext(); + props; const [wallet, setWallet] = useState(null); const [verifyStatus, setVerifyStatus] = useState("idle"); const [emailStatus, setEmailStatus] = useState("sending"); - const [screen, setScreen] = useState("base"); // TODO change + const [screen, setScreen] = useState("base"); const sendEmail = useCallback(async () => { setOtpInput(""); @@ -47,7 +47,7 @@ export const EmbeddedWalletOTPLoginUI: React.FC< setEmailStatus("sending"); try { - const _wallet = createWalletInstance(props.walletConfig); + const _wallet = createWalletInstance(); setWallet(_wallet); const status = await _wallet.sendVerificationEmail({ email }); setEmailStatus(status); @@ -56,7 +56,7 @@ export const EmbeddedWalletOTPLoginUI: React.FC< setVerifyStatus("idle"); setEmailStatus("error"); } - }, [createWalletInstance, email, props.walletConfig]); + }, [createWalletInstance, email]); const verify = async (otp: string) => { if (typeof emailStatus !== "object" || otp.length !== 6) { diff --git a/packages/react/src/wallet/wallets/embeddedWallet/EmbeddedWalletSocialLogin.tsx b/packages/react/src/wallet/wallets/embeddedWallet/EmbeddedWalletSocialLogin.tsx index e1c8a14b11c..51e00f97dec 100644 --- a/packages/react/src/wallet/wallets/embeddedWallet/EmbeddedWalletSocialLogin.tsx +++ b/packages/react/src/wallet/wallets/embeddedWallet/EmbeddedWalletSocialLogin.tsx @@ -1,10 +1,4 @@ -import { - ConnectUIProps, - useConnectionStatus, - useCreateWalletInstance, - useSetConnectedWallet, - useSetConnectionStatus, -} from "@thirdweb-dev/react-core"; +import { ConnectUIProps } from "@thirdweb-dev/react-core"; import { EmbeddedWallet, EmbeddedWalletOauthStrategy, @@ -25,16 +19,19 @@ export const EmbeddedWalletSocialLogin = ( }, ) => { const locale = useTWLocale().wallets.embeddedWallet.socialLoginScreen; - const { goBack, modalSize } = props; - const createWalletInstance = useCreateWalletInstance(); - const setConnectionStatus = useSetConnectionStatus(); - const setConnectedWallet = useSetConnectedWallet(); - const connectionStatus = useConnectionStatus(); + const { + goBack, + modalSize, + createWalletInstance, + setConnectionStatus, + setConnectedWallet, + connectionStatus, + } = props; const themeObj = useCustomTheme(); const socialLogin = async () => { try { - const embeddedWallet = createWalletInstance(props.walletConfig); + const embeddedWallet = createWalletInstance(); setConnectionStatus("connecting"); const socialWindow = openOauthSignInWindow(props.strategy, themeObj); if (!socialWindow) { diff --git a/packages/react/src/wallet/wallets/embeddedWallet/embeddedWallet.tsx b/packages/react/src/wallet/wallets/embeddedWallet/embeddedWallet.tsx index 9c209934bce..b7eea86a35a 100644 --- a/packages/react/src/wallet/wallets/embeddedWallet/embeddedWallet.tsx +++ b/packages/react/src/wallet/wallets/embeddedWallet/embeddedWallet.tsx @@ -213,6 +213,10 @@ const EmbeddedWalletSelectionUI: React.FC< onSelect={props.onSelect} walletConfig={props.walletConfig} authOptions={props.authOptions} + createWalletInstance={props.createWalletInstance} + setConnectedWallet={props.setConnectedWallet} + setConnectionStatus={props.setConnectionStatus} + modalSize={props.modalSize} /> ); @@ -266,6 +270,9 @@ const EmbeddedWalletConnectUI = ( walletConfig={props.walletConfig} onBack={props.goBack} authOptions={props.authOptions} + createWalletInstance={props.createWalletInstance} + setConnectedWallet={props.setConnectedWallet} + setConnectionStatus={props.setConnectionStatus} /> ); }; diff --git a/packages/react/src/wallet/wallets/frame/FrameConnectUI.tsx b/packages/react/src/wallet/wallets/frame/FrameConnectUI.tsx index ed8c8036e19..91e3d752936 100644 --- a/packages/react/src/wallet/wallets/frame/FrameConnectUI.tsx +++ b/packages/react/src/wallet/wallets/frame/FrameConnectUI.tsx @@ -1,4 +1,4 @@ -import { ConnectUIProps, useConnect } from "@thirdweb-dev/react-core"; +import { ConnectUIProps } from "@thirdweb-dev/react-core"; import { FrameWallet } from "@thirdweb-dev/wallets"; import { ConnectingScreen } from "../../ConnectWallet/screens/ConnectingScreen"; import { isMobile } from "../../../evm/utils/isMobile"; @@ -64,7 +64,7 @@ export const FrameConnectUI = (props: ConnectUIProps) => { >("connecting"); const locale = useTWLocale().wallets.frameWallet; - const connect = useConnect(); + const { connect } = props; const connectPrompted = useRef(false); const { walletConfig, connected, goBack } = props; const downloadLink = "https://frame.sh"; @@ -83,7 +83,7 @@ export const FrameConnectUI = (props: ConnectUIProps) => { try { connectPrompted.current = true; setScreen("connecting"); - await connect(walletConfig); + await connect(); connected(); } catch (e) { setScreen("connect-failed"); @@ -95,7 +95,7 @@ export const FrameConnectUI = (props: ConnectUIProps) => { openWindow(downloadLink); } })(); - }, [walletConfig, connect, goBack, connected]); + }, [connect, goBack, connected]); if (screen === "connecting") { return ( diff --git a/packages/react/src/wallet/wallets/headlessConnectUI.tsx b/packages/react/src/wallet/wallets/headlessConnectUI.tsx index e3a4979f13e..1124e8fec33 100644 --- a/packages/react/src/wallet/wallets/headlessConnectUI.tsx +++ b/packages/react/src/wallet/wallets/headlessConnectUI.tsx @@ -1,51 +1,84 @@ -import { ConnectUIProps, useConnect } from "@thirdweb-dev/react-core"; -import { useEffect, useRef } from "react"; -import { Container } from "../../components/basic"; +import { ConnectUIProps, WalletInstance } from "@thirdweb-dev/react-core"; +import { useCallback, useEffect, useRef, useState } from "react"; +import { Container, ModalHeader } from "../../components/basic"; import { Spinner } from "../../components/Spinner"; +import { Text } from "../../components/text"; +import { Button } from "../../components/buttons"; +import { Spacer } from "../../components/Spacer"; +import { wait } from "../../utils/wait"; + +export const HeadlessConnectUI = ( + props: ConnectUIProps, +) => { + const { + connected, + hide, + show, + connect, + setConnectionStatus, + supportedWallets, + } = props; -export const HeadlessConnectUI = ({ - connected, - walletConfig, - hide, - show, - supportedWallets, - goBack, -}: ConnectUIProps) => { - const connect = useConnect(); const prompted = useRef(false); - const singleWallet = supportedWallets.length === 1; + const [connectionFailed, setConnectionFailed] = useState(false); + const showBack = supportedWallets.length > 1; + + const handleConnect = useCallback(async () => { + setConnectionFailed(false); + try { + setConnectionStatus("connecting"); + await wait(1000); + hide(); + await connect(); + connected(); + } catch (e) { + setConnectionStatus("disconnected"); + setConnectionFailed(true); + show(); + console.error(e); + } + }, [connect, connected, hide, show, setConnectionStatus]); useEffect(() => { if (prompted.current) { return; } prompted.current = true; + handleConnect(); + }, [handleConnect]); - (async () => { - hide(); - try { - await connect(walletConfig); - connected(); - } catch (e) { - if (!singleWallet) { - goBack(); - show(); - } - console.error(e); - } - })(); - }, [walletConfig, connect, singleWallet, connected, hide, show, goBack]); + let content = null; + + if (connectionFailed) { + content = ( + <> + {"Failed to connect"} + + + + ); + } else { + content = ; + } return ( - - + + + + {content} + ); }; diff --git a/packages/react/src/wallet/wallets/localWallet/CreateLocalWallet.tsx b/packages/react/src/wallet/wallets/localWallet/CreateLocalWallet.tsx index 675a30075f8..c5daaa014ad 100644 --- a/packages/react/src/wallet/wallets/localWallet/CreateLocalWallet.tsx +++ b/packages/react/src/wallet/wallets/localWallet/CreateLocalWallet.tsx @@ -3,7 +3,7 @@ import { Button } from "../../../components/buttons"; import { FormFieldWithIconButton } from "../../../components/formFields"; import { ModalDescription } from "../../../components/modalElements"; import { EyeClosedIcon, EyeOpenIcon } from "@radix-ui/react-icons"; -import { useWalletContext } from "@thirdweb-dev/react-core"; +import { ConnectUIProps } from "@thirdweb-dev/react-core"; import { useCallback, useEffect, useRef, useState } from "react"; import { useLocalWalletInfo } from "./useLocalWalletInfo"; import { ImportLocalWallet } from "./ImportLocalWallet"; @@ -13,6 +13,7 @@ import { spacing } from "../../../design-system"; import type { LocalWalletConfig } from "./types"; import { wait } from "../../../utils/wait"; import { useTWLocale } from "../../../evm/providers/locale-provider"; +import { LocalWallet } from "@thirdweb-dev/wallets"; export const CreateLocalWallet_Password: React.FC<{ onConnect: () => void; @@ -20,6 +21,8 @@ export const CreateLocalWallet_Password: React.FC<{ localWalletConf: LocalWalletConfig; renderBackButton: boolean; persist: boolean; + setConnectedWallet: ConnectUIProps["setConnectedWallet"]; + setConnectionStatus: ConnectUIProps["setConnectionStatus"]; }> = (props) => { const locale = useTWLocale().wallets.localWallet; const [password, setPassword] = useState(""); @@ -33,7 +36,7 @@ export const CreateLocalWallet_Password: React.FC<{ props.persist, ); - const { setConnectedWallet, setConnectionStatus } = useWalletContext(); + const { setConnectedWallet, setConnectionStatus } = props; const [showImportScreen, setShowImportScreen] = useState(false); const [generatedAddress, setGeneratedAddress] = useState(null); @@ -58,6 +61,8 @@ export const CreateLocalWallet_Password: React.FC<{ setShowImportScreen(false); }} persist={props.persist} + setConnectedWallet={props.setConnectedWallet} + setConnectionStatus={props.setConnectionStatus} /> ); } @@ -202,9 +207,11 @@ export const CreateLocalWallet_Guest: React.FC<{ goBack: () => void; localWallet: LocalWalletConfig; persist: boolean; + setConnectedWallet: ConnectUIProps["setConnectedWallet"]; + setConnectionStatus: ConnectUIProps["setConnectionStatus"]; }> = (props) => { const { localWallet } = useLocalWalletInfo(props.localWallet, props.persist); - const { setConnectedWallet, setConnectionStatus } = useWalletContext(); + const { setConnectedWallet, setConnectionStatus } = props; const { onConnect } = props; const handleConnect = useCallback(async () => { diff --git a/packages/react/src/wallet/wallets/localWallet/ExportLocalWallet.tsx b/packages/react/src/wallet/wallets/localWallet/ExportLocalWallet.tsx index 720170d47e8..9d5a118ac9c 100644 --- a/packages/react/src/wallet/wallets/localWallet/ExportLocalWallet.tsx +++ b/packages/react/src/wallet/wallets/localWallet/ExportLocalWallet.tsx @@ -21,10 +21,9 @@ import { ScreenBottomContainer, } from "../../../components/basic"; import { + WalletInstance, shortenAddress, - useAddress, useCreateWalletInstance, - useWallet, } from "@thirdweb-dev/react-core"; import type { LocalWalletConfig } from "./types"; import { useTWLocale } from "../../../evm/providers/locale-provider"; @@ -36,6 +35,8 @@ export const ExportLocalWallet: React.FC<{ onExport: () => void; localWalletConfig: LocalWalletConfig; modalSize: "wide" | "compact"; + walletInstance?: WalletInstance; + walletAddress?: string; }> = (props) => { const locale = useTWLocale().wallets.localWallet; const [password, setPassword] = useState(""); @@ -43,11 +44,12 @@ export const ExportLocalWallet: React.FC<{ const [isWrongPassword, setIsWrongPassword] = useState(false); const [passwordIsRequired, setPasswordIsRequired] = useState(false); - const wallet = useWallet(); - const address = useAddress(); + const wallet = props.walletInstance; const [savedAddress, setSavedAddress] = useState(""); const createWalletInstance = useCreateWalletInstance(); + const address = props.walletAddress; + // set savedAddress and passwordIsRequired on mount const mounted = useRef(false); useEffect(() => { diff --git a/packages/react/src/wallet/wallets/localWallet/ImportLocalWallet.tsx b/packages/react/src/wallet/wallets/localWallet/ImportLocalWallet.tsx index bb6417dcbfc..3c3860eb85a 100644 --- a/packages/react/src/wallet/wallets/localWallet/ImportLocalWallet.tsx +++ b/packages/react/src/wallet/wallets/localWallet/ImportLocalWallet.tsx @@ -4,8 +4,8 @@ import { FormFieldWithIconButton } from "../../../components/formFields"; import { ModalDescription } from "../../../components/modalElements"; import { EyeClosedIcon, EyeOpenIcon } from "@radix-ui/react-icons"; import { + ConnectUIProps, useCreateWalletInstance, - useWalletContext, } from "@thirdweb-dev/react-core"; import { useState } from "react"; import { DragNDrop } from "../../../components/DragNDrop"; @@ -20,6 +20,8 @@ export const ImportLocalWallet: React.FC<{ goBack: () => void; localWalletConf: LocalWalletConfig; persist: boolean; + setConnectedWallet: ConnectUIProps["setConnectedWallet"]; + setConnectionStatus: ConnectUIProps["setConnectionStatus"]; }> = (props) => { const locale = useTWLocale().wallets.localWallet; const [jsonString, setJsonString] = useState(); @@ -33,7 +35,7 @@ export const ImportLocalWallet: React.FC<{ const [showPassword, setShowPassword] = useState(false); const [importedAddress, setImportedAddress] = useState(); - const { setConnectedWallet, setConnectionStatus } = useWalletContext(); + const { setConnectedWallet, setConnectionStatus } = props; const handleImport = async () => { const localWallet = createWalletInstance( diff --git a/packages/react/src/wallet/wallets/localWallet/LocalWalletConnectUI.tsx b/packages/react/src/wallet/wallets/localWallet/LocalWalletConnectUI.tsx index 8dee03bd232..ccea4acc3f4 100644 --- a/packages/react/src/wallet/wallets/localWallet/LocalWalletConnectUI.tsx +++ b/packages/react/src/wallet/wallets/localWallet/LocalWalletConnectUI.tsx @@ -21,6 +21,8 @@ export const LocalWalletConnectUI = ( localWallet={props.walletConfig} goBack={props.goBack} onConnect={props.connected} + setConnectedWallet={props.setConnectedWallet} + setConnectionStatus={props.setConnectionStatus} /> ); } @@ -49,6 +51,10 @@ export const LocalWalletConnectUI = ( goBack={props.goBack} localWallet={props.walletConfig} persist={props.persist} + walletInstance={props.connectedWallet} + connectedWalletAddress={props.connectedWalletAddress} + setConnectedWallet={props.setConnectedWallet} + setConnectionStatus={props.setConnectionStatus} /> ); } @@ -60,6 +66,8 @@ export const LocalWalletConnectUI = ( onConnect={props.connected} renderBackButton={props.supportedWallets.length > 1} persist={props.persist} + setConnectedWallet={props.setConnectedWallet} + setConnectionStatus={props.setConnectionStatus} /> ); }; diff --git a/packages/react/src/wallet/wallets/localWallet/ReconnectLocalWallet.tsx b/packages/react/src/wallet/wallets/localWallet/ReconnectLocalWallet.tsx index 0f07fd60a9a..8d22f254356 100644 --- a/packages/react/src/wallet/wallets/localWallet/ReconnectLocalWallet.tsx +++ b/packages/react/src/wallet/wallets/localWallet/ReconnectLocalWallet.tsx @@ -3,9 +3,10 @@ import { Button } from "../../../components/buttons"; import { FormFieldWithIconButton } from "../../../components/formFields"; import { EyeClosedIcon, EyeOpenIcon } from "@radix-ui/react-icons"; import { + ConnectUIProps, WalletConfig, + WalletInstance, shortenAddress, - useWalletContext, } from "@thirdweb-dev/react-core"; import { useState } from "react"; import { Label } from "../../../components/formElements"; @@ -19,6 +20,7 @@ import { useLocalWalletInfo } from "./useLocalWalletInfo"; import type { LocalWalletConfig } from "./types"; import { Container, Line, ModalHeader } from "../../../components/basic"; import { useTWLocale } from "../../../evm/providers/locale-provider"; +import { LocalWallet } from "@thirdweb-dev/wallets"; type ReconnectLocalWalletProps = { onConnect: () => void; @@ -28,6 +30,10 @@ type ReconnectLocalWalletProps = { renderBackButton: boolean; persist: boolean; modalSize: "wide" | "compact"; + walletInstance?: WalletInstance; + setConnectedWallet: ConnectUIProps["setConnectedWallet"]; + setConnectionStatus: ConnectUIProps["setConnectionStatus"]; + connectedWalletAddress: ConnectUIProps["connectedWalletAddress"]; }; /** @@ -40,7 +46,7 @@ export const ReconnectLocalWallet: React.FC = ( const [password, setPassword] = useState(""); const [showPassword, setShowPassword] = useState(false); const [isWrongPassword, setIsWrongPassword] = useState(false); - const { setConnectedWallet, setConnectionStatus } = useWalletContext(); + const { setConnectedWallet, setConnectionStatus } = props; const [isConnecting, setIsConnecting] = useState(false); const [showCreate, setShowCreate] = useState(false); const [showBackupConfirmation, setShowBackupConfirmation] = useState(false); @@ -73,6 +79,8 @@ export const ReconnectLocalWallet: React.FC = ( setShowBackupConfirmation(false); setShowCreate(true); }} + walletInstance={props.walletInstance} + walletAddress={props.connectedWalletAddress} /> ); } @@ -87,6 +95,7 @@ export const ReconnectLocalWallet: React.FC = ( onBack={() => { setShowBackupConfirmation(false); }} + modalSize={props.modalSize} /> ); } @@ -101,6 +110,8 @@ export const ReconnectLocalWallet: React.FC = ( }} onConnect={props.onConnect} persist={props.persist} + setConnectedWallet={props.setConnectedWallet} + setConnectionStatus={props.setConnectionStatus} /> ); } diff --git a/packages/react/src/wallet/wallets/localWallet/overrideConfirmation.tsx b/packages/react/src/wallet/wallets/localWallet/overrideConfirmation.tsx index 301760fa2e7..aeeda2665e4 100644 --- a/packages/react/src/wallet/wallets/localWallet/overrideConfirmation.tsx +++ b/packages/react/src/wallet/wallets/localWallet/overrideConfirmation.tsx @@ -3,8 +3,6 @@ import { Text } from "../../../components/text"; import { Spacer } from "../../../components/Spacer"; import { WalletConfig } from "@thirdweb-dev/react-core"; import { Container, ModalHeader } from "../../../components/basic"; -import { useContext } from "react"; -import { ModalConfigCtx } from "../../../evm/providers/wallet-ui-states-provider"; import { ExclamationTriangleIcon } from "@radix-ui/react-icons"; import { iconSize } from "../../../design-system"; import { useTWLocale } from "../../../evm/providers/locale-provider"; @@ -13,9 +11,10 @@ export const OverrideConfirmation: React.FC<{ onBackup: () => void; onBack: () => void; meta: WalletConfig["meta"]; + modalSize: "wide" | "compact"; }> = (props) => { const locale = useTWLocale().wallets.localWallet.warningScreen; - const isCompact = useContext(ModalConfigCtx).modalSize === "compact"; + const isCompact = props.modalSize === "compact"; return ( diff --git a/packages/react/src/wallet/wallets/magic/magicLink.tsx b/packages/react/src/wallet/wallets/magic/magicLink.tsx index bbb2f6955de..f1d17238155 100644 --- a/packages/react/src/wallet/wallets/magic/magicLink.tsx +++ b/packages/react/src/wallet/wallets/magic/magicLink.tsx @@ -2,9 +2,7 @@ import { MagicLink, MagicLinkAdditionalOptions } from "@thirdweb-dev/wallets"; import { ConnectUIProps, SelectUIProps, - WalletConfig, WalletOptions, - useConnect, useWalletContext, } from "@thirdweb-dev/react-core"; import type { ConfiguredMagicLinkWallet } from "./types"; @@ -372,27 +370,26 @@ const MagicUI: React.FC<{ }; function useConnectMagic() { - const connect = useConnect(); const { activeChain } = useWalletContext(); const connector = useCallback( async (data: { selectionData: SelectionData; - walletConfig: WalletConfig; + connect: ConnectUIProps["connect"]; singleWallet: boolean; type: "auth" | "connect"; show: () => void; connected: () => void; hide: () => void; }) => { - const { selectionData, walletConfig, connected, show, hide } = data; + const { selectionData, connected, show, hide, connect } = data; // oauth if (typeof selectionData === "object") { try { hide(); (async () => { - await connect(walletConfig, { + await connect({ oauthProvider: selectionData.provider, chainId: activeChain.chainId, }); @@ -414,7 +411,6 @@ function useConnectMagic() { hide(); try { await connect( - walletConfig, data.type === "connect" ? {} : isEmail @@ -428,7 +424,7 @@ function useConnectMagic() { show(); } }, - [connect, activeChain.chainId], + [activeChain.chainId], ); return connector; @@ -444,6 +440,7 @@ const MagicConnectingUI: React.FC< supportedWallets, type, hide, + connect, }) => { const connectPrompted = useRef(false); const singleWallet = supportedWallets.length === 1; @@ -459,7 +456,7 @@ const MagicConnectingUI: React.FC< selectionData: selectionData as SelectionData, singleWallet, type, - walletConfig, + connect, show, connected, hide, @@ -473,6 +470,7 @@ const MagicConnectingUI: React.FC< type, walletConfig, hide, + connect, ]); return ( @@ -525,7 +523,7 @@ const MagicConnectionUIScreen: React.FC< show: props.show, singleWallet: props.supportedWallets.length === 1, type: props.type, - walletConfig: props.walletConfig, + connect: props.connect, hide: props.hide, }); }} diff --git a/packages/react/src/wallet/wallets/metamask/MetamaskConnectUI.tsx b/packages/react/src/wallet/wallets/metamask/MetamaskConnectUI.tsx index 703985941ca..999b00c77d4 100644 --- a/packages/react/src/wallet/wallets/metamask/MetamaskConnectUI.tsx +++ b/packages/react/src/wallet/wallets/metamask/MetamaskConnectUI.tsx @@ -1,4 +1,4 @@ -import { ConnectUIProps, useConnect } from "@thirdweb-dev/react-core"; +import { ConnectUIProps } from "@thirdweb-dev/react-core"; import { ConnectingScreen } from "../../ConnectWallet/screens/ConnectingScreen"; import { isMobile } from "../../../evm/utils/isMobile"; import { useCallback, useEffect, useRef, useState } from "react"; @@ -13,10 +13,8 @@ export const MetamaskConnectUI = (props: ConnectUIProps) => { "connecting" | "scanning" | "get-started" >("connecting"); const locale = useTWLocale().wallets.metamaskWallet; - const { walletConfig, connected } = props; - const connect = useConnect(); + const { walletConfig, connected, connect } = props; const [errorConnecting, setErrorConnecting] = useState(false); - const hideBackButton = props.supportedWallets.length === 1; const connectToExtension = useCallback(async () => { @@ -25,13 +23,13 @@ export const MetamaskConnectUI = (props: ConnectUIProps) => { setErrorConnecting(false); setScreen("connecting"); await wait(1000); - await connect(walletConfig); + await connect(); connected(); } catch (e) { setErrorConnecting(true); console.error(e); } - }, [connected, connect, walletConfig]); + }, [connected, connect]); const connectPrompted = useRef(false); useEffect(() => { @@ -113,6 +111,9 @@ export const MetamaskConnectUI = (props: ConnectUIProps) => { }} hideBackButton={hideBackButton} walletConfig={walletConfig} + setConnectedWallet={(w: MetaMaskWallet) => props.setConnectedWallet(w)} + setConnectionStatus={props.setConnectionStatus} + createWalletInstance={props.createWalletInstance} /> ); } diff --git a/packages/react/src/wallet/wallets/metamask/MetamaskScan.tsx b/packages/react/src/wallet/wallets/metamask/MetamaskScan.tsx index c279f5b471f..02c794fc584 100644 --- a/packages/react/src/wallet/wallets/metamask/MetamaskScan.tsx +++ b/packages/react/src/wallet/wallets/metamask/MetamaskScan.tsx @@ -1,11 +1,8 @@ import { ScanScreen } from "../../ConnectWallet/screens/ScanScreen"; -import { - useCreateWalletInstance, - useWalletContext, -} from "@thirdweb-dev/react-core"; +import { useWalletContext } from "@thirdweb-dev/react-core"; import { useEffect, useRef, useState } from "react"; import type { MetaMaskWallet } from "@thirdweb-dev/wallets"; -import type { WalletConfig } from "@thirdweb-dev/react-core"; +import type { ConnectUIProps, WalletConfig } from "@thirdweb-dev/react-core"; import { useTWLocale } from "../../../evm/providers/locale-provider"; export const MetamaskScan: React.FC<{ @@ -14,12 +11,25 @@ export const MetamaskScan: React.FC<{ onConnected: () => void; walletConfig: WalletConfig; hideBackButton: boolean; -}> = ({ onBack, onConnected, onGetStarted, walletConfig, hideBackButton }) => { + setConnectedWallet: ConnectUIProps["setConnectedWallet"]; + setConnectionStatus: ConnectUIProps["setConnectionStatus"]; + createWalletInstance: () => MetaMaskWallet; +}> = (props) => { + const { + onBack, + onConnected, + onGetStarted, + walletConfig, + hideBackButton, + setConnectedWallet, + setConnectionStatus, + createWalletInstance, + } = props; const locale = useTWLocale().wallets.metamaskWallet; - const createInstance = useCreateWalletInstance(); + const [qrCodeUri, setQrCodeUri] = useState(); - const { setConnectedWallet, chainToConnect, setConnectionStatus } = - useWalletContext(); + + const { chainToConnect } = useWalletContext(); const scanStarted = useRef(false); useEffect(() => { @@ -28,26 +38,26 @@ export const MetamaskScan: React.FC<{ } scanStarted.current = true; - const metamask = createInstance(walletConfig); + const metamask = createWalletInstance(); - setConnectionStatus("connecting"); metamask.connectWithQrCode({ chainId: chainToConnect?.chainId, onQrCodeUri(uri) { setQrCodeUri(uri); }, onConnected() { + setConnectionStatus("connecting"); setConnectedWallet(metamask); onConnected(); }, }); }, [ - createInstance, setConnectedWallet, chainToConnect, onConnected, walletConfig, setConnectionStatus, + createWalletInstance, ]); return ( diff --git a/packages/react/src/wallet/wallets/okx/OKXConnectUI.tsx b/packages/react/src/wallet/wallets/okx/OKXConnectUI.tsx index b30ec99d25d..4cc0fefc571 100644 --- a/packages/react/src/wallet/wallets/okx/OKXConnectUI.tsx +++ b/packages/react/src/wallet/wallets/okx/OKXConnectUI.tsx @@ -1,4 +1,4 @@ -import { ConnectUIProps, useConnect } from "@thirdweb-dev/react-core"; +import { ConnectUIProps } from "@thirdweb-dev/react-core"; import type { OKXWallet } from "@thirdweb-dev/wallets"; import { useCallback, useEffect, useRef, useState } from "react"; import { useTWLocale } from "../../../evm/providers/locale-provider"; @@ -13,8 +13,7 @@ export const OKXConnectUI = (props: ConnectUIProps) => { "connecting" | "scanning" | "get-started" >("connecting"); const locale = useTWLocale().wallets.okxWallet; - const { walletConfig, connected } = props; - const connect = useConnect(); + const { walletConfig, connected, connect } = props; const [errorConnecting, setErrorConnecting] = useState(false); const hideBackButton = props.supportedWallets.length === 1; @@ -25,13 +24,13 @@ export const OKXConnectUI = (props: ConnectUIProps) => { setErrorConnecting(false); setScreen("connecting"); await wait(1000); - await connect(walletConfig); + await connect(); connected(); } catch (e) { setErrorConnecting(true); console.error(e); } - }, [connected, connect, walletConfig]); + }, [connected, connect]); const connectPrompted = useRef(false); useEffect(() => { @@ -113,6 +112,9 @@ export const OKXConnectUI = (props: ConnectUIProps) => { }} hideBackButton={hideBackButton} walletConfig={walletConfig} + createWalletInstance={props.createWalletInstance} + setConnectedWallet={props.setConnectedWallet} + setConnectionStatus={props.setConnectionStatus} /> ); } diff --git a/packages/react/src/wallet/wallets/okx/OKXScan.tsx b/packages/react/src/wallet/wallets/okx/OKXScan.tsx index 28eb3949d12..bb7a073c4a2 100644 --- a/packages/react/src/wallet/wallets/okx/OKXScan.tsx +++ b/packages/react/src/wallet/wallets/okx/OKXScan.tsx @@ -1,11 +1,8 @@ import { ScanScreen } from "../../ConnectWallet/screens/ScanScreen"; -import { - useCreateWalletInstance, - useWalletContext, -} from "@thirdweb-dev/react-core"; +import { useWalletContext } from "@thirdweb-dev/react-core"; import { useEffect, useRef, useState } from "react"; import type { OKXWallet } from "@thirdweb-dev/wallets"; -import type { WalletConfig } from "@thirdweb-dev/react-core"; +import type { ConnectUIProps, WalletConfig } from "@thirdweb-dev/react-core"; import { useTWLocale } from "../../../evm/providers/locale-provider"; export const OKXScan: React.FC<{ @@ -14,12 +11,23 @@ export const OKXScan: React.FC<{ onConnected: () => void; walletConfig: WalletConfig; hideBackButton: boolean; -}> = ({ onBack, onConnected, onGetStarted, walletConfig, hideBackButton }) => { + createWalletInstance: () => OKXWallet; + setConnectedWallet: ConnectUIProps["setConnectedWallet"]; + setConnectionStatus: ConnectUIProps["setConnectionStatus"]; +}> = (props) => { + const { + onBack, + onConnected, + onGetStarted, + walletConfig, + hideBackButton, + setConnectedWallet, + setConnectionStatus, + createWalletInstance, + } = props; const locale = useTWLocale().wallets.okxWallet; - const createInstance = useCreateWalletInstance(); const [qrCodeUri, setQrCodeUri] = useState(); - const { setConnectedWallet, chainToConnect, setConnectionStatus } = - useWalletContext(); + const { chainToConnect } = useWalletContext(); const scanStarted = useRef(false); useEffect(() => { @@ -28,7 +36,7 @@ export const OKXScan: React.FC<{ } scanStarted.current = true; - const wallet = createInstance(walletConfig); + const wallet = createWalletInstance(); setConnectionStatus("connecting"); wallet.connectWithQrCode({ @@ -42,7 +50,7 @@ export const OKXScan: React.FC<{ }, }); }, [ - createInstance, + createWalletInstance, setConnectedWallet, chainToConnect, onConnected, diff --git a/packages/react/src/wallet/wallets/oneKey/OneKeyConnectUI.tsx b/packages/react/src/wallet/wallets/oneKey/OneKeyConnectUI.tsx deleted file mode 100644 index 4bfa639bf8f..00000000000 --- a/packages/react/src/wallet/wallets/oneKey/OneKeyConnectUI.tsx +++ /dev/null @@ -1,148 +0,0 @@ -import { ConnectUIProps, useConnect } from "@thirdweb-dev/react-core"; -import { ConnectingScreen } from "../../ConnectWallet/screens/ConnectingScreen"; -import { isMobile } from "../../../evm/utils/isMobile"; -import { useCallback, useEffect, useRef, useState } from "react"; -import { OneKeyScan } from "./OneKeyScan"; -import { GetStartedScreen } from "../../ConnectWallet/screens/GetStartedScreen"; -import type { OneKeyWallet } from "@thirdweb-dev/wallets"; -import { wait } from "../../../utils/wait"; -import { useTWLocale } from "../../../evm/providers/locale-provider"; -import { WCOpenURI } from "../../ConnectWallet/screens/WCOpenUri"; -import { oneKeyWalletUris } from "./oneKeyWalletUris"; - -export const OneKeyConnectUI = (props: ConnectUIProps) => { - const [screen, setScreen] = useState< - "connecting" | "scanning" | "get-started" | "open-wc-uri" - >("connecting"); - const locale = useTWLocale().wallets.oneKeyWallet; - const connectingLocale = { - getStartedLink: locale.getStartedLink, - instruction: locale.connectionScreen.instruction, - tryAgain: locale.connectionScreen.retry, - inProgress: locale.connectionScreen.inProgress, - failed: locale.connectionScreen.failed, - }; - const { walletConfig, connected } = props; - const connect = useConnect(); - const [errorConnecting, setErrorConnecting] = useState(false); - - const hideBackButton = props.supportedWallets.length === 1; - - const connectToExtension = useCallback(async () => { - try { - connectPrompted.current = true; - setErrorConnecting(false); - setScreen("connecting"); - await wait(1000); - await connect(walletConfig); - connected(); - } catch (e) { - setErrorConnecting(true); - console.error(e); - } - }, [connected, connect, walletConfig]); - - const connectPrompted = useRef(false); - useEffect(() => { - if (connectPrompted.current) { - return; - } - - const isInstalled = walletConfig.isInstalled - ? walletConfig.isInstalled() - : false; - - // if loading - (async () => { - if (isInstalled) { - connectToExtension(); - } - - // if wallet is not injected - else { - // on mobile, open the OneKey Mobile via wallet connect - if (isMobile()) { - setScreen("open-wc-uri"); - } else { - // on desktop, show the OneKey app scan qr code - setScreen("scanning"); - } - } - })(); - }, [connectToExtension, walletConfig]); - - if (screen === "connecting") { - return ( - { - setScreen("get-started"); - }} - onRetry={connectToExtension} - hideBackButton={hideBackButton} - onBack={props.goBack} - walletName={walletConfig.meta.name} - walletIconURL={walletConfig.meta.iconURL} - /> - ); - } - - if (screen === "open-wc-uri") { - return ( - { - // NOOP - TODO make onRetry optional - }} - errorConnecting={errorConnecting} - onGetStarted={() => { - setScreen("get-started"); - }} - hideBackButton={hideBackButton} - onBack={props.goBack} - onConnected={connected} - walletConfig={walletConfig} - appUriPrefix={oneKeyWalletUris} - /> - ); - } - - if (screen === "get-started") { - return ( - - ); - } - - if (screen === "scanning") { - return ( - { - setScreen("get-started"); - }} - hideBackButton={hideBackButton} - walletConfig={walletConfig} - /> - ); - } - - return null; -}; diff --git a/packages/react/src/wallet/wallets/oneKey/OneKeyScan.tsx b/packages/react/src/wallet/wallets/oneKey/OneKeyScan.tsx deleted file mode 100644 index 38211df8bbd..00000000000 --- a/packages/react/src/wallet/wallets/oneKey/OneKeyScan.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import { ScanScreen } from "../../ConnectWallet/screens/ScanScreen"; -import { - useCreateWalletInstance, - useWalletContext, -} from "@thirdweb-dev/react-core"; -import { useEffect, useRef, useState } from "react"; -import type { OneKeyWallet } from "@thirdweb-dev/wallets"; -import type { WalletConfig } from "@thirdweb-dev/react-core"; -import { useTWLocale } from "../../../evm/providers/locale-provider"; - -export const OneKeyScan: React.FC<{ - onBack: () => void; - onGetStarted: () => void; - onConnected: () => void; - walletConfig: WalletConfig; - hideBackButton: boolean; -}> = ({ onBack, onConnected, onGetStarted, walletConfig, hideBackButton }) => { - const locale = useTWLocale().wallets.oneKeyWallet; - const createInstance = useCreateWalletInstance(); - const [qrCodeUri, setQrCodeUri] = useState(); - const { setConnectedWallet, chainToConnect, setConnectionStatus } = - useWalletContext(); - - const scanStarted = useRef(false); - useEffect(() => { - if (scanStarted.current) { - return; - } - scanStarted.current = true; - - const wallet = createInstance(walletConfig); - - setConnectionStatus("connecting"); - wallet.connectWithQrCode({ - chainId: chainToConnect?.chainId, - onQrCodeUri(uri) { - setQrCodeUri(uri); - }, - onConnected() { - setConnectedWallet(wallet); - onConnected(); - }, - }); - }, [ - createInstance, - setConnectedWallet, - chainToConnect, - onConnected, - walletConfig, - setConnectionStatus, - ]); - - return ( - - ); -}; diff --git a/packages/react/src/wallet/wallets/oneKey/oneKeyWallet.tsx b/packages/react/src/wallet/wallets/oneKey/oneKeyWallet.tsx index 05b8625d9a7..237c5163b49 100644 --- a/packages/react/src/wallet/wallets/oneKey/oneKeyWallet.tsx +++ b/packages/react/src/wallet/wallets/oneKey/oneKeyWallet.tsx @@ -1,6 +1,17 @@ -import type { WalletOptions, WalletConfig } from "@thirdweb-dev/react-core"; +import type { + WalletOptions, + WalletConfig, + ConnectUIProps, +} from "@thirdweb-dev/react-core"; import { OneKeyWallet, getInjectedOneKeyProvider } from "@thirdweb-dev/wallets"; -import { OneKeyConnectUI } from "./OneKeyConnectUI"; +import { useTWLocale } from "../../../evm/providers/locale-provider"; +import { ExtensionOrWCConnectionUI } from "../_common/ExtensionORWCConnectionUI"; + +export const oneKeyWalletUris = { + ios: "onekey-wallet://", + android: "onekey-wallet://", + other: "onekey-wallet://", +}; export type OneKeyWalletConfigOptions = { /** @@ -82,9 +93,30 @@ export const oneKeyWallet = ( return wallet; }, - connectUI: OneKeyConnectUI, - isInstalled() { - return !!getInjectedOneKeyProvider(); - }, + connectUI: ConnectUI, + isInstalled: isInstalled, }; }; + +function isInstalled() { + return !!getInjectedOneKeyProvider(); +} + +function ConnectUI(props: ConnectUIProps) { + const locale = useTWLocale(); + return ( + props.setConnectedWallet(w as OneKeyWallet)} + setConnectionStatus={props.setConnectionStatus} + supportedWallets={props.supportedWallets} + walletConnectUris={oneKeyWalletUris} + walletLocale={locale.wallets.oneKeyWallet} + isInstalled={isInstalled} + /> + ); +} diff --git a/packages/react/src/wallet/wallets/oneKey/oneKeyWalletUris.ts b/packages/react/src/wallet/wallets/oneKey/oneKeyWalletUris.ts deleted file mode 100644 index 441e1bfebfe..00000000000 --- a/packages/react/src/wallet/wallets/oneKey/oneKeyWalletUris.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const oneKeyWalletUris = { - ios: "onekey-wallet://", - android: "onekey-wallet://", - other: "onekey-wallet://", -}; diff --git a/packages/react/src/wallet/wallets/paper/PaperFormUI.tsx b/packages/react/src/wallet/wallets/paper/PaperFormUI.tsx index f1f8468b248..8416086c0d8 100644 --- a/packages/react/src/wallet/wallets/paper/PaperFormUI.tsx +++ b/packages/react/src/wallet/wallets/paper/PaperFormUI.tsx @@ -1,10 +1,5 @@ import styled from "@emotion/styled"; -import { - WalletConfig, - useCreateWalletInstance, - useSetConnectedWallet, - useSetConnectionStatus, -} from "@thirdweb-dev/react-core"; +import { ConnectUIProps, WalletConfig } from "@thirdweb-dev/react-core"; import { PaperWallet } from "@thirdweb-dev/wallets"; import { Spacer } from "../../../components/Spacer"; import { TextDivider } from "../../../components/TextDivider"; @@ -23,18 +18,21 @@ export const PaperFormUI = (props: { onSelect: (loginType: PaperLoginType) => void; googleLoginSupported: boolean; walletConfig: WalletConfig; + setConnectionStatus: ConnectUIProps["setConnectionStatus"]; + setConnectedWallet: ConnectUIProps["setConnectedWallet"]; + createWalletInstance: ConnectUIProps["createWalletInstance"]; }) => { const cwLocale = useTWLocale().connectWallet; const locale = useTWLocale().wallets.paperWallet; - const createWalletInstance = useCreateWalletInstance(); - const setConnectionStatus = useSetConnectionStatus(); - const setConnectedWallet = useSetConnectedWallet(); + const { createWalletInstance, setConnectionStatus, setConnectedWallet } = + props; + const themeObj = useCustomTheme(); // Need to trigger google login on button click to avoid popup from being blocked const googleLogin = async () => { try { - const paperWallet = createWalletInstance(props.walletConfig); + const paperWallet = createWalletInstance(); setConnectionStatus("connecting"); const googleWindow = openOauthSignInWindow("google", themeObj); if (!googleWindow) { @@ -101,6 +99,9 @@ export const PaperFormUIScreen: React.FC<{ modalSize: "compact" | "wide"; googleLoginSupported: boolean; walletConfig: WalletConfig; + setConnectionStatus: ConnectUIProps["setConnectionStatus"]; + setConnectedWallet: ConnectUIProps["setConnectedWallet"]; + createWalletInstance: ConnectUIProps["createWalletInstance"]; }> = (props) => { const isCompact = props.modalSize === "compact"; const locale = useTWLocale().wallets.paperWallet; @@ -127,6 +128,9 @@ export const PaperFormUIScreen: React.FC<{ walletConfig={props.walletConfig} googleLoginSupported={props.googleLoginSupported} onSelect={props.onSelect} + setConnectionStatus={props.setConnectionStatus} + setConnectedWallet={props.setConnectedWallet} + createWalletInstance={props.createWalletInstance} /> diff --git a/packages/react/src/wallet/wallets/paper/PaperGoogleLogin.tsx b/packages/react/src/wallet/wallets/paper/PaperGoogleLogin.tsx index fb2c95898f2..8184a45f4b6 100644 --- a/packages/react/src/wallet/wallets/paper/PaperGoogleLogin.tsx +++ b/packages/react/src/wallet/wallets/paper/PaperGoogleLogin.tsx @@ -1,10 +1,4 @@ -import { - ConnectUIProps, - useConnectionStatus, - useCreateWalletInstance, - useSetConnectedWallet, - useSetConnectionStatus, -} from "@thirdweb-dev/react-core"; +import { ConnectUIProps } from "@thirdweb-dev/react-core"; import { PaperWallet } from "@thirdweb-dev/wallets"; import { useEffect } from "react"; import { Spacer } from "../../../components/Spacer"; @@ -21,19 +15,23 @@ import { googleIconUri } from "../../ConnectWallet/icons/socialLogins"; import { useCustomTheme } from "../../../design-system/CustomThemeProvider"; export const PaperGoogleLogin = (props: ConnectUIProps) => { - const { goBack, modalSize, connected } = props; + const { + goBack, + modalSize, + connected, + createWalletInstance, + setConnectionStatus, + setConnectedWallet, + connectionStatus, + } = props; const locale = useTWLocale().wallets.paperWallet.googleLoginScreen; - const createWalletInstance = useCreateWalletInstance(); - const setConnectionStatus = useSetConnectionStatus(); - const setConnectedWallet = useSetConnectedWallet(); - const connectionStatus = useConnectionStatus(); const themeObj = useCustomTheme(); // Need to trigger google login on button click to avoid popup from being blocked const googleLogin = async () => { try { - const paperWallet = createWalletInstance(props.walletConfig); + const paperWallet = createWalletInstance(); setConnectionStatus("connecting"); const googleWindow = openOauthSignInWindow("google", themeObj); if (!googleWindow) { diff --git a/packages/react/src/wallet/wallets/paper/PaperOTPLoginUI.tsx b/packages/react/src/wallet/wallets/paper/PaperOTPLoginUI.tsx index 24d27d980f7..31d88f989d6 100644 --- a/packages/react/src/wallet/wallets/paper/PaperOTPLoginUI.tsx +++ b/packages/react/src/wallet/wallets/paper/PaperOTPLoginUI.tsx @@ -1,4 +1,4 @@ -import { ConnectUIProps, useWalletContext } from "@thirdweb-dev/react-core"; +import { ConnectUIProps } from "@thirdweb-dev/react-core"; import { PaperWallet } from "@thirdweb-dev/wallets"; import { useCallback, useEffect, useRef, useState } from "react"; import { OTPInput } from "../../../components/OTPInput"; @@ -30,7 +30,7 @@ export const PaperOTPLoginUI: React.FC = (props) => { const [otpInput, setOtpInput] = useState(""); const [recoveryCode, setRecoveryCode] = useState(""); const { createWalletInstance, setConnectedWallet, setConnectionStatus } = - useWalletContext(); + props; const [wallet, setWallet] = useState(null); const isWideModal = props.modalSize === "wide"; @@ -56,7 +56,7 @@ export const PaperOTPLoginUI: React.FC = (props) => { setSendEmailStatus("sending"); try { - const _wallet = createWalletInstance(props.walletConfig); + const _wallet = createWalletInstance(); setWallet(_wallet); const _paperSDK = await _wallet.getPaperSDK(); @@ -71,7 +71,7 @@ export const PaperOTPLoginUI: React.FC = (props) => { setVerifyStatus("idle"); setSendEmailStatus("error"); } - }, [createWalletInstance, email, props.walletConfig]); + }, [createWalletInstance, email]); const handleSubmit = (otp: string) => { if (recoveryCodeRequired && !recoveryCode) { diff --git a/packages/react/src/wallet/wallets/paper/paperWallet.tsx b/packages/react/src/wallet/wallets/paper/paperWallet.tsx index 38f6ab85e7c..a29f0aaf1b5 100644 --- a/packages/react/src/wallet/wallets/paper/paperWallet.tsx +++ b/packages/react/src/wallet/wallets/paper/paperWallet.tsx @@ -124,6 +124,9 @@ const PaperSelectionUI: React.FC< !!props.providers?.includes("google") } onSelect={props.onSelect} + createWalletInstance={props.createWalletInstance} + setConnectedWallet={props.setConnectedWallet} + setConnectionStatus={props.setConnectionStatus} /> ); @@ -181,6 +184,9 @@ const PaperConnectUI = ( setLoginType(_loginType); }} onBack={props.goBack} + createWalletInstance={props.createWalletInstance} + setConnectedWallet={props.setConnectedWallet} + setConnectionStatus={props.setConnectionStatus} /> ); }; diff --git a/packages/react/src/wallet/wallets/phantom/PhantomConnectUI.tsx b/packages/react/src/wallet/wallets/phantom/PhantomConnectUI.tsx index cbda2ba7117..4ad06a2e765 100644 --- a/packages/react/src/wallet/wallets/phantom/PhantomConnectUI.tsx +++ b/packages/react/src/wallet/wallets/phantom/PhantomConnectUI.tsx @@ -1,4 +1,4 @@ -import { ConnectUIProps, useConnect } from "@thirdweb-dev/react-core"; +import { ConnectUIProps } from "@thirdweb-dev/react-core"; import { ConnectingScreen } from "../../ConnectWallet/screens/ConnectingScreen"; import { useCallback, useEffect, useRef, useState } from "react"; import { GetStartedScreen } from "../../ConnectWallet/screens/GetStartedScreen"; @@ -13,7 +13,7 @@ export const PhantomConnectUI = (props: ConnectUIProps) => { const locale = useTWLocale().wallets.phantomWallet; const { walletConfig, connected } = props; const [errorConnecting, setErrorConnecting] = useState(false); - const connect = useConnect(); + const { connect } = props; const hideBackButton = props.supportedWallets.length === 1; const { goBack } = props; @@ -24,13 +24,13 @@ export const PhantomConnectUI = (props: ConnectUIProps) => { setScreen("connecting"); setErrorConnecting(false); await wait(1000); - await connect(walletConfig); + await connect(); connected(); } catch (e) { setErrorConnecting(true); console.error(e); } - }, [walletConfig, connected, connect]); + }, [connected, connect]); const connectPrompted = useRef(false); useEffect(() => { diff --git a/packages/react/src/wallet/wallets/rabby/RabbyConnectUI.tsx b/packages/react/src/wallet/wallets/rabby/RabbyConnectUI.tsx index 46b65bcb050..e0eabc6f263 100644 --- a/packages/react/src/wallet/wallets/rabby/RabbyConnectUI.tsx +++ b/packages/react/src/wallet/wallets/rabby/RabbyConnectUI.tsx @@ -1,4 +1,4 @@ -import { ConnectUIProps, useConnect } from "@thirdweb-dev/react-core"; +import { ConnectUIProps } from "@thirdweb-dev/react-core"; import { ConnectingScreen } from "../../ConnectWallet/screens/ConnectingScreen"; import { useCallback, useEffect, useRef, useState } from "react"; import { GetStartedScreen } from "../../ConnectWallet/screens/GetStartedScreen"; @@ -13,7 +13,7 @@ export const RabbyConnectUI = (props: ConnectUIProps) => { ); const locale = useTWLocale().wallets.rabbyWallet; const { walletConfig, connected } = props; - const connect = useConnect(); + const { connect } = props; const [errorConnecting, setErrorConnecting] = useState(false); const hideBackButton = props.supportedWallets.length === 1; @@ -23,13 +23,13 @@ export const RabbyConnectUI = (props: ConnectUIProps) => { setErrorConnecting(false); setScreen("connecting"); await wait(1000); - await connect(walletConfig); + await connect(); connected(); } catch (e) { setErrorConnecting(true); console.error(e); } - }, [connected, connect, walletConfig]); + }, [connected, connect]); const connectPrompted = useRef(false); useEffect(() => { @@ -104,6 +104,8 @@ export const RabbyConnectUI = (props: ConnectUIProps) => { }} hideBackButton={hideBackButton} walletConfig={walletConfig} + setConnectedWallet={props.setConnectedWallet} + setConnectionStatus={props.setConnectionStatus} /> ); } diff --git a/packages/react/src/wallet/wallets/rabby/RabbyScan.tsx b/packages/react/src/wallet/wallets/rabby/RabbyScan.tsx index 12398f795c5..0ce4a1b843f 100644 --- a/packages/react/src/wallet/wallets/rabby/RabbyScan.tsx +++ b/packages/react/src/wallet/wallets/rabby/RabbyScan.tsx @@ -5,7 +5,7 @@ import { } from "@thirdweb-dev/react-core"; import { useEffect, useRef, useState } from "react"; import type { RabbyWallet } from "@thirdweb-dev/wallets"; -import type { WalletConfig } from "@thirdweb-dev/react-core"; +import type { ConnectUIProps, WalletConfig } from "@thirdweb-dev/react-core"; import { useTWLocale } from "../../../evm/providers/locale-provider"; export const RabbyScan: React.FC<{ @@ -14,12 +14,23 @@ export const RabbyScan: React.FC<{ onConnected: () => void; walletConfig: WalletConfig; hideBackButton: boolean; -}> = ({ onBack, onConnected, onGetStarted, walletConfig, hideBackButton }) => { + setConnectedWallet: ConnectUIProps["setConnectedWallet"]; + setConnectionStatus: ConnectUIProps["setConnectionStatus"]; +}> = (props) => { + const { + onBack, + onConnected, + onGetStarted, + walletConfig, + hideBackButton, + setConnectedWallet, + setConnectionStatus, + } = props; + const locale = useTWLocale().wallets.rabbyWallet; const createInstance = useCreateWalletInstance(); const [qrCodeUri, setQrCodeUri] = useState(); - const { setConnectedWallet, chainToConnect, setConnectionStatus } = - useWalletContext(); + const { chainToConnect } = useWalletContext(); const scanStarted = useRef(false); useEffect(() => { diff --git a/packages/react/src/wallet/wallets/rainbow/RainbowConnectUI.tsx b/packages/react/src/wallet/wallets/rainbow/RainbowConnectUI.tsx deleted file mode 100644 index a63ca2cb7ee..00000000000 --- a/packages/react/src/wallet/wallets/rainbow/RainbowConnectUI.tsx +++ /dev/null @@ -1,143 +0,0 @@ -import { ConnectUIProps, useConnect } from "@thirdweb-dev/react-core"; -import { ConnectingScreen } from "../../ConnectWallet/screens/ConnectingScreen"; -import { isMobile } from "../../../evm/utils/isMobile"; -import { useCallback, useEffect, useRef, useState } from "react"; -import { RainbowScan } from "./RainbowScan"; -import { GetStartedScreen } from "../../ConnectWallet/screens/GetStartedScreen"; -import { RainbowWallet } from "@thirdweb-dev/wallets"; -import { WCOpenURI } from "../../ConnectWallet/screens/WCOpenUri"; -import { wait } from "../../../utils/wait"; -import { rainbowWalletUris } from "./rainbowWalletUris"; -import { useTWLocale } from "../../../evm/providers/locale-provider"; - -export const RainbowConnectUI = (props: ConnectUIProps) => { - const [screen, setScreen] = useState< - "connecting" | "scanning" | "get-started" | "open-wc-uri" - >("connecting"); - const { walletConfig, connected } = props; - const connect = useConnect(); - const [errorConnecting, setErrorConnecting] = useState(false); - const locale = useTWLocale().wallets.rainbowWallet; - - const connectingLocale = { - getStartedLink: locale.getStartedLink, - instruction: locale.connectionScreen.instruction, - tryAgain: locale.connectionScreen.retry, - inProgress: locale.connectionScreen.inProgress, - failed: locale.connectionScreen.failed, - }; - - const hideBackButton = props.supportedWallets.length === 1; - - const connectToExtension = useCallback(async () => { - try { - setErrorConnecting(false); - connectPrompted.current = true; - setScreen("connecting"); - await wait(1000); - await connect(walletConfig); - connected(); - } catch (e) { - setErrorConnecting(true); - console.error(e); - } - }, [connected, connect, walletConfig]); - - const connectPrompted = useRef(false); - useEffect(() => { - if (connectPrompted.current) { - return; - } - - const isInstalled = walletConfig.isInstalled - ? walletConfig.isInstalled() - : false; - - // if loading - (async () => { - if (isInstalled) { - connectToExtension(); - } - - // if rainbow is not injected - else { - // on mobile, open rainbow app link - if (isMobile()) { - setScreen("open-wc-uri"); - } else { - // on desktop, show the rainbow scan qr code - setScreen("scanning"); - } - } - })(); - }, [connectToExtension, walletConfig]); - - if (screen === "connecting") { - return ( - { - setScreen("get-started"); - }} - onRetry={connectToExtension} - hideBackButton={hideBackButton} - onBack={props.goBack} - walletName={walletConfig.meta.name} - walletIconURL={walletConfig.meta.iconURL} - /> - ); - } - - if (screen === "open-wc-uri") { - return ( - { - // NOOP - TODO make onRetry optional - }} - errorConnecting={errorConnecting} - onGetStarted={() => { - setScreen("get-started"); - }} - hideBackButton={hideBackButton} - onBack={props.goBack} - onConnected={connected} - walletConfig={walletConfig} - appUriPrefix={rainbowWalletUris} - /> - ); - } - - if (screen === "get-started") { - return ( - - ); - } - - if (screen === "scanning") { - return ( - { - setScreen("get-started"); - }} - hideBackButton={hideBackButton} - walletConfig={walletConfig} - /> - ); - } - - return null; -}; diff --git a/packages/react/src/wallet/wallets/rainbow/RainbowScan.tsx b/packages/react/src/wallet/wallets/rainbow/RainbowScan.tsx deleted file mode 100644 index 84b5fd28893..00000000000 --- a/packages/react/src/wallet/wallets/rainbow/RainbowScan.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import { ScanScreen } from "../../ConnectWallet/screens/ScanScreen"; -import { - useCreateWalletInstance, - useWalletContext, -} from "@thirdweb-dev/react-core"; -import { useEffect, useRef, useState } from "react"; -import type { RainbowWallet } from "@thirdweb-dev/wallets"; -import type { WalletConfig } from "@thirdweb-dev/react-core"; -import { useTWLocale } from "../../../evm/providers/locale-provider"; - -export const RainbowScan: React.FC<{ - onBack: () => void; - onGetStarted: () => void; - onConnected: () => void; - walletConfig: WalletConfig; - hideBackButton: boolean; -}> = ({ onBack, onConnected, onGetStarted, walletConfig, hideBackButton }) => { - const locale = useTWLocale().wallets.rainbowWallet; - const createInstance = useCreateWalletInstance(); - const [qrCodeUri, setQrCodeUri] = useState(); - const { setConnectedWallet, chainToConnect, setConnectionStatus } = - useWalletContext(); - - const scanStarted = useRef(false); - useEffect(() => { - if (scanStarted.current) { - return; - } - scanStarted.current = true; - - const rainbow = createInstance(walletConfig); - - setConnectionStatus("connecting"); - rainbow.connectWithQrCode({ - chainId: chainToConnect?.chainId, - onQrCodeUri(uri) { - setQrCodeUri(uri); - }, - onConnected() { - setConnectedWallet(rainbow); - onConnected(); - }, - }); - }, [ - createInstance, - setConnectedWallet, - chainToConnect, - onConnected, - walletConfig, - setConnectionStatus, - ]); - - return ( - - ); -}; diff --git a/packages/react/src/wallet/wallets/rainbow/RainbowWallet.tsx b/packages/react/src/wallet/wallets/rainbow/RainbowWallet.tsx index a5c45aa427d..2ee328c1aa4 100644 --- a/packages/react/src/wallet/wallets/rainbow/RainbowWallet.tsx +++ b/packages/react/src/wallet/wallets/rainbow/RainbowWallet.tsx @@ -1,11 +1,21 @@ -import type { WalletOptions, WalletConfig } from "@thirdweb-dev/react-core"; +import type { + WalletOptions, + WalletConfig, + ConnectUIProps, +} from "@thirdweb-dev/react-core"; import { RainbowWallet, getInjectedRainbowProvider, } from "@thirdweb-dev/wallets"; -import { RainbowConnectUI } from "./RainbowConnectUI"; import { handelWCSessionRequest } from "../handleWCSessionRequest"; -import { rainbowWalletUris } from "./rainbowWalletUris"; +import { useTWLocale } from "../../../evm/providers/locale-provider"; +import { ExtensionOrWCConnectionUI } from "../_common/ExtensionORWCConnectionUI"; + +const rainbowWalletUris = { + ios: "rainbow://", + android: "https://rnbwapp.com/", + other: "https://rnbwapp.com/", +}; export type RainbowWalletConfigOptions = { /** @@ -83,9 +93,30 @@ export const rainbowWallet = ( return wallet; }, - connectUI: RainbowConnectUI, - isInstalled() { - return !!getInjectedRainbowProvider(); - }, + connectUI: ConnectUI, + isInstalled: isInstalled, }; }; + +function isInstalled() { + return !!getInjectedRainbowProvider(); +} + +function ConnectUI(props: ConnectUIProps) { + const locale = useTWLocale(); + return ( + props.setConnectedWallet(w as RainbowWallet)} + setConnectionStatus={props.setConnectionStatus} + supportedWallets={props.supportedWallets} + walletConnectUris={rainbowWalletUris} + walletLocale={locale.wallets.rainbowWallet} + isInstalled={isInstalled} + /> + ); +} diff --git a/packages/react/src/wallet/wallets/rainbow/rainbowWalletUris.ts b/packages/react/src/wallet/wallets/rainbow/rainbowWalletUris.ts deleted file mode 100644 index c7d1f8b13b9..00000000000 --- a/packages/react/src/wallet/wallets/rainbow/rainbowWalletUris.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const rainbowWalletUris = { - ios: "rainbow://", - android: "https://rnbwapp.com/", - other: "https://rnbwapp.com/", -}; diff --git a/packages/react/src/wallet/wallets/safe/SelectAccount.tsx b/packages/react/src/wallet/wallets/safe/SelectAccount.tsx index e8217c0ee46..ac7955bcf84 100644 --- a/packages/react/src/wallet/wallets/safe/SelectAccount.tsx +++ b/packages/react/src/wallet/wallets/safe/SelectAccount.tsx @@ -11,18 +11,14 @@ import { ExclamationTriangleIcon, } from "@radix-ui/react-icons"; import { - useChain, - useChainId, - useConnect, - useConnectionStatus, + ConnectUIProps, + WalletConfig, useSupportedChains, - useSwitchChain, - useWallet, + useWalletContext, } from "@thirdweb-dev/react-core"; -import { SafeSupportedChainsSet } from "@thirdweb-dev/wallets"; +import { SafeSupportedChainsSet, SafeWallet } from "@thirdweb-dev/wallets"; import { utils } from "ethers"; import { useContext, useState } from "react"; -import { SafeWalletConfig } from "./types"; import { Container, Line, ModalHeader } from "../../../components/basic"; import { Link, Text } from "../../../components/text"; import { ModalConfigCtx } from "../../../evm/providers/wallet-ui-states-provider"; @@ -34,14 +30,16 @@ import { useCustomTheme } from "../../../design-system/CustomThemeProvider"; export const SelectAccount: React.FC<{ onBack: () => void; onConnect: () => void; - safeWalletConfig: SafeWalletConfig; renderBackButton?: boolean; + connect: ConnectUIProps["connect"]; + connectionStatus: ConnectUIProps["connectionStatus"]; + meta: WalletConfig["meta"]; }> = (props) => { const locale = useTWLocale().wallets.safeWallet.accountDetailsScreen; - const activeWallet = useWallet(); - const connect = useConnect(); - const activeChain = useChain(); - const connectedChainId = useChainId(); + const { personalWalletConnection } = useWalletContext(); + const { activeWallet, connectedChainId, switchChain } = + personalWalletConnection; + const { connect, connectionStatus } = props; const [safeAddress, setSafeAddress] = useState(""); const [safeChainId, setSafeChainId] = useState(-1); @@ -50,7 +48,6 @@ export const SelectAccount: React.FC<{ const [switchError, setSwitchError] = useState(false); const [switchingNetwork, setSwitchingNetwork] = useState(false); - const connectionStatus = useConnectionStatus(); const chains = useSupportedChains(); // put supported chains first @@ -69,13 +66,13 @@ export const SelectAccount: React.FC<{ const useOptGroup = mainnets.length > 0 && testnets.length > 0; const handleSubmit = async () => { - if (!selectedSafeChain || !activeWallet || !activeChain) { + if (!selectedSafeChain || !activeWallet) { return; } setSafeConnectError(false); try { - await connect(props.safeWalletConfig, { + await connect({ chain: selectedSafeChain, personalWallet: activeWallet, safeAddress, @@ -92,7 +89,6 @@ export const SelectAccount: React.FC<{ const isValidAddress = utils.isAddress(safeAddress); const disableNetworkSelection = supportedChains.length === 1; - const switchChain = useSwitchChain(); const modalConfig = useContext(ModalConfigCtx); return ( @@ -110,9 +106,9 @@ export const SelectAccount: React.FC<{ > diff --git a/packages/react/src/wallet/wallets/safe/SelectPersonalWallet.tsx b/packages/react/src/wallet/wallets/safe/SelectPersonalWallet.tsx index 7a4191aaf46..4700dab52fb 100644 --- a/packages/react/src/wallet/wallets/safe/SelectPersonalWallet.tsx +++ b/packages/react/src/wallet/wallets/safe/SelectPersonalWallet.tsx @@ -9,7 +9,7 @@ import { } from "../../../components/basic"; import { Button } from "../../../components/buttons"; import { WalletSelection } from "../../ConnectWallet/WalletSelector"; -import { WalletConfig } from "@thirdweb-dev/react-core"; +import { WalletConfig, useWalletContext } from "@thirdweb-dev/react-core"; import { SafeWalletConfig } from "./types"; import { useEffect, useRef } from "react"; import { Link, Text } from "../../../components/text"; @@ -31,6 +31,8 @@ export const SelectpersonalWallet: React.FC<{ (w) => w.id !== walletIds.localWallet, ); + const { personalWalletConnection } = useWalletContext(); + // auto select guest wallet if no other wallets const { selectWallet } = props; const selected = useRef(false); @@ -92,6 +94,15 @@ export const SelectpersonalWallet: React.FC<{ maxHeight="300px" walletConfigs={personalWallets} selectWallet={props.selectWallet} + selectUIProps={{ + connect: personalWalletConnection.connectWallet, + connectionStatus: personalWalletConnection.connectionStatus, + createWalletInstance: personalWalletConnection.createWalletInstance, + setConnectedWallet: personalWalletConnection.setConnectedWallet, + setConnectionStatus: personalWalletConnection.setConnectionStatus, + connectedWallet: personalWalletConnection.activeWallet, + connectedWalletAddress: personalWalletConnection.connectedAddress, + }} /> diff --git a/packages/react/src/wallet/wallets/safe/safeWallet.tsx b/packages/react/src/wallet/wallets/safe/safeWallet.tsx index 379f9526ad8..2d73dfde4e1 100644 --- a/packages/react/src/wallet/wallets/safe/safeWallet.tsx +++ b/packages/react/src/wallet/wallets/safe/safeWallet.tsx @@ -4,7 +4,8 @@ import { ConnectUIProps, WalletOptions, useDisconnect, - useWallet, + useWalletContext, + WalletInstance, } from "@thirdweb-dev/react-core"; import { defaultWallets } from "../defaultWallets"; import { useState } from "react"; @@ -89,15 +90,20 @@ export const safeWallet = ( export const SafeConnectUI = ( props: ConnectUIProps & { personalWallets: WalletConfig[] }, ) => { - const activeWallet = useWallet(); + const { personalWalletConnection } = useWalletContext(); + const [personalWalletConfig, setPersonalWalletConfig] = useState< WalletConfig | undefined >(); + const disconnect = useDisconnect(); + // screen 2 if (personalWalletConfig) { - const _props: ConnectUIProps = { + const _props: ConnectUIProps = { goBack: () => { + personalWalletConnection.disconnectWallet(); + disconnect(); setPersonalWalletConfig(undefined); }, connected() { @@ -112,6 +118,26 @@ export const SafeConnectUI = ( selectionData: props.selectionData, setSelectionData: props.setSelectionData, modalSize: props.modalSize, + connect(options) { + return personalWalletConnection.connectWallet( + personalWalletConfig, + options, + ); + }, + setConnectedWallet(wallet) { + personalWalletConnection.setConnectedWallet(wallet); + }, + setConnectionStatus(status) { + personalWalletConnection.setConnectionStatus(status); + }, + connectionStatus: personalWalletConnection.connectionStatus, + createWalletInstance() { + return personalWalletConnection.createWalletInstance( + personalWalletConfig, + ); + }, + connectedWallet: personalWalletConnection.activeWallet, + connectedWalletAddress: personalWalletConnection.connectedAddress, }; if (personalWalletConfig.connectUI) { @@ -121,7 +147,8 @@ export const SafeConnectUI = ( return ; } - if (!activeWallet) { + // screen 1 + if (!personalWalletConnection.activeWallet) { return ( 1} onBack={() => { + personalWalletConnection.disconnectWallet(); disconnect(); props.goBack(); }} onConnect={props.connected} - safeWalletConfig={props.walletConfig} + connect={props.connect} + connectionStatus={props.connectionStatus} + meta={props.walletConfig.meta} /> ); }; diff --git a/packages/react/src/wallet/wallets/smartWallet/SmartWalletConnecting.tsx b/packages/react/src/wallet/wallets/smartWallet/SmartWalletConnecting.tsx index 1d9c464c0c4..d2c90a8e29f 100644 --- a/packages/react/src/wallet/wallets/smartWallet/SmartWalletConnecting.tsx +++ b/packages/react/src/wallet/wallets/smartWallet/SmartWalletConnecting.tsx @@ -4,13 +4,11 @@ import { Button } from "../../../components/buttons"; import { iconSize, spacing, fontSize } from "../../../design-system"; import { ExclamationTriangleIcon } from "@radix-ui/react-icons"; import { - useChain, useConnect, useConnectionStatus, - useNetworkMismatch, useWalletContext, - useWallet, - useSwitchChain, + WalletInstance, + useSDKChainId, WalletConfig, } from "@thirdweb-dev/react-core"; import { useCallback, useContext, useEffect, useRef, useState } from "react"; @@ -18,22 +16,25 @@ import { Container, ModalHeader } from "../../../components/basic"; import { Text } from "../../../components/text"; import { ModalConfigCtx } from "../../../evm/providers/wallet-ui-states-provider"; import { useTWLocale } from "../../../evm/providers/locale-provider"; -import type { SmartWallet } from "@thirdweb-dev/wallets"; +import { SmartWallet } from "@thirdweb-dev/wallets"; export const SmartWalletConnecting: React.FC<{ onBack: () => void; onConnect: () => void; smartWallet: WalletConfig; - personalWallet: WalletConfig; + personalWalletConfig: WalletConfig; + personalWallet: WalletInstance; + personalWalletChainId: number; + switchChainPersonalWallet: (chainId: number) => void; }> = (props) => { const locale = useTWLocale().wallets.smartWallet; - const activeWallet = useWallet(); // personal wallet - + const { personalWallet, personalWalletChainId, switchChainPersonalWallet } = + props; const connect = useConnect(); - const connectedChain = useChain(); const targetChain = useWalletContext().activeChain; + const sdkChainId = useSDKChainId(); - const mismatch = useNetworkMismatch(); + const wrongNetwork = sdkChainId !== personalWalletChainId; const [connectError, setConnectError] = useState(false); const [switchError, setSwitchError] = useState(false); @@ -44,11 +45,10 @@ export const SmartWalletConnecting: React.FC<{ const { onConnect } = props; const connectStarted = useRef(false); - const switchChain = useSwitchChain(); const modalSize = useContext(ModalConfigCtx).modalSize; const handleConnect = useCallback(async () => { - if (!activeWallet || !connectedChain || connectStarted.current) { + if (!personalWallet || connectStarted.current) { return; } setConnectError(false); @@ -56,22 +56,22 @@ export const SmartWalletConnecting: React.FC<{ try { connectStarted.current = true; await connect(props.smartWallet, { - personalWallet: activeWallet, + personalWallet: personalWallet, }); onConnect(); } catch (e) { console.error(e); setConnectError(true); } - }, [activeWallet, connectedChain, connect, props.smartWallet, onConnect]); + }, [personalWallet, connect, props.smartWallet, onConnect]); useEffect(() => { - if (!mismatch) { + if (!wrongNetwork) { handleConnect(); } - }, [mismatch, handleConnect, activeWallet, connectedChain]); + }, [wrongNetwork, handleConnect, personalWallet]); - if (!connectError && (connectionStatus === "connecting" || !mismatch)) { + if (!connectError && (connectionStatus === "connecting" || !wrongNetwork)) { return ( - + ); } @@ -111,8 +111,8 @@ export const SmartWalletConnecting: React.FC<{ @@ -150,14 +150,11 @@ export const SmartWalletConnecting: React.FC<{ gap: spacing.sm, }} onClick={async () => { - if (!activeWallet) { - throw new Error("No active wallet"); - } setConnectError(false); setSwitchError(false); setSwitchingNetwork(true); try { - await switchChain(targetChain.chainId); + await switchChainPersonalWallet(targetChain.chainId); } catch (e) { setSwitchError(true); } finally { diff --git a/packages/react/src/wallet/wallets/smartWallet/smartWallet.tsx b/packages/react/src/wallet/wallets/smartWallet/smartWallet.tsx index b4f0e28538e..c0c70e81dec 100644 --- a/packages/react/src/wallet/wallets/smartWallet/smartWallet.tsx +++ b/packages/react/src/wallet/wallets/smartWallet/smartWallet.tsx @@ -2,12 +2,15 @@ import { type WalletConfig, type ConnectUIProps, type WalletOptions, - useWallet, + useWalletContext, + SelectUIProps, + WalletInstance, } from "@thirdweb-dev/react-core"; import { SmartWallet } from "@thirdweb-dev/wallets"; import { SmartWalletConfigOptions } from "./types"; import { SmartWalletConnecting } from "./SmartWalletConnecting"; import { HeadlessConnectUI } from "../headlessConnectUI"; +import { WalletEntryButton } from "../../ConnectWallet/WalletSelector"; /** * A wallet configurator for [Smart Wallet](https://portal.thirdweb.com/wallet/smart-wallet) which allows integrating the wallet with React @@ -87,46 +90,125 @@ import { HeadlessConnectUI } from "../headlessConnectUI"; * ``` */ export const smartWallet = ( - wallet: WalletConfig, + walletConfig: WalletConfig, config: SmartWalletConfigOptions, ): WalletConfig => { - const WalletSelectUI = wallet.selectUI; - return { - ...wallet, + ...walletConfig, create: (options: WalletOptions) => new SmartWallet({ ...options, ...config }), connectUI(props) { - return ; + return ; }, - selectUI: WalletSelectUI - ? (props) => { - return ; - } + selectUI: walletConfig.selectUI + ? (props) => ( + + ) : undefined, - personalWallets: [wallet], + + personalWallets: [walletConfig], }; }; +const SmartSelectUI = ( + props: SelectUIProps & { + personalWalletConfig: WalletConfig; + }, +) => { + const { personalWalletConnection } = useWalletContext(); + + const PersonalWalletSelectUI = props.personalWalletConfig.selectUI; + + if (!PersonalWalletSelectUI) { + return ( + { + props.onSelect(undefined); + }} + /> + ); + } + + return ( + { + return personalWalletConnection.connectWallet( + props.personalWalletConfig, + options, + ); + }} + createWalletInstance={() => { + return personalWalletConnection.createWalletInstance( + props.personalWalletConfig, + ); + }} + modalSize={props.modalSize} + onSelect={props.onSelect} + setConnectedWallet={(wallet) => { + personalWalletConnection.setConnectedWallet(wallet); + }} + setConnectionStatus={(status) => { + personalWalletConnection.setConnectionStatus(status); + }} + connectionStatus={personalWalletConnection.connectionStatus} + supportedWallets={props.supportedWallets} + theme={props.theme} + onLocallyConnected={props.onLocallyConnected} + connectedWallet={personalWalletConnection.activeWallet} + connectedWalletAddress={personalWalletConnection.connectedAddress} + /> + ); +}; + export const SmartConnectUI = ( - props: ConnectUIProps & { personalWallet: WalletConfig }, + props: ConnectUIProps & { personalWalletConfig: WalletConfig }, ) => { - const activeWallet = useWallet(); + const { personalWalletConnection } = useWalletContext(); const { walletConfig } = props; + const { personalWalletConfig } = props; - const PersonalWalletConfig = props.personalWallet; - - if (!activeWallet) { - const _props: ConnectUIProps = { - ...props, - walletConfig: PersonalWalletConfig, + if (!personalWalletConnection.activeWallet) { + const _props: ConnectUIProps = { + walletConfig: personalWalletConfig, connected: () => { // override to no-op }, + connect(options) { + return personalWalletConnection.connectWallet( + personalWalletConfig, + options, + ); + }, + setConnectedWallet(wallet) { + personalWalletConnection.setConnectedWallet(wallet); + }, + setConnectionStatus(status) { + personalWalletConnection.setConnectionStatus(status); + }, + connectionStatus: personalWalletConnection.connectionStatus, + createWalletInstance: () => { + return personalWalletConnection.createWalletInstance( + props.personalWalletConfig, + ); + }, + goBack: props.goBack, + hide: props.hide, + isOpen: props.isOpen, + modalSize: props.modalSize, + selectionData: props.selectionData, + setSelectionData: props.setSelectionData, + show: props.show, + supportedWallets: props.supportedWallets, + theme: props.theme, + onLocallyConnected: props.onLocallyConnected, + connectedWallet: personalWalletConnection.activeWallet, + connectedWalletAddress: personalWalletConnection.connectedAddress, }; - if (PersonalWalletConfig.connectUI) { - return ; + if (personalWalletConfig.connectUI) { + return ; } return ; @@ -135,9 +217,14 @@ export const SmartConnectUI = ( return ( { + props.connected(); + }} smartWallet={walletConfig} - personalWallet={props.personalWallet} + personalWalletConfig={personalWalletConfig} + personalWallet={personalWalletConnection.activeWallet} + personalWalletChainId={personalWalletConnection.connectedChainId || 1} + switchChainPersonalWallet={personalWalletConnection.switchChain} /> ); }; diff --git a/packages/react/src/wallet/wallets/trustWallet/TrustConnectUI.tsx b/packages/react/src/wallet/wallets/trustWallet/TrustConnectUI.tsx deleted file mode 100644 index 211e316d035..00000000000 --- a/packages/react/src/wallet/wallets/trustWallet/TrustConnectUI.tsx +++ /dev/null @@ -1,142 +0,0 @@ -import { ConnectUIProps, useConnect } from "@thirdweb-dev/react-core"; -import { TrustWallet } from "@thirdweb-dev/wallets"; -import { useCallback, useEffect, useRef, useState } from "react"; -import { isMobile } from "../../../evm/utils/isMobile"; -import { ConnectingScreen } from "../../ConnectWallet/screens/ConnectingScreen"; -import { GetStartedScreen } from "../../ConnectWallet/screens/GetStartedScreen"; -import { TrustScan } from "./TrustScan"; -import { WCOpenURI } from "../../ConnectWallet/screens/WCOpenUri"; -import { trustWalletUris } from "./trustWalletUris"; -import { useTWLocale } from "../../../evm/providers/locale-provider"; - -export const TrustConnectUI = (props: ConnectUIProps) => { - const [screen, setScreen] = useState< - "connecting" | "scanning" | "get-started" | "open-wc-uri" - >("connecting"); - const locale = useTWLocale().wallets.trustWallet; - const { walletConfig, connected } = props; - const connect = useConnect(); - const hideBackButton = props.supportedWallets.length === 1; - const [errorConnecting, setErrorConnecting] = useState(false); - - const connectingLocale = { - getStartedLink: locale.getStartedLink, - instruction: locale.connectionScreen.instruction, - tryAgain: locale.connectionScreen.retry, - inProgress: locale.connectionScreen.inProgress, - failed: locale.connectionScreen.failed, - }; - - const connectToExtension = useCallback(async () => { - try { - setErrorConnecting(false); - connectPrompted.current = true; - setScreen("connecting"); - await connect(walletConfig); - connected(); - } catch (e) { - setErrorConnecting(true); - console.error(e); - } - }, [connected, connect, walletConfig]); - - const connectPrompted = useRef(false); - useEffect(() => { - if (connectPrompted.current) { - return; - } - - const isInstalled = walletConfig.isInstalled - ? walletConfig.isInstalled() - : false; - - // if loading - (async () => { - if (isInstalled) { - connectToExtension(); - } - - // if trust is not injected - else { - // on mobile, open trust app link - if (isMobile()) { - setScreen("open-wc-uri"); - } else { - // on desktop, show the trust scan qr code - setScreen("scanning"); - } - } - })(); - }, [connectToExtension, walletConfig]); - - const handleGetStarted = () => { - setScreen("get-started"); - }; - - if (screen === "connecting") { - return ( - - ); - } - - if (screen === "open-wc-uri") { - return ( - { - // NOOP - TODO make onRetry optional - }} - errorConnecting={errorConnecting} - onGetStarted={handleGetStarted} - hideBackButton={hideBackButton} - onBack={props.goBack} - onConnected={props.connected} - walletConfig={walletConfig} - appUriPrefix={trustWalletUris} - // supportLink="https://support.trustwallet.com/en/support/home" - /> - ); - } - - if (screen === "get-started") { - return ( - - ); - } - - if (screen === "scanning") { - return ( - { - setScreen("get-started"); - }} - walletConfig={walletConfig} - /> - ); - } - - return null; -}; diff --git a/packages/react/src/wallet/wallets/trustWallet/TrustScan.tsx b/packages/react/src/wallet/wallets/trustWallet/TrustScan.tsx deleted file mode 100644 index e5696807a06..00000000000 --- a/packages/react/src/wallet/wallets/trustWallet/TrustScan.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import { ScanScreen } from "../../ConnectWallet/screens/ScanScreen"; -import { - useCreateWalletInstance, - useWalletContext, -} from "@thirdweb-dev/react-core"; -import { useEffect, useRef, useState } from "react"; -import type { TrustWallet } from "@thirdweb-dev/wallets"; -import type { WalletConfig } from "@thirdweb-dev/react-core"; -import { useTWLocale } from "../../../evm/providers/locale-provider"; - -export const TrustScan: React.FC<{ - onBack: () => void; - onGetStarted: () => void; - onConnected: () => void; - hideBackButton: boolean; - walletConfig: WalletConfig; -}> = ({ onBack, onConnected, onGetStarted, walletConfig, hideBackButton }) => { - const locale = useTWLocale().wallets.trustWallet; - const createInstance = useCreateWalletInstance(); - const [qrCodeUri, setQrCodeUri] = useState(); - const { setConnectedWallet, chainToConnect, setConnectionStatus } = - useWalletContext(); - - const scanStarted = useRef(false); - useEffect(() => { - if (scanStarted.current) { - return; - } - scanStarted.current = true; - - const trust = createInstance(walletConfig); - - setConnectionStatus("connecting"); - trust.connectWithQrCode({ - chainId: chainToConnect?.chainId, - onQrCodeUri(uri) { - setQrCodeUri(uri); - }, - onConnected() { - setConnectedWallet(trust); - onConnected(); - }, - }); - }, [ - createInstance, - setConnectedWallet, - chainToConnect, - onConnected, - walletConfig, - setConnectionStatus, - ]); - - return ( - - ); -}; diff --git a/packages/react/src/wallet/wallets/trustWallet/TrustWallet.tsx b/packages/react/src/wallet/wallets/trustWallet/TrustWallet.tsx index 89b85b93449..9ca059bd1f2 100644 --- a/packages/react/src/wallet/wallets/trustWallet/TrustWallet.tsx +++ b/packages/react/src/wallet/wallets/trustWallet/TrustWallet.tsx @@ -1,8 +1,18 @@ -import type { WalletOptions, WalletConfig } from "@thirdweb-dev/react-core"; +import type { + WalletOptions, + WalletConfig, + ConnectUIProps, +} from "@thirdweb-dev/react-core"; import { TrustWallet, assertWindowEthereum } from "@thirdweb-dev/wallets"; -import { TrustConnectUI } from "./TrustConnectUI"; -import { trustWalletUris } from "./trustWalletUris"; import { handelWCSessionRequest } from "../handleWCSessionRequest"; +import { useTWLocale } from "../../../evm/providers/locale-provider"; +import { ExtensionOrWCConnectionUI } from "../_common/ExtensionORWCConnectionUI"; + +const trustWalletUris = { + ios: "trust://", + android: "https://link.trustwallet.com/", + other: "https://link.trustwallet.com/", +}; export type TrustWalletConfigOptions = { /** @@ -79,12 +89,33 @@ export const trustWallet = ( return wallet; }, - connectUI: TrustConnectUI, - isInstalled() { - if (assertWindowEthereum(globalThis.window)) { - return !!globalThis.window.ethereum.isTrust; - } - return false; - }, + connectUI: ConnectUI, + isInstalled: isInstalled, }; }; + +function isInstalled() { + if (assertWindowEthereum(globalThis.window)) { + return !!globalThis.window.ethereum.isTrust; + } + return false; +} + +function ConnectUI(props: ConnectUIProps) { + const locale = useTWLocale(); + return ( + props.setConnectedWallet(w as TrustWallet)} + setConnectionStatus={props.setConnectionStatus} + supportedWallets={props.supportedWallets} + walletConnectUris={trustWalletUris} + walletLocale={locale.wallets.trustWallet} + isInstalled={isInstalled} + /> + ); +} diff --git a/packages/react/src/wallet/wallets/trustWallet/trustWalletUris.ts b/packages/react/src/wallet/wallets/trustWallet/trustWalletUris.ts deleted file mode 100644 index d0e2861d95a..00000000000 --- a/packages/react/src/wallet/wallets/trustWallet/trustWalletUris.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const trustWalletUris = { - ios: "trust://", - android: "https://link.trustwallet.com/", - other: "https://link.trustwallet.com/", -}; diff --git a/packages/react/src/wallet/wallets/walletConnect/WalletConnectScan.tsx b/packages/react/src/wallet/wallets/walletConnect/WalletConnectScan.tsx index d0a54ca4fa7..927d025b66b 100644 --- a/packages/react/src/wallet/wallets/walletConnect/WalletConnectScan.tsx +++ b/packages/react/src/wallet/wallets/walletConnect/WalletConnectScan.tsx @@ -1,10 +1,7 @@ -import { - useCreateWalletInstance, - useWalletContext, -} from "@thirdweb-dev/react-core"; +import { useWalletContext } from "@thirdweb-dev/react-core"; import { useEffect, useRef, useState } from "react"; import type { WalletConnect } from "@thirdweb-dev/wallets"; -import type { WalletConfig } from "@thirdweb-dev/react-core"; +import type { ConnectUIProps, WalletConfig } from "@thirdweb-dev/react-core"; import { QRCode } from "../../../components/QRCode"; import { Img } from "../../../components/Img"; import { Spacer } from "../../../components/Spacer"; @@ -19,12 +16,23 @@ export const WalletConnectScan: React.FC<{ walletConfig: WalletConfig; hideBackButton: boolean; modalSize: "wide" | "compact"; -}> = ({ onBack, onConnected, walletConfig }) => { + setConnectedWallet: ConnectUIProps["setConnectedWallet"]; + setConnectionStatus: ConnectUIProps["setConnectionStatus"]; + createWalletInstance: ConnectUIProps["createWalletInstance"]; +}> = (props) => { + const { + setConnectedWallet, + setConnectionStatus, + createWalletInstance, + onBack, + onConnected, + walletConfig, + } = props; + const locale = useTWLocale().wallets.walletConnect; - const createInstance = useCreateWalletInstance(); const [qrCodeUri, setQrCodeUri] = useState(); - const { setConnectedWallet, chainToConnect, setConnectionStatus } = - useWalletContext(); + + const { chainToConnect } = useWalletContext(); const scanStarted = useRef(false); useEffect(() => { @@ -33,7 +41,7 @@ export const WalletConnectScan: React.FC<{ } scanStarted.current = true; - const walletInstance = createInstance(walletConfig); + const walletInstance = createWalletInstance(); setConnectionStatus("connecting"); walletInstance.connectWithQrCode({ @@ -47,7 +55,7 @@ export const WalletConnectScan: React.FC<{ }, }); }, [ - createInstance, + createWalletInstance, setConnectedWallet, chainToConnect, onConnected, @@ -58,7 +66,10 @@ export const WalletConnectScan: React.FC<{ return ( - + diff --git a/packages/react/src/wallet/wallets/walletConnect/walletConnect.tsx b/packages/react/src/wallet/wallets/walletConnect/walletConnect.tsx index 3e26a4c7b17..9166d697796 100644 --- a/packages/react/src/wallet/wallets/walletConnect/walletConnect.tsx +++ b/packages/react/src/wallet/wallets/walletConnect/walletConnect.tsx @@ -97,8 +97,11 @@ export const walletConnect = ( onBack={props.goBack} onConnected={props.connected} walletConfig={props.walletConfig} - hideBackButton={props.supportedWallets.length > 1} + hideBackButton={props.supportedWallets.length === 1} modalSize={props.modalSize} + createWalletInstance={props.createWalletInstance} + setConnectedWallet={props.setConnectedWallet} + setConnectionStatus={props.setConnectionStatus} /> ); }, diff --git a/packages/react/src/wallet/wallets/zerion/ZerionConnectUI.tsx b/packages/react/src/wallet/wallets/zerion/ZerionConnectUI.tsx deleted file mode 100644 index cb49565deb2..00000000000 --- a/packages/react/src/wallet/wallets/zerion/ZerionConnectUI.tsx +++ /dev/null @@ -1,138 +0,0 @@ -import { ConnectUIProps, useConnect } from "@thirdweb-dev/react-core"; -import { ZerionWallet } from "@thirdweb-dev/wallets"; -import { useCallback, useEffect, useRef, useState } from "react"; -import { isMobile } from "../../../evm/utils/isMobile"; -import { ConnectingScreen } from "../../ConnectWallet/screens/ConnectingScreen"; -import { GetStartedScreen } from "../../ConnectWallet/screens/GetStartedScreen"; -import { ZerionScan } from "./ZerionScan"; -import { WCOpenURI } from "../../ConnectWallet/screens/WCOpenUri"; -import { useTWLocale } from "../../../evm/providers/locale-provider"; - -export const ZerionConnectUI = (props: ConnectUIProps) => { - const [screen, setScreen] = useState< - "connecting" | "scanning" | "get-started" | "open-wc-uri" - >("connecting"); - const locale = useTWLocale().wallets.zerionWallet; - const { walletConfig, connected } = props; - const connect = useConnect(); - const hideBackButton = props.supportedWallets.length === 1; - const [errorConnecting, setErrorConnecting] = useState(false); - - const connectingLocale = { - getStartedLink: locale.getStartedLink, - instruction: locale.connectionScreen.instruction, - tryAgain: locale.connectionScreen.retry, - inProgress: locale.connectionScreen.inProgress, - failed: locale.connectionScreen.failed, - }; - - const connectToExtension = useCallback(async () => { - try { - setErrorConnecting(false); - connectPrompted.current = true; - setScreen("connecting"); - await connect(walletConfig); - connected(); - } catch (e) { - setErrorConnecting(true); - } - }, [connect, connected, walletConfig]); - - const connectPrompted = useRef(false); - useEffect(() => { - if (connectPrompted.current) { - return; - } - - const isInstalled = walletConfig.isInstalled - ? walletConfig.isInstalled() - : false; - - // if loading - (async () => { - if (isInstalled) { - connectToExtension(); - } - - // if zerion is not injected - else { - // on mobile, open zerion app link - if (isMobile()) { - setScreen("open-wc-uri"); - } else { - // on desktop, show the metamask scan qr code - setScreen("scanning"); - } - } - })(); - }, [connectToExtension, walletConfig]); - - if (screen === "connecting") { - return ( - setScreen("get-started")} - onRetry={connectToExtension} - onBack={props.goBack} - walletName={walletConfig.meta.name} - walletIconURL={walletConfig.meta.iconURL} - errorConnecting={errorConnecting} - /> - ); - } - - if (screen === "open-wc-uri") { - return ( - { - // NO OP - }} - onGetStarted={() => setScreen("get-started")} - errorConnecting={errorConnecting} - hideBackButton={hideBackButton} - onBack={props.goBack} - onConnected={props.connected} - walletConfig={walletConfig} - appUriPrefix={{ - ios: "zerion://", - android: "https://link.zerion.io/pt3gdRP0njb/", - other: "https://link.zerion.io/pt3gdRP0njb/", - }} - /> - ); - } - - if (screen === "get-started") { - return ( - - ); - } - - if (screen === "scanning") { - return ( - { - setScreen("get-started"); - }} - walletConfig={walletConfig} - /> - ); - } - - return null; -}; diff --git a/packages/react/src/wallet/wallets/zerion/ZerionScan.tsx b/packages/react/src/wallet/wallets/zerion/ZerionScan.tsx deleted file mode 100644 index 6f398aa0334..00000000000 --- a/packages/react/src/wallet/wallets/zerion/ZerionScan.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import { ScanScreen } from "../../ConnectWallet/screens/ScanScreen"; -import { - useCreateWalletInstance, - useWalletContext, -} from "@thirdweb-dev/react-core"; -import { useEffect, useRef, useState } from "react"; -import type { ZerionWallet } from "@thirdweb-dev/wallets"; -import type { WalletConfig } from "@thirdweb-dev/react-core"; -import { useTWLocale } from "../../../evm/providers/locale-provider"; - -export const ZerionScan: React.FC<{ - onBack: () => void; - onGetStarted: () => void; - onConnected: () => void; - walletConfig: WalletConfig; - hideBackButton: boolean; -}> = ({ onBack, onConnected, onGetStarted, walletConfig, hideBackButton }) => { - const locale = useTWLocale().wallets.zerionWallet; - const createInstance = useCreateWalletInstance(); - const [qrCodeUri, setQrCodeUri] = useState(); - const { setConnectedWallet, chainToConnect, setConnectionStatus } = - useWalletContext(); - - const scanStarted = useRef(false); - useEffect(() => { - if (scanStarted.current) { - return; - } - scanStarted.current = true; - - const zerion = createInstance(walletConfig); - - setConnectionStatus("connecting"); - zerion.connectWithQrCode({ - chainId: chainToConnect?.chainId, - onQrCodeUri(uri) { - setQrCodeUri(uri); - }, - onConnected() { - setConnectedWallet(zerion); - onConnected(); - }, - }); - }, [ - createInstance, - setConnectedWallet, - chainToConnect, - onConnected, - walletConfig, - setConnectionStatus, - ]); - - return ( - - ); -}; diff --git a/packages/react/src/wallet/wallets/zerion/zerionWallet.tsx b/packages/react/src/wallet/wallets/zerion/zerionWallet.tsx index 9a95ce4df70..28e690730ba 100644 --- a/packages/react/src/wallet/wallets/zerion/zerionWallet.tsx +++ b/packages/react/src/wallet/wallets/zerion/zerionWallet.tsx @@ -1,8 +1,18 @@ -import type { WalletOptions, WalletConfig } from "@thirdweb-dev/react-core"; +import type { + WalletOptions, + WalletConfig, + ConnectUIProps, +} from "@thirdweb-dev/react-core"; import { ZerionWallet, assertWindowEthereum } from "@thirdweb-dev/wallets"; -import { ZerionConnectUI } from "./ZerionConnectUI"; import { handelWCSessionRequest } from "../handleWCSessionRequest"; -import { zerionWalletUris } from "./zerionWalletUris"; +import { useTWLocale } from "../../../evm/providers/locale-provider"; +import { ExtensionOrWCConnectionUI } from "../_common/ExtensionORWCConnectionUI"; + +const zerionWalletUris = { + ios: "zerion://", + android: "https://link.zerion.io/pt3gdRP0njb/", + other: "https://link.zerion.io/pt3gdRP0njb/", +}; export type ZerionkWalletConfigOptions = { /** @@ -79,12 +89,33 @@ export const zerionWallet = ( return wallet; }, - connectUI: ZerionConnectUI, - isInstalled() { - if (assertWindowEthereum(globalThis.window)) { - return !!globalThis.window.ethereum.isZerion; - } - return false; - }, + connectUI: ConnectUI, + isInstalled: isInstalled, }; }; + +function isInstalled() { + if (assertWindowEthereum(globalThis.window)) { + return !!globalThis.window.ethereum.isZerion; + } + return false; +} + +function ConnectUI(props: ConnectUIProps) { + const locale = useTWLocale(); + return ( + props.setConnectedWallet(w as ZerionWallet)} + setConnectionStatus={props.setConnectionStatus} + supportedWallets={props.supportedWallets} + walletConnectUris={zerionWalletUris} + walletLocale={locale.wallets.zerionWallet} + isInstalled={isInstalled} + /> + ); +} diff --git a/packages/react/src/wallet/wallets/zerion/zerionWalletUris.ts b/packages/react/src/wallet/wallets/zerion/zerionWalletUris.ts deleted file mode 100644 index cf7f1420fad..00000000000 --- a/packages/react/src/wallet/wallets/zerion/zerionWalletUris.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const zerionWalletUris = { - ios: "zerion://", - android: "https://link.zerion.io/pt3gdRP0njb/", - other: "https://link.zerion.io/pt3gdRP0njb/", -}; diff --git a/packages/wallets/src/evm/connectors/embedded-wallet/implementations/utils/iFrameCommunication/IframeCommunicator.ts b/packages/wallets/src/evm/connectors/embedded-wallet/implementations/utils/iFrameCommunication/IframeCommunicator.ts index 70fdbbfa81e..26aaafa0174 100644 --- a/packages/wallets/src/evm/connectors/embedded-wallet/implementations/utils/iFrameCommunication/IframeCommunicator.ts +++ b/packages/wallets/src/evm/connectors/embedded-wallet/implementations/utils/iFrameCommunication/IframeCommunicator.ts @@ -48,7 +48,7 @@ export class IframeCommunicator { // Creating the IFrame element for communication let iframe = document.getElementById(iframeId) as HTMLIFrameElement | null; const hrefLink = new URL(link); - const sdkVersion = process.env.THIRDWEB_EWS_SDK_VERSION; + const sdkVersion = process.env.THIRDWEB_EWS_SDK_VERSION || "2.1.0"; if (!sdkVersion) { throw new Error("Missing THIRDWEB_EWS_SDK_VERSION env var"); } diff --git a/packages/wallets/src/evm/connectors/rainbow/getInjectedRainbowProvider.ts b/packages/wallets/src/evm/connectors/rainbow/getInjectedRainbowProvider.ts index d8e2cb2d9a4..ddebbcdb390 100644 --- a/packages/wallets/src/evm/connectors/rainbow/getInjectedRainbowProvider.ts +++ b/packages/wallets/src/evm/connectors/rainbow/getInjectedRainbowProvider.ts @@ -15,6 +15,7 @@ export function getInjectedRainbowProvider(): Ethereum | undefined { if (!isRainbow) { return; } + // Brave tries to make itself look like MetaMask // Could also try RPC `web3_clientVersion` if following is unreliable if (ethereum.isBraveWallet && !ethereum._events && !ethereum._state) { From b3d0667d2d6317e8e818c96af47a278f3f374758 Mon Sep 17 00:00:00 2001 From: Manan Tank Date: Thu, 7 Dec 2023 04:59:53 +0530 Subject: [PATCH 02/21] add changeset --- .changeset/tidy-falcons-listen.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .changeset/tidy-falcons-listen.md diff --git a/.changeset/tidy-falcons-listen.md b/.changeset/tidy-falcons-listen.md new file mode 100644 index 00000000000..a85eed6a306 --- /dev/null +++ b/.changeset/tidy-falcons-listen.md @@ -0,0 +1,8 @@ +--- +"@thirdweb-dev/react-native": patch +"@thirdweb-dev/react-core": patch +"@thirdweb-dev/wallets": patch +"@thirdweb-dev/react": patch +--- + +Fix double connection issue, Add ConnectEmbed component From 7d74ab9f91ea3cb2e1e44b20f61cd911a47262e1 Mon Sep 17 00:00:00 2001 From: Manan Tank Date: Thu, 7 Dec 2023 05:18:30 +0530 Subject: [PATCH 03/21] remove accidental code --- .../utils/iFrameCommunication/IframeCommunicator.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/wallets/src/evm/connectors/embedded-wallet/implementations/utils/iFrameCommunication/IframeCommunicator.ts b/packages/wallets/src/evm/connectors/embedded-wallet/implementations/utils/iFrameCommunication/IframeCommunicator.ts index 26aaafa0174..70fdbbfa81e 100644 --- a/packages/wallets/src/evm/connectors/embedded-wallet/implementations/utils/iFrameCommunication/IframeCommunicator.ts +++ b/packages/wallets/src/evm/connectors/embedded-wallet/implementations/utils/iFrameCommunication/IframeCommunicator.ts @@ -48,7 +48,7 @@ export class IframeCommunicator { // Creating the IFrame element for communication let iframe = document.getElementById(iframeId) as HTMLIFrameElement | null; const hrefLink = new URL(link); - const sdkVersion = process.env.THIRDWEB_EWS_SDK_VERSION || "2.1.0"; + const sdkVersion = process.env.THIRDWEB_EWS_SDK_VERSION; if (!sdkVersion) { throw new Error("Missing THIRDWEB_EWS_SDK_VERSION env var"); } From 279729f33c58a5e34a8881cb020d71df5f39fc7f Mon Sep 17 00:00:00 2001 From: Manan Tank Date: Fri, 8 Dec 2023 02:51:21 +0530 Subject: [PATCH 04/21] Improved SmartWallet auto connect --- .../providers/thirdweb-wallet-provider.tsx | 46 +++++++++++++++++-- .../src/wallet/ConnectWallet/Details.tsx | 16 ++----- 2 files changed, 45 insertions(+), 17 deletions(-) diff --git a/packages/react-core/src/core/providers/thirdweb-wallet-provider.tsx b/packages/react-core/src/core/providers/thirdweb-wallet-provider.tsx index 20c537ca221..717e18de5e3 100644 --- a/packages/react-core/src/core/providers/thirdweb-wallet-provider.tsx +++ b/packages/react-core/src/core/providers/thirdweb-wallet-provider.tsx @@ -65,6 +65,12 @@ const walletInstanceToConfig: Map< WalletConfig > = new Map(); +/** + * Maps a personal wallet instance to it's wrapper wallet instance ( like smartWallet or safeWallet ) to know it's "wrapper" wallet + */ +const personalWalletToWrapperWallet: Map = + new Map(); + type WalletSetupData = { chains: Chain[]; dAppMeta?: DAppMetaData; @@ -98,6 +104,12 @@ type ThirdwebWalletContextData = { * Get wallet config object from wallet instance */ getWalletConfig: (walletInstance: WalletInstance) => WalletConfig | undefined; + /** + * Get the "wrapper wallet" ( safe/smart wallet ) of the given personal wallet instance + */ + getWrapperWallet: ( + walletInstance: WalletInstance, + ) => WalletInstance | undefined; activeChainSetExplicitly: boolean; clientId?: string; walletConnectHandler: WalletConnectHandler | undefined; @@ -225,6 +237,10 @@ export function useWalletConnectionSetup( const _signer = await wallet.getSigner(); setSigner(_signer); + // if personal wallet exists, we need to replace the connectParams.personalWallet to a stringifiable version + const personalWallet = wallet.getPersonalWallet() as AbstractClientWallet; + personalWalletToWrapperWallet.set(personalWallet, wallet); + // it autoconnected, then the details is already saved in storage, no need to store again if (isAutoConnect) { return; @@ -237,8 +253,6 @@ export function useWalletConnectionSetup( connectParams: connectParams || wallet.getConnectParams(), }; - // if personal wallet exists, we need to replace the connectParams.personalWallet to a stringifiable version - const personalWallet = wallet.getPersonalWallet() as AbstractClientWallet; const personalWalletConfig = walletInstanceToConfig.get(personalWallet); if (personalWallet && personalWalletConfig) { @@ -542,10 +556,13 @@ export function ThirdwebWalletProvider( let _personalWalletInfo = walletInfo.connectParams?.personalWallet; + let shouldSetPersonalWalletAsActive = false; + // if the wallet requires a personal wallet (like smartWallet), but the saved data does not have it // this can happen when user clicks on "switch to personal wallet" and reloads the page // OR when user clicks on magic link social login if (walletObj.personalWallets && !_personalWalletInfo) { + shouldSetPersonalWalletAsActive = true; // fix the connectParams by adding the personal wallet info _personalWalletInfo = { walletId: walletInfo.walletId, @@ -554,6 +571,7 @@ export function ThirdwebWalletProvider( } const personalWalletInfo = _personalWalletInfo; + let personalWalletInstance: WalletInstance | undefined; if (personalWalletInfo) { const personalWallets = walletObj.personalWallets || []; @@ -563,8 +581,7 @@ export function ThirdwebWalletProvider( ); if (personalWalletObj) { // create a personal wallet instance and auto connect it - const personalWalletInstance = - createWalletInstance(personalWalletObj); + personalWalletInstance = createWalletInstance(personalWalletObj); try { await timeoutPromise( @@ -576,6 +593,14 @@ export function ThirdwebWalletProvider( message: autoConnectTimeoutErrorMessage, }, ); + + if (shouldSetPersonalWalletAsActive) { + setConnectedWallet( + personalWalletInstance, + personalWalletInfo.connectParams, + true, + ); + } } catch (e) { console.error("Failed to auto connect personal wallet"); console.error(e); @@ -601,11 +626,19 @@ export function ThirdwebWalletProvider( try { setIsAutoConnecting(true); setConnectionStatus("connecting"); + + if (personalWalletInstance) { + personalWalletToWrapperWallet.set(personalWalletInstance, wallet); + } + await timeoutPromise(wallet.autoConnect(walletInfo.connectParams), { ms: autoConnectTimeout, message: autoConnectTimeoutErrorMessage, }); - setConnectedWallet(wallet, walletInfo.connectParams, true); + + if (!shouldSetPersonalWalletAsActive) { + setConnectedWallet(wallet, walletInfo.connectParams, true); + } } catch (e) { console.error("Failed to auto connect wallet"); console.error(e); @@ -684,6 +717,9 @@ export function ThirdwebWalletProvider( getWalletConfig: (walletInstance: WalletInstance) => { return walletInstanceToConfig.get(walletInstance); }, + getWrapperWallet: (personalWallet: WalletInstance) => { + return personalWalletToWrapperWallet.get(personalWallet); + }, activeChainSetExplicitly: props.activeChainSetExplicitly, clientId: props.clientId, walletConnectHandler: walletConnectHandler, diff --git a/packages/react/src/wallet/ConnectWallet/Details.tsx b/packages/react/src/wallet/ConnectWallet/Details.tsx index d86e965ac65..8178b89e498 100644 --- a/packages/react/src/wallet/ConnectWallet/Details.tsx +++ b/packages/react/src/wallet/ConnectWallet/Details.tsx @@ -116,11 +116,12 @@ export const ConnectedWalletDetails: React.FC<{ const activeWalletConfig = useWalletConfig(); const ensQuery = useENS(); - const [wrapperWallet, setWrapperWallet] = useState< - WalletInstance | undefined - >(); const walletContext = useWalletContext(); + const wrapperWallet = activeWallet + ? walletContext.getWrapperWallet(activeWallet) + : undefined; + const [overrideWalletIconUrl, setOverrideWalletIconUrl] = useState< string | undefined >(undefined); @@ -437,9 +438,6 @@ export const ConnectedWalletDetails: React.FC<{ { - setWrapperWallet(activeWallet); - }} /> )} @@ -452,9 +450,6 @@ export const ConnectedWalletDetails: React.FC<{ : wrapperWalletConfig.meta.name } wallet={wrapperWallet} - onSwitch={() => { - setWrapperWallet(undefined); - }} /> )} @@ -761,11 +756,9 @@ const DisconnectIconButton = /* @__PURE__ */ styled(IconButton)(() => { function WalletSwitcher({ wallet, - onSwitch, name, }: { wallet: WalletInstance; - onSwitch: () => void; name: string; }) { const walletContext = useWalletContext(); @@ -776,7 +769,6 @@ function WalletSwitcher({ type="button" onClick={() => { walletContext.setConnectedWallet(wallet); - onSwitch(); }} style={{ fontSize: fontSize.sm, From 7d7fb456dbb45ec61fc23859bdd87eac441c26ae Mon Sep 17 00:00:00 2001 From: Manan Tank Date: Fri, 8 Dec 2023 03:02:01 +0530 Subject: [PATCH 05/21] Fix smart + magic oauth autoconnect --- .../src/core/providers/thirdweb-wallet-provider.tsx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/react-core/src/core/providers/thirdweb-wallet-provider.tsx b/packages/react-core/src/core/providers/thirdweb-wallet-provider.tsx index 717e18de5e3..72b790bf0c2 100644 --- a/packages/react-core/src/core/providers/thirdweb-wallet-provider.tsx +++ b/packages/react-core/src/core/providers/thirdweb-wallet-provider.tsx @@ -562,7 +562,17 @@ export function ThirdwebWalletProvider( // this can happen when user clicks on "switch to personal wallet" and reloads the page // OR when user clicks on magic link social login if (walletObj.personalWallets && !_personalWalletInfo) { - shouldSetPersonalWalletAsActive = true; + // for magicLink social login - don't switch to personal wallet because smartWallet did not have a chance to connect because of page change + if ( + walletInfo.walletId === walletIds.magicLink && + walletInfo.connectParams && + "oauthProvider" in walletInfo.connectParams + ) { + shouldSetPersonalWalletAsActive = false; + } else { + shouldSetPersonalWalletAsActive = true; + } + // fix the connectParams by adding the personal wallet info _personalWalletInfo = { walletId: walletInfo.walletId, From 205f1766f1964a9581407c8bc715bf894252ddcf Mon Sep 17 00:00:00 2001 From: Manan Tank Date: Sat, 9 Dec 2023 00:48:26 +0530 Subject: [PATCH 06/21] minor ui updates --- packages/react/src/wallet/wallets/headlessConnectUI.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react/src/wallet/wallets/headlessConnectUI.tsx b/packages/react/src/wallet/wallets/headlessConnectUI.tsx index 78d575e6e0d..2354875f788 100644 --- a/packages/react/src/wallet/wallets/headlessConnectUI.tsx +++ b/packages/react/src/wallet/wallets/headlessConnectUI.tsx @@ -61,7 +61,7 @@ export const HeadlessConnectUI = ( ); } else { - content = ; + content = ; } return ( From ba0cedd10a1ceee9bf8a1c34150dfd7ce4c40d65 Mon Sep 17 00:00:00 2001 From: Manan Tank Date: Sat, 9 Dec 2023 00:54:26 +0530 Subject: [PATCH 07/21] fixes --- .../src/wallet/ConnectWallet/Modal/ConnectModal.tsx | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/react/src/wallet/ConnectWallet/Modal/ConnectModal.tsx b/packages/react/src/wallet/ConnectWallet/Modal/ConnectModal.tsx index 4cc5b4536c6..ad811b4367b 100644 --- a/packages/react/src/wallet/ConnectWallet/Modal/ConnectModal.tsx +++ b/packages/react/src/wallet/ConnectWallet/Modal/ConnectModal.tsx @@ -226,6 +226,8 @@ export const ConnectModal = () => { setPrevConnectionStatus(connectionStatus); }, [connectionStatus]); + const disconnect = useDisconnect(); + const wallet = useWallet(); const isWrapperConnected = !!wallet?.getPersonalWallet(); @@ -287,6 +289,16 @@ export const ConnectModal = () => { } setIsWalletModalOpen(value); + + if (!value) { + onModalUnmount(() => { + if (connectionStatus === "connecting") { + disconnect(); + } + + setScreen(initialScreen); + }); + } }} > Date: Sat, 9 Dec 2023 00:58:05 +0530 Subject: [PATCH 08/21] remove temp files --- .../thirdweb-wallet-provider copy.tsx | 682 ------------------ 1 file changed, 682 deletions(-) delete mode 100644 packages/react-core/src/core/providers/thirdweb-wallet-provider copy.tsx diff --git a/packages/react-core/src/core/providers/thirdweb-wallet-provider copy.tsx b/packages/react-core/src/core/providers/thirdweb-wallet-provider copy.tsx deleted file mode 100644 index 7a1423fbe02..00000000000 --- a/packages/react-core/src/core/providers/thirdweb-wallet-provider copy.tsx +++ /dev/null @@ -1,682 +0,0 @@ -import { DAppMetaData } from "../types/dAppMeta"; -import type { - ConnectionStatus, - WalletConfig, - WalletConnectParams, - WalletInstance, - WalletOptions, -} from "../types/wallet"; -import { Chain } from "@thirdweb-dev/chains"; -import { - AbstractClientWallet, - AsyncStorage, - ConnectParams, - CreateAsyncStorage, - SignerWallet, - walletIds, -} from "@thirdweb-dev/wallets"; -import { Signer } from "ethers"; -import { - createContext, - PropsWithChildren, - useCallback, - useContext, - useEffect, - useMemo, - useRef, - useState, -} from "react"; - -const LAST_CONNECTED_WALLET_STORAGE_KEY = "lastConnectedWallet"; - -let lastConnectedWalletStorage: AsyncStorage; - -type LastConnectedWalletInfo = { - walletId: string; - connectParams?: ConnectParams & { - personalWallet?: { - walletId: string; - connectParams?: ConnectParams; - }; - }; -}; - -type ConnectFnArgs = - // if second argument is optional - undefined extends WalletConnectParams - ? [ - wallet: WalletConfig, - connectParams?: NonNullable>, - ] - : // if second argument is required - [ - wallet: WalletConfig, - connectParams: NonNullable>, - ]; - -// maps wallet instance to it's wallet config -const walletInstanceToConfig: Map< - WalletInstance, - WalletConfig -> = new Map(); - -type WalletSetupData = { - chains: Chain[]; - dAppMeta?: DAppMetaData; - activeChain?: Chain; - clientId?: string; - chainToConnect?: Chain; -}; - -type ThirdwebWalletContextData = { - wallets: WalletConfig[]; - signer?: Signer; - activeWallet?: WalletInstance; - activeWalletConfig?: WalletConfig; - connect: (...args: ConnectFnArgs) => Promise; - disconnect: () => Promise; - connectionStatus: ConnectionStatus; - setConnectionStatus: (status: ConnectionStatus) => void; - createWalletInstance: ( - Wallet: WalletConfig, - ) => I; - createdWalletInstance?: WalletInstance; - createWalletStorage: CreateAsyncStorage; - switchChain: (chain: number) => Promise; - chainToConnect?: Chain; - activeChain: Chain; - setConnectedWallet: ( - wallet: WalletInstance, - params?: ConnectParams>, - ) => Promise; - /** - * Get wallet config object from wallet instance - */ - getWalletConfig: (walletInstance: WalletInstance) => WalletConfig | undefined; - activeChainSetExplicitly: boolean; - clientId?: string; - walletSetupData: WalletSetupData; -}; - -const ThirdwebWalletContext = /* @__PURE__ */ createContext< - ThirdwebWalletContextData | undefined ->(undefined); - -export function ThirdwebWalletProvider( - props: PropsWithChildren<{ - activeChain: Chain; - supportedWallets: WalletConfig[]; - shouldAutoConnect?: boolean; - createWalletStorage: CreateAsyncStorage; - dAppMeta?: DAppMetaData; - chains: Chain[]; - autoSwitch?: boolean; - autoConnectTimeout?: number; - clientId?: string; - activeChainSetExplicitly: boolean; - signerWallet?: WalletConfig; - }>, -) { - const autoConnectTimeout = props.autoConnectTimeout || 15000; - - // if autoSwitch is enabled - enforce connection to activeChain - const chainToConnect = props.autoSwitch ? props.activeChain : undefined; - - const walletSetupData: WalletSetupData = { - chains: props.chains, - dAppMeta: props.dAppMeta, - activeChain: props.activeChain, - clientId: props.clientId, - chainToConnect, - }; - - const { - signer, - connectionStatus, - setConnectionStatus, - activeWallet, - createdWalletInstance, - activeWalletConfig, - createWalletInstance, - setConnectedWallet, - switchChain, - connectWallet, - disconnectWallet, - } = useWalletConnectionSetup(walletSetupData); - - if (!lastConnectedWalletStorage) { - lastConnectedWalletStorage = - props.createWalletStorage("coordinatorStorage"); - } - - const autoConnectTriggered = useRef(false); - - // Auto Connect - useEffect(() => { - if (autoConnectTriggered.current) { - return; - } - - autoConnectTriggered.current = true; - - // do not auto connect if signerWallet is given - if (props.signerWallet) { - return; - } - - // if explicitly set to false, don't auto connect - // by default, auto connect - if (props.shouldAutoConnect === false) { - setConnectionStatus("disconnected"); - return; - } - - if (activeWallet) { - // there's already an active wallet, don't auto connect - return; - } - - if (connectionStatus !== "unknown") { - // only try to auto connect if we're in the unknown state - return; - } - - autoConnectTriggered.current = true; - - async function autoConnect() { - const walletInfo = await getLastConnectedWalletInfo(); - - if (!walletInfo) { - setConnectionStatus("disconnected"); - return; - } - - const walletObj = props.supportedWallets.find( - (W) => W.id === walletInfo.walletId, - ); - - if (!walletObj) { - // last connected wallet is no longer present in the supported wallets - setConnectionStatus("disconnected"); - return; - } - - let _personalWalletInfo = walletInfo.connectParams?.personalWallet; - - // when connecting to magicLink with social login, it redirects to other page - // before redirecting, we save the walletInfo to local storage so that we can auto connect after redirect back to current page - // when using smartWallet + magicLink combination - the walletInfo will only contain info about magicLink and not smartWallet because it was never connected - // so if smartWallet + magicLink combination is used, we need to connect magicLink first and then connect smartWallet - if ( - walletInfo.walletId === walletIds.magicLink && - walletInfo.connectParams && - "oauthProvider" in walletInfo.connectParams - ) { - // if the wallet requires a personal wallet (like smartWallet), but the saved data does not have it - if (walletObj.personalWallets && !_personalWalletInfo) { - // fix the connectParams by adding the personal wallet info - _personalWalletInfo = { - walletId: walletInfo.walletId, - connectParams: walletInfo.connectParams, - }; - } - } - - const personalWalletInfo = _personalWalletInfo; - - if (personalWalletInfo) { - const personalWallets = walletObj.personalWallets || []; - - const personalWalletObj = personalWallets.find( - (W) => W.id === personalWalletInfo.walletId, - ); - if (personalWalletObj) { - // create a personal wallet instance and auto connect it - const personalWalletInstance = - createWalletInstance(personalWalletObj); - - try { - await timeoutPromise( - personalWalletInstance.autoConnect( - personalWalletInfo.connectParams, - ), - { - ms: autoConnectTimeout, - message: autoConnectTimeoutErrorMessage, - }, - ); - } catch (e) { - console.error("Failed to auto connect personal wallet"); - console.error(e); - setConnectionStatus("disconnected"); - return; - } - - // set the personal wallet instance to the connectParams - walletInfo.connectParams = { - ...walletInfo.connectParams, - personalWallet: personalWalletInstance, - }; - } else { - // last used personal wallet is no longer present in the supported wallets - setConnectionStatus("disconnected"); - return; - } - } - - // create a wallet instance and auto connect it - const wallet = createWalletInstance(walletObj); - - try { - setConnectionStatus("connecting"); - await timeoutPromise(wallet.autoConnect(walletInfo.connectParams), { - ms: autoConnectTimeout, - message: autoConnectTimeoutErrorMessage, - }); - setConnectedWallet(wallet, walletInfo.connectParams, true); - } catch (e) { - console.error("Failed to auto connect wallet"); - console.error(e); - if ( - e instanceof Error && - e.message === autoConnectTimeoutErrorMessage - ) { - lastConnectedWalletStorage.removeItem( - LAST_CONNECTED_WALLET_STORAGE_KEY, - ); - } - setConnectionStatus("disconnected"); - } - } - - autoConnect(); - }, [ - createWalletInstance, - props.supportedWallets, - setConnectedWallet, - props.shouldAutoConnect, - activeWallet, - connectionStatus, - autoConnectTimeout, - props.signerWallet, - setConnectionStatus, - ]); - - // connect signerWallet immediately if it's passed - // and disconnect it if it's not passed - const signerConnected = useRef(); - useEffect(() => { - if (!props.signerWallet) { - if (signerConnected.current) { - disconnectWallet(); - signerConnected.current = undefined; - } - return; - } - - if (signerConnected.current === props.signerWallet) { - return; - } - - const wallet = createWalletInstance(props.signerWallet); - setConnectedWallet(wallet); - signerConnected.current = props.signerWallet; - }, [ - createWalletInstance, - props.supportedWallets, - setConnectedWallet, - props.signerWallet, - disconnectWallet, - ]); - - return ( - { - return walletInstanceToConfig.get(walletInstance); - }, - activeChainSetExplicitly: props.activeChainSetExplicitly, - clientId: props.clientId, - walletSetupData: walletSetupData, - }} - > - {props.children} - - ); -} - -export function useWalletConnectionSetup(data: WalletSetupData) { - const { chains, chainToConnect } = data; - const [signer, setSigner] = useState(undefined); - const [connectionStatus, setConnectionStatus] = - useState("unknown"); - const [connectedChainId, setConnectedChainId] = useState( - undefined, - ); - - const [activeWallet, setActiveWallet] = useState< - WalletInstance | undefined - >(); - - const [createdWalletInstance, setCreatedWalletInstance] = useState< - WalletInstance | undefined - >(); - - const [activeWalletConfig, setActiveWalletConfig] = useState< - WalletConfig | undefined - >(); - - const walletParams: WalletOptions = useMemo(() => { - return { - chains: data.chains, - dappMetadata: data.dAppMeta, - chain: data.activeChain || data.chains[0], - clientId: data.clientId, - }; - }, [data.chains, data.dAppMeta, data.activeChain, data.clientId]); - - const createWalletInstance = useCallback( - (walletConfig: WalletConfig): I => { - const walletInstance = walletConfig.create(walletParams); - if (walletInstance.walletId === walletIds.magicLink) { - // NOTE: removing this if statement causes the component to re-render - // Patch for magic link wallet in react native - // needed because we need to add a component to the view tree - // from the instance, right before calling connect. - // Check it out in RN's DappContextProvider. - setCreatedWalletInstance(walletInstance); - } - walletInstanceToConfig.set(walletInstance, walletConfig); - return walletInstance; - }, - [walletParams], - ); - - const setConnectedWallet = useCallback( - async ( - wallet: WalletInstance, - connectParams?: ConnectParams>, - isAutoConnect = false, - ) => { - setActiveWallet(wallet); - const walletConfig = walletInstanceToConfig.get(wallet); - if (!walletConfig) { - throw new Error( - "Wallet config not found for given wallet instance. Do not create a wallet instance manually - use the useCreateWalletInstance() hook instead", - ); - } - setActiveWalletConfig(walletConfig); - setConnectionStatus("connected"); - const _signer = await wallet.getSigner(); - setSigner(_signer); - - // it auto-connected, then the details is already saved in storage, no need to store again - if (isAutoConnect) { - return; - } - - // save to storage - - const walletInfo: LastConnectedWalletInfo = { - walletId: walletConfig.id, - connectParams: connectParams || wallet.getConnectParams(), - }; - - // if personal wallet exists, we need to replace the connectParams.personalWallet to a stringifiable version - const personalWallet = wallet.getPersonalWallet() as AbstractClientWallet; - const personalWalletConfig = walletInstanceToConfig.get(personalWallet); - - if (personalWallet && personalWalletConfig) { - walletInfo.connectParams = { - ...walletInfo.connectParams, - personalWallet: { - walletId: personalWalletConfig.id, - connectParams: personalWallet.getConnectParams(), - }, - }; - - saveLastConnectedWalletInfo(walletInfo); - } else { - saveLastConnectedWalletInfo(walletInfo); - } - }, - [], - ); - - const storeLastActiveChainId = useCallback(async (chainId: number) => { - const lastConnectedWallet = await lastConnectedWalletStorage.getItem( - LAST_CONNECTED_WALLET_STORAGE_KEY, - ); - - if (!lastConnectedWallet) { - return; - } - - try { - const parsedWallet = JSON.parse(lastConnectedWallet as string); - if (parsedWallet.connectParams) { - parsedWallet.connectParams.chainId = chainId; - } else { - parsedWallet.connectParams = { chainId }; - } - await lastConnectedWalletStorage.setItem( - LAST_CONNECTED_WALLET_STORAGE_KEY, - JSON.stringify(parsedWallet), - ); - } catch (error) { - console.error(`Error saving the last active chain: ${error}`); - } - }, []); - - const switchChain = useCallback( - async (chainId: number) => { - if (!activeWallet) { - throw new Error("No active wallet"); - } - - await activeWallet.switchChain(chainId); - const _signer = await activeWallet.getSigner(); - await storeLastActiveChainId(chainId); - - setSigner(_signer); - }, - [activeWallet, storeLastActiveChainId], - ); - - const connectWallet = useCallback( - async (...args: ConnectFnArgs): Promise => { - const [WalletObj, connectParams] = args; - - const _connectedParams = { - chainId: chainToConnect?.chainId, - ...(connectParams || {}), - }; - - const wallet = createWalletInstance(WalletObj); - setConnectionStatus("connecting"); - try { - // if magic is using social login - it will redirect the page - so need to save walletInfo before connecting - // TODO: find a better way to handle this - if (WalletObj.id === walletIds.magicLink) { - saveLastConnectedWalletInfo({ - walletId: WalletObj.id, - connectParams: _connectedParams, - }); - } - await wallet.connect(_connectedParams); - setConnectedWallet(wallet, _connectedParams); - } catch (e: any) { - console.error(`Error connecting to wallet: ${e}`); - setConnectionStatus("disconnected"); - throw e; - } - - return wallet; - }, - [createWalletInstance, setConnectedWallet, chainToConnect], - ); - - const onWalletDisconnect = useCallback(async () => { - await lastConnectedWalletStorage.removeItem( - LAST_CONNECTED_WALLET_STORAGE_KEY, - ); - setConnectionStatus("disconnected"); - setSigner(undefined); - setActiveWallet(undefined); - setActiveWalletConfig(undefined); - }, []); - - const disconnectWallet = useCallback(async () => { - // if disconnect is called before the wallet is connected - if (!activeWallet) { - onWalletDisconnect(); - return; - } - - const personalWallet = activeWallet.getPersonalWallet(); - await activeWallet.disconnect(); - - if (personalWallet) { - await (personalWallet as AbstractClientWallet)?.disconnect(); - } - - onWalletDisconnect(); - }, [activeWallet, onWalletDisconnect]); - - // if chains is updated, update the active wallet's chains - useEffect(() => { - if (activeWallet) { - activeWallet.updateChains(chains); - } - }, [activeWallet, chains]); - - // when wallet's network or account is changed using the extension, update UI - useEffect(() => { - if (!activeWallet) { - return; - } - - const updateChainId = () => { - activeWallet?.getChainId().then((_chainId) => { - setConnectedChainId(_chainId); - }); - }; - - const update = async () => { - updateChainId(); - const _signer = await activeWallet.getSigner(); - setSigner(_signer); - }; - - updateChainId(); - activeWallet.addListener("change", update); - activeWallet.addListener("disconnect", onWalletDisconnect); - - return () => { - activeWallet.removeListener("change", update); - activeWallet.removeListener("disconnect", onWalletDisconnect); - }; - }, [activeWallet, onWalletDisconnect, setSigner]); - - return { - signer, - connectionStatus, - setConnectionStatus, - activeWallet, - createdWalletInstance, - activeWalletConfig, - createWalletInstance, - setConnectedWallet, - switchChain, - connectWallet, - disconnectWallet, - connectedChainId, - }; -} - -export function useWalletContext() { - const ctx = useContext(ThirdwebWalletContext); - if (!ctx) { - throw new Error( - `useWalletContext() can only be used inside `, - ); - } - return ctx; -} - -async function getLastConnectedWalletInfo() { - const str = await lastConnectedWalletStorage.getItem( - LAST_CONNECTED_WALLET_STORAGE_KEY, - ); - if (!str) { - return null; - } - - try { - return JSON.parse(str) as LastConnectedWalletInfo; - } catch { - await lastConnectedWalletStorage.removeItem( - LAST_CONNECTED_WALLET_STORAGE_KEY, - ); - return null; - } -} - -async function saveLastConnectedWalletInfo( - walletInfo: LastConnectedWalletInfo, -) { - try { - await lastConnectedWalletStorage.setItem( - LAST_CONNECTED_WALLET_STORAGE_KEY, - JSON.stringify(walletInfo), - ); - } catch (e) { - console.error("Error saving the last connected wallet info", e); - } -} - -/** - * Timeout a promise with a given Error message if the promise does not resolve in given time - * - * @param promise - Promise to track for timeout - * @param option - timeout options - * @returns - */ -function timeoutPromise( - promise: Promise, - option: { ms: number; message: string }, -) { - return new Promise((resolve, reject) => { - const timeoutId = setTimeout(() => { - reject(new Error(option.message)); - }, option.ms); - - promise.then( - (res) => { - clearTimeout(timeoutId); - resolve(res); - }, - (err) => { - clearTimeout(timeoutId); - reject(err); - }, - ); - }); -} - -const autoConnectTimeoutErrorMessage = `Failed to Auto connect. Auto connect timed out. You can increase the timeout duration using the autoConnectTimeout prop on `; From 55f52647559db85078ee18400f46accaad69d911 Mon Sep 17 00:00:00 2001 From: Manan Tank Date: Mon, 11 Dec 2023 18:22:26 +0530 Subject: [PATCH 09/21] cleanup --- .../react-core/src/core/providers/thirdweb-wallet-provider.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/react-core/src/core/providers/thirdweb-wallet-provider.tsx b/packages/react-core/src/core/providers/thirdweb-wallet-provider.tsx index 72b790bf0c2..1997ae3fa51 100644 --- a/packages/react-core/src/core/providers/thirdweb-wallet-provider.tsx +++ b/packages/react-core/src/core/providers/thirdweb-wallet-provider.tsx @@ -1,4 +1,3 @@ -// update here import { DAppMetaData } from "../types/dAppMeta"; import type { ConnectionStatus, From 23d8024ffcad859b4ccf2253f046f9019990203c Mon Sep 17 00:00:00 2001 From: Manan Tank Date: Mon, 11 Dec 2023 18:58:36 +0530 Subject: [PATCH 10/21] update --- .../providers/thirdweb-wallet-provider.tsx | 78 +++++++++---------- .../src/wallet/wallets/safe/SelectAccount.tsx | 5 +- .../wallets/safe/SelectPersonalWallet.tsx | 2 +- .../src/wallet/wallets/safe/safeWallet.tsx | 2 +- .../wallets/smartWallet/smartWallet.tsx | 6 +- 5 files changed, 44 insertions(+), 49 deletions(-) diff --git a/packages/react-core/src/core/providers/thirdweb-wallet-provider.tsx b/packages/react-core/src/core/providers/thirdweb-wallet-provider.tsx index 1997ae3fa51..14c4b19728e 100644 --- a/packages/react-core/src/core/providers/thirdweb-wallet-provider.tsx +++ b/packages/react-core/src/core/providers/thirdweb-wallet-provider.tsx @@ -66,6 +66,8 @@ const walletInstanceToConfig: Map< /** * Maps a personal wallet instance to it's wrapper wallet instance ( like smartWallet or safeWallet ) to know it's "wrapper" wallet + * + * This is used to implement the "switch to personal wallet" and "switch to smart wallet" feature */ const personalWalletToWrapperWallet: Map = new Map(); @@ -140,10 +142,13 @@ export type WalletConnectionSetup = { ...args: ConnectFnArgs ) => Promise; disconnectWallet: () => Promise; - connectedChainId: number | undefined; - connectedAddress: string | undefined; + chainId: number | undefined; + address: string | undefined; }; +/** + * setup states and methods for wallet connection + */ export function useWalletConnectionSetup( data: WalletSetupData, initialValue: { @@ -169,22 +174,12 @@ export function useWalletConnectionSetup( WalletConfig | undefined >(); - const [connectedChainId, setConnectedChainId] = useState( - undefined, - ); + const [chainId, setChainId] = useState(undefined); - const [connectedAddress, setConnectedAddress] = useState( + const [walletAddress, setWalletAddress] = useState( undefined, ); - useEffect(() => { - if (!activeWallet) { - setConnectedAddress(undefined); - } else { - activeWallet.getAddress().then(setConnectedAddress); - } - }, [activeWallet]); - const walletParams: WalletOptions = useMemo(() => { return { chains: chains, @@ -211,13 +206,6 @@ export function useWalletConnectionSetup( [walletParams], ); - // if props.chains is updated, update the active wallet's chains - useEffect(() => { - if (activeWallet) { - activeWallet.updateChains(chains); - } - }, [activeWallet, chains]); - const setConnectedWallet = useCallback( async ( wallet: WalletInstance, @@ -271,7 +259,7 @@ export function useWalletConnectionSetup( [], ); - const storeLastActiveChainId = useCallback(async (chainId: number) => { + const storeLastActiveChainId = useCallback(async (_chainId: number) => { const lastConnectedWallet = await lastConnectedWalletStorage.getItem( LAST_CONNECTED_WALLET_STORAGE_KEY, ); @@ -283,9 +271,9 @@ export function useWalletConnectionSetup( try { const parsedWallet = JSON.parse(lastConnectedWallet as string); if (parsedWallet.connectParams) { - parsedWallet.connectParams.chainId = chainId; + parsedWallet.connectParams.chainId = _chainId; } else { - parsedWallet.connectParams = { chainId }; + parsedWallet.connectParams = { chainId: _chainId }; } await lastConnectedWalletStorage.setItem( LAST_CONNECTED_WALLET_STORAGE_KEY, @@ -297,14 +285,14 @@ export function useWalletConnectionSetup( }, []); const switchChain = useCallback( - async (chainId: number) => { + async (_chainId: number) => { if (!activeWallet) { throw new Error("No active wallet"); } - await activeWallet.switchChain(chainId); + await activeWallet.switchChain(_chainId); const _signer = await activeWallet.getSigner(); - await storeLastActiveChainId(chainId); + await storeLastActiveChainId(_chainId); setSigner(_signer); }, @@ -352,6 +340,8 @@ export function useWalletConnectionSetup( setSigner(undefined); setActiveWallet(undefined); setActiveWalletConfig(undefined); + setChainId(undefined); + setWalletAddress(undefined); }, []); const disconnectWallet = useCallback(async () => { @@ -371,27 +361,23 @@ export function useWalletConnectionSetup( onWalletDisconnect(); }, [activeWallet, onWalletDisconnect]); - // when wallet's network or account is changed using the extension, update UI + // handle wallet change event useEffect(() => { if (!activeWallet) { + setSigner(undefined); + setChainId(undefined); + setWalletAddress(undefined); return; } - const updateChainId = () => { - activeWallet?.getChainId().then((_chainId) => { - setConnectedChainId(_chainId); - }); - }; - const update = async () => { - updateChainId(); - const _signer = await activeWallet.getSigner(); - setSigner(_signer); + activeWallet.getSigner().then(setSigner); + activeWallet.getChainId().then(setChainId); + activeWallet.getAddress().then(setWalletAddress); }; - updateChainId(); + update(); activeWallet.addListener("change", update); - activeWallet.addListener("disconnect", onWalletDisconnect); return () => { @@ -400,6 +386,13 @@ export function useWalletConnectionSetup( }; }, [activeWallet, onWalletDisconnect]); + // if props.chains is updated, update the active wallet's chains + useEffect(() => { + if (activeWallet) { + activeWallet.updateChains(chains); + } + }, [activeWallet, chains]); + return { signer, connectionStatus, @@ -412,8 +405,8 @@ export function useWalletConnectionSetup( switchChain, connectWallet, disconnectWallet, - connectedChainId, - connectedAddress, + chainId, + address: walletAddress, }; } @@ -465,6 +458,9 @@ export function ThirdwebWalletProvider( connectionStatus: "disconnected", }); + /** + * This is used to know if auto connect is in progress + */ const [isAutoConnecting, setIsAutoConnecting] = useState(false); const [walletConnectHandler, setWalletConnectHandler] = diff --git a/packages/react/src/wallet/wallets/safe/SelectAccount.tsx b/packages/react/src/wallet/wallets/safe/SelectAccount.tsx index ac7955bcf84..95780bce96e 100644 --- a/packages/react/src/wallet/wallets/safe/SelectAccount.tsx +++ b/packages/react/src/wallet/wallets/safe/SelectAccount.tsx @@ -37,8 +37,7 @@ export const SelectAccount: React.FC<{ }> = (props) => { const locale = useTWLocale().wallets.safeWallet.accountDetailsScreen; const { personalWalletConnection } = useWalletContext(); - const { activeWallet, connectedChainId, switchChain } = - personalWalletConnection; + const { activeWallet, chainId, switchChain } = personalWalletConnection; const { connect, connectionStatus } = props; const [safeAddress, setSafeAddress] = useState(""); @@ -84,7 +83,7 @@ export const SelectAccount: React.FC<{ } }; - const mismatch = safeChainId !== -1 && connectedChainId !== safeChainId; + const mismatch = safeChainId !== -1 && chainId !== safeChainId; const isValidAddress = utils.isAddress(safeAddress); const disableNetworkSelection = supportedChains.length === 1; diff --git a/packages/react/src/wallet/wallets/safe/SelectPersonalWallet.tsx b/packages/react/src/wallet/wallets/safe/SelectPersonalWallet.tsx index 4700dab52fb..73294d41e4f 100644 --- a/packages/react/src/wallet/wallets/safe/SelectPersonalWallet.tsx +++ b/packages/react/src/wallet/wallets/safe/SelectPersonalWallet.tsx @@ -101,7 +101,7 @@ export const SelectpersonalWallet: React.FC<{ setConnectedWallet: personalWalletConnection.setConnectedWallet, setConnectionStatus: personalWalletConnection.setConnectionStatus, connectedWallet: personalWalletConnection.activeWallet, - connectedWalletAddress: personalWalletConnection.connectedAddress, + connectedWalletAddress: personalWalletConnection.address, }} /> diff --git a/packages/react/src/wallet/wallets/safe/safeWallet.tsx b/packages/react/src/wallet/wallets/safe/safeWallet.tsx index 2d73dfde4e1..8226be447be 100644 --- a/packages/react/src/wallet/wallets/safe/safeWallet.tsx +++ b/packages/react/src/wallet/wallets/safe/safeWallet.tsx @@ -137,7 +137,7 @@ export const SafeConnectUI = ( ); }, connectedWallet: personalWalletConnection.activeWallet, - connectedWalletAddress: personalWalletConnection.connectedAddress, + connectedWalletAddress: personalWalletConnection.address, }; if (personalWalletConfig.connectUI) { diff --git a/packages/react/src/wallet/wallets/smartWallet/smartWallet.tsx b/packages/react/src/wallet/wallets/smartWallet/smartWallet.tsx index c0c70e81dec..3254d85ae27 100644 --- a/packages/react/src/wallet/wallets/smartWallet/smartWallet.tsx +++ b/packages/react/src/wallet/wallets/smartWallet/smartWallet.tsx @@ -157,7 +157,7 @@ const SmartSelectUI = ( theme={props.theme} onLocallyConnected={props.onLocallyConnected} connectedWallet={personalWalletConnection.activeWallet} - connectedWalletAddress={personalWalletConnection.connectedAddress} + connectedWalletAddress={personalWalletConnection.address} /> ); }; @@ -204,7 +204,7 @@ export const SmartConnectUI = ( theme: props.theme, onLocallyConnected: props.onLocallyConnected, connectedWallet: personalWalletConnection.activeWallet, - connectedWalletAddress: personalWalletConnection.connectedAddress, + connectedWalletAddress: personalWalletConnection.address, }; if (personalWalletConfig.connectUI) { @@ -223,7 +223,7 @@ export const SmartConnectUI = ( smartWallet={walletConfig} personalWalletConfig={personalWalletConfig} personalWallet={personalWalletConnection.activeWallet} - personalWalletChainId={personalWalletConnection.connectedChainId || 1} + personalWalletChainId={personalWalletConnection.chainId || 1} switchChainPersonalWallet={personalWalletConnection.switchChain} /> ); From 5fb13a606fbc5d444060193cdc17ef6a5933471d Mon Sep 17 00:00:00 2001 From: Manan Tank Date: Mon, 11 Dec 2023 19:03:16 +0530 Subject: [PATCH 11/21] Fix wallet ids --- packages/wallets/src/evm/wallets/crypto-defi-wallet.ts | 2 +- packages/wallets/src/evm/wallets/onekey.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/wallets/src/evm/wallets/crypto-defi-wallet.ts b/packages/wallets/src/evm/wallets/crypto-defi-wallet.ts index 76e2d03414b..646466f6f8e 100644 --- a/packages/wallets/src/evm/wallets/crypto-defi-wallet.ts +++ b/packages/wallets/src/evm/wallets/crypto-defi-wallet.ts @@ -44,7 +44,7 @@ export class CryptoDefiWallet extends AbstractClientWallet OneKeyConnector?: OneKeyConnectorType; isInjected: boolean; - static id = walletIds.coreWallet as string; + static id = walletIds.oneKey as string; public get walletName() { - return "Core wallet" as const; + return "OneKey wallet" as const; } constructor(options: OneKeyOptions) { From e381f1f2ae1b66d798d38552ab7089b34a45534d Mon Sep 17 00:00:00 2001 From: Manan Tank Date: Mon, 11 Dec 2023 19:28:42 +0530 Subject: [PATCH 12/21] Fix EmbeddedWallet Email icon in Details button --- packages/react/src/wallet/ConnectWallet/Details.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/react/src/wallet/ConnectWallet/Details.tsx b/packages/react/src/wallet/ConnectWallet/Details.tsx index 8178b89e498..4e7b81b4fd1 100644 --- a/packages/react/src/wallet/ConnectWallet/Details.tsx +++ b/packages/react/src/wallet/ConnectWallet/Details.tsx @@ -168,6 +168,8 @@ export const ConnectedWalletDetails: React.FC<{ setOverrideWalletIconUrl(googleIconUri); } else if (auth === "facebook") { setOverrideWalletIconUrl(facebookIconUri); + } else { + setOverrideWalletIconUrl(undefined); } }); } else if (activeWallet.walletId === walletIds.smartWallet) { From f3c29d51dc183b40d98f26e90b2259832ab4c559 Mon Sep 17 00:00:00 2001 From: Manan Tank Date: Mon, 11 Dec 2023 21:55:41 +0530 Subject: [PATCH 13/21] updates --- .changeset/tidy-falcons-listen.md | 33 ++- .../providers/thirdweb-wallet-provider.tsx | 2 +- packages/react-core/src/core/types/wallet.ts | 197 ++++++++++-------- packages/react-core/src/evm/index.ts | 2 - .../ConnectWallet/Modal/ConnectEmbed.tsx | 2 +- .../wallets/smartWallet/smartWallet.tsx | 2 - .../walletConnect/WalletConnectScan.tsx | 5 +- 7 files changed, 147 insertions(+), 96 deletions(-) diff --git a/.changeset/tidy-falcons-listen.md b/.changeset/tidy-falcons-listen.md index a85eed6a306..b8d7f438205 100644 --- a/.changeset/tidy-falcons-listen.md +++ b/.changeset/tidy-falcons-listen.md @@ -1,8 +1,33 @@ --- -"@thirdweb-dev/react-native": patch -"@thirdweb-dev/react-core": patch +"@thirdweb-dev/react-native": minor +"@thirdweb-dev/react-core": minor "@thirdweb-dev/wallets": patch -"@thirdweb-dev/react": patch +"@thirdweb-dev/react": minor --- -Fix double connection issue, Add ConnectEmbed component +- Fix double connection issue when Connecting a Safe / Smart Wallet. Now the personal wallet will not be set as the active wallet - only the final wallet will be set as the active wallet. This fixes the issue of hooks like `useWallet`, `useAddress`, `useConnectionStatus` etc showing the wrong wallet / address / connection status for a brief moment when connecting a Safe / Smart Wallet. + +- Add `ConnectEmbed` component and `useShowConnectEmbed` hook to allow for embedding the ConnectWallet's Modal directly into the page. +- `useShowConnectEmbed` returns `true`` if the ``should be rendered. It returns`true`` if either one of the following conditions are met: + + - the wallet is NOT connected + - the wallet IS connected but the user is NOT signed in and `auth` is required ( loginOptional is NOT set to false ) + + ```tsx + function Example() { + const loginOptional = false; + const showConnectEmbed = useShowConnectEmbed(loginOptional); + + return ( +
+ +
+ ); + } + ``` + +- Show "Disconnect Wallet" option in "Sign in" Screen and don't disconnect wallet instead of disconnecting the wallet when "Sign in" screen is dismissed by closing the modal. This makes this screen reusable for both ConnectWallet and ConnectEmbed components and also improves the user experience. diff --git a/packages/react-core/src/core/providers/thirdweb-wallet-provider.tsx b/packages/react-core/src/core/providers/thirdweb-wallet-provider.tsx index 14c4b19728e..742df0e82f1 100644 --- a/packages/react-core/src/core/providers/thirdweb-wallet-provider.tsx +++ b/packages/react-core/src/core/providers/thirdweb-wallet-provider.tsx @@ -149,7 +149,7 @@ export type WalletConnectionSetup = { /** * setup states and methods for wallet connection */ -export function useWalletConnectionSetup( +function useWalletConnectionSetup( data: WalletSetupData, initialValue: { connectionStatus: ConnectionStatus; diff --git a/packages/react-core/src/core/types/wallet.ts b/packages/react-core/src/core/types/wallet.ts index aa56a4a3b84..e410ceca202 100644 --- a/packages/react-core/src/core/types/wallet.ts +++ b/packages/react-core/src/core/types/wallet.ts @@ -106,44 +106,7 @@ export type ConnectUIProps = { theme: "dark" | "light"; /** - * `WalletConfig` object of your wallet - * - * you can use this to connect to your wallet - * - * ### 1. Using `useConnect` hook - * ```ts - * const connect = useConnect(); - * - * // call this function to connect to your wallet - * async function handleConnect() { - * await connect(walletConfig, options); - * } - * - * ``` - * - * OR - * - * ### 2. Manually creating wallet instance and connecting - * ```ts - * const createWalletInstance = useCreateWalletInstance(); - * const setConnectedWallet = useSetConnectedWallet(); - * const setConnectionStatus = useSetConnectionStatus(); - * - * // call this function to connect to your wallet - * async function handleConnect() { - * // create instance - * const walletInstance = createWalletInstance(walletConfig); - * // connect wallet - * setConnectionStatus('connecting); - * try { - * await walletInstance.connect(options); - * // set connected wallet - * setConnectedWallet(walletInstance); - * } catch { - * setConnectionStatus('disconnected'); - * } - * } - * ``` + * `WalletConfig` object of the wallet */ walletConfig: WalletConfig; @@ -159,7 +122,7 @@ export type ConnectUIProps = { setSelectionData: (data: any) => void; /** - * List of all supported wallets including your wallet. + * Array of supported wallets including this wallet. */ supportedWallets: WalletConfig[]; @@ -171,8 +134,10 @@ export type ConnectUIProps = { modalSize: "compact" | "wide"; /** - * Called when the wallet is connected but it's - * part of another wallet's connection flow. + * Called when the wallet is connected but it's part of another wallet's connection flow. + * + * This is only defined in React Native + * * @param walletInstance - the instance of the connected wallet */ onLocallyConnected?: (walletInstance: WalletInstance) => void; @@ -180,24 +145,37 @@ export type ConnectUIProps = { /** * connect wallet * + * @example + * ```tsx + * const { connect } = props; + * + * async function handleConnect() { + * const wallet = await connect(someOptions); + * console.log('connected to', wallet); + * } + * ``` + * * If you need more control over the connection process, you can manually create wallet instance and call the `connect` method of the wallet instance instead and use `setConnectedWallet` and `setConnectionStatus` to set the connected wallet and connection status. */ connect: (...args: ConnectArgs) => Promise; /** * Set the connection status of the wallet. - * This is only relevant if you are manually creating wallet instance and calling the `wallet.connect` method. + * This is only relevant if you are manually creating wallet instance and calling the `wallet.connect` method. If you are using the `connect` function, this is done automatically. * * @example * ```ts - * const createWalletInstance = useCreateWalletInstance(); - * const wallet = createWalletInstance(walletConfig); - * setConnectionStatus('connecting'); - * try { - * await wallet.connect(options); - * setConnectedWallet(wallet); - * } catch { - * setConnectionStatus('disconnected'); + * const { createWalletInstance, setConnectionStatus, setConnectedWallet } = props; + * + * async function handleConnect() { + * const wallet = createWalletInstance(); + * setConnectionStatus('connecting'); // <-- + * try { + * await wallet.connect(someOptions); + * setConnectedWallet(wallet); + * } catch { + * setConnectionStatus('disconnected'); // <-- + * } * } * ``` */ @@ -209,32 +187,51 @@ export type ConnectUIProps = { connectionStatus: ConnectionStatus; /** - * Set the wallet instance as connected in thirdweb context - * This is only relevant if you are manually creating wallet instance and calling the `wallet.connect` method. + * Set a wallet instance as connected in thirdweb context + * This is only relevant if you are manually creating wallet instance and calling the `wallet.connect` method. If you are using the `connect` function, this is done automatically. * * @example * ```ts - * const createWalletInstance = useCreateWalletInstance(); - * const wallet = createWalletInstance(walletConfig); - * setConnectionStatus('connecting'); - * try { - * await wallet.connect(options); - * setConnectedWallet(wallet); - * } catch { - * setConnectionStatus('disconnected'); + * const { createWalletInstance, setConnectionStatus, setConnectedWallet } = props; + * + * async function handleConnect() { + * const wallet = createWalletInstance(); + * setConnectionStatus('connecting'); + * try { + * await wallet.connect(someOptions); + * setConnectedWallet(wallet); // <-- + * } catch { + * setConnectionStatus('disconnected'); + * } * } * ``` */ setConnectedWallet: (walletInstance: I) => void; /** - * Create an instance of the wallet + * Create an instance of the wallet. This is only relevant if you are manually creating wallet instance and calling the `wallet.connect` method. If you are using the `connect` function, this is done automatically. + * + * @example + * ```ts + * const { createWalletInstance, setConnectionStatus, setConnectedWallet } = props; + * + * async function handleConnect() { + * const wallet = createWalletInstance(); // <-- + * setConnectionStatus('connecting'); + * try { + * await wallet.connect(someOptions); + * setConnectedWallet(wallet); + * } catch { + * setConnectionStatus('disconnected'); + * } + * } + * ``` */ createWalletInstance: () => I; /** * Connected wallet instance - * This is set by if `connect` succeeds or it set manually by the `setConnectedWallet` function + * This is set by `connect` if connection succeeds or it can be set manually by using `setConnectedWallet` */ connectedWallet?: I; @@ -254,8 +251,6 @@ export type SelectUIProps = { /** * `WalletConfig` object of your wallet - * - * you can use this get metadata of your wallet by doing `walletConfig.meta` */ walletConfig: WalletConfig; @@ -289,24 +284,37 @@ export type SelectUIProps = { /** * connect wallet * + * @example + * ```tsx + * const { connect } = props; + * + * async function handleConnect() { + * const wallet = await connect(someOptions); + * console.log('connected to', wallet); + * } + * ``` + * * If you need more control over the connection process, you can manually create wallet instance and call the `connect` method of the wallet instance instead and use `setConnectedWallet` and `setConnectionStatus` to set the connected wallet and connection status. */ connect: (...args: ConnectArgs) => Promise; /** * Set the connection status of the wallet. - * This is only relevant if you are manually creating wallet instance and calling the `wallet.connect` method. + * This is only relevant if you are manually creating wallet instance and calling the `wallet.connect` method. If you are using the `connect` function, this is done automatically. * * @example * ```ts - * const createWalletInstance = useCreateWalletInstance(); - * const wallet = createWalletInstance(walletConfig); - * setConnectionStatus('connecting'); - * try { - * await wallet.connect(options); - * setConnectedWallet(wallet); - * } catch { - * setConnectionStatus('disconnected'); + * const { createWalletInstance, setConnectionStatus, setConnectedWallet } = props; + * + * async function handleConnect() { + * const wallet = createWalletInstance(); + * setConnectionStatus('connecting'); // <-- + * try { + * await wallet.connect(someOptions); + * setConnectedWallet(wallet); + * } catch { + * setConnectionStatus('disconnected'); // <-- + * } * } * ``` */ @@ -318,32 +326,51 @@ export type SelectUIProps = { connectionStatus: ConnectionStatus; /** - * Set the wallet instance as connected in thirdweb context - * This is only relevant if you are manually creating wallet instance and calling the `wallet.connect` method. + * Set a wallet instance as connected in thirdweb context + * This is only relevant if you are manually creating wallet instance and calling the `wallet.connect` method. If you are using the `connect` function, this is done automatically. * * @example * ```ts - * const createWalletInstance = useCreateWalletInstance(); - * const wallet = createWalletInstance(walletConfig); - * setConnectionStatus('connecting'); - * try { - * await wallet.connect(options); - * setConnectedWallet(wallet); - * } catch { - * setConnectionStatus('disconnected'); + * const { createWalletInstance, setConnectionStatus, setConnectedWallet } = props; + * + * async function handleConnect() { + * const wallet = createWalletInstance(); + * setConnectionStatus('connecting'); + * try { + * await wallet.connect(someOptions); + * setConnectedWallet(wallet); // <-- + * } catch { + * setConnectionStatus('disconnected'); + * } * } * ``` */ setConnectedWallet: (walletInstance: I) => void; /** - * Create an instance of the wallet + * Create an instance of the wallet. This is only relevant if you are manually creating wallet instance and calling the `wallet.connect` method. If you are using the `connect` function, this is done automatically. + * + * @example + * ```ts + * const { createWalletInstance, setConnectionStatus, setConnectedWallet } = props; + * + * async function handleConnect() { + * const wallet = createWalletInstance(); // <-- + * setConnectionStatus('connecting'); + * try { + * await wallet.connect(someOptions); + * setConnectedWallet(wallet); + * } catch { + * setConnectionStatus('disconnected'); + * } + * } + * ``` */ createWalletInstance: () => I; /** * Connected wallet instance - * This is set by if `connect` succeeds or it set manually by the `setConnectedWallet` function + * This is set by `connect` if connection succeeds or it can be set manually by using `setConnectedWallet` */ connectedWallet?: I; diff --git a/packages/react-core/src/evm/index.ts b/packages/react-core/src/evm/index.ts index aa26b8468bd..be6245bd73b 100644 --- a/packages/react-core/src/evm/index.ts +++ b/packages/react-core/src/evm/index.ts @@ -7,8 +7,6 @@ export type { ThirdwebProviderCoreProps } from "../core/providers/thirdweb-provi // constants export { __DEV__ } from "../core/constants/runtime"; -export { useWalletConnectionSetup } from "../core/providers/thirdweb-wallet-provider"; - // wallet hooks export { useWallet, diff --git a/packages/react/src/wallet/ConnectWallet/Modal/ConnectEmbed.tsx b/packages/react/src/wallet/ConnectWallet/Modal/ConnectEmbed.tsx index 6406efa6c8c..7fa59fa11fa 100644 --- a/packages/react/src/wallet/ConnectWallet/Modal/ConnectEmbed.tsx +++ b/packages/react/src/wallet/ConnectWallet/Modal/ConnectEmbed.tsx @@ -86,7 +86,7 @@ function useSignInRequired(loginOptional?: boolean) { } /** - * Returns true if the `` should be rendered. + * Returns `true` if the `` should be rendered. * It returns true if either one of the following conditions are met: * - the wallet is not connected * - the wallet is connected but the user is not signed in and `auth` is required ( loginOptional is not set to false ) diff --git a/packages/react/src/wallet/wallets/smartWallet/smartWallet.tsx b/packages/react/src/wallet/wallets/smartWallet/smartWallet.tsx index 3254d85ae27..f379a8a1457 100644 --- a/packages/react/src/wallet/wallets/smartWallet/smartWallet.tsx +++ b/packages/react/src/wallet/wallets/smartWallet/smartWallet.tsx @@ -155,7 +155,6 @@ const SmartSelectUI = ( connectionStatus={personalWalletConnection.connectionStatus} supportedWallets={props.supportedWallets} theme={props.theme} - onLocallyConnected={props.onLocallyConnected} connectedWallet={personalWalletConnection.activeWallet} connectedWalletAddress={personalWalletConnection.address} /> @@ -202,7 +201,6 @@ export const SmartConnectUI = ( show: props.show, supportedWallets: props.supportedWallets, theme: props.theme, - onLocallyConnected: props.onLocallyConnected, connectedWallet: personalWalletConnection.activeWallet, connectedWalletAddress: personalWalletConnection.address, }; diff --git a/packages/react/src/wallet/wallets/walletConnect/WalletConnectScan.tsx b/packages/react/src/wallet/wallets/walletConnect/WalletConnectScan.tsx index 08faca80d74..88ff85641d3 100644 --- a/packages/react/src/wallet/wallets/walletConnect/WalletConnectScan.tsx +++ b/packages/react/src/wallet/wallets/walletConnect/WalletConnectScan.tsx @@ -69,7 +69,10 @@ export const WalletConnectScan: React.FC<{ return ( - + From 1e6f4e336bc44962cd094e0dc338f502511eca3f Mon Sep 17 00:00:00 2001 From: Manan Tank Date: Mon, 11 Dec 2023 22:10:43 +0530 Subject: [PATCH 14/21] Fix ConnectEmbed theme default --- .../ConnectWallet/Modal/ConnectEmbed.tsx | 30 +++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/packages/react/src/wallet/ConnectWallet/Modal/ConnectEmbed.tsx b/packages/react/src/wallet/ConnectWallet/Modal/ConnectEmbed.tsx index 7fa59fa11fa..064647cf52d 100644 --- a/packages/react/src/wallet/ConnectWallet/Modal/ConnectEmbed.tsx +++ b/packages/react/src/wallet/ConnectWallet/Modal/ConnectEmbed.tsx @@ -30,9 +30,35 @@ import { Container } from "../../../components/basic"; import { Spinner } from "../../../components/Spinner"; export type ConnectEmbedProps = { + /** + * Class name to be added to the root element of ConnectEmbed + */ className?: string; + + /** + * theme for the ConnectEmbed + * + * If a theme is set on the `ThirdWebProvider` component, it will be used as the default theme for all thirdweb components, else the default will be "dark" + * + * theme can be set to either "dark" or "light" or a custom theme object. + * + * You can also import `lightTheme` or `darkTheme` functions from `@thirdweb-dev/react` to use the default themes as base and overrides parts of it. + * + * @example + * ```ts + * import { lightTheme } from "@thirdweb-dev/react"; + * const customTheme = lightTheme({ + * colors: { + * modalBg: 'red' + * } + * }) + * ``` + */ theme?: "dark" | "light" | Theme; + /** + * CSS styles to be applied to the root element of ConnectEmbed + */ style?: React.CSSProperties; /** @@ -107,7 +133,6 @@ export const ConnectEmbed = (props: ConnectEmbedProps) => { const loginOptional = props.auth?.loginOptional; const requiresSignIn = useSignInRequired(loginOptional); const show = useShowConnectEmbed(loginOptional); - const { screen, setScreen, initialScreen } = useScreen(); // if showing main screen but signIn is required, switch to signIn screen @@ -145,6 +170,7 @@ const ConnectEmbedContent = ( const modalSize = "compact" as const; const connectionStatus = useConnectionStatus(); const { isAutoConnecting } = useWalletContext(); + const contextTheme = useCustomTheme(); let content = ( Date: Mon, 11 Dec 2023 22:21:45 +0530 Subject: [PATCH 15/21] update --- .changeset/tidy-falcons-listen.md | 16 ++-- .../ConnectWallet/Modal/ConnectEmbed.tsx | 86 +++++++++++++++---- 2 files changed, 80 insertions(+), 22 deletions(-) diff --git a/.changeset/tidy-falcons-listen.md b/.changeset/tidy-falcons-listen.md index b8d7f438205..e8b324aa7e3 100644 --- a/.changeset/tidy-falcons-listen.md +++ b/.changeset/tidy-falcons-listen.md @@ -18,14 +18,16 @@ const loginOptional = false; const showConnectEmbed = useShowConnectEmbed(loginOptional); + if (!showConnectEmbed) { + return
Wallet is connected
; + } + return ( -
- -
+ ); } ``` diff --git a/packages/react/src/wallet/ConnectWallet/Modal/ConnectEmbed.tsx b/packages/react/src/wallet/ConnectWallet/Modal/ConnectEmbed.tsx index 064647cf52d..c923c93951c 100644 --- a/packages/react/src/wallet/ConnectWallet/Modal/ConnectEmbed.tsx +++ b/packages/react/src/wallet/ConnectWallet/Modal/ConnectEmbed.tsx @@ -117,6 +117,28 @@ function useSignInRequired(loginOptional?: boolean) { * - the wallet is not connected * - the wallet is connected but the user is not signed in and `auth` is required ( loginOptional is not set to false ) * + * @example + * ```tsx + * function Example() { + * const loginOptional = false; + * const showConnectEmbed = useShowConnectEmbed(loginOptional); + * + * if (!showConnectEmbed) { + * return
Wallet is connected
+ * } + * + * return ( + *
+ * + *
+ * ); + * } + *``` + * * @param loginOptional - * Specify whether the `` you want to render has auth enabled or not. * If not specified, it is assumed to be `false` ( login is required ) @@ -129,6 +151,37 @@ export function useShowConnectEmbed(loginOptional?: boolean) { return connectionStatus !== "connected" || signInRequired; } +/** + * A component that allows the user to connect their wallet. + * + * It only renders UI if either one of the following conditions are met: + * - wallet is not connected + * - wallet is connected but the user is not signed in and `auth` is required ( loginOptional is not set to false ) + * + * `ConnectEmbed` uses the `useShowConnectEmbed` hook internally to determine if it should be rendered or not. You can also use this hook to determine if you should render something else instead of `ConnectEmbed` + * + * @example + * ```tsx + * function Example() { + * const loginOptional = false; + * const showConnectEmbed = useShowConnectEmbed(loginOptional); + * + * if (!showConnectEmbed) { + * return
Wallet is connected
+ * } + * + * return ( + *
+ * + *
+ * ); + * } + *``` + */ export const ConnectEmbed = (props: ConnectEmbedProps) => { const loginOptional = props.auth?.loginOptional; const requiresSignIn = useSignInRequired(loginOptional); @@ -172,22 +225,9 @@ const ConnectEmbedContent = ( const { isAutoConnecting } = useWalletContext(); const contextTheme = useCustomTheme(); - let content = ( - { - // no op - }} - onShow={() => { - // no op - }} - /> - ); + let content = null; + // show spinner on page load and during auto connecting a wallet if (isAutoConnecting || connectionStatus === "unknown") { content = ( ); + } else { + content = ( + { + // no op + }} + onShow={() => { + // no op + }} + /> + ); } const walletUIStatesProps = { From 5e2e37b2ae7fe33e130340e94df7e0ed1f613937 Mon Sep 17 00:00:00 2001 From: Manan Tank Date: Mon, 11 Dec 2023 22:28:13 +0530 Subject: [PATCH 16/21] useCallback --- .../react/src/wallet/ConnectWallet/Modal/ConnectModal.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/react/src/wallet/ConnectWallet/Modal/ConnectModal.tsx b/packages/react/src/wallet/ConnectWallet/Modal/ConnectModal.tsx index ad811b4367b..7526fcf5f94 100644 --- a/packages/react/src/wallet/ConnectWallet/Modal/ConnectModal.tsx +++ b/packages/react/src/wallet/ConnectWallet/Modal/ConnectModal.tsx @@ -262,6 +262,9 @@ export const ConnectModal = () => { } }, [isWalletModalOpen, setIsWalletModalOpen, screen]); + const onHide = useCallback(() => setHideModal(true), []); + const onShow = useCallback(() => setHideModal(false), []); + // if wallet is suddenly disconnected when showing the sign in screen, close the modal and reset the screen useEffect(() => { if (isWalletModalOpen && screen === reservedScreens.signIn && !wallet) { @@ -305,8 +308,8 @@ export const ConnectModal = () => { initialScreen={initialScreen} screen={screen} setScreen={setScreen} - onHide={() => setHideModal(true)} - onShow={() => setHideModal(false)} + onHide={onHide} + onShow={onShow} isOpen={isWalletModalOpen} onClose={closeModal} /> From 738e181192d825527a9d6da1a83457e44954c923 Mon Sep 17 00:00:00 2001 From: Manan Tank Date: Wed, 13 Dec 2023 01:45:20 +0530 Subject: [PATCH 17/21] Fix useSignInRequired --- packages/react/src/wallet/ConnectWallet/Modal/ConnectEmbed.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react/src/wallet/ConnectWallet/Modal/ConnectEmbed.tsx b/packages/react/src/wallet/ConnectWallet/Modal/ConnectEmbed.tsx index c923c93951c..4a7fcd3d9b3 100644 --- a/packages/react/src/wallet/ConnectWallet/Modal/ConnectEmbed.tsx +++ b/packages/react/src/wallet/ConnectWallet/Modal/ConnectEmbed.tsx @@ -102,7 +102,7 @@ function useSignInRequired(loginOptional?: boolean) { const { user } = useUser(); const authConfig = useThirdwebAuthContext(); - if (loginOptional === false) { + if (loginOptional === true) { return false; } From ddd4a374ef9d33afa45bd82faed7fdece9037268 Mon Sep 17 00:00:00 2001 From: ikethirdweb Date: Mon, 8 Jan 2024 15:26:54 -0500 Subject: [PATCH 18/21] ReactNative --- .../providers/thirdweb-wallet-provider.tsx | 2 - packages/react-core/src/core/types/wallet.ts | 11 ++ packages/react-core/src/evm/index.ts | 1 - .../SmartWallet/SmartWalletFlow.tsx | 175 ++++++++++-------- .../wallets/embedded/EmbeddedConnectionUI.tsx | 13 +- .../embedded/EmbeddedSocialConnection.tsx | 18 +- .../src/evm/wallets/wallets/smart-wallet.tsx | 21 ++- .../wallet-connect/WalletConnectUI.tsx | 16 +- 8 files changed, 146 insertions(+), 111 deletions(-) diff --git a/packages/react-core/src/core/providers/thirdweb-wallet-provider.tsx b/packages/react-core/src/core/providers/thirdweb-wallet-provider.tsx index e19b699a7cd..c7078c73301 100644 --- a/packages/react-core/src/core/providers/thirdweb-wallet-provider.tsx +++ b/packages/react-core/src/core/providers/thirdweb-wallet-provider.tsx @@ -1,8 +1,6 @@ import { DAppMetaData } from "../types/dAppMeta"; import type { - ConnectionStatus, WalletConfig, - WalletConnectParams, WalletInstance, WalletOptions, } from "../types/wallet"; diff --git a/packages/react-core/src/core/types/wallet.ts b/packages/react-core/src/core/types/wallet.ts index 01c92d08a27..c1581b160f6 100644 --- a/packages/react-core/src/core/types/wallet.ts +++ b/packages/react-core/src/core/types/wallet.ts @@ -61,6 +61,17 @@ export type WalletConfig = { isHeadless?: boolean; }; +export type NonNullable = T extends null | undefined ? never : T; +export type WalletConnectParams = Parameters< + I["connect"] +>[0]; + +type ConnectArgs = + // if second argument is optional + undefined extends WalletConnectParams + ? [connectParams?: NonNullable>] + : [connectParams: NonNullable>]; + /** * @wallet */ diff --git a/packages/react-core/src/evm/index.ts b/packages/react-core/src/evm/index.ts index 707e29dd2e8..6d1eeebf48e 100644 --- a/packages/react-core/src/evm/index.ts +++ b/packages/react-core/src/evm/index.ts @@ -44,7 +44,6 @@ export type { RequiredParam } from "../core/query-utils/required-param"; export type { WalletConfig, ConnectUIProps, - ConnectionStatus, SelectUIProps, WalletInstance, WalletOptions, diff --git a/packages/react-native/src/evm/components/ConnectWalletFlow/SmartWallet/SmartWalletFlow.tsx b/packages/react-native/src/evm/components/ConnectWalletFlow/SmartWallet/SmartWalletFlow.tsx index 90d4c32ab26..24ad5ff5f30 100644 --- a/packages/react-native/src/evm/components/ConnectWalletFlow/SmartWallet/SmartWalletFlow.tsx +++ b/packages/react-native/src/evm/components/ConnectWalletFlow/SmartWallet/SmartWalletFlow.tsx @@ -1,11 +1,9 @@ -import { useCallback, useEffect, useState } from "react"; +import { useCallback, useState } from "react"; import { ConnectUIProps, WalletConfig, WalletInstance, - useAddress, useConnect, - useCreateWalletInstance, useWalletContext, } from "@thirdweb-dev/react-core"; import { ActivityIndicator, StyleSheet, View } from "react-native"; @@ -28,7 +26,12 @@ export const SmartWalletFlow = ({ personalWalletConfig, hide, ...props -}: ConnectUIProps & { personalWalletConfig: WalletConfig }) => { +}: ConnectUIProps & { + smartWalletConfig: WalletConfig; + personalWalletConfig: WalletConfig; + personalWallet?: WalletInstance; + personalWalletChainId: number; +}) => { const l = useLocale(); const theme = useGlobalTheme(); const [connectedPersonalWallet, setConnectedPersonalWallet] = @@ -37,25 +40,15 @@ export const SmartWalletFlow = ({ number | undefined >(); const [switchingNetwork, setSwitchingNetwork] = useState(false); - const createWalletInstance = useCreateWalletInstance(); const connect = useConnect(); const targetChain = useWalletContext().activeChain; - // TEMP BUILD FIX - const address = useAddress(); - const { - setConnectedWallet, - setConnectionStatus, - connectionStatus, - activeWallet, - } = useWalletContext(); + const { personalWalletConnection } = useWalletContext(); const mismatch = personalWalletChainId ? personalWalletChainId !== targetChain.chainId : false; - const PersonalWalletConfigUI = personalWalletConfig.connectUI; - const connectSmartWallet = useCallback( async (personalWallet: WalletInstance) => { const eoaWalletChainId = await personalWallet.getChainId(); @@ -74,25 +67,26 @@ export const SmartWalletFlow = ({ const connectPersonalWallet = useCallback( async (wallet: WalletConfig) => { - const walletInstance = createWalletInstance(wallet); + const walletInstance = + personalWalletConnection.createWalletInstance(wallet); await walletInstance.connect(); setConnectedPersonalWallet(walletInstance); connectSmartWallet(walletInstance); }, - [connectSmartWallet, createWalletInstance], + [connectSmartWallet, personalWalletConnection], ); - useEffect(() => { - if (walletConfig.personalWallets?.[0] && !PersonalWalletConfigUI) { - connectPersonalWallet(walletConfig.personalWallets[0]); - } - }, [ - PersonalWalletConfigUI, - connectPersonalWallet, - walletConfig.personalWallets, - ]); + // useEffect(() => { + // if (walletConfig.personalWallets?.[0] && !PersonalWalletConfigUI) { + // connectPersonalWallet(walletConfig.personalWallets[0]); + // } + // }, [ + // PersonalWalletConfigUI, + // connectPersonalWallet, + // walletConfig.personalWallets, + // ]); const onConnectingClosePress = () => { connectedPersonalWallet?.disconnect(); @@ -121,65 +115,88 @@ export const SmartWalletFlow = ({ setPersonalWalletChaindId(undefined); }; - const onPersonalWalletConnected = useCallback( - (walletInstance: WalletInstance) => { - setConnectedPersonalWallet(walletInstance); - connectSmartWallet(walletInstance); - }, - [connectSmartWallet], - ); + // const onPersonalWalletConnected = useCallback( + // (walletInstance: WalletInstance) => { + // setConnectedPersonalWallet(walletInstance); + // connectSmartWallet(walletInstance); + // }, + // [connectSmartWallet], + // ); + + if (!personalWalletConnection.activeWallet) { + const _props: ConnectUIProps = { + walletConfig: personalWalletConfig, + connected: connected, + connect(options) { + return personalWalletConnection.connectWallet( + personalWalletConfig, + options, + ); + }, + setConnectedWallet(wallet) { + personalWalletConnection.setConnectedWallet(wallet); + }, + setConnectionStatus(status) { + personalWalletConnection.setConnectionStatus(status); + }, + createWalletInstance: () => { + return personalWalletConnection.createWalletInstance( + personalWalletConfig, + ); + }, + goBack: goBack, + hide: hide, + isOpen: props.isOpen, + modalSize: props.modalSize, + selectionData: props.selectionData, + setSelectionData: props.setSelectionData, + show: props.show, + supportedWallets: props.supportedWallets, + theme: props.theme, + connectedWallet: personalWalletConnection.activeWallet, + connectedWalletAddress: personalWalletConnection.address, + connectionStatus: personalWalletConnection.connectionStatus, + }; + + if (personalWalletConfig.connectUI) { + return ; + } + + if (walletConfig.personalWallets?.[0] && !personalWalletConfig.connectUI) { + connectPersonalWallet(walletConfig.personalWallets[0]); + } + } return ( <> - {PersonalWalletConfigUI && !connectedPersonalWallet ? ( - connect(personalWalletConfig, options)} - connectedWallet={activeWallet} - connectedWalletAddress={address} - connectionStatus={connectionStatus} - createWalletInstance={() => - createWalletInstance(personalWalletConfig) - } - setConnectedWallet={setConnectedWallet} - setConnectionStatus={setConnectionStatus} + + - ) : ( - - + - - - - - <> - - {mismatch - ? l.smart_wallet.network_mismatch - : `${l.smart_wallet.connecting} ...`} + + + <> + + {mismatch + ? l.smart_wallet.network_mismatch + : `${l.smart_wallet.connecting} ...`} + + {mismatch ? ( + + {l.connect_wallet_details.network_mismatch} - {mismatch ? ( - - {l.connect_wallet_details.network_mismatch} - - ) : null} - - - - )} + ) : null} + + + {mismatch === true ? ( > = ({ goBack, selectionData, onLocallyConnected, + setConnectedWallet, + setConnectionStatus, ...props }) => { const l = useLocale(); @@ -49,8 +46,6 @@ export const EmbeddedConnectionUI: React.FC> = ({ const [checkingOtp, setCheckingOtp] = useState(false); const [requestingNewOtp, setRequestingNewOtp] = useState(false); const [errorMessage, setErrorMessage] = useState(""); - const setConnectedWallet = useSetConnectedWallet(); - const setConnectionStatus = useSetConnectionStatus(); const [password, setPassword] = useState(""); const [focusedIndex, setFocusedIndex] = useState(); const [screen, setScreen] = useState("base"); // TODO change @@ -316,6 +311,8 @@ export const EmbeddedConnectionUI: React.FC> = ({ {}} goBack={goBack} + setConnectedWallet={setConnectedWallet} + setConnectionStatus={setConnectionStatus} onLocallyConnected={onLocallyConnected} selectionData={selectionData} {...props} diff --git a/packages/react-native/src/evm/wallets/wallets/embedded/EmbeddedSocialConnection.tsx b/packages/react-native/src/evm/wallets/wallets/embedded/EmbeddedSocialConnection.tsx index 5c7505ef54d..195681e562f 100644 --- a/packages/react-native/src/evm/wallets/wallets/embedded/EmbeddedSocialConnection.tsx +++ b/packages/react-native/src/evm/wallets/wallets/embedded/EmbeddedSocialConnection.tsx @@ -2,20 +2,20 @@ import { useCallback, useEffect, useState } from "react"; import React from "react"; import { ActivityIndicator } from "react-native"; import { EmbeddedWallet } from "./EmbeddedWallet"; -import { - ConnectUIProps, - useSetConnectedWallet, - useSetConnectionStatus, -} from "@thirdweb-dev/react-core"; +import { ConnectUIProps } from "@thirdweb-dev/react-core"; import { ConnectWalletHeader } from "../../../components/ConnectWalletFlow/ConnectingWallet/ConnectingWalletHeader"; import { Box, Text, WalletButton } from "../../../components/base"; import { AUTH_OPTIONS_ICONS, SocialLogin } from "../../types/embedded-wallet"; export const EmbeddedSocialConnection: React.FC< ConnectUIProps -> = ({ goBack, selectionData, onLocallyConnected }) => { - const setConnectedWallet = useSetConnectedWallet(); - const setConnectionStatus = useSetConnectionStatus(); +> = ({ + goBack, + selectionData, + onLocallyConnected, + setConnectedWallet, + setConnectionStatus, +}) => { const [errorMessage, setErrorMessage] = useState(); const handleSocialLogin = useCallback(() => { @@ -36,7 +36,7 @@ export const EmbeddedSocialConnection: React.FC< if (onLocallyConnected) { onLocallyConnected(selectionData.emailWallet); } else { - await setConnectedWallet(selectionData.emailWallet); + setConnectedWallet(selectionData.emailWallet); setConnectionStatus("connected"); } } else { diff --git a/packages/react-native/src/evm/wallets/wallets/smart-wallet.tsx b/packages/react-native/src/evm/wallets/wallets/smart-wallet.tsx index b0207993f29..de13fa1b547 100644 --- a/packages/react-native/src/evm/wallets/wallets/smart-wallet.tsx +++ b/packages/react-native/src/evm/wallets/wallets/smart-wallet.tsx @@ -1,4 +1,5 @@ import { + ConnectUIProps, useAddress, useConnect, useWalletContext, @@ -46,7 +47,7 @@ export const smartWallet = ( walletStorage: createAsyncLocalStorage("smart-wallet"), }), connectUI(props) { - return ; + return ; }, selectUI: WalletSelectUI ? (props) => { @@ -80,3 +81,21 @@ export const smartWallet = ( personalWallets: [wallet], }; }; + +export const SmartConnectUI = ( + props: ConnectUIProps & { personalWalletConfig: WalletConfig }, +) => { + const { walletConfig } = props; + const { personalWalletConfig } = props; + const { personalWalletConnection } = useWalletContext(); + + return ( + + ); +}; diff --git a/packages/react-native/src/evm/wallets/wallets/wallet-connect/WalletConnectUI.tsx b/packages/react-native/src/evm/wallets/wallets/wallet-connect/WalletConnectUI.tsx index e08a1debd4d..4f3914bfbdb 100644 --- a/packages/react-native/src/evm/wallets/wallets/wallet-connect/WalletConnectUI.tsx +++ b/packages/react-native/src/evm/wallets/wallets/wallet-connect/WalletConnectUI.tsx @@ -12,12 +12,7 @@ import Box from "../../../components/base/Box"; import Text from "../../../components/base/Text"; import { useDebounceCallback } from "../../../hooks/useDebounceCallback"; import { TWModal } from "../../../components/base/modal/TWModal"; -import { - ConnectUIProps, - useCreateWalletInstance, - useSetConnectedWallet, - useSetConnectionStatus, -} from "@thirdweb-dev/react-core"; +import { ConnectUIProps } from "@thirdweb-dev/react-core"; import { WalletConnect } from "./WalletConnect"; import { WalletConnectButton } from "./WalletConnectButton"; import { ModalHeaderTextClose } from "../../../components/base"; @@ -40,12 +35,14 @@ const MODAL_HEIGHT = Dimensions.get("window").height * 0.5; export function WalletConnectUI({ connected, - walletConfig, projectId, goBack, hide, supportedWallets, isVisible, + createWalletInstance, + setConnectedWallet, + setConnectionStatus, }: Omit< ConnectUIProps, | "isOpen" @@ -65,9 +62,6 @@ export function WalletConnectUI({ const [loading, setLoading] = useState(true); const [search, setSearch] = useState(""); const [searchWallets, setSearchWallets] = useState([]); - const createWalletInstance = useCreateWalletInstance(); - const setConnectedWallet = useSetConnectedWallet(); - const setConnectionStatus = useSetConnectionStatus(); const [isVisibleInternal, setIsVisibleInternal] = useState< boolean | undefined >(isVisible); @@ -151,7 +145,7 @@ export function WalletConnectUI({ }, [search, wallets]); const onChooseWalletItem = (walletMeta: WCWallet) => { - const wcWallet = createWalletInstance(walletConfig); + const wcWallet = createWalletInstance(); wcWallet.setWCLinks(walletMeta.links); setConnectionStatus("connecting"); From a5409e335202cacfd1caabd86bfce1e844771358 Mon Sep 17 00:00:00 2001 From: ikethirdweb Date: Mon, 8 Jan 2024 15:30:15 -0500 Subject: [PATCH 19/21] comments --- .../SmartWallet/SmartWalletFlow.tsx | 18 ------------------ .../src/evm/wallets/wallets/smart-wallet.tsx | 3 +-- 2 files changed, 1 insertion(+), 20 deletions(-) diff --git a/packages/react-native/src/evm/components/ConnectWalletFlow/SmartWallet/SmartWalletFlow.tsx b/packages/react-native/src/evm/components/ConnectWalletFlow/SmartWallet/SmartWalletFlow.tsx index 24ad5ff5f30..802665917fc 100644 --- a/packages/react-native/src/evm/components/ConnectWalletFlow/SmartWallet/SmartWalletFlow.tsx +++ b/packages/react-native/src/evm/components/ConnectWalletFlow/SmartWallet/SmartWalletFlow.tsx @@ -78,16 +78,6 @@ export const SmartWalletFlow = ({ [connectSmartWallet, personalWalletConnection], ); - // useEffect(() => { - // if (walletConfig.personalWallets?.[0] && !PersonalWalletConfigUI) { - // connectPersonalWallet(walletConfig.personalWallets[0]); - // } - // }, [ - // PersonalWalletConfigUI, - // connectPersonalWallet, - // walletConfig.personalWallets, - // ]); - const onConnectingClosePress = () => { connectedPersonalWallet?.disconnect(); reset(); @@ -115,14 +105,6 @@ export const SmartWalletFlow = ({ setPersonalWalletChaindId(undefined); }; - // const onPersonalWalletConnected = useCallback( - // (walletInstance: WalletInstance) => { - // setConnectedPersonalWallet(walletInstance); - // connectSmartWallet(walletInstance); - // }, - // [connectSmartWallet], - // ); - if (!personalWalletConnection.activeWallet) { const _props: ConnectUIProps = { walletConfig: personalWalletConfig, diff --git a/packages/react-native/src/evm/wallets/wallets/smart-wallet.tsx b/packages/react-native/src/evm/wallets/wallets/smart-wallet.tsx index de13fa1b547..830854eac1c 100644 --- a/packages/react-native/src/evm/wallets/wallets/smart-wallet.tsx +++ b/packages/react-native/src/evm/wallets/wallets/smart-wallet.tsx @@ -85,8 +85,7 @@ export const smartWallet = ( export const SmartConnectUI = ( props: ConnectUIProps & { personalWalletConfig: WalletConfig }, ) => { - const { walletConfig } = props; - const { personalWalletConfig } = props; + const { walletConfig, personalWalletConfig } = props; const { personalWalletConnection } = useWalletContext(); return ( From 1b1662e1058452f3ee3fcffe4151e6cccc5b01fd Mon Sep 17 00:00:00 2001 From: ikethirdweb Date: Mon, 8 Jan 2024 20:48:41 -0500 Subject: [PATCH 20/21] fix --- packages/react-core/src/core/types/wallet.ts | 14 ------ .../ConnectWalletFlow/ConnectWalletFlow.tsx | 3 +- .../SmartWallet/SmartWalletFlow.tsx | 3 +- .../wallets/embedded/EmbeddedConnectionUI.tsx | 18 ++------ .../embedded/EmbeddedSocialConnection.tsx | 20 +++------ .../src/evm/wallets/wallets/smart-wallet.tsx | 44 +++++++++---------- 6 files changed, 31 insertions(+), 71 deletions(-) diff --git a/packages/react-core/src/core/types/wallet.ts b/packages/react-core/src/core/types/wallet.ts index c1581b160f6..4a1b715b8d5 100644 --- a/packages/react-core/src/core/types/wallet.ts +++ b/packages/react-core/src/core/types/wallet.ts @@ -135,13 +135,6 @@ export type ConnectUIProps = { */ modalSize: "compact" | "wide"; - /** - * Called when the wallet is connected but it's - * part of another wallet's connection flow. - * @param walleInstance - The instance of the connected wallet - */ - onLocallyConnected?: (walletInstance: WalletInstance) => void; - /** * connect wallet * @@ -277,13 +270,6 @@ export type SelectUIProps = { */ modalSize: "compact" | "wide"; - /** - * Called when the wallet is connected but it's - * part of another wallet's connection flow. - * @param walletInstance - the instance of the connected wallet - */ - onLocallyConnected?: (walletInstance: WalletInstance) => void; - /** * connect wallet * diff --git a/packages/react-native/src/evm/components/ConnectWalletFlow/ConnectWalletFlow.tsx b/packages/react-native/src/evm/components/ConnectWalletFlow/ConnectWalletFlow.tsx index 6a03d6ad03a..4767709344a 100644 --- a/packages/react-native/src/evm/components/ConnectWalletFlow/ConnectWalletFlow.tsx +++ b/packages/react-native/src/evm/components/ConnectWalletFlow/ConnectWalletFlow.tsx @@ -4,7 +4,6 @@ import { ConnectingWallet } from "./ConnectingWallet/ConnectingWallet"; import { WalletConfig, useAddress, - useConnect, useWalletContext, useWallets, } from "@thirdweb-dev/react-core"; @@ -40,7 +39,6 @@ export const ConnectWalletFlow = () => { const supportedWallets = useWallets(); const theme = useColorScheme(); const appTheme = useGlobalTheme(); - const connect = useConnect(); // TEMP BUILD FIX const address = useAddress(); const { @@ -48,6 +46,7 @@ export const ConnectWalletFlow = () => { setConnectedWallet, setConnectionStatus, connectionStatus, + connect, createWalletInstance, } = useWalletContext(); diff --git a/packages/react-native/src/evm/components/ConnectWalletFlow/SmartWallet/SmartWalletFlow.tsx b/packages/react-native/src/evm/components/ConnectWalletFlow/SmartWallet/SmartWalletFlow.tsx index 802665917fc..6c84547a6d4 100644 --- a/packages/react-native/src/evm/components/ConnectWalletFlow/SmartWallet/SmartWalletFlow.tsx +++ b/packages/react-native/src/evm/components/ConnectWalletFlow/SmartWallet/SmartWalletFlow.tsx @@ -27,7 +27,6 @@ export const SmartWalletFlow = ({ hide, ...props }: ConnectUIProps & { - smartWalletConfig: WalletConfig; personalWalletConfig: WalletConfig; personalWallet?: WalletInstance; personalWalletChainId: number; @@ -117,6 +116,8 @@ export const SmartWalletFlow = ({ }, setConnectedWallet(wallet) { personalWalletConnection.setConnectedWallet(wallet); + setConnectedPersonalWallet(wallet); + connectSmartWallet(wallet); }, setConnectionStatus(status) { personalWalletConnection.setConnectionStatus(status); diff --git a/packages/react-native/src/evm/wallets/wallets/embedded/EmbeddedConnectionUI.tsx b/packages/react-native/src/evm/wallets/wallets/embedded/EmbeddedConnectionUI.tsx index 5a541fc5e06..9f5037b28f6 100644 --- a/packages/react-native/src/evm/wallets/wallets/embedded/EmbeddedConnectionUI.tsx +++ b/packages/react-native/src/evm/wallets/wallets/embedded/EmbeddedConnectionUI.tsx @@ -32,7 +32,6 @@ export const EmbeddedConnectionUI: React.FC> = ({ connected, goBack, selectionData, - onLocallyConnected, setConnectedWallet, setConnectionStatus, ...props @@ -63,12 +62,8 @@ export const EmbeddedConnectionUI: React.FC> = ({ const postConnect = useCallback( async (response: string) => { if (response) { - if (onLocallyConnected) { - onLocallyConnected(selectionData.emailWallet); - } else { - await setConnectedWallet(selectionData.emailWallet); - setConnectionStatus("connected"); - } + setConnectedWallet(selectionData.emailWallet); + setConnectionStatus("connected"); } else { clearCode(); setErrorMessage(response || "Error validating the code"); @@ -76,12 +71,7 @@ export const EmbeddedConnectionUI: React.FC> = ({ setFocusedIndex(undefined); } }, - [ - onLocallyConnected, - selectionData.emailWallet, - setConnectedWallet, - setConnectionStatus, - ], + [selectionData.emailWallet, setConnectedWallet, setConnectionStatus], ); const connect = useCallback( @@ -201,7 +191,6 @@ export const EmbeddedConnectionUI: React.FC> = ({ connect, onContinuePress, onError, - onLocallyConnected, password, postConnect, screen, @@ -313,7 +302,6 @@ export const EmbeddedConnectionUI: React.FC> = ({ goBack={goBack} setConnectedWallet={setConnectedWallet} setConnectionStatus={setConnectionStatus} - onLocallyConnected={onLocallyConnected} selectionData={selectionData} {...props} /> diff --git a/packages/react-native/src/evm/wallets/wallets/embedded/EmbeddedSocialConnection.tsx b/packages/react-native/src/evm/wallets/wallets/embedded/EmbeddedSocialConnection.tsx index 195681e562f..495fb2d9346 100644 --- a/packages/react-native/src/evm/wallets/wallets/embedded/EmbeddedSocialConnection.tsx +++ b/packages/react-native/src/evm/wallets/wallets/embedded/EmbeddedSocialConnection.tsx @@ -9,13 +9,7 @@ import { AUTH_OPTIONS_ICONS, SocialLogin } from "../../types/embedded-wallet"; export const EmbeddedSocialConnection: React.FC< ConnectUIProps -> = ({ - goBack, - selectionData, - onLocallyConnected, - setConnectedWallet, - setConnectionStatus, -}) => { +> = ({ goBack, selectionData, setConnectedWallet, setConnectionStatus }) => { const [errorMessage, setErrorMessage] = useState(); const handleSocialLogin = useCallback(() => { @@ -33,12 +27,8 @@ export const EmbeddedSocialConnection: React.FC< const response = await embeddedWallet.connect({ authResult }); if (response) { - if (onLocallyConnected) { - onLocallyConnected(selectionData.emailWallet); - } else { - setConnectedWallet(selectionData.emailWallet); - setConnectionStatus("connected"); - } + setConnectedWallet(selectionData.emailWallet); + setConnectionStatus("connected"); } else { setErrorMessage( response || "Error signing in. Please try again later.", @@ -53,9 +43,9 @@ export const EmbeddedSocialConnection: React.FC< } }, 0); }, [ - onLocallyConnected, selectionData.emailWallet, - selectionData.oauthOptions, + selectionData.oauthOptions?.provider, + selectionData.oauthOptions?.redirectUrl, setConnectedWallet, setConnectionStatus, ]); diff --git a/packages/react-native/src/evm/wallets/wallets/smart-wallet.tsx b/packages/react-native/src/evm/wallets/wallets/smart-wallet.tsx index 830854eac1c..191d1c12940 100644 --- a/packages/react-native/src/evm/wallets/wallets/smart-wallet.tsx +++ b/packages/react-native/src/evm/wallets/wallets/smart-wallet.tsx @@ -1,7 +1,5 @@ import { ConnectUIProps, - useAddress, - useConnect, useWalletContext, type WalletConfig, type WalletOptions, @@ -51,29 +49,30 @@ export const smartWallet = ( }, selectUI: WalletSelectUI ? (props) => { - // TEMP BUILD FIX - const connect = useConnect(); - const address = useAddress(); - const { - setConnectedWallet, - setConnectionStatus, - connectionStatus, - createWalletInstance, - activeWallet, - } = useWalletContext(); + const { personalWalletConnection } = useWalletContext(); return ( connect(wallet, options)} - connectedWallet={activeWallet} - connectedWalletAddress={address} - connectionStatus={connectionStatus} - createWalletInstance={() => createWalletInstance(wallet)} - setConnectedWallet={setConnectedWallet} - setConnectionStatus={setConnectionStatus} + connect={(options: any) => { + return personalWalletConnection.connectWallet(wallet, options); + }} + createWalletInstance={() => { + return personalWalletConnection.createWalletInstance(wallet); + }} + setConnectedWallet={(walletInstance) => { + personalWalletConnection.setConnectedWallet(walletInstance); + }} + setConnectionStatus={(status) => { + personalWalletConnection.setConnectionStatus(status); + }} + connectionStatus={personalWalletConnection.connectionStatus} + supportedWallets={props.supportedWallets} + theme={props.theme} + connectedWallet={personalWalletConnection.activeWallet} + connectedWalletAddress={personalWalletConnection.address} + modalSize={props.modalSize} + onSelect={props.onSelect} /> ); } @@ -85,14 +84,11 @@ export const smartWallet = ( export const SmartConnectUI = ( props: ConnectUIProps & { personalWalletConfig: WalletConfig }, ) => { - const { walletConfig, personalWalletConfig } = props; const { personalWalletConnection } = useWalletContext(); return ( From 41252269f98c8fae9f5545ef54fea97d474e87bb Mon Sep 17 00:00:00 2001 From: Manan Tank Date: Wed, 10 Jan 2024 17:22:29 +0530 Subject: [PATCH 21/21] Remove Temp comments in react-native --- .../ConnectWalletFlow/ChooseWallet/ChooseWallet.tsx | 1 - .../ConnectWalletFlow/ChooseWallet/ChooseWalletContent.tsx | 1 - .../evm/components/ConnectWalletFlow/ConnectWalletFlow.tsx | 7 +++---- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/react-native/src/evm/components/ConnectWalletFlow/ChooseWallet/ChooseWallet.tsx b/packages/react-native/src/evm/components/ConnectWalletFlow/ChooseWallet/ChooseWallet.tsx index 32d0288e57a..729bf63e718 100644 --- a/packages/react-native/src/evm/components/ConnectWalletFlow/ChooseWallet/ChooseWallet.tsx +++ b/packages/react-native/src/evm/components/ConnectWalletFlow/ChooseWallet/ChooseWallet.tsx @@ -112,7 +112,6 @@ export function ChooseWallet({ } }; - // TEMP BUILD FIX const connect = useConnect(); const address = useAddress(); const { diff --git a/packages/react-native/src/evm/components/ConnectWalletFlow/ChooseWallet/ChooseWalletContent.tsx b/packages/react-native/src/evm/components/ConnectWalletFlow/ChooseWallet/ChooseWalletContent.tsx index c31bba40bd0..7e75e552dfc 100644 --- a/packages/react-native/src/evm/components/ConnectWalletFlow/ChooseWallet/ChooseWalletContent.tsx +++ b/packages/react-native/src/evm/components/ConnectWalletFlow/ChooseWallet/ChooseWalletContent.tsx @@ -21,7 +21,6 @@ export const ChooseWalletContent = ({ onChooseWallet, }: ChooseWalletContentProps) => { const theme = useTheme(); - // TEMP BUILD FIX const connect = useConnect(); const address = useAddress(); const { diff --git a/packages/react-native/src/evm/components/ConnectWalletFlow/ConnectWalletFlow.tsx b/packages/react-native/src/evm/components/ConnectWalletFlow/ConnectWalletFlow.tsx index 4767709344a..b42bfab9f42 100644 --- a/packages/react-native/src/evm/components/ConnectWalletFlow/ConnectWalletFlow.tsx +++ b/packages/react-native/src/evm/components/ConnectWalletFlow/ConnectWalletFlow.tsx @@ -39,7 +39,6 @@ export const ConnectWalletFlow = () => { const supportedWallets = useWallets(); const theme = useColorScheme(); const appTheme = useGlobalTheme(); - // TEMP BUILD FIX const address = useAddress(); const { activeWallet: connectedWallet, @@ -141,13 +140,13 @@ export const ConnectWalletFlow = () => { setSelectionData={() => {}} // TODO connect={(options) => { return connect(activeWallet, options); - }} // TEMP BUILD FIX + }} connectedWallet={connectedWallet} connectedWalletAddress={address} connectionStatus={connectionStatus} createWalletInstance={() => createWalletInstance(activeWallet)} - setConnectedWallet={setConnectedWallet} // TEMP BUILD FIX - setConnectionStatus={setConnectionStatus} // TEMP BUILD FIX + setConnectedWallet={setConnectedWallet} + setConnectionStatus={setConnectionStatus} /> ); }