@btc-vision/walletconnect
Version:
The OP_NET Wallet Connect library helps your dApp connect to any compatible wallet.
266 lines (265 loc) • 13 kB
JavaScript
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;