UNPKG

@btc-vision/walletconnect

Version:

The OP_NET Wallet Connect library helps your dApp connect to any compatible wallet.

266 lines (265 loc) 13 kB
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime"; import { Address } from '@btc-vision/transaction'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { WalletConnectContext } from '../context/WalletConnectContext'; import '../utils/style.css'; import '../utils/theme.css'; import { WalletController } from '../wallets'; const AUTO_RECONNECT_RETRIES = 5; const WalletConnectProvider = ({ theme, children }) => { const [pageLoaded, setPageLoaded] = useState(false); const [connectError, setConnectError] = useState(undefined); const timeoutRef = useRef(null); const [network, setNetwork] = useState(null); const [supportedWallets] = useState(WalletController.getWallets); const [selectedWallet, setSelectedWallet] = useState(null); const [connecting, setConnecting] = useState(false); const [modalOpen, setModalOpen] = useState(false); const [walletAddress, setWalletAddress] = useState(null); const [publicKey, setPublicKey] = useState(null); const [walletType, setWalletType] = useState(null); const [walletInstance, setWalletInstance] = useState(null); const [provider, setProvider] = useState(null); const [signer, setSigner] = useState(null); const [walletBalance, setWalletBalance] = useState(null); const [mldsaPublicKey, setMldsaPublicKey] = useState(null); const [hashedMLDSAKey, setHashedMLDSAKey] = useState(null); const clearConnectError = useCallback(() => { if (timeoutRef.current) clearTimeout(timeoutRef.current); timeoutRef.current = setTimeout(() => setConnectError(undefined), 5000); }, []); useEffect(() => { const onPageLoad = () => { setPageLoaded(true); }; if (document.readyState === 'complete') { onPageLoad(); } else { window.addEventListener('load', onPageLoad, false); return () => window.removeEventListener('load', onPageLoad); } }, []); useEffect(() => { const savedWallet = localStorage.getItem('WC_SelectedWallet'); if (savedWallet) { setSelectedWallet(savedWallet); } }, []); useEffect(() => { if (connectError) { clearConnectError(); } }, [connectError, clearConnectError]); const openConnectModal = () => { setConnectError(undefined); setModalOpen(true); }; const closeConnectModal = () => { setModalOpen(false); setConnectError(undefined); }; const disconnect = useCallback(async () => { console.log('DISCONNECTING FROM WALLET'); localStorage.removeItem('WC_SelectedWallet'); setSelectedWallet(null); setPublicKey(null); setWalletAddress(null); setConnecting(false); try { WalletController.removeDisconnectHook(); WalletController.removeChainChangedHook(); WalletController.removeAccountsChangedHook(); await WalletController.disconnect(); } catch (hookError) { console.warn('Error disconnecting from wallet:', hookError); } setNetwork(null); }, []); const connectToWallet = useCallback(async (wallet) => { setConnecting(true); try { const response = await WalletController.connect(wallet); if (response.code === 200 && Array.isArray(response.data)) { if (!response.data || response.data.length === 0) { return; } setWalletAddress(response.data[0]); const publicKey = await WalletController.getPublicKey(); setPublicKey(publicKey); const network = await WalletController.getNetwork(); setNetwork(network); try { WalletController.setAccountsChangedHook(accountsChanged); WalletController.setChainChangedHook(chainChanged); WalletController.setDisconnectHook(disconnect); } catch (hookError) { console.warn('Error setting up wallet hooks:', hookError); } closeConnectModal(); console.log('Connected to wallet:', wallet); setSelectedWallet(wallet); localStorage.setItem('WC_SelectedWallet', wallet); } else if (response.data && 'message' in response.data) { setConnectError(response.data.message); } else { setConnectError('Unknown error'); } } catch (err) { setConnectError(err.message || 'Unexpected error'); } finally { setConnecting(false); } }, [disconnect]); const attemptReconnect = useCallback(async () => { console.warn('Trying to reconnect...', selectedWallet, connecting); if (!selectedWallet || connecting) return; const canAutoConnect = await WalletController.canAutoConnect(selectedWallet); console.log('CanAutoConnect', canAutoConnect); if (!canAutoConnect) return; let attempts = 0; const reconnect = async () => { attempts++; const walletAvailable = WalletController.isWalletInstalled(selectedWallet); if (walletAvailable) { console.log(`Attempting to reconnect to ${selectedWallet} (Attempt ${attempts})`); await connectToWallet(selectedWallet); return; } if (attempts < AUTO_RECONNECT_RETRIES) { setTimeout(reconnect, 1000 * attempts); } }; await reconnect(); }, [selectedWallet, connectToWallet, pageLoaded]); useEffect(() => { void attemptReconnect(); }, [attemptReconnect]); const accountsChanged = useCallback(async (accounts) => { console.log('Account changed, updating address'); const account = accounts.length > 0 ? accounts[0] : null; setWalletAddress(account); const publicKey = account ? await WalletController.getPublicKey() : null; setPublicKey(publicKey); }, [setWalletAddress, setPublicKey]); const chainChanged = useCallback((network) => { if (selectedWallet) { setNetwork(network); } }, [selectedWallet, setNetwork]); const allWallets = useMemo(() => { return supportedWallets.map((wallet) => { return { name: wallet.name, icon: wallet.icon, isInstalled: wallet.controller.isInstalled(), isConnected: wallet.controller.isConnected(), }; }); }, [supportedWallets, network, pageLoaded]); const availableWallets = useMemo(() => { return supportedWallets.filter((wallet) => wallet.controller.isInstalled()); }, [supportedWallets, network, pageLoaded]); useEffect(() => { const walletType = walletAddress ? WalletController.getWalletType() : null; setWalletType(walletType); const walletInstance = walletAddress ? WalletController.getWalletInstance() : null; setWalletInstance(walletInstance); }, [walletAddress]); useEffect(() => { const updateWalletInfo = async () => { const provider = walletAddress ? await WalletController.getProvider() : null; setProvider(provider); }; void updateWalletInfo(); }, [walletAddress, network]); useEffect(() => { const updateSigner = async () => { const signer = publicKey ? await WalletController.getSigner() : null; setSigner(signer); }; void updateSigner(); }, [network, publicKey]); useEffect(() => { const fetchBalance = async () => { if (walletAddress && walletInstance) { try { const balance = (await walletInstance.getBalance()); setWalletBalance(balance); } catch (error) { console.error('Error fetching balance:', error); setWalletBalance(null); } } else { setWalletBalance(null); } }; void fetchBalance(); }, [walletAddress, walletInstance]); useEffect(() => { const fetchMLDSAKeys = async () => { if (publicKey) { const mldsaPubKey = await WalletController.getMLDSAPublicKey(); setMldsaPublicKey(mldsaPubKey); const hashedKey = await WalletController.getHashedMLDSAKey(); setHashedMLDSAKey(hashedKey); } else { setMldsaPublicKey(null); setHashedMLDSAKey(null); } }; void fetchMLDSAKeys(); }, [publicKey]); const signMLDSAMessage = useCallback(async (message) => { return WalletController.signMLDSAMessage(message); }, []); const verifyMLDSASignature = useCallback(async (message, signature) => { return WalletController.verifyMLDSASignature(message, signature); }, []); const currentTheme = useMemo(() => { const currentTheme = theme || 'light'; return `wallet-connect-${currentTheme}-theme`; }, [theme]); const address = useMemo(() => { return mldsaPublicKey && publicKey ? Address.fromString(mldsaPublicKey, publicKey) : mldsaPublicKey ? Address.fromString(mldsaPublicKey) : null; }, [mldsaPublicKey, publicKey]); return (_jsxs(WalletConnectContext.Provider, { value: { walletAddress, publicKey, address, connecting, connectToWallet, disconnect, openConnectModal, network, allWallets, walletInstance, provider, signer, walletBalance, walletType, mldsaPublicKey, hashedMLDSAKey, signMLDSAMessage, verifyMLDSASignature, }, children: [children, modalOpen && (_jsx("div", { className: `wallet-connect-modal-backdrop ${currentTheme}`, children: _jsxs("div", { className: "wallet-connect-modal", role: "dialog", "aria-modal": "true", "aria-labelledby": "wallet-connect-modal-title", children: [_jsxs("div", { className: "wallet-connect-header", children: [_jsx("span", { children: "Connect Wallet" }), _jsx("button", { className: "close", onClick: () => closeConnectModal(), children: _jsx("span", { className: "close-icon", children: _jsx("svg", { width: "30px", height: "30px", viewBox: "0 0 24 24", xmlns: "http://www.w3.org/2000/svg", children: _jsx("path", { className: "close-x-path", d: "M16 8L8 16M8.00001 8L16 16", stroke: "#fff", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) }) }) })] }), connectError && (_jsx("div", { className: "wallet-connect-error", children: _jsx("p", { className: "error-message", children: connectError }) })), availableWallets.length > 0 ? (_jsx("div", { className: "wallet-list", children: availableWallets.map((wallet) => (_jsxs("button", { onClick: () => connectToWallet(wallet.name), disabled: connecting || !wallet.controller.isInstalled(), className: `wallet-button ${wallet.controller.isInstalled() ? 'wallet-installed' : 'wallet-not-installed'}`, children: [wallet.icon ? (_jsx("div", { className: "wallet-icon", title: wallet.name, children: _jsx("img", { src: wallet.icon, alt: wallet.name }) })) : (_jsx("div", { className: "wallet-name", children: wallet.name })), wallet.controller.isConnected() ? (_jsx("div", { className: "wallet-connected", children: "(Connected)" })) : (_jsx(_Fragment, {})), wallet.controller.isInstalled() ? (_jsx(_Fragment, {})) : (_jsx("div", { className: "wallet-not-installed", children: "(Not Installed)" }))] }, wallet.name))) })) : pageLoaded ? (_jsxs("div", { children: [_jsx("p", { children: "No wallets available" }), _jsx("p", { children: "Supporting the following wallets" }), _jsx("div", { className: "wallet-list", children: supportedWallets.map((wallet) => (_jsx("a", { href: `https://chromewebstore.google.com/search/${wallet.name}`, children: wallet.icon ? (_jsx("div", { className: "wallet-icon", title: wallet.name, children: _jsx("img", { src: wallet.icon, alt: wallet.name }) })) : (_jsx("div", { className: "wallet-name", children: wallet.name })) }))) })] })) : (_jsxs("div", { className: "wallet-waiting-plugin", children: [_jsx("p", { children: "Loading plugins..." }), _jsx("p", { children: "Please wait" })] }))] }) }))] })); }; export default WalletConnectProvider;