-
-
-
- OpChan
-
-
-
-
-
-
-
-
- {isNetworkConnected ? (
- <>
-
- WAKU: Connected
- >
- ) : (
- <>
-
- WAKU: Offline
- >
- )}
-
-
-
- {isNetworkConnected ? "Waku network connection active." : "Waku network connection lost."}
- {isRefreshing && Refreshing data...
}
-
-
+ <>
+
+
+
+
+
+ OpChan
+
+
+
- {!currentUser ? (
-
- ) : (
-
- {renderAccessBadge()}
- {renderDelegationButton()}
-
-
-
- {currentUser.address.slice(0, 5)}...{currentUser.address.slice(-4)}
-
-
-
- {currentUser.address}
-
-
-
-
-
-
- Disconnect Wallet
-
-
- )}
+
+
+
+
+ {isNetworkConnected ? (
+ <>
+
+ WAKU: Connected
+ >
+ ) : (
+ <>
+
+ WAKU: Offline
+ >
+ )}
+
+
+
+ {isNetworkConnected ? "Waku network connection active." : "Waku network connection lost."}
+ {isRefreshing && Refreshing data...
}
+
+
+
+ {!isConnected ? (
+
+ ) : (
+
+ {renderAccessBadge()}
+ {renderDelegationButton()}
+
+
+
+ {address?.slice(0, 5)}...{address?.slice(-4)}
+
+
+
+ {address}
+
+
+
+
+
+
+ Disconnect Wallet
+
+
+ )}
+
-
-
+
+
+
+ >
);
};
diff --git a/src/components/ui/wallet-dialog.tsx b/src/components/ui/wallet-dialog.tsx
index 256b701..eed00a1 100644
--- a/src/components/ui/wallet-dialog.tsx
+++ b/src/components/ui/wallet-dialog.tsx
@@ -8,60 +8,212 @@ import {
DialogTitle,
} from "@/components/ui/dialog";
import { Button } from "@/components/ui/button";
-import { WalletConnectionStatus } from "@/lib/identity/wallets/phantom";
+import { Badge } from "@/components/ui/badge";
+import { Bitcoin, Coins } from "lucide-react";
+import {
+ useAppKit,
+ useAppKitAccount,
+ useDisconnect,
+ useAppKitState
+} from "@reown/appkit/react";
interface WalletDialogProps {
open: boolean;
onOpenChange: (open: boolean) => void;
- onConnectPhantom: () => void;
- onInstallPhantom: () => void;
- status: WalletConnectionStatus;
- isAuthenticating: boolean;
+ onConnect: () => void;
}
export function WalletConnectionDialog({
open,
onOpenChange,
- onConnectPhantom,
- onInstallPhantom,
- status,
- isAuthenticating,
+ onConnect,
}: WalletDialogProps) {
+ // Always call hooks to follow React rules
+ const { initialized } = useAppKitState();
+ const appKit = useAppKit();
+ const { disconnect } = useDisconnect();
+
+ // Get account info for different chains
+ const bitcoinAccount = useAppKitAccount({ namespace: "bip122" });
+ const ethereumAccount = useAppKitAccount({ namespace: "eip155" });
+
+ // Determine which account is connected
+ const isBitcoinConnected = bitcoinAccount.isConnected;
+ const isEthereumConnected = ethereumAccount.isConnected;
+ const isConnected = isBitcoinConnected || isEthereumConnected;
+
+ // Get the active account info
+ const activeAccount = isBitcoinConnected ? bitcoinAccount : ethereumAccount;
+ const activeAddress = activeAccount.address;
+ const activeChain = isBitcoinConnected ? "Bitcoin" : "Ethereum";
+
+ const handleDisconnect = async () => {
+ await disconnect();
+ onOpenChange(false);
+ };
+
+ const handleBitcoinConnect = () => {
+ if (!initialized || !appKit) {
+ console.error('AppKit not initialized');
+ return;
+ }
+
+ appKit.open({
+ view: "Connect",
+ namespace: "bip122"
+ });
+ onConnect();
+ onOpenChange(false);
+ };
+
+ const handleEthereumConnect = () => {
+ if (!initialized || !appKit) {
+ console.error('AppKit not initialized');
+ return;
+ }
+
+ appKit.open({
+ view: "Connect",
+ namespace: "eip155"
+ });
+ onConnect();
+ onOpenChange(false);
+ };
+
+ // Show loading state if AppKit is not initialized
+ if (!initialized) {
+ return (
+
+ );
+ }
+
return (
);
-}
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/src/contexts/AuthContext.tsx b/src/contexts/AuthContext.tsx
index a156b25..00a1015 100644
--- a/src/contexts/AuthContext.tsx
+++ b/src/contexts/AuthContext.tsx
@@ -3,6 +3,7 @@ import { useToast } from '@/components/ui/use-toast';
import { User } from '@/types';
import { AuthService, AuthResult } from '@/lib/identity/services/AuthService';
import { OpchanMessage } from '@/types';
+import { useAppKitAccount, useDisconnect } from '@reown/appkit/react';
export type VerificationStatus = 'unverified' | 'verified-none' | 'verified-owner' | 'verifying';
@@ -11,8 +12,6 @@ interface AuthContextType {
isAuthenticated: boolean;
isAuthenticating: boolean;
verificationStatus: VerificationStatus;
- connectWallet: () => Promise
;
- disconnectWallet: () => void;
verifyOrdinal: () => Promise;
delegateKey: () => Promise;
isDelegationValid: () => boolean;
@@ -34,76 +33,66 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
const [verificationStatus, setVerificationStatus] = useState('unverified');
const { toast } = useToast();
+ // Use AppKit hooks for multi-chain support
+ const bitcoinAccount = useAppKitAccount({ namespace: "bip122" });
+ const ethereumAccount = useAppKitAccount({ namespace: "eip155" });
+
+ // Determine which account is connected
+ const isBitcoinConnected = bitcoinAccount.isConnected;
+ const isEthereumConnected = ethereumAccount.isConnected;
+ const isConnected = isBitcoinConnected || isEthereumConnected;
+
+ // Get the active account info
+ const activeAccount = isBitcoinConnected ? bitcoinAccount : ethereumAccount;
+ const address = activeAccount.address;
+
// Create ref for AuthService so it persists between renders
const authServiceRef = useRef(new AuthService());
+ // Sync with AppKit wallet state
useEffect(() => {
- const storedUser = authServiceRef.current.loadStoredUser();
- if (storedUser) {
- setCurrentUser(storedUser);
+ if (isConnected && address) {
+ // Check if we have a stored user for this address
+ const storedUser = authServiceRef.current.loadStoredUser();
- if ('ordinalOwnership' in storedUser) {
- setVerificationStatus(storedUser.ordinalOwnership ? 'verified-owner' : 'verified-none');
+ if (storedUser && storedUser.address === address) {
+ // Use stored user data
+ setCurrentUser(storedUser);
+ if ('ordinalOwnership' in storedUser) {
+ setVerificationStatus(storedUser.ordinalOwnership ? 'verified-owner' : 'verified-none');
+ } else {
+ setVerificationStatus('unverified');
+ }
} else {
+ // Create new user from AppKit wallet
+ const newUser: User = {
+ address,
+ walletType: isBitcoinConnected ? 'bitcoin' : 'ethereum',
+ ordinalOwnership: false,
+ delegationExpiry: null,
+ verificationStatus: 'unverified',
+ };
+ setCurrentUser(newUser);
setVerificationStatus('unverified');
- }
- }
- }, []);
-
- const connectWallet = async () => {
- setIsAuthenticating(true);
- try {
- const result: AuthResult = await authServiceRef.current.connectWallet();
-
- if (!result.success) {
+ authServiceRef.current.saveUser(newUser);
+
+ const chainName = isBitcoinConnected ? 'Bitcoin' : 'Ethereum';
toast({
- title: "Connection Failed",
- description: result.error || "Failed to connect to wallet. Please try again.",
- variant: "destructive",
+ title: "Wallet Connected",
+ description: `Connected to ${chainName} with address ${address.slice(0, 6)}...${address.slice(-4)}`,
+ });
+
+ toast({
+ title: "Action Required",
+ description: "Please verify your Ordinal ownership and delegate a signing key for better UX.",
});
- throw new Error(result.error);
}
-
- const newUser = result.user!;
- setCurrentUser(newUser);
- authServiceRef.current.saveUser(newUser);
+ } else {
+ // Wallet disconnected
+ setCurrentUser(null);
setVerificationStatus('unverified');
-
- toast({
- title: "Wallet Connected",
- description: `Connected with address ${newUser.address.slice(0, 6)}...${newUser.address.slice(-4)}`,
- });
-
- toast({
- title: "Action Required",
- description: "Please verify your Ordinal ownership and delegate a signing key for better UX.",
- });
-
- } catch (error) {
- console.error("Error connecting wallet:", error);
- toast({
- title: "Connection Failed",
- description: "Failed to connect to wallet. Please try again.",
- variant: "destructive",
- });
- throw error;
- } finally {
- setIsAuthenticating(false);
}
- };
-
- const disconnectWallet = () => {
- authServiceRef.current.disconnectWallet();
- authServiceRef.current.clearStoredUser();
-
- setCurrentUser(null);
- setVerificationStatus('unverified');
-
- toast({
- title: "Disconnected",
- description: "Your wallet has been disconnected.",
- });
- };
+ }, [isConnected, address, isBitcoinConnected, toast]);
const verifyOrdinal = async (): Promise => {
if (!currentUser || !currentUser.address) {
@@ -222,7 +211,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
} else if (error.message.includes("timeout")) {
errorMessage = "Wallet request timed out. Please try again and approve the signature promptly.";
} else if (error.message.includes("Failed to connect wallet")) {
- errorMessage = "Unable to connect to Phantom wallet. Please ensure it's installed and unlocked, then try again.";
+ errorMessage = "Unable to connect to wallet. Please ensure it's installed and unlocked, then try again.";
} else if (error.message.includes("Wallet is not connected")) {
errorMessage = "Wallet connection was lost. Please reconnect your wallet and try again.";
} else {
@@ -251,7 +240,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
};
const isWalletAvailable = (): boolean => {
- return authServiceRef.current.getWalletInfo()?.type === 'phantom';
+ return isConnected && !!address;
};
return (
@@ -261,8 +250,6 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
isAuthenticated: !!currentUser?.ordinalOwnership,
isAuthenticating,
verificationStatus,
- connectWallet,
- disconnectWallet,
verifyOrdinal,
delegateKey,
isDelegationValid,
diff --git a/src/lib/identity/wallets/appkit.ts b/src/lib/identity/wallets/appkit.ts
new file mode 100644
index 0000000..751ed66
--- /dev/null
+++ b/src/lib/identity/wallets/appkit.ts
@@ -0,0 +1,49 @@
+import { AppKitOptions } from '@reown/appkit'
+import { BitcoinAdapter } from '@reown/appkit-adapter-bitcoin'
+import { WagmiAdapter } from '@reown/appkit-adapter-wagmi'
+import { createStorage } from 'wagmi'
+import { mainnet, bitcoin, AppKitNetwork } from '@reown/appkit/networks'
+
+const networks: [AppKitNetwork, ...AppKitNetwork[]] = [mainnet, bitcoin]
+
+const projectId = process.env.VITE_REOWN_SECRET || '2ead96ea166a03e5ab50e5c190532e72'
+
+if (!projectId) {
+ throw new Error('VITE_REOWN_SECRET is not defined. Please set it in your .env file')
+}
+
+export const wagmiAdapter = new WagmiAdapter({
+ storage: createStorage({ storage: localStorage }),
+ ssr: false, // Set to false for Vite/React apps
+ projectId,
+ networks
+})
+
+// Export the Wagmi config for the provider
+export const config = wagmiAdapter.wagmiConfig
+
+const bitcoinAdapter = new BitcoinAdapter({
+ projectId
+
+})
+
+const metadata = {
+ name: 'OpChan',
+ description: 'Decentralized forum powered by Bitcoin Ordinals',
+ url: process.env.NODE_ENV === 'production' ? 'https://opchan.app' : 'http://localhost:8080',
+ icons: ['https://opchan.com/logo.png']
+}
+
+export const appkitConfig: AppKitOptions = {
+ adapters: [wagmiAdapter, bitcoinAdapter],
+ networks,
+ metadata,
+ projectId,
+ features: {
+ analytics: false,
+ socials: false,
+ allWallets: false,
+ },
+ enableWalletConnect: false
+
+}
diff --git a/src/lib/identity/wallets/index.ts b/src/lib/identity/wallets/index.ts
deleted file mode 100644
index 9899d2f..0000000
--- a/src/lib/identity/wallets/index.ts
+++ /dev/null
@@ -1,216 +0,0 @@
-import { PhantomWalletAdapter } from './phantom';
-import { KeyDelegation } from '../signatures/key-delegation';
-import { DelegationInfo } from '../signatures/types';
-
-export type WalletType = 'phantom';
-
-export interface WalletInfo {
- address: string;
- type: WalletType;
- delegated: boolean;
- delegationExpiry?: number;
-}
-
-/**
- * Service for managing wallet connections and key delegation
- */
-export class WalletService {
- // Default delegation validity period: 24 hours
- private static readonly DEFAULT_DELEGATION_PERIOD = 24 * 60 * 60 * 1000;
-
- private keyDelegation: KeyDelegation;
- private phantomAdapter: PhantomWalletAdapter;
-
- constructor() {
- this.keyDelegation = new KeyDelegation();
- this.phantomAdapter = new PhantomWalletAdapter();
- }
-
- /**
- * Checks if a specific wallet type is available in the browser
- */
- public isWalletAvailable(type: WalletType): boolean {
- return this.phantomAdapter.isInstalled();
- }
-
- /**
- * Check if wallet is available and can be connected
- */
- public async canConnectWallet(type: WalletType = 'phantom'): Promise {
- if (!this.isWalletAvailable(type)) {
- return false;
- }
-
- try {
- const isConnected = await this.phantomAdapter.isConnected();
- return isConnected;
- } catch (error) {
- console.debug('WalletService: Cannot connect wallet:', error);
- return false;
- }
- }
-
- /**
- * Connect to a specific wallet type
- * @param type The wallet type to connect to
- * @returns Promise resolving to the wallet's address
- */
- public async connectWallet(type: WalletType = 'phantom'): Promise {
- return await this.phantomAdapter.connect();
- }
-
- /**
- * Disconnect the current wallet
- * @param type The wallet type to disconnect
- */
- public async disconnectWallet(type: WalletType): Promise {
- this.keyDelegation.clearDelegation(); // Clear any delegation
- await this.phantomAdapter.disconnect();
- }
-
- /**
- * Get the current wallet information from local storage
- * @returns The current wallet info or null if not connected
- */
- public getWalletInfo(): WalletInfo | null {
- const userJson = localStorage.getItem('opchan-user');
- if (!userJson) return null;
-
- try {
- const user = JSON.parse(userJson);
- const delegation = this.keyDelegation.retrieveDelegation();
-
- return {
- address: user.address,
- type: 'phantom',
- delegated: !!delegation && this.keyDelegation.isDelegationValid(),
- delegationExpiry: delegation?.expiryTimestamp
- };
- } catch (e) {
- console.error('Failed to parse user data', e);
- return null;
- }
- }
-
- /**
- * Set up key delegation for the connected wallet
- * @param bitcoinAddress The Bitcoin address to delegate from
- * @param walletType The wallet type
- * @param validityPeriod Milliseconds the delegation should be valid for
- * @returns Promise resolving to the delegation info
- */
- public async setupKeyDelegation(
- bitcoinAddress: string,
- walletType: WalletType,
- validityPeriod: number = WalletService.DEFAULT_DELEGATION_PERIOD
- ): Promise> {
- console.debug('WalletService: Starting key delegation for address:', bitcoinAddress);
-
- let isConnected = await this.phantomAdapter.isConnected();
- console.debug('WalletService: Initial wallet connection check result:', isConnected);
-
- if (!isConnected) {
- console.debug('WalletService: Wallet not connected, attempting to connect automatically');
- try {
- await this.phantomAdapter.connect();
- isConnected = await this.phantomAdapter.isConnected();
- console.debug('WalletService: Auto-connection result:', isConnected);
- } catch (error) {
- console.error('WalletService: Failed to auto-connect wallet:', error);
- throw new Error('Failed to connect wallet. Please ensure Phantom wallet is installed and try again.');
- }
- }
-
- if (!isConnected) {
- console.error('WalletService: Wallet is still not connected after auto-connection attempt');
- throw new Error('Wallet is not connected. Please connect your wallet first.');
- }
-
- // Generate browser keypair
- const keypair = this.keyDelegation.generateKeypair();
- console.debug('WalletService: Generated browser keypair');
-
- // Calculate expiry in hours
- const expiryHours = validityPeriod / (60 * 60 * 1000);
-
- // Create delegation message
- const delegationMessage = this.keyDelegation.createDelegationMessage(
- keypair.publicKey,
- bitcoinAddress,
- Date.now() + validityPeriod
- );
- console.debug('WalletService: Created delegation message');
-
- // Sign the delegation message with the Bitcoin wallet
- console.debug('WalletService: Requesting signature from wallet');
- const signature = await this.phantomAdapter.signMessage(delegationMessage);
- console.debug('WalletService: Received signature from wallet');
-
- // Create and store the delegation
- const delegationInfo = this.keyDelegation.createDelegation(
- bitcoinAddress,
- signature,
- keypair.publicKey,
- keypair.privateKey,
- expiryHours
- );
-
- this.keyDelegation.storeDelegation(delegationInfo);
- console.debug('WalletService: Stored delegation info');
-
- // Return delegation info (excluding private key)
- return {
- signature,
- expiryTimestamp: delegationInfo.expiryTimestamp,
- browserPublicKey: keypair.publicKey,
- bitcoinAddress
- };
- }
-
- /**
- * Signs a message using the delegated browser key
- * @param message The message to sign
- * @returns Promise resolving to the signature or null if no valid delegation
- */
- public async signMessage(message: string): Promise {
- return this.keyDelegation.signMessage(message);
- }
-
- /**
- * Verifies a message signature against a public key
- * @param message The original message
- * @param signature The signature to verify
- * @param publicKey The public key to verify against
- * @returns Promise resolving to a boolean indicating if the signature is valid
- */
- public async verifySignature(
- message: string,
- signature: string,
- publicKey: string
- ): Promise {
- return this.keyDelegation.verifySignature(message, signature, publicKey);
- }
-
- /**
- * Checks if the current key delegation is valid
- * @returns boolean indicating if the delegation is valid
- */
- public isDelegationValid(): boolean {
- return this.keyDelegation.isDelegationValid();
- }
-
- /**
- * Gets the time remaining on the current delegation
- * @returns Time remaining in milliseconds, or 0 if expired/no delegation
- */
- public getDelegationTimeRemaining(): number {
- return this.keyDelegation.getDelegationTimeRemaining();
- }
-
- /**
- * Clears the stored delegation
- */
- public clearDelegation(): void {
- this.keyDelegation.clearDelegation();
- }
-}
\ No newline at end of file
diff --git a/src/lib/identity/wallets/phantom.ts b/src/lib/identity/wallets/phantom.ts
deleted file mode 100644
index 0f8bd48..0000000
--- a/src/lib/identity/wallets/phantom.ts
+++ /dev/null
@@ -1,216 +0,0 @@
-import { sha512 } from '@noble/hashes/sha2';
-import * as ed from '@noble/ed25519';
-import { PhantomBitcoinProvider, PhantomWallet, WalletConnectionStatus } from './types';
-
-ed.etc.sha512Sync = (...m) => sha512(ed.etc.concatBytes(...m));
-
-/**
- * PhantomWalletAdapter provides methods for connecting to and interacting with
- * the Phantom wallet for Bitcoin operations.
- */
-export class PhantomWalletAdapter {
- private provider: PhantomWallet | null = null;
- private btcProvider: PhantomBitcoinProvider | null = null;
- private connectionStatus: WalletConnectionStatus = WalletConnectionStatus.Disconnected;
- private currentAccount: string | null = null;
-
- constructor() {
- this.checkWalletAvailability();
- this.restoreConnectionState();
- }
-
- /**
- * Restore connection state from existing wallet connection
- */
- private async restoreConnectionState(): Promise {
- if (typeof window === 'undefined' || !window?.phantom?.bitcoin) {
- console.debug('PhantomWalletAdapter: No wallet available for connection restoration');
- return;
- }
-
- try {
- console.debug('PhantomWalletAdapter: Attempting to restore connection state');
- this.provider = window.phantom;
- this.btcProvider = window.phantom.bitcoin;
-
- // Check if wallet is already connected by trying to get accounts
- if (this.btcProvider?.requestAccounts) {
- const btcAccounts = await this.btcProvider.requestAccounts();
-
- if (btcAccounts && btcAccounts.length > 0) {
- const ordinalAccount = btcAccounts.find(acc => acc.purpose === 'ordinals');
- const account = ordinalAccount || btcAccounts[0];
-
- this.currentAccount = account.address;
- this.connectionStatus = WalletConnectionStatus.Connected;
- console.debug('PhantomWalletAdapter: Successfully restored connection for account:', account.address);
- } else {
- console.debug('PhantomWalletAdapter: No accounts found during connection restoration');
- }
- } else {
- console.debug('PhantomWalletAdapter: requestAccounts method not available');
- }
- } catch (error) {
- // If we can't restore the connection, that's okay - user will need to reconnect
- console.debug('PhantomWalletAdapter: Could not restore existing wallet connection:', error);
- this.connectionStatus = WalletConnectionStatus.Disconnected;
- }
- }
-
- public getStatus(): WalletConnectionStatus {
- return this.connectionStatus;
- }
-
- /**
- * Check if the wallet is actually connected by attempting to get accounts
- */
- public async isConnected(): Promise {
- if (!this.btcProvider) {
- return false;
- }
-
- try {
- if (this.btcProvider.requestAccounts) {
- const accounts = await this.btcProvider.requestAccounts();
- return accounts && accounts.length > 0;
- }
- return false;
- } catch (error) {
- console.debug('Error checking wallet connection:', error);
- return false;
- }
- }
-
- public isInstalled(): boolean {
- if (typeof window === 'undefined') {
- return false;
- }
- return !!(window?.phantom?.bitcoin?.isPhantom);
- }
-
- async connect(): Promise {
- this.connectionStatus = WalletConnectionStatus.Connecting;
-
- try {
- if (!window?.phantom?.bitcoin) {
- this.connectionStatus = WalletConnectionStatus.NotDetected;
- return Promise.reject(new Error('Phantom wallet not detected. Please install Phantom wallet.'));
- }
-
- this.provider = window.phantom;
- this.btcProvider = window.phantom.bitcoin;
-
- if (this.btcProvider?.connect) {
- await this.btcProvider.connect();
- }
-
- if (this.btcProvider?.requestAccounts) {
- const btcAccounts = await this.btcProvider.requestAccounts();
-
- if (!btcAccounts || btcAccounts.length === 0) {
- this.connectionStatus = WalletConnectionStatus.Disconnected;
- throw new Error('No accounts found');
- }
-
- const ordinalAccount = btcAccounts.find(acc => acc.purpose === 'ordinals');
- const account = ordinalAccount || btcAccounts[0];
-
- this.currentAccount = account.address;
- this.connectionStatus = WalletConnectionStatus.Connected;
- return account.address;
- } else {
- throw new Error('requestAccounts method not available on wallet provider');
- }
- } catch (error) {
- this.connectionStatus = window?.phantom?.bitcoin
- ? WalletConnectionStatus.Disconnected
- : WalletConnectionStatus.NotDetected;
-
- throw error;
- }
- }
-
- async disconnect(): Promise {
- if (this.btcProvider && this.btcProvider.disconnect) {
- try {
- await this.btcProvider.disconnect();
- } catch (error) {
- console.error('Error disconnecting from Phantom wallet:', error);
- }
- }
-
- this.provider = null;
- this.btcProvider = null;
- this.currentAccount = null;
- this.connectionStatus = WalletConnectionStatus.Disconnected;
- }
-
- async signMessage(message: string): Promise {
- console.debug('PhantomWalletAdapter: signMessage called, btcProvider:', !!this.btcProvider, 'currentAccount:', this.currentAccount);
-
- if (!this.btcProvider && window?.phantom?.bitcoin) {
- console.debug('PhantomWalletAdapter: Attempting to restore connection before signing');
- await this.restoreConnectionState();
- }
-
- if (!this.btcProvider || !this.currentAccount) {
- console.debug('PhantomWalletAdapter: Wallet not connected, attempting to connect automatically');
- try {
- await this.connect();
- } catch (error) {
- console.error('PhantomWalletAdapter: Failed to auto-connect wallet:', error);
- throw new Error('Failed to connect wallet. Please ensure Phantom wallet is installed and try again.');
- }
- }
-
- if (!this.btcProvider) {
- console.error('PhantomWalletAdapter: Wallet is not connected - no btcProvider');
- throw new Error('Wallet is not connected');
- }
-
- if (!this.currentAccount) {
- console.error('PhantomWalletAdapter: No active account to sign with');
- throw new Error('No active account to sign with');
- }
-
- try {
- if (!this.btcProvider.signMessage) {
- throw new Error('signMessage method not available on wallet provider');
- }
-
- console.debug('PhantomWalletAdapter: Signing message for account:', this.currentAccount);
- const messageBytes = new TextEncoder().encode(message);
-
- const { signature } = await this.btcProvider.signMessage(
- this.currentAccount,
- messageBytes
- );
-
- if (signature instanceof Uint8Array) {
- const binString = String.fromCodePoint(...signature);
- return btoa(binString);
- }
-
- return String(signature);
- } catch (error) {
- console.error('PhantomWalletAdapter: Error signing message:', error);
- throw error;
- }
- }
-
- private checkWalletAvailability(): void {
- if (typeof window === 'undefined') {
- this.connectionStatus = WalletConnectionStatus.NotDetected;
- return;
- }
-
- const isPhantomInstalled = window?.phantom?.bitcoin || window?.phantom;
-
- if (!isPhantomInstalled) {
- this.connectionStatus = WalletConnectionStatus.NotDetected;
- } else {
- this.connectionStatus = WalletConnectionStatus.Disconnected;
- }
- }
-
-}
diff --git a/src/types/global.d.ts b/src/types/global.d.ts
new file mode 100644
index 0000000..fd8ebf5
--- /dev/null
+++ b/src/types/global.d.ts
@@ -0,0 +1,4 @@
+import 'react';
+
+// Ensures file is treated as a module
+export {};
\ No newline at end of file
diff --git a/src/types/index.ts b/src/types/index.ts
index 95a2d01..0999a79 100644
--- a/src/types/index.ts
+++ b/src/types/index.ts
@@ -3,12 +3,23 @@ import { CellMessage, CommentMessage, PostMessage, VoteMessage, ModerateMessage
export type OpchanMessage = CellMessage | PostMessage | CommentMessage | VoteMessage | ModerateMessage;
export interface User {
- address: string;
+ address: string;
+ walletType: 'bitcoin' | 'ethereum';
+
+ // Bitcoin-specific
ordinalOwnership?: boolean | { id: string; details: string };
+
+ // Ethereum-specific
+ ensName?: string;
+ ensAvatar?: string;
+ ensOwnership?: boolean;
+
+ verificationStatus: 'verified' | 'unverified';
+
signature?: string;
lastChecked?: number;
browserPubKey?: string; // Browser-generated public key for key delegation
- delegationSignature?: string; // Signature from Bitcoin wallet for delegation
+ delegationSignature?: string; // Signature from Bitcoin/Ethereum wallet for delegation
delegationExpiry?: number; // When the delegation expires
}