import { User } from '@/types'; import { WalletService } from '../wallets/index'; import { UseAppKitAccountReturn } from '@reown/appkit/react'; import { AppKit } from '@reown/appkit'; import { OrdinalAPI } from '../ordinal'; import { MessageSigning } from '../signatures/message-signing'; import { KeyDelegation, DelegationDuration } from '../signatures/key-delegation'; import { OpchanMessage } from '@/types'; export interface AuthResult { success: boolean; user?: User; error?: string; } export class AuthService { private walletService: WalletService; private ordinalAPI: OrdinalAPI; public messageSigning: MessageSigning; private keyDelegation: KeyDelegation; constructor() { this.walletService = new WalletService(); this.ordinalAPI = new OrdinalAPI(); this.keyDelegation = new KeyDelegation(); this.messageSigning = new MessageSigning(this.keyDelegation); } /** * Set AppKit accounts for wallet service */ setAccounts(bitcoinAccount: UseAppKitAccountReturn, ethereumAccount: UseAppKitAccountReturn) { this.walletService.setAccounts(bitcoinAccount, ethereumAccount); } /** * Set AppKit instance for wallet service */ setAppKit(appKit: AppKit) { this.walletService.setAppKit(appKit); } /** * Get the active wallet address */ private getActiveAddress(): string | null { const isBitcoinConnected = this.walletService.isWalletAvailable('bitcoin'); const isEthereumConnected = this.walletService.isWalletAvailable('ethereum'); if (isBitcoinConnected) { return this.walletService.getActiveAddress('bitcoin') || null; } else if (isEthereumConnected) { return this.walletService.getActiveAddress('ethereum') || null; } return null; } /** * Get the active wallet type */ private getActiveWalletType(): 'bitcoin' | 'ethereum' | null { if (this.walletService.isWalletAvailable('bitcoin')) { return 'bitcoin'; } else if (this.walletService.isWalletAvailable('ethereum')) { return 'ethereum'; } return null; } /** * Connect to wallet and create user */ async connectWallet(): Promise { try { // Check which wallet is connected const isBitcoinConnected = this.walletService.isWalletAvailable('bitcoin'); const isEthereumConnected = this.walletService.isWalletAvailable('ethereum'); if (!isBitcoinConnected && !isEthereumConnected) { return { success: false, error: 'No wallet connected' }; } // Determine which wallet is active const walletType = isBitcoinConnected ? 'bitcoin' : 'ethereum'; const address = this.getActiveAddress(); if (!address) { return { success: false, error: 'No wallet address available' }; } const user: User = { address: address, walletType: walletType, verificationStatus: 'unverified', lastChecked: Date.now(), }; // Add ENS info for Ethereum wallets (if available) if (walletType === 'ethereum') { try { const walletInfo = await this.walletService.getWalletInfo(); user.ensName = walletInfo?.ensName; user.ensOwnership = !!(walletInfo?.ensName); } catch (error) { console.warn('Failed to resolve ENS during wallet connection:', error); user.ensName = undefined; user.ensOwnership = false; } } return { success: true, user }; } catch (error) { return { success: false, error: error instanceof Error ? error.message : 'Failed to connect wallet' }; } } /** * Disconnect wallet and clear stored data */ async disconnectWallet(): Promise { // Clear any existing delegations when disconnecting this.keyDelegation.clearDelegation(); this.walletService.clearDelegation('bitcoin'); this.walletService.clearDelegation('ethereum'); // Clear stored user data this.clearStoredUser(); } /** * Clear delegation for current wallet */ clearDelegation(): void { this.keyDelegation.clearDelegation(); } /** * Verify ordinal ownership for Bitcoin users or ENS ownership for Ethereum users */ async verifyOwnership(user: User): Promise { try { if (user.walletType === 'bitcoin') { return await this.verifyBitcoinOrdinal(user); } else if (user.walletType === 'ethereum') { return await this.verifyEthereumENS(user); } else { return { success: false, error: 'Unknown wallet type' }; } } catch (error) { return { success: false, error: error instanceof Error ? error.message : 'Failed to verify ownership' }; } } /** * Verify Bitcoin Ordinal ownership */ private async verifyBitcoinOrdinal(user: User): Promise { // TODO: revert when the API is ready // const response = await this.ordinalApi.getOperatorDetails(user.address); // const hasOperators = response.has_operators; const hasOperators = true; const updatedUser = { ...user, ordinalOwnership: hasOperators, lastChecked: Date.now(), }; return { success: true, user: updatedUser }; } /** * Verify Ethereum ENS ownership */ private async verifyEthereumENS(user: User): Promise { try { // Get wallet info with ENS resolution const walletInfo = await this.walletService.getWalletInfo(); const hasENS = !!(walletInfo?.ensName); const ensName = walletInfo?.ensName; const updatedUser = { ...user, ensOwnership: hasENS, ensName: ensName, lastChecked: Date.now(), }; return { success: true, user: updatedUser }; } catch (error) { console.error('Error verifying ENS ownership:', error); // Fall back to no ENS ownership on error const updatedUser = { ...user, ensOwnership: false, ensName: undefined, lastChecked: Date.now(), }; return { success: true, user: updatedUser }; } } /** * Set up key delegation for the user */ async delegateKey(user: User, duration: DelegationDuration = '7days'): Promise { try { const walletType = user.walletType; const isAvailable = this.walletService.isWalletAvailable(walletType); if (!isAvailable) { return { success: false, error: `${walletType} wallet is not available or connected. Please ensure it is connected.` }; } const success = await this.walletService.createKeyDelegation(walletType, duration); if (!success) { return { success: false, error: 'Failed to create key delegation' }; } // Get delegation status to update user const delegationStatus = this.walletService.getDelegationStatus(walletType); // Get the actual browser public key from the delegation const browserPublicKey = this.keyDelegation.getBrowserPublicKey(); const updatedUser = { ...user, browserPubKey: browserPublicKey || undefined, delegationSignature: delegationStatus.isValid ? 'valid' : undefined, delegationExpiry: delegationStatus.timeRemaining ? Date.now() + delegationStatus.timeRemaining : undefined, }; return { success: true, user: updatedUser }; } catch (error) { return { success: false, error: error instanceof Error ? error.message : 'Failed to delegate key' }; } } /** * Check if delegation is valid */ isDelegationValid(): boolean { // Only check the currently connected wallet type const activeWalletType = this.getActiveWalletType(); if (!activeWalletType) return false; const status = this.walletService.getDelegationStatus(activeWalletType); return status.isValid; } /** * Get delegation time remaining */ getDelegationTimeRemaining(): number { // Only check the currently connected wallet type const activeWalletType = this.getActiveWalletType(); if (!activeWalletType) return 0; const status = this.walletService.getDelegationStatus(activeWalletType); return status.timeRemaining || 0; } /** * Get current wallet info */ async getWalletInfo() { // Use the wallet service to get detailed wallet info including ENS return await this.walletService.getWalletInfo(); } /** * Load user from localStorage */ loadStoredUser(): User | null { const storedUser = localStorage.getItem('opchan-user'); if (!storedUser) return null; try { const user = JSON.parse(storedUser); const lastChecked = user.lastChecked || 0; const expiryTime = 24 * 60 * 60 * 1000; if (Date.now() - lastChecked < expiryTime) { return user; } else { localStorage.removeItem('opchan-user'); return null; } } catch (e) { console.error("Failed to parse stored user data", e); localStorage.removeItem('opchan-user'); return null; } } /** * Save user to localStorage */ saveUser(user: User): void { localStorage.setItem('opchan-user', JSON.stringify(user)); } /** * Clear stored user data */ clearStoredUser(): void { localStorage.removeItem('opchan-user'); } }