2025-09-02 10:48:49 +05:30
|
|
|
import React, { createContext, useState, useEffect, useMemo } from 'react';
|
2025-04-15 16:28:03 +05:30
|
|
|
import { useToast } from '@/components/ui/use-toast';
|
2025-08-30 18:34:50 +05:30
|
|
|
import { OpchanMessage } from '@/types/forum';
|
|
|
|
|
import { User, EVerificationStatus, DisplayPreference } from '@/types/identity';
|
2025-09-02 10:48:49 +05:30
|
|
|
import { WalletManager } from '@/lib/wallet';
|
2025-09-02 11:06:18 +05:30
|
|
|
import { DelegationManager, DelegationDuration, DelegationFullStatus } from '@/lib/delegation';
|
2025-08-06 17:21:56 +05:30
|
|
|
import { useAppKitAccount, useDisconnect, modal } from '@reown/appkit/react';
|
2025-04-15 16:28:03 +05:30
|
|
|
|
2025-08-30 18:34:50 +05:30
|
|
|
export type VerificationStatus =
|
|
|
|
|
| 'unverified'
|
|
|
|
|
| 'verified-none'
|
|
|
|
|
| 'verified-basic'
|
|
|
|
|
| 'verified-owner'
|
|
|
|
|
| 'verifying';
|
2025-04-24 14:31:00 +05:30
|
|
|
|
2025-04-15 16:28:03 +05:30
|
|
|
interface AuthContextType {
|
|
|
|
|
currentUser: User | null;
|
|
|
|
|
isAuthenticating: boolean;
|
2025-08-13 12:00:40 +05:30
|
|
|
isAuthenticated: boolean;
|
2025-04-24 14:31:00 +05:30
|
|
|
verificationStatus: VerificationStatus;
|
2025-08-13 12:00:40 +05:30
|
|
|
connectWallet: () => Promise<boolean>;
|
|
|
|
|
disconnectWallet: () => void;
|
2025-08-05 10:51:21 +05:30
|
|
|
verifyOwnership: () => Promise<boolean>;
|
2025-08-13 12:00:40 +05:30
|
|
|
delegateKey: (duration?: DelegationDuration) => Promise<boolean>;
|
2025-09-02 11:06:18 +05:30
|
|
|
getDelegationStatus: () => DelegationFullStatus;
|
2025-08-13 12:00:40 +05:30
|
|
|
clearDelegation: () => void;
|
|
|
|
|
signMessage: (message: OpchanMessage) => Promise<OpchanMessage | null>;
|
|
|
|
|
verifyMessage: (message: OpchanMessage) => boolean;
|
2025-04-15 16:28:03 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const AuthContext = createContext<AuthContextType | undefined>(undefined);
|
|
|
|
|
|
2025-07-30 13:22:06 +05:30
|
|
|
export { AuthContext };
|
|
|
|
|
|
2025-04-15 16:28:03 +05:30
|
|
|
export function AuthProvider({ children }: { children: React.ReactNode }) {
|
|
|
|
|
const [currentUser, setCurrentUser] = useState<User | null>(null);
|
|
|
|
|
const [isAuthenticating, setIsAuthenticating] = useState(false);
|
2025-08-30 18:34:50 +05:30
|
|
|
const [verificationStatus, setVerificationStatus] =
|
|
|
|
|
useState<VerificationStatus>('unverified');
|
2025-04-15 16:28:03 +05:30
|
|
|
const { toast } = useToast();
|
2025-08-30 18:34:50 +05:30
|
|
|
|
2025-08-05 10:51:21 +05:30
|
|
|
// Use AppKit hooks for multi-chain support
|
2025-08-30 18:34:50 +05:30
|
|
|
const bitcoinAccount = useAppKitAccount({ namespace: 'bip122' });
|
|
|
|
|
const ethereumAccount = useAppKitAccount({ namespace: 'eip155' });
|
|
|
|
|
|
2025-08-05 10:51:21 +05:30
|
|
|
// Determine which account is connected
|
|
|
|
|
const isBitcoinConnected = bitcoinAccount.isConnected;
|
|
|
|
|
const isEthereumConnected = ethereumAccount.isConnected;
|
|
|
|
|
const isConnected = isBitcoinConnected || isEthereumConnected;
|
2025-08-30 18:34:50 +05:30
|
|
|
|
2025-08-05 10:51:21 +05:30
|
|
|
// Get the active account info
|
|
|
|
|
const activeAccount = isBitcoinConnected ? bitcoinAccount : ethereumAccount;
|
|
|
|
|
const address = activeAccount.address;
|
2025-08-30 18:34:50 +05:30
|
|
|
|
2025-09-02 10:56:59 +05:30
|
|
|
// Create manager instances
|
2025-09-02 10:48:49 +05:30
|
|
|
const delegationManager = useMemo(() => new DelegationManager(), []);
|
2025-08-30 18:34:50 +05:30
|
|
|
|
2025-09-02 10:56:59 +05:30
|
|
|
// Create wallet manager when we have all dependencies
|
2025-08-06 17:21:56 +05:30
|
|
|
useEffect(() => {
|
2025-09-02 10:56:59 +05:30
|
|
|
if (modal && (bitcoinAccount.isConnected || ethereumAccount.isConnected)) {
|
|
|
|
|
try {
|
|
|
|
|
WalletManager.create(modal, bitcoinAccount, ethereumAccount);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.warn('Failed to create WalletManager:', error);
|
|
|
|
|
WalletManager.clear();
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
WalletManager.clear();
|
2025-08-06 17:21:56 +05:30
|
|
|
}
|
2025-09-02 10:56:59 +05:30
|
|
|
}, [bitcoinAccount, ethereumAccount]);
|
2025-09-02 10:48:49 +05:30
|
|
|
|
|
|
|
|
// Helper functions for user persistence
|
|
|
|
|
const 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;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const saveUser = (user: User): void => {
|
|
|
|
|
localStorage.setItem('opchan-user', JSON.stringify(user));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Helper function for ownership verification
|
|
|
|
|
const verifyUserOwnership = async (user: User): Promise<User> => {
|
|
|
|
|
if (user.walletType === 'bitcoin') {
|
|
|
|
|
// TODO: revert when the API is ready
|
|
|
|
|
// const response = await ordinalApi.getOperatorDetails(user.address);
|
|
|
|
|
// const hasOperators = response.has_operators;
|
|
|
|
|
const hasOperators = true;
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
...user,
|
|
|
|
|
ordinalDetails: hasOperators
|
|
|
|
|
? { ordinalId: 'mock', ordinalDetails: 'Mock ordinal for testing' }
|
|
|
|
|
: undefined,
|
|
|
|
|
verificationStatus: hasOperators
|
|
|
|
|
? EVerificationStatus.VERIFIED_OWNER
|
|
|
|
|
: EVerificationStatus.VERIFIED_BASIC,
|
|
|
|
|
lastChecked: Date.now(),
|
|
|
|
|
};
|
|
|
|
|
} else if (user.walletType === 'ethereum') {
|
|
|
|
|
try {
|
2025-09-02 10:56:59 +05:30
|
|
|
const walletInfo = WalletManager.hasInstance()
|
|
|
|
|
? await WalletManager.getInstance().getWalletInfo()
|
|
|
|
|
: null;
|
2025-09-02 10:48:49 +05:30
|
|
|
const hasENS = !!walletInfo?.ensName;
|
|
|
|
|
const ensName = walletInfo?.ensName;
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
...user,
|
|
|
|
|
ensDetails: hasENS && ensName ? { ensName } : undefined,
|
|
|
|
|
verificationStatus: hasENS
|
|
|
|
|
? EVerificationStatus.VERIFIED_OWNER
|
|
|
|
|
: EVerificationStatus.VERIFIED_BASIC,
|
|
|
|
|
lastChecked: Date.now(),
|
|
|
|
|
};
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Error verifying ENS ownership:', error);
|
|
|
|
|
return {
|
|
|
|
|
...user,
|
|
|
|
|
ensDetails: undefined,
|
|
|
|
|
verificationStatus: EVerificationStatus.VERIFIED_BASIC,
|
|
|
|
|
lastChecked: Date.now(),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
throw new Error('Unknown wallet type');
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Helper function for key delegation
|
|
|
|
|
const createUserDelegation = async (
|
|
|
|
|
user: User,
|
|
|
|
|
duration: DelegationDuration = '7days'
|
|
|
|
|
): Promise<boolean> => {
|
|
|
|
|
try {
|
2025-09-02 10:56:59 +05:30
|
|
|
if (!WalletManager.hasInstance()) {
|
|
|
|
|
throw new Error(
|
|
|
|
|
'Wallet not connected. Please ensure your wallet is connected.'
|
|
|
|
|
);
|
|
|
|
|
}
|
2025-09-02 10:48:49 +05:30
|
|
|
|
2025-09-02 10:56:59 +05:30
|
|
|
const walletManager = WalletManager.getInstance();
|
|
|
|
|
|
|
|
|
|
// Verify wallet type matches
|
|
|
|
|
if (walletManager.getWalletType() !== user.walletType) {
|
2025-09-02 10:48:49 +05:30
|
|
|
throw new Error(
|
2025-09-02 10:56:59 +05:30
|
|
|
`Expected ${user.walletType} wallet, but ${walletManager.getWalletType()} is connected.`
|
2025-09-02 10:48:49 +05:30
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-02 11:06:18 +05:30
|
|
|
// Use the simplified delegation method
|
|
|
|
|
return await delegationManager.delegate(
|
2025-09-02 10:48:49 +05:30
|
|
|
user.address,
|
2025-09-02 11:06:18 +05:30
|
|
|
user.walletType,
|
2025-09-02 10:48:49 +05:30
|
|
|
duration,
|
2025-09-02 11:06:18 +05:30
|
|
|
(message) => walletManager.signMessage(message)
|
2025-09-02 10:48:49 +05:30
|
|
|
);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error(
|
|
|
|
|
`Error creating key delegation for ${user.walletType}:`,
|
|
|
|
|
error
|
|
|
|
|
);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
};
|
2025-08-30 18:34:50 +05:30
|
|
|
|
2025-08-05 10:51:21 +05:30
|
|
|
// Sync with AppKit wallet state
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (isConnected && address) {
|
|
|
|
|
// Check if we have a stored user for this address
|
2025-09-02 10:48:49 +05:30
|
|
|
const storedUser = loadStoredUser();
|
2025-08-30 18:34:50 +05:30
|
|
|
|
2025-08-05 10:51:21 +05:30
|
|
|
if (storedUser && storedUser.address === address) {
|
|
|
|
|
// Use stored user data
|
|
|
|
|
setCurrentUser(storedUser);
|
|
|
|
|
setVerificationStatus(getVerificationStatus(storedUser));
|
2025-07-30 12:27:23 +05:30
|
|
|
} else {
|
2025-08-05 10:51:21 +05:30
|
|
|
// Create new user from AppKit wallet
|
|
|
|
|
const newUser: User = {
|
|
|
|
|
address,
|
|
|
|
|
walletType: isBitcoinConnected ? 'bitcoin' : 'ethereum',
|
2025-08-28 18:44:35 +05:30
|
|
|
verificationStatus: EVerificationStatus.VERIFIED_BASIC, // Connected wallets get basic verification by default
|
2025-08-30 18:34:50 +05:30
|
|
|
displayPreference: DisplayPreference.WALLET_ADDRESS,
|
2025-08-05 10:51:21 +05:30
|
|
|
lastChecked: Date.now(),
|
|
|
|
|
};
|
2025-07-30 12:27:23 +05:30
|
|
|
|
2025-08-18 14:07:01 +05:30
|
|
|
// For Ethereum wallets, try to check ENS ownership immediately
|
|
|
|
|
if (isEthereumConnected) {
|
2025-09-02 10:56:59 +05:30
|
|
|
try {
|
|
|
|
|
const walletManager = WalletManager.getInstance();
|
|
|
|
|
walletManager
|
|
|
|
|
.getWalletInfo()
|
|
|
|
|
.then(walletInfo => {
|
|
|
|
|
if (walletInfo?.ensName) {
|
|
|
|
|
const updatedUser = {
|
|
|
|
|
...newUser,
|
|
|
|
|
ensDetails: { ensName: walletInfo.ensName },
|
|
|
|
|
verificationStatus: EVerificationStatus.VERIFIED_OWNER,
|
|
|
|
|
};
|
|
|
|
|
setCurrentUser(updatedUser);
|
|
|
|
|
setVerificationStatus('verified-owner');
|
|
|
|
|
saveUser(updatedUser);
|
|
|
|
|
} else {
|
|
|
|
|
setCurrentUser(newUser);
|
|
|
|
|
setVerificationStatus('verified-basic');
|
|
|
|
|
saveUser(newUser);
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.catch(() => {
|
|
|
|
|
// Fallback to basic verification if ENS check fails
|
2025-08-30 18:34:50 +05:30
|
|
|
setCurrentUser(newUser);
|
|
|
|
|
setVerificationStatus('verified-basic');
|
2025-09-02 10:48:49 +05:30
|
|
|
saveUser(newUser);
|
2025-09-02 10:56:59 +05:30
|
|
|
});
|
|
|
|
|
} catch {
|
|
|
|
|
// WalletManager not ready, fallback to basic verification
|
|
|
|
|
setCurrentUser(newUser);
|
|
|
|
|
setVerificationStatus('verified-basic');
|
|
|
|
|
saveUser(newUser);
|
|
|
|
|
}
|
2025-08-18 14:07:01 +05:30
|
|
|
} else {
|
|
|
|
|
setCurrentUser(newUser);
|
|
|
|
|
setVerificationStatus('verified-basic');
|
2025-09-02 10:48:49 +05:30
|
|
|
saveUser(newUser);
|
2025-08-18 14:07:01 +05:30
|
|
|
}
|
2025-08-30 18:34:50 +05:30
|
|
|
|
2025-08-05 10:51:21 +05:30
|
|
|
const chainName = isBitcoinConnected ? 'Bitcoin' : 'Ethereum';
|
|
|
|
|
const displayName = `${address.slice(0, 6)}...${address.slice(-4)}`;
|
2025-08-30 18:34:50 +05:30
|
|
|
|
2025-04-24 16:30:50 +05:30
|
|
|
toast({
|
2025-08-30 18:34:50 +05:30
|
|
|
title: 'Wallet Connected',
|
2025-08-05 10:51:21 +05:30
|
|
|
description: `Connected to ${chainName} with ${displayName}`,
|
|
|
|
|
});
|
2025-08-30 18:34:50 +05:30
|
|
|
|
|
|
|
|
const verificationType = isBitcoinConnected
|
|
|
|
|
? 'Ordinal ownership'
|
|
|
|
|
: 'ENS ownership';
|
2025-08-05 10:51:21 +05:30
|
|
|
toast({
|
2025-08-30 18:34:50 +05:30
|
|
|
title: 'Action Required',
|
2025-08-18 14:07:01 +05:30
|
|
|
description: `You can participate in the forum now! Verify your ${verificationType} for premium features and delegate a signing key for better UX.`,
|
2025-04-24 16:30:50 +05:30
|
|
|
});
|
|
|
|
|
}
|
2025-08-05 10:51:21 +05:30
|
|
|
} else {
|
|
|
|
|
// Wallet disconnected
|
|
|
|
|
setCurrentUser(null);
|
2025-04-24 14:31:00 +05:30
|
|
|
setVerificationStatus('unverified');
|
2025-04-15 16:28:03 +05:30
|
|
|
}
|
2025-09-02 10:56:59 +05:30
|
|
|
}, [isConnected, address, isBitcoinConnected, isEthereumConnected, toast]);
|
2025-04-15 16:28:03 +05:30
|
|
|
|
2025-08-13 12:00:40 +05:30
|
|
|
const { disconnect } = useDisconnect();
|
|
|
|
|
|
|
|
|
|
const connectWallet = async (): Promise<boolean> => {
|
|
|
|
|
try {
|
|
|
|
|
if (modal) {
|
|
|
|
|
await modal.open();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Error connecting wallet:', error);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const disconnectWallet = (): void => {
|
|
|
|
|
disconnect();
|
|
|
|
|
};
|
|
|
|
|
|
2025-08-05 10:51:21 +05:30
|
|
|
const getVerificationStatus = (user: User): VerificationStatus => {
|
|
|
|
|
if (user.walletType === 'bitcoin') {
|
2025-09-02 10:48:49 +05:30
|
|
|
return user.ordinalDetails ? 'verified-owner' : 'verified-basic';
|
2025-08-05 10:51:21 +05:30
|
|
|
} else if (user.walletType === 'ethereum') {
|
2025-09-02 10:48:49 +05:30
|
|
|
return user.ensDetails ? 'verified-owner' : 'verified-basic';
|
2025-08-05 10:51:21 +05:30
|
|
|
}
|
|
|
|
|
return 'unverified';
|
2025-04-15 16:28:03 +05:30
|
|
|
};
|
|
|
|
|
|
2025-08-05 10:51:21 +05:30
|
|
|
const verifyOwnership = async (): Promise<boolean> => {
|
2025-04-23 08:22:50 +05:30
|
|
|
if (!currentUser || !currentUser.address) {
|
2025-04-15 16:28:03 +05:30
|
|
|
toast({
|
2025-08-30 18:34:50 +05:30
|
|
|
title: 'Not Connected',
|
|
|
|
|
description: 'Please connect your wallet first.',
|
|
|
|
|
variant: 'destructive',
|
2025-04-15 16:28:03 +05:30
|
|
|
});
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2025-08-30 18:34:50 +05:30
|
|
|
|
2025-04-15 16:28:03 +05:30
|
|
|
setIsAuthenticating(true);
|
2025-04-24 14:31:00 +05:30
|
|
|
setVerificationStatus('verifying');
|
2025-08-30 18:34:50 +05:30
|
|
|
|
2025-04-15 16:28:03 +05:30
|
|
|
try {
|
2025-08-30 18:34:50 +05:30
|
|
|
const verificationType =
|
|
|
|
|
currentUser.walletType === 'bitcoin' ? 'Ordinal' : 'ENS';
|
|
|
|
|
toast({
|
|
|
|
|
title: `Verifying ${verificationType}`,
|
|
|
|
|
description: `Checking your wallet for ${verificationType} ownership...`,
|
2025-04-24 14:31:00 +05:30
|
|
|
});
|
2025-08-30 18:34:50 +05:30
|
|
|
|
2025-09-02 10:48:49 +05:30
|
|
|
const updatedUser = await verifyUserOwnership(currentUser);
|
2025-04-15 16:28:03 +05:30
|
|
|
setCurrentUser(updatedUser);
|
2025-09-02 10:48:49 +05:30
|
|
|
saveUser(updatedUser);
|
2025-08-30 18:34:50 +05:30
|
|
|
|
2025-04-24 14:31:00 +05:30
|
|
|
// Update verification status
|
2025-08-05 10:51:21 +05:30
|
|
|
setVerificationStatus(getVerificationStatus(updatedUser));
|
2025-08-30 18:34:50 +05:30
|
|
|
|
|
|
|
|
if (updatedUser.walletType === 'bitcoin' && updatedUser.ordinalDetails) {
|
2025-04-23 08:22:50 +05:30
|
|
|
toast({
|
2025-08-30 18:34:50 +05:30
|
|
|
title: 'Ordinal Verified',
|
|
|
|
|
description:
|
|
|
|
|
'You now have premium access with higher relevance bonuses. We recommend delegating a key for better UX.',
|
2025-04-23 08:22:50 +05:30
|
|
|
});
|
2025-08-30 18:34:50 +05:30
|
|
|
} else if (
|
|
|
|
|
updatedUser.walletType === 'ethereum' &&
|
|
|
|
|
updatedUser.ensDetails
|
|
|
|
|
) {
|
2025-08-05 10:51:21 +05:30
|
|
|
toast({
|
2025-08-30 18:34:50 +05:30
|
|
|
title: 'ENS Verified',
|
|
|
|
|
description:
|
|
|
|
|
'You now have premium access with higher relevance bonuses. We recommend delegating a key for better UX.',
|
2025-08-05 10:51:21 +05:30
|
|
|
});
|
2025-04-23 08:22:50 +05:30
|
|
|
} else {
|
2025-08-30 18:34:50 +05:30
|
|
|
const verificationType =
|
|
|
|
|
updatedUser.walletType === 'bitcoin'
|
|
|
|
|
? 'Ordinal Operators'
|
|
|
|
|
: 'ENS domain';
|
2025-04-23 08:22:50 +05:30
|
|
|
toast({
|
2025-08-30 18:34:50 +05:30
|
|
|
title: 'Basic Access Granted',
|
2025-08-18 14:07:01 +05:30
|
|
|
description: `No ${verificationType} found, but you can still participate in the forum with your connected wallet.`,
|
2025-08-30 18:34:50 +05:30
|
|
|
variant: 'default',
|
2025-04-23 08:22:50 +05:30
|
|
|
});
|
|
|
|
|
}
|
2025-08-30 18:34:50 +05:30
|
|
|
|
2025-08-05 10:51:21 +05:30
|
|
|
return Boolean(
|
2025-08-30 18:34:50 +05:30
|
|
|
(updatedUser.walletType === 'bitcoin' && updatedUser.ordinalDetails) ||
|
|
|
|
|
(updatedUser.walletType === 'ethereum' && updatedUser.ensDetails)
|
2025-08-05 10:51:21 +05:30
|
|
|
);
|
2025-04-15 16:28:03 +05:30
|
|
|
} catch (error) {
|
2025-08-30 18:34:50 +05:30
|
|
|
console.error('Error verifying ownership:', error);
|
2025-04-24 14:31:00 +05:30
|
|
|
setVerificationStatus('unverified');
|
2025-08-30 18:34:50 +05:30
|
|
|
|
|
|
|
|
let errorMessage = 'Failed to verify ownership. Please try again.';
|
2025-04-23 08:22:50 +05:30
|
|
|
if (error instanceof Error) {
|
|
|
|
|
errorMessage = error.message;
|
|
|
|
|
}
|
2025-08-30 18:34:50 +05:30
|
|
|
|
2025-04-15 16:28:03 +05:30
|
|
|
toast({
|
2025-08-30 18:34:50 +05:30
|
|
|
title: 'Verification Error',
|
2025-04-23 08:22:50 +05:30
|
|
|
description: errorMessage,
|
2025-08-30 18:34:50 +05:30
|
|
|
variant: 'destructive',
|
2025-04-15 16:28:03 +05:30
|
|
|
});
|
2025-08-30 18:34:50 +05:30
|
|
|
|
2025-04-15 16:28:03 +05:30
|
|
|
return false;
|
|
|
|
|
} finally {
|
|
|
|
|
setIsAuthenticating(false);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2025-08-30 18:34:50 +05:30
|
|
|
const delegateKey = async (
|
|
|
|
|
duration: DelegationDuration = '7days'
|
|
|
|
|
): Promise<boolean> => {
|
2025-08-13 12:00:40 +05:30
|
|
|
if (!currentUser) {
|
2025-04-24 16:30:50 +05:30
|
|
|
toast({
|
2025-08-30 18:34:50 +05:30
|
|
|
title: 'No User Found',
|
|
|
|
|
description: 'Please connect your wallet first.',
|
|
|
|
|
variant: 'destructive',
|
2025-04-24 16:30:50 +05:30
|
|
|
});
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2025-08-30 18:34:50 +05:30
|
|
|
|
2025-04-24 16:30:50 +05:30
|
|
|
setIsAuthenticating(true);
|
2025-08-30 18:34:50 +05:30
|
|
|
|
2025-04-24 16:30:50 +05:30
|
|
|
try {
|
2025-08-13 12:00:40 +05:30
|
|
|
const durationText = duration === '7days' ? '1 week' : '30 days';
|
2025-04-24 16:30:50 +05:30
|
|
|
toast({
|
2025-08-30 18:34:50 +05:30
|
|
|
title: 'Starting Key Delegation',
|
2025-08-13 12:00:40 +05:30
|
|
|
description: `This will let you post, comment, and vote without approving each action for ${durationText}.`,
|
2025-04-24 16:30:50 +05:30
|
|
|
});
|
2025-08-30 18:34:50 +05:30
|
|
|
|
2025-09-02 10:48:49 +05:30
|
|
|
const success = await createUserDelegation(currentUser, duration);
|
|
|
|
|
if (!success) {
|
|
|
|
|
throw new Error('Failed to create key delegation');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Update user with delegation info
|
2025-09-02 11:06:18 +05:30
|
|
|
const delegationStatus = delegationManager.getStatus(
|
2025-09-02 10:48:49 +05:30
|
|
|
currentUser.address,
|
|
|
|
|
currentUser.walletType
|
2025-08-30 18:34:50 +05:30
|
|
|
);
|
|
|
|
|
|
2025-09-02 10:48:49 +05:30
|
|
|
const updatedUser = {
|
|
|
|
|
...currentUser,
|
2025-09-02 11:06:18 +05:30
|
|
|
browserPubKey: delegationStatus.publicKey || undefined,
|
2025-09-02 10:48:49 +05:30
|
|
|
delegationSignature: delegationStatus.isValid ? 'valid' : undefined,
|
|
|
|
|
delegationExpiry: delegationStatus.timeRemaining
|
|
|
|
|
? Date.now() + delegationStatus.timeRemaining
|
|
|
|
|
: undefined,
|
|
|
|
|
};
|
2025-08-30 18:34:50 +05:30
|
|
|
|
2025-07-30 13:22:06 +05:30
|
|
|
setCurrentUser(updatedUser);
|
2025-09-02 10:48:49 +05:30
|
|
|
saveUser(updatedUser);
|
2025-08-30 18:34:50 +05:30
|
|
|
|
2025-04-27 15:54:24 +05:30
|
|
|
// Format date for user-friendly display
|
2025-07-30 13:22:06 +05:30
|
|
|
const expiryDate = new Date(updatedUser.delegationExpiry!);
|
2025-04-27 15:54:24 +05:30
|
|
|
const formattedExpiry = expiryDate.toLocaleString();
|
2025-08-30 18:34:50 +05:30
|
|
|
|
2025-04-24 16:30:50 +05:30
|
|
|
toast({
|
2025-08-30 18:34:50 +05:30
|
|
|
title: 'Key Delegation Successful',
|
2025-04-27 15:54:24 +05:30
|
|
|
description: `You can now interact with the forum without additional wallet approvals until ${formattedExpiry}.`,
|
2025-04-24 16:30:50 +05:30
|
|
|
});
|
2025-08-30 18:34:50 +05:30
|
|
|
|
2025-04-24 16:30:50 +05:30
|
|
|
return true;
|
|
|
|
|
} catch (error) {
|
2025-08-30 18:34:50 +05:30
|
|
|
console.error('Error delegating key:', error);
|
|
|
|
|
|
|
|
|
|
let errorMessage = 'Failed to delegate key. Please try again.';
|
2025-04-24 16:30:50 +05:30
|
|
|
if (error instanceof Error) {
|
2025-08-05 10:51:21 +05:30
|
|
|
errorMessage = error.message;
|
2025-04-24 16:30:50 +05:30
|
|
|
}
|
2025-08-30 18:34:50 +05:30
|
|
|
|
2025-04-24 16:30:50 +05:30
|
|
|
toast({
|
2025-08-30 18:34:50 +05:30
|
|
|
title: 'Delegation Error',
|
2025-04-24 16:30:50 +05:30
|
|
|
description: errorMessage,
|
2025-08-30 18:34:50 +05:30
|
|
|
variant: 'destructive',
|
2025-04-24 16:30:50 +05:30
|
|
|
});
|
2025-08-30 18:34:50 +05:30
|
|
|
|
2025-04-24 16:30:50 +05:30
|
|
|
return false;
|
|
|
|
|
} finally {
|
|
|
|
|
setIsAuthenticating(false);
|
|
|
|
|
}
|
|
|
|
|
};
|
2025-08-05 10:51:21 +05:30
|
|
|
|
2025-09-02 11:06:18 +05:30
|
|
|
const getDelegationStatus = (): DelegationFullStatus => {
|
|
|
|
|
return delegationManager.getStatus(currentUser?.address, currentUser?.walletType);
|
2025-04-24 16:30:50 +05:30
|
|
|
};
|
|
|
|
|
|
2025-08-06 17:21:56 +05:30
|
|
|
const clearDelegation = (): void => {
|
2025-09-02 11:06:18 +05:30
|
|
|
delegationManager.clear();
|
2025-08-30 18:34:50 +05:30
|
|
|
|
2025-08-06 17:21:56 +05:30
|
|
|
// Update the current user to remove delegation info
|
|
|
|
|
if (currentUser) {
|
|
|
|
|
const updatedUser = {
|
|
|
|
|
...currentUser,
|
|
|
|
|
delegationExpiry: undefined,
|
2025-08-30 18:34:50 +05:30
|
|
|
browserPublicKey: undefined,
|
2025-08-06 17:21:56 +05:30
|
|
|
};
|
|
|
|
|
setCurrentUser(updatedUser);
|
2025-09-02 10:48:49 +05:30
|
|
|
saveUser(updatedUser);
|
2025-08-06 17:21:56 +05:30
|
|
|
}
|
2025-08-30 18:34:50 +05:30
|
|
|
|
2025-08-06 17:21:56 +05:30
|
|
|
toast({
|
2025-08-30 18:34:50 +05:30
|
|
|
title: 'Delegation Cleared',
|
|
|
|
|
description:
|
|
|
|
|
"Your delegated signing key has been removed. You'll need to delegate a new key to continue posting and voting.",
|
2025-08-06 17:21:56 +05:30
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
2025-08-05 10:51:21 +05:30
|
|
|
const messageSigning = {
|
2025-08-30 18:34:50 +05:30
|
|
|
signMessage: async (
|
|
|
|
|
message: OpchanMessage
|
|
|
|
|
): Promise<OpchanMessage | null> => {
|
2025-09-02 11:06:18 +05:30
|
|
|
return delegationManager.signMessage(message);
|
2025-08-05 10:51:21 +05:30
|
|
|
},
|
|
|
|
|
verifyMessage: (message: OpchanMessage): boolean => {
|
2025-09-02 11:06:18 +05:30
|
|
|
return delegationManager.verify(message);
|
2025-08-30 18:34:50 +05:30
|
|
|
},
|
2025-08-05 10:51:21 +05:30
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const value: AuthContextType = {
|
|
|
|
|
currentUser,
|
|
|
|
|
isAuthenticating,
|
2025-08-13 12:00:40 +05:30
|
|
|
isAuthenticated: Boolean(currentUser && isConnected),
|
2025-08-05 10:51:21 +05:30
|
|
|
verificationStatus,
|
2025-08-13 12:00:40 +05:30
|
|
|
connectWallet,
|
|
|
|
|
disconnectWallet,
|
2025-08-05 10:51:21 +05:30
|
|
|
verifyOwnership,
|
|
|
|
|
delegateKey,
|
2025-09-02 11:06:18 +05:30
|
|
|
getDelegationStatus,
|
2025-08-13 12:00:40 +05:30
|
|
|
clearDelegation,
|
|
|
|
|
signMessage: messageSigning.signMessage,
|
2025-08-30 18:34:50 +05:30
|
|
|
verifyMessage: messageSigning.verifyMessage,
|
2025-07-30 15:55:13 +05:30
|
|
|
};
|
|
|
|
|
|
2025-08-30 18:34:50 +05:30
|
|
|
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
|
2025-04-15 16:28:03 +05:30
|
|
|
}
|