fix: message signing

This commit is contained in:
Danish Arora 2025-09-24 18:58:13 +05:30
parent 0ad1cce551
commit 367cb72834
No known key found for this signature in database
GPG Key ID: 1C6EF37CDAE1426E
6 changed files with 69 additions and 69 deletions

View File

@ -3,7 +3,6 @@ import { Link, useLocation } from 'react-router-dom';
import { useAuth, useForum, useNetwork } from '@/hooks';
import { EVerificationStatus } from '@opchan/core';
import { localDatabase } from '@opchan/core';
import { DelegationFullStatus } from '@opchan/core';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
@ -49,9 +48,7 @@ import { WalletWizard } from '@/components/ui/wallet-wizard';
import { WakuHealthDot } from '@/components/ui/waku-health-indicator';
const Header = () => {
const { currentUser, delegationStatus } = useAuth();
const [delegationInfo, setDelegationInfo] =
useState<DelegationFullStatus | null>(null);
const { currentUser, delegationInfo } = useAuth();
const {statusMessage} = useNetwork();
const location = useLocation()
@ -76,13 +73,6 @@ const Header = () => {
const [walletWizardOpen, setWalletWizardOpen] = useState(false);
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
React.useEffect(() => {
delegationStatus().then(setDelegationInfo).catch(console.error);
}, [delegationStatus]);
useEffect(() => {
console.log({currentUser})

View File

@ -1,8 +1,8 @@
import React, { useState, useEffect } from 'react';
import React from 'react';
import { Button } from './button';
import { useAuth } from '@opchan/react';
import { CheckCircle, AlertCircle, Trash2 } from 'lucide-react';
import { DelegationDuration, DelegationFullStatus } from '@opchan/core';
import { DelegationDuration } from '@opchan/core';
interface DelegationStepProps {
onComplete: () => void;
@ -17,15 +17,7 @@ export function DelegationStep({
isLoading,
setIsLoading,
}: DelegationStepProps) {
const { currentUser, isAuthenticating, getDelegationStatus } = useAuth();
const [delegationInfo, setDelegationInfo] =
useState<DelegationFullStatus | null>(null);
const { delegateKey, clearDelegation } = useAuth();
// Load delegation status
useEffect(() => {
getDelegationStatus().then(setDelegationInfo).catch(console.error);
}, [getDelegationStatus]);
const { currentUser, delegationInfo, delegate, clearDelegation } = useAuth();
const [selectedDuration, setSelectedDuration] =
React.useState<DelegationDuration>('7days');
@ -42,11 +34,11 @@ export function DelegationStep({
setDelegationResult(null);
try {
const success = await delegateKey(selectedDuration);
const success = await delegate(selectedDuration);
if (success) {
const expiryDate = currentUser.delegationExpiry
? new Date(currentUser.delegationExpiry).toLocaleString()
const expiryDate = delegationInfo?.expiresAt
? delegationInfo.expiresAt.toLocaleString()
: `${selectedDuration === '7days' ? '1 week' : '30 days'} from now`;
setDelegationResult({
@ -211,13 +203,7 @@ export function DelegationStep({
<div className="flex justify-end">
<Button
onClick={async () => {
const ok = await clearDelegation();
if (ok) {
// Refresh status so UI immediately reflects cleared state
getDelegationStatus()
.then(setDelegationInfo)
.catch(console.error);
}
await clearDelegation();
}}
variant="outline"
size="sm"
@ -245,7 +231,7 @@ export function DelegationStep({
<Button
onClick={handleDelegate}
className="w-full bg-blue-600 hover:bg-blue-700 text-white"
disabled={isLoading || isAuthenticating}
disabled={isLoading}
>
{isLoading ? 'Delegating...' : 'Delegate Key'}
</Button>

View File

@ -40,7 +40,7 @@ export default function ProfilePage() {
const { toast } = useToast();
// Get current user from auth context for the address
const { currentUser, delegation } = useAuth();
const { currentUser, delegationInfo } = useAuth();
const address = currentUser?.address;
// Debug current user ENS info
@ -461,7 +461,7 @@ export default function ProfilePage() {
<Shield className="h-5 w-5 text-cyber-accent" />
Security
</div>
{delegation.hasDelegation && (
{delegationInfo.hasDelegation && (
<Button
variant="outline"
size="sm"
@ -469,7 +469,7 @@ export default function ProfilePage() {
className="border-cyber-muted/30 text-cyber-neutral hover:bg-cyber-muted/30"
>
<Settings className="w-4 h-4 mr-2" />
{delegation.isValid ? 'Renew' : 'Setup'}
{delegationInfo.isValid ? 'Renew' : 'Setup'}
</Button>
)}
</CardTitle>
@ -482,25 +482,25 @@ export default function ProfilePage() {
Delegation
</span>
<Badge
variant={delegation.isValid ? 'default' : 'secondary'}
variant={delegationInfo.isValid ? 'default' : 'secondary'}
className={
delegation.isValid
delegationInfo.isValid
? 'bg-green-500/20 text-green-400 border-green-500/30'
: 'bg-red-500/20 text-red-400 border-red-500/30'
}
>
{delegation.isValid ? 'Active' : 'Inactive'}
{delegationInfo.isValid ? 'Active' : 'Inactive'}
</Badge>
</div>
{/* Expiry Date */}
{delegation.expiresAt && (
{delegationInfo.expiresAt && (
<div className="space-y-1">
<span className="text-xs text-cyber-neutral">
Valid until
</span>
<div className="text-sm font-mono text-cyber-light">
{delegation.expiresAt.toLocaleDateString()}
{delegationInfo.expiresAt.toLocaleDateString()}
</div>
</div>
)}
@ -513,12 +513,12 @@ export default function ProfilePage() {
<Badge
variant="outline"
className={
delegation.isValid
delegationInfo.isValid
? 'text-green-400 border-green-500/30 bg-green-500/10'
: 'text-red-400 border-red-500/30 bg-red-500/10'
}
>
{delegation.isValid ? 'Valid' : 'Not signed'}
{delegationInfo.isValid ? 'Valid' : 'Not signed'}
</Badge>
</div>
</div>
@ -530,17 +530,17 @@ export default function ProfilePage() {
</Label>
<div className="flex items-center gap-2">
<div className="flex-1 font-mono text-xs bg-cyber-dark/50 border border-cyber-muted/30 px-2 py-1 rounded text-cyber-light">
{delegation.publicKey
? `${delegation.publicKey.slice(0, 12)}...${delegation.publicKey.slice(-8)}`
{delegationInfo.publicKey
? `${delegationInfo.publicKey.slice(0, 12)}...${delegationInfo.publicKey.slice(-8)}`
: 'Not delegated'}
</div>
{delegation.publicKey && (
{delegationInfo.publicKey && (
<Button
variant="outline"
size="sm"
onClick={() =>
copyToClipboard(
delegation.publicKey!,
delegationInfo.publicKey!,
'Public Key'
)
}
@ -553,7 +553,7 @@ export default function ProfilePage() {
</div>
{/* Warning for expired delegation */}
{(!delegation.isValid && delegation.hasDelegation) && (
{(!delegationInfo.isValid && delegationInfo.hasDelegation) && (
<div className="p-3 bg-orange-500/10 border border-orange-500/30 rounded-md">
<div className="flex items-center gap-2 text-orange-400">
<AlertTriangle className="w-4 h-4" />

View File

@ -1,7 +1,9 @@
import * as React from 'react';
import { OpChanProvider, type WalletAdapter, type WalletAdapterAccount } from '@opchan/react';
import { useAppKitAccount } from '@reown/appkit/react';
import { useAppKitAccount, modal } from '@reown/appkit/react';
import { AppKit } from '@reown/appkit';
import type { OpChanClientConfig } from '@opchan/core';
import { walletManager } from '@opchan/core';
interface Props { config: OpChanClientConfig; children: React.ReactNode }
@ -23,16 +25,31 @@ export const OpchanWithAppKit: React.FC<Props> = ({ config, children }) => {
listenersRef.current.add(cb);
return () => { listenersRef.current.delete(cb); };
},
}), [getCurrent]);
}), [getCurrent]);
// Notify listeners when AppKit account changes
React.useEffect(() => {
const account = getCurrent();
listenersRef.current.forEach(cb => {
try { cb(account); } catch (e) { /* ignore */ }
try { cb(account); } catch { /* ignore */ }
});
}, [getCurrent]);
React.useEffect(() => {
if (!modal) return;
try {
const hasBtc = btc.isConnected && !!btc.address;
const hasEth = eth.isConnected && !!eth.address;
if (hasBtc || hasEth) {
walletManager.create(modal as AppKit, btc, eth);
} else if (walletManager.hasInstance()) {
walletManager.clear();
}
} catch (err) {
console.warn('WalletManager initialization error', err);
}
}, [btc, btc.isConnected, btc.address, eth, eth.isConnected, eth.address]);
return (
<OpChanProvider config={config} walletAdapter={adapter}>
{children}

View File

@ -10,7 +10,6 @@ import { config } from './config';
import { Provider } from '@reown/appkit-controllers';
import { WalletInfo, ActiveWallet } from './types';
import { Inscription } from 'ordiscan';
import { environment } from '../utils/environment';
export class WalletManager {
private static instance: WalletManager | null = null;

View File

@ -6,6 +6,7 @@ import {
EVerificationStatus,
DelegationDuration,
EDisplayPreference,
walletManager,
} from '@opchan/core';
import type { DelegationFullStatus } from '@opchan/core';
@ -101,17 +102,17 @@ export function useAuth() {
}, [client, currentUser]);
const delegate = React.useCallback(async (
signFunction: (message: string) => Promise<string>,
duration: DelegationDuration = '7days',
): Promise<boolean> => {
const user = currentUser;
if (!user) return false;
try {
const signer = ((message: string) => walletManager.getInstance().signMessage(message));
const ok = await client.delegation.delegate(
user.address,
user.walletType,
duration,
signFunction,
signer,
);
const status = await client.delegation.getStatus(user.address, user.walletType);
@ -132,12 +133,18 @@ export function useAuth() {
return client.delegation.getStatus(user.address, user.walletType);
}, [client, currentUser]);
const clearDelegation = React.useCallback(async () => {
await client.delegation.clear();
setOpchanState(prev => ({
...prev,
session: { ...prev.session, delegation: null },
}));
const clearDelegation = React.useCallback(async (): Promise<boolean> => {
try {
await client.delegation.clear();
setOpchanState(prev => ({
...prev,
session: { ...prev.session, delegation: null },
}));
return true;
} catch (e) {
console.error('clearDelegation failed', e);
return false;
}
}, [client]);
const updateProfile = React.useCallback(async (updates: { callSign?: string; displayPreference?: EDisplayPreference }): Promise<boolean> => {
@ -167,19 +174,20 @@ export function useAuth() {
}
}, [client, currentUser]);
const delegationInfo = React.useMemo<DelegationFullStatus & { expiresAt?: Date }>(() => {
const base: DelegationFullStatus =
delegation ?? ({ hasDelegation: false, isValid: false } as const);
const expiresAt = base?.proof?.expiryTimestamp
? new Date(base.proof.expiryTimestamp)
: undefined;
return { ...base, expiresAt };
}, [delegation]);
return {
currentUser,
verificationStatus,
isAuthenticated: currentUser !== null,
// Provide a stable, non-null delegation object for UI safety
delegation: ((): DelegationFullStatus & { expiresAt?: Date } => {
const base: DelegationFullStatus =
delegation ?? ({ hasDelegation: false, isValid: false } as const);
const expiresAt = base?.proof?.expiryTimestamp
? new Date(base.proof.expiryTimestamp)
: undefined;
return { ...base, expiresAt };
})(),
delegationInfo,
connect,
disconnect,
verifyOwnership,