UNPKG

@ponziland/account

Version:

Account management library for Starknet with wallet modal and connection state

152 lines (151 loc) 5.7 kB
import { PublicKey } from '@solana/web3.js'; class PhantomWalletStore { state = $state({ isConnected: false, walletAddress: '', loading: false, error: null, }); listenersRegistered = false; skipAutoReconnect = false; forcePromptNextConnect = false; get isConnected() { return this.state.isConnected; } get walletAddress() { return this.state.walletAddress; } get loading() { return this.state.loading; } get error() { return this.state.error; } getProvider() { if ('solana' in window) { const provider = window.solana; if (provider?.isPhantom) { return provider; } } return undefined; } registerEventListeners(provider) { if (this.listenersRegistered) return; provider.on('connect', () => { this.state.isConnected = true; this.state.walletAddress = provider.publicKey?.toString() ?? ''; }); provider.on('disconnect', () => { this.state.isConnected = false; this.state.walletAddress = ''; }); provider.on('accountChanged', (publicKey) => { if (publicKey) { this.state.walletAddress = publicKey.toString(); this.state.isConnected = true; } else { // No account connected in Phantom this.state.walletAddress = ''; this.state.isConnected = false; } }); this.listenersRegistered = true; } async initialize() { const provider = this.getProvider(); if (!provider) { this.state.error = 'Phantom wallet not found. Please install Phantom wallet extension.'; return; } this.registerEventListeners(provider); // Avoid silently reconnecting immediately after an explicit user disconnect if (this.skipAutoReconnect) return; // Check if already connected (on page refresh) without prompting try { const resp = await provider.connect({ onlyIfTrusted: true }); if (resp.publicKey) { this.state.walletAddress = resp.publicKey.toString(); this.state.isConnected = true; } } catch { // Not connected, which is fine } } async connect(options) { this.state.loading = true; this.state.error = null; try { const provider = this.getProvider(); if (!provider) { throw new Error('Phantom wallet not found. Please install Phantom wallet extension.'); } this.registerEventListeners(provider); const shouldForcePrompt = Boolean(options?.forcePrompt || this.forcePromptNextConnect); // If the site is trusted, Phantom may auto-approve connect(). Use signIn when available to show a prompt. if (shouldForcePrompt && typeof provider.signIn === 'function') { try { const resp = await provider.signIn({ domain: typeof window !== 'undefined' ? window.location.hostname : undefined, statement: 'Select an account to connect to this app.', }); if (resp?.publicKey) { this.state.walletAddress = resp.publicKey.toString(); this.state.isConnected = true; this.skipAutoReconnect = false; this.forcePromptNextConnect = false; // Ensure a session is established; connect may auto-approve here which is fine. try { await provider.connect({ onlyIfTrusted: false }); } catch { /* ignore */ } return true; } } catch (promptErr) { // If signIn fails (or user cancels), fall through to normal connect flow to keep behavior consistent console.error('Phantom signIn failed:', promptErr); } } // Ensure a fresh connect flow const resp = await provider.connect({ onlyIfTrusted: false }); this.state.walletAddress = resp.publicKey.toString(); this.state.isConnected = true; this.skipAutoReconnect = false; this.forcePromptNextConnect = false; return true; } catch (err) { this.state.error = err instanceof Error ? err.message : 'Failed to connect wallet'; console.error('Error connecting Phantom:', err); return false; } finally { this.state.loading = false; } } async disconnect() { try { const provider = this.getProvider(); if (provider) { await provider.disconnect(); } this.state.isConnected = false; this.state.walletAddress = ''; // Prevent initialize() from auto-reconnecting in this session this.skipAutoReconnect = true; // Force a prompt on the next explicit connect attempt when possible (via signIn) this.forcePromptNextConnect = true; } catch (err) { console.error('Error disconnecting Phantom:', err); } } } export const phantomWalletStore = new PhantomWalletStore();