diff --git a/projects/TokenizeRWATemplate-frontend/src/App.tsx b/projects/TokenizeRWATemplate-frontend/src/App.tsx index 68d8bc1..6a7de60 100644 --- a/projects/TokenizeRWATemplate-frontend/src/App.tsx +++ b/projects/TokenizeRWATemplate-frontend/src/App.tsx @@ -40,12 +40,7 @@ function buildSupportedWallets(): SupportedWallet[] { id: WalletId.WEB3AUTH, options: { clientId: web3AuthClientId, - // Web3Auth network: 'sapphire_devnet' for development, 'sapphire_mainnet' for production - web3AuthNetwork: 'sapphire_devnet', - // Optional: Set default login provider (e.g., 'google' for direct Google login) - // If not set, shows full provider selection modal - // loginProvider: 'google', - // Optional: UI customization + web3AuthNetwork: 'sapphire_devnet', // Use 'sapphire_mainnet' for production uiConfig: { appName: 'Tokenize RWA Template', mode: 'auto', // 'auto' | 'light' | 'dark' @@ -62,7 +57,7 @@ export default function App() { const supportedWallets = useMemo(() => buildSupportedWallets(), []) const walletManager = useMemo(() => { - const mgr = new WalletManager({ + return new WalletManager({ wallets: supportedWallets, defaultNetwork: algodConfig.network, networks: { @@ -78,8 +73,6 @@ export default function App() { resetNetwork: true, }, }) - - return mgr }, [algodConfig.network, algodConfig.server, algodConfig.port, algodConfig.token, supportedWallets]) return ( diff --git a/projects/TokenizeRWATemplate-frontend/src/components/ConnectWallet.tsx b/projects/TokenizeRWATemplate-frontend/src/components/ConnectWallet.tsx index b017315..80109ff 100644 --- a/projects/TokenizeRWATemplate-frontend/src/components/ConnectWallet.tsx +++ b/projects/TokenizeRWATemplate-frontend/src/components/ConnectWallet.tsx @@ -9,159 +9,82 @@ interface ConnectWalletProps { } const ConnectWallet = ({ openModal, closeModal }: ConnectWalletProps) => { - const { wallets, activeWallet, activeAddress, isReady, isActive, disconnect } = useWallet() + const { wallets, activeWallet, activeAddress } = useWallet() - const [connectingId, setConnectingId] = useState(null) + const [connectingKey, setConnectingKey] = useState(null) const [lastError, setLastError] = useState('') const [copied, setCopied] = useState(false) // Get network config for Lora link const algoConfig = getAlgodConfigFromViteEnvironment() - const networkName = useMemo(() => { - return algoConfig.network === '' ? 'localnet' : algoConfig.network.toLowerCase() - }, [algoConfig.network]) + const networkName = useMemo(() => (algoConfig.network === '' ? 'localnet' : algoConfig.network.toLowerCase()), [algoConfig.network]) - // Get all registered wallets - const visibleWallets = useMemo(() => { - return (wallets ?? []).filter(Boolean) - }, [wallets]) + const visibleWallets = useMemo(() => (wallets ?? []).filter(Boolean), [wallets]) - // Handle wallet connection - const handleConnect = async (wallet: BaseWallet) => { + // Connected if we have an address + const connected = Boolean(activeAddress) + + // Separate Web3Auth from traditional wallets + const web3AuthWallet = visibleWallets.find((w) => w.id === WalletId.WEB3AUTH) + const traditionalWallets = visibleWallets.filter((w) => w.id !== WalletId.WEB3AUTH) + + // Capture wallet ID for logout (before disconnect clears it) + const activeWalletId = activeWallet?.id + + const connectWallet = async (wallet: BaseWallet) => { setLastError('') - setConnectingId(wallet.id) + setConnectingKey(wallet.id) try { if (typeof wallet.connect !== 'function') { throw new Error(`Wallet "${wallet.id}" is missing connect().`) } - // For Web3Auth, try to access and manually initialize if needed - if (wallet.id === WalletId.WEB3AUTH) { - const walletAny = wallet as any - - // Check if we can access the internal Web3Auth instance - const web3AuthInstance = walletAny.web3Auth || walletAny._web3Auth || walletAny.instance - - if (web3AuthInstance) { - // If not initialized, try to initialize - if (web3AuthInstance.status !== 'ready' && typeof web3AuthInstance.init === 'function') { - try { - await web3AuthInstance.init() - } catch (initError) { - setLastError(`Web3Auth initialization failed: ${initError}`) - return - } - } - - // If initialized but not connected, try direct connect - if (web3AuthInstance.status === 'ready' && !web3AuthInstance.connected) { - try { - await web3AuthInstance.connect() - // Don't call wallet.connect() if we already connected directly - closeModal() - return - } catch (directConnectError) { - // Fall through to try wallet.connect() - } - } - } - } - - // Call connect - this should open the Web3Auth modal - const connectPromise = wallet.connect() - - // Add timeout for Web3Auth to detect if it hangs - if (wallet.id === WalletId.WEB3AUTH) { - const timeoutPromise = new Promise((_, reject) => { - setTimeout(() => { - reject( - new Error( - 'Web3Auth connection timed out after 30 seconds. The modal may not have opened. Check browser console for Web3Auth initialization errors.', - ), - ) - }, 30000) - }) - - await Promise.race([connectPromise, timeoutPromise]) - } else { - await connectPromise - } - - // For Web3Auth, verify the address was set - if (wallet.id === WalletId.WEB3AUTH) { - // Wait a bit to see if state updates - await new Promise((resolve) => setTimeout(resolve, 1000)) - - // Check if address was set - const hasAddress = activeAddress && activeAddress.length > 0 - - await new Promise((resolve) => setTimeout(resolve, 1000)) - - if (!hasAddress) { - return - } - } else { - await new Promise((resolve) => setTimeout(resolve, 500)) - } - + await wallet.connect() closeModal() - } catch (e: any) { - const msg = e?.message ? String(e.message) : String(e) + } catch (e: unknown) { + const msg = e instanceof Error ? e.message : String(e) + console.error(`[ConnectWallet] Failed to connect "${wallet.id}"`, e) - // Provide more helpful error messages for Web3Auth if (wallet.id === WalletId.WEB3AUTH) { - if (msg.includes('clientId') || msg.includes('Client ID')) { - setLastError('Web3Auth Client ID is missing or invalid. Check your .env file.') - } else if (msg.includes('network') || msg.includes('Network')) { - setLastError('Web3Auth network configuration error. Check your network settings.') - } else if (msg.includes('timeout')) { - setLastError('Web3Auth connection timed out. The login modal may not have opened. Check browser console for Web3Auth errors.') - } else { - setLastError(`Web3Auth connection failed: ${msg}`) - } + setLastError(`Web3Auth sign-in failed: ${msg}`) } else { setLastError(`Failed to connect ${wallet.id}: ${msg}`) } } finally { - setConnectingId(null) + setConnectingKey(null) } } - // Handle disconnect - const handleDisconnect = async () => { + const handleLogout = async () => { setLastError('') + const wasWeb3Auth = activeWalletId === WalletId.WEB3AUTH + try { - // For Web3Auth, we might need to use the wallet's disconnect method directly - if (activeWallet) { - // Try the wallet's disconnect method first - if (typeof (activeWallet as any).disconnect === 'function') { - await (activeWallet as any).disconnect() - } - // Fall back to hook's disconnect if available - else if (typeof disconnect === 'function') { - await disconnect() - } - } else if (typeof disconnect === 'function') { - await disconnect() + // In use-wallet v4.x, disconnect is on the activeWallet object + if (activeWallet && typeof activeWallet.disconnect === 'function') { + await activeWallet.disconnect() } + + // Web3Auth needs a hard refresh to fully clear cached auth state + if (wasWeb3Auth) { + window.location.reload() + return + } + closeModal() - } catch (e: any) { - // Silently handle disconnect errors + } catch (e: unknown) { + console.error('[ConnectWallet] Logout failed', e) + // Still close modal on error - user wanted to disconnect closeModal() + } finally { + setConnectingKey(null) } } - // Don't render if modal is closed if (!openModal) return null - // Check if wallet is connected - activeAddress is the primary indicator - // isActive might be undefined for Web3Auth, so we check activeAddress - const connected = Boolean(activeAddress) - - // Separate Web3Auth from traditional wallets for better UX - const web3AuthWallet = visibleWallets.find((w) => w.id === WalletId.WEB3AUTH) - const traditionalWallets = visibleWallets.filter((w) => w.id !== WalletId.WEB3AUTH) + const isConnectingAny = Boolean(connectingKey) return (
@@ -172,7 +95,11 @@ const ConnectWallet = ({ openModal, closeModal }: ConnectWalletProps) => { {/* Header */}

{connected ? 'Account' : 'Sign in'}

-
@@ -196,21 +123,21 @@ const ConnectWallet = ({ openModal, closeModal }: ConnectWalletProps) => {
- {/* Address with copy button */} + {/* Address with copy */}
{ellipseAddress(activeAddress || '', 8)}
+
- {/* Full address (collapsible) */} + {/* Full address */}
Show full address @@ -230,51 +157,57 @@ const ConnectWallet = ({ openModal, closeModal }: ConnectWalletProps) => {
- {/* Wallet type and Lora link */} -
-
- Wallet: {activeWallet?.metadata?.name ?? activeWallet?.id} + {/* Wallet + Network + Lora */} +
+
+
+ Wallet: {activeWallet?.metadata?.name ?? activeWallet?.id} +
+ + {activeAddress && ( + + View on Lora + + + + + )} +
+ +
+ Network: {networkName}
- {activeAddress && ( - - View on Lora - - - - - )}
) : (
- {/* Google Sign In (Web3Auth) */} + {/* Web3Auth - opens its own modal for social provider selection */} {web3AuthWallet && (
+

Sign in with Google, GitHub, or other social accounts

)} @@ -312,32 +246,34 @@ const ConnectWallet = ({ openModal, closeModal }: ConnectWalletProps) => { {traditionalWallets.length > 0 && (
Algorand Wallets
- {traditionalWallets.map((w) => ( - - ))} + {traditionalWallets.map((w) => { + const isConnectingThis = connectingKey === w.id + return ( + + ) + })}
)} - {/* Warning if Web3Auth didn't register */} {!web3AuthWallet && (

- Google sign-in is not available. Check console for Web3Auth errors. + Social sign-in is not available. Check your Web3Auth Client ID in .env and the browser console.

)}