From 88012154d39084f978013e4365d3049ebf7b594f Mon Sep 17 00:00:00 2001 From: Danish Arora Date: Mon, 18 Aug 2025 14:07:01 +0530 Subject: [PATCH] feat: anonymous interactions --- src/components/FeedSidebar.tsx | 10 ++++- src/components/Header.tsx | 11 +++-- src/components/PostDetail.tsx | 8 ++-- src/components/ui/author-display.tsx | 45 ++++++++++++++++++--- src/components/ui/verification-step.tsx | 16 ++++---- src/components/ui/wallet-wizard.tsx | 39 +++++++----------- src/contexts/AuthContext.tsx | 50 +++++++++++++++++------ src/contexts/ForumContext.tsx | 46 ++++++++++++++++++--- src/lib/forum/actions.ts | 51 ++++++++++++++++++++++-- src/lib/forum/relevance.ts | 35 ++++++++++------ src/lib/forum/types.ts | 1 + src/lib/identity/ordinal.ts | 2 +- src/lib/identity/services/AuthService.ts | 5 ++- src/types/index.ts | 2 +- 14 files changed, 239 insertions(+), 82 deletions(-) diff --git a/src/components/FeedSidebar.tsx b/src/components/FeedSidebar.tsx index be1fad2..a31314c 100644 --- a/src/components/FeedSidebar.tsx +++ b/src/components/FeedSidebar.tsx @@ -44,8 +44,10 @@ const FeedSidebar: React.FC = () => { // Ethereum wallet with ENS if (currentUser.walletType === 'ethereum') { - if (currentUser.ensName && verificationStatus === 'verified-owner') { + if (currentUser.ensName && (verificationStatus === 'verified-owner' || currentUser.ensOwnership)) { return ✓ Owns ENS: {currentUser.ensName}; + } else if (verificationStatus === 'verified-basic') { + return ✓ Connected Wallet; } else { return Read-only (No ENS detected); } @@ -53,8 +55,10 @@ const FeedSidebar: React.FC = () => { // Bitcoin wallet with Ordinal if (currentUser.walletType === 'bitcoin') { - if (verificationStatus === 'verified-owner') { + if (verificationStatus === 'verified-owner' || currentUser.ordinalOwnership) { return ✓ Owns Ordinal; + } else if (verificationStatus === 'verified-basic') { + return ✓ Connected Wallet; } else { return Read-only (No Ordinal detected); } @@ -62,6 +66,8 @@ const FeedSidebar: React.FC = () => { // Fallback cases switch (verificationStatus) { + case 'verified-basic': + return ✓ Connected Wallet; case 'verified-none': return Read Only; case 'verifying': diff --git a/src/components/Header.tsx b/src/components/Header.tsx index bd5e63e..7847df6 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -18,8 +18,7 @@ const Header = () => { verifyOwnership, delegateKey, isDelegationValid, - delegationTimeRemaining, - isWalletAvailable + delegationTimeRemaining } = useAuth(); const { isNetworkConnected, isRefreshing } = useForum(); const location = useLocation(); @@ -85,8 +84,10 @@ const Header = () => { return 'Verifying...'; case 'verified-none': return 'Read-Only Access'; - case 'verified-owner': + case 'verified-basic': return isDelegationValid() ? 'Full Access' : 'Setup Key'; + case 'verified-owner': + return isDelegationValid() ? 'Premium Access' : 'Setup Key'; default: return 'Setup Account'; } @@ -100,6 +101,8 @@ const Header = () => { return ; case 'verified-none': return ; + case 'verified-basic': + return isDelegationValid() ? : ; case 'verified-owner': return isDelegationValid() ? : ; default: @@ -115,6 +118,8 @@ const Header = () => { return 'outline'; case 'verified-none': return 'secondary'; + case 'verified-basic': + return isDelegationValid() ? 'default' : 'outline'; case 'verified-owner': return isDelegationValid() ? 'default' : 'outline'; default: diff --git a/src/components/PostDetail.tsx b/src/components/PostDetail.tsx index 516f877..2175f98 100644 --- a/src/components/PostDetail.tsx +++ b/src/components/PostDetail.tsx @@ -84,12 +84,12 @@ const PostDetail = () => { }; const handleVotePost = async (isUpvote: boolean) => { - if (verificationStatus !== 'verified-owner') return; + if (verificationStatus !== 'verified-owner' && verificationStatus !== 'verified-basic' && !currentUser?.ensOwnership && !currentUser?.ordinalOwnership) return; await votePost(post.id, isUpvote); }; const handleVoteComment = async (commentId: string, isUpvote: boolean) => { - if (verificationStatus !== 'verified-owner') return; + if (verificationStatus !== 'verified-owner' && verificationStatus !== 'verified-basic' && !currentUser?.ensOwnership && !currentUser?.ordinalOwnership) return; await voteComment(commentId, isUpvote); }; @@ -185,7 +185,7 @@ const PostDetail = () => { - {verificationStatus === 'verified-owner' ? ( + {(verificationStatus === 'verified-owner' || verificationStatus === 'verified-basic' || currentUser?.ensOwnership || currentUser?.ordinalOwnership) ? (
@@ -219,7 +219,7 @@ const PostDetail = () => {
) : (
-

Connect wallet and verify Ordinal ownership to comment

+

Connect wallet and verify ownership to comment

diff --git a/src/components/ui/author-display.tsx b/src/components/ui/author-display.tsx index 56c3a7b..c1b37fd 100644 --- a/src/components/ui/author-display.tsx +++ b/src/components/ui/author-display.tsx @@ -4,6 +4,7 @@ import { Shield, Crown } from 'lucide-react'; import { UserVerificationStatus } from '@/lib/forum/types'; import { getEnsName } from '@wagmi/core'; import { config } from '@/lib/identity/wallets/appkit'; +import { OrdinalAPI } from '@/lib/identity/ordinal'; interface AuthorDisplayProps { address: string; @@ -20,20 +21,52 @@ export function AuthorDisplay({ }: AuthorDisplayProps) { const userStatus = userVerificationStatus?.[address]; const [resolvedEns, setResolvedEns] = React.useState(undefined); + const [resolvedOrdinal, setResolvedOrdinal] = React.useState(undefined); + + // Heuristics for address types + const isEthereumAddress = address.startsWith('0x') && address.length === 42; + const isBitcoinAddress = !isEthereumAddress; // simple heuristic for our context // Lazily resolve ENS name for Ethereum addresses if not provided React.useEffect(() => { - const isEthereumAddress = address.startsWith('0x') && address.length === 42; + let cancelled = false; if (!userStatus?.ensName && isEthereumAddress) { getEnsName(config, { address: address as `0x${string}` }) - .then((name) => setResolvedEns(name || undefined)) - .catch(() => setResolvedEns(undefined)); + .then((name) => { if (!cancelled) setResolvedEns(name || undefined); }) + .catch(() => { if (!cancelled) setResolvedEns(undefined); }); + } else { + setResolvedEns(userStatus?.ensName); } + return () => { cancelled = true; }; // eslint-disable-next-line react-hooks/exhaustive-deps - }, [address]); + }, [address, isEthereumAddress, userStatus?.ensName]); - const hasENS = userStatus?.hasENS || Boolean(resolvedEns) || Boolean(userStatus?.ensName); - const hasOrdinal = userStatus?.hasOrdinal || false; + // Lazily check Ordinal ownership for Bitcoin addresses if not provided + React.useEffect(() => { + let cancelled = false; + const run = async () => { + console.log({ + isBitcoinAddress, userStatus + }) + if (isBitcoinAddress) { + try { + const api = new OrdinalAPI(); + const res = await api.getOperatorDetails(address); + if (!cancelled) setResolvedOrdinal(Boolean(res?.has_operators)); + } catch { + if (!cancelled) setResolvedOrdinal(undefined); + } + } else { + setResolvedOrdinal(userStatus?.hasOrdinal); + } + }; + run(); + return () => { cancelled = true; }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [address, isBitcoinAddress, userStatus?.hasOrdinal]); + + const hasENS = Boolean(userStatus?.hasENS) || Boolean(resolvedEns) || Boolean(userStatus?.ensName); + const hasOrdinal = Boolean(userStatus?.hasOrdinal) || Boolean(resolvedOrdinal); // Only show a badge if the author has ENS or Ordinal ownership (not for basic verification) const shouldShowBadge = showBadge && (hasENS || hasOrdinal); diff --git a/src/components/ui/verification-step.tsx b/src/components/ui/verification-step.tsx index f9b3984..a33199e 100644 --- a/src/components/ui/verification-step.tsx +++ b/src/components/ui/verification-step.tsx @@ -36,7 +36,7 @@ export function VerificationStep({ const [verificationResult, setVerificationResult] = React.useState<{ success: boolean; message: string; - details?: any; + details?: { ensName?: string; ensAvatar?: string } | boolean | { id: string; details: string }; } | null>(null); const handleVerify = async () => { @@ -62,8 +62,8 @@ export function VerificationStep({ setVerificationResult({ success: false, message: walletType === 'bitcoin' - ? "No Ordinal ownership found. You'll have read-only access." - : "No ENS ownership found. You'll have read-only access." + ? "No Ordinal ownership found. You can still participate in the forum with your connected wallet!" + : "No ENS ownership found. You can still participate in the forum with your connected wallet!" }); } } catch (error) { @@ -81,7 +81,7 @@ export function VerificationStep({ }; const getVerificationType = () => { - return walletType === 'bitcoin' ? 'Bitcoin Ordinal' : 'ENS Domain'; + return walletType === 'bitcoin' ? 'Bitcoin Ordinal' : 'Ethereum ENS'; }; const getVerificationIcon = () => { @@ -94,9 +94,9 @@ export function VerificationStep({ const getVerificationDescription = () => { if (walletType === 'bitcoin') { - return "Verify that you own Bitcoin Ordinals to get full posting and voting access."; + return "Verify your Bitcoin Ordinal ownership to unlock premium features. If you don't own any Ordinals, you can still participate in the forum with your connected wallet."; } else { - return "Verify that you own an ENS domain to get full posting and voting access."; + return "Verify your Ethereum ENS ownership to unlock premium features. If you don't own any ENS, you can still participate in the forum with your connected wallet."; } }; @@ -128,9 +128,9 @@ export function VerificationStep({ {verificationResult.details && (
{walletType === 'bitcoin' ? ( -

Ordinal ID: {verificationResult.details.id}

+

Ordinal ID: {typeof verificationResult.details === 'object' && 'id' in verificationResult.details ? verificationResult.details.id : 'Verified'}

) : ( -

ENS Name: {verificationResult.details.ensName}

+

ENS Name: {typeof verificationResult.details === 'object' && 'ensName' in verificationResult.details ? verificationResult.details.ensName : 'Verified'}

)}
)} diff --git a/src/components/ui/wallet-wizard.tsx b/src/components/ui/wallet-wizard.tsx index a601096..bc4e007 100644 --- a/src/components/ui/wallet-wizard.tsx +++ b/src/components/ui/wallet-wizard.tsx @@ -39,7 +39,7 @@ export function WalletWizard({ setCurrentStep(1); // Start at connection step if not authenticated } else if (isAuthenticated && (verificationStatus === 'unverified' || verificationStatus === 'verifying')) { setCurrentStep(2); // Start at verification step if authenticated but not verified - } else if (isAuthenticated && (verificationStatus === 'verified-owner' || verificationStatus === 'verified-none') && !isDelegationValid()) { + } else if (isAuthenticated && (verificationStatus === 'verified-owner' || verificationStatus === 'verified-basic' || verificationStatus === 'verified-none') && !isDelegationValid()) { setCurrentStep(3); // Start at delegation step if verified but no valid delegation } else { setCurrentStep(3); // Default to step 3 if everything is complete @@ -66,34 +66,25 @@ export function WalletWizard({ }; const getStepStatus = (step: WizardStep) => { - // Step 1: Wallet connection - completed when authenticated if (step === 1) { - if (isAuthenticated) return "completed"; - if (currentStep === step) return "current"; - return "pending"; + return isAuthenticated ? 'complete' : 'current'; + } else if (step === 2) { + if (!isAuthenticated) return 'disabled'; + if (verificationStatus === 'unverified' || verificationStatus === 'verifying') return 'current'; + if (verificationStatus === 'verified-owner' || verificationStatus === 'verified-basic' || verificationStatus === 'verified-none') return 'complete'; + return 'disabled'; + } else if (step === 3) { + if (!isAuthenticated || (verificationStatus !== 'verified-owner' && verificationStatus !== 'verified-basic' && verificationStatus !== 'verified-none')) return 'disabled'; + if (isDelegationValid()) return 'complete'; + return 'current'; } - - // Step 2: Verification - completed when verified (either owner or none) - if (step === 2) { - if (verificationStatus === 'verified-owner' || verificationStatus === 'verified-none') return "completed"; - if (currentStep === step) return "current"; - return "pending"; - } - - // Step 3: Key delegation - completed when delegation is valid AND authenticated - if (step === 3) { - if (isAuthenticated && isDelegationValid()) return "completed"; - if (currentStep === step) return "current"; - return "pending"; - } - - return "pending"; + return 'disabled'; }; const renderStepIcon = (step: WizardStep) => { const status = getStepStatus(step); - if (status === "completed") { + if (status === "complete") { return ; } else if (status === "current") { return ; @@ -130,7 +121,7 @@ export function WalletWizard({ @@ -139,7 +130,7 @@ export function WalletWizard({
{step < 3 && (
diff --git a/src/contexts/AuthContext.tsx b/src/contexts/AuthContext.tsx index 045fe5e..e49abd0 100644 --- a/src/contexts/AuthContext.tsx +++ b/src/contexts/AuthContext.tsx @@ -6,7 +6,7 @@ import { OpchanMessage } from '@/types'; import { useAppKitAccount, useDisconnect, modal } from '@reown/appkit/react'; import { DelegationDuration } from '@/lib/identity/signatures/key-delegation'; -export type VerificationStatus = 'unverified' | 'verified-none' | 'verified-owner' | 'verifying'; +export type VerificationStatus = 'unverified' | 'verified-none' | 'verified-basic' | 'verified-owner' | 'verifying'; interface AuthContextType { currentUser: User | null; @@ -76,13 +76,39 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { const newUser: User = { address, walletType: isBitcoinConnected ? 'bitcoin' : 'ethereum', - verificationStatus: 'unverified', + verificationStatus: 'verified-basic', // Connected wallets get basic verification by default lastChecked: Date.now(), }; - setCurrentUser(newUser); - setVerificationStatus('unverified'); - authServiceRef.current.saveUser(newUser); + // For Ethereum wallets, try to check ENS ownership immediately + if (isEthereumConnected) { + authServiceRef.current.getWalletInfo().then((walletInfo) => { + if (walletInfo?.ensName) { + const updatedUser = { + ...newUser, + ensOwnership: true, + ensName: walletInfo.ensName, + verificationStatus: 'verified-owner' as const, + }; + setCurrentUser(updatedUser); + setVerificationStatus('verified-owner'); + authServiceRef.current.saveUser(updatedUser); + } else { + setCurrentUser(newUser); + setVerificationStatus('verified-basic'); + authServiceRef.current.saveUser(newUser); + } + }).catch(() => { + // Fallback to basic verification if ENS check fails + setCurrentUser(newUser); + setVerificationStatus('verified-basic'); + authServiceRef.current.saveUser(newUser); + }); + } else { + setCurrentUser(newUser); + setVerificationStatus('verified-basic'); + authServiceRef.current.saveUser(newUser); + } const chainName = isBitcoinConnected ? 'Bitcoin' : 'Ethereum'; const displayName = `${address.slice(0, 6)}...${address.slice(-4)}`; @@ -95,7 +121,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { const verificationType = isBitcoinConnected ? 'Ordinal ownership' : 'ENS ownership'; toast({ title: "Action Required", - description: `Please verify your ${verificationType} and delegate a signing key for better UX.`, + description: `You can participate in the forum now! Verify your ${verificationType} for premium features and delegate a signing key for better UX.`, }); } } else { @@ -126,9 +152,9 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { const getVerificationStatus = (user: User): VerificationStatus => { if (user.walletType === 'bitcoin') { - return user.ordinalOwnership ? 'verified-owner' : 'verified-none'; + return user.ordinalOwnership ? 'verified-owner' : 'verified-basic'; } else if (user.walletType === 'ethereum') { - return user.ensOwnership ? 'verified-owner' : 'verified-none'; + return user.ensOwnership ? 'verified-owner' : 'verified-basic'; } return 'unverified'; }; @@ -169,18 +195,18 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { if (updatedUser.walletType === 'bitcoin' && updatedUser.ordinalOwnership) { toast({ title: "Ordinal Verified", - description: "You now have full access. We recommend delegating a key for better UX.", + description: "You now have premium access with higher relevance bonuses. We recommend delegating a key for better UX.", }); } else if (updatedUser.walletType === 'ethereum' && updatedUser.ensOwnership) { toast({ title: "ENS Verified", - description: "You now have full access. We recommend delegating a key for better UX.", + description: "You now have premium access with higher relevance bonuses. We recommend delegating a key for better UX.", }); } else { const verificationType = updatedUser.walletType === 'bitcoin' ? 'Ordinal Operators' : 'ENS domain'; toast({ - title: "Read-Only Access", - description: `No ${verificationType} found. You have read-only access.`, + title: "Basic Access Granted", + description: `No ${verificationType} found, but you can still participate in the forum with your connected wallet.`, variant: "default", }); } diff --git a/src/contexts/ForumContext.tsx b/src/contexts/ForumContext.tsx index c62f81a..4d97b6a 100644 --- a/src/contexts/ForumContext.tsx +++ b/src/contexts/ForumContext.tsx @@ -21,6 +21,8 @@ import { getDataFromCache } from '@/lib/forum/transformers'; import { RelevanceCalculator } from '@/lib/forum/relevance'; import { UserVerificationStatus } from '@/lib/forum/types'; import { AuthService } from '@/lib/identity/services/AuthService'; +import { getEnsName } from '@wagmi/core'; +import { config } from '@/lib/identity/wallets/appkit'; interface ForumContextType { cells: Cell[]; @@ -138,21 +140,55 @@ export function ForumProvider({ children }: { children: React.ReactNode }) { // Create generic user object for other addresses allUsers.push({ address, - walletType: 'bitcoin', // Default, will be updated if we have more info + walletType: address.startsWith('0x') ? 'ethereum' : 'bitcoin', verificationStatus: 'unverified' }); } }); - const userVerificationStatus = relevanceCalculator.buildUserVerificationStatus(allUsers); + const initialStatus = relevanceCalculator.buildUserVerificationStatus(allUsers); - // Transform data with relevance calculation - const { cells, posts, comments } = getDataFromCache(verifyFn, userVerificationStatus); + // Transform data with relevance calculation (initial pass) + const { cells, posts, comments } = getDataFromCache(verifyFn, initialStatus); setCells(cells); setPosts(posts); setComments(comments); - setUserVerificationStatus(userVerificationStatus); + setUserVerificationStatus(initialStatus); + + // Enrich: resolve ENS for ethereum addresses asynchronously and update + (async () => { + const targets = allUsers.filter(u => u.walletType === 'ethereum' && !u.ensOwnership); + if (targets.length === 0) return; + const lookups = await Promise.all(targets.map(async (u) => { + try { + const name = await getEnsName(config, { address: u.address as `0x${string}` }); + return { address: u.address, ensName: name || undefined }; + } catch { + return { address: u.address, ensName: undefined }; + } + })); + const ensByAddress = new Map(lookups.map(l => [l.address, l.ensName])); + const enrichedUsers: User[] = allUsers.map(u => { + const ensName = ensByAddress.get(u.address); + if (ensName) { + return { + ...u, + walletType: 'ethereum', + ensOwnership: true, + ensName, + verificationStatus: 'verified-owner' + } as User; + } + return u; + }); + const enrichedStatus = relevanceCalculator.buildUserVerificationStatus(enrichedUsers); + const transformed = getDataFromCache(verifyFn, enrichedStatus); + setCells(transformed.cells); + setPosts(transformed.posts); + setComments(transformed.comments); + setUserVerificationStatus(enrichedStatus); + })(); }, [authService, isAuthenticated, currentUser]); const handleRefreshData = async () => { diff --git a/src/lib/forum/actions.ts b/src/lib/forum/actions.ts index 3d6da0e..e04e7b5 100644 --- a/src/lib/forum/actions.ts +++ b/src/lib/forum/actions.ts @@ -35,7 +35,22 @@ export const createPost = async ( if (!isAuthenticated || !currentUser) { toast({ title: 'Authentication Required', - description: 'You need to verify Ordinal ownership to post.', + description: 'You need to connect your wallet to post.', + variant: 'destructive', + }); + return null; + } + + // Check if user has basic verification or better, or owns ENS/Ordinal + const hasENSOrOrdinal = !!(currentUser.ensOwnership || currentUser.ordinalOwnership); + const isVerified = currentUser.verificationStatus === 'verified-owner' || + currentUser.verificationStatus === 'verified-basic' || + hasENSOrOrdinal; + + if (!isVerified && (currentUser.verificationStatus === 'unverified' || currentUser.verificationStatus === 'verifying')) { + toast({ + title: 'Verification Required', + description: 'Please complete wallet verification to post.', variant: 'destructive', }); return null; @@ -92,7 +107,22 @@ export const createComment = async ( if (!isAuthenticated || !currentUser) { toast({ title: 'Authentication Required', - description: 'You need to verify Ordinal ownership to comment.', + description: 'You need to connect your wallet to comment.', + variant: 'destructive', + }); + return null; + } + + // Check if user has basic verification or better, or owns ENS/Ordinal + const hasENSOrOrdinal = !!(currentUser.ensOwnership || currentUser.ordinalOwnership); + const isVerified = currentUser.verificationStatus === 'verified-owner' || + currentUser.verificationStatus === 'verified-basic' || + hasENSOrOrdinal; + + if (!isVerified && (currentUser.verificationStatus === 'unverified' || currentUser.verificationStatus === 'verifying')) { + toast({ + title: 'Verification Required', + description: 'Please complete wallet verification to comment.', variant: 'destructive', }); return null; @@ -210,7 +240,22 @@ export const vote = async ( if (!isAuthenticated || !currentUser) { toast({ title: 'Authentication Required', - description: 'You need to verify Ordinal ownership to vote.', + description: 'You need to connect your wallet to vote.', + variant: 'destructive', + }); + return false; + } + + // Check if user has basic verification or better, or owns ENS/Ordinal + const hasENSOrOrdinal = !!(currentUser.ensOwnership || currentUser.ordinalOwnership); + const isVerified = currentUser.verificationStatus === 'verified-owner' || + currentUser.verificationStatus === 'verified-basic' || + hasENSOrOrdinal; + + if (!isVerified && (currentUser.verificationStatus === 'unverified' || currentUser.verificationStatus === 'verifying')) { + toast({ + title: 'Verification Required', + description: 'Please complete wallet verification to vote.', variant: 'destructive', }); return false; diff --git a/src/lib/forum/relevance.ts b/src/lib/forum/relevance.ts index 39d7140..2df051f 100644 --- a/src/lib/forum/relevance.ts +++ b/src/lib/forum/relevance.ts @@ -14,7 +14,8 @@ export class RelevanceCalculator { COMMENT: 0.5 }; - private static readonly VERIFICATION_BONUS = 1.25; // 25% increase + private static readonly VERIFICATION_BONUS = 1.25; // 25% increase for ENS/Ordinal owners + private static readonly BASIC_VERIFICATION_BONUS = 1.1; // 10% increase for basic verified users private static readonly VERIFIED_UPVOTE_BONUS = 0.1; private static readonly VERIFIED_COMMENTER_BONUS = 0.05; @@ -201,10 +202,10 @@ export class RelevanceCalculator { /** - * Check if a user is verified (has ENS or ordinal ownership) + * Check if a user is verified (has ENS or ordinal ownership, or basic verification) */ isUserVerified(user: User): boolean { - return !!(user.ensOwnership || user.ordinalOwnership); + return !!(user.ensOwnership || user.ordinalOwnership || user.verificationStatus === 'verified-basic'); } /** @@ -218,7 +219,8 @@ export class RelevanceCalculator { isVerified: this.isUserVerified(user), hasENS: !!user.ensOwnership, hasOrdinal: !!user.ordinalOwnership, - ensName: user.ensName + ensName: user.ensName, + verificationStatus: user.verificationStatus }; }); @@ -245,22 +247,31 @@ export class RelevanceCalculator { } /** - * Apply author verification bonus + * Apply verification bonus for verified authors */ private applyAuthorVerificationBonus( - score: number, - authorAddress: string, + score: number, + authorAddress: string, userVerificationStatus: UserVerificationStatus ): { bonus: number; isVerified: boolean } { const authorStatus = userVerificationStatus[authorAddress]; const isVerified = authorStatus?.isVerified || false; - if (isVerified) { - const bonus = score * (RelevanceCalculator.VERIFICATION_BONUS - 1); - return { bonus, isVerified }; + if (!isVerified) { + return { bonus: 0, isVerified: false }; } - - return { bonus: 0, isVerified }; + + // Apply different bonuses based on verification level + let bonus = 0; + if (authorStatus?.verificationStatus === 'verified-owner') { + // Full bonus for ENS/Ordinal owners + bonus = score * (RelevanceCalculator.VERIFICATION_BONUS - 1); + } else if (authorStatus?.verificationStatus === 'verified-basic') { + // Lower bonus for basic verified users + bonus = score * (RelevanceCalculator.BASIC_VERIFICATION_BONUS - 1); + } + + return { bonus, isVerified: true }; } /** diff --git a/src/lib/forum/types.ts b/src/lib/forum/types.ts index 985c7f3..ed28658 100644 --- a/src/lib/forum/types.ts +++ b/src/lib/forum/types.ts @@ -22,5 +22,6 @@ export interface RelevanceScoreDetails { hasENS: boolean; hasOrdinal: boolean; ensName?: string; + verificationStatus?: 'unverified' | 'verified-none' | 'verified-basic' | 'verified-owner' | 'verifying'; }; } \ No newline at end of file diff --git a/src/lib/identity/ordinal.ts b/src/lib/identity/ordinal.ts index b49e638..18df8c9 100644 --- a/src/lib/identity/ordinal.ts +++ b/src/lib/identity/ordinal.ts @@ -9,7 +9,7 @@ export class OrdinalAPI { * @returns A promise that resolves with the API response. */ async getOperatorDetails(address: string): Promise { - + if (import.meta.env.VITE_OPCHAN_MOCK_ORDINAL_CHECK === 'true') { console.log(`[DEV] Bypassing ordinal verification for address: ${address}`); return { diff --git a/src/lib/identity/services/AuthService.ts b/src/lib/identity/services/AuthService.ts index 71bbf42..45a18e6 100644 --- a/src/lib/identity/services/AuthService.ts +++ b/src/lib/identity/services/AuthService.ts @@ -182,6 +182,7 @@ export class AuthService { const updatedUser = { ...user, ordinalOwnership: hasOperators, + verificationStatus: hasOperators ? 'verified-owner' : 'verified-basic', lastChecked: Date.now(), }; @@ -206,6 +207,7 @@ export class AuthService { ...user, ensOwnership: hasENS, ensName: ensName, + verificationStatus: hasENS ? 'verified-owner' : 'verified-basic', lastChecked: Date.now(), }; @@ -216,11 +218,12 @@ export class AuthService { } catch (error) { console.error('Error verifying ENS ownership:', error); - // Fall back to no ENS ownership on error + // Fall back to basic verification on error const updatedUser = { ...user, ensOwnership: false, ensName: undefined, + verificationStatus: 'verified-basic', lastChecked: Date.now(), }; diff --git a/src/types/index.ts b/src/types/index.ts index ceb76dc..935b4c1 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -15,7 +15,7 @@ export interface User { ensAvatar?: string; ensOwnership?: boolean; - verificationStatus: 'unverified' | 'verified-none' | 'verified-owner' | 'verifying'; + verificationStatus: 'unverified' | 'verified-none' | 'verified-basic' | 'verified-owner' | 'verifying'; signature?: string; lastChecked?: number;